Squashed 'third_party/allwpilib/' changes from 66b57f032..e473a00f9

e473a00f9 [wpiutil] Base64: Add unsigned span/vector variants (#3702)
52f2d580e [wpiutil] raw_uv_ostream: Add reset() (#3701)
d7b1e3576 [wpiutil] WebSocket: move std::function (#3700)
93799fbe9 [examples] Fix description of TrapezoidProfileSubsystem (#3699)
b84644740 [wpimath] Document pose estimator states, inputs, and outputs (#3698)
2dc35c139 [wpimath] Fix classpaths for JNI class loads (#3697)
2cb171f6f [docs] Set Doxygen extract_all to true and fix Doxygen failures (#3695)
a939cd9c8 [wpimath] Print uncontrollable/unobservable models in LQR and KF (#3694)
d5270d113 [wpimath] Clean up C++ StateSpaceUtil tests (#3692)
b20903960 [wpimath] Remove redundant discretization tests from StateSpaceUtilTest (#3689)
c0cb545b4 [wpilibc] Add deprecated Doxygen attribute to SpeedController (#3691)
35c9f66a7 [wpilib] Rename PneumaticsHub to PneumaticHub (#3686)
796d03d10 [wpiutil] Remove unused LLVM header (#3688)
8723caf78 [wpilibj] Make Java TrapezoidProfile.Constraints an immutable class (#3687)
187f50a34 [wpimath] Catch incorrect parameters to state-space models earlier (#3680)
8d04606c4 Replace instances of frc-characterization with SysId (NFC) (#3681)
b82d4f6e5 [hal, cscore, ntcore] Use WPI common handle type base
87e34967e [wpiutil] Add synchronization primitives
e32499c54 [wpiutil] Add ParallelTcpConnector (#3655)
aa0b49228 [wpilib] Remove redundant "quick turn" docs for curvature drive (NFC) (#3674)
57301a7f9 [hal] REVPH: Start closed-loop compressor control on init (#3673)
d1842ea8f [wpilib] Improve interrupt docs (NFC) (#3679)
558151061 [wpiutil] Add DsClient (#3654)
181723e57 Replace `.to<double>()` and `.template to<double>()` with `.value()` (#3667)
6bc1db44b [commands] Add pointer overload of AddRequirements (artf6003) (#3669)
737b57ed5 [wpimath] Update to drake v0.35.0 (#3665)
4d287d1ae [build] Upgrade WPIJREArtifact to JRE 2022-11.0.12u5 (#3666)
f26eb5ada [hal] Fix another typo (get -> gets) (NFC) (#3663)
94ed275ba [hal] Fix misspelling (numer -> number) (NFC) (#3662)
ac2f44da3 [wpiutil] uv: use move for std::function (#3653)
75fa1fbfb [wpiutil] json::serializer: Optimize construction (#3647)
5e689faea [wpiutil] Import MessagePack implementation (mpack) (#3650)
649a50b40 [wpiutil] Add LEB128 byte-by-byte reader (#3652)
e94397a97 [wpiutil] Move json_serializer.h to public headers (#3646)
4ec58724d [wpiutil] uv::Tcp: Clarify SetNoDelay documentation (#3649)
8cb294aa4 [wpiutil] WebSocket: Make Shutdown() public (#3651)
2b3a9a52b [wpiutil] json: Fix map iterator key() for std::string_view (#3645)
138cbb94b [wpiutil] uv::Async: Add direct call for no-parameter specialization (#3648)
e56d6dea8 [ci] Update testbench pool image to ubuntu-latest (#3643)
43f30e44e [build] Enable comments in doxygen source files (#3644)
9e6db17ef [build] Enable doxygen preprocessor expansion of WPI_DEPRECATED (#3642)
0e631ad2f Add WPILib version to issue template (#3641)
6229d8d2f [build] Docs: set case_sense_names to false (#3392)
4647d09b5 [docs] Fix Doxygen warnings, add CI docs lint job (#3639)
4ad3a5402 [hal] Fix PWM allocation channel (#3637)
05e5feac4 [docs] Fix brownout docs (NFC) (#3638)
67df469c5 [examples] Remove old command-based templates and examples (#3263)
689e9ccfb [hal, wpilib] Add brownout voltage configuration (#3632)
9cd4bc407 [docs] Add useLocal to avoid using installer artifacts (#3634)
61996c2bb [cscore] Fix Java direct callback notifications (#3631)
6d3dd99eb [build] Update to newest native-utils (#3633)
f0b484892 [wpiutil] Fix StringMap iterator equality check (#3629)
8352cbb7a Update development build instructions for 2022 (#3616)
6da08b71d [examples] Fix Intermediate Vision Java Example description (#3628)
5d99059bf [wpiutil] Remove optional.h (#3627)
fa41b106a [glass, wpiutil] Add missing format args (#3626)
4e3fd7d42 [build] Enable Zc:__cplusplus for Windows (#3625)
791d8354d [build] Suppress deprecation/removal warnings for old commands (#3618)
10f19e6fc [hal, wpilib] Add REV PneumaticsHub (#3600)
4c61a1305 [ntcore] Revert to per-element copy for toNative() (#3621)
7b3f62244 [wpiutil] SendableRegistry: Print exception stacktrace (#3620)
d347928e4 [hal] Use better error for when console out is enabled while attempting to use onboard serial port (#3622)
cc31079a1 [hal] Use setcap instead of setuid for setting thread priorities (#3613)
4676648b7 [wpimath] Upgrade to Drake v0.34.0 (#3607)
c7594c911 [build] Allow building wpilibc in cmake without cscore and opencv (#3605)
173cb7359 [wpilib] Add TimesliceRobot (#3502)
af295879f [hal] Set error status for I2C port out of range (#3603)
95dd20a15 [build] Enable spotbugs (#3601)
b65fce86b [wpilib] Remove Timer lock in wpilibj and update docs (#3602)
3b8d3bbcb Remove unused and add missing deprecated.h includes (#3599)
f9e976467 [examples] Rename DriveTrain classes to Drivetrain (#3594)
118a27be2 [wpilib] Add Timer tests (#3595)
59c89428e [wpilib] Deprecate Timer::HasPeriodPassed() (#3598)
202ca5e78 Force C++17 in .clang-format (#3597)
d6f185d8e Rename tests for consistency (#3592)
54ca474db [ci] Enable asan and tsan in CI for tests that pass (#3591)
1ca383b23 Add Debouncer (#3590)
179fde3a7 [build] Update to 2022 native utils and gradle 7 (#3588)
50198ffcf [examples] Add Mechanism2d visualization to Elevator Sim (#3587)
a446c2559 [examples] Synchronize C++ and Java Mechanism2d examples (#3589)
a7fb83103 [ci] clang-tidy: Generate compilation commands DB with Gradle (#3585)
4f5e0c9f8 [examples] Update ArmSimulation example to use Mechanism2d (#3572)
8164b91dc [CI] Print CMake test output on failure (#3583)
4d5fca27e [wpilib] Impove Mechanism2D documentation (NFC) (#3584)
fe59e4b9f Make C++ test names more consistent (#3586)
5c8868549 [wpilibc] Fix C++ MechanisimRoot2D to use same NT entries as Java/Glass (#3582)
9359431ba [wpimath] Clean up Eigen usage
72716f51c [wpimath] Upgrade to Eigen 3.4
382deef75 [wpimath] Explicitly export wpimath symbols
161e21173 [ntcore] Match standard handle layout, only allow 16 instances (#3577)
263a24811 [wpimath] Use jinja for codegen (#3574)
725251d29 [wpilib] Increase tolerances of DifferentialDriveSimTest (#3581)
4dff87301 [wpimath] Make LinearFilter::Factorial() constexpr (#3579)
60ede67ab [hal, wpilib] Switch PCM to be a single object that is allowed to be duplicated (#3475)
906bfc846 [build] Add CMake build support for sanitizers (#3576)
0d4f08ad9 [hal] Simplify string copy of joystick name (#3575)
a52bf87b7 [wpiutil] Add Java function package (#3570)
40c7645d6 [wpiutil] UidVector: Return old object from erase() (#3571)
5b886a23f [wpiutil] jni_util: Add size, operator[] to JArrayRef (#3569)
65797caa7 [sim] Fix halsim_ds_socket stringop overflow warning from GCC 10 (#3568)
66abb3988 [hal] Update runtime enum to allow selecting roborio 2 (#3565)
95a12e0ee [hal] UidSetter: Don't revert euid if its already current value (#3566)
27951442b [wpimath] Use external Eigen headers only (#3564)
c42e053ae [docs] Update to doxygen 1.9.2 (#3562)
e7048c8c8 [docs] Disable doxygen linking for common words that are also classes (#3563)
d8e0b6c97 [wpilibj] Fix java async interrupts (#3559)
5e6c34c61 Update to 2022 roborio image (#3537)
828f073eb [wpiutil] Fix uv::Buffer memory leaks caught by asan (#3555)
2dd5701ac [cscore] Fix mutex use-after-free in cscore test (#3557)
531439198 [ntcore] Fix NetworkTables memory leaks caught by asan (#3556)
3d9a4d585 [wpilibc] Fix AnalogTriggerOutput memory leak reported by asan (#3554)
54eda5928 [wpiutil] Ignore ubsan vptr upcast warning in SendableHelper moves (#3553)
5a4f75c9f [wpilib] Replace Speed controller comments with motor controller (NFC) (#3551)
7810f665f [wpiutil] Fix bug in uleb128 (#3540)
697e2dd33 [wpilib] Fix errant jaguar reference in comments (NFC) (#3550)
936c64ff5 [docs] Enable -linksource for javadocs (#3549)
1ea654954 [build] Upgrade CMake build to googletest 1.11.0 (#3548)
32d9949e4 [wpimath] Move controller tests to wpimath (#3541)
01ba56a8a [hal] Replace strncpy with memcpy (#3539)
e109c4251 [build] Rename makeSim flag to forceGazebo to better describe what it does (#3535)
e4c709164 [docs] Use a doxygen theme and add logo (#3533)
960b6e589 [wpimath] Fix Javadoc warning (#3532)
82eef8d5e [hal] Remove over current fault HAL functions from REV PDH (#3526)
aa3848b2c [wpimath] Move RobotDriveBase::ApplyDeadband() to MathUtil (#3529)
3b5d0d141 [wpimath] Add LinearFilter::BackwardFiniteDifference() (#3528)
c8fc715fe [wpimath] Upgrade drake files to v0.33.0 (#3531)
e5fe3a8e1 [build] Treat javadoc warnings as errors in CI and fix warnings (#3530)
e0c6cd3dc [wpimath] Add an operator for composing two Transform2ds (#3527)
2edd510ab [sim] Add sim wrappers for sensors that use SimDevice (#3517)
2b3e2ebc1 [hal] Fix HAL Notifier thread priority setting (#3522)
ab4cb5932 [gitignore] Update gitignore to ignore bazel / clion files (#3524)
57c8615af [build] Generate spotless patch on failure (#3523)
b90317321 Replace std::cout and std::cerr with fmt::print() (#3519)
10cc8b89c [hal] [wpilib] Add initial support for the REV PDH (#3503)
5d9ae3cdb [hal] Set HAL Notifier thread as RT by default (#3482)
192d251ee [wpilibcIntegrationTests] Properly disable DMA integration tests (#3514)
031962608 [wpilib] Add PS4Controller, remove Hand from GenericHID/XboxController (#3345)
25f6f478a [wpilib] Rename DriverStation::IsOperatorControl() to IsTeleop() (#3505)
e80f09f84 [wpilibj] Add unit tests (#3501)
c159f91f0 [wpilib] Only read DS control word once in IterativeRobotBase (#3504)
eb790a74d Add rio development docs documenting myRobot deploy tasks (#3508)
e47451f5a [wpimath] Replace auto with Eigen types (#3511)
252b8c83b Remove Java formatting from build task in CI (#3507)
09666ff29 Shorten Gazebo CI build (#3506)
baf2e501d Update myRobot to use 2021 java (#3509)
5ac60f0a2 [wpilib] Remove IterativeRobotBase mode init prints (#3500)
fb2ee8ec3 [wpilib] Add TimedRobot functions for running code on mode exit (#3499)
94e0db796 [wpilibc] Add more unit tests (#3494)
b25324695 [wpilibj] Add units to parameter names (NFC) (#3497)
1ac73a247 [hal] Rename PowerDistributionPanel to PowerDistribution (#3466)
2014115bc [examples] frisbeebot: Fix typo and reflow comments (NFC) (#3498)
4a944dc39 [examples] Consistently use 0 for controller port (#3496)
3838cc4ec Use unicode characters in docs equations (#3487)
85748f2e6 [examples] Add C++ TankDrive example (#3493)
d7b8aa56d [wpilibj] Rename DriverStation In[Mode] functions to follow style guide (#3488)
16e096cf8 [build] Fix CMake Windows CI (#3490)
50af74c38 [wpimath] Clean up NumericalIntegration and add Discretization tests (#3489)
bfc209b12 Automate fmt update (#3486)
e7f9331e4 [build] Update to Doxygen 1.9.1 (#3008)
ab8e8aa2a [wpimath] Update drake with upstream (#3484)
1ef826d1d [wpimath] Fix IOException path in WPIMath JNI (#3485)
52bddaa97 [wpimath] Disable iostream support for units and enable fmtlib (#3481)
e4dc3908b [wpiutil] Upgrade to fmtlib 8.0.1 (#3483)
1daadb812 [wpimath] Implement Dormand-Prince integration method (#3476)
9c2723391 [cscore] Add [[nodiscard]] to GrabFrame functions (#3479)
7a8796414 [wpilib] Add Notifier integration tests (#3480)
f8f13c536 [wpilibcExamples] Prefix decimal numbers with 0 (#3478)
1adb69c0f [ntcore] Use "NetworkTables" instead of "Network Tables" in NT specs (#3477)
5f5830b96 Upload wpiformat diff if one exists (#3474)
9fb4f35bb [wpimath] Add tests for DARE overload with Q, R, and N matrices (#3472)
c002e6f92 Run wpiformat (#3473)
c154e5262 [wpilib] Make solenoids exclusive use, PCM act like old sendable compressor (#3464)
6ddef1cca [hal] JNI setDIO: use a boolean and not a short (#3469)
9d68d9582 Remove extra newlines after open curly braces (NFC) (#3471)
a4233e1a1 [wpimath] Add script for updating Drake (#3470)
39373c6d2 Update README.md for new GCC version requirement (#3467)
d29acc90a [wpigui] Add option to reset UI on exit (#3463)
a371235b0 [ntcore] Fix dangling pointer in logger (#3465)
53b4891a5 [wpilibcintegrationtests] Fix deprecated Preferences usage (#3461)
646ded912 [wpimath] Remove incorrect discretization in pose estimators (#3460)
ea0b8f48e Fix some deprecation warnings due to fmtlib upgrade (#3459)
2067d7e30 [wpilibjexamples] Add wpimathjni, wpiutiljni to library path (#3455)
866571ab4 [wpiutil] Upgrade to fmtlib 8.0.0 (#3457)
4e1fa0308 [build] Skip PDB copy on windows build servers (#3458)
b45572167 [build] Change CI back to 18.04 docker images (#3456)
57a160f1b [wpilibc] Fix LiveWindow deprecation warning in RobotBase skeleton template (#3454)
29ae8640d [HLT] Implement duty cycle cross connect tests (#3453)
ee6377e54 [HLT] Add relay and analog cross connects (#3452)
b0f1ae7ea [build] CMake: Build the HAL even if WITH_CSCORE=OFF (#3449)
7aae2b72d Replace std::to_string() with fmt::format() (#3451)
73fcbbd74 [HLT] Add relay digital cross connect tests (#3450)
e7bedde83 [HLT] Add PWM tests that use DMA as the back end (#3447)
7253edb1e [wpilibc] Timer: Fix deprecated warning (#3446)
efa28125c [wpilibc] Add message to RobotBase on how to read stacktrace (#3444)
9832fcfe1 [hal] Fix DIO direction getter (#3445)
49c71f9f2 [wpilibj] Clarify robot quit message (#3364)
791770cf6 [wpimath] Move controller from wpilibj to wpimath (#3439)
9ce9188ff [wpimath] Add ReportWarning to MathShared (#3441)
362066a9b [wpilib] Deprecate getInstance() in favor of static functions (#3440)
26ff9371d Initial commit of cross connect integration test project (#3434)
4a36f86c8 [hal] Add support for DMA to Java (#3158)
85144e47f [commands] Unbreak build (#3438)
b417d961e Split Sendable into NT and non-NT portions (#3432)
ef4ea84cb [commands] Change grouping decorator impl to flatten nested group structures (#3335)
b422665a3 [examples] Invert right side of drive subsystems (#3437)
186dadf14 [hal] Error if attempting to set DIO output on an input port (#3436)
04e64db94 Remove redundant C++ lambda parentheses (NFC) (#3433)
f60994ad2 [wpiutil] Rename Java package to edu.wpi.first.util (#3431)
cfa1ca96f [wpilibc] Make ShuffleboardValue non-copyable (#3430)
4d9ff7643 Fix documentation warnings generated by JavaDoc (NFC) (#3428)
9e1b7e046 [build] Fix clang-tidy and clang-format (#3429)
a77c6ff3a [build] Upgrade clang-format and clang-tidy (NFC) (#3422)
099fde97d [wpilib] Improve PDP comments (NFC) (#3427)
f8fc2463e [wpilibc, wpiutil] Clean up includes (NFC) (#3426)
e246b7884 [wpimath] Clean up member initialization in feedforward classes (#3425)
c1e128bd5 Disable frivolous PMD warnings and enable PMD in ntcore (#3419)
8284075ee Run "Lint and Format" CI job on push as well as pull request (#3412)
f7db09a12 [wpimath] Move C++ filters into filter folder to match Java (#3417)
f9c3d54bd [wpimath] Reset error covariance in pose estimator ResetPosition() (#3418)
0773f4033 [hal] Ensure HAL status variables are initialized to zero (#3421)
d068fb321 [build] Upgrade CI to use 20.04 docker images (#3420)
8d054c940 [wpiutil] Remove STLExtras.h
80f1d7921 [wpiutil] Split function_ref to a separate header
64f541325 Use wpi::span instead of wpi::ArrayRef across all libraries (#3414)
2abbbd9e7 [build] clang-tidy: Remove bugprone-exception-escape (#3415)
a5c471af7 [wpimath] Add LQR template specialization for 2x2 system
edd2f0232 [wpimath] Add DARE solver for Q, R, and N with LQR ctor overloads
b2c3b2dd8 Use std::string_view and fmtlib across all libraries (#3402)
4f1cecb8e [wpiutil] Remove Path.h (#3413)
b336eac34 [build] Publish halsim_ws_core to Maven
2a09f6fa4 [build] Also build sim modules as static libraries
0e702eb79 [hal] Add a unified PCM object (#3331)
dea841103 [wpimath] Add fmtlib formatter overloads for Eigen::Matrix and units (#3409)
82856cf81 [wpiutil] Improve wpi::circular_buffer iterators (#3410)
8aecda03e [wpilib] Fix a documentation typo (#3408)
5c817082a [wpilib] Remove InterruptableSensorBase and replace with interrupt classes (#2410)
15c521a7f [wpimath] Fix drivetrain system identification (#3406)
989de4a1b [build] Force all linker warnings to be fatal for rio builds (#3407)
d9eeb45b0 [wpilibc] Add units to Ultrasonic class API (#3403)
fe570e000 [wpiutil] Replace llvm filesystem with C++17 filesystem (#3401)
01dc0249d [wpimath] Move SlewRateLimiter from wpilib to wpimath (#3399)
93523d572 [wpilibc] Clean up integration tests (#3400)
4f7a4464d [wpiutil] Rewrite StringExtras for std::string_view (#3394)
e09293a15 [wpilibc] Transition C++ classes to units::second_t (#3396)
827b17a52 [build] Create run tasks for Glass and OutlineViewer (#3397)
a61037996 [wpiutil] Avoid MSVC warning on span include (#3393)
4e2c3051b [wpilibc] Use std::string_view instead of Twine (#3380)
50915cb7e [wpilibc] MotorSafety::GetDescription(): Return std::string (#3390)
f4e2d26d5 [wpilibc] Move NullDeleter from frc/Base.h to wpi/NullDeleter.h (#3387)
cb0051ae6 [wpilibc] SimDeviceSim: use fmtlib (#3389)
a238cec12 [wpiutil] Deprecate wpi::math constants in favor of wpi::numbers (#3383)
393bf23c0 [ntcore, cscore, wpiutil] Standardize template impl files on .inc extension (NFC) (#3124)
e7d9ba135 [sim] Disable flaky web server integration tests (#3388)
0a0003c11 [wpilibjExamples] Fix name of Java swerve drive pose estimator example (#3382)
7e1b27554 [wpilibc] Use default copies and moves when possible (#3381)
fb2a56e2d [wpilibc] Remove START_ROBOT_CLASS macro (#3384)
84218bfb4 [wpilibc] Remove frc namespace shim (#3385)
dd7824340 [wpilibc] Remove C++ compiler version static asserts (#3386)
484cf9c0e [wpimath] Suppress the -Wmaybe-uninitialized warning in Eigen (#3378)
a04d1b4f9 [wpilibc] DriverStation: Remove ReportError and ReportWarning
831c10bdf [wpilibc] Errors: Use fmtlib
87603e400 [wpiutil] Import fmtlib (#3375)
442621672 [wpiutil] Add ArrayRef/std::span/wpi::span implicit conversions
bc15b953b [wpiutil] Add std::span implementation
6d20b1204 [wpiutil] StringRef, Twine, raw_ostream: Add std::string_view support (#3373)
2385c2a43 [wpilibc] Remove Utility.h (#3376)
87384ea68 [wpilib] Fix PIDController continuous range error calculations (#3170)
04dae799a [wpimath] Add SimpleMotorFeedforward::Calculate(velocity, nextVelocity) overload (#3183)
0768c3903 [wpilib] DifferentialDrive: Remove right side inversion (#3340)
8dd8d4d2d [wpimath] Fix redundant nested math package introduced by #3316 (#3368)
49b06beed [examples] Add Field2d to RamseteController example (#3371)
4c562a445 [wpimath] Fix typo in comment of update_eigen.py (#3369)
fdbbf1188 [wpimath] Add script for updating Eigen
f1e64b349 [wpimath] Move Eigen unsupported folder into eigeninclude
224f3a05c [sim] Fix build error when building with GCC 11.1 (#3361)
ff56d6861 [wpilibj] Fix SpeedController deprecated warnings (#3360)
1873fbefb [examples] Fix Swerve and Mecanum examples (#3359)
80b479e50 [examples] Fix SwerveBot example to use unique encoder ports (#3358)
1f7c9adee [wpilibjExamples] Fix pose estimator examples (#3356)
9ebc3b058 [outlineviewer] Change default size to 600x400 (#3353)
e21b443a4 [build] Gradle: Make C++ examples runnable (#3348)
da590120c [wpilibj] Add MotorController.setVoltage default (#3347)
561d53885 [build] Update opencv to 4.5.2, imgui/implot to latest (#3344)
44ad67ca8 [wpilibj] Preferences: Add missing Deprecated annotation (#3343)
3fe8fc75a [wpilibc] Revert "Return reference from GetInstance" (#3342)
3cc2da332 Merge branch '2022'
a3cd90dd7 [wpimath] Fix classpath used by generate_numbers.py (#3339)
d6cfdd3ba [wpilib] Preferences: Deprecate Put* in favor of Set* (#3337)
ba08baabb [wpimath] Update Drake DARE solver to v0.29.0 (#3336)
497b712f6 [wpilib] Make IterativeRobotBase::m_period private with getter
f00dfed7a [wpilib] Remove IterativeRobot base class
3c0846168 [hal] Use last error reporting instead of PARAMETER_OUT_OF_RANGE (#3328)
5ef2b4fdc [wpilibj] Fix @deprecated warning for SerialPort constructor (#3329)
23d2326d1 [hal] Report previous allocation location for indexed resource duplicates (#3322)
e338f9f19 [build] Fix wpilibc runCpp task (#3327)
c8ff626fe [wpimath] Move Java classes to edu.wpi.first.math (#3316)
4e424d51f [wpilibj] DifferentialDrivetrainSim: Rename constants to match the style guide (#3312)
6b50323b0 [cscore] Use Lock2DSize if possible for Windows USB cameras (#3326)
65c148536 [wpilibc] Fix "control reaches end of non-void function" warning (#3324)
f99f62bee [wpiutil] uv Handle: Use malloc/free instead of new/delete (#3325)
365f5449c [wpimath] Fix MecanumDriveKinematics (#3266)
ff52f207c [glass, wpilib] Rewrite Mechanism2d (#3281)
ee0eed143 [wpimath] Add DCMotor factory function for Romi motors (#3319)
512738072 [hal] Add HAL_GetLastError to enable better error messages from HAL calls (#3320)
ced654880 [glass, outlineviewer] Update Mac icons to macOS 11 style (#3313)
936d3b9f8 [templates] Add Java template for educational robot (#3309)
6e31230ad [examples] Fix odometry update in SwerveControllerCommand example (#3310)
05ebe9318 Merge branch 'main' into 2022
aaf24e255 [wpilib] Fix initial heading behavior in HolonomicDriveController (#3290)
8d961dfd2 [wpilibc] Remove ErrorBase (#3306)
659b37ef9 [wpiutil] StackTrace: Include offset on Linux (#3305)
0abf6c904 [wpilib] Move motor controllers to motorcontrol package (#3302)
4630191fa [wpiutil] circular_buffer: Use value initialization instead of passing zero (#3303)
b7b178f49 [wpilib] Remove Potentiometer interface
687066af3 [wpilib] Remove GyroBase
6b168ab0c [wpilib] Remove PIDController, PIDOutput, PIDSource
948625de9 [wpimath] Document conversion from filter cutoff frequency to time constant (#3299)
3848eb8b1 [wpilibc] Fix flywhel -> flywheel typo in FlywheelSim (#3298)
3abe0b9d4 [cscore] Move java package to edu.wpi.first.cscore (#3294)
d7fabe81f [wpilib] Remove RobotDrive (#3295)
1dc81669c [wpilib] Remove GearTooth (#3293)
01d0e1260 [wpilib] Revert move of RomiGyro into main wpilibc/j (#3296)
397e569aa [ntcore] Remove "using wpi" from nt namespace
79267f9e6 [ntcore] Remove NetworkTable -> nt::NetworkTable shim
48ebe5736 [ntcore] Remove deprecated Java interfaces and classes
c2064c78b [ntcore] Remove deprecated ITable interfaces
36608a283 [ntcore] Remove deprecated C++ APIs
a1c87e1e1 [glass] LogView: Add "copy to clipboard" button (#3274)
fa7240a50 [wpimath] Fix typo in quintic spline basis matrix
ffb4d38e2 [wpimath] Add derivation for spline basis matrices
f57c188f2 [wpilib] Add AnalogEncoder(int) ctor (#3273)
8471c4fb2 [wpilib] FieldObject2d: Add setTrajectory() method (#3277)
c97acd18e [glass] Field2d enhancements (#3234)
ffb590bfc [wpilib] Fix Compressor sendable properties (#3269)
6137f98eb [hal] Rename SimValueCallback2 to SimValueCallback (#3212)
a6f653969 [hal] Move registerSimPeriodic functions to HAL package (#3211)
10c038d9b [glass] Plot: Fix window creation after removal (#3264)
2d2eaa3ef [wpigui] Ensure window will be initially visible (#3256)
4d28b1f0c [wpimath] Use JNI for trajectory serialization (#3257)
3de800a60 [wpimath] TrajectoryUtil.h: Comment formatting (NFC) (#3262)
eff592377 [glass] Plot: Don't overwrite series ID (#3260)
a79faace1 [wpilibc] Return reference from GetInstance (#3247)
9550777b9 [wpilib] PWMSpeedController: Use PWM by composition (#3248)
c8521a3c3 [glass] Plot: Set reasonable default window size (#3261)
d71eb2cf3 [glass] Plot: Show full source name as tooltip and in popup (#3255)
160fb740f [hal] Use std::lround() instead of adding 0.5 and truncating (#3012)
48e9f3951 [wpilibj] Remove wpilibj package CameraServer (#3213)
8afa596fd [wpilib] Remove deprecated Sendable functions and SendableBase (#3210)
d3e45c297 [wpimath] Make C++ geometry classes immutable (#3249)
2c98939c1 [glass] StringChooser: Don't call SameLine() at end
a18a7409f [glass] NTStringChooser: Clear value of deleted entries
2f19cf452 [glass] NetworkTablesHelper: listen to delete events
da96707dc Merge branch 'main' into 2022
c3a8bdc24 [build] Fix clang-tidy action (#3246)
21624ef27 Add ImGui OutlineViewer (#3220)
1032c9b91 [wpiutil] Unbreak wpi::Format on Windows (#3242)
2e07902d7 [glass] NTField2D: Fix name lookup (#3233)
6e23e1840 [wpilibc] Remove WPILib.h (#3235)
3e22e4506 [wpilib] Make KoP drivetrain simulation weight 60 lbs (#3228)
79d1bd6c8 [glass] NetworkTablesSetting: Allow disable of server option (#3227)
fe341a16f [examples] Use more logical elevator setpoints in GearsBot (#3198)
62abf46b3 [glass] NetworkTablesSettings: Don't block GUI (#3226)
a95a5e0d9 [glass] Move NetworkTablesSettings to libglassnt (#3224)
d6f6ceaba [build] Run Spotless formatter (NFC) (#3221)
0922f8af5 [commands] CommandScheduler.requiring(): Note return can be null (NFC) (#2934)
6812302ff [examples] Make DriveDistanceOffboard example work in sim (#3199)
f3f86b8e7 [wpimath] Add pose estimator overload for vision + std dev measurement (#3200)
1a2680b9e [wpilibj] Change CommandBase.withName() to return CommandBase (#3209)
435bbb6a8 [command] RamseteCommand: Output 0 if interrupted (#3216)
3cf44e0a5 [hal] Add function for changing HAL Notifier thread priority (#3218)
40b367513 [wpimath] Units.java: Add kg-lb conversions (#3203)
9f563d584 [glass] NT: Fix return value in StringToDoubleArray (#3208)
af4adf537 [glass] Auto-size plots to fit window (#3193)
2560146da [sim] GUI: Add option to show prefix in Other Devices (#3186)
eae3a6397 gitignore: Ignore .cache directory (#3196)
959611420 [wpilib] Require non-zero positive value for PIDController.period (#3175)
9522f2e8c [wpimath] Add methods to concatenate trajectories (#3139)
e42a0b6cf [wpimath] Rotation2d comment formatting (NFC) (#3162)
d1c7032de [wpimath] Fix order of setting gyro offset in pose estimators (#3176)
d241bc81a [sim] Add DoubleSolenoidSim and SolenoidSim classes (#3177)
cb7f39afa [wpilibc] Add RobotController::GetBatteryVoltage() to C++ (#3179)
99b5ad9eb [wpilibj] Fix warnings that are not unused variables or deprecation (#3161)
c14b23775 [build] Fixup doxygen generated include dirs to match what users would need (#3154)
d447c7dc3 [sim] Add SimDeviceSim ctor overloads (#3134)
247420c9c [build] Remove jcenter repo (#3157)
04b112e00 [build] Include debug info in plugin published artifacts (#3149)
be0ce9900 [examples] Use PWMSparkMax instead of PWMVictorSPX (#3156)
69e8d0b65 [wpilib] Move RomiGyro into main wpilibc/j (#3143)
94e685e1b [wpimath] Add custom residual support to EKF (#3148)
5899f3dd2 [sim] GUI: Make keyboard settings loading more robust (#3167)
f82aa1d56 [wpilib] Fix HolonomicDriveController atReference() behavior (#3163)
fe5c2cf4b [wpimath] Remove ControllerUtil.java (#3169)
43d40c6e9 [wpiutil] Suppress unchecked cast in CombinedRuntimeLoader (#3155)
3d44d8f79 [wpimath] Fix argument order in UKF docs (NFC) (#3147)
ba6fe8ff2 [cscore] Add USB camera change event (#3123)
533725888 [build] Tweak OpenCV cmake search paths to work better on Linux (#3144)
29bf9d6ef [cscore] Add polled support to listener
483beb636 [ntcore] Move CallbackManager to wpiutil
fdaec7759 [examples] Instantiate m_ramseteController in example (#3142)
8494a5761 Rename default branch to main (#3140)
45590eea2 [wpigui] Hardcode window scale to 1 on macOS (#3135)
834a64920 [build] Publish libglass and libglassnt to Maven (#3127)
2c2ccb361 [wpimath] Fix Rotation2d equality operator (#3128)
fb5c8c39a [wpigui] clang-tidy: readability-braces-around-statements
f7d39193a [wpigui] Fix copyright in pfd and wpigui_metal.mm
aec796b21 [ntcore] Fix conditional jump on uninitialized value (#3125)
fb13bb239 [sim] GUI: Add right click popup for keyboard joystick settings (#3119)
c517ec677 [build] Update thirdparty-imgui to 1.79-2 (#3118)
e8cbf2a71 [wpimath] Fix typo in SwerveDrivePoseEstimator doc (NFC) (#3112)
e9c86df46 [wpimath] Add tests for swerve module optimization (#3100)
6ba8c289c [examples] Remove negative of ArcadeDrive(fwd, ..) in the C++ Getting Started Example (#3102)
3f1672e89 [hal] Add SimDevice createInt() and createLong() (#3110)
15be5cbf1 [examples] Fix segfault in GearsBot C++ example (#3111)
4cf0e5e6d Add quick links to API documentation in README (#3082)
6b1898f12 Fix RT priority docs (NFC) (#3098)
b3426e9c0 [wpimath] Fix missing whitespace in pose estimator doc (#3097)
38c1a1f3e [examples] Fix feildRelative -> fieldRelative typo in XControllerCommand examples (#3104)
4488e25f1 [glass] Shorten SmartDashboard window names (#3096)
cfdb3058e [wpilibj] Update SimDeviceSimTest (#3095)
64adff5fe [examples] Fix typo in ArcadeDrive constructor parameter name (#3092)
6efc58e3d [build] Fix issues with build on windows, deprecations, and native utils (#3090)
f393989a5 [wpimath, wpiutil] Add wpi::array for compile time size checking (#3087)
d6ed20c1e [build] Set macOS deployment target to 10.14 (#3088)
7c524014c [hal] Add [[nodiscard]] to HAL_WaitForNotifierAlarm() (#3085)
406d055f0 [wpilib] Fixup wouldHitLowerLimit in elevator and arm simulation classes. (#3076)
04a90b5dd [examples] Don't continually set setpoint in PotentiometerPID Examples (#3084)
8c5bfa013 [sim] GUI: Add max value setting for keyboard joysticks (#3083)
bc80c5535 [hal] Add SimValue reset() function (#3064)
9c3b51ca0 [wpilib] Document simulation APIs (#3079)
26584ff14 [wpimath] Add model description to LinearSystemId Javadocs (#3080)
42c3d5286 [examples] Sync Java and C++ trajectories in sim example (#3081)
64e72f710 [wpilibc] Add missing function RoboRioSim::ResetData (#3073)
e95503798 [wpimath] Add optimize() to SwerveModuleState (#3065)
fb99910c2 [hal] Add SimInt and SimLong wrappers for int/long SimValue (#3066)
e620bd4d3 [doc] Add machine-readable websocket specification (#3059)
a44e761d9 [glass] Add support for plot Y axis labels
ea1974d57 [wpigui] Update imgui and implot to latest
85a0bd43c [wpimath] Add RKF45 integration (#3047)
278e0f126 [glass] Use .controllable to set widgets' read-only state (#3035)
d8652cfd4 [wpimath] Make Java DCMotor API consistent with C++ and fix motor calcs (#3046)
377b7065a [build] Add toggleOffOn to Java spotless (#3053)
1e9c79c58 [sim] Use plant output to retrieve simulated position (#3043)
78147aa34 [sim] GUI: Fix Keyboard Joystick (#3052)
cd4a2265b [ntcore] Fix NetworkTableEntry::GetRaw() (#3051)
767ac1de1 [build] Use deploy key for doc publish (#3048)
d762215d1 [build] Add publish documentation script (#3040)
1fd09593c [examples] Add missing TestInit method to GettingStarted Example (#3039)
e45a0f6ce [examples] Add RomiGyro to the Romi Reference example (#3037)
94f852572 Update imaging link and fix typo (#3038)
d73cf64e5 [examples] Update RomiReference to match motor directions (#3036)
f945462ba Bump copyright year to 2021 (#3033)
b05946175 [wpimath] Catch Drake JNI exceptions and rethrow them (#3032)
62f0f8190 [wpimath] Deduplicate angle modulus functions (#2998)
bf8c0da4b [glass] Add "About" popup with version number (#3031)
dfdd6b389 [build] Increase Gradle heap size in Gazebo build (#3028)
f5e0fc3e9 Finish clang-tidy cleanups (#3003)
d741101fe [sim] Revert accidental commit of WSProvider_PDP.h (#3027)
e1620799c [examples] Add C++ RomiReference example (#2969)
749c7adb1 [command] Fix use-after-free in CommandScheduler (#3024)
921a73391 [sim] Add WS providers for AddressableLED, PCM, and Solenoid (#3026)
26d0004fe [build] Split Actions into different yml files (#3025)
948af6d5b [wpilib] PWMSpeedController.get(): Apply Inversion (#3016)
670a187a3 [wpilibc] SuppliedValueWidget.h: Forward declare ShuffleboardContainer (#3021)
be9f72502 [ntcore] NetworkTableValue: Use std::forward instead of std::move (#3022)
daf3f4cb1 [cscore] cscore_raw_cv.h: Fix error in PutFrame() (#3019)
5acda4cc7 [wpimath] ElevatorFeedforward.h: Add time.h include
8452af606 [wpimath] units/radiation.h: Add mass.h include
630d44952 [hal] ErrorsInternal.h: Add stdint.h include
7372cf7d9 [cscore] Windows NetworkUtil.cpp: Add missing include
b7e46c558 Include .h from .inc/.inl files (NFC) (#3017)
bf8f8710e [examples] Update Romi template and example (#2996)
6ffe5b775 [glass] Ensure NetworkTableTree parent context menu has an id (#3015)
be0805b85 [build] Update to WPILibVersioningPlugin 4.1.0 (#3014)
65b2359b2 [build] Add spotless for other files (#3007)
8651aa73e [examples] Enable NT Flush in Field2d examples (#3013)
78b542737 [build] Add Gazebo build to Actions CI (#3004)
fccf86532 [sim] DriverStationGui: Fix two bugs (#3010)
185741760 [sim] WSProvider_Joystick: Fix off-by-1 in incoming buttons (#3011)
ee7114a58 [glass] Add drive class widgets (#2975)
00fa91d0d [glass] Use ImGui style for gyro widget colors (#3009)
b7a25bfc3 ThirdPartyNotices: Add portable file dialogs license (#3005)
a2e46b9a1 [glass] modernize-use-nullptr (NFC) (#3006)
a751fa22d [build] Apply spotless for java formatting (#1768)
e563a0b7d [wpimath] Make LinearSystemLoop move-constructible and move-assignable (#2967)
49085ca94 [glass] Add context menus to remove and add NetworkTables values (#2979)
560a850a2 [glass] Add NetworkTables Log window (#2997)
66782e231 [sim] Create Left/Right drivetrain current accessors (#3001)
b60eb1544 clang-tidy: bugprone-virtual-near-miss
cbe59fa3b clang-tidy: google-explicit-constructor
c97c6dc06 clang-tidy: google-readability-casting (NFC)
32fa97d68 clang-tidy: modernize-use-nullptr (NFC)
aee460326 clang-tidy: modernize-pass-by-value
29c7da5f1 clang-tidy: modernize-make-unique
6131f4e32 clang-tidy: modernize-concat-nested-namespaces (NFC)
67e03e625 clang-tidy: modernize-use-equals-default
b124f9101 clang-tidy: modernize-use-default-member-init
d11a3a638 clang-tidy: modernize-use-override (NFC)
4cc0706b0 clang-tidy: modernize-use-using (NFC)
885f5a978 [wpilibc] Speed up ScopedTracerTest (#2999)
60b596457 [wpilibj] Fix typos (NFC) (#3000)
6e1919414 [build] Bring naming checkstyle rules up to date with Google Style guide (#1781)
8c8ec5e63 [wpilibj] Suppress unchecked cast warnings (#2995)
b8413ddd5 [wpiutil] Add noexcept to timestamp static functions (#2994)
5d976b6e1 [glass] Load NetworkTableView settings on first draw (#2993)
2b4317452 Replace NOLINT(runtime/explicit) comments with NOLINT (NFC) (#2992)
1c3011ba4 [glass] Fix handling of "/" NetworkTables key (#2991)
574a42f3b [hal] Fix UnsafeManipulateDIO status check (#2987)
9005cd59e [wpilib] Clamp input voltage in sim classes (#2955)
dd494d4ab [glass] NetworkTablesModel::Update(): Avoid use-after-move (#2988)
7cca469a1 [wpimath] NormalizeAngle: Make inline, remove unnamed namespace (#2986)
2aed432b4 Add braces to C++ single-line loops and conditionals (NFC) (#2973)
0291a3ff5 [wpiutil] StringRef: Add noexcept to several constructors (#2984)
5d7315280 [wpimath] Update UnitsTest.cpp copyright (#2985)
254931b9a [wpimath] Remove LinearSystem from LinearSystemLoop (#2968)
aa89744c9 Update OtherVersions.md to include wpimath info (#2983)
1cda3f5ad [glass] Fix styleguide (#2976)
8f1f64ffb Remove year from file copyright message (NFC) (#2972)
2bc0a7795 [examples] Fix wpiformat warning about utility include (#2971)
4204da6ad [glass] Add application icon
7ac39b10f [wpigui] Add icon support
6b567e006 [wpimath] Add support for varying vision standard deviations in pose estimators (#2956)
df299d6ed [wpimath] Add UnscentedKalmanFilter::Correct() overload (#2966)
4e34f0523 [examples] Use ADXRS450_GyroSim class in simulation example (#2964)
9962f6fd7 [wpilib] Give Field2d a default Sendable name (#2953)
f9d492f4b [sim] GUI: Show "Other Devices" window by default (#2961)
a8bb2ef1c [sim] Fix ADXRS450_GyroSim and DutyCycleEncoderSim (#2963)
240c629cd [sim] Try to guess "Map Gamepad" setting (#2960)
952567dd3 [wpilibc] Add missing move constructors and assignment operators (#2959)
10b396b4c [sim] Various WebSockets fixes and enhancements (#2952)
699bbe21a [examples] Fix comments in Gearsbot to match implementation (NFC) (#2957)
27b67deca [glass] Add more widgets (#2947)
581b7ec55 [wpilib] Add option to flush NetworkTables every iterative loop
acfbb1a44 [ntcore] DispatcherBase::Flush: Use wpi::Now()
d85a6d8fe [ntcore] Reduce limit on flush and update rate to 5 ms
20fbb5c63 [sim] Fix stringop truncation warning from GCC 10 (#2945)
1051a06a7 [glass] Show NT timestamps in seconds (#2944)
98dfc2620 [glass] Fix plots (#2943)
1ba0a2ced [sim] GUI: Add keyboard virtual joystick support (#2940)
4afb13f98 [examples] Replace M_PI with wpi::math::pi (#2938)
b27d33675 [examples] Enhance Romi templates (#2931)
00b9ae77f [sim] Change default WS port number to 3300 (#2932)
65219f309 [examples] Update Field2d position in periodic() (#2928)
f78d1d434 [sim] Process WS Encoder reset internally (#2927)
941edca59 [hal] Add Java SimDeviceDataJNI.getSimDeviceName (#2924)
a699435ed [wpilibj] Fix FlywheelSim argument order in constructor (#2922)
66d641718 [examples] Add tasks to run Java examples (#2920)
558e37c41 [examples] Add simple differential drive simulation example (#2918)
4f40d991e [glass] Switch name of Glass back to glass (#2919)
549af9900 [build] Update native-utils to 2021.0.6 (#2914)
b33693009 [glass] Change basename of glass to Glass (#2915)
c9a0edfb8 [glass] Package macOS application bundle
2c5668af4 [wpigui] Add platform-specific preferences save
751dea32a [wpilibc] Try to work around ABI break introduced in #2901 (#2917)
cd8f4bfb1 [build] Package up msvc runtime into maven artifact (#2913)
a6cfcc686 [wpilibc] Move SendableChooser Doxygen comments to header (NFC) (#2911)
b8c4f603d [wpimath] Upgrade to Eigen 3.3.9 (#2910)
0075e4b39 [wpilibj] Fix NPE in Field2d (#2909)
125af556c [simulation] Fix halsim_gui ntcore and wpiutil deps (#2908)
963ad5c25 [wpilib] Add noise to Differential Drive simulator (#2903)
387f56cb7 [examples] Add Romi reference Java example and templates (#2905)
b3deda38c [examples] Zero motors on disabledInit() in sim physics examples (#2906)
2a5ca7745 [glass] Add glass: an application for display of robot data
727940d84 [wpilib] Move Field2d to SmartDashboard
8cd42478e [wpilib] SendableBuilder: Make GetTable() visible
c11d34b26 [command] Use addCommands in command group templates (#2900)
339d7445b [sim] Add HAL hooks for simulationPeriodic (#2881)
d16f05f2c [wpilib] Fix SmartDashboard update order (#2896)
5427b32a4 [wpiutil] unique_function: Restrict implicit conversion (#2899)
f73701239 [ntcore] Add missing SetDefault initializer_list functions (#2898)
f5a6fc070 [sim] Add initialized flag for all solenoids on a PCM (#2897)
bdf5ba91a [wpilibj] Fix typo in ElevatorSim (#2895)
bc8f33877 [wpilib] Add pose estimators (#2867)
3413bfc06 [wpilib] PIDController: Recompute the error in AtSetpoint() (#2822)
2056f0ce0 [wpilib] Fix bugs in Hatchbot examples (#2893)
5eb8cfd69 [wpilibc] Fix MatchDataSender (#2892)
e6a425448 [build] Delete test folders after tests execute (#2891)
d478ad00d [imgui] Allow usage of imgui_stdlib (#2889)
53eda861d [build] Add unit-testing infrastructure to examples (#2863)
cc1d86ba6 [sim] Add title to simulator GUI window (#2888)
f0528f00e [build] CMake: Use project-specific binary and source dirs (#2886)
5cd2ad124 [wpilibc] Add Color::operator!= (#2887)
6c00e7a90 [build] CI CMake: build with GUI enabled (#2884)
53170bbb5 Update roboRIO toolchain installation instructions (#2883)
467258e05 [sim] GUI: Add option to not zero disconnected joysticks (#2876)
129be23c9 Clarify JDK installation instructions in readme (#2882)
8e9290e86 [build] Add separate CMake setting for wpimath (#2885)
7cf5bebf8 [wpilibj] Cache NT writes from DriverStation (#2780)
f7f9087fb [command] Fix timing issue in RamseteCommand (#2871)
256e7904f [wpilibj] SimDeviceSim: Fix sim value changed callback (#2880)
c8ea1b6c3 [wpilib] Add function to adjust LQR controller gain for pure time delay (#2878)
2816b06c0 [sim] HAL_GetControlWord: Fully zero output (#2873)
4c695ea08 Add toolchain installation instructions to README (#2875)
a14d51806 [wpimath] DCMotor: fix doc typo (NFC) (#2868)
017097791 [build] CMake: build sim extensions as shared libs (#2866)
f61726b5a [build] Fix cmake-config files (#2865)
fc27fdac5 [wpilibc] Cache NT values from driver station (#2768)
47c59859e [sim] Make SimDevice callbacks synchronous (#2861)
6e76ab9c0 [build] Turn on WITH_GUI for Windows cmake CI
5f78b7670 [build] Set GLFW_INSTALL to OFF
5e0808c84 [wpigui] Fix Windows cmake build
508f05a47 [imgui] Fix typo in Windows CMake target sources

Change-Id: I1737b45965f31803a96676bedc7dc40e337aa321
git-subtree-dir: third_party/allwpilib
git-subtree-split: e473a00f9785f9949e5ced30901baeaf426d2fc9
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/cscore/.styleguide b/cscore/.styleguide
index fada841..f0732e1 100644
--- a/cscore/.styleguide
+++ b/cscore/.styleguide
@@ -31,6 +31,7 @@
 }
 
 includeOtherLibs {
+  ^fmt/
   ^opencv2/
   ^support/
   ^tcpsockets/
diff --git a/cscore/CMakeLists.txt b/cscore/CMakeLists.txt
index 98f6d7e..9ab9ca7 100644
--- a/cscore/CMakeLists.txt
+++ b/cscore/CMakeLists.txt
@@ -46,8 +46,8 @@
     set (cscore_config_dir share/cscore)
 endif()
 
-configure_file(cscore-config.cmake.in ${CMAKE_BINARY_DIR}/cscore-config.cmake )
-install(FILES ${CMAKE_BINARY_DIR}/cscore-config.cmake DESTINATION ${cscore_config_dir})
+configure_file(cscore-config.cmake.in ${WPILIB_BINARY_DIR}/cscore-config.cmake )
+install(FILES ${WPILIB_BINARY_DIR}/cscore-config.cmake DESTINATION ${cscore_config_dir})
 install(EXPORT cscore DESTINATION ${cscore_config_dir})
 
 SUBDIR_LIST(cscore_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
@@ -79,7 +79,7 @@
     find_package(Java REQUIRED)
     find_package(JNI REQUIRED)
     include(UseJava)
-    set(CMAKE_JAVA_COMPILE_FLAGS "-Xlint:unchecked")
+    set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
 
     #find java files, copy them locally
 
@@ -87,11 +87,11 @@
         set(OPENCV_JAVA_INSTALL_DIR ${OpenCV_INSTALL_PATH}/share/OpenCV/java/)
     endif()
 
-    find_file(OPENCV_JAR_FILE NAMES opencv-${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}.jar PATHS ${OPENCV_JAVA_INSTALL_DIR} ${OpenCV_INSTALL_PATH}/bin NO_DEFAULT_PATH)
+    find_file(OPENCV_JAR_FILE NAMES opencv-${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}.jar PATHS ${OPENCV_JAVA_INSTALL_DIR} ${OpenCV_INSTALL_PATH}/bin ${OpenCV_INSTALL_PATH}/share/java NO_DEFAULT_PATH)
     find_file(OPENCV_JNI_FILE NAMES libopencv_java${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}.so
                                     libopencv_java${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}.dylib
                                     opencv_java${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}.dll
-                                    PATHS ${OPENCV_JAVA_INSTALL_DIR} ${OpenCV_INSTALL_PATH}/bin ${OpenCV_INSTALL_PATH}/bin/Release ${OpenCV_INSTALL_PATH}/bin/Debug ${OpenCV_INSTALL_PATH}/lib NO_DEFAULT_PATH)
+                                    PATHS ${OPENCV_JAVA_INSTALL_DIR} ${OpenCV_INSTALL_PATH}/bin ${OpenCV_INSTALL_PATH}/bin/Release ${OpenCV_INSTALL_PATH}/bin/Debug ${OpenCV_INSTALL_PATH}/lib ${OpenCV_INSTALL_PATH}/lib/jni NO_DEFAULT_PATH)
 
     file(GLOB
         cscore_jni_src src/main/native/cpp/jni/CameraServerJNI.cpp)
diff --git a/cscore/build.gradle b/cscore/build.gradle
index 90e5c20..cd03465 100644
--- a/cscore/build.gradle
+++ b/cscore/build.gradle
@@ -60,12 +60,12 @@
 
 ext {
     sharedCvConfigs = [cscore    : [],
-                       cscoreBase: [],
-                       cscoreDev : [],
-                       cscoreTest: [],
-                       cscoreJNIShared: []]
+        cscoreBase: [],
+        cscoreDev : [],
+        cscoreTest: [],
+        cscoreJNIShared: []]
     staticCvConfigs = [cscoreJNI: [],
-                       cscoreJNICvStatic: []]
+        cscoreJNICvStatic: []]
     useJava = true
     useCpp = true
     cvStaticBuild = true
@@ -127,43 +127,57 @@
 
 File examplesTree = file("$projectDir/examples")
 examplesTree.list(new FilenameFilter() {
-    @Override
-    public boolean accept(File current, String name) {
-        return new File(current, name).isDirectory();
-    }
-}).each {
-    sharedCvConfigs.put(it, [])
-    examplesMap.put(it, [])
-}
+            @Override
+            public boolean accept(File current, String name) {
+                return new File(current, name).isDirectory();
+            }
+        }).each {
+            sharedCvConfigs.put(it, [])
+            examplesMap.put(it, [])
+        }
 
 apply from: "${rootDir}/shared/opencv.gradle"
 
+Action<List<String>> symbolFilter = { symbols ->
+    symbols.removeIf({ !it.startsWith('CS_') })
+} as Action<List<String>>;
+
 nativeUtils.exportsConfigs {
     cscore {
-        x86ExcludeSymbols = ['_CT??_R0?AV_System_error', '_CT??_R0?AVexception', '_CT??_R0?AVfailure',
-                            '_CT??_R0?AVruntime_error', '_CT??_R0?AVsystem_error', '_CTA5?AVfailure',
-                            '_TI5?AVfailure', '_CT??_R0?AVout_of_range', '_CTA3?AVout_of_range',
-                            '_TI3?AVout_of_range', '_CT??_R0?AVbad_cast']
-        x64ExcludeSymbols = ['_CT??_R0?AV_System_error', '_CT??_R0?AVexception', '_CT??_R0?AVfailure',
-                            '_CT??_R0?AVruntime_error', '_CT??_R0?AVsystem_error', '_CTA5?AVfailure',
-                            '_TI5?AVfailure', '_CT??_R0?AVout_of_range', '_CTA3?AVout_of_range',
-                            '_TI3?AVout_of_range', '_CT??_R0?AVbad_cast']
+        x86ExcludeSymbols = [
+            '_CT??_R0?AV_System_error',
+            '_CT??_R0?AVexception',
+            '_CT??_R0?AVfailure',
+            '_CT??_R0?AVruntime_error',
+            '_CT??_R0?AVsystem_error',
+            '_CTA5?AVfailure',
+            '_TI5?AVfailure',
+            '_CT??_R0?AVout_of_range',
+            '_CTA3?AVout_of_range',
+            '_TI3?AVout_of_range',
+            '_CT??_R0?AVbad_cast'
+        ]
+        x64ExcludeSymbols = [
+            '_CT??_R0?AV_System_error',
+            '_CT??_R0?AVexception',
+            '_CT??_R0?AVfailure',
+            '_CT??_R0?AVruntime_error',
+            '_CT??_R0?AVsystem_error',
+            '_CTA5?AVfailure',
+            '_TI5?AVfailure',
+            '_CT??_R0?AVout_of_range',
+            '_CTA3?AVout_of_range',
+            '_TI3?AVout_of_range',
+            '_CT??_R0?AVbad_cast'
+        ]
     }
     cscoreJNI {
-        x86SymbolFilter = { symbols ->
-            symbols.removeIf({ !it.startsWith('CS_') })
-        }
-        x64SymbolFilter = { symbols ->
-            symbols.removeIf({ !it.startsWith('CS_') })
-        }
+        x86SymbolFilter = symbolFilter
+        x64SymbolFilter = symbolFilter
     }
     cscoreJNICvStatic {
-        x86SymbolFilter = { symbols ->
-            symbols.removeIf({ !it.startsWith('CS_') })
-        }
-        x64SymbolFilter = { symbols ->
-            symbols.removeIf({ !it.startsWith('CS_') })
-        }
+        x86SymbolFilter = symbolFilter
+        x64SymbolFilter = symbolFilter
     }
 }
 
diff --git a/cscore/cscore-config.cmake.in b/cscore/cscore-config.cmake.in
index da85e8b..33e0af3 100644
--- a/cscore/cscore-config.cmake.in
+++ b/cscore/cscore-config.cmake.in
@@ -3,4 +3,5 @@
 @WPIUTIL_DEP_REPLACE@
 find_dependency(OpenCV)
 
+@FILENAME_DEP_REPLACE@
 include(${SELF_DIR}/cscore.cmake)
diff --git a/cscore/examples/enum_usb/enum_usb.cpp b/cscore/examples/enum_usb/enum_usb.cpp
index 866e2f1..e9c1225 100644
--- a/cscore/examples/enum_usb/enum_usb.cpp
+++ b/cscore/examples/enum_usb/enum_usb.cpp
@@ -1,84 +1,78 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-#include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
+#include <cstdio>
+
+#include <fmt/format.h>
 
 #include "cscore.h"
 
 int main() {
   CS_Status status = 0;
-  wpi::SmallString<64> buf;
 
   for (const auto& caminfo : cs::EnumerateUsbCameras(&status)) {
-    wpi::outs() << caminfo.dev << ": " << caminfo.path << " (" << caminfo.name
-                << ")\n";
+    fmt::print("{}: {} ({})\n", caminfo.dev, caminfo.path, caminfo.name);
     if (!caminfo.otherPaths.empty()) {
-      wpi::outs() << "Other device paths:\n";
-      for (auto&& path : caminfo.otherPaths)
-        wpi::outs() << "  " << path << '\n';
+      std::puts("Other device paths:");
+      for (auto&& path : caminfo.otherPaths) {
+        fmt::print("  {}\n", path);
+      }
     }
 
     cs::UsbCamera camera{"usbcam", caminfo.dev};
 
-    wpi::outs() << "Properties:\n";
+    std::puts("Properties:");
     for (const auto& prop : camera.EnumerateProperties()) {
-      wpi::outs() << "  " << prop.GetName();
+      fmt::print("  {}", prop.GetName());
       switch (prop.GetKind()) {
         case cs::VideoProperty::kBoolean:
-          wpi::outs() << " (bool): "
-                      << "value=" << prop.Get()
-                      << " default=" << prop.GetDefault();
+          fmt::print(" (bool): value={} default={}", prop.Get(),
+                     prop.GetDefault());
           break;
         case cs::VideoProperty::kInteger:
-          wpi::outs() << " (int): "
-                      << "value=" << prop.Get() << " min=" << prop.GetMin()
-                      << " max=" << prop.GetMax() << " step=" << prop.GetStep()
-                      << " default=" << prop.GetDefault();
+          fmt::print(" (int): value={} min={} max={} step={} default={}",
+                     prop.Get(), prop.GetMin(), prop.GetMax(), prop.GetStep(),
+                     prop.GetDefault());
           break;
         case cs::VideoProperty::kString:
-          wpi::outs() << " (string): " << prop.GetString(buf);
+          fmt::print(" (string): {}", prop.GetString());
           break;
         case cs::VideoProperty::kEnum: {
-          wpi::outs() << " (enum): "
-                      << "value=" << prop.Get();
+          fmt::print(" (enum): value={}", prop.Get());
           auto choices = prop.GetChoices();
           for (size_t i = 0; i < choices.size(); ++i) {
-            if (choices[i].empty()) continue;
-            wpi::outs() << "\n    " << i << ": " << choices[i];
+            if (!choices[i].empty()) {
+              fmt::print("\n    {}: {}", i, choices[i]);
+            }
           }
           break;
         }
         default:
           break;
       }
-      wpi::outs() << '\n';
+      std::fputc('\n', stdout);
     }
 
-    wpi::outs() << "Video Modes:\n";
+    std::puts("Video Modes:");
     for (const auto& mode : camera.EnumerateVideoModes()) {
-      wpi::outs() << "  PixelFormat:";
+      const char* pixelFormat;
       switch (mode.pixelFormat) {
         case cs::VideoMode::kMJPEG:
-          wpi::outs() << "MJPEG";
+          pixelFormat = "MJPEG";
           break;
         case cs::VideoMode::kYUYV:
-          wpi::outs() << "YUYV";
+          pixelFormat = "YUYV";
           break;
         case cs::VideoMode::kRGB565:
-          wpi::outs() << "RGB565";
+          pixelFormat = "RGB565";
           break;
         default:
-          wpi::outs() << "Unknown";
+          pixelFormat = "Unknown";
           break;
       }
-      wpi::outs() << " Width:" << mode.width;
-      wpi::outs() << " Height:" << mode.height;
-      wpi::outs() << " FPS:" << mode.fps << '\n';
+      fmt::print("  PixelFormat:{} Width:{} Height:{} FPS:{}\n", pixelFormat,
+                 mode.width, mode.height, mode.fps);
     }
   }
 }
diff --git a/cscore/examples/httpcvstream/httpcvstream.cpp b/cscore/examples/httpcvstream/httpcvstream.cpp
index 90d61d5..38a00db 100644
--- a/cscore/examples/httpcvstream/httpcvstream.cpp
+++ b/cscore/examples/httpcvstream/httpcvstream.cpp
@@ -1,13 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include <cstdio>
-#include <iostream>
 
+#include <fmt/core.h>
 #include <opencv2/core/core.hpp>
 
 #include "cscore.h"
@@ -27,11 +24,11 @@
   for (;;) {
     uint64_t time = cvsink.GrabFrame(test);
     if (time == 0) {
-      std::cout << "error: " << cvsink.GetError() << std::endl;
+      fmt::print("error: {}\n", cvsink.GetError());
       continue;
     }
-    std::cout << "got frame at time " << time << " size " << test.size()
-              << std::endl;
+    fmt::print("got frame at time {} size ({}, {})\n", time, test.size().width,
+               test.size().height);
     cv::flip(test, flip, 0);
     cvsource.PutFrame(flip);
   }
diff --git a/cscore/examples/settings/settings.cpp b/cscore/examples/settings/settings.cpp
index 5aa919f..40961b4 100644
--- a/cscore/examples/settings/settings.cpp
+++ b/cscore/examples/settings/settings.cpp
@@ -1,28 +1,29 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include <chrono>
+#include <cstdio>
 #include <thread>
 
-#include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
+#include <fmt/format.h>
+#include <wpi/StringExtras.h>
 
 #include "cscore.h"
 
 int main(int argc, char** argv) {
   if (argc < 2) {
-    wpi::errs() << "Usage: settings camera [prop val] ... -- [prop val]...\n";
-    wpi::errs() << "  Example: settings 1 brightness 30 raw_contrast 10\n";
+    std::fputs("Usage: settings camera [prop val] ... -- [prop val]...\n",
+               stderr);
+    std::fputs("  Example: settings 1 brightness 30 raw_contrast 10\n", stderr);
     return 1;
   }
 
   int id;
-  if (wpi::StringRef{argv[1]}.getAsInteger(10, id)) {
-    wpi::errs() << "Expected number for camera\n";
+  if (auto v = wpi::parse_integer<int>(argv[1], 10)) {
+    id = v.value();
+  } else {
+    std::fputs("Expected number for camera\n", stderr);
     return 2;
   }
 
@@ -30,75 +31,75 @@
 
   // Set prior to connect
   int arg = 2;
-  wpi::StringRef propName;
-  for (; arg < argc && wpi::StringRef{argv[arg]} != "--"; ++arg) {
+  std::string_view propName;
+  for (; arg < argc && std::string_view{argv[arg]} != "--"; ++arg) {
     if (propName.empty()) {
       propName = argv[arg];
     } else {
-      wpi::StringRef propVal{argv[arg]};
-      int intVal;
-      if (propVal.getAsInteger(10, intVal))
+      std::string_view propVal{argv[arg]};
+      if (auto v = wpi::parse_integer<int>(propVal, 10)) {
+        camera.GetProperty(propName).Set(v.value());
+      } else {
         camera.GetProperty(propName).SetString(propVal);
-      else
-        camera.GetProperty(propName).Set(intVal);
-      propName = wpi::StringRef{};
+      }
+      propName = {};
     }
   }
-  if (arg < argc && wpi::StringRef{argv[arg]} == "--") ++arg;
+  if (arg < argc && std::string_view{argv[arg]} == "--") {
+    ++arg;
+  }
 
   // Wait to connect
-  while (!camera.IsConnected())
+  while (!camera.IsConnected()) {
     std::this_thread::sleep_for(std::chrono::milliseconds(10));
+  }
 
   // Set rest
-  propName = wpi::StringRef{};
+  propName = {};
   for (; arg < argc; ++arg) {
     if (propName.empty()) {
       propName = argv[arg];
     } else {
-      wpi::StringRef propVal{argv[arg]};
-      int intVal;
-      if (propVal.getAsInteger(10, intVal))
+      std::string_view propVal{argv[arg]};
+      if (auto v = wpi::parse_integer<int>(propVal, 10)) {
+        camera.GetProperty(propName).Set(v.value());
+      } else {
         camera.GetProperty(propName).SetString(propVal);
-      else
-        camera.GetProperty(propName).Set(intVal);
-      propName = wpi::StringRef{};
+      }
+      propName = {};
     }
   }
 
   // Print settings
-  wpi::SmallString<64> buf;
-  wpi::outs() << "Properties:\n";
+  std::puts("Properties:");
   for (const auto& prop : camera.EnumerateProperties()) {
-    wpi::outs() << "  " << prop.GetName();
+    fmt::print("  {}", prop.GetName());
     switch (prop.GetKind()) {
       case cs::VideoProperty::kBoolean:
-        wpi::outs() << " (bool): "
-                    << "value=" << prop.Get()
-                    << " default=" << prop.GetDefault();
+        fmt::print(" (bool): value={} default={}", prop.Get(),
+                   prop.GetDefault());
         break;
       case cs::VideoProperty::kInteger:
-        wpi::outs() << " (int): "
-                    << "value=" << prop.Get() << " min=" << prop.GetMin()
-                    << " max=" << prop.GetMax() << " step=" << prop.GetStep()
-                    << " default=" << prop.GetDefault();
+        fmt::print(" (int): value={} min={} max={} step={} default={}",
+                   prop.Get(), prop.GetMin(), prop.GetMax(), prop.GetStep(),
+                   prop.GetDefault());
         break;
       case cs::VideoProperty::kString:
-        wpi::outs() << " (string): " << prop.GetString(buf);
+        fmt::print(" (string): {}", prop.GetString());
         break;
       case cs::VideoProperty::kEnum: {
-        wpi::outs() << " (enum): "
-                    << "value=" << prop.Get();
+        fmt::print(" (enum): value={}", prop.Get());
         auto choices = prop.GetChoices();
         for (size_t i = 0; i < choices.size(); ++i) {
-          if (choices[i].empty()) continue;
-          wpi::outs() << "\n    " << i << ": " << choices[i];
+          if (!choices[i].empty()) {
+            fmt::print("\n    {}: {}", i, choices[i]);
+          }
         }
         break;
       }
       default:
         break;
     }
-    wpi::outs() << '\n';
+    std::fputc('\n', stdout);
   }
 }
diff --git a/cscore/examples/usbcvstream/usbcvstream.cpp b/cscore/examples/usbcvstream/usbcvstream.cpp
index 9a4ab06..3635221 100644
--- a/cscore/examples/usbcvstream/usbcvstream.cpp
+++ b/cscore/examples/usbcvstream/usbcvstream.cpp
@@ -1,13 +1,8 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-#include <cstdio>
-#include <iostream>
-
+#include <fmt/core.h>
 #include <opencv2/core/core.hpp>
 
 #include "cscore.h"
@@ -29,11 +24,11 @@
   for (;;) {
     uint64_t time = cvsink.GrabFrame(test);
     if (time == 0) {
-      std::cout << "error: " << cvsink.GetError() << std::endl;
+      fmt::print("error: {}\n", cvsink.GetError());
       continue;
     }
-    std::cout << "got frame at time " << time << " size " << test.size()
-              << std::endl;
+    fmt::print("got frame at time {} size ({}, {})\n", time, test.size().width,
+               test.size().height);
     cv::flip(test, flip, 0);
     cvsource.PutFrame(flip);
   }
diff --git a/cscore/examples/usbstream/usbstream.cpp b/cscore/examples/usbstream/usbstream.cpp
index 2f23151..7a57e8d 100644
--- a/cscore/examples/usbstream/usbstream.cpp
+++ b/cscore/examples/usbstream/usbstream.cpp
@@ -1,21 +1,19 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include <cstdio>
 
-#include <wpi/raw_ostream.h>
+#include <fmt/format.h>
 
 #include "cscore.h"
 
 int main() {
-  wpi::outs() << "hostname: " << cs::GetHostname() << '\n';
-  wpi::outs() << "IPv4 network addresses:\n";
-  for (const auto& addr : cs::GetNetworkInterfaces())
-    wpi::outs() << "  " << addr << '\n';
+  fmt::print("hostname: {}\n", cs::GetHostname());
+  std::puts("IPv4 network addresses:");
+  for (const auto& addr : cs::GetNetworkInterfaces()) {
+    fmt::print("  {}\n", addr);
+  }
   cs::UsbCamera camera{"usbcam", 0};
   camera.SetVideoMode(cs::VideoMode::kMJPEG, 320, 240, 30);
   cs::MjpegServer mjpegServer{"httpserver", 8081};
@@ -24,9 +22,8 @@
   CS_Status status = 0;
   cs::AddListener(
       [&](const cs::RawEvent& event) {
-        wpi::outs() << "FPS=" << camera.GetActualFPS()
-                    << " MBPS=" << (camera.GetActualDataRate() / 1000000.0)
-                    << '\n';
+        fmt::print("FPS={} MBPS={}\n", camera.GetActualFPS(),
+                   (camera.GetActualDataRate() / 1000000.0));
       },
       cs::RawEvent::kTelemetryUpdated, false, &status);
   cs::SetTelemetryPeriod(1.0);
diff --git a/cscore/examples/usbviewer/usbviewer.cpp b/cscore/examples/usbviewer/usbviewer.cpp
index 75f961a..5c2e7c9 100644
--- a/cscore/examples/usbviewer/usbviewer.cpp
+++ b/cscore/examples/usbviewer/usbviewer.cpp
@@ -1,20 +1,18 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include <atomic>
 #include <thread>
 #include <vector>
 
+#include <fmt/format.h>
+
 #define IMGUI_DEFINE_MATH_OPERATORS
 #include <imgui.h>
 #include <imgui_internal.h>
 #include <opencv2/core/core.hpp>
 #include <opencv2/imgproc.hpp>
-#include <wpi/raw_ostream.h>
 #include <wpi/spinlock.h>
 #include <wpigui.h>
 
@@ -41,7 +39,7 @@
       // get frame from camera
       uint64_t time = cvsink.GrabFrame(frame);
       if (time == 0) {
-        wpi::outs() << "error: " << cvsink.GetError() << '\n';
+        fmt::print("error: {}\n", cvsink.GetError());
         continue;
       }
 
@@ -53,7 +51,9 @@
       } else {
         {
           std::scoped_lock lock(sharedFreeListMutex);
-          for (auto mat : sharedFreeList) sourceFreeList.emplace_back(mat);
+          for (auto mat : sharedFreeList) {
+            sourceFreeList.emplace_back(mat);
+          }
           sharedFreeList.clear();
         }
         if (!sourceFreeList.empty()) {
@@ -71,7 +71,9 @@
       auto prev = latestFrame.exchange(out);
 
       // put prev on free list
-      if (prev) sourceFreeList.emplace_back(prev);
+      if (prev) {
+        sourceFreeList.emplace_back(prev);
+      }
     }
   });
 
diff --git a/cscore/java-examples/RawCVMatSink.java b/cscore/java-examples/RawCVMatSink.java
index d9557f7..c0acc28 100644
--- a/cscore/java-examples/RawCVMatSink.java
+++ b/cscore/java-examples/RawCVMatSink.java
@@ -1,19 +1,14 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 package edu.wpi.cscore;
 
-import java.nio.ByteBuffer;
-
-import org.opencv.core.CvType;
-import org.opencv.core.Mat;
-
 import edu.wpi.cscore.VideoMode.PixelFormat;
 import edu.wpi.cscore.raw.RawFrame;
+import java.nio.ByteBuffer;
+import org.opencv.core.CvType;
+import org.opencv.core.Mat;
 
 public class RawCVMatSink extends ImageSink {
   RawFrame frame = new RawFrame();
@@ -27,26 +22,25 @@
   private int getCVFormat(PixelFormat pixelFormat) {
     int type = 0;
     switch (pixelFormat) {
-    case kYUYV:
-    case kRGB565:
-      type = CvType.CV_8UC2;
-      break;
-    case kBGR:
-      type = CvType.CV_8UC3;
-      break;
-    case kGray:
-    case kMJPEG:
-    default:
-      type = CvType.CV_8UC1;
-      break;
+      case kYUYV:
+      case kRGB565:
+        type = CvType.CV_8UC2;
+        break;
+      case kBGR:
+        type = CvType.CV_8UC3;
+        break;
+      case kGray:
+      case kMJPEG:
+      default:
+        type = CvType.CV_8UC1;
+        break;
     }
     return type;
   }
 
   /**
-   * Create a sink for accepting OpenCV images.
-   * WaitForFrame() must be called on the created sink to get each new
-   * image.
+   * Create a sink for accepting OpenCV images. WaitForFrame() must be called on the created sink to
+   * get each new image.
    *
    * @param name Source name (arbitrary unique identifier)
    */
@@ -55,24 +49,21 @@
   }
 
   /**
-   * Wait for the next frame and get the image.
-   * Times out (returning 0) after 0.225 seconds.
-   * The provided image will have three 3-bit channels stored in BGR order.
+   * Wait for the next frame and get the image. Times out (returning 0) after 0.225 seconds. The
+   * provided image will have three 3-bit channels stored in BGR order.
    *
-   * @return Frame time, or 0 on error (call GetError() to obtain the error
-   *         message)
+   * @return Frame time, or 0 on error (call GetError() to obtain the error message)
    */
   public long grabFrame(Mat image) {
     return grabFrame(image, 0.225);
   }
 
   /**
-   * Wait for the next frame and get the image.
-   * Times out (returning 0) after timeout seconds.
-   * The provided image will have three 3-bit channels stored in BGR order.
+   * Wait for the next frame and get the image. Times out (returning 0) after timeout seconds. The
+   * provided image will have three 3-bit channels stored in BGR order.
    *
-   * @return Frame time, or 0 on error (call GetError() to obtain the error
-   *         message); the frame time is in 1 us increments.
+   * @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
+   *     is in 1 us increments.
    */
   public long grabFrame(Mat image, double timeout) {
     frame.setWidth(0);
@@ -83,12 +74,20 @@
       return rv;
     }
 
-    if (frame.getDataByteBuffer() != origByteBuffer || width != frame.getWidth() || height != frame.getHeight() || pixelFormat != frame.getPixelFormat()) {
+    if (frame.getDataByteBuffer() != origByteBuffer
+        || width != frame.getWidth()
+        || height != frame.getHeight()
+        || pixelFormat != frame.getPixelFormat()) {
       origByteBuffer = frame.getDataByteBuffer();
       height = frame.getHeight();
       width = frame.getWidth();
       pixelFormat = frame.getPixelFormat();
-      tmpMat = new Mat(frame.getHeight(), frame.getWidth(), getCVFormat(VideoMode.getPixelFormatFromInt(pixelFormat)), origByteBuffer);
+      tmpMat =
+          new Mat(
+              frame.getHeight(),
+              frame.getWidth(),
+              getCVFormat(VideoMode.getPixelFormatFromInt(pixelFormat)),
+              origByteBuffer);
     }
     tmpMat.copyTo(image);
     return rv;
diff --git a/cscore/java-examples/RawCVMatSource.java b/cscore/java-examples/RawCVMatSource.java
index 65bd7d2..e000ae3 100644
--- a/cscore/java-examples/RawCVMatSource.java
+++ b/cscore/java-examples/RawCVMatSource.java
@@ -1,15 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 package edu.wpi.cscore;
 
-import org.opencv.core.Mat;
-
 import edu.wpi.cscore.VideoMode.PixelFormat;
+import org.opencv.core.Mat;
 
 public class RawCVMatSource extends ImageSource {
   /**
@@ -19,11 +15,9 @@
    * @param mode Video mode being generated
    */
   public RawCVMatSource(String name, VideoMode mode) {
-    super(CameraServerJNI.createRawSource(name,
-        mode.pixelFormat.getValue(),
-        mode.width,
-        mode.height,
-        mode.fps));
+    super(
+        CameraServerJNI.createRawSource(
+            name, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps));
   }
 
   /**
@@ -35,16 +29,17 @@
    * @param height height
    * @param fps fps
    */
-  public RawCVMatSource(String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
+  public RawCVMatSource(
+      String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
     super(CameraServerJNI.createRawSource(name, pixelFormat.getValue(), width, height, fps));
   }
 
   /**
    * Put an OpenCV image and notify sinks.
    *
-   * <p>Only 8-bit single-channel or 3-channel (with BGR channel order) images
-   * are supported. If the format, depth or channel order is different, use
-   * Mat.convertTo() and/or cvtColor() to convert it first.
+   * <p>Only 8-bit single-channel or 3-channel (with BGR channel order) images are supported. If the
+   * format, depth or channel order is different, use Mat.convertTo() and/or cvtColor() to convert
+   * it first.
    *
    * @param image OpenCV image
    */
@@ -54,6 +49,12 @@
       throw new VideoException("Unsupported Image Type");
     }
     int imgType = channels == 1 ? PixelFormat.kGray.getValue() : PixelFormat.kBGR.getValue();
-    CameraServerJNI.putRawSourceFrame(m_handle, image.dataAddr(), image.width(), image.height(), imgType, (int)image.total() * channels);
+    CameraServerJNI.putRawSourceFrame(
+        m_handle,
+        image.dataAddr(),
+        image.width(),
+        image.height(),
+        imgType,
+        (int) image.total() * channels);
   }
 }
diff --git a/cscore/src/dev/java/edu/wpi/cscore/DevMain.java b/cscore/src/dev/java/edu/wpi/cscore/DevMain.java
deleted file mode 100644
index 51bfd26..0000000
--- a/cscore/src/dev/java/edu/wpi/cscore/DevMain.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-import edu.wpi.first.wpiutil.RuntimeDetector;
-
-public final class DevMain {
-  /**
-   * Main method.
-   */
-  public static void main(String[] args) {
-    System.out.println("Hello World!");
-    System.out.println(RuntimeDetector.getPlatformPath());
-    System.out.println(CameraServerJNI.getHostname());
-  }
-
-  private DevMain() {
-  }
-}
diff --git a/cscore/src/dev/java/edu/wpi/first/cscore/DevMain.java b/cscore/src/dev/java/edu/wpi/first/cscore/DevMain.java
new file mode 100644
index 0000000..11652f6
--- /dev/null
+++ b/cscore/src/dev/java/edu/wpi/first/cscore/DevMain.java
@@ -0,0 +1,18 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+import edu.wpi.first.util.RuntimeDetector;
+
+public final class DevMain {
+  /** Main method. */
+  public static void main(String[] args) {
+    System.out.println("Hello World!");
+    System.out.println(RuntimeDetector.getPlatformPath());
+    System.out.println(CameraServerJNI.getHostname());
+  }
+
+  private DevMain() {}
+}
diff --git a/cscore/src/dev/native/cpp/main.cpp b/cscore/src/dev/native/cpp/main.cpp
index f27f61f..279d72a 100644
--- a/cscore/src/dev/native/cpp/main.cpp
+++ b/cscore/src/dev/native/cpp/main.cpp
@@ -1,12 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-#include <iostream>
+#include <fmt/core.h>
 
 #include "cscore.h"
 
-int main() { std::cout << cs::GetHostname() << std::endl; }
+int main() {
+  fmt::print("{}\n", cs::GetHostname());
+}
diff --git a/cscore/src/main/java/edu/wpi/cscore/CvSink.java b/cscore/src/main/java/edu/wpi/cscore/CvSink.java
deleted file mode 100644
index f12dcc7..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/CvSink.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-import org.opencv.core.Mat;
-
-/**
- * A sink for user code to accept video frames as OpenCV images.
- * These sinks require the WPILib OpenCV builds.
- * For an alternate OpenCV, see the documentation how to build your own
- * with RawSink.
- */
-public class CvSink extends ImageSink {
-  /**
-   * Create a sink for accepting OpenCV images.
-   * WaitForFrame() must be called on the created sink to get each new
-   * image.
-   *
-   * @param name Source name (arbitrary unique identifier)
-   */
-  public CvSink(String name) {
-    super(CameraServerCvJNI.createCvSink(name));
-  }
-
-  /// Create a sink for accepting OpenCV images in a separate thread.
-  /// A thread will be created that calls WaitForFrame() and calls the
-  /// processFrame() callback each time a new frame arrives.
-  /// @param name Source name (arbitrary unique identifier)
-  /// @param processFrame Frame processing function; will be called with a
-  ///        time=0 if an error occurred.  processFrame should call GetImage()
-  ///        or GetError() as needed, but should not call (except in very
-  ///        unusual circumstances) WaitForImage().
-  //public CvSink(wpi::StringRef name,
-  //       std::function<void(uint64_t time)> processFrame) {
-  //  super(CameraServerJNI.createCvSinkCallback(name, processFrame));
-  //}
-
-  /**
-   * Wait for the next frame and get the image.
-   * Times out (returning 0) after 0.225 seconds.
-   * The provided image will have three 3-bit channels stored in BGR order.
-   *
-   * @return Frame time, or 0 on error (call GetError() to obtain the error
-   *         message)
-   */
-  public long grabFrame(Mat image) {
-    return grabFrame(image, 0.225);
-  }
-
-  /**
-   * Wait for the next frame and get the image.
-   * Times out (returning 0) after timeout seconds.
-   * The provided image will have three 3-bit channels stored in BGR order.
-   *
-   * @return Frame time, or 0 on error (call GetError() to obtain the error
-   *         message); the frame time is in 1 us increments.
-   */
-  public long grabFrame(Mat image, double timeout) {
-    return CameraServerCvJNI.grabSinkFrameTimeout(m_handle, image.nativeObj, timeout);
-  }
-
-  /**
-   * Wait for the next frame and get the image.  May block forever.
-   * The provided image will have three 3-bit channels stored in BGR order.
-   *
-   * @return Frame time, or 0 on error (call GetError() to obtain the error
-   *         message); the frame time is in 1 us increments.
-   */
-  public long grabFrameNoTimeout(Mat image) {
-    return CameraServerCvJNI.grabSinkFrame(m_handle, image.nativeObj);
-  }
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/CvSource.java b/cscore/src/main/java/edu/wpi/cscore/CvSource.java
deleted file mode 100644
index c04d197..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/CvSource.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-import org.opencv.core.Mat;
-
-/**
- * A source that represents a video camera.
- * These sources require the WPILib OpenCV builds.
- * For an alternate OpenCV, see the documentation how to build your own
- * with RawSource.
- */
-public class CvSource extends ImageSource {
-  /**
-   * Create an OpenCV source.
-   *
-   * @param name Source name (arbitrary unique identifier)
-   * @param mode Video mode being generated
-   */
-  public CvSource(String name, VideoMode mode) {
-    super(CameraServerCvJNI.createCvSource(name,
-        mode.pixelFormat.getValue(),
-        mode.width,
-        mode.height,
-        mode.fps));
-  }
-
-  /**
-   * Create an OpenCV source.
-   *
-   * @param name Source name (arbitrary unique identifier)
-   * @param pixelFormat Pixel format
-   * @param width width
-   * @param height height
-   * @param fps fps
-   */
-  public CvSource(String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
-    super(CameraServerCvJNI.createCvSource(name, pixelFormat.getValue(), width, height, fps));
-  }
-
-  /**
-   * Put an OpenCV image and notify sinks.
-   *
-   * <p>Only 8-bit single-channel or 3-channel (with BGR channel order) images
-   * are supported. If the format, depth or channel order is different, use
-   * Mat.convertTo() and/or cvtColor() to convert it first.
-   *
-   * @param image OpenCV image
-   */
-  public void putFrame(Mat image) {
-    CameraServerCvJNI.putSourceFrame(m_handle, image.nativeObj);
-  }
-
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/ImageSink.java b/cscore/src/main/java/edu/wpi/cscore/ImageSink.java
deleted file mode 100644
index f755fb6..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/ImageSink.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-public abstract class ImageSink extends VideoSink {
-  protected ImageSink(int handle) {
-    super(handle);
-  }
-
-  /**
-   * Set sink description.
-   *
-   * @param description Description
-   */
-  public void setDescription(String description) {
-    CameraServerJNI.setSinkDescription(m_handle, description);
-  }
-
-  /**
-   * Get error string.  Call this if WaitForFrame() returns 0 to determine
-   * what the error is.
-   */
-  public String getError() {
-    return CameraServerJNI.getSinkError(m_handle);
-  }
-
-  /**
-   * Enable or disable getting new frames.
-   * Disabling will cause processFrame (for callback-based CvSinks) to not
-   * be called and WaitForFrame() to not return.  This can be used to save
-   * processor resources when frames are not needed.
-   */
-  public void setEnabled(boolean enabled) {
-    CameraServerJNI.setSinkEnabled(m_handle, enabled);
-  }
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/ImageSource.java b/cscore/src/main/java/edu/wpi/cscore/ImageSource.java
deleted file mode 100644
index 3787516..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/ImageSource.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-public abstract class ImageSource extends VideoSource {
-  protected ImageSource(int handle) {
-    super(handle);
-  }
-
-  /**
-   * Signal sinks that an error has occurred.  This should be called instead
-   * of NotifyFrame when an error occurs.
-   */
-  public void notifyError(String msg) {
-    CameraServerJNI.notifySourceError(m_handle, msg);
-  }
-
-  /**
-   * Set source connection status.  Defaults to true.
-   *
-   * @param connected True for connected, false for disconnected
-   */
-  public void setConnected(boolean connected) {
-    CameraServerJNI.setSourceConnected(m_handle, connected);
-  }
-
-  /**
-   * Set source description.
-   *
-   * @param description Description
-   */
-  public void setDescription(String description) {
-    CameraServerJNI.setSourceDescription(m_handle, description);
-  }
-
-  /**
-   * Create a property.
-   *
-   * @param name Property name
-   * @param kind Property kind
-   * @param minimum Minimum value
-   * @param maximum Maximum value
-   * @param step Step value
-   * @param defaultValue Default value
-   * @param value Current value
-   * @return Property
-   */
-  public VideoProperty createProperty(String name,
-                                      VideoProperty.Kind kind,
-                                      int minimum,
-                                      int maximum,
-                                      int step,
-                                      int defaultValue,
-                                      int value) {
-    return new VideoProperty(
-        CameraServerJNI.createSourceProperty(m_handle,
-            name,
-            kind.getValue(),
-            minimum,
-            maximum,
-            step,
-            defaultValue,
-            value));
-  }
-
-  /**
-   * Create an integer property.
-   *
-   * @param name Property name
-   * @param minimum Minimum value
-   * @param maximum Maximum value
-   * @param step Step value
-   * @param defaultValue Default value
-   * @param value Current value
-   * @return Property
-   */
-  public VideoProperty createIntegerProperty(String name,
-                                             int minimum,
-                                             int maximum,
-                                             int step,
-                                             int defaultValue,
-                                             int value) {
-    return new VideoProperty(
-        CameraServerJNI.createSourceProperty(m_handle,
-            name,
-            VideoProperty.Kind.kInteger.getValue(),
-            minimum,
-            maximum,
-            step,
-            defaultValue,
-            value));
-  }
-
-  /**
-   * Create a boolean property.
-   *
-   * @param name Property name
-   * @param defaultValue Default value
-   * @param value Current value
-   * @return Property
-   */
-  public VideoProperty createBooleanProperty(String name, boolean defaultValue, boolean value) {
-    return new VideoProperty(
-        CameraServerJNI.createSourceProperty(m_handle,
-            name,
-            VideoProperty.Kind.kBoolean.getValue(),
-            0,
-            1,
-            1,
-            defaultValue ? 1 : 0,
-            value ? 1 : 0));
-  }
-
-  /**
-   * Create a string property.
-   *
-   * @param name Property name
-   * @param value Current value
-   * @return Property
-   */
-  public VideoProperty createStringProperty(String name, String value) {
-    VideoProperty prop = new VideoProperty(
-        CameraServerJNI.createSourceProperty(m_handle,
-            name,
-            VideoProperty.Kind.kString.getValue(),
-            0,
-            0,
-            0,
-            0,
-            0));
-    prop.setString(value);
-    return prop;
-  }
-
-  /**
-   * Configure enum property choices.
-   *
-   * @param property Property
-   * @param choices Choices
-   */
-  public void setEnumPropertyChoices(VideoProperty property, String[] choices) {
-    CameraServerJNI.setSourceEnumPropertyChoices(m_handle, property.m_handle, choices);
-  }
-
-  /**
-   * Configure enum property choices.
-   *
-   * @param property Property
-   * @param choices Choices
-   * @deprecated Use {@code setEnumPropertyChoices} instead.
-   */
-  @Deprecated
-  @SuppressWarnings("MethodName")
-  public void SetEnumPropertyChoices(VideoProperty property, String[] choices) {
-    setEnumPropertyChoices(property, choices);
-  }
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/UsbCameraInfo.java b/cscore/src/main/java/edu/wpi/cscore/UsbCameraInfo.java
deleted file mode 100644
index c3a8309..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/UsbCameraInfo.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-/**
- * USB camera information.
- */
-public class UsbCameraInfo {
-  /**
-   * Create a new set of UsbCameraInfo.
-   *
-   * @param dev Device number (e.g. N in '/dev/videoN' on Linux)
-   * @param path Path to device if available (e.g. '/dev/video0' on Linux)
-   * @param name Vendor/model name of the camera as provided by the USB driver
-   * @param otherPaths Other path aliases to device
-   * @param vendorId USB vendor id
-   * @param productId USB product id
-   */
-  @SuppressWarnings("PMD.ArrayIsStoredDirectly")
-  public UsbCameraInfo(int dev, String path, String name, String[] otherPaths, int vendorId,
-      int productId) {
-    this.dev = dev;
-    this.path = path;
-    this.name = name;
-    this.otherPaths = otherPaths;
-    this.vendorId = vendorId;
-    this.productId = productId;
-  }
-
-  /**
-   * Device number (e.g. N in '/dev/videoN' on Linux).
-   */
-  @SuppressWarnings("MemberName")
-  public int dev;
-
-  /**
-   * Path to device if available (e.g. '/dev/video0' on Linux).
-   */
-  @SuppressWarnings("MemberName")
-  public String path;
-
-  /**
-   * Vendor/model name of the camera as provided by the USB driver.
-   */
-  @SuppressWarnings("MemberName")
-  public String name;
-
-  /**
-   * Other path aliases to device (e.g. '/dev/v4l/by-id/...' etc on Linux).
-   */
-  @SuppressWarnings("MemberName")
-  public String[] otherPaths;
-
-  /**
-   * USB vendor id.
-   */
-  @SuppressWarnings("MemberName")
-  public int vendorId;
-
-  /**
-   * USB product id.
-   */
-  @SuppressWarnings("MemberName")
-  public int productId;
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoEvent.java b/cscore/src/main/java/edu/wpi/cscore/VideoEvent.java
deleted file mode 100644
index 7f4599d..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/VideoEvent.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-/**
- * Video event.
- */
-public class VideoEvent {
-  public enum Kind {
-    kUnknown(0x0000),
-    kSourceCreated(0x0001),
-    kSourceDestroyed(0x0002),
-    kSourceConnected(0x0004),
-    kSourceDisconnected(0x0008),
-    kSourceVideoModesUpdated(0x0010),
-    kSourceVideoModeChanged(0x0020),
-    kSourcePropertyCreated(0x0040),
-    kSourcePropertyValueUpdated(0x0080),
-    kSourcePropertyChoicesUpdated(0x0100),
-    kSinkSourceChanged(0x0200),
-    kSinkCreated(0x0400),
-    kSinkDestroyed(0x0800),
-    kSinkEnabled(0x1000),
-    kSinkDisabled(0x2000),
-    kNetworkInterfacesChanged(0x4000),
-    kTelemetryUpdated(0x8000),
-    kSinkPropertyCreated(0x10000),
-    kSinkPropertyValueUpdated(0x20000),
-    kSinkPropertyChoicesUpdated(0x40000);
-
-    @SuppressWarnings("MemberName")
-    private final int value;
-
-    Kind(int value) {
-      this.value = value;
-    }
-
-    public int getValue() {
-      return value;
-    }
-  }
-
-  /**
-   * Convert from the numerical representation of kind to an enum type.
-   *
-   * @param kind The numerical representation of kind
-   * @return The kind
-   */
-  @SuppressWarnings("PMD.CyclomaticComplexity")
-  public static Kind getKindFromInt(int kind) {
-    switch (kind) {
-      case 0x0001: return Kind.kSourceCreated;
-      case 0x0002: return Kind.kSourceDestroyed;
-      case 0x0004: return Kind.kSourceConnected;
-      case 0x0008: return Kind.kSourceDisconnected;
-      case 0x0010: return Kind.kSourceVideoModesUpdated;
-      case 0x0020: return Kind.kSourceVideoModeChanged;
-      case 0x0040: return Kind.kSourcePropertyCreated;
-      case 0x0080: return Kind.kSourcePropertyValueUpdated;
-      case 0x0100: return Kind.kSourcePropertyChoicesUpdated;
-      case 0x0200: return Kind.kSinkSourceChanged;
-      case 0x0400: return Kind.kSinkCreated;
-      case 0x0800: return Kind.kSinkDestroyed;
-      case 0x1000: return Kind.kSinkEnabled;
-      case 0x2000: return Kind.kSinkDisabled;
-      case 0x4000: return Kind.kNetworkInterfacesChanged;
-      case 0x10000: return Kind.kSinkPropertyCreated;
-      case 0x20000: return Kind.kSinkPropertyValueUpdated;
-      case 0x40000: return Kind.kSinkPropertyChoicesUpdated;
-      default: return Kind.kUnknown;
-    }
-  }
-
-  @SuppressWarnings("PMD.ExcessiveParameterList")
-  VideoEvent(int kind, int source, int sink, String name, int pixelFormat,
-             int width, int height, int fps, int property, int propertyKind,
-             int value, String valueStr) {
-    this.kind = getKindFromInt(kind);
-    this.sourceHandle = source;
-    this.sinkHandle = sink;
-    this.name = name;
-    this.mode = new VideoMode(pixelFormat, width, height, fps);
-    this.propertyHandle = property;
-    this.propertyKind = VideoProperty.getKindFromInt(propertyKind);
-    this.value = value;
-    this.valueStr = valueStr;
-  }
-
-  @SuppressWarnings("MemberName")
-  public Kind kind;
-
-  // Valid for kSource* and kSink* respectively
-  @SuppressWarnings("MemberName")
-  public int sourceHandle;
-  @SuppressWarnings("MemberName")
-  public int sinkHandle;
-
-  // Source/sink/property name
-  @SuppressWarnings("MemberName")
-  public String name;
-
-  // Fields for kSourceVideoModeChanged event
-  @SuppressWarnings("MemberName")
-  public VideoMode mode;
-
-  // Fields for kSourceProperty* events
-  @SuppressWarnings("MemberName")
-  public int propertyHandle;
-  @SuppressWarnings("MemberName")
-  public VideoProperty.Kind propertyKind;
-  @SuppressWarnings("MemberName")
-  public int value;
-  @SuppressWarnings("MemberName")
-  public String valueStr;
-
-  public VideoSource getSource() {
-    return new VideoSource(CameraServerJNI.copySource(sourceHandle));
-  }
-
-  public VideoSink getSink() {
-    return new VideoSink(CameraServerJNI.copySink(sinkHandle));
-  }
-
-  public VideoProperty getProperty() {
-    return new VideoProperty(propertyHandle, propertyKind);
-  }
-
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoException.java b/cscore/src/main/java/edu/wpi/cscore/VideoException.java
deleted file mode 100644
index 8c35517..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/VideoException.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-/**
- * An exception raised by the camera server.
- */
-public class VideoException extends RuntimeException {
-  private static final long serialVersionUID = -9155939328084105145L;
-
-  public VideoException(String msg) {
-    super(msg);
-  }
-
-  @Override
-  public String toString() {
-    return "VideoException [" + super.toString() + "]";
-  }
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoListener.java b/cscore/src/main/java/edu/wpi/cscore/VideoListener.java
deleted file mode 100644
index 2a35505..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/VideoListener.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-import java.util.function.Consumer;
-
-/**
- * An event listener.  This calls back to a desigated callback function when
- * an event matching the specified mask is generated by the library.
- */
-public class VideoListener implements AutoCloseable {
-  /**
-   * Create an event listener.
-   *
-   * @param listener Listener function
-   * @param eventMask Bitmask of VideoEvent.Type values
-   * @param immediateNotify Whether callback should be immediately called with
-   *        a representative set of events for the current library state.
-   */
-  public VideoListener(Consumer<VideoEvent> listener, int eventMask,
-                       boolean immediateNotify) {
-    m_handle = CameraServerJNI.addListener(listener, eventMask, immediateNotify);
-  }
-
-  @Override
-  public synchronized void close() {
-    if (m_handle != 0) {
-      CameraServerJNI.removeListener(m_handle);
-    }
-    m_handle = 0;
-  }
-
-  public boolean isValid() {
-    return m_handle != 0;
-  }
-
-  private int m_handle;
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoMode.java b/cscore/src/main/java/edu/wpi/cscore/VideoMode.java
deleted file mode 100644
index dec4686..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/VideoMode.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-/**
- * Video mode.
- */
-public class VideoMode {
-  public enum PixelFormat {
-    kUnknown(0), kMJPEG(1), kYUYV(2), kRGB565(3), kBGR(4), kGray(5);
-
-    @SuppressWarnings("MemberName")
-    private final int value;
-
-    PixelFormat(int value) {
-      this.value = value;
-    }
-
-    public int getValue() {
-      return value;
-    }
-  }
-
-  private static final PixelFormat[] m_pixelFormatValues = PixelFormat.values();
-
-  public static PixelFormat getPixelFormatFromInt(int pixelFormat) {
-    return m_pixelFormatValues[pixelFormat];
-  }
-
-  /**
-   * Create a new video mode.
-   */
-  public VideoMode(int pixelFormat, int width, int height, int fps) {
-    this.pixelFormat = getPixelFormatFromInt(pixelFormat);
-    this.width = width;
-    this.height = height;
-    this.fps = fps;
-  }
-
-  /**
-   * Create a new video mode.
-   */
-  public VideoMode(PixelFormat pixelFormat, int width, int height, int fps) {
-    this.pixelFormat = pixelFormat;
-    this.width = width;
-    this.height = height;
-    this.fps = fps;
-  }
-
-  /**
-   * Pixel format.
-   */
-  @SuppressWarnings("MemberName")
-  public PixelFormat pixelFormat;
-
-  /**
-   * Width in pixels.
-   */
-  @SuppressWarnings("MemberName")
-  public int width;
-
-  /**
-   * Height in pixels.
-   */
-  @SuppressWarnings("MemberName")
-  public int height;
-
-  /**
-   * Frames per second.
-   */
-  @SuppressWarnings("MemberName")
-  public int fps;
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/raw/RawFrame.java b/cscore/src/main/java/edu/wpi/cscore/raw/RawFrame.java
deleted file mode 100644
index 0e7a9ce..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/raw/RawFrame.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore.raw;
-
-import java.nio.ByteBuffer;
-
-import edu.wpi.cscore.CameraServerJNI;
-
-/**
- * Class for storing raw frame data between image read call.
- *
- * <p>Data is reused for each frame read, rather then reallocating every frame.
- */
-public class RawFrame implements AutoCloseable {
-  private final long m_framePtr;
-  private ByteBuffer m_dataByteBuffer;
-  private long m_dataPtr;
-  private int m_totalData;
-  private int m_width;
-  private int m_height;
-  private int m_pixelFormat;
-
-  /**
-   * Construct a new RawFrame.
-   */
-  public RawFrame() {
-    m_framePtr = CameraServerJNI.allocateRawFrame();
-  }
-
-  /**
-   * Close the RawFrame, releasing native resources.
-   * Any images currently using the data will be invalidated.
-   */
-  @Override
-  public void close() {
-    CameraServerJNI.freeRawFrame(m_framePtr);
-  }
-
-  /**
-   * Called from JNI to set data in class.
-   */
-  public void setData(ByteBuffer dataByteBuffer, long dataPtr, int totalData,
-                      int width, int height, int pixelFormat) {
-    m_dataByteBuffer = dataByteBuffer;
-    m_dataPtr = dataPtr;
-    m_totalData = totalData;
-    m_width = width;
-    m_height = height;
-    m_pixelFormat = pixelFormat;
-  }
-
-  /**
-   * Get the pointer to native representation of this frame.
-   */
-  public long getFramePtr() {
-    return m_framePtr;
-  }
-
-  /**
-   * Get a ByteBuffer pointing to the frame data.
-   * This ByteBuffer is backed by the frame directly. Its lifetime is controlled by
-   * the frame. If a new frame gets read, it will overwrite the current one.
-   */
-  public ByteBuffer getDataByteBuffer() {
-    return m_dataByteBuffer;
-  }
-
-  /**
-   * Get a long (is a char* in native code) pointing to the frame data.
-   * This pointer is backed by the frame directly. Its lifetime is controlled by
-   * the frame. If a new frame gets read, it will overwrite the current one.
-   */
-  public long getDataPtr() {
-    return m_dataPtr;
-  }
-
-  /**
-   * Get the total length of the data stored in the frame.
-   */
-  public int getTotalData() {
-    return m_totalData;
-  }
-
-  /**
-   * Get the width of the frame.
-   */
-  public int getWidth() {
-    return m_width;
-  }
-
-  /**
-   * Set the width of the frame.
-   */
-  public void setWidth(int width) {
-    this.m_width = width;
-  }
-
-  /**
-   * Get the height of the frame.
-   */
-  public int getHeight() {
-    return m_height;
-  }
-
-  /**
-   * Set the height of the frame.
-   */
-  public void setHeight(int height) {
-    this.m_height = height;
-  }
-
-  /**
-   * Get the PixelFormat of the frame.
-   */
-  public int getPixelFormat() {
-    return m_pixelFormat;
-  }
-
-  /**
-   * Set the PixelFormat of the frame.
-   */
-  public void setPixelFormat(int pixelFormat) {
-    this.m_pixelFormat = pixelFormat;
-  }
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/raw/RawSink.java b/cscore/src/main/java/edu/wpi/cscore/raw/RawSink.java
deleted file mode 100644
index 535f356..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/raw/RawSink.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore.raw;
-
-import edu.wpi.cscore.CameraServerJNI;
-import edu.wpi.cscore.ImageSink;
-
-/**
- * A sink for user code to accept video frames as raw bytes.
- *
- * <p>This is a complex API, most cases should use CvSink.
- */
-public class RawSink extends ImageSink {
-  /**
-   * Create a sink for accepting raw images.
-   *
-   * <p>grabFrame() must be called on the created sink to get each new
-   * image.
-   *
-   * @param name Source name (arbitrary unique identifier)
-   */
-  public RawSink(String name) {
-    super(CameraServerJNI.createRawSink(name));
-  }
-
-  /**
-   * Wait for the next frame and get the image.
-   * Times out (returning 0) after 0.225 seconds.
-   * The provided image will have three 8-bit channels stored in BGR order.
-   *
-   * @return Frame time, or 0 on error (call getError() to obtain the error
-   *         message); the frame time is in the same time base as wpi::Now(),
-   *         and is in 1 us increments.
-   */
-  protected long grabFrame(RawFrame frame) {
-    return grabFrame(frame, 0.225);
-  }
-
-  /**
-   * Wait for the next frame and get the image.
-   * Times out (returning 0) after timeout seconds.
-   * The provided image will have three 8-bit channels stored in BGR order.
-   *
-   * @return Frame time, or 0 on error (call getError() to obtain the error
-   *         message); the frame time is in the same time base as wpi::Now(),
-   *         and is in 1 us increments.
-   */
-  protected long grabFrame(RawFrame frame, double timeout) {
-    return CameraServerJNI.grabSinkFrameTimeout(m_handle, frame, timeout);
-  }
-
-  /**
-   * Wait for the next frame and get the image.  May block forever.
-   * The provided image will have three 8-bit channels stored in BGR order.
-   *
-   * @return Frame time, or 0 on error (call getError() to obtain the error
-   *         message); the frame time is in the same time base as wpi::Now(),
-   *         and is in 1 us increments.
-   */
-  protected long grabFrameNoTimeout(RawFrame frame) {
-    return CameraServerJNI.grabSinkFrame(m_handle, frame);
-  }
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/raw/RawSource.java b/cscore/src/main/java/edu/wpi/cscore/raw/RawSource.java
deleted file mode 100644
index 9dfb3f3..0000000
--- a/cscore/src/main/java/edu/wpi/cscore/raw/RawSource.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore.raw;
-
-import edu.wpi.cscore.CameraServerJNI;
-import edu.wpi.cscore.ImageSource;
-import edu.wpi.cscore.VideoMode;
-
-/**
- * A source for user code to provide video frames as raw bytes.
- *
- * <p>This is a complex API, most cases should use CvSource.
- */
-public class RawSource extends ImageSource {
-  /**
-   * Create a raw frame source.
-   *
-   * @param name Source name (arbitrary unique identifier)
-   * @param mode Video mode being generated
-   */
-  public RawSource(String name, VideoMode mode) {
-    super(CameraServerJNI.createRawSource(name,
-        mode.pixelFormat.getValue(),
-        mode.width, mode.height,
-        mode.fps));
-  }
-
-  /**
-   * Create a raw frame source.
-   *
-   * @param name Source name (arbitrary unique identifier)
-   * @param pixelFormat Pixel format
-   * @param width width
-   * @param height height
-   * @param fps fps
-   */
-  public RawSource(String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
-    super(CameraServerJNI.createRawSource(name,
-        pixelFormat.getValue(),
-        width, height,
-        fps));
-  }
-
-  /**
-   * Put a raw image and notify sinks.
-   *
-   * @param image raw frame image
-   */
-  protected void putFrame(RawFrame image) {
-    CameraServerJNI.putRawSourceFrame(m_handle, image);
-  }
-
-  /**
-   * Put a raw image and notify sinks.
-   *
-   * @param data raw frame data pointer
-   * @param width frame width
-   * @param height frame height
-   * @param pixelFormat pixel format
-   * @param totalData length of data in total
-   */
-  protected void putFrame(long data, int width, int height, int pixelFormat, int totalData) {
-    CameraServerJNI.putRawSourceFrame(m_handle, data, width, height, pixelFormat, totalData);
-  }
-
-  /**
-   * Put a raw image and notify sinks.
-   *
-   * @param data raw frame data pointer
-   * @param width frame width
-   * @param height frame height
-   * @param pixelFormat pixel format
-   * @param totalData length of data in total
-   */
-  protected void putFrame(long data, int width, int height, VideoMode.PixelFormat pixelFormat,
-                          int totalData) {
-    CameraServerJNI.putRawSourceFrame(m_handle, data, width, height, pixelFormat.getValue(),
-                                      totalData);
-  }
-}
diff --git a/cscore/src/main/java/edu/wpi/cscore/AxisCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/AxisCamera.java
similarity index 62%
rename from cscore/src/main/java/edu/wpi/cscore/AxisCamera.java
rename to cscore/src/main/java/edu/wpi/first/cscore/AxisCamera.java
index 8eef0b3..296bd6b 100644
--- a/cscore/src/main/java/edu/wpi/cscore/AxisCamera.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/AxisCamera.java
@@ -1,15 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
-/**
- * A source that represents an Axis IP camera.
- */
+/** A source that represents an Axis IP camera. */
 public class AxisCamera extends HttpCamera {
   private static String hostToUrl(String host) {
     return "http://" + host + "/mjpg/video.mjpg";
diff --git a/cscore/src/main/java/edu/wpi/cscore/CameraServerCvJNI.java b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java
similarity index 61%
rename from cscore/src/main/java/edu/wpi/cscore/CameraServerCvJNI.java
rename to cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java
index 78b4ff8..3527dae 100644
--- a/cscore/src/main/java/edu/wpi/cscore/CameraServerCvJNI.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerCvJNI.java
@@ -1,19 +1,14 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
+import edu.wpi.first.util.RuntimeLoader;
 import java.io.IOException;
 import java.util.concurrent.atomic.AtomicBoolean;
-
 import org.opencv.core.Core;
 
-import edu.wpi.first.wpiutil.RuntimeLoader;
-
 public class CameraServerCvJNI {
   static boolean libraryLoaded = false;
 
@@ -36,7 +31,8 @@
     if (Helper.getExtractOnStaticLoad()) {
       try {
         CameraServerJNI.forceLoad();
-        loader = new RuntimeLoader<>(opencvName, RuntimeLoader.getDefaultExtractionRoot(), Core.class);
+        loader =
+            new RuntimeLoader<>(opencvName, RuntimeLoader.getDefaultExtractionRoot(), Core.class);
         loader.loadLibraryHashed();
       } catch (IOException ex) {
         ex.printStackTrace();
@@ -48,25 +44,31 @@
 
   /**
    * Force load the library.
+   *
+   * @throws IOException if library load failed
    */
   public static synchronized void forceLoad() throws IOException {
     if (libraryLoaded) {
       return;
     }
     CameraServerJNI.forceLoad();
-    loader = new RuntimeLoader<>(Core.NATIVE_LIBRARY_NAME, RuntimeLoader.getDefaultExtractionRoot(), Core.class);
+    loader =
+        new RuntimeLoader<>(
+            Core.NATIVE_LIBRARY_NAME, RuntimeLoader.getDefaultExtractionRoot(), Core.class);
     loader.loadLibrary();
     libraryLoaded = true;
   }
 
-  public static native int createCvSource(String name, int pixelFormat, int width, int height, int fps);
+  public static native int createCvSource(
+      String name, int pixelFormat, int width, int height, int fps);
 
   public static native void putSourceFrame(int source, long imageNativeObj);
 
   public static native int createCvSink(String name);
-  //public static native int createCvSinkCallback(String name,
+  // public static native int createCvSinkCallback(String name,
   //                            void (*processFrame)(long time));
 
   public static native long grabSinkFrame(int sink, long imageNativeObj);
+
   public static native long grabSinkFrameTimeout(int sink, long imageNativeObj, double timeout);
 }
diff --git a/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java
similarity index 71%
rename from cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
rename to cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java
index b8a548c..fe46baf 100644
--- a/cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/CameraServerJNI.java
@@ -1,20 +1,16 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
+import edu.wpi.first.cscore.raw.RawFrame;
+import edu.wpi.first.util.RuntimeLoader;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 
-import edu.wpi.cscore.raw.RawFrame;
-import edu.wpi.first.wpiutil.RuntimeLoader;
-
 public class CameraServerJNI {
   static boolean libraryLoaded = false;
 
@@ -35,7 +31,9 @@
   static {
     if (Helper.getExtractOnStaticLoad()) {
       try {
-        loader = new RuntimeLoader<>("cscorejni", RuntimeLoader.getDefaultExtractionRoot(), CameraServerJNI.class);
+        loader =
+            new RuntimeLoader<>(
+                "cscorejni", RuntimeLoader.getDefaultExtractionRoot(), CameraServerJNI.class);
         loader.loadLibrary();
       } catch (IOException ex) {
         ex.printStackTrace();
@@ -47,12 +45,16 @@
 
   /**
    * Force load the library.
+   *
+   * @throws IOException if library load failed
    */
   public static synchronized void forceLoad() throws IOException {
     if (libraryLoaded) {
       return;
     }
-    loader = new RuntimeLoader<>("cscorejni", RuntimeLoader.getDefaultExtractionRoot(), CameraServerJNI.class);
+    loader =
+        new RuntimeLoader<>(
+            "cscorejni", RuntimeLoader.getDefaultExtractionRoot(), CameraServerJNI.class);
     loader.loadLibrary();
     libraryLoaded = true;
   }
@@ -61,89 +63,159 @@
   // Property Functions
   //
   public static native int getPropertyKind(int property);
+
   public static native String getPropertyName(int property);
+
   public static native int getProperty(int property);
+
   public static native void setProperty(int property, int value);
+
   public static native int getPropertyMin(int property);
+
   public static native int getPropertyMax(int property);
+
   public static native int getPropertyStep(int property);
+
   public static native int getPropertyDefault(int property);
+
   public static native String getStringProperty(int property);
+
   public static native void setStringProperty(int property, String value);
+
   public static native String[] getEnumPropertyChoices(int property);
 
   //
   // Source Creation Functions
   //
   public static native int createUsbCameraDev(String name, int dev);
+
   public static native int createUsbCameraPath(String name, String path);
+
   public static native int createHttpCamera(String name, String url, int kind);
+
   public static native int createHttpCameraMulti(String name, String[] urls, int kind);
-  public static native int createRawSource(String name, int pixelFormat, int width, int height, int fps);
+
+  public static native int createRawSource(
+      String name, int pixelFormat, int width, int height, int fps);
 
   //
   // Source Functions
   //
   public static native int getSourceKind(int source);
+
   public static native String getSourceName(int source);
+
   public static native String getSourceDescription(int source);
+
   public static native long getSourceLastFrameTime(int source);
+
   public static native void setSourceConnectionStrategy(int source, int strategy);
+
   public static native boolean isSourceConnected(int source);
+
   public static native boolean isSourceEnabled(int source);
+
   public static native int getSourceProperty(int source, String name);
+
   public static native int[] enumerateSourceProperties(int source);
+
   public static native VideoMode getSourceVideoMode(int source);
-  public static native boolean setSourceVideoMode(int source, int pixelFormat, int width, int height, int fps);
+
+  public static native boolean setSourceVideoMode(
+      int source, int pixelFormat, int width, int height, int fps);
+
   public static native boolean setSourcePixelFormat(int source, int pixelFormat);
+
   public static native boolean setSourceResolution(int source, int width, int height);
+
   public static native boolean setSourceFPS(int source, int fps);
+
   public static native boolean setSourceConfigJson(int source, String config);
+
   public static native String getSourceConfigJson(int source);
+
   public static native VideoMode[] enumerateSourceVideoModes(int source);
+
   public static native int[] enumerateSourceSinks(int source);
+
   public static native int copySource(int source);
+
   public static native void releaseSource(int source);
 
   //
   // Camera Source Common Property Fuctions
   //
   public static native void setCameraBrightness(int source, int brightness);
+
   public static native int getCameraBrightness(int source);
+
   public static native void setCameraWhiteBalanceAuto(int source);
+
   public static native void setCameraWhiteBalanceHoldCurrent(int source);
+
   public static native void setCameraWhiteBalanceManual(int source, int value);
+
   public static native void setCameraExposureAuto(int source);
+
   public static native void setCameraExposureHoldCurrent(int source);
+
   public static native void setCameraExposureManual(int source, int value);
 
   //
   // UsbCamera Source Functions
   //
   public static native void setUsbCameraPath(int source, String path);
+
   public static native String getUsbCameraPath(int source);
+
   public static native UsbCameraInfo getUsbCameraInfo(int source);
 
   //
   // HttpCamera Source Functions
   //
   public static native int getHttpCameraKind(int source);
+
   public static native void setHttpCameraUrls(int source, String[] urls);
+
   public static native String[] getHttpCameraUrls(int source);
 
   //
   // Image Source Functions
   //
-  public static native void putRawSourceFrameBB(int source, ByteBuffer data, int width, int height, int pixelFormat, int totalData);
-  public static native void putRawSourceFrame(int source, long data, int width, int height, int pixelFormat, int totalData);
+  public static native void putRawSourceFrameBB(
+      int source, ByteBuffer data, int width, int height, int pixelFormat, int totalData);
+
+  public static native void putRawSourceFrame(
+      int source, long data, int width, int height, int pixelFormat, int totalData);
+
   public static void putRawSourceFrame(int source, RawFrame raw) {
-    putRawSourceFrame(source, raw.getDataPtr(), raw.getWidth(), raw.getHeight(), raw.getPixelFormat(), raw.getTotalData());
+    putRawSourceFrame(
+        source,
+        raw.getDataPtr(),
+        raw.getWidth(),
+        raw.getHeight(),
+        raw.getPixelFormat(),
+        raw.getTotalData());
   }
+
   public static native void notifySourceError(int source, String msg);
+
   public static native void setSourceConnected(int source, boolean connected);
+
   public static native void setSourceDescription(int source, String description);
-  public static native int createSourceProperty(int source, String name, int kind, int minimum, int maximum, int step, int defaultValue, int value);
-  public static native void setSourceEnumPropertyChoices(int source, int property, String[] choices);
+
+  public static native int createSourceProperty(
+      int source,
+      String name,
+      int kind,
+      int minimum,
+      int maximum,
+      int step,
+      int defaultValue,
+      int value);
+
+  public static native void setSourceEnumPropertyChoices(
+      int source, int property, String[] choices);
 
   //
   // Sink Creation Functions
@@ -156,22 +228,34 @@
   // Sink Functions
   //
   public static native int getSinkKind(int sink);
+
   public static native String getSinkName(int sink);
+
   public static native String getSinkDescription(int sink);
+
   public static native int getSinkProperty(int sink, String name);
+
   public static native int[] enumerateSinkProperties(int sink);
+
   public static native boolean setSinkConfigJson(int sink, String config);
+
   public static native String getSinkConfigJson(int sink);
+
   public static native void setSinkSource(int sink, int source);
+
   public static native int getSinkSourceProperty(int sink, String name);
+
   public static native int getSinkSource(int sink);
+
   public static native int copySink(int sink);
+
   public static native void releaseSink(int sink);
 
   //
   // MjpegServer Sink Functions
   //
   public static native String getMjpegServerListenAddress(int sink);
+
   public static native int getMjpegServerPort(int sink);
 
   //
@@ -179,26 +263,73 @@
   //
   public static native void setSinkDescription(int sink, String description);
 
-  private static native long grabRawSinkFrameImpl(int sink, RawFrame rawFrame, long rawFramePtr, ByteBuffer byteBuffer, int width, int height, int pixelFormat);
-  private static native long grabRawSinkFrameTimeoutImpl(int sink, RawFrame rawFrame, long rawFramePtr, ByteBuffer byteBuffer, int width, int height, int pixelFormat, double timeout);
+  private static native long grabRawSinkFrameImpl(
+      int sink,
+      RawFrame rawFrame,
+      long rawFramePtr,
+      ByteBuffer byteBuffer,
+      int width,
+      int height,
+      int pixelFormat);
+
+  private static native long grabRawSinkFrameTimeoutImpl(
+      int sink,
+      RawFrame rawFrame,
+      long rawFramePtr,
+      ByteBuffer byteBuffer,
+      int width,
+      int height,
+      int pixelFormat,
+      double timeout);
 
   public static long grabSinkFrame(int sink, RawFrame rawFrame) {
-    return grabRawSinkFrameImpl(sink, rawFrame, rawFrame.getFramePtr(), rawFrame.getDataByteBuffer(), rawFrame.getWidth(), rawFrame.getHeight(), rawFrame.getPixelFormat());
+    return grabRawSinkFrameImpl(
+        sink,
+        rawFrame,
+        rawFrame.getFramePtr(),
+        rawFrame.getDataByteBuffer(),
+        rawFrame.getWidth(),
+        rawFrame.getHeight(),
+        rawFrame.getPixelFormat());
   }
+
   public static long grabSinkFrameTimeout(int sink, RawFrame rawFrame, double timeout) {
-    return grabRawSinkFrameTimeoutImpl(sink, rawFrame, rawFrame.getFramePtr(), rawFrame.getDataByteBuffer(), rawFrame.getWidth(), rawFrame.getHeight(), rawFrame.getPixelFormat(), timeout);
+    return grabRawSinkFrameTimeoutImpl(
+        sink,
+        rawFrame,
+        rawFrame.getFramePtr(),
+        rawFrame.getDataByteBuffer(),
+        rawFrame.getWidth(),
+        rawFrame.getHeight(),
+        rawFrame.getPixelFormat(),
+        timeout);
   }
+
   public static native String getSinkError(int sink);
+
   public static native void setSinkEnabled(int sink, boolean enabled);
 
   //
   // Listener Functions
   //
-  public static native int addListener(Consumer<VideoEvent> listener,
-                                       int eventMask, boolean immediateNotify);
+  public static native int addListener(
+      Consumer<VideoEvent> listener, int eventMask, boolean immediateNotify);
 
   public static native void removeListener(int handle);
 
+  public static native int createListenerPoller();
+
+  public static native void destroyListenerPoller(int poller);
+
+  public static native int addPolledListener(int poller, int eventMask, boolean immediateNotify);
+
+  public static native VideoEvent[] pollListener(int poller) throws InterruptedException;
+
+  public static native VideoEvent[] pollListenerTimeout(int poller, double timeout)
+      throws InterruptedException;
+
+  public static native void cancelPollListener(int poller);
+
   //
   // Telemetry Functions
   //
@@ -206,7 +337,6 @@
     kSourceBytesReceived(1),
     kSourceFramesReceived(2);
 
-    @SuppressWarnings("MemberName")
     private final int value;
 
     TelemetryKind(int value) {
@@ -217,13 +347,19 @@
       return value;
     }
   }
+
   public static native void setTelemetryPeriod(double seconds);
+
   public static native double getTelemetryElapsedTime();
+
   public static native long getTelemetryValue(int handle, int kind);
+
   public static long getTelemetryValue(int handle, TelemetryKind kind) {
     return getTelemetryValue(handle, kind.getValue());
   }
+
   public static native double getTelemetryAverageValue(int handle, int kind);
+
   public static double getTelemetryAverageValue(int handle, TelemetryKind kind) {
     return getTelemetryAverageValue(handle, kind.getValue());
   }
@@ -235,6 +371,7 @@
   public interface LoggerFunction {
     void apply(int level, String file, int line, String msg);
   }
+
   public static native void setLogger(LoggerFunction func, int minLevel);
 
   //
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/CvSink.java b/cscore/src/main/java/edu/wpi/first/cscore/CvSink.java
new file mode 100644
index 0000000..88ca8b1
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/CvSink.java
@@ -0,0 +1,72 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+import org.opencv.core.Mat;
+
+/**
+ * A sink for user code to accept video frames as OpenCV images. These sinks require the WPILib
+ * OpenCV builds. For an alternate OpenCV, see the documentation how to build your own with RawSink.
+ */
+public class CvSink extends ImageSink {
+  /**
+   * Create a sink for accepting OpenCV images. WaitForFrame() must be called on the created sink to
+   * get each new image.
+   *
+   * @param name Source name (arbitrary unique identifier)
+   */
+  public CvSink(String name) {
+    super(CameraServerCvJNI.createCvSink(name));
+  }
+
+  /// Create a sink for accepting OpenCV images in a separate thread.
+  /// A thread will be created that calls WaitForFrame() and calls the
+  /// processFrame() callback each time a new frame arrives.
+  /// @param name Source name (arbitrary unique identifier)
+  /// @param processFrame Frame processing function; will be called with a
+  ///        time=0 if an error occurred.  processFrame should call GetImage()
+  ///        or GetError() as needed, but should not call (except in very
+  ///        unusual circumstances) WaitForImage().
+  // public CvSink(wpi::StringRef name,
+  //       std::function<void(uint64_t time)> processFrame) {
+  //  super(CameraServerJNI.createCvSinkCallback(name, processFrame));
+  // }
+
+  /**
+   * Wait for the next frame and get the image. Times out (returning 0) after 0.225 seconds. The
+   * provided image will have three 3-bit channels stored in BGR order.
+   *
+   * @param image Where to store the image.
+   * @return Frame time, or 0 on error (call GetError() to obtain the error message)
+   */
+  public long grabFrame(Mat image) {
+    return grabFrame(image, 0.225);
+  }
+
+  /**
+   * Wait for the next frame and get the image. Times out (returning 0) after timeout seconds. The
+   * provided image will have three 3-bit channels stored in BGR order.
+   *
+   * @param image Where to store the image.
+   * @param timeout Retrieval timeout in seconds.
+   * @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
+   *     is in 1 us increments.
+   */
+  public long grabFrame(Mat image, double timeout) {
+    return CameraServerCvJNI.grabSinkFrameTimeout(m_handle, image.nativeObj, timeout);
+  }
+
+  /**
+   * Wait for the next frame and get the image. May block forever. The provided image will have
+   * three 3-bit channels stored in BGR order.
+   *
+   * @param image Where to store the image.
+   * @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
+   *     is in 1 us increments.
+   */
+  public long grabFrameNoTimeout(Mat image) {
+    return CameraServerCvJNI.grabSinkFrame(m_handle, image.nativeObj);
+  }
+}
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/CvSource.java b/cscore/src/main/java/edu/wpi/first/cscore/CvSource.java
new file mode 100644
index 0000000..3934a09
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/CvSource.java
@@ -0,0 +1,51 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+import org.opencv.core.Mat;
+
+/**
+ * A source that represents a video camera. These sources require the WPILib OpenCV builds. For an
+ * alternate OpenCV, see the documentation how to build your own with RawSource.
+ */
+public class CvSource extends ImageSource {
+  /**
+   * Create an OpenCV source.
+   *
+   * @param name Source name (arbitrary unique identifier)
+   * @param mode Video mode being generated
+   */
+  public CvSource(String name, VideoMode mode) {
+    super(
+        CameraServerCvJNI.createCvSource(
+            name, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps));
+  }
+
+  /**
+   * Create an OpenCV source.
+   *
+   * @param name Source name (arbitrary unique identifier)
+   * @param pixelFormat Pixel format
+   * @param width width
+   * @param height height
+   * @param fps fps
+   */
+  public CvSource(String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
+    super(CameraServerCvJNI.createCvSource(name, pixelFormat.getValue(), width, height, fps));
+  }
+
+  /**
+   * Put an OpenCV image and notify sinks.
+   *
+   * <p>Only 8-bit single-channel or 3-channel (with BGR channel order) images are supported. If the
+   * format, depth or channel order is different, use Mat.convertTo() and/or cvtColor() to convert
+   * it first.
+   *
+   * @param image OpenCV image
+   */
+  public void putFrame(Mat image) {
+    CameraServerCvJNI.putSourceFrame(m_handle, image.nativeObj);
+  }
+}
diff --git a/cscore/src/main/java/edu/wpi/cscore/HttpCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java
similarity index 73%
rename from cscore/src/main/java/edu/wpi/cscore/HttpCamera.java
rename to cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java
index 533d00f..8c72350 100644
--- a/cscore/src/main/java/edu/wpi/cscore/HttpCamera.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/HttpCamera.java
@@ -1,20 +1,17 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
-/**
- * A source that represents a MJPEG-over-HTTP (IP) camera.
- */
+/** A source that represents a MJPEG-over-HTTP (IP) camera. */
 public class HttpCamera extends VideoCamera {
   public enum HttpCameraKind {
-    kUnknown(0), kMJPGStreamer(1), kCSCore(2), kAxis(3);
+    kUnknown(0),
+    kMJPGStreamer(1),
+    kCSCore(2),
+    kAxis(3);
 
-    @SuppressWarnings("MemberName")
     private final int value;
 
     HttpCameraKind(int value) {
@@ -34,10 +31,14 @@
    */
   public static HttpCameraKind getHttpCameraKindFromInt(int kind) {
     switch (kind) {
-      case 1: return HttpCameraKind.kMJPGStreamer;
-      case 2: return HttpCameraKind.kCSCore;
-      case 3: return HttpCameraKind.kAxis;
-      default: return HttpCameraKind.kUnknown;
+      case 1:
+        return HttpCameraKind.kMJPGStreamer;
+      case 2:
+        return HttpCameraKind.kCSCore;
+      case 3:
+        return HttpCameraKind.kAxis;
+      default:
+        return HttpCameraKind.kUnknown;
     }
   }
 
@@ -86,8 +87,9 @@
   /**
    * Get the kind of HTTP camera.
    *
-   * <p>Autodetection can result in returning a different value than the camera
-   * was created with.
+   * <p>Autodetection can result in returning a different value than the camera was created with.
+   *
+   * @return The kind of HTTP camera.
    */
   public HttpCameraKind getHttpCameraKind() {
     return getHttpCameraKindFromInt(CameraServerJNI.getHttpCameraKind(m_handle));
@@ -95,6 +97,8 @@
 
   /**
    * Change the URLs used to connect to the camera.
+   *
+   * @param urls Array of Camera URLs
    */
   public void setUrls(String[] urls) {
     CameraServerJNI.setHttpCameraUrls(m_handle, urls);
@@ -102,6 +106,8 @@
 
   /**
    * Get the URLs used to connect to the camera.
+   *
+   * @return Array of camera URLs.
    */
   public String[] getUrls() {
     return CameraServerJNI.getHttpCameraUrls(m_handle);
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java b/cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java
new file mode 100644
index 0000000..1c4cac2
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/ImageSink.java
@@ -0,0 +1,40 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+public abstract class ImageSink extends VideoSink {
+  protected ImageSink(int handle) {
+    super(handle);
+  }
+
+  /**
+   * Set sink description.
+   *
+   * @param description Description
+   */
+  public void setDescription(String description) {
+    CameraServerJNI.setSinkDescription(m_handle, description);
+  }
+
+  /**
+   * Get error string. Call this if WaitForFrame() returns 0 to determine what the error is.
+   *
+   * @return Error string.
+   */
+  public String getError() {
+    return CameraServerJNI.getSinkError(m_handle);
+  }
+
+  /**
+   * Enable or disable getting new frames. Disabling will cause processFrame (for callback-based
+   * CvSinks) to not be called and WaitForFrame() to not return. This can be used to save processor
+   * resources when frames are not needed.
+   *
+   * @param enabled Enable to get new frames.
+   */
+  public void setEnabled(boolean enabled) {
+    CameraServerJNI.setSinkEnabled(m_handle, enabled);
+  }
+}
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java b/cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java
new file mode 100644
index 0000000..6bebed8
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/ImageSource.java
@@ -0,0 +1,136 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+public abstract class ImageSource extends VideoSource {
+  protected ImageSource(int handle) {
+    super(handle);
+  }
+
+  /**
+   * Signal sinks that an error has occurred. This should be called instead of NotifyFrame when an
+   * error occurs.
+   *
+   * @param msg Error message.
+   */
+  public void notifyError(String msg) {
+    CameraServerJNI.notifySourceError(m_handle, msg);
+  }
+
+  /**
+   * Set source connection status. Defaults to true.
+   *
+   * @param connected True for connected, false for disconnected
+   */
+  public void setConnected(boolean connected) {
+    CameraServerJNI.setSourceConnected(m_handle, connected);
+  }
+
+  /**
+   * Set source description.
+   *
+   * @param description Description
+   */
+  public void setDescription(String description) {
+    CameraServerJNI.setSourceDescription(m_handle, description);
+  }
+
+  /**
+   * Create a property.
+   *
+   * @param name Property name
+   * @param kind Property kind
+   * @param minimum Minimum value
+   * @param maximum Maximum value
+   * @param step Step value
+   * @param defaultValue Default value
+   * @param value Current value
+   * @return Property
+   */
+  public VideoProperty createProperty(
+      String name,
+      VideoProperty.Kind kind,
+      int minimum,
+      int maximum,
+      int step,
+      int defaultValue,
+      int value) {
+    return new VideoProperty(
+        CameraServerJNI.createSourceProperty(
+            m_handle, name, kind.getValue(), minimum, maximum, step, defaultValue, value));
+  }
+
+  /**
+   * Create an integer property.
+   *
+   * @param name Property name
+   * @param minimum Minimum value
+   * @param maximum Maximum value
+   * @param step Step value
+   * @param defaultValue Default value
+   * @param value Current value
+   * @return Property
+   */
+  public VideoProperty createIntegerProperty(
+      String name, int minimum, int maximum, int step, int defaultValue, int value) {
+    return new VideoProperty(
+        CameraServerJNI.createSourceProperty(
+            m_handle,
+            name,
+            VideoProperty.Kind.kInteger.getValue(),
+            minimum,
+            maximum,
+            step,
+            defaultValue,
+            value));
+  }
+
+  /**
+   * Create a boolean property.
+   *
+   * @param name Property name
+   * @param defaultValue Default value
+   * @param value Current value
+   * @return Property
+   */
+  public VideoProperty createBooleanProperty(String name, boolean defaultValue, boolean value) {
+    return new VideoProperty(
+        CameraServerJNI.createSourceProperty(
+            m_handle,
+            name,
+            VideoProperty.Kind.kBoolean.getValue(),
+            0,
+            1,
+            1,
+            defaultValue ? 1 : 0,
+            value ? 1 : 0));
+  }
+
+  /**
+   * Create a string property.
+   *
+   * @param name Property name
+   * @param value Current value
+   * @return Property
+   */
+  public VideoProperty createStringProperty(String name, String value) {
+    VideoProperty prop =
+        new VideoProperty(
+            CameraServerJNI.createSourceProperty(
+                m_handle, name, VideoProperty.Kind.kString.getValue(), 0, 0, 0, 0, 0));
+    prop.setString(value);
+    return prop;
+  }
+
+  /**
+   * Configure enum property choices.
+   *
+   * @param property Property
+   * @param choices Choices
+   */
+  public void setEnumPropertyChoices(VideoProperty property, String[] choices) {
+    CameraServerJNI.setSourceEnumPropertyChoices(m_handle, property.m_handle, choices);
+  }
+}
diff --git a/cscore/src/main/java/edu/wpi/cscore/MjpegServer.java b/cscore/src/main/java/edu/wpi/first/cscore/MjpegServer.java
similarity index 63%
rename from cscore/src/main/java/edu/wpi/cscore/MjpegServer.java
rename to cscore/src/main/java/edu/wpi/first/cscore/MjpegServer.java
index 4c00aed..54c8053 100644
--- a/cscore/src/main/java/edu/wpi/cscore/MjpegServer.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/MjpegServer.java
@@ -1,15 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
-/**
- * A sink that acts as a MJPEG-over-HTTP network server.
- */
+/** A sink that acts as a MJPEG-over-HTTP network server. */
 public class MjpegServer extends VideoSink {
   /**
    * Create a MJPEG-over-HTTP server sink.
@@ -34,6 +29,8 @@
 
   /**
    * Get the listen address of the server.
+   *
+   * @return The listen address.
    */
   public String getListenAddress() {
     return CameraServerJNI.getMjpegServerListenAddress(m_handle);
@@ -41,6 +38,8 @@
 
   /**
    * Get the port number of the server.
+   *
+   * @return The port number.
    */
   public int getPort() {
     return CameraServerJNI.getMjpegServerPort(m_handle);
@@ -49,13 +48,11 @@
   /**
    * Set the stream resolution for clients that don't specify it.
    *
-   * <p>It is not necessary to set this if it is the same as the source
-   * resolution.
+   * <p>It is not necessary to set this if it is the same as the source resolution.
    *
-   * <p>Setting this different than the source resolution will result in
-   * increased CPU usage, particularly for MJPEG source cameras, as it will
-   * decompress, resize, and recompress the image, instead of using the
-   * camera's MJPEG image directly.
+   * <p>Setting this different than the source resolution will result in increased CPU usage,
+   * particularly for MJPEG source cameras, as it will decompress, resize, and recompress the image,
+   * instead of using the camera's MJPEG image directly.
    *
    * @param width width, 0 for unspecified
    * @param height height, 0 for unspecified
@@ -79,26 +76,24 @@
   /**
    * Set the compression for clients that don't specify it.
    *
-   * <p>Setting this will result in increased CPU usage for MJPEG source cameras
-   * as it will decompress and recompress the image instead of using the
-   * camera's MJPEG image directly.
+   * <p>Setting this will result in increased CPU usage for MJPEG source cameras as it will
+   * decompress and recompress the image instead of using the camera's MJPEG image directly.
    *
    * @param quality JPEG compression quality (0-100), -1 for unspecified
    */
   public void setCompression(int quality) {
-    CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "compression"),
-                                quality);
+    CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "compression"), quality);
   }
 
   /**
-   * Set the default compression used for non-MJPEG sources.  If not set,
-   * 80 is used.  This function has no effect on MJPEG source cameras; use
-   * SetCompression() instead to force recompression of MJPEG source images.
+   * Set the default compression used for non-MJPEG sources. If not set, 80 is used. This function
+   * has no effect on MJPEG source cameras; use SetCompression() instead to force recompression of
+   * MJPEG source images.
    *
    * @param quality JPEG compression quality (0-100)
    */
   public void setDefaultCompression(int quality) {
-    CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "default_compression"),
-                                quality);
+    CameraServerJNI.setProperty(
+        CameraServerJNI.getSinkProperty(m_handle, "default_compression"), quality);
   }
 }
diff --git a/cscore/src/main/java/edu/wpi/cscore/UsbCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/UsbCamera.java
similarity index 68%
rename from cscore/src/main/java/edu/wpi/cscore/UsbCamera.java
rename to cscore/src/main/java/edu/wpi/first/cscore/UsbCamera.java
index 7526335..8e0817e 100644
--- a/cscore/src/main/java/edu/wpi/cscore/UsbCamera.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/UsbCamera.java
@@ -1,15 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
-/**
- * A source that represents a USB camera.
- */
+/** A source that represents a USB camera. */
 public class UsbCamera extends VideoCamera {
   /**
    * Create a source for a USB camera based on device number.
@@ -42,6 +37,8 @@
 
   /**
    * Change the path to the device.
+   *
+   * @param path New device path.
    */
   void setPath(String path) {
     CameraServerJNI.setUsbCameraPath(m_handle, path);
@@ -49,6 +46,8 @@
 
   /**
    * Get the path to the device.
+   *
+   * @return The device path.
    */
   public String getPath() {
     return CameraServerJNI.getUsbCameraPath(m_handle);
@@ -56,6 +55,8 @@
 
   /**
    * Get the full camera information for the device.
+   *
+   * @return The camera information.
    */
   public UsbCameraInfo getInfo() {
     return CameraServerJNI.getUsbCameraInfo(m_handle);
@@ -67,7 +68,7 @@
    * @param level 0=don't display Connecting message, 1=do display message
    */
   public void setConnectVerbose(int level) {
-    CameraServerJNI.setProperty(CameraServerJNI.getSourceProperty(m_handle, "connect_verbose"),
-                                level);
+    CameraServerJNI.setProperty(
+        CameraServerJNI.getSourceProperty(m_handle, "connect_verbose"), level);
   }
 }
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/UsbCameraInfo.java b/cscore/src/main/java/edu/wpi/first/cscore/UsbCameraInfo.java
new file mode 100644
index 0000000..df856e8
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/UsbCameraInfo.java
@@ -0,0 +1,53 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+/** USB camera information. */
+public class UsbCameraInfo {
+  /**
+   * Create a new set of UsbCameraInfo.
+   *
+   * @param dev Device number (e.g. N in '/dev/videoN' on Linux)
+   * @param path Path to device if available (e.g. '/dev/video0' on Linux)
+   * @param name Vendor/model name of the camera as provided by the USB driver
+   * @param otherPaths Other path aliases to device
+   * @param vendorId USB vendor id
+   * @param productId USB product id
+   */
+  @SuppressWarnings("PMD.ArrayIsStoredDirectly")
+  public UsbCameraInfo(
+      int dev, String path, String name, String[] otherPaths, int vendorId, int productId) {
+    this.dev = dev;
+    this.path = path;
+    this.name = name;
+    this.otherPaths = otherPaths;
+    this.vendorId = vendorId;
+    this.productId = productId;
+  }
+
+  /** Device number (e.g. N in '/dev/videoN' on Linux). */
+  @SuppressWarnings("MemberName")
+  public int dev;
+
+  /** Path to device if available (e.g. '/dev/video0' on Linux). */
+  @SuppressWarnings("MemberName")
+  public String path;
+
+  /** Vendor/model name of the camera as provided by the USB driver. */
+  @SuppressWarnings("MemberName")
+  public String name;
+
+  /** Other path aliases to device (e.g. '/dev/v4l/by-id/...' etc on Linux). */
+  @SuppressWarnings("MemberName")
+  public String[] otherPaths;
+
+  /** USB vendor id. */
+  @SuppressWarnings("MemberName")
+  public int vendorId;
+
+  /** USB product id. */
+  @SuppressWarnings("MemberName")
+  public int productId;
+}
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoCamera.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java
similarity index 68%
rename from cscore/src/main/java/edu/wpi/cscore/VideoCamera.java
rename to cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java
index 0ab95f1..23cf7d6 100644
--- a/cscore/src/main/java/edu/wpi/cscore/VideoCamera.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoCamera.java
@@ -1,15 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
-/**
- * A source that represents a video camera.
- */
+/** A source that represents a video camera. */
 public class VideoCamera extends VideoSource {
   public static class WhiteBalance {
     public static final int kFixedIndoor = 3000;
@@ -25,6 +20,8 @@
 
   /**
    * Set the brightness, as a percentage (0-100).
+   *
+   * @param brightness Brightness as a percentage (0-100).
    */
   public synchronized void setBrightness(int brightness) {
     CameraServerJNI.setCameraBrightness(m_handle, brightness);
@@ -32,48 +29,46 @@
 
   /**
    * Get the brightness, as a percentage (0-100).
+   *
+   * @return The brightness as a percentage (0-100).
    */
   public synchronized int getBrightness() {
     return CameraServerJNI.getCameraBrightness(m_handle);
   }
 
-  /**
-   * Set the white balance to auto.
-   */
+  /** Set the white balance to auto. */
   public synchronized void setWhiteBalanceAuto() {
     CameraServerJNI.setCameraWhiteBalanceAuto(m_handle);
   }
 
-  /**
-   * Set the white balance to hold current.
-   */
+  /** Set the white balance to hold current. */
   public synchronized void setWhiteBalanceHoldCurrent() {
     CameraServerJNI.setCameraWhiteBalanceHoldCurrent(m_handle);
   }
 
   /**
    * Set the white balance to manual, with specified color temperature.
+   *
+   * @param value The specified color temperature.
    */
   public synchronized void setWhiteBalanceManual(int value) {
     CameraServerJNI.setCameraWhiteBalanceManual(m_handle, value);
   }
 
-  /**
-   * Set the exposure to auto aperture.
-   */
+  /** Set the exposure to auto aperture. */
   public synchronized void setExposureAuto() {
     CameraServerJNI.setCameraExposureAuto(m_handle);
   }
 
-  /**
-   * Set the exposure to hold current.
-   */
+  /** Set the exposure to hold current. */
   public synchronized void setExposureHoldCurrent() {
     CameraServerJNI.setCameraExposureHoldCurrent(m_handle);
   }
 
   /**
    * Set the exposure to manual, as a percentage (0-100).
+   *
+   * @param value The exposure as a percentage (0-100).
    */
   public synchronized void setExposureManual(int value) {
     CameraServerJNI.setCameraExposureManual(m_handle, value);
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java
new file mode 100644
index 0000000..50f041c
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoEvent.java
@@ -0,0 +1,166 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+/** Video event. */
+public class VideoEvent {
+  public enum Kind {
+    kUnknown(0x0000),
+    kSourceCreated(0x0001),
+    kSourceDestroyed(0x0002),
+    kSourceConnected(0x0004),
+    kSourceDisconnected(0x0008),
+    kSourceVideoModesUpdated(0x0010),
+    kSourceVideoModeChanged(0x0020),
+    kSourcePropertyCreated(0x0040),
+    kSourcePropertyValueUpdated(0x0080),
+    kSourcePropertyChoicesUpdated(0x0100),
+    kSinkSourceChanged(0x0200),
+    kSinkCreated(0x0400),
+    kSinkDestroyed(0x0800),
+    kSinkEnabled(0x1000),
+    kSinkDisabled(0x2000),
+    kNetworkInterfacesChanged(0x4000),
+    kTelemetryUpdated(0x8000),
+    kSinkPropertyCreated(0x10000),
+    kSinkPropertyValueUpdated(0x20000),
+    kSinkPropertyChoicesUpdated(0x40000),
+    kUsbCamerasChanged(0x80000);
+
+    private final int value;
+
+    Kind(int value) {
+      this.value = value;
+    }
+
+    public int getValue() {
+      return value;
+    }
+  }
+
+  /**
+   * Convert from the numerical representation of kind to an enum type.
+   *
+   * @param kind The numerical representation of kind
+   * @return The kind
+   */
+  public static Kind getKindFromInt(int kind) {
+    switch (kind) {
+      case 0x0001:
+        return Kind.kSourceCreated;
+      case 0x0002:
+        return Kind.kSourceDestroyed;
+      case 0x0004:
+        return Kind.kSourceConnected;
+      case 0x0008:
+        return Kind.kSourceDisconnected;
+      case 0x0010:
+        return Kind.kSourceVideoModesUpdated;
+      case 0x0020:
+        return Kind.kSourceVideoModeChanged;
+      case 0x0040:
+        return Kind.kSourcePropertyCreated;
+      case 0x0080:
+        return Kind.kSourcePropertyValueUpdated;
+      case 0x0100:
+        return Kind.kSourcePropertyChoicesUpdated;
+      case 0x0200:
+        return Kind.kSinkSourceChanged;
+      case 0x0400:
+        return Kind.kSinkCreated;
+      case 0x0800:
+        return Kind.kSinkDestroyed;
+      case 0x1000:
+        return Kind.kSinkEnabled;
+      case 0x2000:
+        return Kind.kSinkDisabled;
+      case 0x4000:
+        return Kind.kNetworkInterfacesChanged;
+      case 0x10000:
+        return Kind.kSinkPropertyCreated;
+      case 0x20000:
+        return Kind.kSinkPropertyValueUpdated;
+      case 0x40000:
+        return Kind.kSinkPropertyChoicesUpdated;
+      case 0x80000:
+        return Kind.kUsbCamerasChanged;
+      default:
+        return Kind.kUnknown;
+    }
+  }
+
+  VideoEvent(
+      int kind,
+      int source,
+      int sink,
+      String name,
+      int pixelFormat,
+      int width,
+      int height,
+      int fps,
+      int property,
+      int propertyKind,
+      int value,
+      String valueStr,
+      int listener) {
+    this.kind = getKindFromInt(kind);
+    this.sourceHandle = source;
+    this.sinkHandle = sink;
+    this.name = name;
+    this.mode = new VideoMode(pixelFormat, width, height, fps);
+    this.propertyHandle = property;
+    this.propertyKind = VideoProperty.getKindFromInt(propertyKind);
+    this.value = value;
+    this.valueStr = valueStr;
+    this.listener = listener;
+  }
+
+  @SuppressWarnings("MemberName")
+  public Kind kind;
+
+  // Valid for kSource* and kSink* respectively
+  @SuppressWarnings("MemberName")
+  public int sourceHandle;
+
+  @SuppressWarnings("MemberName")
+  public int sinkHandle;
+
+  // Source/sink/property name
+  @SuppressWarnings("MemberName")
+  public String name;
+
+  // Fields for kSourceVideoModeChanged event
+  @SuppressWarnings("MemberName")
+  public VideoMode mode;
+
+  // Fields for kSourceProperty* events
+  @SuppressWarnings("MemberName")
+  public int propertyHandle;
+
+  @SuppressWarnings("MemberName")
+  public VideoProperty.Kind propertyKind;
+
+  @SuppressWarnings("MemberName")
+  public int value;
+
+  @SuppressWarnings("MemberName")
+  public String valueStr;
+
+  // Listener that was triggered
+  @SuppressWarnings("MemberName")
+  public int listener;
+
+  public VideoSource getSource() {
+    return new VideoSource(CameraServerJNI.copySource(sourceHandle));
+  }
+
+  public VideoSink getSink() {
+    return new VideoSink(CameraServerJNI.copySink(sinkHandle));
+  }
+
+  public VideoProperty getProperty() {
+    return new VideoProperty(propertyHandle, propertyKind);
+  }
+}
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoException.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoException.java
new file mode 100644
index 0000000..1c445c6
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoException.java
@@ -0,0 +1,19 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+/** An exception raised by the camera server. */
+public class VideoException extends RuntimeException {
+  private static final long serialVersionUID = -9155939328084105145L;
+
+  public VideoException(String msg) {
+    super(msg);
+  }
+
+  @Override
+  public String toString() {
+    return "VideoException [" + super.toString() + "]";
+  }
+}
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java
new file mode 100644
index 0000000..0d77461
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoListener.java
@@ -0,0 +1,126 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
+
+/**
+ * An event listener. This calls back to a desigated callback function when an event matching the
+ * specified mask is generated by the library.
+ */
+public class VideoListener implements AutoCloseable {
+  /**
+   * Create an event listener.
+   *
+   * @param listener Listener function
+   * @param eventMask Bitmask of VideoEvent.Type values
+   * @param immediateNotify Whether callback should be immediately called with a representative set
+   *     of events for the current library state.
+   */
+  public VideoListener(Consumer<VideoEvent> listener, int eventMask, boolean immediateNotify) {
+    s_lock.lock();
+    try {
+      if (s_poller == 0) {
+        s_poller = CameraServerJNI.createListenerPoller();
+        startThread();
+      }
+      m_handle = CameraServerJNI.addPolledListener(s_poller, eventMask, immediateNotify);
+      s_listeners.put(m_handle, listener);
+    } finally {
+      s_lock.unlock();
+    }
+  }
+
+  @Override
+  public synchronized void close() {
+    if (m_handle != 0) {
+      s_lock.lock();
+      try {
+        s_listeners.remove(m_handle);
+      } finally {
+        s_lock.unlock();
+      }
+      CameraServerJNI.removeListener(m_handle);
+      m_handle = 0;
+    }
+  }
+
+  public boolean isValid() {
+    return m_handle != 0;
+  }
+
+  private int m_handle;
+
+  private static final ReentrantLock s_lock = new ReentrantLock();
+  private static final Map<Integer, Consumer<VideoEvent>> s_listeners = new HashMap<>();
+  private static Thread s_thread;
+  private static int s_poller;
+  private static boolean s_waitQueue;
+  private static final Condition s_waitQueueCond = s_lock.newCondition();
+
+  @SuppressWarnings("PMD.AvoidCatchingThrowable")
+  private static void startThread() {
+    s_thread =
+        new Thread(
+            () -> {
+              boolean wasInterrupted = false;
+              while (!Thread.interrupted()) {
+                VideoEvent[] events;
+                try {
+                  events = CameraServerJNI.pollListener(s_poller);
+                } catch (InterruptedException ex) {
+                  s_lock.lock();
+                  try {
+                    if (s_waitQueue) {
+                      s_waitQueue = false;
+                      s_waitQueueCond.signalAll();
+                      continue;
+                    }
+                  } finally {
+                    s_lock.unlock();
+                  }
+                  Thread.currentThread().interrupt();
+                  // don't try to destroy poller, as its handle is likely no longer valid
+                  wasInterrupted = true;
+                  break;
+                }
+                for (VideoEvent event : events) {
+                  Consumer<VideoEvent> listener;
+                  s_lock.lock();
+                  try {
+                    listener = s_listeners.get(event.listener);
+                  } finally {
+                    s_lock.unlock();
+                  }
+                  if (listener != null) {
+                    try {
+                      listener.accept(event);
+                    } catch (Throwable throwable) {
+                      System.err.println(
+                          "Unhandled exception during listener callback: " + throwable.toString());
+                      throwable.printStackTrace();
+                    }
+                  }
+                }
+              }
+              s_lock.lock();
+              try {
+                if (!wasInterrupted) {
+                  CameraServerJNI.destroyListenerPoller(s_poller);
+                }
+                s_poller = 0;
+              } finally {
+                s_lock.unlock();
+              }
+            },
+            "VideoListener");
+    s_thread.setDaemon(true);
+    s_thread.start();
+  }
+}
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java
new file mode 100644
index 0000000..726b210
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoMode.java
@@ -0,0 +1,79 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+/** Video mode. */
+public class VideoMode {
+  public enum PixelFormat {
+    kUnknown(0),
+    kMJPEG(1),
+    kYUYV(2),
+    kRGB565(3),
+    kBGR(4),
+    kGray(5);
+
+    private final int value;
+
+    PixelFormat(int value) {
+      this.value = value;
+    }
+
+    public int getValue() {
+      return value;
+    }
+  }
+
+  private static final PixelFormat[] m_pixelFormatValues = PixelFormat.values();
+
+  public static PixelFormat getPixelFormatFromInt(int pixelFormat) {
+    return m_pixelFormatValues[pixelFormat];
+  }
+
+  /**
+   * Create a new video mode.
+   *
+   * @param pixelFormat The pixel format enum as an integer.
+   * @param width The image width in pixels.
+   * @param height The image height in pixels.
+   * @param fps The camera's frames per second.
+   */
+  public VideoMode(int pixelFormat, int width, int height, int fps) {
+    this.pixelFormat = getPixelFormatFromInt(pixelFormat);
+    this.width = width;
+    this.height = height;
+    this.fps = fps;
+  }
+
+  /**
+   * Create a new video mode.
+   *
+   * @param pixelFormat The pixel format.
+   * @param width The image width in pixels.
+   * @param height The image height in pixels.
+   * @param fps The camera's frames per second.
+   */
+  public VideoMode(PixelFormat pixelFormat, int width, int height, int fps) {
+    this.pixelFormat = pixelFormat;
+    this.width = width;
+    this.height = height;
+    this.fps = fps;
+  }
+
+  /** Pixel format. */
+  @SuppressWarnings("MemberName")
+  public PixelFormat pixelFormat;
+
+  /** Width in pixels. */
+  @SuppressWarnings("MemberName")
+  public int width;
+
+  /** Height in pixels. */
+  @SuppressWarnings("MemberName")
+  public int height;
+
+  /** Frames per second. */
+  @SuppressWarnings("MemberName")
+  public int fps;
+}
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoProperty.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java
similarity index 71%
rename from cscore/src/main/java/edu/wpi/cscore/VideoProperty.java
rename to cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java
index 407e107..8179ba3 100644
--- a/cscore/src/main/java/edu/wpi/cscore/VideoProperty.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoProperty.java
@@ -1,20 +1,18 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
-/**
- * A source or sink property.
- */
+/** A source or sink property. */
 public class VideoProperty {
   public enum Kind {
-    kNone(0), kBoolean(1), kInteger(2), kString(4), kEnum(8);
+    kNone(0),
+    kBoolean(1),
+    kInteger(2),
+    kString(4),
+    kEnum(8);
 
-    @SuppressWarnings("MemberName")
     private final int value;
 
     Kind(int value) {
@@ -34,11 +32,16 @@
    */
   public static Kind getKindFromInt(int kind) {
     switch (kind) {
-      case 1: return Kind.kBoolean;
-      case 2: return Kind.kInteger;
-      case 4: return Kind.kString;
-      case 8: return Kind.kEnum;
-      default: return Kind.kNone;
+      case 1:
+        return Kind.kBoolean;
+      case 2:
+        return Kind.kInteger;
+      case 4:
+        return Kind.kString;
+      case 8:
+        return Kind.kEnum;
+      default:
+        return Kind.kNone;
     }
   }
 
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoSink.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java
similarity index 71%
rename from cscore/src/main/java/edu/wpi/cscore/VideoSink.java
rename to cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java
index 107f6d9..8b07f46 100644
--- a/cscore/src/main/java/edu/wpi/cscore/VideoSink.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoSink.java
@@ -1,22 +1,20 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
 /**
- * A source for video that provides a sequence of frames.  Each frame may
- * consist of multiple images (e.g. from a stereo or depth camera); these
- * are called channels.
+ * A source for video that provides a sequence of frames. Each frame may consist of multiple images
+ * (e.g. from a stereo or depth camera); these are called channels.
  */
 public class VideoSink implements AutoCloseable {
   public enum Kind {
-    kUnknown(0), kMjpeg(2), kCv(4), kRaw(8);
+    kUnknown(0),
+    kMjpeg(2),
+    kCv(4),
+    kRaw(8);
 
-    @SuppressWarnings("MemberName")
     private final int value;
 
     Kind(int value) {
@@ -36,9 +34,12 @@
    */
   public static Kind getKindFromInt(int kind) {
     switch (kind) {
-      case 2: return Kind.kMjpeg;
-      case 4: return Kind.kCv;
-      default: return Kind.kUnknown;
+      case 2:
+        return Kind.kMjpeg;
+      case 4:
+        return Kind.kCv;
+      default:
+        return Kind.kUnknown;
     }
   }
 
@@ -84,21 +85,27 @@
 
   /**
    * Get the kind of the sink.
+   *
+   * @return The kind of the sink.
    */
   public Kind getKind() {
     return getKindFromInt(CameraServerJNI.getSinkKind(m_handle));
   }
 
   /**
-   * Get the name of the sink.  The name is an arbitrary identifier
-   * provided when the sink is created, and should be unique.
+   * Get the name of the sink. The name is an arbitrary identifier provided when the sink is
+   * created, and should be unique.
+   *
+   * @return The name of the sink.
    */
   public String getName() {
     return CameraServerJNI.getSinkName(m_handle);
   }
 
   /**
-   * Get the sink description.  This is sink-kind specific.
+   * Get the sink description. This is sink-kind specific.
+   *
+   * @return The sink description.
    */
   public String getDescription() {
     return CameraServerJNI.getSinkDescription(m_handle);
@@ -108,8 +115,7 @@
    * Get a property of the sink.
    *
    * @param name Property name
-   * @return Property (kind Property::kNone if no property with
-   *         the given name exists)
+   * @return Property (kind Property::kNone if no property with the given name exists)
    */
   public VideoProperty getProperty(String name) {
     return new VideoProperty(CameraServerJNI.getSinkProperty(m_handle, name));
@@ -117,8 +123,9 @@
 
   /**
    * Enumerate all properties of this sink.
+   *
+   * @return List of properties.
    */
-  @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
   public VideoProperty[] enumerateProperties() {
     int[] handles = CameraServerJNI.enumerateSinkProperties(m_handle);
     VideoProperty[] rv = new VideoProperty[handles.length];
@@ -161,9 +168,8 @@
   }
 
   /**
-   * Configure which source should provide frames to this sink.  Each sink
-   * can accept frames from only a single source, but a single source can
-   * provide frames to multiple clients.
+   * Configure which source should provide frames to this sink. Each sink can accept frames from
+   * only a single source, but a single source can provide frames to multiple clients.
    *
    * @param source Source
    */
@@ -190,12 +196,11 @@
    * Get a property of the associated source.
    *
    * @param name Property name
-   * @return Property (kind Property::kNone if no property with
-   *         the given name exists or no source connected)
+   * @return Property (kind Property::kNone if no property with the given name exists or no source
+   *     connected)
    */
   public VideoProperty getSourceProperty(String name) {
-    return new VideoProperty(
-        CameraServerJNI.getSinkSourceProperty(m_handle, name));
+    return new VideoProperty(CameraServerJNI.getSinkSourceProperty(m_handle, name));
   }
 
   /**
@@ -203,7 +208,6 @@
    *
    * @return Vector of sinks.
    */
-  @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
   public static VideoSink[] enumerateSinks() {
     int[] handles = CameraServerJNI.enumerateSinks();
     VideoSink[] rv = new VideoSink[handles.length];
diff --git a/cscore/src/main/java/edu/wpi/cscore/VideoSource.java b/cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java
similarity index 72%
rename from cscore/src/main/java/edu/wpi/cscore/VideoSource.java
rename to cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java
index 51d3821..0ae5add 100644
--- a/cscore/src/main/java/edu/wpi/cscore/VideoSource.java
+++ b/cscore/src/main/java/edu/wpi/first/cscore/VideoSource.java
@@ -1,22 +1,21 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
 
 /**
- * A source for video that provides a sequence of frames.  Each frame may
- * consist of multiple images (e.g. from a stereo or depth camera); these
- * are called channels.
+ * A source for video that provides a sequence of frames. Each frame may consist of multiple images
+ * (e.g. from a stereo or depth camera); these are called channels.
  */
 public class VideoSource implements AutoCloseable {
   public enum Kind {
-    kUnknown(0), kUsb(1), kHttp(2), kCv(4), kRaw(8);
+    kUnknown(0),
+    kUsb(1),
+    kHttp(2),
+    kCv(4),
+    kRaw(8);
 
-    @SuppressWarnings("MemberName")
     private final int value;
 
     Kind(int value) {
@@ -28,29 +27,22 @@
     }
   }
 
-  /**
-   * Connection strategy.
-   */
+  /** Connection strategy. */
   public enum ConnectionStrategy {
     /**
-     * Automatically connect or disconnect based on whether any sinks are
-     * connected to this source.  This is the default behavior.
+     * Automatically connect or disconnect based on whether any sinks are connected to this source.
+     * This is the default behavior.
      */
     kAutoManage(0),
 
-    /**
-     * Try to keep the connection open regardless of whether any sinks are
-     * connected.
-     */
+    /** Try to keep the connection open regardless of whether any sinks are connected. */
     kKeepOpen(1),
 
     /**
-     * Never open the connection.  If this is set when the connection is open,
-     * close the connection.
+     * Never open the connection. If this is set when the connection is open, close the connection.
      */
     kForceClose(2);
 
-    @SuppressWarnings("MemberName")
     private final int value;
 
     ConnectionStrategy(int value) {
@@ -70,10 +62,14 @@
    */
   public static Kind getKindFromInt(int kind) {
     switch (kind) {
-      case 1: return Kind.kUsb;
-      case 2: return Kind.kHttp;
-      case 4: return Kind.kCv;
-      default: return Kind.kUnknown;
+      case 1:
+        return Kind.kUsb;
+      case 2:
+        return Kind.kHttp;
+      case 4:
+        return Kind.kCv;
+      default:
+        return Kind.kUnknown;
     }
   }
 
@@ -119,21 +115,27 @@
 
   /**
    * Get the kind of the source.
+   *
+   * @return The kind of the source.
    */
   public Kind getKind() {
     return getKindFromInt(CameraServerJNI.getSourceKind(m_handle));
   }
 
   /**
-   * Get the name of the source.  The name is an arbitrary identifier
-   * provided when the source is created, and should be unique.
+   * Get the name of the source. The name is an arbitrary identifier provided when the source is
+   * created, and should be unique.
+   *
+   * @return The name of the source.
    */
   public String getName() {
     return CameraServerJNI.getSourceName(m_handle);
   }
 
   /**
-   * Get the source description.  This is source-kind specific.
+   * Get the source description. This is source-kind specific.
+   *
+   * @return The source description.
    */
   public String getDescription() {
     return CameraServerJNI.getSourceDescription(m_handle);
@@ -141,6 +143,7 @@
 
   /**
    * Get the last time a frame was captured.
+   *
    * @return Time in 1 us increments.
    */
   public long getLastFrameTime() {
@@ -148,12 +151,11 @@
   }
 
   /**
-   * Sets the connection strategy.  By default, the source will automatically
-   * connect or disconnect based on whether any sinks are connected.
+   * Sets the connection strategy. By default, the source will automatically connect or disconnect
+   * based on whether any sinks are connected.
    *
-   * <p>This function is non-blocking; look for either a connection open or
-   * close event or call {@link #isConnected()} to determine the connection
-   * state.
+   * <p>This function is non-blocking; look for either a connection open or close event or call
+   * {@link #isConnected()} to determine the connection state.
    *
    * @param strategy connection strategy (auto, keep open, or force close)
    */
@@ -162,15 +164,17 @@
   }
 
   /**
-   * Returns if the source currently connected to whatever is providing the images.
+   * Returns true if the source currently connected to whatever is providing the images.
+   *
+   * @return True if the source currently connected to whatever is providing the images.
    */
   public boolean isConnected() {
     return CameraServerJNI.isSourceConnected(m_handle);
   }
 
   /**
-   * Gets source enable status.  This is determined with a combination of
-   * connection strategy and the number of sinks connected.
+   * Gets source enable status. This is determined with a combination of connection strategy and the
+   * number of sinks connected.
    *
    * @return True if enabled, false otherwise.
    */
@@ -182,8 +186,7 @@
    * Get a property.
    *
    * @param name Property name
-   * @return Property contents (of kind Property::kNone if no property with
-   *         the given name exists)
+   * @return Property contents (of kind Property::kNone if no property with the given name exists)
    */
   public VideoProperty getProperty(String name) {
     return new VideoProperty(CameraServerJNI.getSourceProperty(m_handle, name));
@@ -191,8 +194,9 @@
 
   /**
    * Enumerate all properties of this source.
+   *
+   * @return Array of video properties.
    */
-  @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
   public VideoProperty[] enumerateProperties() {
     int[] handles = CameraServerJNI.enumerateSourceProperties(m_handle);
     VideoProperty[] rv = new VideoProperty[handles.length];
@@ -204,6 +208,8 @@
 
   /**
    * Get the current video mode.
+   *
+   * @return The current video mode.
    */
   public VideoMode getVideoMode() {
     return CameraServerJNI.getSourceVideoMode(m_handle);
@@ -211,14 +217,13 @@
 
   /**
    * Set the video mode.
+   *
    * @param mode Video mode
+   * @return True if set successfully.
    */
   public boolean setVideoMode(VideoMode mode) {
-    return CameraServerJNI.setSourceVideoMode(m_handle,
-        mode.pixelFormat.getValue(),
-        mode.width,
-        mode.height,
-        mode.fps);
+    return CameraServerJNI.setSourceVideoMode(
+        m_handle, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps);
   }
 
   /**
@@ -307,31 +312,33 @@
   /**
    * Get the actual FPS.
    *
-   * <p>CameraServerJNI#setTelemetryPeriod() must be called for this to be valid
-   * (throws VisionException if telemetry is not enabled).
+   * <p>CameraServerJNI#setTelemetryPeriod() must be called for this to be valid (throws
+   * VisionException if telemetry is not enabled).
    *
    * @return Actual FPS averaged over the telemetry period.
    */
   public double getActualFPS() {
-    return CameraServerJNI.getTelemetryAverageValue(m_handle,
-        CameraServerJNI.TelemetryKind.kSourceFramesReceived);
+    return CameraServerJNI.getTelemetryAverageValue(
+        m_handle, CameraServerJNI.TelemetryKind.kSourceFramesReceived);
   }
 
   /**
    * Get the data rate (in bytes per second).
    *
-   * <p>CameraServerJNI#setTelemetryPeriod() must be called for this to be valid
-   * (throws VisionException if telemetry is not enabled).
+   * <p>CameraServerJNI#setTelemetryPeriod() must be called for this to be valid (throws
+   * VisionException if telemetry is not enabled).
    *
    * @return Data rate averaged over the telemetry period.
    */
   public double getActualDataRate() {
-    return CameraServerJNI.getTelemetryAverageValue(m_handle,
-        CameraServerJNI.TelemetryKind.kSourceBytesReceived);
+    return CameraServerJNI.getTelemetryAverageValue(
+        m_handle, CameraServerJNI.TelemetryKind.kSourceBytesReceived);
   }
 
   /**
    * Enumerate all known video modes for this source.
+   *
+   * @return Vector of video modes.
    */
   public VideoMode[] enumerateVideoModes() {
     return CameraServerJNI.enumerateSourceVideoModes(m_handle);
@@ -342,7 +349,6 @@
    *
    * @return Vector of sinks.
    */
-  @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
   public VideoSink[] enumerateSinks() {
     int[] handles = CameraServerJNI.enumerateSourceSinks(m_handle);
     VideoSink[] rv = new VideoSink[handles.length];
@@ -357,7 +363,6 @@
    *
    * @return Vector of sources.
    */
-  @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
   public static VideoSource[] enumerateSources() {
     int[] handles = CameraServerJNI.enumerateSources();
     VideoSource[] rv = new VideoSource[handles.length];
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/raw/RawFrame.java b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawFrame.java
new file mode 100644
index 0000000..5f62481
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawFrame.java
@@ -0,0 +1,156 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore.raw;
+
+import edu.wpi.first.cscore.CameraServerJNI;
+import java.nio.ByteBuffer;
+
+/**
+ * Class for storing raw frame data between image read call.
+ *
+ * <p>Data is reused for each frame read, rather then reallocating every frame.
+ */
+public class RawFrame implements AutoCloseable {
+  private final long m_framePtr;
+  private ByteBuffer m_dataByteBuffer;
+  private long m_dataPtr;
+  private int m_totalData;
+  private int m_width;
+  private int m_height;
+  private int m_pixelFormat;
+
+  /** Construct a new RawFrame. */
+  public RawFrame() {
+    m_framePtr = CameraServerJNI.allocateRawFrame();
+  }
+
+  /**
+   * Close the RawFrame, releasing native resources. Any images currently using the data will be
+   * invalidated.
+   */
+  @Override
+  public void close() {
+    CameraServerJNI.freeRawFrame(m_framePtr);
+  }
+
+  /**
+   * Called from JNI to set data in class.
+   *
+   * @param dataByteBuffer A ByteBuffer pointing to the frame data.
+   * @param dataPtr A long (a char* in native code) pointing to the frame data.
+   * @param totalData The total length of the data stored in the frame.
+   * @param width The width of the frame.
+   * @param height The height of the frame.
+   * @param pixelFormat The PixelFormat of the frame.
+   */
+  public void setData(
+      ByteBuffer dataByteBuffer,
+      long dataPtr,
+      int totalData,
+      int width,
+      int height,
+      int pixelFormat) {
+    m_dataByteBuffer = dataByteBuffer;
+    m_dataPtr = dataPtr;
+    m_totalData = totalData;
+    m_width = width;
+    m_height = height;
+    m_pixelFormat = pixelFormat;
+  }
+
+  /**
+   * Get the pointer to native representation of this frame.
+   *
+   * @return The pointer to native representation of this frame.
+   */
+  public long getFramePtr() {
+    return m_framePtr;
+  }
+
+  /**
+   * Get a ByteBuffer pointing to the frame data. This ByteBuffer is backed by the frame directly.
+   * Its lifetime is controlled by the frame. If a new frame gets read, it will overwrite the
+   * current one.
+   *
+   * @return A ByteBuffer pointing to the frame data.
+   */
+  public ByteBuffer getDataByteBuffer() {
+    return m_dataByteBuffer;
+  }
+
+  /**
+   * Get a long (is a char* in native code) pointing to the frame data. This pointer is backed by
+   * the frame directly. Its lifetime is controlled by the frame. If a new frame gets read, it will
+   * overwrite the current one.
+   *
+   * @return A long pointing to the frame data.
+   */
+  public long getDataPtr() {
+    return m_dataPtr;
+  }
+
+  /**
+   * Get the total length of the data stored in the frame.
+   *
+   * @return The total length of the data stored in the frame.
+   */
+  public int getTotalData() {
+    return m_totalData;
+  }
+
+  /**
+   * Get the width of the frame.
+   *
+   * @return The width of the frame.
+   */
+  public int getWidth() {
+    return m_width;
+  }
+
+  /**
+   * Set the width of the frame.
+   *
+   * @param width The width of the frame.
+   */
+  public void setWidth(int width) {
+    this.m_width = width;
+  }
+
+  /**
+   * Get the height of the frame.
+   *
+   * @return The height of the frame.
+   */
+  public int getHeight() {
+    return m_height;
+  }
+
+  /**
+   * Set the height of the frame.
+   *
+   * @param height The height of the frame.
+   */
+  public void setHeight(int height) {
+    this.m_height = height;
+  }
+
+  /**
+   * Get the PixelFormat of the frame.
+   *
+   * @return The PixelFormat of the frame.
+   */
+  public int getPixelFormat() {
+    return m_pixelFormat;
+  }
+
+  /**
+   * Set the PixelFormat of the frame.
+   *
+   * @param pixelFormat The PixelFormat of the frame.
+   */
+  public void setPixelFormat(int pixelFormat) {
+    this.m_pixelFormat = pixelFormat;
+  }
+}
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSink.java b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSink.java
new file mode 100644
index 0000000..ae5b0ef
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSink.java
@@ -0,0 +1,63 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore.raw;
+
+import edu.wpi.first.cscore.CameraServerJNI;
+import edu.wpi.first.cscore.ImageSink;
+
+/**
+ * A sink for user code to accept video frames as raw bytes.
+ *
+ * <p>This is a complex API, most cases should use CvSink.
+ */
+public class RawSink extends ImageSink {
+  /**
+   * Create a sink for accepting raw images.
+   *
+   * <p>grabFrame() must be called on the created sink to get each new image.
+   *
+   * @param name Source name (arbitrary unique identifier)
+   */
+  public RawSink(String name) {
+    super(CameraServerJNI.createRawSink(name));
+  }
+
+  /**
+   * Wait for the next frame and get the image. Times out (returning 0) after 0.225 seconds. The
+   * provided image will have three 8-bit channels stored in BGR order.
+   *
+   * @param frame The frame object in which to store the image.
+   * @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
+   *     is in the same time base as wpi::Now(), and is in 1 us increments.
+   */
+  protected long grabFrame(RawFrame frame) {
+    return grabFrame(frame, 0.225);
+  }
+
+  /**
+   * Wait for the next frame and get the image. Times out (returning 0) after timeout seconds. The
+   * provided image will have three 8-bit channels stored in BGR order.
+   *
+   * @param frame The frame object in which to store the image.
+   * @param timeout The frame timeout in seconds.
+   * @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
+   *     is in the same time base as wpi::Now(), and is in 1 us increments.
+   */
+  protected long grabFrame(RawFrame frame, double timeout) {
+    return CameraServerJNI.grabSinkFrameTimeout(m_handle, frame, timeout);
+  }
+
+  /**
+   * Wait for the next frame and get the image. May block forever. The provided image will have
+   * three 8-bit channels stored in BGR order.
+   *
+   * @param frame The frame object in which to store the image.
+   * @return Frame time, or 0 on error (call getError() to obtain the error message); the frame time
+   *     is in the same time base as wpi::Now(), and is in 1 us increments.
+   */
+  protected long grabFrameNoTimeout(RawFrame frame) {
+    return CameraServerJNI.grabSinkFrame(m_handle, frame);
+  }
+}
diff --git a/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSource.java b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSource.java
new file mode 100644
index 0000000..f1be050
--- /dev/null
+++ b/cscore/src/main/java/edu/wpi/first/cscore/raw/RawSource.java
@@ -0,0 +1,78 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore.raw;
+
+import edu.wpi.first.cscore.CameraServerJNI;
+import edu.wpi.first.cscore.ImageSource;
+import edu.wpi.first.cscore.VideoMode;
+
+/**
+ * A source for user code to provide video frames as raw bytes.
+ *
+ * <p>This is a complex API, most cases should use CvSource.
+ */
+public class RawSource extends ImageSource {
+  /**
+   * Create a raw frame source.
+   *
+   * @param name Source name (arbitrary unique identifier)
+   * @param mode Video mode being generated
+   */
+  public RawSource(String name, VideoMode mode) {
+    super(
+        CameraServerJNI.createRawSource(
+            name, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps));
+  }
+
+  /**
+   * Create a raw frame source.
+   *
+   * @param name Source name (arbitrary unique identifier)
+   * @param pixelFormat Pixel format
+   * @param width width
+   * @param height height
+   * @param fps fps
+   */
+  public RawSource(String name, VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
+    super(CameraServerJNI.createRawSource(name, pixelFormat.getValue(), width, height, fps));
+  }
+
+  /**
+   * Put a raw image and notify sinks.
+   *
+   * @param image raw frame image
+   */
+  protected void putFrame(RawFrame image) {
+    CameraServerJNI.putRawSourceFrame(m_handle, image);
+  }
+
+  /**
+   * Put a raw image and notify sinks.
+   *
+   * @param data raw frame data pointer
+   * @param width frame width
+   * @param height frame height
+   * @param pixelFormat pixel format
+   * @param totalData length of data in total
+   */
+  protected void putFrame(long data, int width, int height, int pixelFormat, int totalData) {
+    CameraServerJNI.putRawSourceFrame(m_handle, data, width, height, pixelFormat, totalData);
+  }
+
+  /**
+   * Put a raw image and notify sinks.
+   *
+   * @param data raw frame data pointer
+   * @param width frame width
+   * @param height frame height
+   * @param pixelFormat pixel format
+   * @param totalData length of data in total
+   */
+  protected void putFrame(
+      long data, int width, int height, VideoMode.PixelFormat pixelFormat, int totalData) {
+    CameraServerJNI.putRawSourceFrame(
+        m_handle, data, width, height, pixelFormat.getValue(), totalData);
+  }
+}
diff --git a/cscore/src/main/native/cpp/ConfigurableSourceImpl.cpp b/cscore/src/main/native/cpp/ConfigurableSourceImpl.cpp
index 3ba1784..16452f1 100644
--- a/cscore/src/main/native/cpp/ConfigurableSourceImpl.cpp
+++ b/cscore/src/main/native/cpp/ConfigurableSourceImpl.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "ConfigurableSourceImpl.h"
 
@@ -16,7 +13,7 @@
 
 using namespace cs;
 
-ConfigurableSourceImpl::ConfigurableSourceImpl(const wpi::Twine& name,
+ConfigurableSourceImpl::ConfigurableSourceImpl(std::string_view name,
                                                wpi::Logger& logger,
                                                Notifier& notifier,
                                                Telemetry& telemetry,
@@ -26,7 +23,7 @@
   m_videoModes.push_back(m_mode);
 }
 
-ConfigurableSourceImpl::~ConfigurableSourceImpl() {}
+ConfigurableSourceImpl::~ConfigurableSourceImpl() = default;
 
 void ConfigurableSourceImpl::Start() {
   m_notifier.NotifySource(*this, CS_SOURCE_CONNECTED);
@@ -53,11 +50,11 @@
   // ignore
 }
 
-void ConfigurableSourceImpl::NotifyError(const wpi::Twine& msg) {
+void ConfigurableSourceImpl::NotifyError(std::string_view msg) {
   PutError(msg, wpi::Now());
 }
 
-int ConfigurableSourceImpl::CreateProperty(const wpi::Twine& name,
+int ConfigurableSourceImpl::CreateProperty(std::string_view name,
                                            CS_PropertyKind kind, int minimum,
                                            int maximum, int step,
                                            int defaultValue, int value) {
@@ -78,12 +75,12 @@
         value = prop.value;
       });
   m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name, ndx,
-                                  kind, value, wpi::Twine{});
+                                  kind, value, {});
   return ndx;
 }
 
 int ConfigurableSourceImpl::CreateProperty(
-    const wpi::Twine& name, CS_PropertyKind kind, int minimum, int maximum,
+    std::string_view name, CS_PropertyKind kind, int minimum, int maximum,
     int step, int defaultValue, int value,
     std::function<void(CS_Property property)> onChange) {
   // TODO
@@ -91,7 +88,7 @@
 }
 
 void ConfigurableSourceImpl::SetEnumPropertyChoices(
-    int property, wpi::ArrayRef<std::string> choices, CS_Status* status) {
+    int property, wpi::span<const std::string> choices, CS_Status* status) {
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
   if (!prop) {
@@ -102,8 +99,8 @@
     *status = CS_WRONG_PROPERTY_TYPE;
     return;
   }
-  prop->enumChoices = choices;
+  prop->enumChoices.assign(choices.begin(), choices.end());
   m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
                                   prop->name, property, CS_PROP_ENUM,
-                                  prop->value, wpi::Twine{});
+                                  prop->value, {});
 }
diff --git a/cscore/src/main/native/cpp/ConfigurableSourceImpl.h b/cscore/src/main/native/cpp/ConfigurableSourceImpl.h
index a10f27e..bb9f3ed 100644
--- a/cscore/src/main/native/cpp/ConfigurableSourceImpl.h
+++ b/cscore/src/main/native/cpp/ConfigurableSourceImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CONFIGURABLESOURCEIMPL_H_
 #define CSCORE_CONFIGURABLESOURCEIMPL_H_
@@ -12,10 +9,10 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
-#include <wpi/ArrayRef.h>
-#include <wpi/Twine.h>
+#include <wpi/span.h>
 
 #include "SourceImpl.h"
 
@@ -23,7 +20,7 @@
 
 class ConfigurableSourceImpl : public SourceImpl {
  protected:
-  ConfigurableSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
+  ConfigurableSourceImpl(std::string_view name, wpi::Logger& logger,
                          Notifier& notifier, Telemetry& telemetry,
                          const VideoMode& mode);
 
@@ -38,13 +35,14 @@
   void NumSinksEnabledChanged() override;
 
   // OpenCV-specific functions
-  void NotifyError(const wpi::Twine& msg);
-  int CreateProperty(const wpi::Twine& name, CS_PropertyKind kind, int minimum,
+  void NotifyError(std::string_view msg);
+  int CreateProperty(std::string_view name, CS_PropertyKind kind, int minimum,
                      int maximum, int step, int defaultValue, int value);
-  int CreateProperty(const wpi::Twine& name, CS_PropertyKind kind, int minimum,
+  int CreateProperty(std::string_view name, CS_PropertyKind kind, int minimum,
                      int maximum, int step, int defaultValue, int value,
                      std::function<void(CS_Property property)> onChange);
-  void SetEnumPropertyChoices(int property, wpi::ArrayRef<std::string> choices,
+  void SetEnumPropertyChoices(int property,
+                              wpi::span<const std::string> choices,
                               CS_Status* status);
 
  private:
diff --git a/cscore/src/main/native/cpp/CvSinkImpl.cpp b/cscore/src/main/native/cpp/CvSinkImpl.cpp
index 7ba505a..dbda6c6 100644
--- a/cscore/src/main/native/cpp/CvSinkImpl.cpp
+++ b/cscore/src/main/native/cpp/CvSinkImpl.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "CvSinkImpl.h"
 
@@ -21,28 +18,34 @@
 
 using namespace cs;
 
-CvSinkImpl::CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
+CvSinkImpl::CvSinkImpl(std::string_view name, wpi::Logger& logger,
                        Notifier& notifier, Telemetry& telemetry)
     : SinkImpl{name, logger, notifier, telemetry} {
   m_active = true;
   // m_thread = std::thread(&CvSinkImpl::ThreadMain, this);
 }
 
-CvSinkImpl::CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
+CvSinkImpl::CvSinkImpl(std::string_view name, wpi::Logger& logger,
                        Notifier& notifier, Telemetry& telemetry,
                        std::function<void(uint64_t time)> processFrame)
     : SinkImpl{name, logger, notifier, telemetry} {}
 
-CvSinkImpl::~CvSinkImpl() { Stop(); }
+CvSinkImpl::~CvSinkImpl() {
+  Stop();
+}
 
 void CvSinkImpl::Stop() {
   m_active = false;
 
   // wake up any waiters by forcing an empty frame to be sent
-  if (auto source = GetSource()) source->Wakeup();
+  if (auto source = GetSource()) {
+    source->Wakeup();
+  }
 
   // join thread
-  if (m_thread.joinable()) m_thread.join();
+  if (m_thread.joinable()) {
+    m_thread.join();
+  }
 }
 
 uint64_t CvSinkImpl::GrabFrame(cv::Mat& image) {
@@ -107,9 +110,11 @@
       std::this_thread::sleep_for(std::chrono::seconds(1));
       continue;
     }
-    SDEBUG4("waiting for frame");
+    SDEBUG4("{}", "waiting for frame");
     Frame frame = source->GetNextFrame();  // blocks
-    if (!m_active) break;
+    if (!m_active) {
+      break;
+    }
     if (!frame) {
       // Bad frame; sleep for 10 ms so we don't consume all processor time.
       std::this_thread::sleep_for(std::chrono::milliseconds(10));
@@ -122,14 +127,14 @@
 
 namespace cs {
 
-CS_Sink CreateCvSink(const wpi::Twine& name, CS_Status* status) {
+CS_Sink CreateCvSink(std::string_view name, CS_Status* status) {
   auto& inst = Instance::GetInstance();
   return inst.CreateSink(
       CS_SINK_CV, std::make_shared<CvSinkImpl>(name, inst.logger, inst.notifier,
                                                inst.telemetry));
 }
 
-CS_Sink CreateCvSinkCallback(const wpi::Twine& name,
+CS_Sink CreateCvSinkCallback(std::string_view name,
                              std::function<void(uint64_t time)> processFrame,
                              CS_Status* status) {
   auto& inst = Instance::GetInstance();
@@ -140,7 +145,7 @@
 
 static constexpr unsigned SinkMask = CS_SINK_CV | CS_SINK_RAW;
 
-void SetSinkDescription(CS_Sink sink, const wpi::Twine& description,
+void SetSinkDescription(CS_Sink sink, std::string_view description,
                         CS_Status* status) {
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data || (data->kind & SinkMask) == 0) {
@@ -178,12 +183,12 @@
   return static_cast<CvSinkImpl&>(*data->sink).GetError();
 }
 
-wpi::StringRef GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
-                            CS_Status* status) {
+std::string_view GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
+                              CS_Status* status) {
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data || (data->kind & SinkMask) == 0) {
     *status = CS_INVALID_HANDLE;
-    return wpi::StringRef{};
+    return {};
   }
   return static_cast<CvSinkImpl&>(*data->sink).GetError(buf);
 }
@@ -243,7 +248,9 @@
 char* CS_GetSinkError(CS_Sink sink, CS_Status* status) {
   wpi::SmallString<128> buf;
   auto str = cs::GetSinkError(sink, buf, status);
-  if (*status != 0) return nullptr;
+  if (*status != 0) {
+    return nullptr;
+  }
   return cs::ConvertToC(str);
 }
 
diff --git a/cscore/src/main/native/cpp/CvSinkImpl.h b/cscore/src/main/native/cpp/CvSinkImpl.h
index 9b7820f..ad63a20 100644
--- a/cscore/src/main/native/cpp/CvSinkImpl.h
+++ b/cscore/src/main/native/cpp/CvSinkImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CVSINKIMPL_H_
 #define CSCORE_CVSINKIMPL_H_
@@ -12,10 +9,10 @@
 
 #include <atomic>
 #include <functional>
+#include <string_view>
 #include <thread>
 
 #include <opencv2/core/core.hpp>
-#include <wpi/Twine.h>
 #include <wpi/condition_variable.h>
 
 #include "Frame.h"
@@ -27,9 +24,9 @@
 
 class CvSinkImpl : public SinkImpl {
  public:
-  CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
+  CvSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
              Telemetry& telemetry);
-  CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
+  CvSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
              Telemetry& telemetry,
              std::function<void(uint64_t time)> processFrame);
   ~CvSinkImpl() override;
diff --git a/cscore/src/main/native/cpp/CvSourceImpl.cpp b/cscore/src/main/native/cpp/CvSourceImpl.cpp
index 49b9d28..7cd4fe0 100644
--- a/cscore/src/main/native/cpp/CvSourceImpl.cpp
+++ b/cscore/src/main/native/cpp/CvSourceImpl.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "CvSourceImpl.h"
 
 #include <opencv2/core/core.hpp>
 #include <opencv2/highgui/highgui.hpp>
 #include <opencv2/imgproc/imgproc.hpp>
-#include <wpi/STLExtras.h>
 #include <wpi/timestamp.h>
 
 #include "Handle.h"
@@ -22,20 +18,21 @@
 
 using namespace cs;
 
-CvSourceImpl::CvSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
+CvSourceImpl::CvSourceImpl(std::string_view name, wpi::Logger& logger,
                            Notifier& notifier, Telemetry& telemetry,
                            const VideoMode& mode)
     : ConfigurableSourceImpl{name, logger, notifier, telemetry, mode} {}
 
-CvSourceImpl::~CvSourceImpl() {}
+CvSourceImpl::~CvSourceImpl() = default;
 
 void CvSourceImpl::PutFrame(cv::Mat& image) {
   // We only support 8-bit images; convert if necessary.
   cv::Mat finalImage;
-  if (image.depth() == CV_8U)
+  if (image.depth() == CV_8U) {
     finalImage = image;
-  else
+  } else {
     image.convertTo(finalImage, CV_8U);
+  }
 
   std::unique_ptr<Image> dest;
   switch (image.channels()) {
@@ -55,8 +52,7 @@
       cv::cvtColor(finalImage, dest->AsMat(), cv::COLOR_BGRA2BGR);
       break;
     default:
-      SERROR("PutFrame: " << image.channels()
-                          << "-channel images not supported");
+      SERROR("PutFrame: {}-channel images not supported", image.channels());
       return;
   }
   SourceImpl::PutFrame(std::move(dest), wpi::Now());
@@ -64,7 +60,7 @@
 
 namespace cs {
 
-CS_Source CreateCvSource(const wpi::Twine& name, const VideoMode& mode,
+CS_Source CreateCvSource(std::string_view name, const VideoMode& mode,
                          CS_Status* status) {
   auto& inst = Instance::GetInstance();
   return inst.CreateSource(CS_SOURCE_CV, std::make_shared<CvSourceImpl>(
@@ -83,7 +79,7 @@
 
 static constexpr unsigned SourceMask = CS_SINK_CV | CS_SINK_RAW;
 
-void NotifySourceError(CS_Source source, const wpi::Twine& msg,
+void NotifySourceError(CS_Source source, std::string_view msg,
                        CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data || (data->kind & SourceMask) == 0) {
@@ -102,7 +98,7 @@
   static_cast<CvSourceImpl&>(*data->source).SetConnected(connected);
 }
 
-void SetSourceDescription(CS_Source source, const wpi::Twine& description,
+void SetSourceDescription(CS_Source source, std::string_view description,
                           CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data || (data->kind & SourceMask) == 0) {
@@ -112,7 +108,7 @@
   static_cast<CvSourceImpl&>(*data->source).SetDescription(description);
 }
 
-CS_Property CreateSourceProperty(CS_Source source, const wpi::Twine& name,
+CS_Property CreateSourceProperty(CS_Source source, std::string_view name,
                                  CS_PropertyKind kind, int minimum, int maximum,
                                  int step, int defaultValue, int value,
                                  CS_Status* status) {
@@ -128,7 +124,7 @@
 }
 
 CS_Property CreateSourcePropertyCallback(
-    CS_Source source, const wpi::Twine& name, CS_PropertyKind kind, int minimum,
+    CS_Source source, std::string_view name, CS_PropertyKind kind, int minimum,
     int maximum, int step, int defaultValue, int value,
     std::function<void(CS_Property property)> onChange, CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
@@ -143,7 +139,7 @@
 }
 
 void SetSourceEnumPropertyChoices(CS_Source source, CS_Property property,
-                                  wpi::ArrayRef<std::string> choices,
+                                  wpi::span<const std::string> choices,
                                   CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data || (data->kind & SourceMask) == 0) {
@@ -227,7 +223,9 @@
                                      CS_Status* status) {
   wpi::SmallVector<std::string, 8> vec;
   vec.reserve(count);
-  for (int i = 0; i < count; ++i) vec.push_back(choices[i]);
+  for (int i = 0; i < count; ++i) {
+    vec.push_back(choices[i]);
+  }
   return cs::SetSourceEnumPropertyChoices(source, property, vec, status);
 }
 
diff --git a/cscore/src/main/native/cpp/CvSourceImpl.h b/cscore/src/main/native/cpp/CvSourceImpl.h
index 978d012..fba7131 100644
--- a/cscore/src/main/native/cpp/CvSourceImpl.h
+++ b/cscore/src/main/native/cpp/CvSourceImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CVSOURCEIMPL_H_
 #define CSCORE_CVSOURCEIMPL_H_
@@ -12,11 +9,10 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <opencv2/core/core.hpp>
-#include <wpi/ArrayRef.h>
-#include <wpi/Twine.h>
 
 #include "ConfigurableSourceImpl.h"
 #include "SourceImpl.h"
@@ -25,7 +21,7 @@
 
 class CvSourceImpl : public ConfigurableSourceImpl {
  public:
-  CvSourceImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
+  CvSourceImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
                Telemetry& telemetry, const VideoMode& mode);
   ~CvSourceImpl() override;
 
diff --git a/cscore/src/main/native/cpp/Frame.cpp b/cscore/src/main/native/cpp/Frame.cpp
index 466cb52..0aa1947 100644
--- a/cscore/src/main/native/cpp/Frame.cpp
+++ b/cscore/src/main/native/cpp/Frame.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "Frame.h"
 
@@ -19,10 +16,10 @@
 
 using namespace cs;
 
-Frame::Frame(SourceImpl& source, const wpi::Twine& error, Time time)
+Frame::Frame(SourceImpl& source, std::string_view error, Time time)
     : m_impl{source.AllocFrameImpl().release()} {
   m_impl->refcount = 1;
-  m_impl->error = error.str();
+  m_impl->error = error;
   m_impl->time = time;
 }
 
@@ -35,22 +32,31 @@
 }
 
 Image* Frame::GetNearestImage(int width, int height) const {
-  if (!m_impl) return nullptr;
+  if (!m_impl) {
+    return nullptr;
+  }
   std::scoped_lock lock(m_impl->mutex);
   Image* found = nullptr;
 
   // Ideally we want the smallest image at least width/height in size
   for (auto i : m_impl->images) {
-    if (i->IsLarger(width, height) && (!found || (i->IsSmaller(*found))))
+    if (i->IsLarger(width, height) && (!found || (i->IsSmaller(*found)))) {
       found = i;
+    }
   }
-  if (found) return found;
+  if (found) {
+    return found;
+  }
 
   // Find the largest image (will be less than width/height)
   for (auto i : m_impl->images) {
-    if (!found || (i->IsLarger(*found))) found = i;
+    if (!found || (i->IsLarger(*found))) {
+      found = i;
+    }
   }
-  if (found) return found;
+  if (found) {
+    return found;
+  }
 
   // Shouldn't reach this, but just in case...
   return m_impl->images.empty() ? nullptr : m_impl->images[0];
@@ -59,7 +65,9 @@
 Image* Frame::GetNearestImage(int width, int height,
                               VideoMode::PixelFormat pixelFormat,
                               int jpegQuality) const {
-  if (!m_impl) return nullptr;
+  if (!m_impl) {
+    return nullptr;
+  }
   std::scoped_lock lock(m_impl->mutex);
   Image* found = nullptr;
 
@@ -74,19 +82,25 @@
   // 1) Same width, height, pixelFormat, and (possibly) JPEG quality
   //    (e.g. exactly what we want)
   for (auto i : m_impl->images) {
-    if (i->Is(width, height, pixelFormat, jpegQuality)) return i;
+    if (i->Is(width, height, pixelFormat, jpegQuality)) {
+      return i;
+    }
   }
 
   // 2) Same width, height, different (but non-JPEG) pixelFormat (color conv)
   // 2a) If we want JPEG output, prefer BGR over other pixel formats
   if (pixelFormat == VideoMode::kMJPEG) {
     for (auto i : m_impl->images) {
-      if (i->Is(width, height, VideoMode::kBGR)) return i;
+      if (i->Is(width, height, VideoMode::kBGR)) {
+        return i;
+      }
     }
   }
 
   for (auto i : m_impl->images) {
-    if (i->Is(width, height) && i->pixelFormat != VideoMode::kMJPEG) return i;
+    if (i->Is(width, height) && i->pixelFormat != VideoMode::kMJPEG) {
+      return i;
+    }
   }
 
   // 3) Different width, height, same pixelFormat (only if non-JPEG) (resample)
@@ -94,17 +108,23 @@
     // 3a) Smallest image at least width/height in size
     for (auto i : m_impl->images) {
       if (i->IsLarger(width, height) && i->pixelFormat == pixelFormat &&
-          (!found || (i->IsSmaller(*found))))
+          (!found || (i->IsSmaller(*found)))) {
         found = i;
+      }
     }
-    if (found) return found;
+    if (found) {
+      return found;
+    }
 
     // 3b) Largest image (less than width/height)
     for (auto i : m_impl->images) {
-      if (i->pixelFormat == pixelFormat && (!found || (i->IsLarger(*found))))
+      if (i->pixelFormat == pixelFormat && (!found || (i->IsLarger(*found)))) {
         found = i;
+      }
     }
-    if (found) return found;
+    if (found) {
+      return found;
+    }
   }
 
   // 4) Different width, height, different (but non-JPEG) pixelFormat
@@ -112,18 +132,24 @@
   // 4a) Smallest image at least width/height in size
   for (auto i : m_impl->images) {
     if (i->IsLarger(width, height) && i->pixelFormat != VideoMode::kMJPEG &&
-        (!found || (i->IsSmaller(*found))))
+        (!found || (i->IsSmaller(*found)))) {
       found = i;
+    }
   }
-  if (found) return found;
+  if (found) {
+    return found;
+  }
 
   // 4b) Largest image (less than width/height)
   for (auto i : m_impl->images) {
     if (i->pixelFormat != VideoMode::kMJPEG &&
-        (!found || (i->IsLarger(*found))))
+        (!found || (i->IsLarger(*found)))) {
       found = i;
+    }
   }
-  if (found) return found;
+  if (found) {
+    return found;
+  }
 
   // 5) Same width, height, JPEG pixelFormat (decompression).  As there may be
   //    multiple JPEG images, find the highest quality one.
@@ -133,27 +159,37 @@
       found = i;
       // consider one without a quality setting to be the highest quality
       // (e.g. directly from the camera)
-      if (i->jpegQuality == -1) break;
+      if (i->jpegQuality == -1) {
+        break;
+      }
     }
   }
-  if (found) return found;
+  if (found) {
+    return found;
+  }
 
   // 6) Different width, height, JPEG pixelFormat (decompression)
   // 6a) Smallest image at least width/height in size
   for (auto i : m_impl->images) {
     if (i->IsLarger(width, height) && i->pixelFormat == VideoMode::kMJPEG &&
-        (!found || (i->IsSmaller(*found))))
+        (!found || (i->IsSmaller(*found)))) {
       found = i;
+    }
   }
-  if (found) return found;
+  if (found) {
+    return found;
+  }
 
   // 6b) Largest image (less than width/height)
   for (auto i : m_impl->images) {
     if (i->pixelFormat != VideoMode::kMJPEG &&
-        (!found || (i->IsLarger(*found))))
+        (!found || (i->IsLarger(*found)))) {
       found = i;
+    }
   }
-  if (found) return found;
+  if (found) {
+    return found;
+  }
 
   // Shouldn't reach this, but just in case...
   return m_impl->images.empty() ? nullptr : m_impl->images[0];
@@ -161,9 +197,10 @@
 
 Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
                           int requiredJpegQuality, int defaultJpegQuality) {
-  if (!image ||
-      image->Is(image->width, image->height, pixelFormat, requiredJpegQuality))
+  if (!image || image->Is(image->width, image->height, pixelFormat,
+                          requiredJpegQuality)) {
     return image;
+  }
   Image* cur = image;
 
   // If the source image is a JPEG, we need to decode it before we can do
@@ -172,7 +209,9 @@
   // would have returned above).
   if (cur->pixelFormat == VideoMode::kMJPEG) {
     cur = ConvertMJPEGToBGR(cur);
-    if (pixelFormat == VideoMode::kBGR) return cur;
+    if (pixelFormat == VideoMode::kBGR) {
+      return cur;
+    }
   }
 
   // Color convert
@@ -182,17 +221,19 @@
       if (cur->pixelFormat == VideoMode::kYUYV) {
         // Check to see if BGR version already exists...
         if (Image* newImage =
-                GetExistingImage(cur->width, cur->height, VideoMode::kBGR))
+                GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
           cur = newImage;
-        else
+        } else {
           cur = ConvertYUYVToBGR(cur);
+        }
       } else if (cur->pixelFormat == VideoMode::kGray) {
         // Check to see if BGR version already exists...
         if (Image* newImage =
-                GetExistingImage(cur->width, cur->height, VideoMode::kBGR))
+                GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
           cur = newImage;
-        else
+        } else {
           cur = ConvertGrayToBGR(cur);
+        }
       }
       return ConvertBGRToRGB565(cur);
     case VideoMode::kGray:
@@ -200,17 +241,19 @@
       if (cur->pixelFormat == VideoMode::kYUYV) {
         // Check to see if BGR version already exists...
         if (Image* newImage =
-                GetExistingImage(cur->width, cur->height, VideoMode::kBGR))
+                GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
           cur = newImage;
-        else
+        } else {
           cur = ConvertYUYVToBGR(cur);
+        }
       } else if (cur->pixelFormat == VideoMode::kRGB565) {
         // Check to see if BGR version already exists...
         if (Image* newImage =
-                GetExistingImage(cur->width, cur->height, VideoMode::kBGR))
+                GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
           cur = newImage;
-        else
+        } else {
           cur = ConvertRGB565ToBGR(cur);
+        }
       }
       return ConvertBGRToGray(cur);
     case VideoMode::kBGR:
@@ -220,10 +263,11 @@
       } else if (cur->pixelFormat == VideoMode::kRGB565) {
         cur = ConvertRGB565ToBGR(cur);
       } else if (cur->pixelFormat == VideoMode::kGray) {
-        if (pixelFormat == VideoMode::kBGR)
+        if (pixelFormat == VideoMode::kBGR) {
           return ConvertGrayToBGR(cur);
-        else
+        } else {
           return ConvertGrayToMJPEG(cur, defaultJpegQuality);
+        }
       }
       break;
     case VideoMode::kYUYV:
@@ -232,14 +276,17 @@
   }
 
   // Compress if destination is JPEG
-  if (pixelFormat == VideoMode::kMJPEG)
+  if (pixelFormat == VideoMode::kMJPEG) {
     cur = ConvertBGRToMJPEG(cur, defaultJpegQuality);
+  }
 
   return cur;
 }
 
 Image* Frame::ConvertMJPEGToBGR(Image* image) {
-  if (!image || image->pixelFormat != VideoMode::kMJPEG) return nullptr;
+  if (!image || image->pixelFormat != VideoMode::kMJPEG) {
+    return nullptr;
+  }
 
   // Allocate an BGR image
   auto newImage =
@@ -260,7 +307,9 @@
 }
 
 Image* Frame::ConvertMJPEGToGray(Image* image) {
-  if (!image || image->pixelFormat != VideoMode::kMJPEG) return nullptr;
+  if (!image || image->pixelFormat != VideoMode::kMJPEG) {
+    return nullptr;
+  }
 
   // Allocate an grayscale image
   auto newImage =
@@ -281,7 +330,9 @@
 }
 
 Image* Frame::ConvertYUYVToBGR(Image* image) {
-  if (!image || image->pixelFormat != VideoMode::kYUYV) return nullptr;
+  if (!image || image->pixelFormat != VideoMode::kYUYV) {
+    return nullptr;
+  }
 
   // Allocate a BGR image
   auto newImage =
@@ -301,7 +352,9 @@
 }
 
 Image* Frame::ConvertBGRToRGB565(Image* image) {
-  if (!image || image->pixelFormat != VideoMode::kBGR) return nullptr;
+  if (!image || image->pixelFormat != VideoMode::kBGR) {
+    return nullptr;
+  }
 
   // Allocate a RGB565 image
   auto newImage =
@@ -321,7 +374,9 @@
 }
 
 Image* Frame::ConvertRGB565ToBGR(Image* image) {
-  if (!image || image->pixelFormat != VideoMode::kRGB565) return nullptr;
+  if (!image || image->pixelFormat != VideoMode::kRGB565) {
+    return nullptr;
+  }
 
   // Allocate a BGR image
   auto newImage =
@@ -341,7 +396,9 @@
 }
 
 Image* Frame::ConvertBGRToGray(Image* image) {
-  if (!image || image->pixelFormat != VideoMode::kBGR) return nullptr;
+  if (!image || image->pixelFormat != VideoMode::kBGR) {
+    return nullptr;
+  }
 
   // Allocate a Grayscale image
   auto newImage =
@@ -361,7 +418,9 @@
 }
 
 Image* Frame::ConvertGrayToBGR(Image* image) {
-  if (!image || image->pixelFormat != VideoMode::kGray) return nullptr;
+  if (!image || image->pixelFormat != VideoMode::kGray) {
+    return nullptr;
+  }
 
   // Allocate a BGR image
   auto newImage =
@@ -381,8 +440,12 @@
 }
 
 Image* Frame::ConvertBGRToMJPEG(Image* image, int quality) {
-  if (!image || image->pixelFormat != VideoMode::kBGR) return nullptr;
-  if (!m_impl) return nullptr;
+  if (!image || image->pixelFormat != VideoMode::kBGR) {
+    return nullptr;
+  }
+  if (!m_impl) {
+    return nullptr;
+  }
   std::scoped_lock lock(m_impl->mutex);
 
   // Allocate a JPEG image.  We don't actually know what the resulting size
@@ -412,8 +475,12 @@
 }
 
 Image* Frame::ConvertGrayToMJPEG(Image* image, int quality) {
-  if (!image || image->pixelFormat != VideoMode::kGray) return nullptr;
-  if (!m_impl) return nullptr;
+  if (!image || image->pixelFormat != VideoMode::kGray) {
+    return nullptr;
+  }
+  if (!m_impl) {
+    return nullptr;
+  }
   std::scoped_lock lock(m_impl->mutex);
 
   // Allocate a JPEG image.  We don't actually know what the resulting size
@@ -445,23 +512,26 @@
 Image* Frame::GetImageImpl(int width, int height,
                            VideoMode::PixelFormat pixelFormat,
                            int requiredJpegQuality, int defaultJpegQuality) {
-  if (!m_impl) return nullptr;
+  if (!m_impl) {
+    return nullptr;
+  }
   std::scoped_lock lock(m_impl->mutex);
   Image* cur = GetNearestImage(width, height, pixelFormat, requiredJpegQuality);
-  if (!cur || cur->Is(width, height, pixelFormat, requiredJpegQuality))
+  if (!cur || cur->Is(width, height, pixelFormat, requiredJpegQuality)) {
     return cur;
+  }
 
   WPI_DEBUG4(Instance::GetInstance().logger,
-             "converting image from " << cur->width << "x" << cur->height
-                                      << " type " << cur->pixelFormat << " to "
-                                      << width << "x" << height << " type "
-                                      << pixelFormat);
+             "converting image from {}x{} type {} to {}x{} type {}", cur->width,
+             cur->height, cur->pixelFormat, width, height, pixelFormat);
 
   // If the source image is a JPEG, we need to decode it before we can do
   // anything else with it.  Note that if the destination format is JPEG, we
   // still need to do this (unless the width/height/compression were the same,
   // in which case we already returned the existing JPEG above).
-  if (cur->pixelFormat == VideoMode::kMJPEG) cur = ConvertMJPEGToBGR(cur);
+  if (cur->pixelFormat == VideoMode::kMJPEG) {
+    cur = ConvertMJPEGToBGR(cur);
+  }
 
   // Resize
   if (!cur->Is(width, height)) {
@@ -485,14 +555,17 @@
 
 bool Frame::GetCv(cv::Mat& image, int width, int height) {
   Image* rawImage = GetImage(width, height, VideoMode::kBGR);
-  if (!rawImage) return false;
+  if (!rawImage) {
+    return false;
+  }
   rawImage->AsMat().copyTo(image);
   return true;
 }
 
 void Frame::ReleaseFrame() {
-  for (auto image : m_impl->images)
+  for (auto image : m_impl->images) {
     m_impl->source.ReleaseImage(std::unique_ptr<Image>(image));
+  }
   m_impl->images.clear();
   m_impl->source.ReleaseFrameImpl(std::unique_ptr<Impl>(m_impl));
   m_impl = nullptr;
diff --git a/cscore/src/main/native/cpp/Frame.h b/cscore/src/main/native/cpp/Frame.h
index 07fa24f..9a16824 100644
--- a/cscore/src/main/native/cpp/Frame.h
+++ b/cscore/src/main/native/cpp/Frame.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_FRAME_H_
 #define CSCORE_FRAME_H_
@@ -11,11 +8,11 @@
 #include <atomic>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
 #include <wpi/SmallVector.h>
-#include <wpi/Twine.h>
 #include <wpi/mutex.h>
 
 #include "Image.h"
@@ -45,14 +42,16 @@
   };
 
  public:
-  Frame() noexcept : m_impl{nullptr} {}
+  Frame() noexcept = default;
 
-  Frame(SourceImpl& source, const wpi::Twine& error, Time time);
+  Frame(SourceImpl& source, std::string_view error, Time time);
 
   Frame(SourceImpl& source, std::unique_ptr<Image> image, Time time);
 
   Frame(const Frame& frame) noexcept : m_impl{frame.m_impl} {
-    if (m_impl) ++m_impl->refcount;
+    if (m_impl) {
+      ++m_impl->refcount;
+    }
   }
 
   Frame(Frame&& other) noexcept : Frame() { swap(*this, other); }
@@ -73,61 +72,91 @@
 
   Time GetTime() const { return m_impl ? m_impl->time : 0; }
 
-  wpi::StringRef GetError() const {
-    if (!m_impl) return wpi::StringRef{};
+  std::string_view GetError() const {
+    if (!m_impl) {
+      return {};
+    }
     return m_impl->error;
   }
 
   int GetOriginalWidth() const {
-    if (!m_impl) return 0;
+    if (!m_impl) {
+      return 0;
+    }
     std::scoped_lock lock(m_impl->mutex);
-    if (m_impl->images.empty()) return 0;
+    if (m_impl->images.empty()) {
+      return 0;
+    }
     return m_impl->images[0]->width;
   }
 
   int GetOriginalHeight() const {
-    if (!m_impl) return 0;
+    if (!m_impl) {
+      return 0;
+    }
     std::scoped_lock lock(m_impl->mutex);
-    if (m_impl->images.empty()) return 0;
+    if (m_impl->images.empty()) {
+      return 0;
+    }
     return m_impl->images[0]->height;
   }
 
   int GetOriginalPixelFormat() const {
-    if (!m_impl) return 0;
+    if (!m_impl) {
+      return 0;
+    }
     std::scoped_lock lock(m_impl->mutex);
-    if (m_impl->images.empty()) return 0;
+    if (m_impl->images.empty()) {
+      return 0;
+    }
     return m_impl->images[0]->pixelFormat;
   }
 
   int GetOriginalJpegQuality() const {
-    if (!m_impl) return 0;
+    if (!m_impl) {
+      return 0;
+    }
     std::scoped_lock lock(m_impl->mutex);
-    if (m_impl->images.empty()) return 0;
+    if (m_impl->images.empty()) {
+      return 0;
+    }
     return m_impl->images[0]->jpegQuality;
   }
 
   Image* GetExistingImage(size_t i = 0) const {
-    if (!m_impl) return nullptr;
+    if (!m_impl) {
+      return nullptr;
+    }
     std::scoped_lock lock(m_impl->mutex);
-    if (i >= m_impl->images.size()) return nullptr;
+    if (i >= m_impl->images.size()) {
+      return nullptr;
+    }
     return m_impl->images[i];
   }
 
   Image* GetExistingImage(int width, int height) const {
-    if (!m_impl) return nullptr;
+    if (!m_impl) {
+      return nullptr;
+    }
     std::scoped_lock lock(m_impl->mutex);
     for (auto i : m_impl->images) {
-      if (i->Is(width, height)) return i;
+      if (i->Is(width, height)) {
+        return i;
+      }
     }
     return nullptr;
   }
 
   Image* GetExistingImage(int width, int height,
                           VideoMode::PixelFormat pixelFormat) const {
-    if (!m_impl) return nullptr;
+    if (!m_impl) {
+      return nullptr;
+    }
     std::scoped_lock lock(m_impl->mutex);
     for (auto i : m_impl->images) {
-      if (i->Is(width, height, pixelFormat)) return i;
+      if (i->Is(width, height, pixelFormat)) {
+        return i;
+      }
     }
     return nullptr;
   }
@@ -135,10 +164,14 @@
   Image* GetExistingImage(int width, int height,
                           VideoMode::PixelFormat pixelFormat,
                           int jpegQuality) const {
-    if (!m_impl) return nullptr;
+    if (!m_impl) {
+      return nullptr;
+    }
     std::scoped_lock lock(m_impl->mutex);
     for (auto i : m_impl->images) {
-      if (i->Is(width, height, pixelFormat, jpegQuality)) return i;
+      if (i->Is(width, height, pixelFormat, jpegQuality)) {
+        return i;
+      }
     }
     return nullptr;
   }
@@ -149,7 +182,9 @@
                          int jpegQuality = -1) const;
 
   Image* Convert(Image* image, VideoMode::PixelFormat pixelFormat) {
-    if (pixelFormat == VideoMode::kMJPEG) return nullptr;
+    if (pixelFormat == VideoMode::kMJPEG) {
+      return nullptr;
+    }
     return ConvertImpl(image, pixelFormat, -1, 80);
   }
   Image* ConvertToMJPEG(Image* image, int requiredQuality,
@@ -168,7 +203,9 @@
   Image* ConvertGrayToMJPEG(Image* image, int quality);
 
   Image* GetImage(int width, int height, VideoMode::PixelFormat pixelFormat) {
-    if (pixelFormat == VideoMode::kMJPEG) return nullptr;
+    if (pixelFormat == VideoMode::kMJPEG) {
+      return nullptr;
+    }
     return GetImageImpl(width, height, pixelFormat, -1, 80);
   }
   Image* GetImageMJPEG(int width, int height, int requiredQuality,
@@ -188,11 +225,13 @@
   Image* GetImageImpl(int width, int height, VideoMode::PixelFormat pixelFormat,
                       int requiredJpegQuality, int defaultJpegQuality);
   void DecRef() {
-    if (m_impl && --(m_impl->refcount) == 0) ReleaseFrame();
+    if (m_impl && --(m_impl->refcount) == 0) {
+      ReleaseFrame();
+    }
   }
   void ReleaseFrame();
 
-  Impl* m_impl;
+  Impl* m_impl{nullptr};
 };
 
 }  // namespace cs
diff --git a/cscore/src/main/native/cpp/Handle.h b/cscore/src/main/native/cpp/Handle.h
index ebb3a55..91df0eb 100644
--- a/cscore/src/main/native/cpp/Handle.h
+++ b/cscore/src/main/native/cpp/Handle.h
@@ -1,13 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_HANDLE_H_
 #define CSCORE_HANDLE_H_
 
+#include <wpi/Synchronization.h>
+
 #include "cscore_c.h"
 
 namespace cs {
@@ -21,11 +20,12 @@
  public:
   enum Type {
     kUndefined = 0,
-    kProperty = 0x40,
+    kProperty = wpi::kHandleTypeCSBase,
     kSource,
     kSink,
     kListener,
-    kSinkProperty
+    kSinkProperty,
+    kListenerPoller
   };
   enum { kIndexMax = 0xffff };
 
diff --git a/cscore/src/main/native/cpp/HttpCameraImpl.cpp b/cscore/src/main/native/cpp/HttpCameraImpl.cpp
index 57c86a8..068393b 100644
--- a/cscore/src/main/native/cpp/HttpCameraImpl.cpp
+++ b/cscore/src/main/native/cpp/HttpCameraImpl.cpp
@@ -1,13 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "HttpCameraImpl.h"
 
 #include <wpi/MemAlloc.h>
+#include <wpi/StringExtras.h>
 #include <wpi/TCPConnector.h>
 #include <wpi/timestamp.h>
 
@@ -21,7 +19,7 @@
 
 using namespace cs;
 
-HttpCameraImpl::HttpCameraImpl(const wpi::Twine& name, CS_HttpCameraKind kind,
+HttpCameraImpl::HttpCameraImpl(std::string_view name, CS_HttpCameraKind kind,
                                wpi::Logger& logger, Notifier& notifier,
                                Telemetry& telemetry)
     : SourceImpl{name, logger, notifier, telemetry}, m_kind{kind} {}
@@ -33,26 +31,36 @@
   m_monitorCond.notify_one();
 
   // join monitor thread
-  if (m_monitorThread.joinable()) m_monitorThread.join();
+  if (m_monitorThread.joinable()) {
+    m_monitorThread.join();
+  }
 
   // Close file if it's open
   {
     std::scoped_lock lock(m_mutex);
-    if (m_streamConn) m_streamConn->stream->close();
-    if (m_settingsConn) m_settingsConn->stream->close();
+    if (m_streamConn) {
+      m_streamConn->stream->close();
+    }
+    if (m_settingsConn) {
+      m_settingsConn->stream->close();
+    }
   }
 
   // force wakeup of camera thread in case it's waiting on cv
   m_sinkEnabledCond.notify_one();
 
   // join camera thread
-  if (m_streamThread.joinable()) m_streamThread.join();
+  if (m_streamThread.joinable()) {
+    m_streamThread.join();
+  }
 
   // force wakeup of settings thread
   m_settingsCond.notify_one();
 
   // join settings thread
-  if (m_settingsThread.joinable()) m_settingsThread.join();
+  if (m_settingsThread.joinable()) {
+    m_settingsThread.join();
+  }
 }
 
 void HttpCameraImpl::Start() {
@@ -69,13 +77,15 @@
     m_monitorCond.wait_for(lock, std::chrono::seconds(1),
                            [=] { return !m_active; });
 
-    if (!m_active) break;
+    if (!m_active) {
+      break;
+    }
 
     // check to see if we got any frames, and close the stream if not
     // (this will result in an error at the read point, and ultimately
     // a reconnect attempt)
     if (m_streamConn && m_frameCount == 0) {
-      SWARNING("Monitor detected stream hung, disconnecting");
+      SWARNING("{}", "Monitor detected stream hung, disconnecting");
       m_streamConn->stream->close();
     }
 
@@ -83,7 +93,7 @@
     m_frameCount = 0;
   }
 
-  SDEBUG("Monitor Thread exiting");
+  SDEBUG("{}", "Monitor Thread exiting");
 }
 
 void HttpCameraImpl::StreamThreadMain() {
@@ -96,33 +106,41 @@
     // disconnect if not enabled
     if (!IsEnabled()) {
       std::unique_lock lock(m_mutex);
-      if (m_streamConn) m_streamConn->stream->close();
+      if (m_streamConn) {
+        m_streamConn->stream->close();
+      }
       // Wait for enable
       m_sinkEnabledCond.wait(lock, [=] { return !m_active || IsEnabled(); });
-      if (!m_active) return;
+      if (!m_active) {
+        return;
+      }
     }
 
     // connect
     wpi::SmallString<64> boundary;
     wpi::HttpConnection* conn = DeviceStreamConnect(boundary);
 
-    if (!m_active) break;
+    if (!m_active) {
+      break;
+    }
 
     // keep retrying
-    if (!conn) continue;
+    if (!conn) {
+      continue;
+    }
 
     // update connected since we're actually connected
     SetConnected(true);
 
     // stream
-    DeviceStream(conn->is, boundary);
+    DeviceStream(conn->is, boundary.str());
     {
       std::unique_lock lock(m_mutex);
       m_streamConn = nullptr;
     }
   }
 
-  SDEBUG("Camera Thread exiting");
+  SDEBUG("{}", "Camera Thread exiting");
   SetConnected(false);
 }
 
@@ -133,11 +151,13 @@
   {
     std::scoped_lock lock(m_mutex);
     if (m_locations.empty()) {
-      SERROR("locations array is empty!?");
+      SERROR("{}", "locations array is empty!?");
       std::this_thread::sleep_for(std::chrono::seconds(1));
       return nullptr;
     }
-    if (m_nextLocation >= m_locations.size()) m_nextLocation = 0;
+    if (m_nextLocation >= m_locations.size()) {
+      m_nextLocation = 0;
+    }
     req = wpi::HttpRequest{m_locations[m_nextLocation++], m_streamSettings};
     m_streamSettingsUpdated = false;
   }
@@ -146,7 +166,9 @@
   auto stream =
       wpi::TCPConnector::connect(req.host.c_str(), req.port, m_logger, 1);
 
-  if (!m_active || !stream) return nullptr;
+  if (!m_active || !stream) {
+    return nullptr;
+  }
 
   auto connPtr = std::make_unique<wpi::HttpConnection>(std::move(stream), 1);
   wpi::HttpConnection* conn = connPtr.get();
@@ -160,19 +182,17 @@
 
   std::string warn;
   if (!conn->Handshake(req, &warn)) {
-    SWARNING(GetName() << ": " << warn);
+    SWARNING("{}", warn);
     std::scoped_lock lock(m_mutex);
     m_streamConn = nullptr;
     return nullptr;
   }
 
   // Parse Content-Type header to get the boundary
-  wpi::StringRef mediaType, contentType;
-  std::tie(mediaType, contentType) = conn->contentType.str().split(';');
-  mediaType = mediaType.trim();
+  auto [mediaType, contentType] = wpi::split(conn->contentType.str(), ';');
+  mediaType = wpi::trim(mediaType);
   if (mediaType != "multipart/x-mixed-replace") {
-    SWARNING("\"" << req.host << "\": unrecognized Content-Type \"" << mediaType
-                  << "\"");
+    SWARNING("\"{}\": unrecognized Content-Type \"{}\"", req.host, mediaType);
     std::scoped_lock lock(m_mutex);
     m_streamConn = nullptr;
     return nullptr;
@@ -181,14 +201,13 @@
   // media parameters
   boundary.clear();
   while (!contentType.empty()) {
-    wpi::StringRef keyvalue;
-    std::tie(keyvalue, contentType) = contentType.split(';');
-    contentType = contentType.ltrim();
-    wpi::StringRef key, value;
-    std::tie(key, value) = keyvalue.split('=');
-    if (key.trim() == "boundary") {
-      value = value.trim().trim('"');  // value may be quoted
-      if (value.startswith("--")) {
+    std::string_view keyvalue;
+    std::tie(keyvalue, contentType) = wpi::split(contentType, ';');
+    contentType = wpi::ltrim(contentType);
+    auto [key, value] = wpi::split(keyvalue, '=');
+    if (wpi::trim(key) == "boundary") {
+      value = wpi::trim(wpi::trim(value), '"');  // value may be quoted
+      if (wpi::starts_with(value, "--")) {
         value = value.substr(2);
       }
       boundary.append(value.begin(), value.end());
@@ -196,8 +215,7 @@
   }
 
   if (boundary.empty()) {
-    SWARNING("\"" << req.host
-                  << "\": empty multi-part boundary or no Content-Type");
+    SWARNING("\"{}\": empty multi-part boundary or no Content-Type", req.host);
     std::scoped_lock lock(m_mutex);
     m_streamConn = nullptr;
     return nullptr;
@@ -207,7 +225,7 @@
 }
 
 void HttpCameraImpl::DeviceStream(wpi::raw_istream& is,
-                                  wpi::StringRef boundary) {
+                                  std::string_view boundary) {
   // Stored here so we reuse it from frame to frame
   std::string imageBuf;
 
@@ -218,24 +236,33 @@
   // streaming loop
   while (m_active && !is.has_error() && IsEnabled() && numErrors < 3 &&
          !m_streamSettingsUpdated) {
-    if (!FindMultipartBoundary(is, boundary, nullptr)) break;
+    if (!FindMultipartBoundary(is, boundary, nullptr)) {
+      break;
+    }
 
     // Read the next two characters after the boundary (normally \r\n)
     // Handle just \n for LabVIEW however
     char eol[2];
     is.read(eol, 1);
-    if (!m_active || is.has_error()) break;
+    if (!m_active || is.has_error()) {
+      break;
+    }
     if (eol[0] != '\n') {
       is.read(eol + 1, 1);
-      if (!m_active || is.has_error()) break;
+      if (!m_active || is.has_error()) {
+        break;
+      }
       // End-of-stream is indicated with trailing --
-      if (eol[0] == '-' && eol[1] == '-') break;
+      if (eol[0] == '-' && eol[1] == '-') {
+        break;
+      }
     }
 
-    if (!DeviceStreamFrame(is, imageBuf))
+    if (!DeviceStreamFrame(is, imageBuf)) {
       ++numErrors;
-    else
+    } else {
       numErrors = 0;
+    }
   }
 }
 
@@ -245,28 +272,29 @@
   wpi::SmallString<64> contentTypeBuf;
   wpi::SmallString<64> contentLengthBuf;
   if (!ParseHttpHeaders(is, &contentTypeBuf, &contentLengthBuf)) {
-    SWARNING("disconnected during headers");
+    SWARNING("{}", "disconnected during headers");
     PutError("disconnected during headers", wpi::Now());
     return false;
   }
 
   // Check the content type (if present)
   if (!contentTypeBuf.str().empty() &&
-      !contentTypeBuf.str().startswith("image/jpeg")) {
-    wpi::SmallString<64> errBuf;
-    wpi::raw_svector_ostream errMsg{errBuf};
-    errMsg << "received unknown Content-Type \"" << contentTypeBuf << "\"";
-    SWARNING(errMsg.str());
-    PutError(errMsg.str(), wpi::Now());
+      !wpi::starts_with(contentTypeBuf, "image/jpeg")) {
+    auto errMsg =
+        fmt::format("received unknown Content-Type \"{}\"", contentTypeBuf);
+    SWARNING("{}", errMsg);
+    PutError(errMsg, wpi::Now());
     return false;
   }
 
   unsigned int contentLength = 0;
-  if (contentLengthBuf.str().getAsInteger(10, contentLength)) {
+  if (auto v = wpi::parse_integer<unsigned int>(contentLengthBuf, 10)) {
+    contentLength = v.value();
+  } else {
     // Ugh, no Content-Length?  Read the blocks of the JPEG file.
     int width, height;
     if (!ReadJpeg(is, imageBuf, &width, &height)) {
-      SWARNING("did not receive a JPEG image");
+      SWARNING("{}", "did not receive a JPEG image");
       PutError("did not receive a JPEG image", wpi::Now());
       return false;
     }
@@ -280,10 +308,12 @@
   // the data directly into it.
   auto image = AllocImage(VideoMode::PixelFormat::kMJPEG, 0, 0, contentLength);
   is.read(image->data(), contentLength);
-  if (!m_active || is.has_error()) return false;
+  if (!m_active || is.has_error()) {
+    return false;
+  }
   int width, height;
   if (!GetJpegSize(image->str(), &width, &height)) {
-    SWARNING("did not receive a JPEG image");
+    SWARNING("{}", "did not receive a JPEG image");
     PutError("did not receive a JPEG image", wpi::Now());
     return false;
   }
@@ -302,7 +332,9 @@
       m_settingsCond.wait(lock, [=] {
         return !m_active || (m_prefLocation != -1 && !m_settings.empty());
       });
-      if (!m_active) break;
+      if (!m_active) {
+        break;
+      }
 
       // Build the request
       req = wpi::HttpRequest{m_locations[m_prefLocation], m_settings};
@@ -311,7 +343,7 @@
     DeviceSendSettings(req);
   }
 
-  SDEBUG("Settings Thread exiting");
+  SDEBUG("{}", "Settings Thread exiting");
 }
 
 void HttpCameraImpl::DeviceSendSettings(wpi::HttpRequest& req) {
@@ -319,7 +351,9 @@
   auto stream =
       wpi::TCPConnector::connect(req.host.c_str(), req.port, m_logger, 1);
 
-  if (!m_active || !stream) return;
+  if (!m_active || !stream) {
+    return;
+  }
 
   auto connPtr = std::make_unique<wpi::HttpConnection>(std::move(stream), 1);
   wpi::HttpConnection* conn = connPtr.get();
@@ -332,7 +366,9 @@
 
   // Just need a handshake as settings are sent via GET parameters
   std::string warn;
-  if (!conn->Handshake(req, &warn)) SWARNING(GetName() << ": " << warn);
+  if (!conn->Handshake(req, &warn)) {
+    SWARNING("{}", warn);
+  }
 
   conn->stream->close();
 }
@@ -342,7 +378,7 @@
   return m_kind;
 }
 
-bool HttpCameraImpl::SetUrls(wpi::ArrayRef<std::string> urls,
+bool HttpCameraImpl::SetUrls(wpi::span<const std::string> urls,
                              CS_Status* status) {
   std::vector<wpi::HttpLocation> locations;
   for (const auto& url : urls) {
@@ -350,7 +386,7 @@
     std::string errorMsg;
     locations.emplace_back(url, &error, &errorMsg);
     if (error) {
-      SERROR(GetName() << ": " << errorMsg);
+      SERROR("{}", errorMsg);
       *status = CS_BAD_URL;
       return false;
     }
@@ -366,12 +402,14 @@
 std::vector<std::string> HttpCameraImpl::GetUrls() const {
   std::scoped_lock lock(m_mutex);
   std::vector<std::string> urls;
-  for (const auto& loc : m_locations) urls.push_back(loc.url);
+  for (const auto& loc : m_locations) {
+    urls.push_back(loc.url);
+  }
   return urls;
 }
 
-void HttpCameraImpl::CreateProperty(const wpi::Twine& name,
-                                    const wpi::Twine& httpParam,
+void HttpCameraImpl::CreateProperty(std::string_view name,
+                                    std::string_view httpParam,
                                     bool viaSettings, CS_PropertyKind kind,
                                     int minimum, int maximum, int step,
                                     int defaultValue, int value) const {
@@ -381,13 +419,12 @@
       value));
 
   m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name,
-                                  m_propertyData.size() + 1, kind, value,
-                                  wpi::Twine{});
+                                  m_propertyData.size() + 1, kind, value, {});
 }
 
 template <typename T>
 void HttpCameraImpl::CreateEnumProperty(
-    const wpi::Twine& name, const wpi::Twine& httpParam, bool viaSettings,
+    std::string_view name, std::string_view httpParam, bool viaSettings,
     int defaultValue, int value, std::initializer_list<T> choices) const {
   std::scoped_lock lock(m_mutex);
   m_propertyData.emplace_back(std::make_unique<PropertyData>(
@@ -396,18 +433,20 @@
 
   auto& enumChoices = m_propertyData.back()->enumChoices;
   enumChoices.clear();
-  for (const auto& choice : choices) enumChoices.emplace_back(choice);
+  for (const auto& choice : choices) {
+    enumChoices.emplace_back(choice);
+  }
 
   m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name,
                                   m_propertyData.size() + 1, CS_PROP_ENUM,
-                                  value, wpi::Twine{});
+                                  value, {});
   m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
                                   name, m_propertyData.size() + 1, CS_PROP_ENUM,
-                                  value, wpi::Twine{});
+                                  value, {});
 }
 
 std::unique_ptr<PropertyImpl> HttpCameraImpl::CreateEmptyProperty(
-    const wpi::Twine& name) const {
+    std::string_view name) const {
   return std::make_unique<PropertyData>(name);
 }
 
@@ -432,7 +471,7 @@
   // TODO
 }
 
-void HttpCameraImpl::SetStringProperty(int property, const wpi::Twine& value,
+void HttpCameraImpl::SetStringProperty(int property, std::string_view value,
                                        CS_Status* status) {
   // TODO
 }
@@ -471,7 +510,9 @@
 }
 
 bool HttpCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
-  if (mode.pixelFormat != VideoMode::kMJPEG) return false;
+  if (mode.pixelFormat != VideoMode::kMJPEG) {
+    return false;
+  }
   std::scoped_lock lock(m_mutex);
   m_mode = mode;
   m_streamSettingsUpdated = true;
@@ -516,7 +557,7 @@
 
 namespace cs {
 
-CS_Source CreateHttpCamera(const wpi::Twine& name, const wpi::Twine& url,
+CS_Source CreateHttpCamera(std::string_view name, std::string_view url,
                            CS_HttpCameraKind kind, CS_Status* status) {
   auto& inst = Instance::GetInstance();
   std::shared_ptr<HttpCameraImpl> source;
@@ -530,12 +571,15 @@
                                                 inst.notifier, inst.telemetry);
       break;
   }
-  if (!source->SetUrls(url.str(), status)) return 0;
+  std::string urlStr{url};
+  if (!source->SetUrls(wpi::span{&urlStr, 1}, status)) {
+    return 0;
+  }
   return inst.CreateSource(CS_SOURCE_HTTP, source);
 }
 
-CS_Source CreateHttpCamera(const wpi::Twine& name,
-                           wpi::ArrayRef<std::string> urls,
+CS_Source CreateHttpCamera(std::string_view name,
+                           wpi::span<const std::string> urls,
                            CS_HttpCameraKind kind, CS_Status* status) {
   auto& inst = Instance::GetInstance();
   if (urls.empty()) {
@@ -544,7 +588,9 @@
   }
   auto source = std::make_shared<HttpCameraImpl>(name, kind, inst.logger,
                                                  inst.notifier, inst.telemetry);
-  if (!source->SetUrls(urls, status)) return 0;
+  if (!source->SetUrls(urls, status)) {
+    return 0;
+  }
   return inst.CreateSource(CS_SOURCE_HTTP, source);
 }
 
@@ -557,7 +603,7 @@
   return static_cast<HttpCameraImpl&>(*data->source).GetKind();
 }
 
-void SetHttpCameraUrls(CS_Source source, wpi::ArrayRef<std::string> urls,
+void SetHttpCameraUrls(CS_Source source, wpi::span<const std::string> urls,
                        CS_Status* status) {
   if (urls.empty()) {
     *status = CS_EMPTY_VALUE;
@@ -595,7 +641,9 @@
                                    CS_Status* status) {
   wpi::SmallVector<std::string, 4> vec;
   vec.reserve(count);
-  for (int i = 0; i < count; ++i) vec.push_back(urls[i]);
+  for (int i = 0; i < count; ++i) {
+    vec.push_back(urls[i]);
+  }
   return cs::CreateHttpCamera(name, vec, kind, status);
 }
 
@@ -607,7 +655,9 @@
                           CS_Status* status) {
   wpi::SmallVector<std::string, 4> vec;
   vec.reserve(count);
-  for (int i = 0; i < count; ++i) vec.push_back(urls[i]);
+  for (int i = 0; i < count; ++i) {
+    vec.push_back(urls[i]);
+  }
   cs::SetHttpCameraUrls(source, vec, status);
 }
 
@@ -616,13 +666,19 @@
   char** out =
       static_cast<char**>(wpi::safe_malloc(urls.size() * sizeof(char*)));
   *count = urls.size();
-  for (size_t i = 0; i < urls.size(); ++i) out[i] = cs::ConvertToC(urls[i]);
+  for (size_t i = 0; i < urls.size(); ++i) {
+    out[i] = cs::ConvertToC(urls[i]);
+  }
   return out;
 }
 
 void CS_FreeHttpCameraUrls(char** urls, int count) {
-  if (!urls) return;
-  for (int i = 0; i < count; ++i) std::free(urls[i]);
+  if (!urls) {
+    return;
+  }
+  for (int i = 0; i < count; ++i) {
+    std::free(urls[i]);
+  }
   std::free(urls);
 }
 
diff --git a/cscore/src/main/native/cpp/HttpCameraImpl.h b/cscore/src/main/native/cpp/HttpCameraImpl.h
index 5967015..2c58936 100644
--- a/cscore/src/main/native/cpp/HttpCameraImpl.h
+++ b/cscore/src/main/native/cpp/HttpCameraImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_HTTPCAMERAIMPL_H_
 #define CSCORE_HTTPCAMERAIMPL_H_
@@ -13,15 +10,16 @@
 #include <initializer_list>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <thread>
 #include <vector>
 
 #include <wpi/HttpUtil.h>
 #include <wpi/SmallString.h>
 #include <wpi/StringMap.h>
-#include <wpi/Twine.h>
 #include <wpi/condition_variable.h>
 #include <wpi/raw_istream.h>
+#include <wpi/span.h>
 
 #include "SourceImpl.h"
 #include "cscore_cpp.h"
@@ -30,7 +28,7 @@
 
 class HttpCameraImpl : public SourceImpl {
  public:
-  HttpCameraImpl(const wpi::Twine& name, CS_HttpCameraKind kind,
+  HttpCameraImpl(std::string_view name, CS_HttpCameraKind kind,
                  wpi::Logger& logger, Notifier& notifier, Telemetry& telemetry);
   ~HttpCameraImpl() override;
 
@@ -38,7 +36,7 @@
 
   // Property functions
   void SetProperty(int property, int value, CS_Status* status) override;
-  void SetStringProperty(int property, const wpi::Twine& value,
+  void SetStringProperty(int property, std::string_view value,
                          CS_Status* status) override;
 
   // Standard common camera properties
@@ -57,20 +55,20 @@
   void NumSinksEnabledChanged() override;
 
   CS_HttpCameraKind GetKind() const;
-  bool SetUrls(wpi::ArrayRef<std::string> urls, CS_Status* status);
+  bool SetUrls(wpi::span<const std::string> urls, CS_Status* status);
   std::vector<std::string> GetUrls() const;
 
   // Property data
   class PropertyData : public PropertyImpl {
    public:
     PropertyData() = default;
-    explicit PropertyData(const wpi::Twine& name_) : PropertyImpl{name_} {}
-    PropertyData(const wpi::Twine& name_, const wpi::Twine& httpParam_,
+    explicit PropertyData(std::string_view name_) : PropertyImpl{name_} {}
+    PropertyData(std::string_view name_, std::string_view httpParam_,
                  bool viaSettings_, CS_PropertyKind kind_, int minimum_,
                  int maximum_, int step_, int defaultValue_, int value_)
         : PropertyImpl(name_, kind_, step_, defaultValue_, value_),
           viaSettings(viaSettings_),
-          httpParam(httpParam_.str()) {
+          httpParam(httpParam_) {
       hasMinimum = true;
       minimum = minimum_;
       hasMaximum = true;
@@ -84,16 +82,16 @@
 
  protected:
   std::unique_ptr<PropertyImpl> CreateEmptyProperty(
-      const wpi::Twine& name) const override;
+      std::string_view name) const override;
 
   bool CacheProperties(CS_Status* status) const override;
 
-  void CreateProperty(const wpi::Twine& name, const wpi::Twine& httpParam,
+  void CreateProperty(std::string_view name, std::string_view httpParam,
                       bool viaSettings, CS_PropertyKind kind, int minimum,
                       int maximum, int step, int defaultValue, int value) const;
 
   template <typename T>
-  void CreateEnumProperty(const wpi::Twine& name, const wpi::Twine& httpParam,
+  void CreateEnumProperty(std::string_view name, std::string_view httpParam,
                           bool viaSettings, int defaultValue, int value,
                           std::initializer_list<T> choices) const;
 
@@ -104,7 +102,7 @@
   // Functions used by StreamThreadMain()
   wpi::HttpConnection* DeviceStreamConnect(
       wpi::SmallVectorImpl<char>& boundary);
-  void DeviceStream(wpi::raw_istream& is, wpi::StringRef boundary);
+  void DeviceStream(wpi::raw_istream& is, std::string_view boundary);
   bool DeviceStreamFrame(wpi::raw_istream& is, std::string& imageBuf);
 
   // The camera settings thread
@@ -149,12 +147,12 @@
 
 class AxisCameraImpl : public HttpCameraImpl {
  public:
-  AxisCameraImpl(const wpi::Twine& name, wpi::Logger& logger,
-                 Notifier& notifier, Telemetry& telemetry)
+  AxisCameraImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
+                 Telemetry& telemetry)
       : HttpCameraImpl{name, CS_HTTP_AXIS, logger, notifier, telemetry} {}
 #if 0
   void SetProperty(int property, int value, CS_Status* status) override;
-  void SetStringProperty(int property, const wpi::Twine& value,
+  void SetStringProperty(int property, std::string_view value,
                          CS_Status* status) override;
 #endif
  protected:
diff --git a/cscore/src/main/native/cpp/Image.h b/cscore/src/main/native/cpp/Image.h
index 6305d93..9a1579f 100644
--- a/cscore/src/main/native/cpp/Image.h
+++ b/cscore/src/main/native/cpp/Image.h
@@ -1,17 +1,14 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_IMAGE_H_
 #define CSCORE_IMAGE_H_
 
+#include <string_view>
 #include <vector>
 
 #include <opencv2/core/core.hpp>
-#include <wpi/StringRef.h>
 
 #include "cscore_cpp.h"
 #include "default_init_allocator.h"
@@ -37,8 +34,8 @@
   Image& operator=(const Image&) = delete;
 
   // Getters
-  operator wpi::StringRef() const { return str(); }
-  wpi::StringRef str() const { return wpi::StringRef(data(), size()); }
+  operator std::string_view() const { return str(); }  // NOLINT
+  std::string_view str() const { return {data(), size()}; }
   size_t capacity() const { return m_data.capacity(); }
   const char* data() const {
     return reinterpret_cast<const char*>(m_data.data());
diff --git a/cscore/src/main/native/cpp/Instance.cpp b/cscore/src/main/native/cpp/Instance.cpp
index 8dd5c26..3b81f01 100644
--- a/cscore/src/main/native/cpp/Instance.cpp
+++ b/cscore/src/main/native/cpp/Instance.cpp
@@ -1,48 +1,45 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "Instance.h"
 
-#include <wpi/Path.h>
-#include <wpi/SmallString.h>
-#include <wpi/StringRef.h>
-#include <wpi/raw_ostream.h>
+#include <string_view>
+
+#include <fmt/format.h>
+#include <wpi/fs.h>
 
 using namespace cs;
 
 static void def_log_func(unsigned int level, const char* file,
                          unsigned int line, const char* msg) {
-  wpi::SmallString<128> buf;
-  wpi::raw_svector_ostream oss(buf);
   if (level == 20) {
-    oss << "CS: " << msg << '\n';
-    wpi::errs() << oss.str();
+    fmt::print(stderr, "CS: {}\n", msg);
     return;
   }
 
-  wpi::StringRef levelmsg;
-  if (level >= 50)
-    levelmsg = "CRITICAL: ";
-  else if (level >= 40)
-    levelmsg = "ERROR: ";
-  else if (level >= 30)
-    levelmsg = "WARNING: ";
-  else
+  std::string_view levelmsg;
+  if (level >= 50) {
+    levelmsg = "CRITICAL";
+  } else if (level >= 40) {
+    levelmsg = "ERROR";
+  } else if (level >= 30) {
+    levelmsg = "WARNING";
+  } else {
     return;
-  oss << "CS: " << levelmsg << msg << " (" << wpi::sys::path::filename(file)
-      << ':' << line << ")\n";
-  wpi::errs() << oss.str();
+  }
+  fmt::print(stderr, "CS: {}: {} ({}:{})\n", levelmsg, msg,
+             fs::path{file}.filename().string(), line);
 }
 
-Instance::Instance() : telemetry(notifier), networkListener(logger, notifier) {
+Instance::Instance()
+    : telemetry(notifier),
+      networkListener(logger, notifier),
+      usbCameraListener(logger, notifier) {
   SetDefaultLogger();
 }
 
-Instance::~Instance() {}
+Instance::~Instance() = default;
 
 Instance& Instance::GetInstance() {
   static Instance* inst = new Instance;
@@ -54,11 +51,14 @@
   m_sinks.FreeAll();
   m_sources.FreeAll();
   networkListener.Stop();
+  usbCameraListener.Stop();
   telemetry.Stop();
   notifier.Stop();
 }
 
-void Instance::SetDefaultLogger() { logger.SetLogger(def_log_func); }
+void Instance::SetDefaultLogger() {
+  logger.SetLogger(def_log_func);
+}
 
 std::pair<CS_Source, std::shared_ptr<SourceData>> Instance::FindSource(
     const SourceImpl& source) {
@@ -87,11 +87,13 @@
 }
 
 void Instance::DestroySource(CS_Source handle) {
-  if (auto data = m_sources.Free(handle))
+  if (auto data = m_sources.Free(handle)) {
     notifier.NotifySource(data->source->GetName(), handle, CS_SOURCE_DESTROYED);
+  }
 }
 
 void Instance::DestroySink(CS_Sink handle) {
-  if (auto data = m_sinks.Free(handle))
+  if (auto data = m_sinks.Free(handle)) {
     notifier.NotifySink(data->sink->GetName(), handle, CS_SINK_DESTROYED);
+  }
 }
diff --git a/cscore/src/main/native/cpp/Instance.h b/cscore/src/main/native/cpp/Instance.h
index 0c566f3..f84eacf 100644
--- a/cscore/src/main/native/cpp/Instance.h
+++ b/cscore/src/main/native/cpp/Instance.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_INSTANCE_H_
 #define CSCORE_INSTANCE_H_
@@ -21,12 +18,13 @@
 #include "SourceImpl.h"
 #include "Telemetry.h"
 #include "UnlimitedHandleResource.h"
+#include "UsbCameraListener.h"
 
 namespace cs {
 
 struct SourceData {
   SourceData(CS_SourceKind kind_, std::shared_ptr<SourceImpl> source_)
-      : kind{kind_}, refCount{0}, source{source_} {}
+      : kind{kind_}, refCount{0}, source{std::move(source_)} {}
 
   CS_SourceKind kind;
   std::atomic_int refCount;
@@ -35,7 +33,7 @@
 
 struct SinkData {
   explicit SinkData(CS_SinkKind kind_, std::shared_ptr<SinkImpl> sink_)
-      : kind{kind_}, refCount{0}, sourceHandle{0}, sink{sink_} {}
+      : kind{kind_}, refCount{0}, sourceHandle{0}, sink{std::move(sink_)} {}
 
   CS_SinkKind kind;
   std::atomic_int refCount;
@@ -57,6 +55,7 @@
   Notifier notifier;
   Telemetry telemetry;
   NetworkListener networkListener;
+  UsbCameraListener usbCameraListener;
 
  private:
   UnlimitedHandleResource<Handle, SourceData, Handle::kSource> m_sources;
@@ -87,21 +86,22 @@
   void DestroySource(CS_Source handle);
   void DestroySink(CS_Sink handle);
 
-  wpi::ArrayRef<CS_Source> EnumerateSourceHandles(
+  wpi::span<CS_Source> EnumerateSourceHandles(
       wpi::SmallVectorImpl<CS_Source>& vec) {
     return m_sources.GetAll(vec);
   }
 
-  wpi::ArrayRef<CS_Sink> EnumerateSinkHandles(
-      wpi::SmallVectorImpl<CS_Sink>& vec) {
+  wpi::span<CS_Sink> EnumerateSinkHandles(wpi::SmallVectorImpl<CS_Sink>& vec) {
     return m_sinks.GetAll(vec);
   }
 
-  wpi::ArrayRef<CS_Sink> EnumerateSourceSinks(
-      CS_Source source, wpi::SmallVectorImpl<CS_Sink>& vec) {
+  wpi::span<CS_Sink> EnumerateSourceSinks(CS_Source source,
+                                          wpi::SmallVectorImpl<CS_Sink>& vec) {
     vec.clear();
     m_sinks.ForEach([&](CS_Sink sinkHandle, const SinkData& data) {
-      if (source == data.sourceHandle.load()) vec.push_back(sinkHandle);
+      if (source == data.sourceHandle.load()) {
+        vec.push_back(sinkHandle);
+      }
     });
     return vec;
   }
diff --git a/cscore/src/main/native/cpp/JpegUtil.cpp b/cscore/src/main/native/cpp/JpegUtil.cpp
index 35891e8..2e66338 100644
--- a/cscore/src/main/native/cpp/JpegUtil.cpp
+++ b/cscore/src/main/native/cpp/JpegUtil.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "JpegUtil.h"
 
@@ -49,29 +46,44 @@
     0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
     0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
 
-bool IsJpeg(wpi::StringRef data) {
-  if (data.size() < 11) return false;
+bool IsJpeg(std::string_view data) {
+  if (data.size() < 11) {
+    return false;
+  }
 
   // Check for valid SOI
-  auto bytes = data.bytes_begin();
-  if (bytes[0] != 0xff || bytes[1] != 0xd8) return false;
+  auto bytes = reinterpret_cast<const unsigned char*>(data.data());
+  if (bytes[0] != 0xff || bytes[1] != 0xd8) {
+    return false;
+  }
   return true;
 }
 
-bool GetJpegSize(wpi::StringRef data, int* width, int* height) {
-  if (!IsJpeg(data)) return false;
+bool GetJpegSize(std::string_view data, int* width, int* height) {
+  if (!IsJpeg(data)) {
+    return false;
+  }
 
   data = data.substr(2);  // Get to the first block
-  auto bytes = data.bytes_begin();
   for (;;) {
-    if (data.size() < 4) return false;  // EOF
-    bytes = data.bytes_begin();
-    if (bytes[0] != 0xff) return false;  // not a tag
-    if (bytes[1] == 0xd9) return false;  // EOI without finding SOF?
-    if (bytes[1] == 0xda) return false;  // SOS without finding SOF?
+    if (data.size() < 4) {
+      return false;  // EOF
+    }
+    auto bytes = reinterpret_cast<const unsigned char*>(data.data());
+    if (bytes[0] != 0xff) {
+      return false;  // not a tag
+    }
+    if (bytes[1] == 0xd9) {
+      return false;  // EOI without finding SOF?
+    }
+    if (bytes[1] == 0xda) {
+      return false;  // SOS without finding SOF?
+    }
     if (bytes[1] == 0xc0) {
       // SOF contains the file size
-      if (data.size() < 9) return false;
+      if (data.size() < 9) {
+        return false;
+      }
       *height = bytes[5] * 256 + bytes[6];
       *width = bytes[7] * 256 + bytes[8];
       return true;
@@ -82,21 +94,32 @@
 }
 
 bool JpegNeedsDHT(const char* data, size_t* size, size_t* locSOF) {
-  wpi::StringRef sdata(data, *size);
-  if (!IsJpeg(sdata)) return false;
+  std::string_view sdata(data, *size);
+  if (!IsJpeg(sdata)) {
+    return false;
+  }
 
   *locSOF = *size;
 
   // Search until SOS for DHT tag
   sdata = sdata.substr(2);  // Get to the first block
-  auto bytes = sdata.bytes_begin();
   for (;;) {
-    if (sdata.size() < 4) return false;  // EOF
-    bytes = sdata.bytes_begin();
-    if (bytes[0] != 0xff) return false;                   // not a tag
-    if (bytes[1] == 0xda) break;                          // SOS
-    if (bytes[1] == 0xc4) return false;                   // DHT
-    if (bytes[1] == 0xc0) *locSOF = sdata.data() - data;  // SOF
+    if (sdata.size() < 4) {
+      return false;  // EOF
+    }
+    auto bytes = reinterpret_cast<const unsigned char*>(sdata.data());
+    if (bytes[0] != 0xff) {
+      return false;  // not a tag
+    }
+    if (bytes[1] == 0xda) {
+      break;  // SOS
+    }
+    if (bytes[1] == 0xc4) {
+      return false;  // DHT
+    }
+    if (bytes[1] == 0xc0) {
+      *locSOF = sdata.data() - data;  // SOF
+    }
     // Go to the next block
     sdata = sdata.substr(bytes[2] * 256 + bytes[3] + 2);
   }
@@ -109,9 +132,8 @@
   return false;
 }
 
-wpi::StringRef JpegGetDHT() {
-  return wpi::StringRef(reinterpret_cast<const char*>(dhtData),
-                        sizeof(dhtData));
+std::string_view JpegGetDHT() {
+  return {reinterpret_cast<const char*>(dhtData), sizeof(dhtData)};
 }
 
 static inline void ReadInto(wpi::raw_istream& is, std::string& buf,
@@ -129,18 +151,26 @@
   // read SOI and first marker
   buf.resize(4);
   is.read(&(*buf.begin()), 4);
-  if (is.has_error()) return false;
+  if (is.has_error()) {
+    return false;
+  }
 
   // Check for valid SOI
   auto bytes = reinterpret_cast<const unsigned char*>(buf.data());
-  if (bytes[0] != 0xff || bytes[1] != 0xd8) return false;
+  if (bytes[0] != 0xff || bytes[1] != 0xd8) {
+    return false;
+  }
   size_t pos = 2;  // point to first marker
   for (;;) {
     bytes = reinterpret_cast<const unsigned char*>(buf.data() + pos);
-    if (bytes[0] != 0xff) return false;  // not a marker
+    if (bytes[0] != 0xff) {
+      return false;  // not a marker
+    }
     unsigned char marker = bytes[1];
 
-    if (marker == 0xd9) return true;  // EOI, we're done
+    if (marker == 0xd9) {
+      return true;  // EOI, we're done
+    }
 
     if (marker == 0xda) {
       // SOS: need to keep reading until we reach a normal marker.
@@ -150,12 +180,15 @@
       bool maybeMarker = false;
       for (;;) {
         ReadInto(is, buf, 1);
-        if (is.has_error()) return false;
+        if (is.has_error()) {
+          return false;
+        }
         bytes = reinterpret_cast<const unsigned char*>(buf.data() + pos);
         if (maybeMarker) {
           if (bytes[0] != 0x00 && bytes[0] != 0xff &&
-              (bytes[0] < 0xd0 || bytes[0] > 0xd7))
+              (bytes[0] < 0xd0 || bytes[0] > 0xd7)) {
             break;
+          }
           maybeMarker = false;
         } else if (bytes[0] == 0xff) {
           maybeMarker = true;
@@ -168,7 +201,9 @@
 
     // A normal block. Read the length
     ReadInto(is, buf, 2);  // read length
-    if (is.has_error()) return false;
+    if (is.has_error()) {
+      return false;
+    }
 
     // Point to length
     pos += 2;
@@ -177,7 +212,9 @@
     // Read the block and the next marker
     size_t blockLength = bytes[0] * 256 + bytes[1];
     ReadInto(is, buf, blockLength);
-    if (is.has_error()) return false;
+    if (is.has_error()) {
+      return false;
+    }
     bytes = reinterpret_cast<const unsigned char*>(buf.data() + pos);
 
     // Special block processing
diff --git a/cscore/src/main/native/cpp/JpegUtil.h b/cscore/src/main/native/cpp/JpegUtil.h
index 082fdc4..4fe0693 100644
--- a/cscore/src/main/native/cpp/JpegUtil.h
+++ b/cscore/src/main/native/cpp/JpegUtil.h
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_JPEGUTIL_H_
 #define CSCORE_JPEGUTIL_H_
 
 #include <string>
-
-#include <wpi/StringRef.h>
+#include <string_view>
 
 namespace wpi {
 class raw_istream;
@@ -18,13 +14,13 @@
 
 namespace cs {
 
-bool IsJpeg(wpi::StringRef data);
+bool IsJpeg(std::string_view data);
 
-bool GetJpegSize(wpi::StringRef data, int* width, int* height);
+bool GetJpegSize(std::string_view data, int* width, int* height);
 
 bool JpegNeedsDHT(const char* data, size_t* size, size_t* locSOF);
 
-wpi::StringRef JpegGetDHT();
+std::string_view JpegGetDHT();
 
 bool ReadJpeg(wpi::raw_istream& is, std::string& buf, int* width, int* height);
 
diff --git a/cscore/src/main/native/cpp/Log.cpp b/cscore/src/main/native/cpp/Log.cpp
new file mode 100644
index 0000000..cf671bb
--- /dev/null
+++ b/cscore/src/main/native/cpp/Log.cpp
@@ -0,0 +1,15 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "Log.h"
+
+void cs::NamedLogV(wpi::Logger& logger, unsigned int level, const char* file,
+                   unsigned int line, std::string_view name,
+                   fmt::string_view format, fmt::format_args args) {
+  fmt::memory_buffer out;
+  fmt::format_to(fmt::appender{out}, "{}: ", name);
+  fmt::vformat_to(fmt::appender{out}, format, args);
+  out.push_back('\0');
+  logger.DoLog(level, file, line, out.data());
+}
diff --git a/cscore/src/main/native/cpp/Log.h b/cscore/src/main/native/cpp/Log.h
index b9fd9ab..21becdf 100644
--- a/cscore/src/main/native/cpp/Log.h
+++ b/cscore/src/main/native/cpp/Log.h
@@ -1,36 +1,75 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_LOG_H_
 #define CSCORE_LOG_H_
 
+#include <string_view>
+
 #include <wpi/Logger.h>
 
-#define LOG(level, x) WPI_LOG(m_logger, level, x)
+namespace cs {
+
+void NamedLogV(wpi::Logger& logger, unsigned int level, const char* file,
+               unsigned int line, std::string_view name,
+               fmt::string_view format, fmt::format_args args);
+
+template <typename S, typename... Args>
+inline void NamedLog(wpi::Logger& logger, unsigned int level, const char* file,
+                     unsigned int line, std::string_view name, const S& format,
+                     Args&&... args) {
+  if (logger.HasLogger() && level >= logger.min_level()) {
+    NamedLogV(logger, level, file, line, name, format,
+              fmt::make_args_checked<Args...>(format, args...));
+  }
+}
+
+}  // namespace cs
+
+#define LOG(level, format, ...) WPI_LOG(m_logger, level, format, __VA_ARGS__)
 
 #undef ERROR
-#define ERROR(x) WPI_ERROR(m_logger, x)
-#define WARNING(x) WPI_WARNING(m_logger, x)
-#define INFO(x) WPI_INFO(m_logger, x)
+#define ERROR(format, ...) WPI_ERROR(m_logger, format, __VA_ARGS__)
+#define WARNING(format, ...) WPI_WARNING(m_logger, format, __VA_ARGS__)
+#define INFO(format, ...) WPI_INFO(m_logger, format, __VA_ARGS__)
 
-#define DEBUG0(x) WPI_DEBUG(m_logger, x)
-#define DEBUG1(x) WPI_DEBUG1(m_logger, x)
-#define DEBUG2(x) WPI_DEBUG2(m_logger, x)
-#define DEBUG3(x) WPI_DEBUG3(m_logger, x)
-#define DEBUG4(x) WPI_DEBUG4(m_logger, x)
+#define DEBUG0(format, ...) WPI_DEBUG(m_logger, format, __VA_ARGS__)
+#define DEBUG1(format, ...) WPI_DEBUG1(m_logger, format, __VA_ARGS__)
+#define DEBUG2(format, ...) WPI_DEBUG2(m_logger, format, __VA_ARGS__)
+#define DEBUG3(format, ...) WPI_DEBUG3(m_logger, format, __VA_ARGS__)
+#define DEBUG4(format, ...) WPI_DEBUG4(m_logger, format, __VA_ARGS__)
 
-#define SERROR(x) ERROR(GetName() << ": " << x)
-#define SWARNING(x) WARNING(GetName() << ": " << x)
-#define SINFO(x) INFO(GetName() << ": " << x)
+#define SLOG(level, format, ...)                                               \
+  NamedLog(m_logger, level, __FILE__, __LINE__, GetName(), FMT_STRING(format), \
+           __VA_ARGS__)
 
-#define SDEBUG(x) DEBUG0(GetName() << ": " << x)
-#define SDEBUG1(x) DEBUG1(GetName() << ": " << x)
-#define SDEBUG2(x) DEBUG2(GetName() << ": " << x)
-#define SDEBUG3(x) DEBUG3(GetName() << ": " << x)
-#define SDEBUG4(x) DEBUG4(GetName() << ": " << x)
+#define SERROR(format, ...) SLOG(::wpi::WPI_LOG_ERROR, format, __VA_ARGS__)
+#define SWARNING(format, ...) SLOG(::wpi::WPI_LOG_WARNING, format, __VA_ARGS__)
+#define SINFO(format, ...) SLOG(::wpi::WPI_LOG_INFO, format, __VA_ARGS__)
+
+#ifdef NDEBUG
+#define SDEBUG(format, ...) \
+  do {                      \
+  } while (0)
+#define SDEBUG1(format, ...) \
+  do {                       \
+  } while (0)
+#define SDEBUG2(format, ...) \
+  do {                       \
+  } while (0)
+#define SDEBUG3(format, ...) \
+  do {                       \
+  } while (0)
+#define SDEBUG4(format, ...) \
+  do {                       \
+  } while (0)
+#else
+#define SDEBUG(format, ...) SLOG(::wpi::WPI_LOG_DEBUG, format, __VA_ARGS__)
+#define SDEBUG1(format, ...) SLOG(::wpi::WPI_LOG_DEBUG1, format, __VA_ARGS__)
+#define SDEBUG2(format, ...) SLOG(::wpi::WPI_LOG_DEBUG2, format, __VA_ARGS__)
+#define SDEBUG3(format, ...) SLOG(::wpi::WPI_LOG_DEBUG3, format, __VA_ARGS__)
+#define SDEBUG4(format, ...) SLOG(::wpi::WPI_LOG_DEBUG4, format, __VA_ARGS__)
+#endif
 
 #endif  // CSCORE_LOG_H_
diff --git a/cscore/src/main/native/cpp/MjpegServerImpl.cpp b/cscore/src/main/native/cpp/MjpegServerImpl.cpp
index e30b745..92c23ff 100644
--- a/cscore/src/main/native/cpp/MjpegServerImpl.cpp
+++ b/cscore/src/main/native/cpp/MjpegServerImpl.cpp
@@ -1,17 +1,17 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "MjpegServerImpl.h"
 
 #include <chrono>
 
+#include <fmt/format.h>
 #include <wpi/HttpUtil.h>
 #include <wpi/SmallString.h>
+#include <wpi/StringExtras.h>
 #include <wpi/TCPAcceptor.h>
+#include <wpi/fmt/raw_ostream.h>
 #include <wpi/raw_socket_istream.h>
 #include <wpi/raw_socket_ostream.h>
 
@@ -75,13 +75,13 @@
 
 class MjpegServerImpl::ConnThread : public wpi::SafeThread {
  public:
-  explicit ConnThread(const wpi::Twine& name, wpi::Logger& logger)
-      : m_name(name.str()), m_logger(logger) {}
+  explicit ConnThread(std::string_view name, wpi::Logger& logger)
+      : m_name(name), m_logger(logger) {}
 
-  void Main();
+  void Main() override;
 
   bool ProcessCommand(wpi::raw_ostream& os, SourceImpl& source,
-                      wpi::StringRef parameters, bool respond);
+                      std::string_view parameters, bool respond);
   void SendJSON(wpi::raw_ostream& os, SourceImpl& source, bool header);
   void SendHTMLHeadTitle(wpi::raw_ostream& os) const;
   void SendHTML(wpi::raw_ostream& os, SourceImpl& source, bool header);
@@ -102,7 +102,7 @@
   std::string m_name;
   wpi::Logger& m_logger;
 
-  wpi::StringRef GetName() { return m_name; }
+  std::string_view GetName() { return m_name; }
 
   std::shared_ptr<SourceImpl> GetSource() {
     std::scoped_lock lock(m_mutex);
@@ -111,13 +111,17 @@
 
   void StartStream() {
     std::scoped_lock lock(m_mutex);
-    if (m_source) m_source->EnableSink();
+    if (m_source) {
+      m_source->EnableSink();
+    }
     m_streaming = true;
   }
 
   void StopStream() {
     std::scoped_lock lock(m_mutex);
-    if (m_source) m_source->DisableSink();
+    if (m_source) {
+      m_source->DisableSink();
+    }
     m_streaming = false;
   }
 };
@@ -129,10 +133,9 @@
 // Using cached pictures would lead to showing old/outdated pictures.
 // Many browsers seem to ignore, or at least not always obey, those headers.
 static void SendHeader(wpi::raw_ostream& os, int code,
-                       const wpi::Twine& codeText,
-                       const wpi::Twine& contentType,
-                       const wpi::Twine& extra = wpi::Twine{}) {
-  os << "HTTP/1.0 " << code << ' ' << codeText << "\r\n";
+                       std::string_view codeText, std::string_view contentType,
+                       std::string_view extra = {}) {
+  fmt::print(os, "HTTP/1.0 {} {}\r\n", code, codeText);
   os << "Connection: close\r\n"
         "Server: CameraServer/1.0\r\n"
         "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, "
@@ -141,9 +144,9 @@
         "Expires: Mon, 3 Jan 2000 12:34:56 GMT\r\n";
   os << "Content-Type: " << contentType << "\r\n";
   os << "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: *\r\n";
-  wpi::SmallString<128> extraBuf;
-  wpi::StringRef extraStr = extra.toStringRef(extraBuf);
-  if (!extraStr.empty()) os << extraStr << "\r\n";
+  if (!extra.empty()) {
+    os << extra << "\r\n";
+  }
   os << "\r\n";  // header ends with a blank line
 }
 
@@ -151,8 +154,8 @@
 // @param code HTTP error code (e.g. 404)
 // @param message Additional message text
 static void SendError(wpi::raw_ostream& os, int code,
-                      const wpi::Twine& message) {
-  wpi::StringRef codeText, extra, baseMessage;
+                      std::string_view message) {
+  std::string_view codeText, extra, baseMessage;
   switch (code) {
     case 401:
       codeText = "Unauthorized";
@@ -192,62 +195,61 @@
 // Perform a command specified by HTTP GET parameters.
 bool MjpegServerImpl::ConnThread::ProcessCommand(wpi::raw_ostream& os,
                                                  SourceImpl& source,
-                                                 wpi::StringRef parameters,
+                                                 std::string_view parameters,
                                                  bool respond) {
   wpi::SmallString<256> responseBuf;
   wpi::raw_svector_ostream response{responseBuf};
   // command format: param1=value1&param2=value2...
   while (!parameters.empty()) {
     // split out next param and value
-    wpi::StringRef rawParam, rawValue;
-    std::tie(rawParam, parameters) = parameters.split('&');
-    if (rawParam.empty()) continue;  // ignore "&&"
-    std::tie(rawParam, rawValue) = rawParam.split('=');
-    if (rawParam.empty() || rawValue.empty()) continue;  // ignore "param="
-    SDEBUG4("HTTP parameter \"" << rawParam << "\" value \"" << rawValue
-                                << "\"");
+    std::string_view rawParam, rawValue;
+    std::tie(rawParam, parameters) = wpi::split(parameters, '&');
+    if (rawParam.empty()) {
+      continue;  // ignore "&&"
+    }
+    std::tie(rawParam, rawValue) = wpi::split(rawParam, '=');
+    if (rawParam.empty() || rawValue.empty()) {
+      continue;  // ignore "param="
+    }
+    SDEBUG4("HTTP parameter \"{}\" value \"{}\"", rawParam, rawValue);
 
     // unescape param
     bool error = false;
     wpi::SmallString<64> paramBuf;
-    wpi::StringRef param = wpi::UnescapeURI(rawParam, paramBuf, &error);
+    std::string_view param = wpi::UnescapeURI(rawParam, paramBuf, &error);
     if (error) {
-      wpi::SmallString<128> error;
-      wpi::raw_svector_ostream oss{error};
-      oss << "could not unescape parameter \"" << rawParam << "\"";
-      SendError(os, 500, error.str());
-      SDEBUG(error.str());
+      auto estr = fmt::format("could not unescape parameter \"{}\"", rawParam);
+      SendError(os, 500, estr);
+      SDEBUG("{}", estr);
       return false;
     }
 
     // unescape value
     wpi::SmallString<64> valueBuf;
-    wpi::StringRef value = wpi::UnescapeURI(rawValue, valueBuf, &error);
+    std::string_view value = wpi::UnescapeURI(rawValue, valueBuf, &error);
     if (error) {
-      wpi::SmallString<128> error;
-      wpi::raw_svector_ostream oss{error};
-      oss << "could not unescape value \"" << rawValue << "\"";
-      SendError(os, 500, error.str());
-      SDEBUG(error.str());
+      auto estr = fmt::format("could not unescape value \"{}\"", rawValue);
+      SendError(os, 500, estr);
+      SDEBUG("{}", estr);
       return false;
     }
 
     // Handle resolution, compression, and FPS.  These are handled locally
     // rather than passed to the source.
     if (param == "resolution") {
-      wpi::StringRef widthStr, heightStr;
-      std::tie(widthStr, heightStr) = value.split('x');
-      int width, height;
-      if (widthStr.getAsInteger(10, width)) {
+      auto [widthStr, heightStr] = wpi::split(value, 'x');
+      int width = wpi::parse_integer<int>(widthStr, 10).value_or(-1);
+      int height = wpi::parse_integer<int>(heightStr, 10).value_or(-1);
+      if (width < 0) {
         response << param << ": \"width is not an integer\"\r\n";
-        SWARNING("HTTP parameter \"" << param << "\" width \"" << widthStr
-                                     << "\" is not an integer");
+        SWARNING("HTTP parameter \"{}\" width \"{}\" is not an integer", param,
+                 widthStr);
         continue;
       }
-      if (heightStr.getAsInteger(10, height)) {
+      if (height < 0) {
         response << param << ": \"height is not an integer\"\r\n";
-        SWARNING("HTTP parameter \"" << param << "\" height \"" << heightStr
-                                     << "\" is not an integer");
+        SWARNING("HTTP parameter \"{}\" height \"{}\" is not an integer", param,
+                 heightStr);
         continue;
       }
       m_width = width;
@@ -257,41 +259,39 @@
     }
 
     if (param == "fps") {
-      int fps;
-      if (value.getAsInteger(10, fps)) {
-        response << param << ": \"invalid integer\"\r\n";
-        SWARNING("HTTP parameter \"" << param << "\" value \"" << value
-                                     << "\" is not an integer");
-        continue;
-      } else {
-        m_fps = fps;
+      if (auto v = wpi::parse_integer<int>(value, 10)) {
+        m_fps = v.value();
         response << param << ": \"ok\"\r\n";
+      } else {
+        response << param << ": \"invalid integer\"\r\n";
+        SWARNING("HTTP parameter \"{}\" value \"{}\" is not an integer", param,
+                 value);
       }
       continue;
     }
 
     if (param == "compression") {
-      int compression;
-      if (value.getAsInteger(10, compression)) {
-        response << param << ": \"invalid integer\"\r\n";
-        SWARNING("HTTP parameter \"" << param << "\" value \"" << value
-                                     << "\" is not an integer");
-        continue;
-      } else {
-        m_compression = compression;
+      if (auto v = wpi::parse_integer<int>(value, 10)) {
+        m_compression = v.value();
         response << param << ": \"ok\"\r\n";
+      } else {
+        response << param << ": \"invalid integer\"\r\n";
+        SWARNING("HTTP parameter \"{}\" value \"{}\" is not an integer", param,
+                 value);
       }
       continue;
     }
 
     // ignore name parameter
-    if (param == "name") continue;
+    if (param == "name") {
+      continue;
+    }
 
     // try to assign parameter
     auto prop = source.GetPropertyIndex(param);
     if (!prop) {
       response << param << ": \"ignored\"\r\n";
-      SWARNING("ignoring HTTP parameter \"" << param << "\"");
+      SWARNING("ignoring HTTP parameter \"{}\"", param);
       continue;
     }
 
@@ -301,21 +301,20 @@
       case CS_PROP_BOOLEAN:
       case CS_PROP_INTEGER:
       case CS_PROP_ENUM: {
-        int val = 0;
-        if (value.getAsInteger(10, val)) {
-          response << param << ": \"invalid integer\"\r\n";
-          SWARNING("HTTP parameter \"" << param << "\" value \"" << value
-                                       << "\" is not an integer");
+        if (auto v = wpi::parse_integer<int>(value, 10)) {
+          fmt::print(response, "{}: {}\r\n", param, v.value());
+          SDEBUG4("HTTP parameter \"{}\" value {}", param, value);
+          source.SetProperty(prop, v.value(), &status);
         } else {
-          response << param << ": " << val << "\r\n";
-          SDEBUG4("HTTP parameter \"" << param << "\" value " << value);
-          source.SetProperty(prop, val, &status);
+          response << param << ": \"invalid integer\"\r\n";
+          SWARNING("HTTP parameter \"{}\" value \"{}\" is not an integer",
+                   param, value);
         }
         break;
       }
       case CS_PROP_STRING: {
         response << param << ": \"ok\"\r\n";
-        SDEBUG4("HTTP parameter \"" << param << "\" value \"" << value << "\"");
+        SDEBUG4("HTTP parameter \"{}\" value \"{}\"", param, value);
         source.SetStringProperty(prop, value, &status);
         break;
       }
@@ -342,7 +341,9 @@
 // Send the root html file with controls for all the settable properties.
 void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
                                            SourceImpl& source, bool header) {
-  if (header) SendHeader(os, 200, "OK", "text/html");
+  if (header) {
+    SendHeader(os, 200, "OK", "text/html");
+  }
 
   SendHTMLHeadTitle(os);
   os << startRootPage;
@@ -351,31 +352,35 @@
   for (auto prop : source.EnumerateProperties(properties_vec, &status)) {
     wpi::SmallString<128> name_buf;
     auto name = source.GetPropertyName(prop, name_buf, &status);
-    if (name.startswith("raw_")) continue;
+    if (wpi::starts_with(name, "raw_")) {
+      continue;
+    }
     auto kind = source.GetPropertyKind(prop);
-    os << "<p />"
-       << "<label for=\"" << name << "\">" << name << "</label>\n";
+    fmt::print(os, "<p /><label for=\"{0}\">{0}</label>\n", name);
     switch (kind) {
       case CS_PROP_BOOLEAN:
-        os << "<input id=\"" << name
-           << "\" type=\"checkbox\" onclick=\"update('" << name
-           << "', this.checked ? 1 : 0)\" ";
-        if (source.GetProperty(prop, &status) != 0)
+        fmt::print(os,
+                   "<input id=\"{0}\" type=\"checkbox\" "
+                   "onclick=\"update('{0}', this.checked ? 1 : 0)\" ",
+                   name);
+        if (source.GetProperty(prop, &status) != 0) {
           os << "checked />\n";
-        else
+        } else {
           os << " />\n";
+        }
         break;
       case CS_PROP_INTEGER: {
         auto valI = source.GetProperty(prop, &status);
         auto min = source.GetPropertyMin(prop, &status);
         auto max = source.GetPropertyMax(prop, &status);
         auto step = source.GetPropertyStep(prop, &status);
-        os << "<input type=\"range\" min=\"" << min << "\" max=\"" << max
-           << "\" value=\"" << valI << "\" id=\"" << name << "\" step=\""
-           << step << "\" oninput=\"updateInt('#" << name << "op', '" << name
-           << "', value)\" />\n";
-        os << "<output for=\"" << name << "\" id=\"" << name << "op\">" << valI
-           << "</output>\n";
+        fmt::print(os,
+                   "<input type=\"range\" min=\"{1}\" max=\"{2}\" "
+                   "value=\"{3}\" id=\"{0}\" step=\"{4}\" "
+                   "oninput=\"updateInt('#{0}op', '{0}', value)\" />\n",
+                   name, min, max, valI, step);
+        fmt::print(os, "<output for=\"{0}\" id=\"{0}op\">{1}</output>\n", name,
+                   valI);
         break;
       }
       case CS_PROP_ENUM: {
@@ -384,29 +389,36 @@
         int j = 0;
         for (auto choice = choices.begin(), end = choices.end(); choice != end;
              ++j, ++choice) {
-          if (choice->empty()) continue;  // skip empty choices
+          if (choice->empty()) {
+            continue;  // skip empty choices
+          }
           // replace any non-printable characters in name with spaces
           wpi::SmallString<128> ch_name;
-          for (char ch : *choice)
-            ch_name.push_back(std::isprint(ch) ? ch : ' ');
-          os << "<input id=\"" << name << j << "\" type=\"radio\" name=\""
-             << name << "\" value=\"" << ch_name << "\" onclick=\"update('"
-             << name << "', " << j << ")\"";
+          for (char ch : *choice) {
+            ch_name.push_back(wpi::isPrint(ch) ? ch : ' ');
+          }
+          fmt::print(os,
+                     "<input id=\"{0}{1}\" type=\"radio\" name=\"{0}\" "
+                     "value=\"{2}\" onclick=\"update('{0}', {1})\"",
+                     name, j, ch_name);
           if (j == valE) {
             os << " checked";
           }
-          os << " /><label for=\"" << name << j << "\">" << ch_name
-             << "</label>\n";
+          fmt::print(os, " /><label for=\"{}{}\">{}</label>\n", name, j,
+                     ch_name);
         }
         break;
       }
       case CS_PROP_STRING: {
         wpi::SmallString<128> strval_buf;
-        os << "<input type=\"text\" id=\"" << name << "box\" name=\"" << name
-           << "\" value=\""
-           << source.GetStringProperty(prop, strval_buf, &status) << "\" />\n";
-        os << "<input type=\"button\" value =\"Submit\" onclick=\"update('"
-           << name << "', " << name << "box.value)\" />\n";
+        fmt::print(os,
+                   "<input type=\"text\" id=\"{0}box\" name=\"{0}\" "
+                   "value=\"{1}\" />\n",
+                   name, source.GetStringProperty(prop, strval_buf, &status));
+        fmt::print(os,
+                   "<input type=\"button\" value =\"Submit\" "
+                   "onclick=\"update('{0}', {0}box.value)\" />\n",
+                   name);
         break;
       }
       default:
@@ -419,8 +431,9 @@
                                &status);
   if (status == CS_OK) {
     os << "<p>USB device path: " << info.path << '\n';
-    for (auto&& path : info.otherPaths)
+    for (auto&& path : info.otherPaths) {
       os << "<p>Alternate device path: " << path << '\n';
+    }
   }
 
   os << "<p>Supported Video Modes:</p>\n";
@@ -451,10 +464,8 @@
         os << "unknown";
         break;
     }
-    os << "</td><td>" << mode.width;
-    os << "</td><td>" << mode.height;
-    os << "</td><td>" << mode.fps;
-    os << "</td></tr>";
+    fmt::print(os, "</td><td>{}</td><td>{}</td><td>{}</td></tr>", mode.width,
+               mode.height, mode.fps);
   }
   os << "</table>\n";
   os << endRootPage << "\r\n";
@@ -464,35 +475,39 @@
 // Send a JSON file which is contains information about the source parameters.
 void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os,
                                            SourceImpl& source, bool header) {
-  if (header) SendHeader(os, 200, "OK", "application/json");
+  if (header) {
+    SendHeader(os, 200, "OK", "application/json");
+  }
 
   os << "{\n\"controls\": [\n";
   wpi::SmallVector<int, 32> properties_vec;
   bool first = true;
   CS_Status status = 0;
   for (auto prop : source.EnumerateProperties(properties_vec, &status)) {
-    if (first)
+    if (first) {
       first = false;
-    else
+    } else {
       os << ",\n";
+    }
     os << '{';
     wpi::SmallString<128> name_buf;
     auto name = source.GetPropertyName(prop, name_buf, &status);
     auto kind = source.GetPropertyKind(prop);
-    os << "\n\"name\": \"" << name << '"';
-    os << ",\n\"id\": \"" << prop << '"';
-    os << ",\n\"type\": \"" << kind << '"';
-    os << ",\n\"min\": \"" << source.GetPropertyMin(prop, &status) << '"';
-    os << ",\n\"max\": \"" << source.GetPropertyMax(prop, &status) << '"';
-    os << ",\n\"step\": \"" << source.GetPropertyStep(prop, &status) << '"';
-    os << ",\n\"default\": \"" << source.GetPropertyDefault(prop, &status)
-       << '"';
+    fmt::print(os, "\n\"name\": \"{}\"", name);
+    fmt::print(os, ",\n\"id\": \"{}\"", prop);
+    fmt::print(os, ",\n\"type\": \"{}\"", kind);
+    fmt::print(os, ",\n\"min\": \"{}\"", source.GetPropertyMin(prop, &status));
+    fmt::print(os, ",\n\"max\": \"{}\"", source.GetPropertyMax(prop, &status));
+    fmt::print(os, ",\n\"step\": \"{}\"",
+               source.GetPropertyStep(prop, &status));
+    fmt::print(os, ",\n\"default\": \"{}\"",
+               source.GetPropertyDefault(prop, &status));
     os << ",\n\"value\": \"";
     switch (kind) {
       case CS_PROP_BOOLEAN:
       case CS_PROP_INTEGER:
       case CS_PROP_ENUM:
-        os << source.GetProperty(prop, &status);
+        fmt::print(os, "{}", source.GetProperty(prop, &status));
         break;
       case CS_PROP_STRING: {
         wpi::SmallString<128> strval_buf;
@@ -514,11 +529,15 @@
       int j = 0;
       for (auto choice = choices.begin(), end = choices.end(); choice != end;
            ++j, ++choice) {
-        if (j != 0) os << ", ";
+        if (j != 0) {
+          os << ", ";
+        }
         // replace any non-printable characters in name with spaces
         wpi::SmallString<128> ch_name;
-        for (char ch : *choice) ch_name.push_back(std::isprint(ch) ? ch : ' ');
-        os << '"' << j << "\": \"" << ch_name << '"';
+        for (char ch : *choice) {
+          ch_name.push_back(std::isprint(ch) ? ch : ' ');
+        }
+        fmt::print(os, "\"{}\": \"{}\"", j, ch_name);
       }
       os << "}\n";
     }
@@ -527,10 +546,11 @@
   os << "\n],\n\"modes\": [\n";
   first = true;
   for (auto mode : source.EnumerateVideoModes(&status)) {
-    if (first)
+    if (first) {
       first = false;
-    else
+    } else {
       os << ",\n";
+    }
     os << '{';
     os << "\n\"pixelFormat\": \"";
     switch (mode.pixelFormat) {
@@ -553,29 +573,26 @@
         os << "unknown";
         break;
     }
-    os << "\",\n\"width\": \"" << mode.width << '"';
-    os << ",\n\"height\": \"" << mode.height << '"';
-    os << ",\n\"fps\": \"" << mode.fps << '"';
+    fmt::print(os, "\",\n\"width\": \"{}\"", mode.width);
+    fmt::print(os, ",\n\"height\": \"{}\"", mode.height);
+    fmt::print(os, ",\n\"fps\": \"{}\"", mode.fps);
     os << '}';
   }
   os << "\n]\n}\n";
   os.flush();
 }
 
-MjpegServerImpl::MjpegServerImpl(const wpi::Twine& name, wpi::Logger& logger,
+MjpegServerImpl::MjpegServerImpl(std::string_view name, wpi::Logger& logger,
                                  Notifier& notifier, Telemetry& telemetry,
-                                 const wpi::Twine& listenAddress, int port,
+                                 std::string_view listenAddress, int port,
                                  std::unique_ptr<wpi::NetworkAcceptor> acceptor)
     : SinkImpl{name, logger, notifier, telemetry},
-      m_listenAddress(listenAddress.str()),
+      m_listenAddress(listenAddress),
       m_port(port),
       m_acceptor{std::move(acceptor)} {
   m_active = true;
 
-  wpi::SmallString<128> descBuf;
-  wpi::raw_svector_ostream desc{descBuf};
-  desc << "HTTP Server on port " << port;
-  SetDescription(desc.str());
+  SetDescription(fmt::format("HTTP Server on port {}", port));
 
   // Create properties
   m_widthProp = CreateProperty("width", [] {
@@ -599,7 +616,9 @@
   m_serverThread = std::thread(&MjpegServerImpl::ServerThreadMain, this);
 }
 
-MjpegServerImpl::~MjpegServerImpl() { Stop(); }
+MjpegServerImpl::~MjpegServerImpl() {
+  Stop();
+}
 
 void MjpegServerImpl::Stop() {
   m_active = false;
@@ -608,24 +627,30 @@
   m_acceptor->shutdown();
 
   // join server thread
-  if (m_serverThread.joinable()) m_serverThread.join();
+  if (m_serverThread.joinable()) {
+    m_serverThread.join();
+  }
 
   // close streams
   for (auto& connThread : m_connThreads) {
     if (auto thr = connThread.GetThread()) {
-      if (thr->m_stream) thr->m_stream->close();
+      if (thr->m_stream) {
+        thr->m_stream->close();
+      }
     }
     connThread.Stop();
   }
 
   // wake up connection threads by forcing an empty frame to be sent
-  if (auto source = GetSource()) source->Wakeup();
+  if (auto source = GetSource()) {
+    source->Wakeup();
+  }
 }
 
 // Send HTTP response and a stream of JPG-frames
 void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
   if (m_noStreaming) {
-    SERROR("Too many simultaneous client streams");
+    SERROR("{}", "Too many simultaneous client streams");
     SendError(os, 503, "Too many simultaneous streams");
     return;
   }
@@ -638,14 +663,18 @@
   SendHeader(oss, 200, "OK", "multipart/x-mixed-replace;boundary=" BOUNDARY);
   os << oss.str();
 
-  SDEBUG("Headers send, sending stream now");
+  SDEBUG("{}", "Headers send, sending stream now");
 
   Frame::Time lastFrameTime = 0;
   Frame::Time timePerFrame = 0;
-  if (m_fps != 0) timePerFrame = 1000000.0 / m_fps;
+  if (m_fps != 0) {
+    timePerFrame = 1000000.0 / m_fps;
+  }
   Frame::Time averageFrameTime = 0;
   Frame::Time averagePeriod = 1000000;  // 1 second window
-  if (averagePeriod < timePerFrame) averagePeriod = timePerFrame * 10;
+  if (averagePeriod < timePerFrame) {
+    averagePeriod = timePerFrame * 10;
+  }
 
   StartStream();
   while (m_active && !os.has_error()) {
@@ -656,9 +685,11 @@
       std::this_thread::sleep_for(std::chrono::milliseconds(200));
       continue;
     }
-    SDEBUG4("waiting for frame");
+    SDEBUG4("{}", "waiting for frame");
     Frame frame = source->GetNextFrame(0.225);  // blocks
-    if (!m_active) break;
+    if (!m_active) {
+      break;
+    }
     if (!frame) {
       // Bad frame; sleep for 20 ms so we don't consume all processor time.
       os << "\r\n";  // Keep connection alive
@@ -717,7 +748,7 @@
         continue;
     }
 
-    SDEBUG4("sending frame size=" << size << " addDHT=" << addDHT);
+    SDEBUG4("sending frame size={} addDHT={}", size, addDHT);
 
     // print the individual mimetype and the length
     // sending the content-length fixes random stream disruption observed
@@ -726,18 +757,18 @@
     double timestamp = lastFrameTime / 1000000.0;
     header.clear();
     oss << "\r\n--" BOUNDARY "\r\n"
-        << "Content-Type: image/jpeg\r\n"
-        << "Content-Length: " << size << "\r\n"
-        << "X-Timestamp: " << timestamp << "\r\n"
-        << "\r\n";
+        << "Content-Type: image/jpeg\r\n";
+    fmt::print(oss, "Content-Length: {}\r\n", size);
+    fmt::print(oss, "X-Timestamp: {}\r\n", timestamp);
+    oss << "\r\n";
     os << oss.str();
     if (addDHT) {
       // Insert DHT data immediately before SOF
-      os << wpi::StringRef(data, locSOF);
+      os << std::string_view(data, locSOF);
       os << JpegGetDHT();
-      os << wpi::StringRef(data + locSOF, image->size() - locSOF);
+      os << std::string_view(data + locSOF, image->size() - locSOF);
     } else {
-      os << wpi::StringRef(data, size);
+      os << std::string_view(data, size);
     }
     // os.flush();
   }
@@ -750,48 +781,50 @@
 
   // Read the request string from the stream
   wpi::SmallString<128> reqBuf;
-  wpi::StringRef req = is.getline(reqBuf, 4096);
+  std::string_view req = is.getline(reqBuf, 4096);
   if (is.has_error()) {
-    SDEBUG("error getting request string");
+    SDEBUG("{}", "error getting request string");
     return;
   }
 
   enum { kCommand, kStream, kGetSettings, kGetSourceConfig, kRootPage } kind;
-  wpi::StringRef parameters;
+  std::string_view parameters;
   size_t pos;
 
-  SDEBUG("HTTP request: '" << req << "'\n");
+  SDEBUG("HTTP request: '{}'\n", req);
 
   // Determine request kind.  Most of these are for mjpgstreamer
   // compatibility, others are for Axis camera compatibility.
-  if ((pos = req.find("POST /stream")) != wpi::StringRef::npos) {
+  if ((pos = req.find("POST /stream")) != std::string_view::npos) {
     kind = kStream;
     parameters = req.substr(req.find('?', pos + 12)).substr(1);
-  } else if ((pos = req.find("GET /?action=stream")) != wpi::StringRef::npos) {
+  } else if ((pos = req.find("GET /?action=stream")) !=
+             std::string_view::npos) {
     kind = kStream;
     parameters = req.substr(req.find('&', pos + 19)).substr(1);
-  } else if ((pos = req.find("GET /stream.mjpg")) != wpi::StringRef::npos) {
+  } else if ((pos = req.find("GET /stream.mjpg")) != std::string_view::npos) {
     kind = kStream;
     parameters = req.substr(req.find('?', pos + 16)).substr(1);
-  } else if (req.find("GET /settings") != wpi::StringRef::npos &&
-             req.find(".json") != wpi::StringRef::npos) {
+  } else if (req.find("GET /settings") != std::string_view::npos &&
+             req.find(".json") != std::string_view::npos) {
     kind = kGetSettings;
-  } else if (req.find("GET /config") != wpi::StringRef::npos &&
-             req.find(".json") != wpi::StringRef::npos) {
+  } else if (req.find("GET /config") != std::string_view::npos &&
+             req.find(".json") != std::string_view::npos) {
     kind = kGetSourceConfig;
-  } else if (req.find("GET /input") != wpi::StringRef::npos &&
-             req.find(".json") != wpi::StringRef::npos) {
+  } else if (req.find("GET /input") != std::string_view::npos &&
+             req.find(".json") != std::string_view::npos) {
     kind = kGetSettings;
-  } else if (req.find("GET /output") != wpi::StringRef::npos &&
-             req.find(".json") != wpi::StringRef::npos) {
+  } else if (req.find("GET /output") != std::string_view::npos &&
+             req.find(".json") != std::string_view::npos) {
     kind = kGetSettings;
-  } else if ((pos = req.find("GET /?action=command")) != wpi::StringRef::npos) {
+  } else if ((pos = req.find("GET /?action=command")) !=
+             std::string_view::npos) {
     kind = kCommand;
     parameters = req.substr(req.find('&', pos + 20)).substr(1);
-  } else if (req.find("GET / ") != wpi::StringRef::npos || req == "GET /\n") {
+  } else if (req.find("GET / ") != std::string_view::npos || req == "GET /\n") {
     kind = kRootPage;
   } else {
-    SDEBUG("HTTP request resource not found");
+    SDEBUG("{}", "HTTP request resource not found");
     SendError(os, 404, "Resource not found");
     return;
   }
@@ -801,22 +834,28 @@
       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
       "-=&1234567890%./");
   parameters = parameters.substr(0, pos);
-  SDEBUG("command parameters: \"" << parameters << "\"");
+  SDEBUG("command parameters: \"{}\"", parameters);
 
   // Read the rest of the HTTP request.
   // The end of the request is marked by a single, empty line
   wpi::SmallString<128> lineBuf;
   for (;;) {
-    if (is.getline(lineBuf, 4096).startswith("\n")) break;
-    if (is.has_error()) return;
+    if (wpi::starts_with(is.getline(lineBuf, 4096), "\n")) {
+      break;
+    }
+    if (is.has_error()) {
+      return;
+    }
   }
 
   // Send response
   switch (kind) {
     case kStream:
       if (auto source = GetSource()) {
-        SDEBUG("request for stream " << source->GetName());
-        if (!ProcessCommand(os, *source, parameters, false)) return;
+        SDEBUG("request for stream {}", source->GetName());
+        if (!ProcessCommand(os, *source, parameters, false)) {
+          return;
+        }
       }
       SendStream(os);
       break;
@@ -827,18 +866,19 @@
         SendHeader(os, 200, "OK", "text/plain");
         os << "Ignored due to no connected source."
            << "\r\n";
-        SDEBUG("Ignored due to no connected source.");
+        SDEBUG("{}", "Ignored due to no connected source.");
       }
       break;
     case kGetSettings:
-      SDEBUG("request for JSON file");
-      if (auto source = GetSource())
+      SDEBUG("{}", "request for JSON file");
+      if (auto source = GetSource()) {
         SendJSON(os, *source, true);
-      else
+      } else {
         SendError(os, 404, "Resource not found");
+      }
       break;
     case kGetSourceConfig:
-      SDEBUG("request for JSON file");
+      SDEBUG("{}", "request for JSON file");
       if (auto source = GetSource()) {
         SendHeader(os, 200, "OK", "application/json");
         CS_Status status = CS_OK;
@@ -849,7 +889,7 @@
       }
       break;
     case kRootPage:
-      SDEBUG("request for root page");
+      SDEBUG("{}", "request for root page");
       SendHeader(os, 200, "OK", "text/html");
       if (auto source = GetSource()) {
         SendHTML(os, *source, false);
@@ -860,7 +900,7 @@
       break;
   }
 
-  SDEBUG("leaving HTTP client thread");
+  SDEBUG("{}", "leaving HTTP client thread");
 }
 
 // worker thread for clients that connected to this server
@@ -869,7 +909,9 @@
   while (m_active) {
     while (!m_stream) {
       m_cond.wait(lock);
-      if (!m_active) return;
+      if (!m_active) {
+        return;
+      }
     }
     lock.unlock();
     ProcessRequest();
@@ -885,16 +927,18 @@
     return;
   }
 
-  SDEBUG("waiting for clients to connect");
+  SDEBUG("{}", "waiting for clients to connect");
   while (m_active) {
     auto stream = m_acceptor->accept();
     if (!stream) {
       m_active = false;
       return;
     }
-    if (!m_active) return;
+    if (!m_active) {
+      return;
+    }
 
-    SDEBUG("client connection from " << stream->getPeerIP());
+    SDEBUG("client connection from {}", stream->getPeerIP());
 
     auto source = GetSource();
 
@@ -933,7 +977,7 @@
     thr->m_cond.notify_one();
   }
 
-  SDEBUG("leaving server thread");
+  SDEBUG("{}", "leaving server thread");
 }
 
 void MjpegServerImpl::SetSourceImpl(std::shared_ptr<SourceImpl> source) {
@@ -942,9 +986,13 @@
     if (auto thr = connThread.GetThread()) {
       if (thr->m_source != source) {
         bool streaming = thr->m_streaming;
-        if (thr->m_source && streaming) thr->m_source->DisableSink();
+        if (thr->m_source && streaming) {
+          thr->m_source->DisableSink();
+        }
         thr->m_source = source;
-        if (source && streaming) thr->m_source->EnableSink();
+        if (source && streaming) {
+          thr->m_source->EnableSink();
+        }
       }
     }
   }
@@ -952,19 +1000,15 @@
 
 namespace cs {
 
-CS_Sink CreateMjpegServer(const wpi::Twine& name,
-                          const wpi::Twine& listenAddress, int port,
-                          CS_Status* status) {
+CS_Sink CreateMjpegServer(std::string_view name, std::string_view listenAddress,
+                          int port, CS_Status* status) {
   auto& inst = Instance::GetInstance();
-  wpi::SmallString<128> listenAddressBuf;
   return inst.CreateSink(
       CS_SINK_MJPEG,
       std::make_shared<MjpegServerImpl>(
           name, inst.logger, inst.notifier, inst.telemetry, listenAddress, port,
-          std::unique_ptr<wpi::NetworkAcceptor>(new wpi::TCPAcceptor(
-              port,
-              listenAddress.toNullTerminatedStringRef(listenAddressBuf).data(),
-              inst.logger))));
+          std::unique_ptr<wpi::NetworkAcceptor>(
+              new wpi::TCPAcceptor(port, listenAddress, inst.logger))));
 }
 
 std::string GetMjpegServerListenAddress(CS_Sink sink, CS_Status* status) {
diff --git a/cscore/src/main/native/cpp/MjpegServerImpl.h b/cscore/src/main/native/cpp/MjpegServerImpl.h
index db60b86..e323e69 100644
--- a/cscore/src/main/native/cpp/MjpegServerImpl.h
+++ b/cscore/src/main/native/cpp/MjpegServerImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_MJPEGSERVERIMPL_H_
 #define CSCORE_MJPEGSERVERIMPL_H_
@@ -11,6 +8,7 @@
 #include <atomic>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <thread>
 #include <vector>
 
@@ -18,7 +16,6 @@
 #include <wpi/NetworkStream.h>
 #include <wpi/SafeThread.h>
 #include <wpi/SmallVector.h>
-#include <wpi/Twine.h>
 #include <wpi/raw_istream.h>
 #include <wpi/raw_ostream.h>
 #include <wpi/raw_socket_ostream.h>
@@ -31,9 +28,9 @@
 
 class MjpegServerImpl : public SinkImpl {
  public:
-  MjpegServerImpl(const wpi::Twine& name, wpi::Logger& logger,
+  MjpegServerImpl(std::string_view name, wpi::Logger& logger,
                   Notifier& notifier, Telemetry& telemetry,
-                  const wpi::Twine& listenAddress, int port,
+                  std::string_view listenAddress, int port,
                   std::unique_ptr<wpi::NetworkAcceptor> acceptor);
   ~MjpegServerImpl() override;
 
diff --git a/cscore/src/main/native/cpp/NetworkListener.h b/cscore/src/main/native/cpp/NetworkListener.h
index 6d49adc..d48de18 100644
--- a/cscore/src/main/native/cpp/NetworkListener.h
+++ b/cscore/src/main/native/cpp/NetworkListener.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_NETWORKLISTENER_H_
 #define CSCORE_NETWORKLISTENER_H_
diff --git a/cscore/src/main/native/cpp/Notifier.cpp b/cscore/src/main/native/cpp/Notifier.cpp
index e4645c9..3711896 100644
--- a/cscore/src/main/native/cpp/Notifier.cpp
+++ b/cscore/src/main/native/cpp/Notifier.cpp
@@ -1,13 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "Notifier.h"
 
 #include <queue>
+#include <utility>
 #include <vector>
 
 #include "Handle.h"
@@ -17,144 +15,26 @@
 
 using namespace cs;
 
-bool Notifier::s_destroyed = false;
+Notifier::Notifier() {}
 
-namespace {
-// Vector which provides an integrated freelist for removal and reuse of
-// individual elements.
-template <typename T>
-class UidVector {
- public:
-  using size_type = typename std::vector<T>::size_type;
+Notifier::~Notifier() {}
 
-  size_type size() const { return m_vector.size(); }
-  T& operator[](size_type i) { return m_vector[i]; }
-  const T& operator[](size_type i) const { return m_vector[i]; }
-
-  // Add a new T to the vector.  If there are elements on the freelist,
-  // reuses the last one; otherwise adds to the end of the vector.
-  // Returns the resulting element index (+1).
-  template <class... Args>
-  unsigned int emplace_back(Args&&... args) {
-    unsigned int uid;
-    if (m_free.empty()) {
-      uid = m_vector.size();
-      m_vector.emplace_back(std::forward<Args>(args)...);
-    } else {
-      uid = m_free.back();
-      m_free.pop_back();
-      m_vector[uid] = T(std::forward<Args>(args)...);
-    }
-    return uid + 1;
-  }
-
-  // Removes the identified element by replacing it with a default-constructed
-  // one.  The element is added to the freelist for later reuse.
-  void erase(unsigned int uid) {
-    --uid;
-    if (uid >= m_vector.size() || !m_vector[uid]) return;
-    m_free.push_back(uid);
-    m_vector[uid] = T();
-  }
-
- private:
-  std::vector<T> m_vector;
-  std::vector<unsigned int> m_free;
-};
-
-}  // namespace
-
-class Notifier::Thread : public wpi::SafeThread {
- public:
-  Thread(std::function<void()> on_start, std::function<void()> on_exit)
-      : m_on_start(on_start), m_on_exit(on_exit) {}
-
-  void Main();
-
-  struct Listener {
-    Listener() = default;
-    Listener(std::function<void(const RawEvent& event)> callback_,
-             int eventMask_)
-        : callback(callback_), eventMask(eventMask_) {}
-
-    explicit operator bool() const { return static_cast<bool>(callback); }
-
-    std::string prefix;
-    std::function<void(const RawEvent& event)> callback;
-    int eventMask;
-  };
-  UidVector<Listener> m_listeners;
-
-  std::queue<RawEvent> m_notifications;
-
-  std::function<void()> m_on_start;
-  std::function<void()> m_on_exit;
-};
-
-Notifier::Notifier() { s_destroyed = false; }
-
-Notifier::~Notifier() { s_destroyed = true; }
-
-void Notifier::Start() { m_owner.Start(m_on_start, m_on_exit); }
-
-void Notifier::Stop() { m_owner.Stop(); }
-
-void Notifier::Thread::Main() {
-  if (m_on_start) m_on_start();
-
-  std::unique_lock lock(m_mutex);
-  while (m_active) {
-    while (m_notifications.empty()) {
-      m_cond.wait(lock);
-      if (!m_active) goto done;
-    }
-
-    while (!m_notifications.empty()) {
-      if (!m_active) goto done;
-      auto item = std::move(m_notifications.front());
-      m_notifications.pop();
-
-      // Use index because iterator might get invalidated.
-      for (size_t i = 0; i < m_listeners.size(); ++i) {
-        if (!m_listeners[i]) continue;  // removed
-
-        // Event type must be within requested set for this listener.
-        if ((item.kind & m_listeners[i].eventMask) == 0) continue;
-
-        // make a copy of the callback so we can safely release the mutex
-        auto callback = m_listeners[i].callback;
-
-        // Don't hold mutex during callback execution!
-        lock.unlock();
-        callback(item);
-        lock.lock();
-      }
-    }
-  }
-
-done:
-  if (m_on_exit) m_on_exit();
+void Notifier::Start() {
+  DoStart();
 }
 
-int Notifier::AddListener(std::function<void(const RawEvent& event)> callback,
-                          int eventMask) {
-  Start();
-  auto thr = m_owner.GetThread();
-  return thr->m_listeners.emplace_back(callback, eventMask);
+unsigned int Notifier::Add(std::function<void(const RawEvent& event)> callback,
+                           int eventMask) {
+  return DoAdd(callback, eventMask);
 }
 
-void Notifier::RemoveListener(int uid) {
-  auto thr = m_owner.GetThread();
-  if (!thr) return;
-  thr->m_listeners.erase(uid);
+unsigned int Notifier::AddPolled(unsigned int pollerUid, int eventMask) {
+  return DoAdd(pollerUid, eventMask);
 }
 
-void Notifier::NotifySource(const wpi::Twine& name, CS_Source source,
+void Notifier::NotifySource(std::string_view name, CS_Source source,
                             CS_EventKind kind) {
-  auto thr = m_owner.GetThread();
-  if (!thr) return;
-  thr->m_notifications.emplace(name, source, static_cast<RawEvent::Kind>(kind));
-  thr->m_cond.notify_one();
+  Send(UINT_MAX, name, source, static_cast<RawEvent::Kind>(kind));
 }
 
 void Notifier::NotifySource(const SourceImpl& source, CS_EventKind kind) {
@@ -164,38 +44,24 @@
 
 void Notifier::NotifySourceVideoMode(const SourceImpl& source,
                                      const VideoMode& mode) {
-  auto thr = m_owner.GetThread();
-  if (!thr) return;
-
   auto handleData = Instance::GetInstance().FindSource(source);
-
-  thr->m_notifications.emplace(source.GetName(), handleData.first, mode);
-  thr->m_cond.notify_one();
+  Send(UINT_MAX, source.GetName(), handleData.first, mode);
 }
 
 void Notifier::NotifySourceProperty(const SourceImpl& source, CS_EventKind kind,
-                                    const wpi::Twine& propertyName,
-                                    int property, CS_PropertyKind propertyKind,
-                                    int value, const wpi::Twine& valueStr) {
-  auto thr = m_owner.GetThread();
-  if (!thr) return;
-
+                                    std::string_view propertyName, int property,
+                                    CS_PropertyKind propertyKind, int value,
+                                    std::string_view valueStr) {
   auto handleData = Instance::GetInstance().FindSource(source);
-
-  thr->m_notifications.emplace(
-      propertyName, handleData.first, static_cast<RawEvent::Kind>(kind),
-      Handle{handleData.first, property, Handle::kProperty}, propertyKind,
-      value, valueStr);
-  thr->m_cond.notify_one();
+  Send(UINT_MAX, propertyName, handleData.first,
+       static_cast<RawEvent::Kind>(kind),
+       Handle{handleData.first, property, Handle::kProperty}, propertyKind,
+       value, valueStr);
 }
 
-void Notifier::NotifySink(const wpi::Twine& name, CS_Sink sink,
+void Notifier::NotifySink(std::string_view name, CS_Sink sink,
                           CS_EventKind kind) {
-  auto thr = m_owner.GetThread();
-  if (!thr) return;
-
-  thr->m_notifications.emplace(name, sink, static_cast<RawEvent::Kind>(kind));
-  thr->m_cond.notify_one();
+  Send(UINT_MAX, name, sink, static_cast<RawEvent::Kind>(kind));
 }
 
 void Notifier::NotifySink(const SinkImpl& sink, CS_EventKind kind) {
@@ -203,46 +69,32 @@
   NotifySink(sink.GetName(), handleData.first, kind);
 }
 
-void Notifier::NotifySinkSourceChanged(const wpi::Twine& name, CS_Sink sink,
+void Notifier::NotifySinkSourceChanged(std::string_view name, CS_Sink sink,
                                        CS_Source source) {
-  auto thr = m_owner.GetThread();
-  if (!thr) return;
-
   RawEvent event{name, sink, RawEvent::kSinkSourceChanged};
   event.sourceHandle = source;
-
-  thr->m_notifications.emplace(std::move(event));
-  thr->m_cond.notify_one();
+  Send(UINT_MAX, std::move(event));
 }
 
 void Notifier::NotifySinkProperty(const SinkImpl& sink, CS_EventKind kind,
-                                  const wpi::Twine& propertyName, int property,
+                                  std::string_view propertyName, int property,
                                   CS_PropertyKind propertyKind, int value,
-                                  const wpi::Twine& valueStr) {
-  auto thr = m_owner.GetThread();
-  if (!thr) return;
-
+                                  std::string_view valueStr) {
   auto handleData = Instance::GetInstance().FindSink(sink);
-
-  thr->m_notifications.emplace(
-      propertyName, handleData.first, static_cast<RawEvent::Kind>(kind),
-      Handle{handleData.first, property, Handle::kSinkProperty}, propertyKind,
-      value, valueStr);
-  thr->m_cond.notify_one();
+  Send(UINT_MAX, propertyName, handleData.first,
+       static_cast<RawEvent::Kind>(kind),
+       Handle{handleData.first, property, Handle::kSinkProperty}, propertyKind,
+       value, valueStr);
 }
 
 void Notifier::NotifyNetworkInterfacesChanged() {
-  auto thr = m_owner.GetThread();
-  if (!thr) return;
-
-  thr->m_notifications.emplace(RawEvent::kNetworkInterfacesChanged);
-  thr->m_cond.notify_one();
+  Send(UINT_MAX, RawEvent::kNetworkInterfacesChanged);
 }
 
 void Notifier::NotifyTelemetryUpdated() {
-  auto thr = m_owner.GetThread();
-  if (!thr) return;
+  Send(UINT_MAX, RawEvent::kTelemetryUpdated);
+}
 
-  thr->m_notifications.emplace(RawEvent::kTelemetryUpdated);
-  thr->m_cond.notify_one();
+void Notifier::NotifyUsbCamerasChanged() {
+  Send(UINT_MAX, RawEvent::kUsbCamerasChanged);
 }
diff --git a/cscore/src/main/native/cpp/Notifier.h b/cscore/src/main/native/cpp/Notifier.h
index 526cea8..c28e25c 100644
--- a/cscore/src/main/native/cpp/Notifier.h
+++ b/cscore/src/main/native/cpp/Notifier.h
@@ -1,17 +1,16 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_NOTIFIER_H_
 #define CSCORE_NOTIFIER_H_
 
 #include <functional>
+#include <utility>
 
-#include <wpi/SafeThread.h>
+#include <wpi/CallbackManager.h>
 
+#include "Handle.h"
 #include "cscore_cpp.h"
 
 namespace cs {
@@ -19,7 +18,43 @@
 class SinkImpl;
 class SourceImpl;
 
-class Notifier {
+namespace impl {
+
+struct ListenerData : public wpi::CallbackListenerData<
+                          std::function<void(const RawEvent& event)>> {
+  ListenerData() = default;
+  ListenerData(std::function<void(const RawEvent& event)> callback_,
+               int eventMask_)
+      : CallbackListenerData(std::move(callback_)), eventMask(eventMask_) {}
+  ListenerData(unsigned int pollerUid_, int eventMask_)
+      : CallbackListenerData(pollerUid_), eventMask(eventMask_) {}
+
+  int eventMask;
+};
+
+class NotifierThread
+    : public wpi::CallbackThread<NotifierThread, RawEvent, ListenerData> {
+ public:
+  NotifierThread(std::function<void()> on_start, std::function<void()> on_exit)
+      : CallbackThread(std::move(on_start), std::move(on_exit)) {}
+
+  bool Matches(const ListenerData& listener, const RawEvent& data) {
+    return (data.kind & listener.eventMask) != 0;
+  }
+
+  void SetListener(RawEvent* data, unsigned int listener_uid) {
+    data->listener = Handle(listener_uid, Handle::kListener);
+  }
+
+  void DoCallback(std::function<void(const RawEvent& event)> callback,
+                  const RawEvent& data) {
+    callback(data);
+  }
+};
+
+}  // namespace impl
+
+class Notifier : public wpi::CallbackManager<Notifier, impl::NotifierThread> {
   friend class NotifierTest;
 
  public:
@@ -27,44 +62,30 @@
   ~Notifier();
 
   void Start();
-  void Stop();
 
-  static bool destroyed() { return s_destroyed; }
-
-  void SetOnStart(std::function<void()> on_start) { m_on_start = on_start; }
-  void SetOnExit(std::function<void()> on_exit) { m_on_exit = on_exit; }
-
-  int AddListener(std::function<void(const RawEvent& event)> callback,
-                  int eventMask);
-  void RemoveListener(int uid);
+  unsigned int Add(std::function<void(const RawEvent& event)> callback,
+                   int eventMask);
+  unsigned int AddPolled(unsigned int pollerUid, int eventMask);
 
   // Notification events
-  void NotifySource(const wpi::Twine& name, CS_Source source,
-                    CS_EventKind kind);
+  void NotifySource(std::string_view name, CS_Source source, CS_EventKind kind);
   void NotifySource(const SourceImpl& source, CS_EventKind kind);
   void NotifySourceVideoMode(const SourceImpl& source, const VideoMode& mode);
   void NotifySourceProperty(const SourceImpl& source, CS_EventKind kind,
-                            const wpi::Twine& propertyName, int property,
+                            std::string_view propertyName, int property,
                             CS_PropertyKind propertyKind, int value,
-                            const wpi::Twine& valueStr);
-  void NotifySink(const wpi::Twine& name, CS_Sink sink, CS_EventKind kind);
+                            std::string_view valueStr);
+  void NotifySink(std::string_view name, CS_Sink sink, CS_EventKind kind);
   void NotifySink(const SinkImpl& sink, CS_EventKind kind);
-  void NotifySinkSourceChanged(const wpi::Twine& name, CS_Sink sink,
+  void NotifySinkSourceChanged(std::string_view name, CS_Sink sink,
                                CS_Source source);
   void NotifySinkProperty(const SinkImpl& sink, CS_EventKind kind,
-                          const wpi::Twine& propertyName, int property,
+                          std::string_view propertyName, int property,
                           CS_PropertyKind propertyKind, int value,
-                          const wpi::Twine& valueStr);
+                          std::string_view valueStr);
   void NotifyNetworkInterfacesChanged();
   void NotifyTelemetryUpdated();
-
- private:
-  class Thread;
-  wpi::SafeThreadOwner<Thread> m_owner;
-
-  std::function<void()> m_on_start;
-  std::function<void()> m_on_exit;
-  static bool s_destroyed;
+  void NotifyUsbCamerasChanged();
 };
 
 }  // namespace cs
diff --git a/cscore/src/main/native/cpp/PropertyContainer.cpp b/cscore/src/main/native/cpp/PropertyContainer.cpp
index 17bf94b..b1d3a6f 100644
--- a/cscore/src/main/native/cpp/PropertyContainer.cpp
+++ b/cscore/src/main/native/cpp/PropertyContainer.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "PropertyContainer.h"
 
@@ -14,13 +11,14 @@
 
 using namespace cs;
 
-int PropertyContainer::GetPropertyIndex(const wpi::Twine& name) const {
+int PropertyContainer::GetPropertyIndex(std::string_view name) const {
   // We can't fail, so instead we create a new index if caching fails.
   CS_Status status = 0;
-  if (!m_properties_cached) CacheProperties(&status);
+  if (!m_properties_cached) {
+    CacheProperties(&status);
+  }
   std::scoped_lock lock(m_mutex);
-  wpi::SmallVector<char, 64> nameBuf;
-  int& ndx = m_properties[name.toStringRef(nameBuf)];
+  int& ndx = m_properties[name];
   if (ndx == 0) {
     // create a new index
     ndx = m_propertyData.size() + 1;
@@ -29,41 +27,52 @@
   return ndx;
 }
 
-wpi::ArrayRef<int> PropertyContainer::EnumerateProperties(
+wpi::span<int> PropertyContainer::EnumerateProperties(
     wpi::SmallVectorImpl<int>& vec, CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status))
-    return wpi::ArrayRef<int>{};
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return {};
+  }
   std::scoped_lock lock(m_mutex);
   for (int i = 0; i < static_cast<int>(m_propertyData.size()); ++i) {
-    if (m_propertyData[i]) vec.push_back(i + 1);
+    if (m_propertyData[i]) {
+      vec.push_back(i + 1);
+    }
   }
   return vec;
 }
 
 CS_PropertyKind PropertyContainer::GetPropertyKind(int property) const {
   CS_Status status = 0;
-  if (!m_properties_cached && !CacheProperties(&status)) return CS_PROP_NONE;
+  if (!m_properties_cached && !CacheProperties(&status)) {
+    return CS_PROP_NONE;
+  }
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
-  if (!prop) return CS_PROP_NONE;
+  if (!prop) {
+    return CS_PROP_NONE;
+  }
   return prop->propKind;
 }
 
-wpi::StringRef PropertyContainer::GetPropertyName(
+std::string_view PropertyContainer::GetPropertyName(
     int property, wpi::SmallVectorImpl<char>& buf, CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status)) return wpi::StringRef{};
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return {};
+  }
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
   if (!prop) {
     *status = CS_INVALID_PROPERTY;
-    return wpi::StringRef{};
+    return {};
   }
   // safe to not copy because we never modify it after caching
   return prop->name;
 }
 
 int PropertyContainer::GetProperty(int property, CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status)) return 0;
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return 0;
+  }
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
   if (!prop) {
@@ -88,7 +97,9 @@
   }
 
   // Guess it's integer if we've set before get
-  if (prop->propKind == CS_PROP_NONE) prop->propKind = CS_PROP_INTEGER;
+  if (prop->propKind == CS_PROP_NONE) {
+    prop->propKind = CS_PROP_INTEGER;
+  }
 
   if ((prop->propKind & (CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) ==
       0) {
@@ -96,11 +107,13 @@
     return;
   }
 
-  UpdatePropertyValue(property, false, value, wpi::Twine{});
+  UpdatePropertyValue(property, false, value, {});
 }
 
 int PropertyContainer::GetPropertyMin(int property, CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status)) return 0;
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return 0;
+  }
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
   if (!prop) {
@@ -111,7 +124,9 @@
 }
 
 int PropertyContainer::GetPropertyMax(int property, CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status)) return 0;
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return 0;
+  }
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
   if (!prop) {
@@ -122,7 +137,9 @@
 }
 
 int PropertyContainer::GetPropertyStep(int property, CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status)) return 0;
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return 0;
+  }
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
   if (!prop) {
@@ -134,7 +151,9 @@
 
 int PropertyContainer::GetPropertyDefault(int property,
                                           CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status)) return 0;
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return 0;
+  }
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
   if (!prop) {
@@ -144,25 +163,27 @@
   return prop->defaultValue;
 }
 
-wpi::StringRef PropertyContainer::GetStringProperty(
+std::string_view PropertyContainer::GetStringProperty(
     int property, wpi::SmallVectorImpl<char>& buf, CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status)) return wpi::StringRef{};
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return {};
+  }
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
   if (!prop) {
     *status = CS_INVALID_PROPERTY;
-    return wpi::StringRef{};
+    return {};
   }
   if (prop->propKind != CS_PROP_STRING) {
     *status = CS_WRONG_PROPERTY_TYPE;
-    return wpi::StringRef{};
+    return {};
   }
   buf.clear();
   buf.append(prop->valueStr.begin(), prop->valueStr.end());
-  return wpi::StringRef(buf.data(), buf.size());
+  return {buf.data(), buf.size()};
 }
 
-void PropertyContainer::SetStringProperty(int property, const wpi::Twine& value,
+void PropertyContainer::SetStringProperty(int property, std::string_view value,
                                           CS_Status* status) {
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
@@ -172,7 +193,9 @@
   }
 
   // Guess it's string if we've set before get
-  if (prop->propKind == CS_PROP_NONE) prop->propKind = CS_PROP_STRING;
+  if (prop->propKind == CS_PROP_NONE) {
+    prop->propKind = CS_PROP_STRING;
+  }
 
   if (prop->propKind != CS_PROP_STRING) {
     *status = CS_WRONG_PROPERTY_TYPE;
@@ -184,23 +207,24 @@
 
 std::vector<std::string> PropertyContainer::GetEnumPropertyChoices(
     int property, CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status))
-    return std::vector<std::string>{};
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return {};
+  }
   std::scoped_lock lock(m_mutex);
   auto prop = GetProperty(property);
   if (!prop) {
     *status = CS_INVALID_PROPERTY;
-    return std::vector<std::string>{};
+    return {};
   }
   if (prop->propKind != CS_PROP_ENUM) {
     *status = CS_WRONG_PROPERTY_TYPE;
-    return std::vector<std::string>{};
+    return {};
   }
   return prop->enumChoices;
 }
 
 std::unique_ptr<PropertyImpl> PropertyContainer::CreateEmptyProperty(
-    const wpi::Twine& name) const {
+    std::string_view name) const {
   return std::make_unique<PropertyImpl>(name);
 }
 
@@ -212,16 +236,15 @@
 
 bool PropertyContainer::SetPropertiesJson(const wpi::json& config,
                                           wpi::Logger& logger,
-                                          wpi::StringRef logName,
+                                          std::string_view logName,
                                           CS_Status* status) {
   for (auto&& prop : config) {
     std::string name;
     try {
       name = prop.at("name").get<std::string>();
     } catch (const wpi::json::exception& e) {
-      WPI_WARNING(logger,
-                  logName << ": SetConfigJson: could not read property name: "
-                          << e.what());
+      WPI_WARNING(logger, "{}: SetConfigJson: could not read property name: {}",
+                  logName, e.what());
       continue;
     }
     int n = GetPropertyIndex(name);
@@ -229,24 +252,24 @@
       auto& v = prop.at("value");
       if (v.is_string()) {
         std::string val = v.get<std::string>();
-        WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
-                                 << name << "' to '" << val << '\'');
+        WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to '{}'",
+                 logName, name, val);
         SetStringProperty(n, val, status);
       } else if (v.is_boolean()) {
         bool val = v.get<bool>();
-        WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
-                                 << name << "' to " << val);
+        WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to {}",
+                 logName, name, val);
         SetProperty(n, val, status);
       } else {
         int val = v.get<int>();
-        WPI_INFO(logger, logName << ": SetConfigJson: setting property '"
-                                 << name << "' to " << val);
+        WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to {}",
+                 logName, name, val);
         SetProperty(n, val, status);
       }
     } catch (const wpi::json::exception& e) {
       WPI_WARNING(logger,
-                  logName << ": SetConfigJson: could not read property value: "
-                          << e.what());
+                  "{}: SetConfigJson: could not read property value: {}",
+                  logName, e.what());
       continue;
     }
   }
diff --git a/cscore/src/main/native/cpp/PropertyContainer.h b/cscore/src/main/native/cpp/PropertyContainer.h
index 9bbb9c7..a00c675 100644
--- a/cscore/src/main/native/cpp/PropertyContainer.h
+++ b/cscore/src/main/native/cpp/PropertyContainer.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_PROPERTYCONTAINER_H_
 #define CSCORE_PROPERTYCONTAINER_H_
@@ -12,20 +9,20 @@
 #include <cstddef>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
-#include <wpi/ArrayRef.h>
-#include <wpi/SmallVector.h>
 #include <wpi/StringMap.h>
-#include <wpi/StringRef.h>
-#include <wpi/Twine.h>
 #include <wpi/mutex.h>
+#include <wpi/span.h>
 
 #include "PropertyImpl.h"
 #include "cscore_cpp.h"
 
 namespace wpi {
 class Logger;
+template <typename T>
+class SmallVectorImpl;
 class json;
 }  // namespace wpi
 
@@ -35,50 +32,54 @@
  public:
   virtual ~PropertyContainer() = default;
 
-  int GetPropertyIndex(const wpi::Twine& name) const;
-  wpi::ArrayRef<int> EnumerateProperties(wpi::SmallVectorImpl<int>& vec,
-                                         CS_Status* status) const;
+  int GetPropertyIndex(std::string_view name) const;
+  wpi::span<int> EnumerateProperties(wpi::SmallVectorImpl<int>& vec,
+                                     CS_Status* status) const;
   CS_PropertyKind GetPropertyKind(int property) const;
-  wpi::StringRef GetPropertyName(int property, wpi::SmallVectorImpl<char>& buf,
-                                 CS_Status* status) const;
+  std::string_view GetPropertyName(int property,
+                                   wpi::SmallVectorImpl<char>& buf,
+                                   CS_Status* status) const;
   int GetProperty(int property, CS_Status* status) const;
   virtual void SetProperty(int property, int value, CS_Status* status);
   int GetPropertyMin(int property, CS_Status* status) const;
   int GetPropertyMax(int property, CS_Status* status) const;
   int GetPropertyStep(int property, CS_Status* status) const;
   int GetPropertyDefault(int property, CS_Status* status) const;
-  wpi::StringRef GetStringProperty(int property,
-                                   wpi::SmallVectorImpl<char>& buf,
-                                   CS_Status* status) const;
-  virtual void SetStringProperty(int property, const wpi::Twine& value,
+  std::string_view GetStringProperty(int property,
+                                     wpi::SmallVectorImpl<char>& buf,
+                                     CS_Status* status) const;
+  virtual void SetStringProperty(int property, std::string_view value,
                                  CS_Status* status);
   std::vector<std::string> GetEnumPropertyChoices(int property,
                                                   CS_Status* status) const;
 
   bool SetPropertiesJson(const wpi::json& config, wpi::Logger& logger,
-                         wpi::StringRef logName, CS_Status* status);
+                         std::string_view logName, CS_Status* status);
   wpi::json GetPropertiesJsonObject(CS_Status* status);
 
  protected:
   // Get a property; must be called with m_mutex held.
   PropertyImpl* GetProperty(int property) {
-    if (property <= 0 || static_cast<size_t>(property) > m_propertyData.size())
+    if (property <= 0 ||
+        static_cast<size_t>(property) > m_propertyData.size()) {
       return nullptr;
+    }
     return m_propertyData[property - 1].get();
   }
   const PropertyImpl* GetProperty(int property) const {
-    if (property <= 0 || static_cast<size_t>(property) > m_propertyData.size())
+    if (property <= 0 ||
+        static_cast<size_t>(property) > m_propertyData.size()) {
       return nullptr;
+    }
     return m_propertyData[property - 1].get();
   }
   // Create or update a property; must be called with m_mutex held.
   // @tparam NewFunc functor that returns a std::unique_ptr<PropertyImpl>
   // @tparam UpdateFunc functor that takes a PropertyImpl&.
   template <typename NewFunc, typename UpdateFunc>
-  int CreateOrUpdateProperty(const wpi::Twine& name, NewFunc newFunc,
+  int CreateOrUpdateProperty(std::string_view name, NewFunc newFunc,
                              UpdateFunc updateFunc) {
-    wpi::SmallVector<char, 64> nameBuf;
-    int& ndx = m_properties[name.toStringRef(nameBuf)];
+    int& ndx = m_properties[name];
     if (ndx == 0) {
       // create a new index
       ndx = m_propertyData.size() + 1;
@@ -90,7 +91,7 @@
     return ndx;
   }
   template <typename NewFunc>
-  int CreateProperty(const wpi::Twine& name, NewFunc newFunc) {
+  int CreateProperty(std::string_view name, NewFunc newFunc) {
     return CreateOrUpdateProperty(name, newFunc, [](PropertyImpl&) {});
   }
 
@@ -99,7 +100,7 @@
   // Note: called with m_mutex held.
   // The default implementation simply creates a PropertyImpl object.
   virtual std::unique_ptr<PropertyImpl> CreateEmptyProperty(
-      const wpi::Twine& name) const;
+      std::string_view name) const;
 
   // Cache properties.  Implementations must return false and set status to
   // CS_SOURCE_IS_DISCONNECTED if not possible to cache.
@@ -110,7 +111,7 @@
 
   // Update property value; must be called with m_mutex held.
   virtual void UpdatePropertyValue(int property, bool setString, int value,
-                                   const wpi::Twine& valueStr) = 0;
+                                   std::string_view valueStr) = 0;
 
   // Whether CacheProperties() has been successful at least once (and thus
   // should not be called again)
diff --git a/cscore/src/main/native/cpp/PropertyImpl.cpp b/cscore/src/main/native/cpp/PropertyImpl.cpp
index 4f1602e..3c36630 100644
--- a/cscore/src/main/native/cpp/PropertyImpl.cpp
+++ b/cscore/src/main/native/cpp/PropertyImpl.cpp
@@ -1,26 +1,23 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "PropertyImpl.h"
 
 using namespace cs;
 
-PropertyImpl::PropertyImpl(const wpi::Twine& name_) : name{name_.str()} {}
-PropertyImpl::PropertyImpl(const wpi::Twine& name_, CS_PropertyKind kind_,
+PropertyImpl::PropertyImpl(std::string_view name_) : name{name_} {}
+PropertyImpl::PropertyImpl(std::string_view name_, CS_PropertyKind kind_,
                            int step_, int defaultValue_, int value_)
-    : name{name_.str()},
+    : name{name_},
       propKind{kind_},
       step{step_},
       defaultValue{defaultValue_},
       value{value_} {}
-PropertyImpl::PropertyImpl(const wpi::Twine& name_, CS_PropertyKind kind_,
+PropertyImpl::PropertyImpl(std::string_view name_, CS_PropertyKind kind_,
                            int minimum_, int maximum_, int step_,
                            int defaultValue_, int value_)
-    : name{name_.str()},
+    : name{name_},
       propKind{kind_},
       hasMinimum{true},
       hasMaximum{true},
@@ -32,34 +29,39 @@
 
 void PropertyImpl::SetValue(int v) {
   int oldValue = value;
-  if (hasMinimum && v < minimum)
+  if (hasMinimum && v < minimum) {
     value = minimum;
-  else if (hasMaximum && v > maximum)
+  } else if (hasMaximum && v > maximum) {
     value = maximum;
-  else
+  } else {
     value = v;
+  }
   bool wasValueSet = valueSet;
   valueSet = true;
-  if (!wasValueSet || value != oldValue) changed();
+  if (!wasValueSet || value != oldValue) {
+    changed();
+  }
 }
 
-void PropertyImpl::SetValue(const wpi::Twine& v) {
+void PropertyImpl::SetValue(std::string_view v) {
   bool valueChanged = false;
-  std::string vStr = v.str();
-  if (valueStr != vStr) {
-    valueStr = vStr;
+  if (valueStr != v) {
+    valueStr = v;
     valueChanged = true;
   }
   bool wasValueSet = valueSet;
   valueSet = true;
-  if (!wasValueSet || valueChanged) changed();
+  if (!wasValueSet || valueChanged) {
+    changed();
+  }
 }
 
 void PropertyImpl::SetDefaultValue(int v) {
-  if (hasMinimum && v < minimum)
+  if (hasMinimum && v < minimum) {
     defaultValue = minimum;
-  else if (hasMaximum && v > maximum)
+  } else if (hasMaximum && v > maximum) {
     defaultValue = maximum;
-  else
+  } else {
     defaultValue = v;
+  }
 }
diff --git a/cscore/src/main/native/cpp/PropertyImpl.h b/cscore/src/main/native/cpp/PropertyImpl.h
index d932132..77e7908 100644
--- a/cscore/src/main/native/cpp/PropertyImpl.h
+++ b/cscore/src/main/native/cpp/PropertyImpl.h
@@ -1,19 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_PROPERTYIMPL_H_
 #define CSCORE_PROPERTYIMPL_H_
 
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <wpi/Signal.h>
-#include <wpi/StringRef.h>
-#include <wpi/Twine.h>
 
 #include "cscore_c.h"
 
@@ -23,17 +19,17 @@
 class PropertyImpl {
  public:
   PropertyImpl() = default;
-  explicit PropertyImpl(const wpi::Twine& name_);
-  PropertyImpl(const wpi::Twine& name_, CS_PropertyKind kind_, int step_,
+  explicit PropertyImpl(std::string_view name_);
+  PropertyImpl(std::string_view name_, CS_PropertyKind kind_, int step_,
                int defaultValue_, int value_);
-  PropertyImpl(const wpi::Twine& name_, CS_PropertyKind kind_, int minimum_,
+  PropertyImpl(std::string_view name_, CS_PropertyKind kind_, int minimum_,
                int maximum_, int step_, int defaultValue_, int value_);
   virtual ~PropertyImpl() = default;
   PropertyImpl(const PropertyImpl& oth) = delete;
   PropertyImpl& operator=(const PropertyImpl& oth) = delete;
 
   void SetValue(int v);
-  void SetValue(const wpi::Twine& v);
+  void SetValue(std::string_view v);
   void SetDefaultValue(int v);
 
   std::string name;
diff --git a/cscore/src/main/native/cpp/RawSinkImpl.cpp b/cscore/src/main/native/cpp/RawSinkImpl.cpp
index 986378f..fbc1028 100644
--- a/cscore/src/main/native/cpp/RawSinkImpl.cpp
+++ b/cscore/src/main/native/cpp/RawSinkImpl.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "RawSinkImpl.h"
 
@@ -13,28 +10,34 @@
 
 using namespace cs;
 
-RawSinkImpl::RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
+RawSinkImpl::RawSinkImpl(std::string_view name, wpi::Logger& logger,
                          Notifier& notifier, Telemetry& telemetry)
     : SinkImpl{name, logger, notifier, telemetry} {
   m_active = true;
   // m_thread = std::thread(&RawSinkImpl::ThreadMain, this);
 }
 
-RawSinkImpl::RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger,
+RawSinkImpl::RawSinkImpl(std::string_view name, wpi::Logger& logger,
                          Notifier& notifier, Telemetry& telemetry,
                          std::function<void(uint64_t time)> processFrame)
     : SinkImpl{name, logger, notifier, telemetry} {}
 
-RawSinkImpl::~RawSinkImpl() { Stop(); }
+RawSinkImpl::~RawSinkImpl() {
+  Stop();
+}
 
 void RawSinkImpl::Stop() {
   m_active = false;
 
   // wake up any waiters by forcing an empty frame to be sent
-  if (auto source = GetSource()) source->Wakeup();
+  if (auto source = GetSource()) {
+    source->Wakeup();
+  }
 
   // join thread
-  if (m_thread.joinable()) m_thread.join();
+  if (m_thread.joinable()) {
+    m_thread.join();
+  }
 }
 
 uint64_t RawSinkImpl::GrabFrame(CS_RawFrame& image) {
@@ -124,9 +127,11 @@
       std::this_thread::sleep_for(std::chrono::seconds(1));
       continue;
     }
-    SDEBUG4("waiting for frame");
+    SDEBUG4("{}", "waiting for frame");
     Frame frame = source->GetNextFrame();  // blocks
-    if (!m_active) break;
+    if (!m_active) {
+      break;
+    }
     if (!frame) {
       // Bad frame; sleep for 10 ms so we don't consume all processor time.
       std::this_thread::sleep_for(std::chrono::milliseconds(10));
@@ -138,14 +143,14 @@
 }
 
 namespace cs {
-CS_Sink CreateRawSink(const wpi::Twine& name, CS_Status* status) {
+CS_Sink CreateRawSink(std::string_view name, CS_Status* status) {
   auto& inst = Instance::GetInstance();
   return inst.CreateSink(CS_SINK_RAW,
                          std::make_shared<RawSinkImpl>(
                              name, inst.logger, inst.notifier, inst.telemetry));
 }
 
-CS_Sink CreateRawSinkCallback(const wpi::Twine& name,
+CS_Sink CreateRawSinkCallback(std::string_view name,
                               std::function<void(uint64_t time)> processFrame,
                               CS_Status* status) {
   auto& inst = Instance::GetInstance();
diff --git a/cscore/src/main/native/cpp/RawSinkImpl.h b/cscore/src/main/native/cpp/RawSinkImpl.h
index 3e69485..6e8032a 100644
--- a/cscore/src/main/native/cpp/RawSinkImpl.h
+++ b/cscore/src/main/native/cpp/RawSinkImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_RAWSINKIMPL_H_
 #define CSCORE_RAWSINKIMPL_H_
@@ -12,9 +9,9 @@
 
 #include <atomic>
 #include <functional>
+#include <string_view>
 #include <thread>
 
-#include <wpi/Twine.h>
 #include <wpi/condition_variable.h>
 
 #include "Frame.h"
@@ -26,9 +23,9 @@
 
 class RawSinkImpl : public SinkImpl {
  public:
-  RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
+  RawSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
               Telemetry& telemetry);
-  RawSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
+  RawSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
               Telemetry& telemetry,
               std::function<void(uint64_t time)> processFrame);
   ~RawSinkImpl() override;
diff --git a/cscore/src/main/native/cpp/RawSourceImpl.cpp b/cscore/src/main/native/cpp/RawSourceImpl.cpp
index e0dba2d..9ce0628 100644
--- a/cscore/src/main/native/cpp/RawSourceImpl.cpp
+++ b/cscore/src/main/native/cpp/RawSourceImpl.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "RawSourceImpl.h"
 
@@ -17,12 +14,12 @@
 
 using namespace cs;
 
-RawSourceImpl::RawSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
+RawSourceImpl::RawSourceImpl(std::string_view name, wpi::Logger& logger,
                              Notifier& notifier, Telemetry& telemetry,
                              const VideoMode& mode)
     : ConfigurableSourceImpl{name, logger, notifier, telemetry, mode} {}
 
-RawSourceImpl::~RawSourceImpl() {}
+RawSourceImpl::~RawSourceImpl() = default;
 
 void RawSourceImpl::PutFrame(const CS_RawFrame& image) {
   int type;
@@ -50,7 +47,7 @@
 }
 
 namespace cs {
-CS_Source CreateRawSource(const wpi::Twine& name, const VideoMode& mode,
+CS_Source CreateRawSource(std::string_view name, const VideoMode& mode,
                           CS_Status* status) {
   auto& inst = Instance::GetInstance();
   return inst.CreateSource(CS_SOURCE_RAW, std::make_shared<RawSourceImpl>(
diff --git a/cscore/src/main/native/cpp/RawSourceImpl.h b/cscore/src/main/native/cpp/RawSourceImpl.h
index 1fdc749..5887ed4 100644
--- a/cscore/src/main/native/cpp/RawSourceImpl.h
+++ b/cscore/src/main/native/cpp/RawSourceImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_RAWSOURCEIMPL_H_
 #define CSCORE_RAWSOURCEIMPL_H_
@@ -12,11 +9,9 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
-#include <wpi/ArrayRef.h>
-#include <wpi/Twine.h>
-
 #include "ConfigurableSourceImpl.h"
 #include "SourceImpl.h"
 #include "cscore_raw.h"
@@ -25,7 +20,7 @@
 
 class RawSourceImpl : public ConfigurableSourceImpl {
  public:
-  RawSourceImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
+  RawSourceImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
                 Telemetry& telemetry, const VideoMode& mode);
   ~RawSourceImpl() override;
 
diff --git a/cscore/src/main/native/cpp/SinkImpl.cpp b/cscore/src/main/native/cpp/SinkImpl.cpp
index 5d4235a..93a625f 100644
--- a/cscore/src/main/native/cpp/SinkImpl.cpp
+++ b/cscore/src/main/native/cpp/SinkImpl.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "SinkImpl.h"
 
@@ -15,36 +12,41 @@
 
 using namespace cs;
 
-SinkImpl::SinkImpl(const wpi::Twine& name, wpi::Logger& logger,
+SinkImpl::SinkImpl(std::string_view name, wpi::Logger& logger,
                    Notifier& notifier, Telemetry& telemetry)
     : m_logger(logger),
       m_notifier(notifier),
       m_telemetry(telemetry),
-      m_name{name.str()} {}
+      m_name{name} {}
 
 SinkImpl::~SinkImpl() {
   if (m_source) {
-    if (m_enabledCount > 0) m_source->DisableSink();
+    if (m_enabledCount > 0) {
+      m_source->DisableSink();
+    }
     m_source->RemoveSink();
   }
 }
 
-void SinkImpl::SetDescription(const wpi::Twine& description) {
+void SinkImpl::SetDescription(std::string_view description) {
   std::scoped_lock lock(m_mutex);
-  m_description = description.str();
+  m_description = description;
 }
 
-wpi::StringRef SinkImpl::GetDescription(wpi::SmallVectorImpl<char>& buf) const {
+std::string_view SinkImpl::GetDescription(
+    wpi::SmallVectorImpl<char>& buf) const {
   std::scoped_lock lock(m_mutex);
   buf.append(m_description.begin(), m_description.end());
-  return wpi::StringRef{buf.data(), buf.size()};
+  return {buf.data(), buf.size()};
 }
 
 void SinkImpl::Enable() {
   std::scoped_lock lock(m_mutex);
   ++m_enabledCount;
   if (m_enabledCount == 1) {
-    if (m_source) m_source->EnableSink();
+    if (m_source) {
+      m_source->EnableSink();
+    }
     m_notifier.NotifySink(*this, CS_SINK_ENABLED);
   }
 }
@@ -53,7 +55,9 @@
   std::scoped_lock lock(m_mutex);
   --m_enabledCount;
   if (m_enabledCount == 0) {
-    if (m_source) m_source->DisableSink();
+    if (m_source) {
+      m_source->DisableSink();
+    }
     m_notifier.NotifySink(*this, CS_SINK_DISABLED);
   }
 }
@@ -61,11 +65,15 @@
 void SinkImpl::SetEnabled(bool enabled) {
   std::scoped_lock lock(m_mutex);
   if (enabled && m_enabledCount == 0) {
-    if (m_source) m_source->EnableSink();
+    if (m_source) {
+      m_source->EnableSink();
+    }
     m_enabledCount = 1;
     m_notifier.NotifySink(*this, CS_SINK_ENABLED);
   } else if (!enabled && m_enabledCount > 0) {
-    if (m_source) m_source->DisableSink();
+    if (m_source) {
+      m_source->DisableSink();
+    }
     m_enabledCount = 0;
     m_notifier.NotifySink(*this, CS_SINK_DISABLED);
   }
@@ -74,15 +82,21 @@
 void SinkImpl::SetSource(std::shared_ptr<SourceImpl> source) {
   {
     std::scoped_lock lock(m_mutex);
-    if (m_source == source) return;
+    if (m_source == source) {
+      return;
+    }
     if (m_source) {
-      if (m_enabledCount > 0) m_source->DisableSink();
+      if (m_enabledCount > 0) {
+        m_source->DisableSink();
+      }
       m_source->RemoveSink();
     }
     m_source = source;
     if (m_source) {
       m_source->AddSink();
-      if (m_enabledCount > 0) m_source->EnableSink();
+      if (m_enabledCount > 0) {
+        m_source->EnableSink();
+      }
     }
   }
   SetSourceImpl(source);
@@ -90,27 +104,30 @@
 
 std::string SinkImpl::GetError() const {
   std::scoped_lock lock(m_mutex);
-  if (!m_source) return "no source connected";
-  return m_source->GetCurFrame().GetError();
+  if (!m_source) {
+    return "no source connected";
+  }
+  return std::string{m_source->GetCurFrame().GetError()};
 }
 
-wpi::StringRef SinkImpl::GetError(wpi::SmallVectorImpl<char>& buf) const {
+std::string_view SinkImpl::GetError(wpi::SmallVectorImpl<char>& buf) const {
   std::scoped_lock lock(m_mutex);
-  if (!m_source) return "no source connected";
+  if (!m_source) {
+    return "no source connected";
+  }
   // Make a copy as it's shared data
-  wpi::StringRef error = m_source->GetCurFrame().GetError();
+  std::string_view error = m_source->GetCurFrame().GetError();
   buf.clear();
   buf.append(error.data(), error.data() + error.size());
-  return wpi::StringRef{buf.data(), buf.size()};
+  return {buf.data(), buf.size()};
 }
 
-bool SinkImpl::SetConfigJson(wpi::StringRef config, CS_Status* status) {
+bool SinkImpl::SetConfigJson(std::string_view config, CS_Status* status) {
   wpi::json j;
   try {
     j = wpi::json::parse(config);
   } catch (const wpi::json::parse_error& e) {
-    SWARNING("SetConfigJson: parse error at byte " << e.byte << ": "
-                                                   << e.what());
+    SWARNING("SetConfigJson: parse error at byte {}: {}", e.byte, e.what());
     *status = CS_PROPERTY_WRITE_FAILED;
     return false;
   }
@@ -118,8 +135,9 @@
 }
 
 bool SinkImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
-  if (config.count("properties") != 0)
+  if (config.count("properties") != 0) {
     SetPropertiesJson(config.at("properties"), m_logger, GetName(), status);
+  }
 
   return true;
 }
@@ -136,7 +154,9 @@
   wpi::json j;
 
   wpi::json props = GetPropertiesJsonObject(status);
-  if (props.is_array()) j.emplace("properties", props);
+  if (props.is_array()) {
+    j.emplace("properties", props);
+  }
 
   return j;
 }
@@ -146,21 +166,25 @@
                                 propIndex, prop.propKind, prop.value,
                                 prop.valueStr);
   // also notify choices updated event for enum types
-  if (prop.propKind == CS_PROP_ENUM)
+  if (prop.propKind == CS_PROP_ENUM) {
     m_notifier.NotifySinkProperty(*this, CS_SINK_PROPERTY_CHOICES_UPDATED,
                                   prop.name, propIndex, prop.propKind,
-                                  prop.value, wpi::Twine{});
+                                  prop.value, {});
+  }
 }
 
 void SinkImpl::UpdatePropertyValue(int property, bool setString, int value,
-                                   const wpi::Twine& valueStr) {
+                                   std::string_view valueStr) {
   auto prop = GetProperty(property);
-  if (!prop) return;
+  if (!prop) {
+    return;
+  }
 
-  if (setString)
+  if (setString) {
     prop->SetValue(valueStr);
-  else
+  } else {
     prop->SetValue(value);
+  }
 
   // Only notify updates after we've notified created
   if (m_properties_cached) {
diff --git a/cscore/src/main/native/cpp/SinkImpl.h b/cscore/src/main/native/cpp/SinkImpl.h
index 7ad831f..aa37d61 100644
--- a/cscore/src/main/native/cpp/SinkImpl.h
+++ b/cscore/src/main/native/cpp/SinkImpl.h
@@ -1,19 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_SINKIMPL_H_
 #define CSCORE_SINKIMPL_H_
 
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include <wpi/Logger.h>
-#include <wpi/StringRef.h>
-#include <wpi/Twine.h>
 #include <wpi/mutex.h>
 
 #include "SourceImpl.h"
@@ -30,16 +26,16 @@
 
 class SinkImpl : public PropertyContainer {
  public:
-  explicit SinkImpl(const wpi::Twine& name, wpi::Logger& logger,
+  explicit SinkImpl(std::string_view name, wpi::Logger& logger,
                     Notifier& notifier, Telemetry& telemetry);
-  virtual ~SinkImpl();
+  ~SinkImpl() override;
   SinkImpl(const SinkImpl& queue) = delete;
   SinkImpl& operator=(const SinkImpl& queue) = delete;
 
-  wpi::StringRef GetName() const { return m_name; }
+  std::string_view GetName() const { return m_name; }
 
-  void SetDescription(const wpi::Twine& description);
-  wpi::StringRef GetDescription(wpi::SmallVectorImpl<char>& buf) const;
+  void SetDescription(std::string_view description);
+  std::string_view GetDescription(wpi::SmallVectorImpl<char>& buf) const;
 
   void Enable();
   void Disable();
@@ -53,9 +49,9 @@
   }
 
   std::string GetError() const;
-  wpi::StringRef GetError(wpi::SmallVectorImpl<char>& buf) const;
+  std::string_view GetError(wpi::SmallVectorImpl<char>& buf) const;
 
-  bool SetConfigJson(wpi::StringRef config, CS_Status* status);
+  bool SetConfigJson(std::string_view config, CS_Status* status);
   virtual bool SetConfigJson(const wpi::json& config, CS_Status* status);
   std::string GetConfigJson(CS_Status* status);
   virtual wpi::json GetConfigJsonObject(CS_Status* status);
@@ -64,7 +60,7 @@
   // PropertyContainer implementation
   void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
   void UpdatePropertyValue(int property, bool setString, int value,
-                           const wpi::Twine& valueStr) override;
+                           std::string_view valueStr) override;
 
   virtual void SetSourceImpl(std::shared_ptr<SourceImpl> source);
 
diff --git a/cscore/src/main/native/cpp/SourceImpl.cpp b/cscore/src/main/native/cpp/SourceImpl.cpp
index 455b6cd..da5aa1d 100644
--- a/cscore/src/main/native/cpp/SourceImpl.cpp
+++ b/cscore/src/main/native/cpp/SourceImpl.cpp
@@ -1,15 +1,14 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "SourceImpl.h"
 
 #include <algorithm>
 #include <cstring>
+#include <memory>
 
+#include <wpi/StringExtras.h>
 #include <wpi/json.h>
 #include <wpi/timestamp.h>
 
@@ -21,13 +20,13 @@
 
 static constexpr size_t kMaxImagesAvail = 32;
 
-SourceImpl::SourceImpl(const wpi::Twine& name, wpi::Logger& logger,
+SourceImpl::SourceImpl(std::string_view name, wpi::Logger& logger,
                        Notifier& notifier, Telemetry& telemetry)
     : m_logger(logger),
       m_notifier(notifier),
       m_telemetry(telemetry),
-      m_name{name.str()} {
-  m_frame = Frame{*this, wpi::StringRef{}, 0};
+      m_name{name} {
+  m_frame = Frame{*this, std::string_view{}, 0};
 }
 
 SourceImpl::~SourceImpl() {
@@ -43,24 +42,25 @@
   // Everything else can clean up itself.
 }
 
-void SourceImpl::SetDescription(const wpi::Twine& description) {
+void SourceImpl::SetDescription(std::string_view description) {
   std::scoped_lock lock(m_mutex);
-  m_description = description.str();
+  m_description = description;
 }
 
-wpi::StringRef SourceImpl::GetDescription(
+std::string_view SourceImpl::GetDescription(
     wpi::SmallVectorImpl<char>& buf) const {
   std::scoped_lock lock(m_mutex);
   buf.append(m_description.begin(), m_description.end());
-  return wpi::StringRef{buf.data(), buf.size()};
+  return {buf.data(), buf.size()};
 }
 
 void SourceImpl::SetConnected(bool connected) {
   bool wasConnected = m_connected.exchange(connected);
-  if (wasConnected && !connected)
+  if (wasConnected && !connected) {
     m_notifier.NotifySource(*this, CS_SOURCE_DISCONNECTED);
-  else if (!wasConnected && connected)
+  } else if (!wasConnected && connected) {
     m_notifier.NotifySource(*this, CS_SOURCE_CONNECTED);
+  }
 }
 
 uint64_t SourceImpl::GetCurFrameTime() {
@@ -94,7 +94,7 @@
 void SourceImpl::Wakeup() {
   {
     std::scoped_lock lock{m_frameMutex};
-    m_frame = Frame{*this, wpi::StringRef{}, 0};
+    m_frame = Frame{*this, std::string_view{}, 0};
   }
   m_frameCv.notify_all();
 }
@@ -133,7 +133,9 @@
 }
 
 VideoMode SourceImpl::GetVideoMode(CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status)) return VideoMode{};
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return {};
+  }
   std::scoped_lock lock(m_mutex);
   return m_mode;
 }
@@ -141,14 +143,18 @@
 bool SourceImpl::SetPixelFormat(VideoMode::PixelFormat pixelFormat,
                                 CS_Status* status) {
   auto mode = GetVideoMode(status);
-  if (!mode) return false;
+  if (!mode) {
+    return false;
+  }
   mode.pixelFormat = pixelFormat;
   return SetVideoMode(mode, status);
 }
 
 bool SourceImpl::SetResolution(int width, int height, CS_Status* status) {
   auto mode = GetVideoMode(status);
-  if (!mode) return false;
+  if (!mode) {
+    return false;
+  }
   mode.width = width;
   mode.height = height;
   return SetVideoMode(mode, status);
@@ -156,18 +162,19 @@
 
 bool SourceImpl::SetFPS(int fps, CS_Status* status) {
   auto mode = GetVideoMode(status);
-  if (!mode) return false;
+  if (!mode) {
+    return false;
+  }
   mode.fps = fps;
   return SetVideoMode(mode, status);
 }
 
-bool SourceImpl::SetConfigJson(wpi::StringRef config, CS_Status* status) {
+bool SourceImpl::SetConfigJson(std::string_view config, CS_Status* status) {
   wpi::json j;
   try {
     j = wpi::json::parse(config);
   } catch (const wpi::json::parse_error& e) {
-    SWARNING("SetConfigJson: parse error at byte " << e.byte << ": "
-                                                   << e.what());
+    SWARNING("SetConfigJson: parse error at byte {}: {}", e.byte, e.what());
     *status = CS_PROPERTY_WRITE_FAILED;
     return false;
   }
@@ -181,23 +188,22 @@
   if (config.count("pixel format") != 0) {
     try {
       auto str = config.at("pixel format").get<std::string>();
-      wpi::StringRef s(str);
-      if (s.equals_lower("mjpeg")) {
+      if (wpi::equals_lower(str, "mjpeg")) {
         mode.pixelFormat = cs::VideoMode::kMJPEG;
-      } else if (s.equals_lower("yuyv")) {
+      } else if (wpi::equals_lower(str, "yuyv")) {
         mode.pixelFormat = cs::VideoMode::kYUYV;
-      } else if (s.equals_lower("rgb565")) {
+      } else if (wpi::equals_lower(str, "rgb565")) {
         mode.pixelFormat = cs::VideoMode::kRGB565;
-      } else if (s.equals_lower("bgr")) {
+      } else if (wpi::equals_lower(str, "bgr")) {
         mode.pixelFormat = cs::VideoMode::kBGR;
-      } else if (s.equals_lower("gray")) {
+      } else if (wpi::equals_lower(str, "gray")) {
         mode.pixelFormat = cs::VideoMode::kGray;
       } else {
-        SWARNING("SetConfigJson: could not understand pixel format value '"
-                 << str << '\'');
+        SWARNING("SetConfigJson: could not understand pixel format value '{}'",
+                 str);
       }
     } catch (const wpi::json::exception& e) {
-      SWARNING("SetConfigJson: could not read pixel format: " << e.what());
+      SWARNING("SetConfigJson: could not read pixel format: {}", e.what());
     }
   }
 
@@ -206,7 +212,7 @@
     try {
       mode.width = config.at("width").get<unsigned int>();
     } catch (const wpi::json::exception& e) {
-      SWARNING("SetConfigJson: could not read width: " << e.what());
+      SWARNING("SetConfigJson: could not read width: {}", e.what());
     }
   }
 
@@ -215,7 +221,7 @@
     try {
       mode.height = config.at("height").get<unsigned int>();
     } catch (const wpi::json::exception& e) {
-      SWARNING("SetConfigJson: could not read height: " << e.what());
+      SWARNING("SetConfigJson: could not read height: {}", e.what());
     }
   }
 
@@ -224,30 +230,31 @@
     try {
       mode.fps = config.at("fps").get<unsigned int>();
     } catch (const wpi::json::exception& e) {
-      SWARNING("SetConfigJson: could not read fps: " << e.what());
+      SWARNING("SetConfigJson: could not read fps: {}", e.what());
     }
   }
 
   // if all of video mode is set, use SetVideoMode, otherwise piecemeal it
   if (mode.pixelFormat != VideoMode::kUnknown && mode.width != 0 &&
       mode.height != 0 && mode.fps != 0) {
-    SINFO("SetConfigJson: setting video mode to pixelFormat "
-          << mode.pixelFormat << ", width " << mode.width << ", height "
-          << mode.height << ", fps " << mode.fps);
+    SINFO(
+        "SetConfigJson: setting video mode to pixelFormat {}, width {}, height "
+        "{}, fps {}",
+        mode.pixelFormat, mode.width, mode.height, mode.fps);
     SetVideoMode(mode, status);
   } else {
     if (mode.pixelFormat != cs::VideoMode::kUnknown) {
-      SINFO("SetConfigJson: setting pixelFormat " << mode.pixelFormat);
+      SINFO("SetConfigJson: setting pixelFormat {}", mode.pixelFormat);
       SetPixelFormat(static_cast<cs::VideoMode::PixelFormat>(mode.pixelFormat),
                      status);
     }
     if (mode.width != 0 && mode.height != 0) {
-      SINFO("SetConfigJson: setting width " << mode.width << ", height "
-                                            << mode.height);
+      SINFO("SetConfigJson: setting width {}, height {}", mode.width,
+            mode.height);
       SetResolution(mode.width, mode.height, status);
     }
     if (mode.fps != 0) {
-      SINFO("SetConfigJson: setting fps " << mode.fps);
+      SINFO("SetConfigJson: setting fps {}", mode.fps);
       SetFPS(mode.fps, status);
     }
   }
@@ -256,10 +263,10 @@
   if (config.count("brightness") != 0) {
     try {
       int val = config.at("brightness").get<int>();
-      SINFO("SetConfigJson: setting brightness to " << val);
+      SINFO("SetConfigJson: setting brightness to {}", val);
       SetBrightness(val, status);
     } catch (const wpi::json::exception& e) {
-      SWARNING("SetConfigJson: could not read brightness: " << e.what());
+      SWARNING("SetConfigJson: could not read brightness: {}", e.what());
     }
   }
 
@@ -269,24 +276,24 @@
       auto& setting = config.at("white balance");
       if (setting.is_string()) {
         auto str = setting.get<std::string>();
-        wpi::StringRef s(str);
-        if (s.equals_lower("auto")) {
-          SINFO("SetConfigJson: setting white balance to auto");
+        if (wpi::equals_lower(str, "auto")) {
+          SINFO("SetConfigJson: setting white balance to {}", "auto");
           SetWhiteBalanceAuto(status);
-        } else if (s.equals_lower("hold")) {
-          SINFO("SetConfigJson: setting white balance to hold current");
+        } else if (wpi::equals_lower(str, "hold")) {
+          SINFO("SetConfigJson: setting white balance to {}", "hold current");
           SetWhiteBalanceHoldCurrent(status);
         } else {
-          SWARNING("SetConfigJson: could not understand white balance value '"
-                   << str << '\'');
+          SWARNING(
+              "SetConfigJson: could not understand white balance value '{}'",
+              str);
         }
       } else {
         int val = setting.get<int>();
-        SINFO("SetConfigJson: setting white balance to " << val);
+        SINFO("SetConfigJson: setting white balance to {}", val);
         SetWhiteBalanceManual(val, status);
       }
     } catch (const wpi::json::exception& e) {
-      SWARNING("SetConfigJson: could not read white balance: " << e.what());
+      SWARNING("SetConfigJson: could not read white balance: {}", e.what());
     }
   }
 
@@ -296,30 +303,30 @@
       auto& setting = config.at("exposure");
       if (setting.is_string()) {
         auto str = setting.get<std::string>();
-        wpi::StringRef s(str);
-        if (s.equals_lower("auto")) {
-          SINFO("SetConfigJson: setting exposure to auto");
+        if (wpi::equals_lower(str, "auto")) {
+          SINFO("SetConfigJson: setting exposure to {}", "auto");
           SetExposureAuto(status);
-        } else if (s.equals_lower("hold")) {
-          SINFO("SetConfigJson: setting exposure to hold current");
+        } else if (wpi::equals_lower(str, "hold")) {
+          SINFO("SetConfigJson: setting exposure to {}", "hold current");
           SetExposureHoldCurrent(status);
         } else {
-          SWARNING("SetConfigJson: could not understand exposure value '"
-                   << str << '\'');
+          SWARNING("SetConfigJson: could not understand exposure value '{}'",
+                   str);
         }
       } else {
         int val = setting.get<int>();
-        SINFO("SetConfigJson: setting exposure to " << val);
+        SINFO("SetConfigJson: setting exposure to {}", val);
         SetExposureManual(val, status);
       }
     } catch (const wpi::json::exception& e) {
-      SWARNING("SetConfigJson: could not read exposure: " << e.what());
+      SWARNING("SetConfigJson: could not read exposure: {}", e.what());
     }
   }
 
   // properties
-  if (config.count("properties") != 0)
+  if (config.count("properties") != 0) {
     SetPropertiesJson(config.at("properties"), m_logger, GetName(), status);
+  }
 
   return true;
 }
@@ -336,7 +343,7 @@
   wpi::json j;
 
   // pixel format
-  wpi::StringRef pixelFormat;
+  std::string_view pixelFormat;
   switch (m_mode.pixelFormat) {
     case VideoMode::kMJPEG:
       pixelFormat = "mjpeg";
@@ -356,30 +363,41 @@
     default:
       break;
   }
-  if (!pixelFormat.empty()) j.emplace("pixel format", pixelFormat);
+  if (!pixelFormat.empty()) {
+    j.emplace("pixel format", pixelFormat);
+  }
 
   // width
-  if (m_mode.width != 0) j.emplace("width", m_mode.width);
+  if (m_mode.width != 0) {
+    j.emplace("width", m_mode.width);
+  }
 
   // height
-  if (m_mode.height != 0) j.emplace("height", m_mode.height);
+  if (m_mode.height != 0) {
+    j.emplace("height", m_mode.height);
+  }
 
   // fps
-  if (m_mode.fps != 0) j.emplace("fps", m_mode.fps);
+  if (m_mode.fps != 0) {
+    j.emplace("fps", m_mode.fps);
+  }
 
   // TODO: output brightness, white balance, and exposure?
 
   // properties
   wpi::json props = GetPropertiesJsonObject(status);
-  if (props.is_array()) j.emplace("properties", props);
+  if (props.is_array()) {
+    j.emplace("properties", props);
+  }
 
   return j;
 }
 
 std::vector<VideoMode> SourceImpl::EnumerateVideoModes(
     CS_Status* status) const {
-  if (!m_properties_cached && !CacheProperties(status))
-    return std::vector<VideoMode>{};
+  if (!m_properties_cached && !CacheProperties(status)) {
+    return {};
+  }
   std::scoped_lock lock(m_mutex);
   return m_videoModes;
 }
@@ -404,10 +422,11 @@
     }
 
     // if nothing found, allocate a new buffer
-    if (found < 0)
-      image.reset(new Image{size});
-    else
+    if (found < 0) {
+      image = std::make_unique<Image>(size);
+    } else {
       image = std::move(m_imagesAvail[found]);
+    }
   }
 
   // Initialize image
@@ -420,14 +439,12 @@
 }
 
 void SourceImpl::PutFrame(VideoMode::PixelFormat pixelFormat, int width,
-                          int height, wpi::StringRef data, Frame::Time time) {
+                          int height, std::string_view data, Frame::Time time) {
   auto image = AllocImage(pixelFormat, width, height, data.size());
 
   // Copy in image data
-  SDEBUG4("Copying data to "
-          << reinterpret_cast<const void*>(image->data()) << " from "
-          << reinterpret_cast<const void*>(data.data()) << " (" << data.size()
-          << " bytes)");
+  SDEBUG4("Copying data to {} from {} ({} bytes)", fmt::ptr(image->data()),
+          fmt::ptr(data.data()), data.size());
   std::memcpy(image->data(), data.data(), data.size());
 
   PutFrame(std::move(image), time);
@@ -448,7 +465,7 @@
   m_frameCv.notify_all();
 }
 
-void SourceImpl::PutError(const wpi::Twine& msg, Frame::Time time) {
+void SourceImpl::PutError(std::string_view msg, Frame::Time time) {
   // Update frame
   {
     std::scoped_lock lock{m_frameMutex};
@@ -464,21 +481,25 @@
                                   propIndex, prop.propKind, prop.value,
                                   prop.valueStr);
   // also notify choices updated event for enum types
-  if (prop.propKind == CS_PROP_ENUM)
+  if (prop.propKind == CS_PROP_ENUM) {
     m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
                                     prop.name, propIndex, prop.propKind,
-                                    prop.value, wpi::Twine{});
+                                    prop.value, {});
+  }
 }
 
 void SourceImpl::UpdatePropertyValue(int property, bool setString, int value,
-                                     const wpi::Twine& valueStr) {
+                                     std::string_view valueStr) {
   auto prop = GetProperty(property);
-  if (!prop) return;
+  if (!prop) {
+    return;
+  }
 
-  if (setString)
+  if (setString) {
     prop->SetValue(valueStr);
-  else
+  } else {
     prop->SetValue(value);
+  }
 
   // Only notify updates after we've notified created
   if (m_properties_cached) {
@@ -490,7 +511,9 @@
 
 void SourceImpl::ReleaseImage(std::unique_ptr<Image> image) {
   std::scoped_lock lock{m_poolMutex};
-  if (m_destroyFrames) return;
+  if (m_destroyFrames) {
+    return;
+  }
   // Return the frame to the pool.  First try to find an empty slot, otherwise
   // add it to the end.
   auto it = std::find(m_imagesAvail.begin(), m_imagesAvail.end(), nullptr);
@@ -504,7 +527,9 @@
         [](const std::unique_ptr<Image>& a, const std::unique_ptr<Image>& b) {
           return a->capacity() < b->capacity();
         });
-    if ((*it2)->capacity() < image->capacity()) *it2 = std::move(image);
+    if ((*it2)->capacity() < image->capacity()) {
+      *it2 = std::move(image);
+    }
   } else {
     m_imagesAvail.emplace_back(std::move(image));
   }
@@ -513,7 +538,9 @@
 std::unique_ptr<Frame::Impl> SourceImpl::AllocFrameImpl() {
   std::scoped_lock lock{m_poolMutex};
 
-  if (m_framesAvail.empty()) return std::make_unique<Frame::Impl>(*this);
+  if (m_framesAvail.empty()) {
+    return std::make_unique<Frame::Impl>(*this);
+  }
 
   auto impl = std::move(m_framesAvail.back());
   m_framesAvail.pop_back();
@@ -522,6 +549,8 @@
 
 void SourceImpl::ReleaseFrameImpl(std::unique_ptr<Frame::Impl> impl) {
   std::scoped_lock lock{m_poolMutex};
-  if (m_destroyFrames) return;
+  if (m_destroyFrames) {
+    return;
+  }
   m_framesAvail.push_back(std::move(impl));
 }
diff --git a/cscore/src/main/native/cpp/SourceImpl.h b/cscore/src/main/native/cpp/SourceImpl.h
index d74d8fa..dd2e574 100644
--- a/cscore/src/main/native/cpp/SourceImpl.h
+++ b/cscore/src/main/native/cpp/SourceImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_SOURCEIMPL_H_
 #define CSCORE_SOURCEIMPL_H_
@@ -12,12 +9,10 @@
 #include <cstddef>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <vector>
 
-#include <wpi/ArrayRef.h>
 #include <wpi/Logger.h>
-#include <wpi/StringRef.h>
-#include <wpi/Twine.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
 
@@ -40,18 +35,18 @@
   friend class Frame;
 
  public:
-  SourceImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
+  SourceImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
              Telemetry& telemetry);
-  virtual ~SourceImpl();
+  ~SourceImpl() override;
   SourceImpl(const SourceImpl& oth) = delete;
   SourceImpl& operator=(const SourceImpl& oth) = delete;
 
   virtual void Start() = 0;
 
-  wpi::StringRef GetName() const { return m_name; }
+  std::string_view GetName() const { return m_name; }
 
-  void SetDescription(const wpi::Twine& description);
-  wpi::StringRef GetDescription(wpi::SmallVectorImpl<char>& buf) const;
+  void SetDescription(std::string_view description);
+  std::string_view GetDescription(wpi::SmallVectorImpl<char>& buf) const;
 
   void SetConnectionStrategy(CS_ConnectionStrategy strategy) {
     m_strategy = static_cast<int>(strategy);
@@ -131,7 +126,7 @@
   virtual bool SetResolution(int width, int height, CS_Status* status);
   virtual bool SetFPS(int fps, CS_Status* status);
 
-  bool SetConfigJson(wpi::StringRef config, CS_Status* status);
+  bool SetConfigJson(std::string_view config, CS_Status* status);
   virtual bool SetConfigJson(const wpi::json& config, CS_Status* status);
   std::string GetConfigJson(CS_Status* status);
   virtual wpi::json GetConfigJsonObject(CS_Status* status);
@@ -144,12 +139,12 @@
  protected:
   void NotifyPropertyCreated(int propIndex, PropertyImpl& prop) override;
   void UpdatePropertyValue(int property, bool setString, int value,
-                           const wpi::Twine& valueStr) override;
+                           std::string_view valueStr) override;
 
   void PutFrame(VideoMode::PixelFormat pixelFormat, int width, int height,
-                wpi::StringRef data, Frame::Time time);
+                std::string_view data, Frame::Time time);
   void PutFrame(std::unique_ptr<Image> image, Frame::Time time);
-  void PutError(const wpi::Twine& msg, Frame::Time time);
+  void PutError(std::string_view msg, Frame::Time time);
 
   // Notification functions for corresponding atomics
   virtual void NumSinksChanged() = 0;
diff --git a/cscore/src/main/native/cpp/Telemetry.cpp b/cscore/src/main/native/cpp/Telemetry.cpp
index 77130f6..751ef44 100644
--- a/cscore/src/main/native/cpp/Telemetry.cpp
+++ b/cscore/src/main/native/cpp/Telemetry.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "Telemetry.h"
 
@@ -25,7 +22,7 @@
  public:
   explicit Thread(Notifier& notifier) : m_notifier(notifier) {}
 
-  void Main();
+  void Main() override;
 
   Notifier& m_notifier;
   wpi::DenseMap<std::pair<CS_Handle, int>, int64_t> m_user;
@@ -46,24 +43,33 @@
   return it->getSecond();
 }
 
-Telemetry::~Telemetry() {}
+Telemetry::~Telemetry() = default;
 
-void Telemetry::Start() { m_owner.Start(m_notifier); }
+void Telemetry::Start() {
+  m_owner.Start(m_notifier);
+}
 
-void Telemetry::Stop() { m_owner.Stop(); }
+void Telemetry::Stop() {
+  m_owner.Stop();
+}
 
 void Telemetry::Thread::Main() {
   std::unique_lock lock(m_mutex);
   auto prevTime = std::chrono::steady_clock::now();
   while (m_active) {
     double period = m_period;
-    if (period == 0) period = 1000.0;
+    if (period == 0) {
+      period = 1000.0;
+    }
     auto timeoutTime = prevTime + std::chrono::duration<double>(period);
     while (m_active && !m_updated) {
-      if (m_cond.wait_until(lock, timeoutTime) == std::cv_status::timeout)
+      if (m_cond.wait_until(lock, timeoutTime) == std::cv_status::timeout) {
         break;
+      }
     }
-    if (!m_active) break;
+    if (!m_active) {
+      break;
+    }
     if (m_updated) {
       m_updated = false;
       continue;
@@ -83,8 +89,12 @@
 
 void Telemetry::SetPeriod(double seconds) {
   auto thr = m_owner.GetThread();
-  if (!thr) return;
-  if (thr->m_period == seconds) return;  // no change
+  if (!thr) {
+    return;
+  }
+  if (thr->m_period == seconds) {
+    return;  // no change
+  }
   thr->m_period = seconds;
   thr->m_updated = true;
   thr->m_cond.notify_one();
@@ -92,7 +102,9 @@
 
 double Telemetry::GetElapsedTime() {
   auto thr = m_owner.GetThread();
-  if (!thr) return 0;
+  if (!thr) {
+    return 0;
+  }
   return thr->m_elapsed;
 }
 
@@ -113,13 +125,17 @@
     *status = CS_TELEMETRY_NOT_ENABLED;
     return 0;
   }
-  if (thr->m_elapsed == 0) return 0.0;
+  if (thr->m_elapsed == 0) {
+    return 0.0;
+  }
   return thr->GetValue(handle, kind, status) / thr->m_elapsed;
 }
 
 void Telemetry::RecordSourceBytes(const SourceImpl& source, int quantity) {
   auto thr = m_owner.GetThread();
-  if (!thr) return;
+  if (!thr) {
+    return;
+  }
   auto handleData = Instance::GetInstance().FindSource(source);
   thr->m_current[std::make_pair(Handle{handleData.first, Handle::kSource},
                                 static_cast<int>(CS_SOURCE_BYTES_RECEIVED))] +=
@@ -128,7 +144,9 @@
 
 void Telemetry::RecordSourceFrames(const SourceImpl& source, int quantity) {
   auto thr = m_owner.GetThread();
-  if (!thr) return;
+  if (!thr) {
+    return;
+  }
   auto handleData = Instance::GetInstance().FindSource(source);
   thr->m_current[std::make_pair(Handle{handleData.first, Handle::kSource},
                                 static_cast<int>(CS_SOURCE_FRAMES_RECEIVED))] +=
diff --git a/cscore/src/main/native/cpp/Telemetry.h b/cscore/src/main/native/cpp/Telemetry.h
index 8729704..0a32d21 100644
--- a/cscore/src/main/native/cpp/Telemetry.h
+++ b/cscore/src/main/native/cpp/Telemetry.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_TELEMETRY_H_
 #define CSCORE_TELEMETRY_H_
diff --git a/cscore/src/main/native/cpp/UnlimitedHandleResource.h b/cscore/src/main/native/cpp/UnlimitedHandleResource.h
index 200572c..f7671ae 100644
--- a/cscore/src/main/native/cpp/UnlimitedHandleResource.h
+++ b/cscore/src/main/native/cpp/UnlimitedHandleResource.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_UNLIMITEDHANDLERESOURCE_H_
 #define CSCORE_UNLIMITEDHANDLERESOURCE_H_
@@ -12,9 +9,9 @@
 #include <utility>
 #include <vector>
 
-#include <wpi/ArrayRef.h>
 #include <wpi/SmallVector.h>
 #include <wpi/mutex.h>
+#include <wpi/span.h>
 
 namespace cs {
 
@@ -53,7 +50,7 @@
   std::shared_ptr<TStruct> Free(THandle handle);
 
   template <typename T>
-  wpi::ArrayRef<T> GetAll(wpi::SmallVectorImpl<T>& vec);
+  wpi::span<T> GetAll(wpi::SmallVectorImpl<T>& vec);
 
   std::vector<std::shared_ptr<TStruct>> FreeAll();
 
@@ -90,7 +87,9 @@
       return MakeHandle(i);
     }
   }
-  if (i >= THandle::kIndexMax) return 0;
+  if (i >= THandle::kIndexMax) {
+    return 0;
+  }
 
   m_structures.emplace_back(
       std::make_shared<TStruct>(std::forward<Args>(args)...));
@@ -108,7 +107,9 @@
       return MakeHandle(i);
     }
   }
-  if (i >= THandle::kIndexMax) return 0;
+  if (i >= THandle::kIndexMax) {
+    return 0;
+  }
 
   m_structures.push_back(structure);
   return MakeHandle(i);
@@ -120,9 +121,13 @@
     THandle handle) {
   auto index =
       handle.GetTypedIndex(static_cast<typename THandle::Type>(typeValue));
-  if (index < 0) return nullptr;
+  if (index < 0) {
+    return nullptr;
+  }
   std::scoped_lock sync(m_handleMutex);
-  if (index >= static_cast<int>(m_structures.size())) return nullptr;
+  if (index >= static_cast<int>(m_structures.size())) {
+    return nullptr;
+  }
   return m_structures[index];
 }
 
@@ -132,9 +137,13 @@
     THandle handle) {
   auto index =
       handle.GetTypedIndex(static_cast<typename THandle::Type>(typeValue));
-  if (index < 0) return nullptr;
+  if (index < 0) {
+    return nullptr;
+  }
   std::scoped_lock sync(m_handleMutex);
-  if (index >= static_cast<int>(m_structures.size())) return nullptr;
+  if (index >= static_cast<int>(m_structures.size())) {
+    return nullptr;
+  }
   auto rv = std::move(m_structures[index]);
   m_structures[index].reset();
   return rv;
@@ -142,7 +151,7 @@
 
 template <typename THandle, typename TStruct, int typeValue, typename TMutex>
 template <typename T>
-inline wpi::ArrayRef<T>
+inline wpi::span<T>
 UnlimitedHandleResource<THandle, TStruct, typeValue, TMutex>::GetAll(
     wpi::SmallVectorImpl<T>& vec) {
   ForEach([&](THandle handle, const TStruct& data) { vec.push_back(handle); });
@@ -164,7 +173,9 @@
 UnlimitedHandleResource<THandle, TStruct, typeValue, TMutex>::ForEach(F func) {
   std::scoped_lock sync(m_handleMutex);
   for (size_t i = 0; i < m_structures.size(); i++) {
-    if (m_structures[i] != nullptr) func(MakeHandle(i), *(m_structures[i]));
+    if (m_structures[i] != nullptr) {
+      func(MakeHandle(i), *(m_structures[i]));
+    }
   }
 }
 
@@ -175,8 +186,9 @@
   std::scoped_lock sync(m_handleMutex);
   for (size_t i = 0; i < m_structures.size(); i++) {
     auto& structure = m_structures[i];
-    if (structure != nullptr && func(*structure))
+    if (structure != nullptr && func(*structure)) {
       return std::make_pair(MakeHandle(i), structure);
+    }
   }
   return std::make_pair(0, nullptr);
 }
diff --git a/cscore/src/main/native/cpp/UsbCameraImplCommon.cpp b/cscore/src/main/native/cpp/UsbCameraImplCommon.cpp
index 41906a7..5731f1f 100644
--- a/cscore/src/main/native/cpp/UsbCameraImplCommon.cpp
+++ b/cscore/src/main/native/cpp/UsbCameraImplCommon.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "cscore_c.h"  // NOLINT(build/include_order)
 
@@ -19,8 +16,9 @@
   out->otherPaths = static_cast<char**>(
       wpi::safe_malloc(in.otherPaths.size() * sizeof(char*)));
   out->otherPathsCount = in.otherPaths.size();
-  for (size_t i = 0; i < in.otherPaths.size(); ++i)
+  for (size_t i = 0; i < in.otherPaths.size(); ++i) {
     out->otherPaths[i] = cs::ConvertToC(in.otherPaths[i]);
+  }
   out->vendorId = in.vendorId;
   out->productId = in.productId;
 }
@@ -28,8 +26,9 @@
 static void FreeUsbCameraInfo(CS_UsbCameraInfo* info) {
   std::free(info->path);
   std::free(info->name);
-  for (int i = 0; i < info->otherPathsCount; ++i)
+  for (int i = 0; i < info->otherPathsCount; ++i) {
     std::free(info->otherPaths[i]);
+  }
   std::free(info->otherPaths);
 }
 
@@ -55,7 +54,9 @@
 
 CS_UsbCameraInfo* CS_GetUsbCameraInfo(CS_Source source, CS_Status* status) {
   auto info = cs::GetUsbCameraInfo(source, status);
-  if (*status != CS_OK) return nullptr;
+  if (*status != CS_OK) {
+    return nullptr;
+  }
   CS_UsbCameraInfo* out = static_cast<CS_UsbCameraInfo*>(
       wpi::safe_malloc(sizeof(CS_UsbCameraInfo)));
   ConvertToC(out, info);
@@ -67,18 +68,26 @@
   CS_UsbCameraInfo* out = static_cast<CS_UsbCameraInfo*>(
       wpi::safe_malloc(cameras.size() * sizeof(CS_UsbCameraInfo)));
   *count = cameras.size();
-  for (size_t i = 0; i < cameras.size(); ++i) ConvertToC(&out[i], cameras[i]);
+  for (size_t i = 0; i < cameras.size(); ++i) {
+    ConvertToC(&out[i], cameras[i]);
+  }
   return out;
 }
 
 void CS_FreeEnumeratedUsbCameras(CS_UsbCameraInfo* cameras, int count) {
-  if (!cameras) return;
-  for (int i = 0; i < count; ++i) FreeUsbCameraInfo(&cameras[i]);
+  if (!cameras) {
+    return;
+  }
+  for (int i = 0; i < count; ++i) {
+    FreeUsbCameraInfo(&cameras[i]);
+  }
   std::free(cameras);
 }
 
 void CS_FreeUsbCameraInfo(CS_UsbCameraInfo* info) {
-  if (!info) return;
+  if (!info) {
+    return;
+  }
   FreeUsbCameraInfo(info);
   std::free(info);
 }
diff --git a/cscore/src/main/native/cpp/UsbCameraListener.h b/cscore/src/main/native/cpp/UsbCameraListener.h
new file mode 100644
index 0000000..c8e8e15
--- /dev/null
+++ b/cscore/src/main/native/cpp/UsbCameraListener.h
@@ -0,0 +1,31 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#ifndef CSCORE_USBCAMERALISTENER_H_
+#define CSCORE_USBCAMERALISTENER_H_
+
+#include <memory>
+
+#include <wpi/Logger.h>
+
+namespace cs {
+
+class Notifier;
+
+class UsbCameraListener {
+ public:
+  UsbCameraListener(wpi::Logger& logger, Notifier& notifier);
+  ~UsbCameraListener();
+
+  void Start();
+  void Stop();
+
+ private:
+  class Impl;
+  std::unique_ptr<Impl> m_impl;
+};
+
+}  // namespace cs
+
+#endif  // CSCORE_USBCAMERALISTENER_H_
diff --git a/cscore/src/main/native/cpp/c_util.h b/cscore/src/main/native/cpp/c_util.h
index f985fe2..904d049 100644
--- a/cscore/src/main/native/cpp/c_util.h
+++ b/cscore/src/main/native/cpp/c_util.h
@@ -1,22 +1,19 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_C_UTIL_H_
 #define CSCORE_C_UTIL_H_
 
 #include <cstdlib>
 #include <cstring>
+#include <string_view>
 
 #include <wpi/MemAlloc.h>
-#include <wpi/StringRef.h>
 
 namespace cs {
 
-inline char* ConvertToC(wpi::StringRef in) {
+inline char* ConvertToC(std::string_view in) {
   char* out = static_cast<char*>(wpi::safe_malloc(in.size() + 1));
   std::memmove(out, in.data(), in.size());
   out[in.size()] = '\0';
diff --git a/cscore/src/main/native/cpp/cscore_c.cpp b/cscore/src/main/native/cpp/cscore_c.cpp
index 321572e..ffbc164 100644
--- a/cscore/src/main/native/cpp/cscore_c.cpp
+++ b/cscore/src/main/native/cpp/cscore_c.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "cscore_c.h"
 
@@ -18,6 +15,39 @@
 #include "cscore_cpp.h"
 #include "cscore_raw.h"
 
+static CS_Event ConvertToC(const cs::RawEvent& rawEvent) {
+  CS_Event event;
+  event.kind = static_cast<CS_EventKind>(static_cast<int>(rawEvent.kind));
+  event.source = rawEvent.sourceHandle;
+  event.sink = rawEvent.sinkHandle;
+  event.name = rawEvent.name.c_str();
+  event.mode = rawEvent.mode;
+  event.property = rawEvent.propertyHandle;
+  event.propertyKind = rawEvent.propertyKind;
+  event.value = rawEvent.value;
+  event.valueStr = rawEvent.valueStr.c_str();
+  event.listener = rawEvent.listener;
+  return event;
+}
+
+template <typename O, typename I>
+static O* ConvertToC(std::vector<I>&& in, int* count) {
+  using T = std::vector<I>;
+  size_t size = in.size();
+  O* out = static_cast<O*>(wpi::safe_malloc(size * sizeof(O) + sizeof(T)));
+  *count = size;
+  for (size_t i = 0; i < size; ++i) {
+    out[i] = ConvertToC(in[i]);
+  }
+
+  // retain vector at end of returned array
+  alignas(T) unsigned char buf[sizeof(T)];
+  new (buf) T(std::move(in));
+  std::memcpy(out + size * sizeof(O), buf, sizeof(T));
+
+  return out;
+}
+
 extern "C" {
 
 CS_PropertyKind CS_GetPropertyKind(CS_Property property, CS_Status* status) {
@@ -27,7 +57,9 @@
 char* CS_GetPropertyName(CS_Property property, CS_Status* status) {
   wpi::SmallString<128> buf;
   auto str = cs::GetPropertyName(property, buf, status);
-  if (*status != 0) return nullptr;
+  if (*status != 0) {
+    return nullptr;
+  }
   return cs::ConvertToC(str);
 }
 
@@ -58,7 +90,9 @@
 char* CS_GetStringProperty(CS_Property property, CS_Status* status) {
   wpi::SmallString<128> buf;
   auto str = cs::GetStringProperty(property, buf, status);
-  if (*status != 0) return nullptr;
+  if (*status != 0) {
+    return nullptr;
+  }
   return cs::ConvertToC(str);
 }
 
@@ -73,8 +107,9 @@
   char** out =
       static_cast<char**>(wpi::safe_malloc(choices.size() * sizeof(char*)));
   *count = choices.size();
-  for (size_t i = 0; i < choices.size(); ++i)
+  for (size_t i = 0; i < choices.size(); ++i) {
     out[i] = cs::ConvertToC(choices[i]);
+  }
   return out;
 }
 
@@ -85,14 +120,18 @@
 char* CS_GetSourceName(CS_Source source, CS_Status* status) {
   wpi::SmallString<128> buf;
   auto str = cs::GetSourceName(source, buf, status);
-  if (*status != 0) return nullptr;
+  if (*status != 0) {
+    return nullptr;
+  }
   return cs::ConvertToC(str);
 }
 
 char* CS_GetSourceDescription(CS_Source source, CS_Status* status) {
   wpi::SmallString<128> buf;
   auto str = cs::GetSourceDescription(source, buf, status);
-  if (*status != 0) return nullptr;
+  if (*status != 0) {
+    return nullptr;
+  }
   return cs::ConvertToC(str);
 }
 
@@ -251,14 +290,18 @@
 char* CS_GetSinkName(CS_Sink sink, CS_Status* status) {
   wpi::SmallString<128> buf;
   auto str = cs::GetSinkName(sink, buf, status);
-  if (*status != 0) return nullptr;
+  if (*status != 0) {
+    return nullptr;
+  }
   return cs::ConvertToC(str);
 }
 
 char* CS_GetSinkDescription(CS_Sink sink, CS_Status* status) {
   wpi::SmallString<128> buf;
   auto str = cs::GetSinkDescription(sink, buf, status);
-  if (*status != 0) return nullptr;
+  if (*status != 0) {
+    return nullptr;
+  }
   return cs::ConvertToC(str);
 }
 
@@ -309,11 +352,11 @@
 }
 
 void CS_SetListenerOnStart(void (*onStart)(void* data), void* data) {
-  cs::SetListenerOnStart([=]() { onStart(data); });
+  cs::SetListenerOnStart([=] { onStart(data); });
 }
 
 void CS_SetListenerOnExit(void (*onExit)(void* data), void* data) {
-  cs::SetListenerOnExit([=]() { onExit(data); });
+  cs::SetListenerOnExit([=] { onExit(data); });
 }
 
 CS_Listener CS_AddListener(void* data,
@@ -322,16 +365,7 @@
                            CS_Status* status) {
   return cs::AddListener(
       [=](const cs::RawEvent& rawEvent) {
-        CS_Event event;
-        event.kind = static_cast<CS_EventKind>(static_cast<int>(rawEvent.kind));
-        event.source = rawEvent.sourceHandle;
-        event.sink = rawEvent.sinkHandle;
-        event.name = rawEvent.name.c_str();
-        event.mode = rawEvent.mode;
-        event.property = rawEvent.propertyHandle;
-        event.propertyKind = rawEvent.propertyKind;
-        event.value = rawEvent.value;
-        event.valueStr = rawEvent.valueStr.c_str();
+        CS_Event event = ConvertToC(rawEvent);
         callback(data, &event);
       },
       eventMask, immediateNotify, status);
@@ -341,9 +375,52 @@
   return cs::RemoveListener(handle, status);
 }
 
-int CS_NotifierDestroyed(void) { return cs::NotifierDestroyed(); }
+CS_ListenerPoller CS_CreateListenerPoller(void) {
+  return cs::CreateListenerPoller();
+}
 
-void CS_SetTelemetryPeriod(double seconds) { cs::SetTelemetryPeriod(seconds); }
+void CS_DestroyListenerPoller(CS_ListenerPoller poller) {
+  cs::DestroyListenerPoller(poller);
+}
+
+CS_Listener CS_AddPolledListener(CS_ListenerPoller poller, int eventMask,
+                                 CS_Bool immediateNotify, CS_Status* status) {
+  return cs::AddPolledListener(poller, eventMask, immediateNotify, status);
+}
+
+struct CS_Event* CS_PollListener(CS_ListenerPoller poller, int* count) {
+  return ConvertToC<CS_Event>(cs::PollListener(poller), count);
+}
+
+struct CS_Event* CS_PollListenerTimeout(CS_ListenerPoller poller, int* count,
+                                        double timeout, CS_Bool* timedOut) {
+  bool cppTimedOut = false;
+  auto arrCpp = cs::PollListener(poller, timeout, &cppTimedOut);
+  *timedOut = cppTimedOut;
+  return ConvertToC<CS_Event>(std::move(arrCpp), count);
+}
+
+void CS_CancelPollListener(CS_ListenerPoller poller) {
+  cs::CancelPollListener(poller);
+}
+
+void CS_FreeEvents(CS_Event* arr, int count) {
+  // destroy vector saved at end of array
+  using T = std::vector<cs::RawEvent>;
+  alignas(T) unsigned char buf[sizeof(T)];
+  std::memcpy(buf, arr + count * sizeof(CS_Event), sizeof(T));
+  reinterpret_cast<T*>(buf)->~T();
+
+  std::free(arr);
+}
+
+int CS_NotifierDestroyed(void) {
+  return cs::NotifierDestroyed();
+}
+
+void CS_SetTelemetryPeriod(double seconds) {
+  cs::SetTelemetryPeriod(seconds);
+}
 
 double CS_GetTelemetryElapsedTime(void) {
   return cs::GetTelemetryElapsedTime();
@@ -367,7 +444,9 @@
   cs::SetDefaultLogger(min_level);
 }
 
-void CS_Shutdown(void) { cs::Shutdown(); }
+void CS_Shutdown(void) {
+  cs::Shutdown();
+}
 
 CS_Source* CS_EnumerateSources(int* count, CS_Status* status) {
   wpi::SmallVector<CS_Source, 32> buf;
@@ -380,10 +459,14 @@
 }
 
 void CS_ReleaseEnumeratedSources(CS_Source* sources, int count) {
-  if (!sources) return;
+  if (!sources) {
+    return;
+  }
   for (int i = 0; i < count; ++i) {
     CS_Status status = 0;
-    if (sources[i] != 0) cs::ReleaseSource(sources[i], &status);
+    if (sources[i] != 0) {
+      cs::ReleaseSource(sources[i], &status);
+    }
   }
   std::free(sources);
 }
@@ -399,19 +482,29 @@
 }
 
 void CS_ReleaseEnumeratedSinks(CS_Sink* sinks, int count) {
-  if (!sinks) return;
+  if (!sinks) {
+    return;
+  }
   for (int i = 0; i < count; ++i) {
     CS_Status status = 0;
-    if (sinks[i] != 0) cs::ReleaseSink(sinks[i], &status);
+    if (sinks[i] != 0) {
+      cs::ReleaseSink(sinks[i], &status);
+    }
   }
   std::free(sinks);
 }
 
-void CS_FreeString(char* str) { std::free(str); }
+void CS_FreeString(char* str) {
+  std::free(str);
+}
 
 void CS_FreeEnumPropertyChoices(char** choices, int count) {
-  if (!choices) return;
-  for (int i = 0; i < count; ++i) std::free(choices[i]);
+  if (!choices) {
+    return;
+  }
+  for (int i = 0; i < count; ++i) {
+    std::free(choices[i]);
+  }
   std::free(choices);
 }
 
@@ -423,26 +516,35 @@
   std::free(modes);
 }
 
-char* CS_GetHostname() { return cs::ConvertToC(cs::GetHostname()); }
+char* CS_GetHostname() {
+  return cs::ConvertToC(cs::GetHostname());
+}
 
 char** CS_GetNetworkInterfaces(int* count) {
   auto interfaces = cs::GetNetworkInterfaces();
   char** out =
       static_cast<char**>(wpi::safe_malloc(interfaces.size() * sizeof(char*)));
   *count = interfaces.size();
-  for (size_t i = 0; i < interfaces.size(); ++i)
+  for (size_t i = 0; i < interfaces.size(); ++i) {
     out[i] = cs::ConvertToC(interfaces[i]);
+  }
   return out;
 }
 
 void CS_FreeNetworkInterfaces(char** interfaces, int count) {
-  if (!interfaces) return;
-  for (int i = 0; i < count; ++i) std::free(interfaces[i]);
+  if (!interfaces) {
+    return;
+  }
+  for (int i = 0; i < count; ++i) {
+    std::free(interfaces[i]);
+  }
   std::free(interfaces);
 }
 
 void CS_AllocateRawFrameData(CS_RawFrame* frame, int requestedSize) {
-  if (frame->dataLength >= requestedSize) return;
+  if (frame->dataLength >= requestedSize) {
+    return;
+  }
   if (frame->data) {
     frame->data =
         static_cast<char*>(wpi::safe_realloc(frame->data, requestedSize));
diff --git a/cscore/src/main/native/cpp/cscore_cpp.cpp b/cscore/src/main/native/cpp/cscore_cpp.cpp
index d49fb04..3b4e570 100644
--- a/cscore/src/main/native/cpp/cscore_cpp.cpp
+++ b/cscore/src/main/native/cpp/cscore_cpp.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "cscore_cpp.h"
 
@@ -60,7 +57,9 @@
 CS_PropertyKind GetPropertyKind(CS_Property property, CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return CS_PROP_NONE;
+  if (!container) {
+    return CS_PROP_NONE;
+  }
   return container->GetPropertyKind(propertyIndex);
 }
 
@@ -68,58 +67,74 @@
   wpi::SmallString<128> buf;
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return std::string{};
-  return container->GetPropertyName(propertyIndex, buf, status);
+  if (!container) {
+    return {};
+  }
+  return std::string{container->GetPropertyName(propertyIndex, buf, status)};
 }
 
-wpi::StringRef GetPropertyName(CS_Property property,
-                               wpi::SmallVectorImpl<char>& buf,
-                               CS_Status* status) {
+std::string_view GetPropertyName(CS_Property property,
+                                 wpi::SmallVectorImpl<char>& buf,
+                                 CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return wpi::StringRef{};
+  if (!container) {
+    return {};
+  }
   return container->GetPropertyName(propertyIndex, buf, status);
 }
 
 int GetProperty(CS_Property property, CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return false;
+  if (!container) {
+    return false;
+  }
   return container->GetProperty(propertyIndex, status);
 }
 
 void SetProperty(CS_Property property, int value, CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return;
+  if (!container) {
+    return;
+  }
   container->SetProperty(propertyIndex, value, status);
 }
 
 int GetPropertyMin(CS_Property property, CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return 0.0;
+  if (!container) {
+    return 0.0;
+  }
   return container->GetPropertyMin(propertyIndex, status);
 }
 
 int GetPropertyMax(CS_Property property, CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return 0.0;
+  if (!container) {
+    return 0.0;
+  }
   return container->GetPropertyMax(propertyIndex, status);
 }
 
 int GetPropertyStep(CS_Property property, CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return 0.0;
+  if (!container) {
+    return 0.0;
+  }
   return container->GetPropertyStep(propertyIndex, status);
 }
 
 int GetPropertyDefault(CS_Property property, CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return 0.0;
+  if (!container) {
+    return 0.0;
+  }
   return container->GetPropertyDefault(propertyIndex, status);
 }
 
@@ -127,24 +142,30 @@
   wpi::SmallString<128> buf;
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return std::string{};
-  return container->GetStringProperty(propertyIndex, buf, status);
+  if (!container) {
+    return {};
+  }
+  return std::string{container->GetStringProperty(propertyIndex, buf, status)};
 }
 
-wpi::StringRef GetStringProperty(CS_Property property,
-                                 wpi::SmallVectorImpl<char>& buf,
-                                 CS_Status* status) {
+std::string_view GetStringProperty(CS_Property property,
+                                   wpi::SmallVectorImpl<char>& buf,
+                                   CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return wpi::StringRef{};
+  if (!container) {
+    return {};
+  }
   return container->GetStringProperty(propertyIndex, buf, status);
 }
 
-void SetStringProperty(CS_Property property, const wpi::Twine& value,
+void SetStringProperty(CS_Property property, std::string_view value,
                        CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return;
+  if (!container) {
+    return;
+  }
   container->SetStringProperty(propertyIndex, value, status);
 }
 
@@ -152,7 +173,9 @@
                                                 CS_Status* status) {
   int propertyIndex;
   auto container = GetPropertyContainer(property, &propertyIndex, status);
-  if (!container) return std::vector<std::string>{};
+  if (!container) {
+    return {};
+  }
   return container->GetEnumPropertyChoices(propertyIndex, status);
 }
 
@@ -173,17 +196,18 @@
   auto data = Instance::GetInstance().GetSource(source);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return std::string{};
+    return {};
   }
-  return data->source->GetName();
+  return std::string{data->source->GetName()};
 }
 
-wpi::StringRef GetSourceName(CS_Source source, wpi::SmallVectorImpl<char>& buf,
-                             CS_Status* status) {
+std::string_view GetSourceName(CS_Source source,
+                               wpi::SmallVectorImpl<char>& buf,
+                               CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return wpi::StringRef{};
+    return {};
   }
   return data->source->GetName();
 }
@@ -192,19 +216,19 @@
   auto data = Instance::GetInstance().GetSource(source);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return std::string{};
+    return {};
   }
   wpi::SmallString<128> buf;
-  return data->source->GetDescription(buf);
+  return std::string{data->source->GetDescription(buf)};
 }
 
-wpi::StringRef GetSourceDescription(CS_Source source,
-                                    wpi::SmallVectorImpl<char>& buf,
-                                    CS_Status* status) {
+std::string_view GetSourceDescription(CS_Source source,
+                                      wpi::SmallVectorImpl<char>& buf,
+                                      CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return wpi::StringRef{};
+    return {};
   }
   return data->source->GetDescription(buf);
 }
@@ -247,7 +271,7 @@
   return data->source->IsEnabled();
 }
 
-CS_Property GetSourceProperty(CS_Source source, const wpi::Twine& name,
+CS_Property GetSourceProperty(CS_Source source, std::string_view name,
                               CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data) {
@@ -262,18 +286,19 @@
   return Handle{source, property, Handle::kProperty};
 }
 
-wpi::ArrayRef<CS_Property> EnumerateSourceProperties(
+wpi::span<CS_Property> EnumerateSourceProperties(
     CS_Source source, wpi::SmallVectorImpl<CS_Property>& vec,
     CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return 0;
+    return {};
   }
   wpi::SmallVector<int, 32> properties_buf;
   for (auto property :
-       data->source->EnumerateProperties(properties_buf, status))
+       data->source->EnumerateProperties(properties_buf, status)) {
     vec.push_back(Handle{source, property, Handle::kProperty});
+  }
   return vec;
 }
 
@@ -325,7 +350,7 @@
   return data->source->SetFPS(fps, status);
 }
 
-bool SetSourceConfigJson(CS_Source source, wpi::StringRef config,
+bool SetSourceConfigJson(CS_Source source, std::string_view config,
                          CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data) {
@@ -373,20 +398,22 @@
   return data->source->EnumerateVideoModes(status);
 }
 
-wpi::ArrayRef<CS_Sink> EnumerateSourceSinks(CS_Source source,
-                                            wpi::SmallVectorImpl<CS_Sink>& vec,
-                                            CS_Status* status) {
+wpi::span<CS_Sink> EnumerateSourceSinks(CS_Source source,
+                                        wpi::SmallVectorImpl<CS_Sink>& vec,
+                                        CS_Status* status) {
   auto& inst = Instance::GetInstance();
   auto data = inst.GetSource(source);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return wpi::ArrayRef<CS_Sink>{};
+    return {};
   }
   return inst.EnumerateSourceSinks(source, vec);
 }
 
 CS_Source CopySource(CS_Source source, CS_Status* status) {
-  if (source == 0) return 0;
+  if (source == 0) {
+    return 0;
+  }
   auto data = Instance::GetInstance().GetSource(source);
   if (!data) {
     *status = CS_INVALID_HANDLE;
@@ -397,14 +424,18 @@
 }
 
 void ReleaseSource(CS_Source source, CS_Status* status) {
-  if (source == 0) return;
+  if (source == 0) {
+    return;
+  }
   auto& inst = Instance::GetInstance();
   auto data = inst.GetSource(source);
   if (!data) {
     *status = CS_INVALID_HANDLE;
     return;
   }
-  if (data->refCount-- == 0) inst.DestroySource(source);
+  if (data->refCount-- == 0) {
+    inst.DestroySource(source);
+  }
 }
 
 //
@@ -501,17 +532,17 @@
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return std::string{};
+    return {};
   }
-  return data->sink->GetName();
+  return std::string{data->sink->GetName()};
 }
 
-wpi::StringRef GetSinkName(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
-                           CS_Status* status) {
+std::string_view GetSinkName(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
+                             CS_Status* status) {
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return wpi::StringRef{};
+    return {};
   }
   return data->sink->GetName();
 }
@@ -520,23 +551,24 @@
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return std::string{};
+    return {};
   }
   wpi::SmallString<128> buf;
-  return data->sink->GetDescription(buf);
+  return std::string{data->sink->GetDescription(buf)};
 }
 
-wpi::StringRef GetSinkDescription(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
-                                  CS_Status* status) {
+std::string_view GetSinkDescription(CS_Sink sink,
+                                    wpi::SmallVectorImpl<char>& buf,
+                                    CS_Status* status) {
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return wpi::StringRef{};
+    return {};
   }
   return data->sink->GetDescription(buf);
 }
 
-CS_Property GetSinkProperty(CS_Sink sink, const wpi::Twine& name,
+CS_Property GetSinkProperty(CS_Sink sink, std::string_view name,
                             CS_Status* status) {
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data) {
@@ -551,20 +583,23 @@
   return Handle{sink, property, Handle::kSinkProperty};
 }
 
-wpi::ArrayRef<CS_Property> EnumerateSinkProperties(
+wpi::span<CS_Property> EnumerateSinkProperties(
     CS_Sink sink, wpi::SmallVectorImpl<CS_Property>& vec, CS_Status* status) {
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data) {
     *status = CS_INVALID_HANDLE;
-    return 0;
+    return {};
   }
   wpi::SmallVector<int, 32> properties_buf;
-  for (auto property : data->sink->EnumerateProperties(properties_buf, status))
+  for (auto property :
+       data->sink->EnumerateProperties(properties_buf, status)) {
     vec.push_back(Handle{sink, property, Handle::kSinkProperty});
+  }
   return vec;
 }
 
-bool SetSinkConfigJson(CS_Sink sink, wpi::StringRef config, CS_Status* status) {
+bool SetSinkConfigJson(CS_Sink sink, std::string_view config,
+                       CS_Status* status) {
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data) {
     *status = CS_INVALID_HANDLE;
@@ -631,7 +666,7 @@
   return data->sourceHandle.load();
 }
 
-CS_Property GetSinkSourceProperty(CS_Sink sink, const wpi::Twine& name,
+CS_Property GetSinkSourceProperty(CS_Sink sink, std::string_view name,
                                   CS_Status* status) {
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data) {
@@ -642,7 +677,9 @@
 }
 
 CS_Sink CopySink(CS_Sink sink, CS_Status* status) {
-  if (sink == 0) return 0;
+  if (sink == 0) {
+    return 0;
+  }
   auto data = Instance::GetInstance().GetSink(sink);
   if (!data) {
     *status = CS_INVALID_HANDLE;
@@ -653,14 +690,18 @@
 }
 
 void ReleaseSink(CS_Sink sink, CS_Status* status) {
-  if (sink == 0) return;
+  if (sink == 0) {
+    return;
+  }
   auto& inst = Instance::GetInstance();
   auto data = inst.GetSink(sink);
   if (!data) {
     *status = CS_INVALID_HANDLE;
     return;
   }
-  if (data->refCount-- == 0) inst.DestroySink(sink);
+  if (data->refCount-- == 0) {
+    inst.DestroySink(sink);
+  }
 }
 
 //
@@ -675,16 +716,30 @@
   Instance::GetInstance().notifier.SetOnExit(onExit);
 }
 
+static void StartBackground(int eventMask, bool immediateNotify) {
+  auto& inst = Instance::GetInstance();
+  if ((eventMask & CS_NETWORK_INTERFACES_CHANGED) != 0) {
+    // start network interface event listener
+    inst.networkListener.Start();
+    if (immediateNotify) {
+      inst.notifier.NotifyNetworkInterfacesChanged();
+    }
+  }
+  if ((eventMask & CS_USB_CAMERAS_CHANGED) != 0) {
+    // start network interface event listener
+    inst.usbCameraListener.Start();
+    if (immediateNotify) {
+      inst.notifier.NotifyUsbCamerasChanged();
+    }
+  }
+}
+
 CS_Listener AddListener(std::function<void(const RawEvent& event)> callback,
                         int eventMask, bool immediateNotify,
                         CS_Status* status) {
   auto& inst = Instance::GetInstance();
-  int uid = inst.notifier.AddListener(callback, eventMask);
-  if ((eventMask & CS_NETWORK_INTERFACES_CHANGED) != 0) {
-    // start network interface event listener
-    inst.networkListener.Start();
-    if (immediateNotify) inst.notifier.NotifyNetworkInterfacesChanged();
-  }
+  int uid = inst.notifier.Add(callback, eventMask);
+  StartBackground(eventMask, immediateNotify);
   if (immediateNotify) {
     // TODO
   }
@@ -697,10 +752,68 @@
     *status = CS_INVALID_HANDLE;
     return;
   }
-  Instance::GetInstance().notifier.RemoveListener(uid);
+  Instance::GetInstance().notifier.Remove(uid);
 }
 
-bool NotifierDestroyed() { return Notifier::destroyed(); }
+CS_ListenerPoller CreateListenerPoller() {
+  auto& inst = Instance::GetInstance();
+  return Handle(inst.notifier.CreatePoller(), Handle::kListenerPoller);
+}
+
+void DestroyListenerPoller(CS_ListenerPoller poller) {
+  int uid = Handle{poller}.GetTypedIndex(Handle::kListenerPoller);
+  if (uid < 0) {
+    return;
+  }
+  Instance::GetInstance().notifier.RemovePoller(uid);
+}
+
+CS_Listener AddPolledListener(CS_ListenerPoller poller, int eventMask,
+                              bool immediateNotify, CS_Status* status) {
+  Handle handle{poller};
+  int id = handle.GetTypedIndex(Handle::kListenerPoller);
+  if (id < 0) {
+    *status = CS_INVALID_HANDLE;
+    return 0;
+  }
+
+  auto& inst = Instance::GetInstance();
+  int uid = inst.notifier.AddPolled(id, eventMask);
+  StartBackground(eventMask, immediateNotify);
+  return Handle{uid, Handle::kListener};
+}
+
+std::vector<RawEvent> PollListener(CS_ListenerPoller poller) {
+  Handle handle{poller};
+  int id = handle.GetTypedIndex(Handle::kListenerPoller);
+  if (id < 0) {
+    return {};
+  }
+  return Instance::GetInstance().notifier.Poll(id);
+}
+
+std::vector<RawEvent> PollListener(CS_ListenerPoller poller, double timeout,
+                                   bool* timedOut) {
+  Handle handle{poller};
+  int id = handle.GetTypedIndex(Handle::kListenerPoller);
+  if (id < 0) {
+    return {};
+  }
+  return Instance::GetInstance().notifier.Poll(id, timeout, timedOut);
+}
+
+void CancelPollListener(CS_ListenerPoller poller) {
+  Handle handle{poller};
+  int id = handle.GetTypedIndex(Handle::kListenerPoller);
+  if (id < 0) {
+    return;
+  }
+  return Instance::GetInstance().notifier.CancelPoll(id);
+}
+
+bool NotifierDestroyed() {
+  return false;
+}
 
 //
 // Telemetry Functions
@@ -744,22 +857,26 @@
 //
 // Shutdown Function
 //
-void Shutdown() { Instance::GetInstance().Shutdown(); }
+void Shutdown() {
+  Instance::GetInstance().Shutdown();
+}
 
 //
 // Utility Functions
 //
 
-wpi::ArrayRef<CS_Source> EnumerateSourceHandles(
+wpi::span<CS_Source> EnumerateSourceHandles(
     wpi::SmallVectorImpl<CS_Source>& vec, CS_Status* status) {
   return Instance::GetInstance().EnumerateSourceHandles(vec);
 }
 
-wpi::ArrayRef<CS_Sink> EnumerateSinkHandles(wpi::SmallVectorImpl<CS_Sink>& vec,
-                                            CS_Status* status) {
+wpi::span<CS_Sink> EnumerateSinkHandles(wpi::SmallVectorImpl<CS_Sink>& vec,
+                                        CS_Status* status) {
   return Instance::GetInstance().EnumerateSinkHandles(vec);
 }
 
-std::string GetHostname() { return wpi::GetHostname(); }
+std::string GetHostname() {
+  return wpi::GetHostname();
+}
 
 }  // namespace cs
diff --git a/cscore/src/main/native/cpp/cscore_oo.cpp b/cscore/src/main/native/cpp/cscore_oo.cpp
index f42ed5a..cca5ba7 100644
--- a/cscore/src/main/native/cpp/cscore_oo.cpp
+++ b/cscore/src/main/native/cpp/cscore_oo.cpp
@@ -1,12 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "cscore_oo.h"
 
+#include <fmt/format.h>
 #include <wpi/json.h>
 
 using namespace cs;
@@ -28,8 +26,9 @@
 
   std::vector<VideoProperty> properties;
   properties.reserve(handles.size());
-  for (CS_Property handle : handles)
+  for (CS_Property handle : handles) {
     properties.emplace_back(VideoProperty{handle});
+  }
   return properties;
 }
 
@@ -40,7 +39,9 @@
 
   std::vector<VideoSink> sinks;
   sinks.reserve(handles.size());
-  for (int handle : handles) sinks.emplace_back(VideoSink{handle});
+  for (int handle : handles) {
+    sinks.emplace_back(VideoSink{handle});
+  }
   return sinks;
 }
 
@@ -51,7 +52,9 @@
 
   std::vector<VideoSource> sources;
   sources.reserve(handles.size());
-  for (int handle : handles) sources.emplace_back(VideoSource{handle});
+  for (int handle : handles) {
+    sources.emplace_back(VideoSource{handle});
+  }
   return sources;
 }
 
@@ -62,8 +65,9 @@
 
   std::vector<VideoProperty> properties;
   properties.reserve(handles.size());
-  for (CS_Property handle : handles)
+  for (CS_Property handle : handles) {
     properties.emplace_back(VideoProperty{handle});
+  }
   return properties;
 }
 
@@ -74,6 +78,12 @@
 
   std::vector<VideoSink> sinks;
   sinks.reserve(handles.size());
-  for (int handle : handles) sinks.emplace_back(VideoSink{handle});
+  for (int handle : handles) {
+    sinks.emplace_back(VideoSink{handle});
+  }
   return sinks;
 }
+
+std::string AxisCamera::HostToUrl(std::string_view host) {
+  return fmt::format("http://{}/mjpg/video.mjpg", host);
+}
diff --git a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
index e9408fd..b99d322 100644
--- a/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
+++ b/cscore/src/main/native/cpp/jni/CameraServerJNI.cpp
@@ -1,21 +1,19 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include <exception>
 
+#include <fmt/format.h>
 #include <opencv2/core/core.hpp>
 #include <wpi/SmallString.h>
 #include <wpi/jni_util.h>
-#include <wpi/raw_ostream.h>
+#include <wpi/span.h>
 
 #include "cscore_cpp.h"
 #include "cscore_cv.h"
 #include "cscore_raw.h"
-#include "edu_wpi_cscore_CameraServerJNI.h"
+#include "edu_wpi_first_cscore_CameraServerJNI.h"
 
 namespace cv {
 class Mat;
@@ -34,6 +32,7 @@
 static JClass videoEventCls;
 static JClass rawFrameCls;
 static JException videoEx;
+static JException interruptedEx;
 static JException nullPointerEx;
 static JException unsupportedEx;
 static JException exceptionEx;
@@ -41,41 +40,49 @@
 static JNIEnv* listenerEnv = nullptr;
 
 static const JClassInit classes[] = {
-    {"edu/wpi/cscore/UsbCameraInfo", &usbCameraInfoCls},
-    {"edu/wpi/cscore/VideoMode", &videoModeCls},
-    {"edu/wpi/cscore/VideoEvent", &videoEventCls},
-    {"edu/wpi/cscore/raw/RawFrame", &rawFrameCls}};
+    {"edu/wpi/first/cscore/UsbCameraInfo", &usbCameraInfoCls},
+    {"edu/wpi/first/cscore/VideoMode", &videoModeCls},
+    {"edu/wpi/first/cscore/VideoEvent", &videoEventCls},
+    {"edu/wpi/first/cscore/raw/RawFrame", &rawFrameCls}};
 
 static const JExceptionInit exceptions[] = {
-    {"edu/wpi/cscore/VideoException", &videoEx},
+    {"edu/wpi/first/cscore/VideoException", &videoEx},
+    {"java/lang/InterruptedException", &interruptedEx},
     {"java/lang/NullPointerException", &nullPointerEx},
     {"java/lang/UnsupportedOperationException", &unsupportedEx},
     {"java/lang/Exception", &exceptionEx}};
 
 static void ListenerOnStart() {
-  if (!jvm) return;
+  if (!jvm) {
+    return;
+  }
   JNIEnv* env;
   JavaVMAttachArgs args;
   args.version = JNI_VERSION_1_2;
   args.name = const_cast<char*>("CSListener");
   args.group = nullptr;
   if (jvm->AttachCurrentThreadAsDaemon(reinterpret_cast<void**>(&env), &args) !=
-      JNI_OK)
+      JNI_OK) {
     return;
-  if (!env || !env->functions) return;
+  }
+  if (!env || !env->functions) {
+    return;
+  }
   listenerEnv = env;
 }
 
 static void ListenerOnExit() {
   listenerEnv = nullptr;
-  if (!jvm) return;
+  if (!jvm) {
+    return;
+  }
   jvm->DetachCurrentThread();
 }
 
 /// throw java exception
 static void ThrowJavaException(JNIEnv* env, const std::exception* e) {
   wpi::SmallString<128> what;
-  jclass je = 0;
+  jclass je = nullptr;
 
   if (e) {
     const char* exception_type = "std::exception";
@@ -92,7 +99,9 @@
     what = "unknown exception";
   }
 
-  if (!je) je = exceptionEx;
+  if (!je) {
+    je = exceptionEx;
+  }
   env->ThrowNew(je, what.c_str());
 }
 
@@ -102,18 +111,23 @@
   jvm = vm;
 
   JNIEnv* env;
-  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
+  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
     return JNI_ERR;
+  }
 
   // Cache references to classes
   for (auto& c : classes) {
     *c.cls = JClass(env, c.name);
-    if (!*c.cls) return JNI_ERR;
+    if (!*c.cls) {
+      return JNI_ERR;
+    }
   }
 
   for (auto& c : exceptions) {
     *c.cls = JException(env, c.name);
-    if (!*c.cls) return JNI_ERR;
+    if (!*c.cls) {
+      return JNI_ERR;
+    }
   }
 
   // Initial configuration of listener start/exit
@@ -127,8 +141,9 @@
   cs::Shutdown();
 
   JNIEnv* env;
-  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
+  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
     return;
+  }
   // Delete global references
   for (auto& c : classes) {
     c.cls->free(env);
@@ -150,22 +165,29 @@
   JCSGlobal(JNIEnv* env, T obj)
       : m_obj(static_cast<T>(env->NewGlobalRef(obj))) {}
   ~JCSGlobal() {
-    if (!jvm || cs::NotifierDestroyed()) return;
+    if (!jvm || cs::NotifierDestroyed()) {
+      return;
+    }
     JNIEnv* env;
     bool attached = false;
     // don't attach and de-attach if already attached to a thread.
     if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) ==
         JNI_EDETACHED) {
       if (jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), nullptr) !=
-          JNI_OK)
+          JNI_OK) {
         return;
+      }
       attached = true;
     }
-    if (!env || !env->functions) return;
+    if (!env || !env->functions) {
+      return;
+    }
     env->DeleteGlobalRef(m_obj);
-    if (attached) jvm->DetachCurrentThread();
+    if (attached) {
+      jvm->DetachCurrentThread();
+    }
   }
-  operator T() { return m_obj; }
+  operator T() { return m_obj; }  // NOLINT
   T obj() { return m_obj; }
 
  private:
@@ -173,8 +195,11 @@
 };
 
 static void ReportError(JNIEnv* env, CS_Status status) {
-  if (status == CS_OK) return;
-  wpi::SmallString<64> msg;
+  if (status == CS_OK) {
+    return;
+  }
+  std::string_view msg;
+  std::string msgBuf;
   switch (status) {
     case CS_PROPERTY_WRITE_FAILED:
       msg = "property write failed";
@@ -207,8 +232,8 @@
       msg = "telemetry not enabled";
       break;
     default: {
-      wpi::raw_svector_ostream oss{msg};
-      oss << "unknown error code=" << status;
+      msgBuf = fmt::format("unknown error code={}", status);
+      msg = msgBuf;
       break;
     }
   }
@@ -216,7 +241,9 @@
 }
 
 static inline bool CheckStatus(JNIEnv* env, CS_Status status) {
-  if (status != CS_OK) ReportError(env, status);
+  if (status != CS_OK) {
+    ReportError(env, status);
+  }
   return status == CS_OK;
 }
 
@@ -245,7 +272,7 @@
 static jobject MakeJObject(JNIEnv* env, const cs::RawEvent& event) {
   static jmethodID constructor =
       env->GetMethodID(videoEventCls, "<init>",
-                       "(IIILjava/lang/String;IIIIIIILjava/lang/String;)V");
+                       "(IIILjava/lang/String;IIIIIIILjava/lang/String;I)V");
   JLocal<jstring> name(env, MakeJString(env, event.name));
   JLocal<jstring> valueStr(env, MakeJString(env, event.valueStr));
   // clang-format off
@@ -263,19 +290,33 @@
       static_cast<jint>(event.propertyHandle),
       static_cast<jint>(event.propertyKind),
       static_cast<jint>(event.value),
-      valueStr.obj());
+      valueStr.obj(),
+      static_cast<jint>(event.listener));
   // clang-format on
 }
 
+static jobjectArray MakeJObject(JNIEnv* env,
+                                wpi::span<const cs::RawEvent> arr) {
+  jobjectArray jarr = env->NewObjectArray(arr.size(), videoEventCls, nullptr);
+  if (!jarr) {
+    return nullptr;
+  }
+  for (size_t i = 0; i < arr.size(); ++i) {
+    JLocal<jobject> elem{env, MakeJObject(env, arr[i])};
+    env->SetObjectArrayElement(jarr, i, elem.obj());
+  }
+  return jarr;
+}
+
 extern "C" {
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getPropertyKind
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getPropertyKind
+Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyKind
   (JNIEnv* env, jclass, jint property)
 {
   CS_Status status = 0;
@@ -285,28 +326,30 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getPropertyName
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getPropertyName
+Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyName
   (JNIEnv* env, jclass, jint property)
 {
   CS_Status status = 0;
   wpi::SmallString<128> buf;
   auto str = cs::GetPropertyName(property, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJString(env, str);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getProperty
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getProperty
+Java_edu_wpi_first_cscore_CameraServerJNI_getProperty
   (JNIEnv* env, jclass, jint property)
 {
   CS_Status status = 0;
@@ -316,12 +359,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setProperty
  * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setProperty
+Java_edu_wpi_first_cscore_CameraServerJNI_setProperty
   (JNIEnv* env, jclass, jint property, jint value)
 {
   CS_Status status = 0;
@@ -330,12 +373,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getPropertyMin
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getPropertyMin
+Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyMin
   (JNIEnv* env, jclass, jint property)
 {
   CS_Status status = 0;
@@ -345,12 +388,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getPropertyMax
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getPropertyMax
+Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyMax
   (JNIEnv* env, jclass, jint property)
 {
   CS_Status status = 0;
@@ -360,12 +403,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getPropertyStep
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getPropertyStep
+Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyStep
   (JNIEnv* env, jclass, jint property)
 {
   CS_Status status = 0;
@@ -375,12 +418,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getPropertyDefault
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getPropertyDefault
+Java_edu_wpi_first_cscore_CameraServerJNI_getPropertyDefault
   (JNIEnv* env, jclass, jint property)
 {
   CS_Status status = 0;
@@ -390,28 +433,30 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getStringProperty
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getStringProperty
+Java_edu_wpi_first_cscore_CameraServerJNI_getStringProperty
   (JNIEnv* env, jclass, jint property)
 {
   CS_Status status = 0;
   wpi::SmallString<128> buf;
   auto str = cs::GetStringProperty(property, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJString(env, str);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setStringProperty
  * Signature: (ILjava/lang/String;)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setStringProperty
+Java_edu_wpi_first_cscore_CameraServerJNI_setStringProperty
   (JNIEnv* env, jclass, jint property, jstring value)
 {
   if (!value) {
@@ -424,27 +469,29 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getEnumPropertyChoices
  * Signature: (I)[Ljava/lang/Object;
  */
 JNIEXPORT jobjectArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getEnumPropertyChoices
+Java_edu_wpi_first_cscore_CameraServerJNI_getEnumPropertyChoices
   (JNIEnv* env, jclass, jint property)
 {
   CS_Status status = 0;
   auto arr = cs::GetEnumPropertyChoices(property, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJStringArray(env, arr);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    createUsbCameraDev
  * Signature: (Ljava/lang/String;I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_createUsbCameraDev
+Java_edu_wpi_first_cscore_CameraServerJNI_createUsbCameraDev
   (JNIEnv* env, jclass, jstring name, jint dev)
 {
   if (!name) {
@@ -458,12 +505,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    createUsbCameraPath
  * Signature: (Ljava/lang/String;Ljava/lang/String;)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_createUsbCameraPath
+Java_edu_wpi_first_cscore_CameraServerJNI_createUsbCameraPath
   (JNIEnv* env, jclass, jstring name, jstring path)
 {
   if (!name) {
@@ -482,12 +529,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    createHttpCamera
  * Signature: (Ljava/lang/String;Ljava/lang/String;I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_createHttpCamera
+Java_edu_wpi_first_cscore_CameraServerJNI_createHttpCamera
   (JNIEnv* env, jclass, jstring name, jstring url, jint kind)
 {
   if (!name) {
@@ -507,12 +554,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    createHttpCameraMulti
  * Signature: (Ljava/lang/String;[Ljava/lang/Object;I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_createHttpCameraMulti
+Java_edu_wpi_first_cscore_CameraServerJNI_createHttpCameraMulti
   (JNIEnv* env, jclass, jstring name, jobjectArray urls, jint kind)
 {
   if (!name) {
@@ -533,7 +580,7 @@
       // TODO
       return 0;
     }
-    vec.push_back(JStringRef{env, elem}.str());
+    vec.emplace_back(JStringRef{env, elem}.str());
   }
   CS_Status status = 0;
   auto val =
@@ -544,12 +591,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerCvJNI
+ * Class:     edu_wpi_first_cscore_CameraServerCvJNI
  * Method:    createCvSource
  * Signature: (Ljava/lang/String;IIII)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerCvJNI_createCvSource
+Java_edu_wpi_first_cscore_CameraServerCvJNI_createCvSource
   (JNIEnv* env, jclass, jstring name, jint pixelFormat, jint width, jint height,
    jint fps)
 {
@@ -569,12 +616,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    createRawSource
  * Signature: (Ljava/lang/String;IIII)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_createRawSource
+Java_edu_wpi_first_cscore_CameraServerJNI_createRawSource
   (JNIEnv* env, jclass, jstring name, jint pixelFormat, jint width, jint height,
    jint fps)
 {
@@ -594,12 +641,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSourceKind
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSourceKind
+Java_edu_wpi_first_cscore_CameraServerJNI_getSourceKind
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -609,44 +656,48 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSourceName
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSourceName
+Java_edu_wpi_first_cscore_CameraServerJNI_getSourceName
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   wpi::SmallString<128> buf;
   auto str = cs::GetSourceName(source, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJString(env, str);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSourceDescription
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSourceDescription
+Java_edu_wpi_first_cscore_CameraServerJNI_getSourceDescription
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   wpi::SmallString<128> buf;
   auto str = cs::GetSourceDescription(source, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJString(env, str);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSourceLastFrameTime
  * Signature: (I)J
  */
 JNIEXPORT jlong JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSourceLastFrameTime
+Java_edu_wpi_first_cscore_CameraServerJNI_getSourceLastFrameTime
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -656,12 +707,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSourceConnectionStrategy
  * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSourceConnectionStrategy
+Java_edu_wpi_first_cscore_CameraServerJNI_setSourceConnectionStrategy
   (JNIEnv* env, jclass, jint source, jint strategy)
 {
   CS_Status status = 0;
@@ -671,12 +722,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    isSourceConnected
  * Signature: (I)Z
  */
 JNIEXPORT jboolean JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_isSourceConnected
+Java_edu_wpi_first_cscore_CameraServerJNI_isSourceConnected
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -686,12 +737,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    isSourceEnabled
  * Signature: (I)Z
  */
 JNIEXPORT jboolean JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_isSourceEnabled
+Java_edu_wpi_first_cscore_CameraServerJNI_isSourceEnabled
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -701,12 +752,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSourceProperty
  * Signature: (ILjava/lang/String;)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSourceProperty
+Java_edu_wpi_first_cscore_CameraServerJNI_getSourceProperty
   (JNIEnv* env, jclass, jint source, jstring name)
 {
   if (!name) {
@@ -721,43 +772,47 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    enumerateSourceProperties
  * Signature: (I)[I
  */
 JNIEXPORT jintArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_enumerateSourceProperties
+Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSourceProperties
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   wpi::SmallVector<CS_Property, 32> buf;
   auto arr = cs::EnumerateSourceProperties(source, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJIntArray(env, arr);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSourceVideoMode
  * Signature: (I)Ljava/lang/Object;
  */
 JNIEXPORT jobject JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSourceVideoMode
+Java_edu_wpi_first_cscore_CameraServerJNI_getSourceVideoMode
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   auto val = cs::GetSourceVideoMode(source, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJObject(env, val);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSourceVideoMode
  * Signature: (IIIII)Z
  */
 JNIEXPORT jboolean JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSourceVideoMode
+Java_edu_wpi_first_cscore_CameraServerJNI_setSourceVideoMode
   (JNIEnv* env, jclass, jint source, jint pixelFormat, jint width, jint height,
    jint fps)
 {
@@ -772,12 +827,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSourcePixelFormat
  * Signature: (II)Z
  */
 JNIEXPORT jboolean JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSourcePixelFormat
+Java_edu_wpi_first_cscore_CameraServerJNI_setSourcePixelFormat
   (JNIEnv* env, jclass, jint source, jint pixelFormat)
 {
   CS_Status status = 0;
@@ -788,12 +843,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSourceResolution
  * Signature: (III)Z
  */
 JNIEXPORT jboolean JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSourceResolution
+Java_edu_wpi_first_cscore_CameraServerJNI_setSourceResolution
   (JNIEnv* env, jclass, jint source, jint width, jint height)
 {
   CS_Status status = 0;
@@ -803,12 +858,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSourceFPS
  * Signature: (II)Z
  */
 JNIEXPORT jboolean JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSourceFPS
+Java_edu_wpi_first_cscore_CameraServerJNI_setSourceFPS
   (JNIEnv* env, jclass, jint source, jint fps)
 {
   CS_Status status = 0;
@@ -818,12 +873,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSourceConfigJson
  * Signature: (ILjava/lang/String;)Z
  */
 JNIEXPORT jboolean JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSourceConfigJson
+Java_edu_wpi_first_cscore_CameraServerJNI_setSourceConfigJson
   (JNIEnv* env, jclass, jint source, jstring config)
 {
   CS_Status status = 0;
@@ -833,12 +888,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSourceConfigJson
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSourceConfigJson
+Java_edu_wpi_first_cscore_CameraServerJNI_getSourceConfigJson
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -848,19 +903,23 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    enumerateSourceVideoModes
  * Signature: (I)[Ljava/lang/Object;
  */
 JNIEXPORT jobjectArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_enumerateSourceVideoModes
+Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSourceVideoModes
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   auto arr = cs::EnumerateSourceVideoModes(source, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   jobjectArray jarr = env->NewObjectArray(arr.size(), videoModeCls, nullptr);
-  if (!jarr) return nullptr;
+  if (!jarr) {
+    return nullptr;
+  }
   for (size_t i = 0; i < arr.size(); ++i) {
     JLocal<jobject> jelem{env, MakeJObject(env, arr[i])};
     env->SetObjectArrayElement(jarr, i, jelem);
@@ -869,28 +928,30 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    enumerateSourceSinks
  * Signature: (I)[I
  */
 JNIEXPORT jintArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_enumerateSourceSinks
+Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSourceSinks
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   wpi::SmallVector<CS_Sink, 16> buf;
   auto arr = cs::EnumerateSourceSinks(source, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJIntArray(env, arr);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    copySource
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_copySource
+Java_edu_wpi_first_cscore_CameraServerJNI_copySource
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -900,12 +961,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    releaseSource
  * Signature: (I)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_releaseSource
+Java_edu_wpi_first_cscore_CameraServerJNI_releaseSource
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -914,12 +975,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setCameraBrightness
  * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setCameraBrightness
+Java_edu_wpi_first_cscore_CameraServerJNI_setCameraBrightness
   (JNIEnv* env, jclass, jint source, jint brightness)
 {
   CS_Status status = 0;
@@ -928,12 +989,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getCameraBrightness
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getCameraBrightness
+Java_edu_wpi_first_cscore_CameraServerJNI_getCameraBrightness
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -943,12 +1004,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setCameraWhiteBalanceAuto
  * Signature: (I)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setCameraWhiteBalanceAuto
+Java_edu_wpi_first_cscore_CameraServerJNI_setCameraWhiteBalanceAuto
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -957,12 +1018,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setCameraWhiteBalanceHoldCurrent
  * Signature: (I)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setCameraWhiteBalanceHoldCurrent
+Java_edu_wpi_first_cscore_CameraServerJNI_setCameraWhiteBalanceHoldCurrent
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -971,12 +1032,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setCameraWhiteBalanceManual
  * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setCameraWhiteBalanceManual
+Java_edu_wpi_first_cscore_CameraServerJNI_setCameraWhiteBalanceManual
   (JNIEnv* env, jclass, jint source, jint value)
 {
   CS_Status status = 0;
@@ -985,12 +1046,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setCameraExposureAuto
  * Signature: (I)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setCameraExposureAuto
+Java_edu_wpi_first_cscore_CameraServerJNI_setCameraExposureAuto
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -999,12 +1060,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setCameraExposureHoldCurrent
  * Signature: (I)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setCameraExposureHoldCurrent
+Java_edu_wpi_first_cscore_CameraServerJNI_setCameraExposureHoldCurrent
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -1013,12 +1074,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setCameraExposureManual
  * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setCameraExposureManual
+Java_edu_wpi_first_cscore_CameraServerJNI_setCameraExposureManual
   (JNIEnv* env, jclass, jint source, jint value)
 {
   CS_Status status = 0;
@@ -1027,12 +1088,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setUsbCameraPath
  * Signature: (ILjava/lang/String;)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setUsbCameraPath
+Java_edu_wpi_first_cscore_CameraServerJNI_setUsbCameraPath
   (JNIEnv* env, jclass, jint source, jstring path)
 {
   CS_Status status = 0;
@@ -1041,57 +1102,63 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getUsbCameraPath
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getUsbCameraPath
+Java_edu_wpi_first_cscore_CameraServerJNI_getUsbCameraPath
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   auto str = cs::GetUsbCameraPath(source, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJString(env, str);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getUsbCameraInfo
  * Signature: (I)Ljava/lang/Object;
  */
 JNIEXPORT jobject JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getUsbCameraInfo
+Java_edu_wpi_first_cscore_CameraServerJNI_getUsbCameraInfo
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   auto info = cs::GetUsbCameraInfo(source, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJObject(env, info);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getHttpCameraKind
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getHttpCameraKind
+Java_edu_wpi_first_cscore_CameraServerJNI_getHttpCameraKind
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   auto kind = cs::GetHttpCameraKind(source, &status);
-  if (!CheckStatus(env, status)) return 0;
+  if (!CheckStatus(env, status)) {
+    return 0;
+  }
   return kind;
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setHttpCameraUrls
  * Signature: (I[Ljava/lang/Object;)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setHttpCameraUrls
+Java_edu_wpi_first_cscore_CameraServerJNI_setHttpCameraUrls
   (JNIEnv* env, jclass, jint source, jobjectArray urls)
 {
   if (!urls) {
@@ -1108,7 +1175,7 @@
       // TODO
       return;
     }
-    vec.push_back(JStringRef{env, elem}.str());
+    vec.emplace_back(JStringRef{env, elem}.str());
   }
   CS_Status status = 0;
   cs::SetHttpCameraUrls(source, vec, &status);
@@ -1116,27 +1183,29 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getHttpCameraUrls
  * Signature: (I)[Ljava/lang/Object;
  */
 JNIEXPORT jobjectArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getHttpCameraUrls
+Java_edu_wpi_first_cscore_CameraServerJNI_getHttpCameraUrls
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   auto arr = cs::GetHttpCameraUrls(source, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJStringArray(env, arr);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerCvJNI
+ * Class:     edu_wpi_first_cscore_CameraServerCvJNI
  * Method:    putSourceFrame
  * Signature: (IJ)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerCvJNI_putSourceFrame
+Java_edu_wpi_first_cscore_CameraServerCvJNI_putSourceFrame
   (JNIEnv* env, jclass, jint source, jlong imageNativeObj)
 {
   try {
@@ -1147,19 +1216,19 @@
   } catch (const std::exception& e) {
     ThrowJavaException(env, &e);
   } catch (...) {
-    ThrowJavaException(env, 0);
+    ThrowJavaException(env, nullptr);
   }
 }
 
 // int width, int height, int pixelFormat, int totalData
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    putRawSourceFrameBB
  * Signature: (ILjava/lang/Object;IIII)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_putRawSourceFrameBB
+Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrameBB
   (JNIEnv* env, jclass, jint source, jobject byteBuffer, jint width,
    jint height, jint pixelFormat, jint totalData)
 {
@@ -1176,12 +1245,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    putRawSourceFrame
  * Signature: (IJIIII)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_putRawSourceFrame
+Java_edu_wpi_first_cscore_CameraServerJNI_putRawSourceFrame
   (JNIEnv* env, jclass, jint source, jlong ptr, jint width, jint height,
    jint pixelFormat, jint totalData)
 {
@@ -1197,12 +1266,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    notifySourceError
  * Signature: (ILjava/lang/String;)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_notifySourceError
+Java_edu_wpi_first_cscore_CameraServerJNI_notifySourceError
   (JNIEnv* env, jclass, jint source, jstring msg)
 {
   if (!msg) {
@@ -1215,12 +1284,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSourceConnected
  * Signature: (IZ)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSourceConnected
+Java_edu_wpi_first_cscore_CameraServerJNI_setSourceConnected
   (JNIEnv* env, jclass, jint source, jboolean connected)
 {
   CS_Status status = 0;
@@ -1229,12 +1298,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSourceDescription
  * Signature: (ILjava/lang/String;)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSourceDescription
+Java_edu_wpi_first_cscore_CameraServerJNI_setSourceDescription
   (JNIEnv* env, jclass, jint source, jstring description)
 {
   if (!description) {
@@ -1247,12 +1316,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    createSourceProperty
  * Signature: (ILjava/lang/String;IIIIII)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_createSourceProperty
+Java_edu_wpi_first_cscore_CameraServerJNI_createSourceProperty
   (JNIEnv* env, jclass, jint source, jstring name, jint kind, jint minimum,
    jint maximum, jint step, jint defaultValue, jint value)
 {
@@ -1265,12 +1334,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSourceEnumPropertyChoices
  * Signature: (II[Ljava/lang/Object;)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSourceEnumPropertyChoices
+Java_edu_wpi_first_cscore_CameraServerJNI_setSourceEnumPropertyChoices
   (JNIEnv* env, jclass, jint source, jint property, jobjectArray choices)
 {
   if (!choices) {
@@ -1287,7 +1356,7 @@
       // TODO
       return;
     }
-    vec.push_back(JStringRef{env, elem}.str());
+    vec.emplace_back(JStringRef{env, elem}.str());
   }
   CS_Status status = 0;
   cs::SetSourceEnumPropertyChoices(source, property, vec, &status);
@@ -1295,12 +1364,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    createMjpegServer
  * Signature: (Ljava/lang/String;Ljava/lang/String;I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_createMjpegServer
+Java_edu_wpi_first_cscore_CameraServerJNI_createMjpegServer
   (JNIEnv* env, jclass, jstring name, jstring listenAddress, jint port)
 {
   if (!name) {
@@ -1320,12 +1389,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerCvJNI
+ * Class:     edu_wpi_first_cscore_CameraServerCvJNI
  * Method:    createCvSink
  * Signature: (Ljava/lang/String;)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerCvJNI_createCvSink
+Java_edu_wpi_first_cscore_CameraServerCvJNI_createCvSink
   (JNIEnv* env, jclass, jstring name)
 {
   if (!name) {
@@ -1339,12 +1408,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    createRawSink
  * Signature: (Ljava/lang/String;)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_createRawSink
+Java_edu_wpi_first_cscore_CameraServerJNI_createRawSink
   (JNIEnv* env, jclass, jstring name)
 {
   if (!name) {
@@ -1358,12 +1427,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSinkKind
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSinkKind
+Java_edu_wpi_first_cscore_CameraServerJNI_getSinkKind
   (JNIEnv* env, jclass, jint sink)
 {
   CS_Status status = 0;
@@ -1373,44 +1442,48 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSinkName
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSinkName
+Java_edu_wpi_first_cscore_CameraServerJNI_getSinkName
   (JNIEnv* env, jclass, jint sink)
 {
   CS_Status status = 0;
   wpi::SmallString<128> buf;
   auto str = cs::GetSinkName(sink, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJString(env, str);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSinkDescription
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSinkDescription
+Java_edu_wpi_first_cscore_CameraServerJNI_getSinkDescription
   (JNIEnv* env, jclass, jint sink)
 {
   CS_Status status = 0;
   wpi::SmallString<128> buf;
   auto str = cs::GetSinkDescription(sink, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJString(env, str);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSinkProperty
  * Signature: (ILjava/lang/String;)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSinkProperty
+Java_edu_wpi_first_cscore_CameraServerJNI_getSinkProperty
   (JNIEnv* env, jclass, jint sink, jstring name)
 {
   if (!name) {
@@ -1424,28 +1497,30 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    enumerateSinkProperties
  * Signature: (I)[I
  */
 JNIEXPORT jintArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_enumerateSinkProperties
+Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSinkProperties
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
   wpi::SmallVector<CS_Property, 32> buf;
   auto arr = cs::EnumerateSinkProperties(source, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJIntArray(env, arr);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSinkConfigJson
  * Signature: (ILjava/lang/String;)Z
  */
 JNIEXPORT jboolean JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSinkConfigJson
+Java_edu_wpi_first_cscore_CameraServerJNI_setSinkConfigJson
   (JNIEnv* env, jclass, jint source, jstring config)
 {
   CS_Status status = 0;
@@ -1455,12 +1530,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSinkConfigJson
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSinkConfigJson
+Java_edu_wpi_first_cscore_CameraServerJNI_getSinkConfigJson
   (JNIEnv* env, jclass, jint source)
 {
   CS_Status status = 0;
@@ -1470,12 +1545,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSinkSource
  * Signature: (II)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSinkSource
+Java_edu_wpi_first_cscore_CameraServerJNI_setSinkSource
   (JNIEnv* env, jclass, jint sink, jint source)
 {
   CS_Status status = 0;
@@ -1484,12 +1559,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSinkSourceProperty
  * Signature: (ILjava/lang/String;)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSinkSourceProperty
+Java_edu_wpi_first_cscore_CameraServerJNI_getSinkSourceProperty
   (JNIEnv* env, jclass, jint sink, jstring name)
 {
   if (!name) {
@@ -1504,12 +1579,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSinkSource
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSinkSource
+Java_edu_wpi_first_cscore_CameraServerJNI_getSinkSource
   (JNIEnv* env, jclass, jint sink)
 {
   CS_Status status = 0;
@@ -1519,12 +1594,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    copySink
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_copySink
+Java_edu_wpi_first_cscore_CameraServerJNI_copySink
   (JNIEnv* env, jclass, jint sink)
 {
   CS_Status status = 0;
@@ -1534,12 +1609,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    releaseSink
  * Signature: (I)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_releaseSink
+Java_edu_wpi_first_cscore_CameraServerJNI_releaseSink
   (JNIEnv* env, jclass, jint sink)
 {
   CS_Status status = 0;
@@ -1548,27 +1623,29 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getMjpegServerListenAddress
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getMjpegServerListenAddress
+Java_edu_wpi_first_cscore_CameraServerJNI_getMjpegServerListenAddress
   (JNIEnv* env, jclass, jint sink)
 {
   CS_Status status = 0;
   auto str = cs::GetMjpegServerListenAddress(sink, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJString(env, str);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getMjpegServerPort
  * Signature: (I)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getMjpegServerPort
+Java_edu_wpi_first_cscore_CameraServerJNI_getMjpegServerPort
   (JNIEnv* env, jclass, jint sink)
 {
   CS_Status status = 0;
@@ -1578,12 +1655,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSinkDescription
  * Signature: (ILjava/lang/String;)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSinkDescription
+Java_edu_wpi_first_cscore_CameraServerJNI_setSinkDescription
   (JNIEnv* env, jclass, jint sink, jstring description)
 {
   if (!description) {
@@ -1596,12 +1673,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerCvJNI
+ * Class:     edu_wpi_first_cscore_CameraServerCvJNI
  * Method:    grabSinkFrame
  * Signature: (IJ)J
  */
 JNIEXPORT jlong JNICALL
-Java_edu_wpi_cscore_CameraServerCvJNI_grabSinkFrame
+Java_edu_wpi_first_cscore_CameraServerCvJNI_grabSinkFrame
   (JNIEnv* env, jclass, jint sink, jlong imageNativeObj)
 {
   try {
@@ -1614,18 +1691,18 @@
     ThrowJavaException(env, &e);
     return 0;
   } catch (...) {
-    ThrowJavaException(env, 0);
+    ThrowJavaException(env, nullptr);
     return 0;
   }
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerCvJNI
+ * Class:     edu_wpi_first_cscore_CameraServerCvJNI
  * Method:    grabSinkFrameTimeout
  * Signature: (IJD)J
  */
 JNIEXPORT jlong JNICALL
-Java_edu_wpi_cscore_CameraServerCvJNI_grabSinkFrameTimeout
+Java_edu_wpi_first_cscore_CameraServerCvJNI_grabSinkFrameTimeout
   (JNIEnv* env, jclass, jint sink, jlong imageNativeObj, jdouble timeout)
 {
   try {
@@ -1638,7 +1715,7 @@
     ThrowJavaException(env, &e);
     return 0;
   } catch (...) {
-    ThrowJavaException(env, 0);
+    ThrowJavaException(env, nullptr);
     return 0;
   }
 }
@@ -1661,12 +1738,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    grabRawSinkFrameImpl
  * Signature: (ILjava/lang/Object;JLjava/lang/Object;III)J
  */
 JNIEXPORT jlong JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_grabRawSinkFrameImpl
+Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameImpl
   (JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr,
    jobject byteBuffer, jint width, jint height, jint pixelFormat)
 {
@@ -1686,12 +1763,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    grabRawSinkFrameTimeoutImpl
  * Signature: (ILjava/lang/Object;JLjava/lang/Object;IIID)J
  */
 JNIEXPORT jlong JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_grabRawSinkFrameTimeoutImpl
+Java_edu_wpi_first_cscore_CameraServerJNI_grabRawSinkFrameTimeoutImpl
   (JNIEnv* env, jclass, jint sink, jobject rawFrameObj, jlong rawFramePtr,
    jobject byteBuffer, jint width, jint height, jint pixelFormat,
    jdouble timeout)
@@ -1713,28 +1790,30 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getSinkError
  * Signature: (I)Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getSinkError
+Java_edu_wpi_first_cscore_CameraServerJNI_getSinkError
   (JNIEnv* env, jclass, jint sink)
 {
   CS_Status status = 0;
   wpi::SmallString<128> buf;
   auto str = cs::GetSinkError(sink, buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJString(env, str);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setSinkEnabled
  * Signature: (IZ)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setSinkEnabled
+Java_edu_wpi_first_cscore_CameraServerJNI_setSinkEnabled
   (JNIEnv* env, jclass, jint sink, jboolean enabled)
 {
   CS_Status status = 0;
@@ -1743,12 +1822,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    addListener
  * Signature: (Ljava/lang/Object;IZ)I
  */
 JNIEXPORT jint JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_addListener
+Java_edu_wpi_first_cscore_CameraServerJNI_addListener
   (JNIEnv* envouter, jclass, jobject listener, jint eventMask,
    jboolean immediateNotify)
 {
@@ -1763,17 +1842,23 @@
 
   // cls is a temporary here; cannot be used within callback functor
   jclass cls = envouter->GetObjectClass(listener);
-  if (!cls) return 0;
+  if (!cls) {
+    return 0;
+  }
 
   // method ids, on the other hand, are safe to retain
   jmethodID mid = envouter->GetMethodID(cls, "accept", "(Ljava/lang/Object;)V");
-  if (!mid) return 0;
+  if (!mid) {
+    return 0;
+  }
 
   CS_Status status = 0;
   CS_Listener handle = cs::AddListener(
       [=](const cs::RawEvent& event) {
         JNIEnv* env = listenerEnv;
-        if (!env || !env->functions) return;
+        if (!env || !env->functions) {
+          return;
+        }
 
         // get the handler
         auto handler = listener_global->obj();
@@ -1785,7 +1870,9 @@
           env->ExceptionClear();
           return;
         }
-        if (!jobj) return;
+        if (!jobj) {
+          return;
+        }
 
         env->CallVoidMethod(handler, mid, jobj.obj());
         if (env->ExceptionCheck()) {
@@ -1799,12 +1886,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    removeListener
  * Signature: (I)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_removeListener
+Java_edu_wpi_first_cscore_CameraServerJNI_removeListener
   (JNIEnv* env, jclass, jint handle)
 {
   CS_Status status = 0;
@@ -1813,36 +1900,122 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
+ * Method:    createListenerPoller
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_cscore_CameraServerJNI_createListenerPoller
+  (JNIEnv*, jclass)
+{
+  return cs::CreateListenerPoller();
+}
+
+/*
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
+ * Method:    destroyListenerPoller
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_cscore_CameraServerJNI_destroyListenerPoller
+  (JNIEnv*, jclass, jint poller)
+{
+  cs::DestroyListenerPoller(poller);
+}
+
+/*
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
+ * Method:    addPolledListener
+ * Signature: (IIZ)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_cscore_CameraServerJNI_addPolledListener
+  (JNIEnv* env, jclass, jint poller, jint eventMask, jboolean immediateNotify)
+{
+  CS_Status status = 0;
+  auto rv = cs::AddPolledListener(poller, eventMask, immediateNotify, &status);
+  CheckStatus(env, status);
+  return rv;
+}
+
+/*
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
+ * Method:    pollListener
+ * Signature: (I)[Ljava/lang/Object;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_edu_wpi_first_cscore_CameraServerJNI_pollListener
+  (JNIEnv* env, jclass, jint poller)
+{
+  auto events = cs::PollListener(poller);
+  if (events.empty()) {
+    interruptedEx.Throw(env, "PollListener interrupted");
+    return nullptr;
+  }
+  return MakeJObject(env, events);
+}
+
+/*
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
+ * Method:    pollListenerTimeout
+ * Signature: (ID)[Ljava/lang/Object;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_edu_wpi_first_cscore_CameraServerJNI_pollListenerTimeout
+  (JNIEnv* env, jclass, jint poller, jdouble timeout)
+{
+  bool timed_out = false;
+  auto events = cs::PollListener(poller, timeout, &timed_out);
+  if (events.empty() && !timed_out) {
+    interruptedEx.Throw(env, "PollListener interrupted");
+    return nullptr;
+  }
+  return MakeJObject(env, events);
+}
+
+/*
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
+ * Method:    cancelPollListener
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_cscore_CameraServerJNI_cancelPollListener
+  (JNIEnv*, jclass, jint poller)
+{
+  cs::CancelPollListener(poller);
+}
+
+/*
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setTelemetryPeriod
  * Signature: (D)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setTelemetryPeriod
+Java_edu_wpi_first_cscore_CameraServerJNI_setTelemetryPeriod
   (JNIEnv* env, jclass, jdouble seconds)
 {
   cs::SetTelemetryPeriod(seconds);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getTelemetryElapsedTime
  * Signature: ()D
  */
 JNIEXPORT jdouble JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getTelemetryElapsedTime
+Java_edu_wpi_first_cscore_CameraServerJNI_getTelemetryElapsedTime
   (JNIEnv* env, jclass)
 {
   return cs::GetTelemetryElapsedTime();
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getTelemetryValue
  * Signature: (II)J
  */
 JNIEXPORT jlong JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getTelemetryValue
+Java_edu_wpi_first_cscore_CameraServerJNI_getTelemetryValue
   (JNIEnv* env, jclass, jint handle, jint kind)
 {
   CS_Status status = 0;
@@ -1853,12 +2026,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getTelemetryAverageValue
  * Signature: (II)D
  */
 JNIEXPORT jdouble JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getTelemetryAverageValue
+Java_edu_wpi_first_cscore_CameraServerJNI_getTelemetryAverageValue
   (JNIEnv* env, jclass, jint handle, jint kind)
 {
   CS_Status status = 0;
@@ -1869,20 +2042,24 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    enumerateUsbCameras
  * Signature: ()[Ljava/lang/Object;
  */
 JNIEXPORT jobjectArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_enumerateUsbCameras
+Java_edu_wpi_first_cscore_CameraServerJNI_enumerateUsbCameras
   (JNIEnv* env, jclass)
 {
   CS_Status status = 0;
   auto arr = cs::EnumerateUsbCameras(&status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   jobjectArray jarr =
       env->NewObjectArray(arr.size(), usbCameraInfoCls, nullptr);
-  if (!jarr) return nullptr;
+  if (!jarr) {
+    return nullptr;
+  }
   for (size_t i = 0; i < arr.size(); ++i) {
     JLocal<jobject> jelem{env, MakeJObject(env, arr[i])};
     env->SetObjectArrayElement(jarr, i, jelem);
@@ -1891,56 +2068,60 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    enumerateSources
  * Signature: ()[I
  */
 JNIEXPORT jintArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_enumerateSources
+Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSources
   (JNIEnv* env, jclass)
 {
   CS_Status status = 0;
   wpi::SmallVector<CS_Source, 16> buf;
   auto arr = cs::EnumerateSourceHandles(buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJIntArray(env, arr);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    enumerateSinks
  * Signature: ()[I
  */
 JNIEXPORT jintArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_enumerateSinks
+Java_edu_wpi_first_cscore_CameraServerJNI_enumerateSinks
   (JNIEnv* env, jclass)
 {
   CS_Status status = 0;
   wpi::SmallVector<CS_Sink, 16> buf;
   auto arr = cs::EnumerateSinkHandles(buf, &status);
-  if (!CheckStatus(env, status)) return nullptr;
+  if (!CheckStatus(env, status)) {
+    return nullptr;
+  }
   return MakeJIntArray(env, arr);
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getHostname
  * Signature: ()Ljava/lang/String;
  */
 JNIEXPORT jstring JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getHostname
+Java_edu_wpi_first_cscore_CameraServerJNI_getHostname
   (JNIEnv* env, jclass)
 {
   return MakeJString(env, cs::GetHostname());
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    getNetworkInterfaces
  * Signature: ()[Ljava/lang/Object;
  */
 JNIEXPORT jobjectArray JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_getNetworkInterfaces
+Java_edu_wpi_first_cscore_CameraServerJNI_getNetworkInterfaces
   (JNIEnv* env, jclass)
 {
   return MakeJStringArray(env, cs::GetNetworkInterfaces());
@@ -1959,8 +2140,8 @@
   void CallJava(JNIEnv* env, jobject func, jmethodID mid) {
     JLocal<jstring> file{env, MakeJString(env, m_file)};
     JLocal<jstring> msg{env, MakeJString(env, m_msg)};
-    env->CallVoidMethod(func, mid, (jint)m_level, file.obj(), (jint)m_line,
-                        msg.obj());
+    env->CallVoidMethod(func, mid, static_cast<jint>(m_level), file.obj(),
+                        static_cast<jint>(m_line), msg.obj());
   }
 
   static const char* GetName() { return "CSLogger"; }
@@ -1973,19 +2154,19 @@
   std::string m_msg;
 };
 
-typedef JSingletonCallbackManager<LogMessage> LoggerJNI;
+using LoggerJNI = JSingletonCallbackManager<LogMessage>;
 
 }  // namespace
 
 extern "C" {
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    setLogger
  * Signature: (Ljava/lang/Object;I)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_setLogger
+Java_edu_wpi_first_cscore_CameraServerJNI_setLogger
   (JNIEnv* env, jclass, jobject func, jint minLevel)
 {
   if (!func) {
@@ -1994,12 +2175,16 @@
   }
   // cls is a temporary here; cannot be used within callback functor
   jclass cls = env->GetObjectClass(func);
-  if (!cls) return;
+  if (!cls) {
+    return;
+  }
 
   // method ids, on the other hand, are safe to retain
   jmethodID mid = env->GetMethodID(cls, "apply",
                                    "(ILjava/lang/String;ILjava/lang/String;)V");
-  if (!mid) return;
+  if (!mid) {
+    return;
+  }
 
   auto& logger = LoggerJNI::GetInstance();
   logger.Start();
@@ -2014,12 +2199,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    allocateRawFrame
  * Signature: ()J
  */
 JNIEXPORT jlong JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_allocateRawFrame
+Java_edu_wpi_first_cscore_CameraServerJNI_allocateRawFrame
   (JNIEnv*, jclass)
 {
   cs::RawFrame* rawFrame = new cs::RawFrame{};
@@ -2028,12 +2213,12 @@
 }
 
 /*
- * Class:     edu_wpi_cscore_CameraServerJNI
+ * Class:     edu_wpi_first_cscore_CameraServerJNI
  * Method:    freeRawFrame
  * Signature: (J)V
  */
 JNIEXPORT void JNICALL
-Java_edu_wpi_cscore_CameraServerJNI_freeRawFrame
+Java_edu_wpi_first_cscore_CameraServerJNI_freeRawFrame
   (JNIEnv*, jclass, jlong rawFrame)
 {
   cs::RawFrame* ptr =
diff --git a/cscore/src/main/native/include/cscore.h b/cscore/src/main/native/include/cscore.h
index eff3856..085d6c7 100644
--- a/cscore/src/main/native/include/cscore.h
+++ b/cscore/src/main/native/include/cscore.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CSCORE_H_
 #define CSCORE_CSCORE_H_
diff --git a/cscore/src/main/native/include/cscore_c.h b/cscore/src/main/native/include/cscore_c.h
index ee5d33a..76bd836 100644
--- a/cscore/src/main/native/include/cscore_c.h
+++ b/cscore/src/main/native/include/cscore_c.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CSCORE_C_H_
 #define CSCORE_CSCORE_C_H_
@@ -49,6 +46,7 @@
 typedef int CS_Handle;
 typedef CS_Handle CS_Property;
 typedef CS_Handle CS_Listener;
+typedef CS_Handle CS_ListenerPoller;
 typedef CS_Handle CS_Sink;
 typedef CS_Handle CS_Source;
 /** @} */
@@ -172,7 +170,8 @@
   CS_TELEMETRY_UPDATED = 0x8000,
   CS_SINK_PROPERTY_CREATED = 0x10000,
   CS_SINK_PROPERTY_VALUE_UPDATED = 0x20000,
-  CS_SINK_PROPERTY_CHOICES_UPDATED = 0x40000
+  CS_SINK_PROPERTY_CHOICES_UPDATED = 0x40000,
+  CS_USB_CAMERAS_CHANGED = 0x80000
 };
 
 /**
@@ -225,6 +224,9 @@
   enum CS_PropertyKind propertyKind;
   int value;
   const char* valueStr;
+
+  // Listener that was triggered
+  CS_Listener listener;
 };
 
 /**
@@ -432,9 +434,19 @@
 void CS_SetListenerOnExit(void (*onExit)(void* data), void* data);
 CS_Listener CS_AddListener(
     void* data, void (*callback)(void* data, const struct CS_Event* event),
-    int eventMask, int immediateNotify, CS_Status* status);
+    int eventMask, CS_Bool immediateNotify, CS_Status* status);
 
 void CS_RemoveListener(CS_Listener handle, CS_Status* status);
+
+CS_ListenerPoller CS_CreateListenerPoller(void);
+void CS_DestroyListenerPoller(CS_ListenerPoller poller);
+CS_Listener CS_AddPolledListener(CS_ListenerPoller poller, int eventMask,
+                                 CS_Bool immediateNotify, CS_Status* status);
+struct CS_Event* CS_PollListener(CS_ListenerPoller poller, int* count);
+struct CS_Event* CS_PollListenerTimeout(CS_ListenerPoller poller, int* count,
+                                        double timeout, CS_Bool* timedOut);
+void CS_FreeEvents(struct CS_Event* arr, int count);
+void CS_CancelPollListener(CS_ListenerPoller poller);
 /** @} */
 
 int CS_NotifierDestroyed(void);
diff --git a/cscore/src/main/native/include/cscore_cpp.h b/cscore/src/main/native/include/cscore_cpp.h
index 1ee1615..8b1eab1 100644
--- a/cscore/src/main/native/include/cscore_cpp.h
+++ b/cscore/src/main/native/include/cscore_cpp.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CSCORE_CPP_H_
 #define CSCORE_CSCORE_CPP_H_
@@ -12,12 +9,11 @@
 
 #include <functional>
 #include <string>
+#include <string_view>
 #include <vector>
 
-#include <wpi/ArrayRef.h>
 #include <wpi/SmallVector.h>
-#include <wpi/StringRef.h>
-#include <wpi/Twine.h>
+#include <wpi/span.h>
 
 #include "cscore_c.h"
 
@@ -119,34 +115,36 @@
     kTelemetryUpdated = CS_TELEMETRY_UPDATED,
     kSinkPropertyCreated = CS_SINK_PROPERTY_CREATED,
     kSinkPropertyValueUpdated = CS_SINK_PROPERTY_VALUE_UPDATED,
-    kSinkPropertyChoicesUpdated = CS_SINK_PROPERTY_CHOICES_UPDATED
+    kSinkPropertyChoicesUpdated = CS_SINK_PROPERTY_CHOICES_UPDATED,
+    kUsbCamerasChanged = CS_USB_CAMERAS_CHANGED
   };
 
   RawEvent() = default;
   explicit RawEvent(RawEvent::Kind kind_) : kind{kind_} {}
-  RawEvent(const wpi::Twine& name_, CS_Handle handle_, RawEvent::Kind kind_)
-      : kind{kind_}, name{name_.str()} {
+  RawEvent(std::string_view name_, CS_Handle handle_, RawEvent::Kind kind_)
+      : kind{kind_}, name{name_} {
     if (kind_ == kSinkCreated || kind_ == kSinkDestroyed ||
-        kind_ == kSinkEnabled || kind_ == kSinkDisabled)
+        kind_ == kSinkEnabled || kind_ == kSinkDisabled) {
       sinkHandle = handle_;
-    else
+    } else {
       sourceHandle = handle_;
+    }
   }
-  RawEvent(const wpi::Twine& name_, CS_Source source_, const VideoMode& mode_)
+  RawEvent(std::string_view name_, CS_Source source_, const VideoMode& mode_)
       : kind{kSourceVideoModeChanged},
         sourceHandle{source_},
-        name{name_.str()},
+        name{name_},
         mode{mode_} {}
-  RawEvent(const wpi::Twine& name_, CS_Source source_, RawEvent::Kind kind_,
+  RawEvent(std::string_view name_, CS_Source source_, RawEvent::Kind kind_,
            CS_Property property_, CS_PropertyKind propertyKind_, int value_,
-           const wpi::Twine& valueStr_)
+           std::string_view valueStr_)
       : kind{kind_},
         sourceHandle{source_},
-        name{name_.str()},
+        name{name_},
         propertyHandle{property_},
         propertyKind{propertyKind_},
         value{value_},
-        valueStr{valueStr_.str()} {}
+        valueStr{valueStr_} {}
 
   Kind kind;
 
@@ -165,6 +163,9 @@
   CS_PropertyKind propertyKind;
   int value;
   std::string valueStr;
+
+  // Listener that was triggered
+  CS_Listener listener{0};
 };
 
 /**
@@ -173,9 +174,9 @@
  */
 CS_PropertyKind GetPropertyKind(CS_Property property, CS_Status* status);
 std::string GetPropertyName(CS_Property property, CS_Status* status);
-wpi::StringRef GetPropertyName(CS_Property property,
-                               wpi::SmallVectorImpl<char>& buf,
-                               CS_Status* status);
+std::string_view GetPropertyName(CS_Property property,
+                                 wpi::SmallVectorImpl<char>& buf,
+                                 CS_Status* status);
 int GetProperty(CS_Property property, CS_Status* status);
 void SetProperty(CS_Property property, int value, CS_Status* status);
 int GetPropertyMin(CS_Property property, CS_Status* status);
@@ -183,10 +184,10 @@
 int GetPropertyStep(CS_Property property, CS_Status* status);
 int GetPropertyDefault(CS_Property property, CS_Status* status);
 std::string GetStringProperty(CS_Property property, CS_Status* status);
-wpi::StringRef GetStringProperty(CS_Property property,
-                                 wpi::SmallVectorImpl<char>& buf,
-                                 CS_Status* status);
-void SetStringProperty(CS_Property property, const wpi::Twine& value,
+std::string_view GetStringProperty(CS_Property property,
+                                   wpi::SmallVectorImpl<char>& buf,
+                                   CS_Status* status);
+void SetStringProperty(CS_Property property, std::string_view value,
                        CS_Status* status);
 std::vector<std::string> GetEnumPropertyChoices(CS_Property property,
                                                 CS_Status* status);
@@ -196,16 +197,15 @@
  * @defgroup cscore_source_create_func Source Creation Functions
  * @{
  */
-CS_Source CreateUsbCameraDev(const wpi::Twine& name, int dev,
-                             CS_Status* status);
-CS_Source CreateUsbCameraPath(const wpi::Twine& name, const wpi::Twine& path,
+CS_Source CreateUsbCameraDev(std::string_view name, int dev, CS_Status* status);
+CS_Source CreateUsbCameraPath(std::string_view name, std::string_view path,
                               CS_Status* status);
-CS_Source CreateHttpCamera(const wpi::Twine& name, const wpi::Twine& url,
+CS_Source CreateHttpCamera(std::string_view name, std::string_view url,
                            CS_HttpCameraKind kind, CS_Status* status);
-CS_Source CreateHttpCamera(const wpi::Twine& name,
-                           wpi::ArrayRef<std::string> urls,
+CS_Source CreateHttpCamera(std::string_view name,
+                           wpi::span<const std::string> urls,
                            CS_HttpCameraKind kind, CS_Status* status);
-CS_Source CreateCvSource(const wpi::Twine& name, const VideoMode& mode,
+CS_Source CreateCvSource(std::string_view name, const VideoMode& mode,
                          CS_Status* status);
 /** @} */
 
@@ -215,21 +215,22 @@
  */
 CS_SourceKind GetSourceKind(CS_Source source, CS_Status* status);
 std::string GetSourceName(CS_Source source, CS_Status* status);
-wpi::StringRef GetSourceName(CS_Source source, wpi::SmallVectorImpl<char>& buf,
-                             CS_Status* status);
+std::string_view GetSourceName(CS_Source source,
+                               wpi::SmallVectorImpl<char>& buf,
+                               CS_Status* status);
 std::string GetSourceDescription(CS_Source source, CS_Status* status);
-wpi::StringRef GetSourceDescription(CS_Source source,
-                                    wpi::SmallVectorImpl<char>& buf,
-                                    CS_Status* status);
+std::string_view GetSourceDescription(CS_Source source,
+                                      wpi::SmallVectorImpl<char>& buf,
+                                      CS_Status* status);
 uint64_t GetSourceLastFrameTime(CS_Source source, CS_Status* status);
 void SetSourceConnectionStrategy(CS_Source source,
                                  CS_ConnectionStrategy strategy,
                                  CS_Status* status);
 bool IsSourceConnected(CS_Source source, CS_Status* status);
 bool IsSourceEnabled(CS_Source source, CS_Status* status);
-CS_Property GetSourceProperty(CS_Source source, const wpi::Twine& name,
+CS_Property GetSourceProperty(CS_Source source, std::string_view name,
                               CS_Status* status);
-wpi::ArrayRef<CS_Property> EnumerateSourceProperties(
+wpi::span<CS_Property> EnumerateSourceProperties(
     CS_Source source, wpi::SmallVectorImpl<CS_Property>& vec,
     CS_Status* status);
 VideoMode GetSourceVideoMode(CS_Source source, CS_Status* status);
@@ -240,7 +241,7 @@
 bool SetSourceResolution(CS_Source source, int width, int height,
                          CS_Status* status);
 bool SetSourceFPS(CS_Source source, int fps, CS_Status* status);
-bool SetSourceConfigJson(CS_Source source, wpi::StringRef config,
+bool SetSourceConfigJson(CS_Source source, std::string_view config,
                          CS_Status* status);
 bool SetSourceConfigJson(CS_Source source, const wpi::json& config,
                          CS_Status* status);
@@ -248,9 +249,9 @@
 wpi::json GetSourceConfigJsonObject(CS_Source source, CS_Status* status);
 std::vector<VideoMode> EnumerateSourceVideoModes(CS_Source source,
                                                  CS_Status* status);
-wpi::ArrayRef<CS_Sink> EnumerateSourceSinks(CS_Source source,
-                                            wpi::SmallVectorImpl<CS_Sink>& vec,
-                                            CS_Status* status);
+wpi::span<CS_Sink> EnumerateSourceSinks(CS_Source source,
+                                        wpi::SmallVectorImpl<CS_Sink>& vec,
+                                        CS_Status* status);
 CS_Source CopySource(CS_Source source, CS_Status* status);
 void ReleaseSource(CS_Source source, CS_Status* status);
 /** @} */
@@ -274,7 +275,7 @@
  * @defgroup cscore_usbcamera_func UsbCamera Source Functions
  * @{
  */
-void SetUsbCameraPath(CS_Source, const wpi::Twine& path, CS_Status* status);
+void SetUsbCameraPath(CS_Source, std::string_view path, CS_Status* status);
 std::string GetUsbCameraPath(CS_Source source, CS_Status* status);
 UsbCameraInfo GetUsbCameraInfo(CS_Source source, CS_Status* status);
 /** @} */
@@ -284,7 +285,7 @@
  * @{
  */
 CS_HttpCameraKind GetHttpCameraKind(CS_Source source, CS_Status* status);
-void SetHttpCameraUrls(CS_Source source, wpi::ArrayRef<std::string> urls,
+void SetHttpCameraUrls(CS_Source source, wpi::span<const std::string> urls,
                        CS_Status* status);
 std::vector<std::string> GetHttpCameraUrls(CS_Source source, CS_Status* status);
 /** @} */
@@ -293,17 +294,17 @@
  * @defgroup cscore_opencv_source_func OpenCV Source Functions
  * @{
  */
-void NotifySourceError(CS_Source source, const wpi::Twine& msg,
+void NotifySourceError(CS_Source source, std::string_view msg,
                        CS_Status* status);
 void SetSourceConnected(CS_Source source, bool connected, CS_Status* status);
-void SetSourceDescription(CS_Source source, const wpi::Twine& description,
+void SetSourceDescription(CS_Source source, std::string_view description,
                           CS_Status* status);
-CS_Property CreateSourceProperty(CS_Source source, const wpi::Twine& name,
+CS_Property CreateSourceProperty(CS_Source source, std::string_view name,
                                  CS_PropertyKind kind, int minimum, int maximum,
                                  int step, int defaultValue, int value,
                                  CS_Status* status);
 void SetSourceEnumPropertyChoices(CS_Source source, CS_Property property,
-                                  wpi::ArrayRef<std::string> choices,
+                                  wpi::span<const std::string> choices,
                                   CS_Status* status);
 /** @} */
 
@@ -311,11 +312,10 @@
  * @defgroup cscore_sink_create_func Sink Creation Functions
  * @{
  */
-CS_Sink CreateMjpegServer(const wpi::Twine& name,
-                          const wpi::Twine& listenAddress, int port,
-                          CS_Status* status);
-CS_Sink CreateCvSink(const wpi::Twine& name, CS_Status* status);
-CS_Sink CreateCvSinkCallback(const wpi::Twine& name,
+CS_Sink CreateMjpegServer(std::string_view name, std::string_view listenAddress,
+                          int port, CS_Status* status);
+CS_Sink CreateCvSink(std::string_view name, CS_Status* status);
+CS_Sink CreateCvSinkCallback(std::string_view name,
                              std::function<void(uint64_t time)> processFrame,
                              CS_Status* status);
 
@@ -327,19 +327,21 @@
  */
 CS_SinkKind GetSinkKind(CS_Sink sink, CS_Status* status);
 std::string GetSinkName(CS_Sink sink, CS_Status* status);
-wpi::StringRef GetSinkName(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
-                           CS_Status* status);
+std::string_view GetSinkName(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
+                             CS_Status* status);
 std::string GetSinkDescription(CS_Sink sink, CS_Status* status);
-wpi::StringRef GetSinkDescription(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
-                                  CS_Status* status);
-CS_Property GetSinkProperty(CS_Sink sink, const wpi::Twine& name,
+std::string_view GetSinkDescription(CS_Sink sink,
+                                    wpi::SmallVectorImpl<char>& buf,
+                                    CS_Status* status);
+CS_Property GetSinkProperty(CS_Sink sink, std::string_view name,
                             CS_Status* status);
-wpi::ArrayRef<CS_Property> EnumerateSinkProperties(
+wpi::span<CS_Property> EnumerateSinkProperties(
     CS_Sink sink, wpi::SmallVectorImpl<CS_Property>& vec, CS_Status* status);
 void SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status);
-CS_Property GetSinkSourceProperty(CS_Sink sink, const wpi::Twine& name,
+CS_Property GetSinkSourceProperty(CS_Sink sink, std::string_view name,
                                   CS_Status* status);
-bool SetSinkConfigJson(CS_Sink sink, wpi::StringRef config, CS_Status* status);
+bool SetSinkConfigJson(CS_Sink sink, std::string_view config,
+                       CS_Status* status);
 bool SetSinkConfigJson(CS_Sink sink, const wpi::json& config,
                        CS_Status* status);
 std::string GetSinkConfigJson(CS_Sink sink, CS_Status* status);
@@ -361,11 +363,11 @@
  * @defgroup cscore_opencv_sink_func OpenCV Sink Functions
  * @{
  */
-void SetSinkDescription(CS_Sink sink, const wpi::Twine& description,
+void SetSinkDescription(CS_Sink sink, std::string_view description,
                         CS_Status* status);
 std::string GetSinkError(CS_Sink sink, CS_Status* status);
-wpi::StringRef GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
-                            CS_Status* status);
+std::string_view GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
+                              CS_Status* status);
 void SetSinkEnabled(CS_Sink sink, bool enabled, CS_Status* status);
 /** @} */
 
@@ -380,6 +382,15 @@
                         int eventMask, bool immediateNotify, CS_Status* status);
 
 void RemoveListener(CS_Listener handle, CS_Status* status);
+
+CS_ListenerPoller CreateListenerPoller();
+void DestroyListenerPoller(CS_ListenerPoller poller);
+CS_Listener AddPolledListener(CS_ListenerPoller poller, int eventMask,
+                              bool immediateNotify, CS_Status* status);
+std::vector<RawEvent> PollListener(CS_ListenerPoller poller);
+std::vector<RawEvent> PollListener(CS_ListenerPoller poller, double timeout,
+                                   bool* timedOut);
+void CancelPollListener(CS_ListenerPoller poller);
 /** @} */
 
 bool NotifierDestroyed();
@@ -419,10 +430,10 @@
  */
 std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status);
 
-wpi::ArrayRef<CS_Source> EnumerateSourceHandles(
+wpi::span<CS_Source> EnumerateSourceHandles(
     wpi::SmallVectorImpl<CS_Source>& vec, CS_Status* status);
-wpi::ArrayRef<CS_Sink> EnumerateSinkHandles(wpi::SmallVectorImpl<CS_Sink>& vec,
-                                            CS_Status* status);
+wpi::span<CS_Sink> EnumerateSinkHandles(wpi::SmallVectorImpl<CS_Sink>& vec,
+                                        CS_Status* status);
 
 std::string GetHostname();
 
diff --git a/cscore/src/main/native/include/cscore_cv.h b/cscore/src/main/native/include/cscore_cv.h
index 650399b..30a356b 100644
--- a/cscore/src/main/native/include/cscore_cv.h
+++ b/cscore/src/main/native/include/cscore_cv.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CSCORE_CV_H_
 #define CSCORE_CSCORE_CV_H_
@@ -85,7 +82,7 @@
    * @param name Source name (arbitrary unique identifier)
    * @param mode Video mode being generated
    */
-  CvSource(const wpi::Twine& name, const VideoMode& mode);
+  CvSource(std::string_view name, const VideoMode& mode);
 
   /**
    * Create an OpenCV source.
@@ -96,8 +93,8 @@
    * @param height height
    * @param fps fps
    */
-  CvSource(const wpi::Twine& name, VideoMode::PixelFormat pixelFormat,
-           int width, int height, int fps);
+  CvSource(std::string_view name, VideoMode::PixelFormat pixelFormat, int width,
+           int height, int fps);
 
   /**
    * Put an OpenCV image and notify sinks.
@@ -129,7 +126,7 @@
    *
    * @param name Source name (arbitrary unique identifier)
    */
-  explicit CvSink(const wpi::Twine& name);
+  explicit CvSink(std::string_view name);
 
   /**
    * Create a sink for accepting OpenCV images in a separate thread.
@@ -143,7 +140,7 @@
    *        or GetError() as needed, but should not call (except in very
    *        unusual circumstances) WaitForImage().
    */
-  CvSink(const wpi::Twine& name,
+  CvSink(std::string_view name,
          std::function<void(uint64_t time)> processFrame);
 
   /**
@@ -155,7 +152,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225) const;
+  [[nodiscard]] uint64_t GrabFrame(cv::Mat& image,
+                                   double timeout = 0.225) const;
 
   /**
    * Wait for the next frame and get the image.  May block forever.
@@ -165,14 +163,14 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  uint64_t GrabFrameNoTimeout(cv::Mat& image) const;
+  [[nodiscard]] uint64_t GrabFrameNoTimeout(cv::Mat& image) const;
 };
 
-inline CvSource::CvSource(const wpi::Twine& name, const VideoMode& mode) {
+inline CvSource::CvSource(std::string_view name, const VideoMode& mode) {
   m_handle = CreateCvSource(name, mode, &m_status);
 }
 
-inline CvSource::CvSource(const wpi::Twine& name, VideoMode::PixelFormat format,
+inline CvSource::CvSource(std::string_view name, VideoMode::PixelFormat format,
                           int width, int height, int fps) {
   m_handle =
       CreateCvSource(name, VideoMode{format, width, height, fps}, &m_status);
@@ -183,11 +181,11 @@
   PutSourceFrame(m_handle, image, &m_status);
 }
 
-inline CvSink::CvSink(const wpi::Twine& name) {
+inline CvSink::CvSink(std::string_view name) {
   m_handle = CreateCvSink(name, &m_status);
 }
 
-inline CvSink::CvSink(const wpi::Twine& name,
+inline CvSink::CvSink(std::string_view name,
                       std::function<void(uint64_t time)> processFrame) {
   m_handle = CreateCvSinkCallback(name, processFrame, &m_status);
 }
diff --git a/cscore/src/main/native/include/cscore_oo.h b/cscore/src/main/native/include/cscore_oo.h
index 9e6e290..4fd1bff 100644
--- a/cscore/src/main/native/include/cscore_oo.h
+++ b/cscore/src/main/native/include/cscore_oo.h
@@ -1,18 +1,18 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CSCORE_OO_H_
 #define CSCORE_CSCORE_OO_H_
 
 #include <initializer_list>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
+#include <wpi/span.h>
+
 #include "cscore_cpp.h"
 
 namespace cs {
@@ -51,7 +51,7 @@
     kEnum = CS_PROP_ENUM
   };
 
-  VideoProperty() : m_status(0), m_handle(0), m_kind(kNone) {}
+  VideoProperty() = default;
 
   std::string GetName() const;
 
@@ -74,8 +74,8 @@
 
   // String-specific functions
   std::string GetString() const;
-  wpi::StringRef GetString(wpi::SmallVectorImpl<char>& buf) const;
-  void SetString(const wpi::Twine& value);
+  std::string_view GetString(wpi::SmallVectorImpl<char>& buf) const;
+  void SetString(std::string_view value);
 
   // Enum-specific functions
   std::vector<std::string> GetChoices() const;
@@ -86,9 +86,9 @@
   explicit VideoProperty(CS_Property handle);
   VideoProperty(CS_Property handle, Kind kind);
 
-  mutable CS_Status m_status;
-  CS_Property m_handle;
-  Kind m_kind;
+  mutable CS_Status m_status{0};
+  CS_Property m_handle{0};
+  Kind m_kind{kNone};
 };
 
 /**
@@ -127,7 +127,7 @@
     kConnectionForceClose = CS_CONNECTION_FORCE_CLOSE
   };
 
-  VideoSource() noexcept : m_handle(0) {}
+  VideoSource() noexcept = default;
   VideoSource(const VideoSource& source);
   VideoSource(VideoSource&& other) noexcept;
   VideoSource& operator=(VideoSource other) noexcept;
@@ -197,7 +197,7 @@
    * @return Property contents (of kind Property::kNone if no property with
    *         the given name exists)
    */
-  VideoProperty GetProperty(const wpi::Twine& name);
+  VideoProperty GetProperty(std::string_view name);
 
   /**
    * Enumerate all properties of this source.
@@ -279,7 +279,7 @@
    * @param config configuration
    * @return True if set successfully
    */
-  bool SetConfigJson(wpi::StringRef config);
+  bool SetConfigJson(std::string_view config);
 
   /**
    * Set video mode and properties from a JSON configuration object.
@@ -352,7 +352,7 @@
   explicit VideoSource(CS_Source handle) : m_handle(handle) {}
 
   mutable CS_Status m_status = 0;
-  CS_Source m_handle;
+  CS_Source m_handle{0};
 };
 
 /**
@@ -427,7 +427,7 @@
    * @param name Source name (arbitrary unique identifier)
    * @param dev Device number (e.g. 0 for /dev/video0)
    */
-  UsbCamera(const wpi::Twine& name, int dev);
+  UsbCamera(std::string_view name, int dev);
 
   /**
    * Create a source for a USB camera based on device path.
@@ -435,7 +435,7 @@
    * @param name Source name (arbitrary unique identifier)
    * @param path Path to device (e.g. "/dev/video0" on Linux)
    */
-  UsbCamera(const wpi::Twine& name, const wpi::Twine& path);
+  UsbCamera(std::string_view name, std::string_view path);
 
   /**
    * Enumerate USB cameras on the local system.
@@ -447,7 +447,7 @@
   /**
    * Change the path to the device.
    */
-  void SetPath(const wpi::Twine& path);
+  void SetPath(std::string_view path);
 
   /**
    * Get the path to the device.
@@ -486,7 +486,7 @@
    * @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg")
    * @param kind Camera kind (e.g. kAxis)
    */
-  HttpCamera(const wpi::Twine& name, const wpi::Twine& url,
+  HttpCamera(std::string_view name, std::string_view url,
              HttpCameraKind kind = kUnknown);
 
   /**
@@ -496,7 +496,7 @@
    * @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg")
    * @param kind Camera kind (e.g. kAxis)
    */
-  HttpCamera(const wpi::Twine& name, const char* url,
+  HttpCamera(std::string_view name, const char* url,
              HttpCameraKind kind = kUnknown);
 
   /**
@@ -506,7 +506,7 @@
    * @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg")
    * @param kind Camera kind (e.g. kAxis)
    */
-  HttpCamera(const wpi::Twine& name, const std::string& url,
+  HttpCamera(std::string_view name, const std::string& url,
              HttpCameraKind kind = kUnknown);
 
   /**
@@ -516,7 +516,7 @@
    * @param urls Array of Camera URLs
    * @param kind Camera kind (e.g. kAxis)
    */
-  HttpCamera(const wpi::Twine& name, wpi::ArrayRef<std::string> urls,
+  HttpCamera(std::string_view name, wpi::span<const std::string> urls,
              HttpCameraKind kind = kUnknown);
 
   /**
@@ -527,7 +527,7 @@
    * @param kind Camera kind (e.g. kAxis)
    */
   template <typename T>
-  HttpCamera(const wpi::Twine& name, std::initializer_list<T> urls,
+  HttpCamera(std::string_view name, std::initializer_list<T> urls,
              HttpCameraKind kind = kUnknown);
 
   /**
@@ -541,7 +541,7 @@
   /**
    * Change the URLs used to connect to the camera.
    */
-  void SetUrls(wpi::ArrayRef<std::string> urls);
+  void SetUrls(wpi::span<const std::string> urls);
 
   /**
    * Change the URLs used to connect to the camera.
@@ -559,8 +559,8 @@
  * A source that represents an Axis IP camera.
  */
 class AxisCamera : public HttpCamera {
-  static std::string HostToUrl(const wpi::Twine& host);
-  static std::vector<std::string> HostToUrl(wpi::ArrayRef<std::string> hosts);
+  static std::string HostToUrl(std::string_view host);
+  static std::vector<std::string> HostToUrl(wpi::span<const std::string> hosts);
   template <typename T>
   static std::vector<std::string> HostToUrl(std::initializer_list<T> hosts);
 
@@ -570,55 +570,41 @@
    *
    * @param name Source name (arbitrary unique identifier)
    * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
-   * @param kind Camera kind (e.g. kAxis)
    */
-  AxisCamera(const wpi::Twine& name, const wpi::Twine& host);
+  AxisCamera(std::string_view name, std::string_view host);
 
   /**
    * Create a source for an Axis IP camera.
    *
    * @param name Source name (arbitrary unique identifier)
    * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
-   * @param kind Camera kind (e.g. kAxis)
    */
-  AxisCamera(const wpi::Twine& name, const char* host);
+  AxisCamera(std::string_view name, const char* host);
 
   /**
    * Create a source for an Axis IP camera.
    *
    * @param name Source name (arbitrary unique identifier)
    * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
-   * @param kind Camera kind (e.g. kAxis)
    */
-  AxisCamera(const wpi::Twine& name, const std::string& host);
-
-  /**
-   * Create a source for an Axis IP camera.
-   *
-   * @param name Source name (arbitrary unique identifier)
-   * @param host Camera host IP or DNS name (e.g. "10.x.y.11")
-   * @param kind Camera kind (e.g. kAxis)
-   */
-  AxisCamera(const wpi::Twine& name, wpi::StringRef host);
+  AxisCamera(std::string_view name, const std::string& host);
 
   /**
    * Create a source for an Axis IP camera.
    *
    * @param name Source name (arbitrary unique identifier)
    * @param hosts Array of Camera host IPs/DNS names
-   * @param kind Camera kind (e.g. kAxis)
    */
-  AxisCamera(const wpi::Twine& name, wpi::ArrayRef<std::string> hosts);
+  AxisCamera(std::string_view name, wpi::span<const std::string> hosts);
 
   /**
    * Create a source for an Axis IP camera.
    *
    * @param name Source name (arbitrary unique identifier)
    * @param hosts Array of Camera host IPs/DNS names
-   * @param kind Camera kind (e.g. kAxis)
    */
   template <typename T>
-  AxisCamera(const wpi::Twine& name, std::initializer_list<T> hosts);
+  AxisCamera(std::string_view name, std::initializer_list<T> hosts);
 };
 
 /**
@@ -632,8 +618,10 @@
   /**
    * Signal sinks that an error has occurred.  This should be called instead
    * of NotifyFrame when an error occurs.
+   *
+   * @param msg Notification message.
    */
-  void NotifyError(const wpi::Twine& msg);
+  void NotifyError(std::string_view msg);
 
   /**
    * Set source connection status.  Defaults to true.
@@ -647,7 +635,7 @@
    *
    * @param description Description
    */
-  void SetDescription(const wpi::Twine& description);
+  void SetDescription(std::string_view description);
 
   /**
    * Create a property.
@@ -661,7 +649,7 @@
    * @param value Current value
    * @return Property
    */
-  VideoProperty CreateProperty(const wpi::Twine& name, VideoProperty::Kind kind,
+  VideoProperty CreateProperty(std::string_view name, VideoProperty::Kind kind,
                                int minimum, int maximum, int step,
                                int defaultValue, int value);
 
@@ -676,7 +664,7 @@
    * @param value Current value
    * @return Property
    */
-  VideoProperty CreateIntegerProperty(const wpi::Twine& name, int minimum,
+  VideoProperty CreateIntegerProperty(std::string_view name, int minimum,
                                       int maximum, int step, int defaultValue,
                                       int value);
 
@@ -688,19 +676,18 @@
    * @param value Current value
    * @return Property
    */
-  VideoProperty CreateBooleanProperty(const wpi::Twine& name, bool defaultValue,
+  VideoProperty CreateBooleanProperty(std::string_view name, bool defaultValue,
                                       bool value);
 
   /**
    * Create a string property.
    *
    * @param name Property name
-   * @param defaultValue Default value
    * @param value Current value
    * @return Property
    */
-  VideoProperty CreateStringProperty(const wpi::Twine& name,
-                                     const wpi::Twine& value);
+  VideoProperty CreateStringProperty(std::string_view name,
+                                     std::string_view value);
 
   /**
    * Configure enum property choices.
@@ -709,7 +696,7 @@
    * @param choices Choices
    */
   void SetEnumPropertyChoices(const VideoProperty& property,
-                              wpi::ArrayRef<std::string> choices);
+                              wpi::span<const std::string> choices);
 
   /**
    * Configure enum property choices.
@@ -736,7 +723,7 @@
     kCv = CS_SINK_CV
   };
 
-  VideoSink() noexcept : m_handle(0) {}
+  VideoSink() noexcept = default;
   VideoSink(const VideoSink& sink);
   VideoSink(VideoSink&& sink) noexcept;
   VideoSink& operator=(VideoSink other) noexcept;
@@ -775,7 +762,7 @@
    * @return Property (kind Property::kNone if no property with
    *         the given name exists)
    */
-  VideoProperty GetProperty(const wpi::Twine& name);
+  VideoProperty GetProperty(std::string_view name);
 
   /**
    * Enumerate all properties of this sink.
@@ -801,7 +788,7 @@
    * @param config configuration
    * @return True if set successfully
    */
-  bool SetConfigJson(wpi::StringRef config);
+  bool SetConfigJson(std::string_view config);
 
   /**
    * Set properties from a JSON configuration object.
@@ -848,7 +835,7 @@
    * @return Property (kind Property::kNone if no property with
    *         the given name exists or no source connected)
    */
-  VideoProperty GetSourceProperty(const wpi::Twine& name);
+  VideoProperty GetSourceProperty(std::string_view name);
 
   CS_Status GetLastStatus() const { return m_status; }
 
@@ -869,7 +856,7 @@
   explicit VideoSink(CS_Sink handle) : m_handle(handle) {}
 
   mutable CS_Status m_status = 0;
-  CS_Sink m_handle;
+  CS_Sink m_handle{0};
 };
 
 /**
@@ -886,8 +873,7 @@
    * @param listenAddress TCP listen address (empty string for all addresses)
    * @param port TCP port number
    */
-  MjpegServer(const wpi::Twine& name, const wpi::Twine& listenAddress,
-              int port);
+  MjpegServer(std::string_view name, std::string_view listenAddress, int port);
 
   /**
    * Create a MJPEG-over-HTTP server sink.
@@ -895,7 +881,7 @@
    * @param name Sink name (arbitrary unique identifier)
    * @param port TCP port number
    */
-  MjpegServer(const wpi::Twine& name, int port) : MjpegServer(name, "", port) {}
+  MjpegServer(std::string_view name, int port) : MjpegServer(name, "", port) {}
 
   /**
    * Get the listen address of the server.
@@ -966,7 +952,7 @@
    *
    * @param description Description
    */
-  void SetDescription(const wpi::Twine& description);
+  void SetDescription(std::string_view description);
 
   /**
    * Get error string.  Call this if WaitForFrame() returns 0 to determine
@@ -1011,7 +997,7 @@
  */
 class VideoListener {
  public:
-  VideoListener() : m_handle(0) {}
+  VideoListener() = default;
 
   /**
    * Create an event listener.
@@ -1036,13 +1022,13 @@
   }
 
  private:
-  CS_Listener m_handle;
+  CS_Listener m_handle{0};
 };
 
 /** @} */
 
 }  // namespace cs
 
-#include "cscore_oo.inl"
+#include "cscore_oo.inc"
 
 #endif  // CSCORE_CSCORE_OO_H_
diff --git a/cscore/src/main/native/include/cscore_oo.inl b/cscore/src/main/native/include/cscore_oo.inc
similarity index 79%
rename from cscore/src/main/native/include/cscore_oo.inl
rename to cscore/src/main/native/include/cscore_oo.inc
index 2d56e1c..5037d97 100644
--- a/cscore/src/main/native/include/cscore_oo.inl
+++ b/cscore/src/main/native/include/cscore_oo.inc
@@ -1,17 +1,17 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-#ifndef CSCORE_CSCORE_OO_INL_
-#define CSCORE_CSCORE_OO_INL_
+#ifndef CSCORE_CSCORE_OO_INC_
+#define CSCORE_CSCORE_OO_INC_
 
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
+#include "cscore_oo.h"
+
 namespace cs {
 
 inline std::string VideoProperty::GetName() const {
@@ -54,13 +54,13 @@
   return GetStringProperty(m_handle, &m_status);
 }
 
-inline wpi::StringRef VideoProperty::GetString(
+inline std::string_view VideoProperty::GetString(
     wpi::SmallVectorImpl<char>& buf) const {
   m_status = 0;
   return GetStringProperty(m_handle, buf, &m_status);
 }
 
-inline void VideoProperty::SetString(const wpi::Twine& value) {
+inline void VideoProperty::SetString(std::string_view value) {
   m_status = 0;
   SetStringProperty(m_handle, value, &m_status);
 }
@@ -72,15 +72,16 @@
 
 inline VideoProperty::VideoProperty(CS_Property handle) : m_handle(handle) {
   m_status = 0;
-  if (handle == 0)
+  if (handle == 0) {
     m_kind = kNone;
-  else
+  } else {
     m_kind =
         static_cast<Kind>(static_cast<int>(GetPropertyKind(handle, &m_status)));
+  }
 }
 
 inline VideoProperty::VideoProperty(CS_Property handle, Kind kind)
-    : m_status(0), m_handle(handle), m_kind(kind) {}
+    : m_handle(handle), m_kind(kind) {}
 
 inline VideoSource::VideoSource(const VideoSource& source)
     : m_handle(source.m_handle == 0 ? 0
@@ -97,7 +98,9 @@
 
 inline VideoSource::~VideoSource() {
   m_status = 0;
-  if (m_handle != 0) ReleaseSource(m_handle, &m_status);
+  if (m_handle != 0) {
+    ReleaseSource(m_handle, &m_status);
+  }
 }
 
 inline VideoSource::Kind VideoSource::GetKind() const {
@@ -137,7 +140,7 @@
   return IsSourceEnabled(m_handle, &m_status);
 }
 
-inline VideoProperty VideoSource::GetProperty(const wpi::Twine& name) {
+inline VideoProperty VideoSource::GetProperty(std::string_view name) {
   m_status = 0;
   return VideoProperty{GetSourceProperty(m_handle, name, &m_status)};
 }
@@ -174,7 +177,7 @@
   return SetSourceFPS(m_handle, fps, &m_status);
 }
 
-inline bool VideoSource::SetConfigJson(wpi::StringRef config) {
+inline bool VideoSource::SetConfigJson(std::string_view config) {
   m_status = 0;
   return SetSourceConfigJson(m_handle, config, &m_status);
 }
@@ -246,11 +249,11 @@
   SetCameraExposureManual(m_handle, value, &m_status);
 }
 
-inline UsbCamera::UsbCamera(const wpi::Twine& name, int dev) {
+inline UsbCamera::UsbCamera(std::string_view name, int dev) {
   m_handle = CreateUsbCameraDev(name, dev, &m_status);
 }
 
-inline UsbCamera::UsbCamera(const wpi::Twine& name, const wpi::Twine& path) {
+inline UsbCamera::UsbCamera(std::string_view name, std::string_view path) {
   m_handle = CreateUsbCameraPath(name, path, &m_status);
 }
 
@@ -259,7 +262,7 @@
   return ::cs::EnumerateUsbCameras(&status);
 }
 
-inline void UsbCamera::SetPath(const wpi::Twine& path) {
+inline void UsbCamera::SetPath(std::string_view path) {
   m_status = 0;
   return ::cs::SetUsbCameraPath(m_handle, path, &m_status);
 }
@@ -280,26 +283,26 @@
               &m_status);
 }
 
-inline HttpCamera::HttpCamera(const wpi::Twine& name, const wpi::Twine& url,
+inline HttpCamera::HttpCamera(std::string_view name, std::string_view url,
                               HttpCameraKind kind) {
   m_handle = CreateHttpCamera(
       name, url, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
       &m_status);
 }
 
-inline HttpCamera::HttpCamera(const wpi::Twine& name, const char* url,
+inline HttpCamera::HttpCamera(std::string_view name, const char* url,
                               HttpCameraKind kind) {
   m_handle = CreateHttpCamera(
       name, url, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
       &m_status);
 }
 
-inline HttpCamera::HttpCamera(const wpi::Twine& name, const std::string& url,
+inline HttpCamera::HttpCamera(std::string_view name, const std::string& url,
                               HttpCameraKind kind)
-    : HttpCamera(name, wpi::Twine{url}, kind) {}
+    : HttpCamera(name, std::string_view{url}, kind) {}
 
-inline HttpCamera::HttpCamera(const wpi::Twine& name,
-                              wpi::ArrayRef<std::string> urls,
+inline HttpCamera::HttpCamera(std::string_view name,
+                              wpi::span<const std::string> urls,
                               HttpCameraKind kind) {
   m_handle = CreateHttpCamera(
       name, urls, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
@@ -307,12 +310,14 @@
 }
 
 template <typename T>
-inline HttpCamera::HttpCamera(const wpi::Twine& name,
+inline HttpCamera::HttpCamera(std::string_view name,
                               std::initializer_list<T> urls,
                               HttpCameraKind kind) {
   std::vector<std::string> vec;
   vec.reserve(urls.size());
-  for (const auto& url : urls) vec.emplace_back(url);
+  for (const auto& url : urls) {
+    vec.emplace_back(url);
+  }
   m_handle = CreateHttpCamera(
       name, vec, static_cast<CS_HttpCameraKind>(static_cast<int>(kind)),
       &m_status);
@@ -324,7 +329,7 @@
       static_cast<int>(::cs::GetHttpCameraKind(m_handle, &m_status)));
 }
 
-inline void HttpCamera::SetUrls(wpi::ArrayRef<std::string> urls) {
+inline void HttpCamera::SetUrls(wpi::span<const std::string> urls) {
   m_status = 0;
   ::cs::SetHttpCameraUrls(m_handle, urls, &m_status);
 }
@@ -333,7 +338,9 @@
 inline void HttpCamera::SetUrls(std::initializer_list<T> urls) {
   std::vector<std::string> vec;
   vec.reserve(urls.size());
-  for (const auto& url : urls) vec.emplace_back(url);
+  for (const auto& url : urls) {
+    vec.emplace_back(url);
+  }
   m_status = 0;
   ::cs::SetHttpCameraUrls(m_handle, vec, &m_status);
 }
@@ -343,16 +350,13 @@
   return ::cs::GetHttpCameraUrls(m_handle, &m_status);
 }
 
-inline std::string AxisCamera::HostToUrl(const wpi::Twine& host) {
-  return ("http://" + host + "/mjpg/video.mjpg").str();
-}
-
 inline std::vector<std::string> AxisCamera::HostToUrl(
-    wpi::ArrayRef<std::string> hosts) {
+    wpi::span<const std::string> hosts) {
   std::vector<std::string> rv;
   rv.reserve(hosts.size());
-  for (const auto& host : hosts)
-    rv.emplace_back(HostToUrl(wpi::StringRef{host}));
+  for (const auto& host : hosts) {
+    rv.emplace_back(HostToUrl(std::string_view{host}));
+  }
   return rv;
 }
 
@@ -361,33 +365,31 @@
     std::initializer_list<T> hosts) {
   std::vector<std::string> rv;
   rv.reserve(hosts.size());
-  for (const auto& host : hosts)
-    rv.emplace_back(HostToUrl(wpi::StringRef{host}));
+  for (const auto& host : hosts) {
+    rv.emplace_back(HostToUrl(std::string_view{host}));
+  }
   return rv;
 }
 
-inline AxisCamera::AxisCamera(const wpi::Twine& name, const wpi::Twine& host)
+inline AxisCamera::AxisCamera(std::string_view name, std::string_view host)
     : HttpCamera(name, HostToUrl(host), kAxis) {}
 
-inline AxisCamera::AxisCamera(const wpi::Twine& name, const char* host)
+inline AxisCamera::AxisCamera(std::string_view name, const char* host)
     : HttpCamera(name, HostToUrl(host), kAxis) {}
 
-inline AxisCamera::AxisCamera(const wpi::Twine& name, const std::string& host)
-    : HttpCamera(name, HostToUrl(wpi::Twine{host}), kAxis) {}
+inline AxisCamera::AxisCamera(std::string_view name, const std::string& host)
+    : HttpCamera(name, HostToUrl(std::string_view{host}), kAxis) {}
 
-inline AxisCamera::AxisCamera(const wpi::Twine& name, wpi::StringRef host)
-    : HttpCamera(name, HostToUrl(host), kAxis) {}
-
-inline AxisCamera::AxisCamera(const wpi::Twine& name,
-                              wpi::ArrayRef<std::string> hosts)
+inline AxisCamera::AxisCamera(std::string_view name,
+                              wpi::span<const std::string> hosts)
     : HttpCamera(name, HostToUrl(hosts), kAxis) {}
 
 template <typename T>
-inline AxisCamera::AxisCamera(const wpi::Twine& name,
+inline AxisCamera::AxisCamera(std::string_view name,
                               std::initializer_list<T> hosts)
     : HttpCamera(name, HostToUrl(hosts), kAxis) {}
 
-inline void ImageSource::NotifyError(const wpi::Twine& msg) {
+inline void ImageSource::NotifyError(std::string_view msg) {
   m_status = 0;
   NotifySourceError(m_handle, msg, &m_status);
 }
@@ -397,12 +399,12 @@
   SetSourceConnected(m_handle, connected, &m_status);
 }
 
-inline void ImageSource::SetDescription(const wpi::Twine& description) {
+inline void ImageSource::SetDescription(std::string_view description) {
   m_status = 0;
   SetSourceDescription(m_handle, description, &m_status);
 }
 
-inline VideoProperty ImageSource::CreateProperty(const wpi::Twine& name,
+inline VideoProperty ImageSource::CreateProperty(std::string_view name,
                                                  VideoProperty::Kind kind,
                                                  int minimum, int maximum,
                                                  int step, int defaultValue,
@@ -413,7 +415,7 @@
       minimum, maximum, step, defaultValue, value, &m_status)};
 }
 
-inline VideoProperty ImageSource::CreateIntegerProperty(const wpi::Twine& name,
+inline VideoProperty ImageSource::CreateIntegerProperty(std::string_view name,
                                                         int minimum,
                                                         int maximum, int step,
                                                         int defaultValue,
@@ -426,7 +428,7 @@
       minimum, maximum, step, defaultValue, value, &m_status)};
 }
 
-inline VideoProperty ImageSource::CreateBooleanProperty(const wpi::Twine& name,
+inline VideoProperty ImageSource::CreateBooleanProperty(std::string_view name,
                                                         bool defaultValue,
                                                         bool value) {
   m_status = 0;
@@ -437,8 +439,8 @@
       0, 1, 1, defaultValue ? 1 : 0, value ? 1 : 0, &m_status)};
 }
 
-inline VideoProperty ImageSource::CreateStringProperty(
-    const wpi::Twine& name, const wpi::Twine& value) {
+inline VideoProperty ImageSource::CreateStringProperty(std::string_view name,
+                                                       std::string_view value) {
   m_status = 0;
   auto prop = VideoProperty{
       CreateSourceProperty(m_handle, name,
@@ -450,7 +452,7 @@
 }
 
 inline void ImageSource::SetEnumPropertyChoices(
-    const VideoProperty& property, wpi::ArrayRef<std::string> choices) {
+    const VideoProperty& property, wpi::span<const std::string> choices) {
   m_status = 0;
   SetSourceEnumPropertyChoices(m_handle, property.m_handle, choices, &m_status);
 }
@@ -460,7 +462,9 @@
     const VideoProperty& property, std::initializer_list<T> choices) {
   std::vector<std::string> vec;
   vec.reserve(choices.size());
-  for (const auto& choice : choices) vec.emplace_back(choice);
+  for (const auto& choice : choices) {
+    vec.emplace_back(choice);
+  }
   m_status = 0;
   SetSourceEnumPropertyChoices(m_handle, property.m_handle, vec, &m_status);
 }
@@ -479,7 +483,9 @@
 
 inline VideoSink::~VideoSink() {
   m_status = 0;
-  if (m_handle != 0) ReleaseSink(m_handle, &m_status);
+  if (m_handle != 0) {
+    ReleaseSink(m_handle, &m_status);
+  }
 }
 
 inline VideoSink::Kind VideoSink::GetKind() const {
@@ -497,17 +503,18 @@
   return GetSinkDescription(m_handle, &m_status);
 }
 
-inline VideoProperty VideoSink::GetProperty(const wpi::Twine& name) {
+inline VideoProperty VideoSink::GetProperty(std::string_view name) {
   m_status = 0;
   return VideoProperty{GetSinkProperty(m_handle, name, &m_status)};
 }
 
 inline void VideoSink::SetSource(VideoSource source) {
   m_status = 0;
-  if (!source)
+  if (!source) {
     SetSinkSource(m_handle, 0, &m_status);
-  else
+  } else {
     SetSinkSource(m_handle, source.m_handle, &m_status);
+  }
 }
 
 inline VideoSource VideoSink::GetSource() const {
@@ -516,12 +523,12 @@
   return VideoSource{handle == 0 ? 0 : CopySource(handle, &m_status)};
 }
 
-inline VideoProperty VideoSink::GetSourceProperty(const wpi::Twine& name) {
+inline VideoProperty VideoSink::GetSourceProperty(std::string_view name) {
   m_status = 0;
   return VideoProperty{GetSinkSourceProperty(m_handle, name, &m_status)};
 }
 
-inline bool VideoSink::SetConfigJson(wpi::StringRef config) {
+inline bool VideoSink::SetConfigJson(std::string_view config) {
   m_status = 0;
   return SetSinkConfigJson(m_handle, config, &m_status);
 }
@@ -536,8 +543,8 @@
   return GetSinkConfigJson(m_handle, &m_status);
 }
 
-inline MjpegServer::MjpegServer(const wpi::Twine& name,
-                                const wpi::Twine& listenAddress, int port) {
+inline MjpegServer::MjpegServer(std::string_view name,
+                                std::string_view listenAddress, int port) {
   m_handle = CreateMjpegServer(name, listenAddress, port, &m_status);
 }
 
@@ -575,7 +582,7 @@
               quality, &m_status);
 }
 
-inline void ImageSink::SetDescription(const wpi::Twine& description) {
+inline void ImageSink::SetDescription(std::string_view description) {
   m_status = 0;
   SetSinkDescription(m_handle, description, &m_status);
 }
@@ -628,9 +635,11 @@
 
 inline VideoListener::~VideoListener() {
   CS_Status status = 0;
-  if (m_handle != 0) RemoveListener(m_handle, &status);
+  if (m_handle != 0) {
+    RemoveListener(m_handle, &status);
+  }
 }
 
 }  // namespace cs
 
-#endif  // CSCORE_CSCORE_OO_INL_
+#endif  // CSCORE_CSCORE_OO_INC_
diff --git a/cscore/src/main/native/include/cscore_raw.h b/cscore/src/main/native/include/cscore_raw.h
index 902d90e..0aaaeff 100644
--- a/cscore/src/main/native/include/cscore_raw.h
+++ b/cscore/src/main/native/include/cscore_raw.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CSCORE_RAW_H_
 #define CSCORE_CSCORE_RAW_H_
@@ -17,7 +14,7 @@
 /**
  * Raw Frame
  */
-typedef struct CS_RawFrame {
+typedef struct CS_RawFrame {  // NOLINT
   char* data;
   int dataLength;
   int pixelFormat;
@@ -81,11 +78,11 @@
  * @{
  */
 
-CS_Source CreateRawSource(const wpi::Twine& name, const VideoMode& mode,
+CS_Source CreateRawSource(std::string_view name, const VideoMode& mode,
                           CS_Status* status);
 
-CS_Sink CreateRawSink(const wpi::Twine& name, CS_Status* status);
-CS_Sink CreateRawSinkCallback(const wpi::Twine& name,
+CS_Sink CreateRawSink(std::string_view name, CS_Status* status);
+CS_Sink CreateRawSinkCallback(std::string_view name,
                               std::function<void(uint64_t time)> processFrame,
                               CS_Status* status);
 
@@ -110,7 +107,7 @@
    * @param name Source name (arbitrary unique identifier)
    * @param mode Video mode being generated
    */
-  RawSource(const wpi::Twine& name, const VideoMode& mode);
+  RawSource(std::string_view name, const VideoMode& mode);
 
   /**
    * Create a raw frame source.
@@ -121,7 +118,7 @@
    * @param height height
    * @param fps fps
    */
-  RawSource(const wpi::Twine& name, VideoMode::PixelFormat pixelFormat,
+  RawSource(std::string_view name, VideoMode::PixelFormat pixelFormat,
             int width, int height, int fps);
 
  protected:
@@ -150,7 +147,7 @@
    *
    * @param name Source name (arbitrary unique identifier)
    */
-  explicit RawSink(const wpi::Twine& name);
+  explicit RawSink(std::string_view name);
 
   /**
    * Create a sink for accepting raws images in a separate thread.
@@ -164,7 +161,7 @@
    *        or GetError() as needed, but should not call (except in very
    *        unusual circumstances) WaitForImage().
    */
-  RawSink(const wpi::Twine& name,
+  RawSink(std::string_view name,
           std::function<void(uint64_t time)> processFrame);
 
  protected:
@@ -177,7 +174,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  uint64_t GrabFrame(RawFrame& image, double timeout = 0.225) const;
+  [[nodiscard]] uint64_t GrabFrame(RawFrame& image,
+                                   double timeout = 0.225) const;
 
   /**
    * Wait for the next frame and get the image.  May block forever.
@@ -187,14 +185,14 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  uint64_t GrabFrameNoTimeout(RawFrame& image) const;
+  [[nodiscard]] uint64_t GrabFrameNoTimeout(RawFrame& image) const;
 };
 
-inline RawSource::RawSource(const wpi::Twine& name, const VideoMode& mode) {
+inline RawSource::RawSource(std::string_view name, const VideoMode& mode) {
   m_handle = CreateRawSource(name, mode, &m_status);
 }
 
-inline RawSource::RawSource(const wpi::Twine& name,
+inline RawSource::RawSource(std::string_view name,
                             VideoMode::PixelFormat format, int width,
                             int height, int fps) {
   m_handle =
@@ -206,11 +204,11 @@
   PutSourceFrame(m_handle, image, &m_status);
 }
 
-inline RawSink::RawSink(const wpi::Twine& name) {
+inline RawSink::RawSink(std::string_view name) {
   m_handle = CreateRawSink(name, &m_status);
 }
 
-inline RawSink::RawSink(const wpi::Twine& name,
+inline RawSink::RawSink(std::string_view name,
                         std::function<void(uint64_t time)> processFrame) {
   m_handle = CreateRawSinkCallback(name, processFrame, &m_status);
 }
diff --git a/cscore/src/main/native/include/cscore_raw_cv.h b/cscore/src/main/native/include/cscore_raw_cv.h
index ed40006..5b9a374 100644
--- a/cscore/src/main/native/include/cscore_raw_cv.h
+++ b/cscore/src/main/native/include/cscore_raw_cv.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_CSCORE_RAW_CV_H_
 #define CSCORE_CSCORE_RAW_CV_H_
@@ -12,6 +9,8 @@
 #error "Cannot include both cscore_cv.h and cscore_raw_cv.h in the same file"
 #endif
 
+#include <opencv2/core/mat.hpp>
+
 #include "cscore_raw.h"
 
 namespace cs {
@@ -35,7 +34,7 @@
    * @param name Source name (arbitrary unique identifier)
    * @param mode Video mode being generated
    */
-  RawCvSource(const wpi::Twine& name, const VideoMode& mode);
+  RawCvSource(std::string_view name, const VideoMode& mode);
 
   /**
    * Create a Raw OpenCV source.
@@ -46,7 +45,7 @@
    * @param height height
    * @param fps fps
    */
-  RawCvSource(const wpi::Twine& name, VideoMode::PixelFormat pixelFormat,
+  RawCvSource(std::string_view name, VideoMode::PixelFormat pixelFormat,
               int width, int height, int fps);
 
   /**
@@ -86,7 +85,7 @@
    *
    * @param name Source name (arbitrary unique identifier)
    */
-  explicit RawCvSink(const wpi::Twine& name);
+  explicit RawCvSink(std::string_view name);
 
   /**
    * Create a sink for accepting OpenCV images in a separate thread.
@@ -100,7 +99,7 @@
    *        or GetError() as needed, but should not call (except in very
    *        unusual circumstances) WaitForImage().
    */
-  RawCvSink(const wpi::Twine& name,
+  RawCvSink(std::string_view name,
             std::function<void(uint64_t time)> processFrame);
 
   /**
@@ -112,7 +111,7 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225);
+  [[nodiscard]] uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225);
 
   /**
    * Wait for the next frame and get the image.  May block forever.
@@ -122,7 +121,7 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  uint64_t GrabFrameNoTimeout(cv::Mat& image);
+  [[nodiscard]] uint64_t GrabFrameNoTimeout(cv::Mat& image);
 
   /**
    * Wait for the next frame and get the image.
@@ -133,7 +132,8 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225);
+  [[nodiscard]] uint64_t GrabFrameDirect(cv::Mat& image,
+                                         double timeout = 0.225);
 
   /**
    * Wait for the next frame and get the image.  May block forever.
@@ -143,16 +143,16 @@
    *         message); the frame time is in the same time base as wpi::Now(),
    *         and is in 1 us increments.
    */
-  uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
+  [[nodiscard]] uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
 
  private:
   RawFrame rawFrame;
 };
 
-inline RawCvSource::RawCvSource(const wpi::Twine& name, const VideoMode& mode)
+inline RawCvSource::RawCvSource(std::string_view name, const VideoMode& mode)
     : RawSource{name, mode} {}
 
-inline RawCvSource::RawCvSource(const wpi::Twine& name,
+inline RawCvSource::RawCvSource(std::string_view name,
                                 VideoMode::PixelFormat format, int width,
                                 int height, int fps)
     : RawSource{name, format, width, height, fps} {}
@@ -162,34 +162,34 @@
   rawFrame.data = reinterpret_cast<char*>(image.data);
   rawFrame.width = image.cols;
   rawFrame.height = image.rows;
-  rawFrame.totalData = image.total() * image.channels;
-  rawFrame.pixelFormat = image.channels == 3 ? CS_PIXFMT_BGR : CS_PIXFMT_GRAY;
+  rawFrame.totalData = image.total() * image.channels();
+  rawFrame.pixelFormat = image.channels() == 3 ? CS_PIXFMT_BGR : CS_PIXFMT_GRAY;
   PutSourceFrame(m_handle, rawFrame, &m_status);
 }
 
-inline RawCvSink::RawCvSink(const wpi::Twine& name) : RawSink{name} {}
+inline RawCvSink::RawCvSink(std::string_view name) : RawSink{name} {}
 
-inline RawCvSink::RawCvSink(const wpi::Twine& name,
+inline RawCvSink::RawCvSink(std::string_view name,
                             std::function<void(uint64_t time)> processFrame)
     : RawSink{name, processFrame} {}
 
 inline uint64_t RawCvSink::GrabFrame(cv::Mat& image, double timeout) {
-  cv::Mat tmpMat;
-  auto retVal = GrabFrameDirect(tmpMat);
+  cv::Mat tmpnam;
+  auto retVal = GrabFrameDirect(tmpnam);
   if (retVal <= 0) {
     return retVal;
   }
-  tmpMat.copyTo(image);
+  tmpnam.copyTo(image);
   return retVal;
 }
 
 inline uint64_t RawCvSink::GrabFrameNoTimeout(cv::Mat& image) {
-  cv::Mat tmpMat;
-  auto retVal = GrabFrameNoTimeoutDirect(tmpMat);
+  cv::Mat tmpnam;
+  auto retVal = GrabFrameNoTimeoutDirect(tmpnam);
   if (retVal <= 0) {
     return retVal;
   }
-  tmpMat.copyTo(image);
+  tmpnam.copyTo(image);
   return retVal;
 }
 
@@ -198,7 +198,9 @@
   rawFrame.width = 0;
   rawFrame.pixelFormat = CS_PixelFormat::CS_PIXFMT_BGR;
   m_status = RawSink::GrabFrame(rawFrame, timeout);
-  if (m_status <= 0) return m_status;
+  if (m_status <= 0) {
+    return m_status;
+  }
   image = cv::Mat{rawFrame.height, rawFrame.width, CV_8UC3, rawFrame.data};
   return m_status;
 }
@@ -208,7 +210,9 @@
   rawFrame.width = 0;
   rawFrame.pixelFormat = CS_PixelFormat::CS_PIXFMT_BGR;
   m_status = RawSink::GrabFrameNoTimeout(rawFrame);
-  if (m_status <= 0) return m_status;
+  if (m_status <= 0) {
+    return m_status;
+  }
   image = cv::Mat{rawFrame.height, rawFrame.width, CV_8UC3, rawFrame.data};
   return m_status;
 }
diff --git a/cscore/src/main/native/linux/NetworkListener.cpp b/cscore/src/main/native/linux/NetworkListener.cpp
index 6915b30..1a0ac8b 100644
--- a/cscore/src/main/native/linux/NetworkListener.cpp
+++ b/cscore/src/main/native/linux/NetworkListener.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "NetworkListener.h"
 
@@ -37,7 +34,7 @@
    public:
     Thread(wpi::Logger& logger, Notifier& notifier)
         : m_logger(logger), m_notifier(notifier) {}
-    void Main();
+    void Main() override;
 
     wpi::Logger& m_logger;
     Notifier& m_notifier;
@@ -50,7 +47,9 @@
 NetworkListener::NetworkListener(wpi::Logger& logger, Notifier& notifier)
     : m_impl(std::make_unique<Impl>(logger, notifier)) {}
 
-NetworkListener::~NetworkListener() { Stop(); }
+NetworkListener::~NetworkListener() {
+  Stop();
+}
 
 void NetworkListener::Start() {
   m_impl->m_owner.Start(m_impl->m_logger, m_impl->m_notifier);
@@ -60,7 +59,9 @@
   // Wake up thread
   if (auto thr = m_impl->m_owner.GetThread()) {
     thr->m_active = false;
-    if (thr->m_command_fd >= 0) eventfd_write(thr->m_command_fd, 1);
+    if (thr->m_command_fd >= 0) {
+      eventfd_write(thr->m_command_fd, 1);
+    }
   }
   m_impl->m_owner.Stop();
 }
@@ -69,15 +70,15 @@
   // Create event socket so we can be shut down
   m_command_fd = ::eventfd(0, 0);
   if (m_command_fd < 0) {
-    ERROR(
-        "NetworkListener: could not create eventfd: " << std::strerror(errno));
+    ERROR("NetworkListener: could not create eventfd: {}",
+          std::strerror(errno));
     return;
   }
 
   // Create netlink socket
   int sd = ::socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
   if (sd < 0) {
-    ERROR("NetworkListener: could not create socket: " << std::strerror(errno));
+    ERROR("NetworkListener: could not create socket: {}", std::strerror(errno));
     ::close(m_command_fd);
     m_command_fd = -1;
     return;
@@ -89,7 +90,7 @@
   addr.nl_family = AF_NETLINK;
   addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
   if (bind(sd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
-    ERROR("NetworkListener: could not create socket: " << std::strerror(errno));
+    ERROR("NetworkListener: could not create socket: {}", std::strerror(errno));
     ::close(sd);
     ::close(m_command_fd);
     m_command_fd = -1;
@@ -112,30 +113,40 @@
     int nfds = std::max(m_command_fd, sd) + 1;
 
     if (::select(nfds, &readfds, nullptr, nullptr, &tv) < 0) {
-      ERROR("NetworkListener: select(): " << std::strerror(errno));
+      ERROR("NetworkListener: select(): {}", std::strerror(errno));
       break;  // XXX: is this the right thing to do here?
     }
 
     // Double-check to see if we're shutting down
-    if (!m_active) break;
+    if (!m_active) {
+      break;
+    }
 
-    if (!FD_ISSET(sd, &readfds)) continue;
+    if (!FD_ISSET(sd, &readfds)) {
+      continue;
+    }
 
     std::memset(&addr, 0, sizeof(addr));
     struct iovec iov = {buf, sizeof(buf)};
     struct msghdr msg = {&addr, sizeof(addr), &iov, 1, nullptr, 0, 0};
     int len = ::recvmsg(sd, &msg, 0);
     if (len < 0) {
-      if (errno == EWOULDBLOCK || errno == EAGAIN) continue;
-      ERROR(
-          "NetworkListener: could not read netlink: " << std::strerror(errno));
+      if (errno == EWOULDBLOCK || errno == EAGAIN) {
+        continue;
+      }
+      ERROR("NetworkListener: could not read netlink: {}",
+            std::strerror(errno));
       break;  // XXX: is this the right thing to do here?
     }
-    if (len == 0) continue;  // EOF?
+    if (len == 0) {
+      continue;  // EOF?
+    }
     unsigned int ulen = static_cast<unsigned int>(len);
     for (struct nlmsghdr* nh = reinterpret_cast<struct nlmsghdr*>(buf);
          NLMSG_OK(nh, ulen); nh = NLMSG_NEXT(nh, ulen)) {
-      if (nh->nlmsg_type == NLMSG_DONE) break;
+      if (nh->nlmsg_type == NLMSG_DONE) {
+        break;
+      }
       if (nh->nlmsg_type == RTM_NEWLINK || nh->nlmsg_type == RTM_DELLINK ||
           nh->nlmsg_type == RTM_NEWADDR || nh->nlmsg_type == RTM_DELADDR) {
         m_notifier.NotifyNetworkInterfacesChanged();
diff --git a/cscore/src/main/native/linux/NetworkUtil.cpp b/cscore/src/main/native/linux/NetworkUtil.cpp
index 2c1f3cd..43e0fa8 100644
--- a/cscore/src/main/native/linux/NetworkUtil.cpp
+++ b/cscore/src/main/native/linux/NetworkUtil.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "cscore_cpp.h"  // NOLINT(build/include_order)
 
@@ -16,17 +13,25 @@
 
 std::vector<std::string> GetNetworkInterfaces() {
   struct ifaddrs* ifa;
-  if (::getifaddrs(&ifa) != 0) return std::vector<std::string>{};
+  if (::getifaddrs(&ifa) != 0) {
+    return {};
+  }
 
   std::vector<std::string> rv;
   char buf[256];
   for (struct ifaddrs* i = ifa; i; i = i->ifa_next) {
-    if (!i->ifa_addr) continue;                       // no address
-    if (i->ifa_addr->sa_family != AF_INET) continue;  // only return IPv4
+    if (!i->ifa_addr) {
+      continue;  // no address
+    }
+    if (i->ifa_addr->sa_family != AF_INET) {
+      continue;  // only return IPv4
+    }
     struct sockaddr_in* addr_in = reinterpret_cast<sockaddr_in*>(i->ifa_addr);
     const char* addr =
         ::inet_ntop(addr_in->sin_family, &addr_in->sin_addr, buf, sizeof(buf));
-    if (!addr) continue;  // error converting address
+    if (!addr) {
+      continue;  // error converting address
+    }
     rv.emplace_back(addr);
   }
 
diff --git a/cscore/src/main/native/linux/UsbCameraBuffer.h b/cscore/src/main/native/linux/UsbCameraBuffer.h
index 98ac149..91fd588 100644
--- a/cscore/src/main/native/linux/UsbCameraBuffer.h
+++ b/cscore/src/main/native/linux/UsbCameraBuffer.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_USBCAMERABUFFER_H_
 #define CSCORE_USBCAMERABUFFER_H_
@@ -16,7 +13,7 @@
 
 class UsbCameraBuffer {
  public:
-  UsbCameraBuffer() noexcept : m_data{nullptr}, m_length{0} {}
+  UsbCameraBuffer() noexcept = default;
   UsbCameraBuffer(UsbCameraBuffer&& other) noexcept : UsbCameraBuffer() {
     swap(*this, other);
   }
@@ -38,7 +35,9 @@
   }
 
   ~UsbCameraBuffer() {
-    if (m_data) munmap(m_data, m_length);
+    if (m_data) {
+      munmap(m_data, m_length);
+    }
   }
 
   friend void swap(UsbCameraBuffer& first, UsbCameraBuffer& second) noexcept {
@@ -47,8 +46,8 @@
     swap(first.m_length, second.m_length);
   }
 
-  void* m_data;
-  size_t m_length;
+  void* m_data{nullptr};
+  size_t m_length{0};
 };
 
 }  // namespace cs
diff --git a/cscore/src/main/native/linux/UsbCameraImpl.cpp b/cscore/src/main/native/linux/UsbCameraImpl.cpp
index 1175ca3..a08fbbc 100644
--- a/cscore/src/main/native/linux/UsbCameraImpl.cpp
+++ b/cscore/src/main/native/linux/UsbCameraImpl.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "UsbCameraImpl.h"
 
@@ -23,11 +20,13 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <memory>
 
-#include <wpi/FileSystem.h>
+#include <fmt/format.h>
 #include <wpi/MemAlloc.h>
-#include <wpi/Path.h>
 #include <wpi/SmallString.h>
+#include <wpi/StringExtras.h>
+#include <wpi/fs.h>
 #include <wpi/raw_ostream.h>
 #include <wpi/timestamp.h>
 
@@ -42,6 +41,14 @@
 
 using namespace cs;
 
+namespace {
+// Find the length of an array.
+template <class T, size_t N>
+constexpr inline size_t array_lengthof(T (&)[N]) {
+  return N;
+}
+}  // namespace
+
 static constexpr char const* kPropWbAuto = "white_balance_temperature_auto";
 static constexpr char const* kPropWbValue = "white_balance_temperature";
 static constexpr char const* kPropExAuto = "exposure_auto";
@@ -98,8 +105,10 @@
   }
 }
 
-static bool IsPercentageProperty(wpi::StringRef name) {
-  if (name.startswith("raw_")) name = name.substr(4);
+static bool IsPercentageProperty(std::string_view name) {
+  if (wpi::starts_with(name, "raw_")) {
+    name = name.substr(4);
+  }
   return name == "brightness" || name == "contrast" || name == "saturation" ||
          name == "hue" || name == "sharpness" || name == "gain" ||
          name == "exposure_absolute" || name == "exposure_time_absolute";
@@ -123,9 +132,11 @@
   // LifeCam exposure setting quirk
   if (m_lifecam_exposure && rawProp.name == "raw_exposure_absolute" &&
       rawProp.minimum == 5 && rawProp.maximum == 20000) {
-    int nelems = wpi::array_lengthof(quirkLifeCamHd3000);
+    int nelems = array_lengthof(quirkLifeCamHd3000);
     for (int i = 0; i < nelems; ++i) {
-      if (rawValue < quirkLifeCamHd3000[i]) return 100.0 * i / nelems;
+      if (rawValue < quirkLifeCamHd3000[i]) {
+        return 100.0 * i / nelems;
+      }
     }
     return 100;
   }
@@ -138,10 +149,14 @@
   // LifeCam exposure setting quirk
   if (m_lifecam_exposure && rawProp.name == "raw_exposure_absolute" &&
       rawProp.minimum == 5 && rawProp.maximum == 20000) {
-    int nelems = wpi::array_lengthof(quirkLifeCamHd3000);
+    int nelems = array_lengthof(quirkLifeCamHd3000);
     int ndx = nelems * percentValue / 100.0;
-    if (ndx < 0) ndx = 0;
-    if (ndx >= nelems) ndx = nelems - 1;
+    if (ndx < 0) {
+      ndx = 0;
+    }
+    if (ndx >= nelems) {
+      ndx = nelems - 1;
+    }
     return quirkLifeCamHd3000[ndx];
   }
   return rawProp.minimum +
@@ -149,52 +164,64 @@
 }
 
 static bool GetVendorProduct(int dev, int* vendor, int* product) {
-  wpi::SmallString<64> ifpath;
-  {
-    wpi::raw_svector_ostream oss{ifpath};
-    oss << "/sys/class/video4linux/video" << dev << "/device/modalias";
-  }
+  auto ifpath =
+      fmt::format("/sys/class/video4linux/video{}/device/modalias", dev);
 
   int fd = open(ifpath.c_str(), O_RDONLY);
-  if (fd < 0) return false;
+  if (fd < 0) {
+    return false;
+  }
 
   char readBuf[128];
   ssize_t n = read(fd, readBuf, sizeof(readBuf));
   close(fd);
 
-  if (n <= 0) return false;
-  wpi::StringRef readStr{readBuf};
-  if (readStr.substr(readStr.find('v')).substr(1, 4).getAsInteger(16, *vendor))
+  if (n <= 0) {
     return false;
-  if (readStr.substr(readStr.find('p')).substr(1, 4).getAsInteger(16, *product))
+  }
+  std::string_view readStr{readBuf};
+  if (auto v = wpi::parse_integer<int>(
+          readStr.substr(readStr.find('v')).substr(1, 4), 16)) {
+    *vendor = v.value();
+  } else {
     return false;
+  }
+  if (auto v = wpi::parse_integer<int>(
+          readStr.substr(readStr.find('p')).substr(1, 4), 16)) {
+    *product = v.value();
+  } else {
+    return false;
+  }
 
   return true;
 }
 
 static bool GetDescriptionSysV4L(int dev, std::string* desc) {
-  wpi::SmallString<64> ifpath;
-  {
-    wpi::raw_svector_ostream oss{ifpath};
-    oss << "/sys/class/video4linux/video" << dev << "/device/interface";
-  }
+  auto ifpath =
+      fmt::format("/sys/class/video4linux/video{}/device/interface", dev);
 
   int fd = open(ifpath.c_str(), O_RDONLY);
-  if (fd < 0) return false;
+  if (fd < 0) {
+    return false;
+  }
 
   char readBuf[128];
   ssize_t n = read(fd, readBuf, sizeof(readBuf));
   close(fd);
 
-  if (n <= 0) return false;
+  if (n <= 0) {
+    return false;
+  }
 
-  *desc = wpi::StringRef(readBuf, n).rtrim();
+  *desc = wpi::rtrim(std::string_view(readBuf, n));
   return true;
 }
 
 static bool GetDescriptionIoctl(const char* cpath, std::string* desc) {
   int fd = open(cpath, O_RDWR);
-  if (fd < 0) return false;
+  if (fd < 0) {
+    return false;
+  }
 
   struct v4l2_capability vcap;
   std::memset(&vcap, 0, sizeof(vcap));
@@ -204,17 +231,16 @@
   }
   close(fd);
 
-  wpi::StringRef card{reinterpret_cast<const char*>(vcap.card)};
+  std::string_view card{reinterpret_cast<const char*>(vcap.card)};
   // try to convert "UVC Camera (0000:0000)" into a better name
-  int vendor = 0;
-  int product = 0;
-  if (card.startswith("UVC Camera (") &&
-      !card.substr(12, 4).getAsInteger(16, vendor) &&
-      !card.substr(17, 4).getAsInteger(16, product)) {
-    wpi::SmallString<64> card2Buf;
-    wpi::StringRef card2 = GetUsbNameFromId(vendor, product, card2Buf);
+  std::optional<int> vendor;
+  std::optional<int> product;
+  if (wpi::starts_with(card, "UVC Camera (") &&
+      (vendor = wpi::parse_integer<int>(card.substr(12, 4), 16)) &&
+      (product = wpi::parse_integer<int>(card.substr(17, 4), 16))) {
+    std::string card2 = GetUsbNameFromId(vendor.value(), product.value());
     if (!card2.empty()) {
-      *desc = card2;
+      *desc = std::move(card2);
       return true;
     }
   }
@@ -225,7 +251,9 @@
 
 static bool IsVideoCaptureDevice(const char* cpath) {
   int fd = open(cpath, O_RDWR);
-  if (fd < 0) return false;
+  if (fd < 0) {
+    return false;
+  }
 
   struct v4l2_capability vcap;
   std::memset(&vcap, 0, sizeof(vcap));
@@ -243,25 +271,22 @@
 }
 
 static int GetDeviceNum(const char* cpath) {
-  wpi::StringRef path{cpath};
-  std::string pathBuf;
+  fs::path path{cpath};
 
   // it might be a symlink; if so, find the symlink target (e.g. /dev/videoN),
   // add that to the list and make it the keypath
-  if (wpi::sys::fs::is_symlink_file(cpath)) {
-    char* target = ::realpath(cpath, nullptr);
-    if (target) {
-      pathBuf = target;
-      path = pathBuf;
-      std::free(target);
-    }
+  if (fs::is_symlink(path)) {
+    path = fs::canonical(path);
   }
 
-  path = wpi::sys::path::filename(path);
-  if (!path.startswith("video")) return -1;
-  int dev = -1;
-  if (path.substr(5).getAsInteger(10, dev)) return -1;
-  return dev;
+  std::string fn = path.filename();
+  if (!wpi::starts_with(fn, "video")) {
+    return -1;
+  }
+  if (auto dev = wpi::parse_integer<int>(fn.substr(5), 10)) {
+    return dev.value();
+  }
+  return -1;
 }
 
 static std::string GetDescriptionImpl(const char* cpath) {
@@ -270,23 +295,27 @@
   int dev = GetDeviceNum(cpath);
   if (dev >= 0) {
     // Sometimes the /sys tree gives a better name.
-    if (GetDescriptionSysV4L(dev, &rv)) return rv;
+    if (GetDescriptionSysV4L(dev, &rv)) {
+      return rv;
+    }
   }
 
   // Otherwise use an ioctl to query the caps and get the card name
-  if (GetDescriptionIoctl(cpath, &rv)) return rv;
+  if (GetDescriptionIoctl(cpath, &rv)) {
+    return rv;
+  }
 
   return std::string{};
 }
 
-UsbCameraImpl::UsbCameraImpl(const wpi::Twine& name, wpi::Logger& logger,
+UsbCameraImpl::UsbCameraImpl(std::string_view name, wpi::Logger& logger,
                              Notifier& notifier, Telemetry& telemetry,
-                             const wpi::Twine& path)
+                             std::string_view path)
     : SourceImpl{name, logger, notifier, telemetry},
       m_fd{-1},
       m_command_fd{eventfd(0, 0)},
       m_active{true},
-      m_path{path.str()} {
+      m_path{path} {
   SetDescription(GetDescriptionImpl(m_path.c_str()));
   SetQuirks();
 
@@ -308,17 +337,23 @@
   Send(Message{Message::kNone});
 
   // join camera thread
-  if (m_cameraThread.joinable()) m_cameraThread.join();
+  if (m_cameraThread.joinable()) {
+    m_cameraThread.join();
+  }
 
   // close command fd
   int fd = m_command_fd.exchange(-1);
-  if (fd >= 0) close(fd);
+  if (fd >= 0) {
+    close(fd);
+  }
 }
 
 static inline void DoFdSet(int fd, fd_set* set, int* nfds) {
   if (fd >= 0) {
     FD_SET(fd, set);
-    if ((fd + 1) > *nfds) *nfds = fd + 1;
+    if ((fd + 1) > *nfds) {
+      *nfds = fd + 1;
+    }
   }
 }
 
@@ -341,8 +376,8 @@
       close(notify_fd);
       notify_fd = -1;
     } else {
-      notify_is.reset(new wpi::raw_fd_istream{
-          notify_fd, true, sizeof(struct inotify_event) + NAME_MAX + 1});
+      notify_is = std::make_unique<wpi::raw_fd_istream>(
+          notify_fd, true, sizeof(struct inotify_event) + NAME_MAX + 1);
     }
   }
   bool notified = (notify_fd < 0);  // treat as always notified if cannot notify
@@ -360,12 +395,16 @@
 
   while (m_active) {
     // If not connected, try to reconnect
-    if (m_fd < 0) DeviceConnect();
+    if (m_fd < 0) {
+      DeviceConnect();
+    }
 
     // Make copies of fd's in case they go away
     int command_fd = m_command_fd.load();
     int fd = m_fd.load();
-    if (!m_active) break;
+    if (!m_active) {
+      break;
+    }
 
     // Reset notified flag and restart streaming if necessary
     if (fd >= 0) {
@@ -398,20 +437,24 @@
     fd_set readfds;
     FD_ZERO(&readfds);
     DoFdSet(command_fd, &readfds, &nfds);
-    if (m_streaming) DoFdSet(fd, &readfds, &nfds);
+    if (m_streaming) {
+      DoFdSet(fd, &readfds, &nfds);
+    }
     DoFdSet(notify_fd, &readfds, &nfds);
 
     if (select(nfds, &readfds, nullptr, nullptr, &tv) < 0) {
-      SERROR("select(): " << std::strerror(errno));
+      SERROR("select(): {}", std::strerror(errno));
       break;  // XXX: is this the right thing to do here?
     }
 
     // Double-check to see if we're shutting down
-    if (!m_active) break;
+    if (!m_active) {
+      break;
+    }
 
     // Handle notify events
     if (notify_fd >= 0 && FD_ISSET(notify_fd, &readfds)) {
-      SDEBUG4("notify event");
+      SDEBUG4("{}", "notify event");
       struct inotify_event event;
       do {
         // Read the event structure
@@ -421,10 +464,9 @@
         raw_name.resize(event.len);
         notify_is->read(raw_name.data(), event.len);
         // If the name is what we expect...
-        wpi::StringRef name{raw_name.c_str()};
-        SDEBUG4("got event on '" << name << "' (" << name.size()
-                                 << ") compare to '" << base << "' ("
-                                 << base.size() << ") mask " << event.mask);
+        std::string_view name{raw_name.c_str()};
+        SDEBUG4("got event on '{}' ({}) compare to '{}' ({}) mask {}", name,
+                name.size(), base, base.size(), event.mask);
         if (name == base) {
           if ((event.mask & IN_DELETE) != 0) {
             wasStreaming = m_streaming;
@@ -441,7 +483,7 @@
 
     // Handle commands
     if (command_fd >= 0 && FD_ISSET(command_fd, &readfds)) {
-      SDEBUG4("got command");
+      SDEBUG4("{}", "got command");
       // Read it to clear
       eventfd_t val;
       eventfd_read(command_fd, &val);
@@ -451,7 +493,7 @@
 
     // Handle frames
     if (m_streaming && fd >= 0 && FD_ISSET(fd, &readfds)) {
-      SDEBUG4("grabbing image");
+      SDEBUG4("{}", "grabbing image");
 
       // Dequeue buffer
       struct v4l2_buffer buf;
@@ -459,7 +501,7 @@
       buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
       buf.memory = V4L2_MEMORY_MMAP;
       if (DoIoctl(fd, VIDIOC_DQBUF, &buf) != 0) {
-        SWARNING("could not dequeue buffer");
+        SWARNING("{}", "could not dequeue buffer");
         wasStreaming = m_streaming;
         DeviceStreamOff();
         DeviceDisconnect();
@@ -468,14 +510,14 @@
       }
 
       if ((buf.flags & V4L2_BUF_FLAG_ERROR) == 0) {
-        SDEBUG4("got image size=" << buf.bytesused << " index=" << buf.index);
+        SDEBUG4("got image size={} index={}", buf.bytesused, buf.index);
 
         if (buf.index >= kNumBuffers || !m_buffers[buf.index].m_data) {
-          SWARNING("invalid buffer" << buf.index);
+          SWARNING("invalid buffer {}", buf.index);
           continue;
         }
 
-        wpi::StringRef image{
+        std::string_view image{
             static_cast<const char*>(m_buffers[buf.index].m_data),
             static_cast<size_t>(buf.bytesused)};
         int width = m_mode.width;
@@ -483,7 +525,7 @@
         bool good = true;
         if (m_mode.pixelFormat == VideoMode::kMJPEG &&
             !GetJpegSize(image, &width, &height)) {
-          SWARNING("invalid JPEG image received from camera");
+          SWARNING("{}", "invalid JPEG image received from camera");
           good = false;
         }
         if (good) {
@@ -494,7 +536,7 @@
 
       // Requeue buffer
       if (DoIoctl(fd, VIDIOC_QBUF, &buf) != 0) {
-        SWARNING("could not requeue buffer");
+        SWARNING("{}", "could not requeue buffer");
         wasStreaming = m_streaming;
         DeviceStreamOff();
         DeviceDisconnect();
@@ -511,10 +553,14 @@
 
 void UsbCameraImpl::DeviceDisconnect() {
   int fd = m_fd.exchange(-1);
-  if (fd < 0) return;  // already disconnected
+  if (fd < 0) {
+    return;  // already disconnected
+  }
 
   // Unmap buffers
-  for (int i = 0; i < kNumBuffers; ++i) m_buffers[i] = UsbCameraBuffer{};
+  for (int i = 0; i < kNumBuffers; ++i) {
+    m_buffers[i] = UsbCameraBuffer{};
+  }
 
   // Close device
   close(fd);
@@ -524,67 +570,76 @@
 }
 
 void UsbCameraImpl::DeviceConnect() {
-  if (m_fd >= 0) return;
+  if (m_fd >= 0) {
+    return;
+  }
 
-  if (m_connectVerbose) SINFO("Connecting to USB camera on " << m_path);
+  if (m_connectVerbose) {
+    SINFO("Connecting to USB camera on {}", m_path);
+  }
 
   // Try to open the device
-  SDEBUG3("opening device");
+  SDEBUG3("{}", "opening device");
   int fd = open(m_path.c_str(), O_RDWR);
-  if (fd < 0) return;
+  if (fd < 0) {
+    return;
+  }
   m_fd = fd;
 
   // Get capabilities
-  SDEBUG3("getting capabilities");
+  SDEBUG3("{}", "getting capabilities");
   struct v4l2_capability vcap;
   std::memset(&vcap, 0, sizeof(vcap));
   if (DoIoctl(fd, VIDIOC_QUERYCAP, &vcap) >= 0) {
     m_capabilities = vcap.capabilities;
-    if (m_capabilities & V4L2_CAP_DEVICE_CAPS)
+    if (m_capabilities & V4L2_CAP_DEVICE_CAPS) {
       m_capabilities = vcap.device_caps;
+    }
   }
 
   // Get or restore video mode
   if (!m_properties_cached) {
-    SDEBUG3("caching properties");
+    SDEBUG3("{}", "caching properties");
     DeviceCacheProperties();
     DeviceCacheVideoModes();
     DeviceCacheMode();
     m_properties_cached = true;
   } else {
-    SDEBUG3("restoring video mode");
+    SDEBUG3("{}", "restoring video mode");
     DeviceSetMode();
     DeviceSetFPS();
 
     // Restore settings
-    SDEBUG3("restoring settings");
+    SDEBUG3("{}", "restoring settings");
     std::unique_lock lock2(m_mutex);
     for (size_t i = 0; i < m_propertyData.size(); ++i) {
       const auto prop =
           static_cast<const UsbCameraProperty*>(m_propertyData[i].get());
-      if (!prop || !prop->valueSet || !prop->device || prop->percentage)
+      if (!prop || !prop->valueSet || !prop->device || prop->percentage) {
         continue;
-      if (!prop->DeviceSet(lock2, m_fd))
-        SWARNING("failed to set property " << prop->name);
+      }
+      if (!prop->DeviceSet(lock2, m_fd)) {
+        SWARNING("failed to set property {}", prop->name);
+      }
     }
   }
 
   // Request buffers
-  SDEBUG3("allocating buffers");
+  SDEBUG3("{}", "allocating buffers");
   struct v4l2_requestbuffers rb;
   std::memset(&rb, 0, sizeof(rb));
   rb.count = kNumBuffers;
   rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   rb.memory = V4L2_MEMORY_MMAP;
   if (DoIoctl(fd, VIDIOC_REQBUFS, &rb) != 0) {
-    SWARNING("could not allocate buffers");
+    SWARNING("{}", "could not allocate buffers");
     close(fd);
     m_fd = -1;
     return;
   }
 
   // Map buffers
-  SDEBUG3("mapping buffers");
+  SDEBUG3("{}", "mapping buffers");
   for (int i = 0; i < kNumBuffers; ++i) {
     struct v4l2_buffer buf;
     std::memset(&buf, 0, sizeof(buf));
@@ -592,25 +647,26 @@
     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     buf.memory = V4L2_MEMORY_MMAP;
     if (DoIoctl(fd, VIDIOC_QUERYBUF, &buf) != 0) {
-      SWARNING("could not query buffer " << i);
+      SWARNING("could not query buffer {}", i);
       close(fd);
       m_fd = -1;
       return;
     }
-    SDEBUG4("buf " << i << " length=" << buf.length
-                   << " offset=" << buf.m.offset);
+    SDEBUG4("buf {} length={} offset={}", i, buf.length, buf.m.offset);
 
     m_buffers[i] = UsbCameraBuffer(fd, buf.length, buf.m.offset);
     if (!m_buffers[i].m_data) {
-      SWARNING("could not map buffer " << i);
+      SWARNING("could not map buffer {}", i);
       // release other buffers
-      for (int j = 0; j < i; ++j) m_buffers[j] = UsbCameraBuffer{};
+      for (int j = 0; j < i; ++j) {
+        m_buffers[j] = UsbCameraBuffer{};
+      }
       close(fd);
       m_fd = -1;
       return;
     }
 
-    SDEBUG4("buf " << i << " address=" << m_buffers[i].m_data);
+    SDEBUG4("buf {} address={}", i, m_buffers[i].m_data);
   }
 
   // Update description (as it may have changed)
@@ -624,12 +680,16 @@
 }
 
 bool UsbCameraImpl::DeviceStreamOn() {
-  if (m_streaming) return false;  // ignore if already enabled
+  if (m_streaming) {
+    return false;  // ignore if already enabled
+  }
   int fd = m_fd.load();
-  if (fd < 0) return false;
+  if (fd < 0) {
+    return false;
+  }
 
   // Queue buffers
-  SDEBUG3("queuing buffers");
+  SDEBUG3("{}", "queuing buffers");
   for (int i = 0; i < kNumBuffers; ++i) {
     struct v4l2_buffer buf;
     std::memset(&buf, 0, sizeof(buf));
@@ -637,7 +697,7 @@
     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     buf.memory = V4L2_MEMORY_MMAP;
     if (DoIoctl(fd, VIDIOC_QBUF, &buf) != 0) {
-      SWARNING("could not queue buffer " << i);
+      SWARNING("could not queue buffer {}", i);
       return false;
     }
   }
@@ -648,27 +708,34 @@
     if (errno == ENOSPC) {
       // indicates too much USB bandwidth requested
       SERROR(
+          "{}",
           "could not start streaming due to USB bandwidth limitations; try a "
           "lower resolution or a different pixel format (VIDIOC_STREAMON: "
           "No space left on device)");
     } else {
       // some other error
-      SERROR("ioctl VIDIOC_STREAMON failed: " << std::strerror(errno));
+      SERROR("ioctl VIDIOC_STREAMON failed: {}", std::strerror(errno));
     }
     return false;
   }
-  SDEBUG4("enabled streaming");
+  SDEBUG4("{}", "enabled streaming");
   m_streaming = true;
   return true;
 }
 
 bool UsbCameraImpl::DeviceStreamOff() {
-  if (!m_streaming) return false;  // ignore if already disabled
+  if (!m_streaming) {
+    return false;  // ignore if already disabled
+  }
   int fd = m_fd.load();
-  if (fd < 0) return false;
+  if (fd < 0) {
+    return false;
+  }
   int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-  if (DoIoctl(fd, VIDIOC_STREAMOFF, &type) != 0) return false;
-  SDEBUG4("disabled streaming");
+  if (DoIoctl(fd, VIDIOC_STREAMOFF, &type) != 0) {
+    return false;
+  }
+  SDEBUG4("{}", "disabled streaming");
   m_streaming = false;
   return true;
 }
@@ -706,12 +773,16 @@
     m_mode = newMode;
     lock.unlock();
     bool wasStreaming = m_streaming;
-    if (wasStreaming) DeviceStreamOff();
+    if (wasStreaming) {
+      DeviceStreamOff();
+    }
     if (m_fd >= 0) {
       DeviceDisconnect();
       DeviceConnect();
     }
-    if (wasStreaming) DeviceStreamOn();
+    if (wasStreaming) {
+      DeviceStreamOn();
+    }
     m_notifier.NotifySourceVideoMode(*this, newMode);
     lock.lock();
   } else if (newMode.fps != m_mode.fps) {
@@ -719,9 +790,13 @@
     lock.unlock();
     // Need to stop streaming to set FPS
     bool wasStreaming = m_streaming;
-    if (wasStreaming) DeviceStreamOff();
+    if (wasStreaming) {
+      DeviceStreamOff();
+    }
     DeviceSetFPS();
-    if (wasStreaming) DeviceStreamOn();
+    if (wasStreaming) {
+      DeviceStreamOn();
+    }
     m_notifier.NotifySourceVideoMode(*this, newMode);
     lock.lock();
   }
@@ -734,25 +809,29 @@
   bool setString = (msg.kind == Message::kCmdSetPropertyStr);
   int property = msg.data[0];
   int value = msg.data[1];
-  wpi::StringRef valueStr = msg.dataStr;
+  std::string_view valueStr = msg.dataStr;
 
   // Look up
   auto prop = static_cast<UsbCameraProperty*>(GetProperty(property));
-  if (!prop) return CS_INVALID_PROPERTY;
+  if (!prop) {
+    return CS_INVALID_PROPERTY;
+  }
 
   // If setting before we get, guess initial type based on set
   if (prop->propKind == CS_PROP_NONE) {
-    if (setString)
+    if (setString) {
       prop->propKind = CS_PROP_STRING;
-    else
+    } else {
       prop->propKind = CS_PROP_INTEGER;
+    }
   }
 
   // Check kind match
   if ((setString && prop->propKind != CS_PROP_STRING) ||
-      (!setString && (prop->propKind &
-                      (CS_PROP_BOOLEAN | CS_PROP_INTEGER | CS_PROP_ENUM)) == 0))
+      (!setString && (prop->propKind & (CS_PROP_BOOLEAN | CS_PROP_INTEGER |
+                                        CS_PROP_ENUM)) == 0)) {
     return CS_WRONG_PROPERTY_TYPE;
+  }
 
   // Handle percentage property
   int percentageProperty = prop->propPair;
@@ -769,17 +848,21 @@
 
   // Actually set the new value on the device (if possible)
   if (!prop->device) {
-    if (prop->id == kPropConnectVerboseId) m_connectVerbose = value;
+    if (prop->id == kPropConnectVerboseId) {
+      m_connectVerbose = value;
+    }
   } else {
-    if (!prop->DeviceSet(lock, m_fd, value, valueStr))
+    if (!prop->DeviceSet(lock, m_fd, value, valueStr)) {
       return CS_PROPERTY_WRITE_FAILED;
+    }
   }
 
   // Cache the set values
   UpdatePropertyValue(property, setString, value, valueStr);
-  if (percentageProperty != 0)
+  if (percentageProperty != 0) {
     UpdatePropertyValue(percentageProperty, setString, percentageValue,
                         valueStr);
+  }
 
   return CS_OK;
 }
@@ -790,12 +873,16 @@
   lock.unlock();
   // disconnect and reconnect
   bool wasStreaming = m_streaming;
-  if (wasStreaming) DeviceStreamOff();
+  if (wasStreaming) {
+    DeviceStreamOff();
+  }
   if (m_fd >= 0) {
     DeviceDisconnect();
     DeviceConnect();
   }
-  if (wasStreaming) DeviceStreamOn();
+  if (wasStreaming) {
+    DeviceStreamOn();
+  }
   lock.lock();
   return CS_OK;
 }
@@ -822,15 +909,18 @@
 
 void UsbCameraImpl::DeviceProcessCommands() {
   std::unique_lock lock(m_mutex);
-  if (m_commands.empty()) return;
+  if (m_commands.empty()) {
+    return;
+  }
   while (!m_commands.empty()) {
     auto msg = std::move(m_commands.back());
     m_commands.pop_back();
 
     CS_StatusValue status = DeviceProcessCommand(lock, msg);
     if (msg.kind != Message::kNumSinksChanged &&
-        msg.kind != Message::kNumSinksEnabledChanged)
+        msg.kind != Message::kNumSinksEnabledChanged) {
       m_responses.emplace_back(msg.from, status);
+    }
   }
   lock.unlock();
   m_responseCv.notify_all();
@@ -838,7 +928,9 @@
 
 void UsbCameraImpl::DeviceSetMode() {
   int fd = m_fd.load();
-  if (fd < 0) return;
+  if (fd < 0) {
+    return;
+  }
 
   struct v4l2_format vfmt;
   std::memset(&vfmt, 0, sizeof(vfmt));
@@ -851,43 +943,52 @@
   vfmt.fmt.pix.pixelformat =
       FromPixelFormat(static_cast<VideoMode::PixelFormat>(m_mode.pixelFormat));
   if (vfmt.fmt.pix.pixelformat == 0) {
-    SWARNING("could not set format " << m_mode.pixelFormat
-                                     << ", defaulting to MJPEG");
+    SWARNING("could not set format {}, defaulting to MJPEG",
+             m_mode.pixelFormat);
     vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
   }
   vfmt.fmt.pix.width = m_mode.width;
   vfmt.fmt.pix.height = m_mode.height;
   vfmt.fmt.pix.field = V4L2_FIELD_ANY;
   if (DoIoctl(fd, VIDIOC_S_FMT, &vfmt) != 0) {
-    SWARNING("could not set format " << m_mode.pixelFormat << " res "
-                                     << m_mode.width << "x" << m_mode.height);
+    SWARNING("could not set format {} res {}x{}", m_mode.pixelFormat,
+             m_mode.width, m_mode.height);
   } else {
-    SINFO("set format " << m_mode.pixelFormat << " res " << m_mode.width << "x"
-                        << m_mode.height);
+    SINFO("set format {} res {}x{}", m_mode.pixelFormat, m_mode.width,
+          m_mode.height);
   }
 }
 
 void UsbCameraImpl::DeviceSetFPS() {
   int fd = m_fd.load();
-  if (fd < 0) return;
+  if (fd < 0) {
+    return;
+  }
 
   struct v4l2_streamparm parm;
   std::memset(&parm, 0, sizeof(parm));
   parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-  if (DoIoctl(fd, VIDIOC_G_PARM, &parm) != 0) return;
-  if ((parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) == 0) return;
+  if (DoIoctl(fd, VIDIOC_G_PARM, &parm) != 0) {
+    return;
+  }
+  if ((parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) == 0) {
+    return;
+  }
   std::memset(&parm, 0, sizeof(parm));
   parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   parm.parm.capture.timeperframe = FPSToFract(m_mode.fps);
-  if (DoIoctl(fd, VIDIOC_S_PARM, &parm) != 0)
-    SWARNING("could not set FPS to " << m_mode.fps);
-  else
-    SINFO("set FPS to " << m_mode.fps);
+  if (DoIoctl(fd, VIDIOC_S_PARM, &parm) != 0) {
+    SWARNING("could not set FPS to {}", m_mode.fps);
+  } else {
+    SINFO("set FPS to {}", m_mode.fps);
+  }
 }
 
 void UsbCameraImpl::DeviceCacheMode() {
   int fd = m_fd.load();
-  if (fd < 0) return;
+  if (fd < 0) {
+    return;
+  }
 
   // Get format
   struct v4l2_format vfmt;
@@ -899,7 +1000,7 @@
 #endif
   vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   if (DoIoctl(fd, VIDIOC_G_FMT, &vfmt) != 0) {
-    SERROR("could not read current video mode");
+    SERROR("{}", "could not read current video mode");
     std::scoped_lock lock(m_mutex);
     m_mode = VideoMode{VideoMode::kMJPEG, 320, 240, 30};
     return;
@@ -914,8 +1015,9 @@
   std::memset(&parm, 0, sizeof(parm));
   parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   if (TryIoctl(fd, VIDIOC_G_PARM, &parm) == 0) {
-    if (parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)
+    if (parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
       fps = FractToFPS(parm.parm.capture.timeperframe);
+    }
   }
 
   // Update format with user changes.
@@ -946,7 +1048,9 @@
     // Default to lowest known resolution (based on number of total pixels)
     int numPixels = width * height;
     for (const auto& mode : m_videoModes) {
-      if (mode.pixelFormat != pixelFormat) continue;
+      if (mode.pixelFormat != pixelFormat) {
+        continue;
+      }
       int numPixelsHere = mode.width * mode.height;
       if (numPixelsHere < numPixels) {
         formatChanged = true;
@@ -973,8 +1077,12 @@
     m_mode.fps = fps;
   }
 
-  if (formatChanged) DeviceSetMode();
-  if (fpsChanged) DeviceSetFPS();
+  if (formatChanged) {
+    DeviceSetMode();
+  }
+  if (fpsChanged) {
+    DeviceSetFPS();
+  }
 
   m_notifier.NotifySourceVideoMode(*this, m_mode);
 }
@@ -1027,8 +1135,9 @@
     rawProp->valueStr = perProp->valueStr;  // copy
   } else {
     // Read current raw value and set percentage from it
-    if (!rawProp->DeviceGet(lock, m_fd))
-      SWARNING("failed to get property " << rawProp->name);
+    if (!rawProp->DeviceGet(lock, m_fd)) {
+      SWARNING("failed to get property {}", rawProp->name);
+    }
 
     if (perProp) {
       perProp->SetValue(RawToPercentage(*rawProp, rawProp->value));
@@ -1038,8 +1147,9 @@
 
   // Set value on device if user-configured
   if (rawProp->valueSet) {
-    if (!rawProp->DeviceSet(lock, m_fd))
-      SWARNING("failed to set property " << rawProp->name);
+    if (!rawProp->DeviceSet(lock, m_fd)) {
+      SWARNING("failed to set property {}", rawProp->name);
+    }
   }
 
   // Update pointers since we released the lock
@@ -1079,12 +1189,16 @@
   }
 
   NotifyPropertyCreated(*rawIndex, *rawPropPtr);
-  if (perPropPtr) NotifyPropertyCreated(*perIndex, *perPropPtr);
+  if (perPropPtr) {
+    NotifyPropertyCreated(*perIndex, *perPropPtr);
+  }
 }
 
 void UsbCameraImpl::DeviceCacheProperties() {
   int fd = m_fd.load();
-  if (fd < 0) return;
+  if (fd < 0) {
+    return;
+  }
 
 #ifdef V4L2_CTRL_FLAG_NEXT_COMPOUND
   constexpr __u32 nextFlags =
@@ -1102,20 +1216,24 @@
   if (id == nextFlags) {
     // try just enumerating standard...
     for (id = V4L2_CID_BASE; id < V4L2_CID_LASTP1; ++id) {
-      if (auto prop = UsbCameraProperty::DeviceQuery(fd, &id))
+      if (auto prop = UsbCameraProperty::DeviceQuery(fd, &id)) {
         DeviceCacheProperty(std::move(prop));
+      }
     }
     // ... and custom controls
     std::unique_ptr<UsbCameraProperty> prop;
     for (id = V4L2_CID_PRIVATE_BASE;
-         (prop = UsbCameraProperty::DeviceQuery(fd, &id)); ++id)
+         (prop = UsbCameraProperty::DeviceQuery(fd, &id)); ++id) {
       DeviceCacheProperty(std::move(prop));
+    }
   }
 }
 
 void UsbCameraImpl::DeviceCacheVideoModes() {
   int fd = m_fd.load();
-  if (fd < 0) return;
+  if (fd < 0) {
+    return;
+  }
 
   std::vector<VideoMode> modes;
 
@@ -1125,7 +1243,9 @@
   fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   for (fmt.index = 0; TryIoctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0; ++fmt.index) {
     VideoMode::PixelFormat pixelFormat = ToPixelFormat(fmt.pixelformat);
-    if (pixelFormat == VideoMode::kUnknown) continue;
+    if (pixelFormat == VideoMode::kUnknown) {
+      continue;
+    }
 
     // Frame sizes
     struct v4l2_frmsizeenum frmsize;
@@ -1133,7 +1253,9 @@
     frmsize.pixel_format = fmt.pixelformat;
     for (frmsize.index = 0; TryIoctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0;
          ++frmsize.index) {
-      if (frmsize.type != V4L2_FRMSIZE_TYPE_DISCRETE) continue;
+      if (frmsize.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
+        continue;
+      }
 
       // Frame intervals
       struct v4l2_frmivalenum frmival;
@@ -1144,7 +1266,9 @@
       for (frmival.index = 0;
            TryIoctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0;
            ++frmival.index) {
-        if (frmival.type != V4L2_FRMIVAL_TYPE_DISCRETE) continue;
+        if (frmival.type != V4L2_FRMIVAL_TYPE_DISCRETE) {
+          continue;
+        }
 
         modes.emplace_back(pixelFormat,
                            static_cast<int>(frmsize.discrete.width),
@@ -1183,7 +1307,9 @@
 CS_StatusValue UsbCameraImpl::SendAndWait(Message&& msg) const {
   int fd = m_command_fd.load();
   // exit early if not possible to signal
-  if (fd < 0) return CS_SOURCE_IS_DISCONNECTED;
+  if (fd < 0) {
+    return CS_SOURCE_IS_DISCONNECTED;
+  }
 
   auto from = msg.from;
 
@@ -1194,7 +1320,9 @@
   }
 
   // Signal the camera thread
-  if (eventfd_write(fd, 1) < 0) return CS_SOURCE_IS_DISCONNECTED;
+  if (eventfd_write(fd, 1) < 0) {
+    return CS_SOURCE_IS_DISCONNECTED;
+  }
 
   std::unique_lock lock(m_mutex);
   while (m_active) {
@@ -1220,7 +1348,9 @@
 void UsbCameraImpl::Send(Message&& msg) const {
   int fd = m_command_fd.load();
   // exit early if not possible to signal
-  if (fd < 0) return;
+  if (fd < 0) {
+    return;
+  }
 
   // Add the message to the command queue
   {
@@ -1233,14 +1363,16 @@
 }
 
 std::unique_ptr<PropertyImpl> UsbCameraImpl::CreateEmptyProperty(
-    const wpi::Twine& name) const {
+    std::string_view name) const {
   return std::make_unique<UsbCameraProperty>(name);
 }
 
 bool UsbCameraImpl::CacheProperties(CS_Status* status) const {
   // Wake up camera thread; this will try to reconnect
   *status = SendAndWait(Message{Message::kNone});
-  if (*status != CS_OK) return false;
+  if (*status != CS_OK) {
+    return false;
+  }
   if (!m_properties_cached) {
     *status = CS_SOURCE_IS_DISCONNECTED;
     return false;
@@ -1250,10 +1382,10 @@
 
 void UsbCameraImpl::SetQuirks() {
   wpi::SmallString<128> descbuf;
-  wpi::StringRef desc = GetDescription(descbuf);
-  m_lifecam_exposure =
-      desc.endswith("LifeCam HD-3000") || desc.endswith("LifeCam Cinema (TM)");
-  m_picamera = desc.startswith("mmal service");
+  std::string_view desc = GetDescription(descbuf);
+  m_lifecam_exposure = wpi::ends_with(desc, "LifeCam HD-3000") ||
+                       wpi::ends_with(desc, "LifeCam Cinema (TM)");
+  m_picamera = wpi::ends_with(desc, "mmal service");
 
   int deviceNum = GetDeviceNum(m_path.c_str());
   if (deviceNum >= 0) {
@@ -1271,11 +1403,11 @@
   *status = SendAndWait(std::move(msg));
 }
 
-void UsbCameraImpl::SetStringProperty(int property, const wpi::Twine& value,
+void UsbCameraImpl::SetStringProperty(int property, std::string_view value,
                                       CS_Status* status) {
   Message msg{Message::kCmdSetPropertyStr};
   msg.data[0] = property;
-  msg.dataStr = value.str();
+  msg.dataStr = value;
   *status = SendAndWait(std::move(msg));
 }
 
@@ -1395,9 +1527,9 @@
   Send(Message{Message::kNumSinksEnabledChanged});
 }
 
-void UsbCameraImpl::SetPath(const wpi::Twine& path, CS_Status* status) {
+void UsbCameraImpl::SetPath(std::string_view path, CS_Status* status) {
   Message msg{Message::kCmdSetPath};
-  msg.dataStr = path.str();
+  msg.dataStr = path;
   *status = SendAndWait(std::move(msg));
 }
 
@@ -1408,15 +1540,12 @@
 
 namespace cs {
 
-CS_Source CreateUsbCameraDev(const wpi::Twine& name, int dev,
+CS_Source CreateUsbCameraDev(std::string_view name, int dev,
                              CS_Status* status) {
-  wpi::SmallString<32> path;
-  wpi::raw_svector_ostream oss{path};
-  oss << "/dev/video" << dev;
-  return CreateUsbCameraPath(name, oss.str(), status);
+  return CreateUsbCameraPath(name, fmt::format("/dev/video{}", dev), status);
 }
 
-CS_Source CreateUsbCameraPath(const wpi::Twine& name, const wpi::Twine& path,
+CS_Source CreateUsbCameraPath(std::string_view name, std::string_view path,
                               CS_Status* status) {
   auto& inst = Instance::GetInstance();
   return inst.CreateSource(CS_SOURCE_USB, std::make_shared<UsbCameraImpl>(
@@ -1424,7 +1553,7 @@
                                               inst.telemetry, path));
 }
 
-void SetUsbCameraPath(CS_Source source, const wpi::Twine& path,
+void SetUsbCameraPath(CS_Source source, std::string_view path,
                       CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data || data->kind != CS_SOURCE_USB) {
@@ -1476,7 +1605,9 @@
           path += ep->d_name;
           char* target = ::realpath(path.c_str(), nullptr);
           if (target) {
-            if (keypath == target) info.otherPaths.emplace_back(path.str());
+            if (keypath == target) {
+              info.otherPaths.emplace_back(path.str());
+            }
             std::free(target);
           }
         }
@@ -1498,11 +1629,17 @@
 
   if (DIR* dp = ::opendir("/dev")) {
     while (struct dirent* ep = ::readdir(dp)) {
-      wpi::StringRef fname{ep->d_name};
-      if (!fname.startswith("video")) continue;
+      std::string_view fname{ep->d_name};
+      if (!wpi::starts_with(fname, "video")) {
+        continue;
+      }
 
       unsigned int dev = 0;
-      if (fname.substr(5).getAsInteger(10, dev)) continue;
+      if (auto v = wpi::parse_integer<unsigned int>(fname.substr(5), 10)) {
+        dev = v.value();
+      } else {
+        continue;
+      }
 
       UsbCameraInfo info;
       info.dev = dev;
@@ -1511,20 +1648,26 @@
       path += fname;
       info.path = path.str();
 
-      if (!IsVideoCaptureDevice(path.c_str())) continue;
+      if (!IsVideoCaptureDevice(path.c_str())) {
+        continue;
+      }
 
       info.name = GetDescriptionImpl(path.c_str());
-      if (info.name.empty()) continue;
+      if (info.name.empty()) {
+        continue;
+      }
 
       GetVendorProduct(dev, &info.vendorId, &info.productId);
 
-      if (dev >= retval.size()) retval.resize(info.dev + 1);
+      if (dev >= retval.size()) {
+        retval.resize(info.dev + 1);
+      }
       retval[info.dev] = std::move(info);
     }
     ::closedir(dp);
   } else {
     // *status = ;
-    WPI_ERROR(Instance::GetInstance().logger, "Could not open /dev");
+    WPI_ERROR(Instance::GetInstance().logger, "{}", "Could not open /dev");
     return retval;
   }
 
@@ -1540,11 +1683,12 @@
           path += ep->d_name;
           char* target = ::realpath(path.c_str(), nullptr);
           if (target) {
-            wpi::StringRef fname = wpi::sys::path::filename(target);
-            unsigned int dev = 0;
-            if (fname.startswith("video") &&
-                !fname.substr(5).getAsInteger(10, dev) && dev < retval.size()) {
-              retval[dev].otherPaths.emplace_back(path.str());
+            std::string fname = fs::path{target}.filename();
+            std::optional<unsigned int> dev;
+            if (wpi::starts_with(fname, "video") &&
+                (dev = wpi::parse_integer<unsigned int>(fname.substr(5), 10)) &&
+                dev.value() < retval.size()) {
+              retval[dev.value()].otherPaths.emplace_back(path.str());
             }
             std::free(target);
           }
diff --git a/cscore/src/main/native/linux/UsbCameraImpl.h b/cscore/src/main/native/linux/UsbCameraImpl.h
index 62b94a0..a032466 100644
--- a/cscore/src/main/native/linux/UsbCameraImpl.h
+++ b/cscore/src/main/native/linux/UsbCameraImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_USBCAMERAIMPL_H_
 #define CSCORE_USBCAMERAIMPL_H_
@@ -13,13 +10,12 @@
 #include <atomic>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <thread>
 #include <utility>
 #include <vector>
 
-#include <wpi/STLExtras.h>
 #include <wpi/SmallVector.h>
-#include <wpi/Twine.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
 #include <wpi/raw_istream.h>
@@ -36,15 +32,15 @@
 
 class UsbCameraImpl : public SourceImpl {
  public:
-  UsbCameraImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
-                Telemetry& telemetry, const wpi::Twine& path);
+  UsbCameraImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
+                Telemetry& telemetry, std::string_view path);
   ~UsbCameraImpl() override;
 
   void Start() override;
 
   // Property functions
   void SetProperty(int property, int value, CS_Status* status) override;
-  void SetStringProperty(int property, const wpi::Twine& value,
+  void SetStringProperty(int property, std::string_view value,
                          CS_Status* status) override;
 
   // Standard common camera properties
@@ -66,7 +62,7 @@
   void NumSinksChanged() override;
   void NumSinksEnabledChanged() override;
 
-  void SetPath(const wpi::Twine& path, CS_Status* status);
+  void SetPath(std::string_view path, CS_Status* status);
   std::string GetPath() const;
 
   // Messages passed to/from camera thread
@@ -98,7 +94,7 @@
 
  protected:
   std::unique_ptr<PropertyImpl> CreateEmptyProperty(
-      const wpi::Twine& name) const override;
+      std::string_view name) const override;
 
   // Cache properties.  Immediately successful if properties are already cached.
   // If they are not, tries to connect to the camera to do so; returns false and
diff --git a/cscore/src/main/native/linux/UsbCameraListener.cpp b/cscore/src/main/native/linux/UsbCameraListener.cpp
new file mode 100644
index 0000000..40e84c5
--- /dev/null
+++ b/cscore/src/main/native/linux/UsbCameraListener.cpp
@@ -0,0 +1,56 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "UsbCameraListener.h"
+
+#include <wpi/EventLoopRunner.h>
+#include <wpi/StringExtras.h>
+#include <wpi/uv/FsEvent.h>
+#include <wpi/uv/Timer.h>
+
+#include "Notifier.h"
+
+using namespace cs;
+
+class UsbCameraListener::Impl {
+ public:
+  explicit Impl(Notifier& notifier) : m_notifier(notifier) {}
+
+  Notifier& m_notifier;
+
+  std::unique_ptr<wpi::EventLoopRunner> m_runner;
+};
+
+UsbCameraListener::UsbCameraListener(wpi::Logger& logger, Notifier& notifier)
+    : m_impl(std::make_unique<Impl>(notifier)) {}
+
+UsbCameraListener::~UsbCameraListener() = default;
+
+void UsbCameraListener::Start() {
+  if (!m_impl->m_runner) {
+    m_impl->m_runner = std::make_unique<wpi::EventLoopRunner>();
+    m_impl->m_runner->ExecAsync([impl = m_impl.get()](wpi::uv::Loop& loop) {
+      auto refreshTimer = wpi::uv::Timer::Create(loop);
+      refreshTimer->timeout.connect([notifier = &impl->m_notifier] {
+        notifier->NotifyUsbCamerasChanged();
+      });
+      refreshTimer->Unreference();
+
+      auto devEvents = wpi::uv::FsEvent::Create(loop);
+      devEvents->fsEvent.connect([refreshTimer](const char* fn, int flags) {
+        if (wpi::starts_with(fn, "video")) {
+          refreshTimer->Start(wpi::uv::Timer::Time(200));
+        }
+      });
+      devEvents->Start("/dev");
+      devEvents->Unreference();
+    });
+  }
+}
+
+void UsbCameraListener::Stop() {
+  if (m_impl->m_runner) {
+    m_impl->m_runner.reset();
+  }
+}
diff --git a/cscore/src/main/native/linux/UsbCameraProperty.cpp b/cscore/src/main/native/linux/UsbCameraProperty.cpp
index 4dfa39c..149ca1b 100644
--- a/cscore/src/main/native/linux/UsbCameraProperty.cpp
+++ b/cscore/src/main/native/linux/UsbCameraProperty.cpp
@@ -1,14 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "UsbCameraProperty.h"
 
+#include <fmt/format.h>
 #include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
 
 #include "UsbUtil.h"
 
@@ -29,7 +26,9 @@
     ctrls.count = 1;
     ctrls.controls = &ctrl;
     int rc = DoIoctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls);
-    if (rc < 0) return rc;
+    if (rc < 0) {
+      return rc;
+    }
     *value = ctrl.value;
   } else {
     // Use normal control
@@ -37,7 +36,9 @@
     std::memset(&ctrl, 0, sizeof(ctrl));
     ctrl.id = id;
     int rc = DoIoctl(fd, VIDIOC_G_CTRL, &ctrl);
-    if (rc < 0) return rc;
+    if (rc < 0) {
+      return rc;
+    }
     *value = ctrl.value;
   }
   return 0;
@@ -54,10 +55,11 @@
     std::memset(&ctrl, 0, sizeof(ctrl));
     std::memset(&ctrls, 0, sizeof(ctrls));
     ctrl.id = id;
-    if (type == V4L2_CTRL_TYPE_INTEGER64)
+    if (type == V4L2_CTRL_TYPE_INTEGER64) {
       ctrl.value64 = value;
-    else
+    } else {
       ctrl.value = static_cast<__s32>(value);
+    }
     ctrls.ctrl_class = ctrl_class;
     ctrls.count = 1;
     ctrls.controls = &ctrl;
@@ -90,15 +92,8 @@
 }
 
 static int SetStringCtrlIoctl(int fd, int id, int maximum,
-                              const wpi::Twine& value) {
-  wpi::SmallString<64> strBuf, strBuf2;
-  wpi::StringRef str = value.toNullTerminatedStringRef(strBuf);
-  if (str.size() > static_cast<size_t>(maximum)) {
-    // don't know if strBuf was used, just recopy
-    strBuf2 = str.take_front(maximum);
-    str = strBuf2;
-    strBuf2.push_back('\0');  // null terminate
-  }
+                              std::string_view value) {
+  wpi::SmallString<64> str{value.substr(0, maximum)};
 
   struct v4l2_ext_control ctrl;
   struct v4l2_ext_controls ctrls;
@@ -106,7 +101,7 @@
   std::memset(&ctrls, 0, sizeof(ctrls));
   ctrl.id = id;
   ctrl.size = str.size();
-  ctrl.string = const_cast<char*>(str.data());
+  ctrl.string = const_cast<char*>(str.c_str());
   ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(id);
   ctrls.count = 1;
   ctrls.controls = &ctrl;
@@ -115,25 +110,26 @@
 
 // Removes non-alphanumeric characters and replaces spaces with underscores.
 // e.g. "Zoom, Absolute" -> "zoom_absolute", "Pan (Absolute)" -> "pan_absolute"
-static wpi::StringRef NormalizeName(wpi::StringRef name,
-                                    wpi::SmallVectorImpl<char>& buf) {
+static std::string_view NormalizeName(std::string_view name,
+                                      wpi::SmallVectorImpl<char>& buf) {
   bool newWord = false;
   for (auto ch : name) {
     if (std::isalnum(ch)) {
-      if (newWord) buf.push_back('_');
+      if (newWord) {
+        buf.push_back('_');
+      }
       newWord = false;
       buf.push_back(std::tolower(ch));
     } else if (!buf.empty()) {
       newWord = true;
     }
   }
-  return wpi::StringRef(buf.data(), buf.size());
+  return {buf.data(), buf.size()};
 }
 
 #ifdef VIDIOC_QUERY_EXT_CTRL
 UsbCameraProperty::UsbCameraProperty(const struct v4l2_query_ext_ctrl& ctrl)
-    : PropertyImpl(wpi::StringRef{}, CS_PROP_NONE, ctrl.step,
-                   ctrl.default_value, 0),
+    : PropertyImpl({}, CS_PROP_NONE, ctrl.step, ctrl.default_value, 0),
       id(ctrl.id & V4L2_CTRL_ID_MASK),
       type(ctrl.type) {
   hasMinimum = true;
@@ -166,15 +162,16 @@
 
   // name
   size_t len = 0;
-  while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') ++len;
+  while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') {
+    ++len;
+  }
   wpi::SmallString<64> name_buf;
-  name = NormalizeName(wpi::StringRef(ctrl.name, len), name_buf);
+  name = NormalizeName({ctrl.name, len}, name_buf);
 }
 #endif
 
 UsbCameraProperty::UsbCameraProperty(const struct v4l2_queryctrl& ctrl)
-    : PropertyImpl(wpi::StringRef{}, CS_PROP_NONE, ctrl.step,
-                   ctrl.default_value, 0),
+    : PropertyImpl({}, CS_PROP_NONE, ctrl.step, ctrl.default_value, 0),
       id(ctrl.id & V4L2_CTRL_ID_MASK),
       type(ctrl.type) {
   hasMinimum = true;
@@ -204,10 +201,12 @@
 
   // name
   size_t len = 0;
-  while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') ++len;
+  while (len < sizeof(ctrl.name) && ctrl.name[len] != '\0') {
+    ++len;
+  }
   wpi::SmallString<64> name_buf;
-  name = NormalizeName(
-      wpi::StringRef(reinterpret_cast<const char*>(ctrl.name), len), name_buf);
+  name =
+      NormalizeName({reinterpret_cast<const char*>(ctrl.name), len}, name_buf);
 }
 
 std::unique_ptr<UsbCameraProperty> UsbCameraProperty::DeviceQuery(int fd,
@@ -222,7 +221,9 @@
   if (rc == 0) {
     *id = qc_ext.id;  // copy back
     // We don't support array types
-    if (qc_ext.elems > 1 || qc_ext.nr_of_dims > 0) return nullptr;
+    if (qc_ext.elems > 1 || qc_ext.nr_of_dims > 0) {
+      return nullptr;
+    }
     prop = std::make_unique<UsbCameraProperty>(qc_ext);
   }
 #endif
@@ -233,7 +234,9 @@
     qc.id = *id;
     rc = TryIoctl(fd, VIDIOC_QUERYCTRL, &qc);
     *id = qc.id;  // copy back
-    if (rc != 0) return nullptr;
+    if (rc != 0) {
+      return nullptr;
+    }
     prop = std::make_unique<UsbCameraProperty>(qc);
   }
 
@@ -245,10 +248,11 @@
     qmenu.id = *id;
     for (int i = prop->minimum; i <= prop->maximum; ++i) {
       qmenu.index = static_cast<__u32>(i);
-      if (TryIoctl(fd, VIDIOC_QUERYMENU, &qmenu) != 0) continue;
+      if (TryIoctl(fd, VIDIOC_QUERYMENU, &qmenu) != 0) {
+        continue;
+      }
       if (prop->intMenu) {
-        wpi::raw_string_ostream os(prop->enumChoices[i]);
-        os << qmenu.value;
+        prop->enumChoices[i] = fmt::to_string(qmenu.value);
       } else {
         prop->enumChoices[i] = reinterpret_cast<const char*>(qmenu.name);
       }
@@ -259,7 +263,9 @@
 }
 
 bool UsbCameraProperty::DeviceGet(std::unique_lock<wpi::mutex>& lock, int fd) {
-  if (fd < 0) return true;
+  if (fd < 0) {
+    return true;
+  }
   unsigned idCopy = id;
   int rv = 0;
 
@@ -272,7 +278,9 @@
       lock.unlock();
       rv = GetIntCtrlIoctl(fd, idCopy, typeCopy, &newValue);
       lock.lock();
-      if (rv >= 0) value = newValue;
+      if (rv >= 0) {
+        value = newValue;
+      }
       break;
     }
     case CS_PROP_STRING: {
@@ -281,7 +289,9 @@
       lock.unlock();
       rv = GetStringCtrlIoctl(fd, idCopy, maximumCopy, &newValueStr);
       lock.lock();
-      if (rv >= 0) valueStr = std::move(newValueStr);
+      if (rv >= 0) {
+        valueStr = std::move(newValueStr);
+      }
       break;
     }
     default:
@@ -295,13 +305,15 @@
                                   int fd) const {
   // Make a copy of the string as we're about to release the lock
   wpi::SmallString<128> valueStrCopy{valueStr};
-  return DeviceSet(lock, fd, value, valueStrCopy);
+  return DeviceSet(lock, fd, value, valueStrCopy.str());
 }
 
 bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock, int fd,
                                   int newValue,
-                                  const wpi::Twine& newValueStr) const {
-  if (!device || fd < 0) return true;
+                                  std::string_view newValueStr) const {
+  if (!device || fd < 0) {
+    return true;
+  }
   unsigned idCopy = id;
   int rv = 0;
 
diff --git a/cscore/src/main/native/linux/UsbCameraProperty.h b/cscore/src/main/native/linux/UsbCameraProperty.h
index 32c5e19..b0291a1 100644
--- a/cscore/src/main/native/linux/UsbCameraProperty.h
+++ b/cscore/src/main/native/linux/UsbCameraProperty.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_USBCAMERAPROPERTY_H_
 #define CSCORE_USBCAMERAPROPERTY_H_
@@ -11,6 +8,7 @@
 #include <linux/videodev2.h>
 
 #include <memory>
+#include <string_view>
 
 #include <wpi/mutex.h>
 
@@ -22,19 +20,19 @@
 class UsbCameraProperty : public PropertyImpl {
  public:
   UsbCameraProperty() = default;
-  explicit UsbCameraProperty(const wpi::Twine& name_) : PropertyImpl{name_} {}
+  explicit UsbCameraProperty(std::string_view name_) : PropertyImpl{name_} {}
 
   // Software property constructor
-  UsbCameraProperty(const wpi::Twine& name_, unsigned id_,
-                    CS_PropertyKind kind_, int minimum_, int maximum_,
-                    int step_, int defaultValue_, int value_)
+  UsbCameraProperty(std::string_view name_, unsigned id_, CS_PropertyKind kind_,
+                    int minimum_, int maximum_, int step_, int defaultValue_,
+                    int value_)
       : PropertyImpl(name_, kind_, minimum_, maximum_, step_, defaultValue_,
                      value_),
         device{false},
         id{id_} {}
 
   // Normalized property constructor
-  UsbCameraProperty(const wpi::Twine& name_, int rawIndex_,
+  UsbCameraProperty(std::string_view name_, int rawIndex_,
                     const UsbCameraProperty& rawProp, int defaultValue_,
                     int value_)
       : PropertyImpl(name_, rawProp.propKind, 1, defaultValue_, value_),
@@ -58,7 +56,7 @@
   bool DeviceGet(std::unique_lock<wpi::mutex>& lock, int fd);
   bool DeviceSet(std::unique_lock<wpi::mutex>& lock, int fd) const;
   bool DeviceSet(std::unique_lock<wpi::mutex>& lock, int fd, int newValue,
-                 const wpi::Twine& newValueStr) const;
+                 std::string_view newValueStr) const;
 
   // If this is a device (rather than software) property
   bool device{true};
diff --git a/cscore/src/main/native/linux/UsbUtil.cpp b/cscore/src/main/native/linux/UsbUtil.cpp
index ac48178..afe3e42 100644
--- a/cscore/src/main/native/linux/UsbUtil.cpp
+++ b/cscore/src/main/native/linux/UsbUtil.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "UsbUtil.h"
 
@@ -11,8 +8,10 @@
 #include <libgen.h>
 #include <sys/ioctl.h>
 
-#include <wpi/Format.h>
+#include <fmt/format.h>
 #include <wpi/SmallString.h>
+#include <wpi/StringExtras.h>
+#include <wpi/fs.h>
 #include <wpi/raw_istream.h>
 #include <wpi/raw_ostream.h>
 
@@ -21,139 +20,142 @@
 
 namespace cs {
 
-static wpi::StringRef GetUsbNameFromFile(int vendor, int product,
-                                         wpi::SmallVectorImpl<char>& buf) {
+static std::string GetUsbNameFromFile(int vendor, int product) {
   int fd = open("/var/lib/usbutils/usb.ids", O_RDONLY);
-  if (fd < 0) return wpi::StringRef{};
+  if (fd < 0) {
+    return {};
+  }
 
-  wpi::raw_svector_ostream os{buf};
+  wpi::SmallString<128> buf;
   wpi::raw_fd_istream is{fd, true};
 
   // build vendor and product 4-char hex strings
-  wpi::SmallString<16> vendorStr, productStr;
-  wpi::raw_svector_ostream vendorOs{vendorStr}, productOs{productStr};
-  vendorOs << wpi::format_hex_no_prefix(vendor, 4);
-  productOs << wpi::format_hex_no_prefix(product, 4);
+  auto vendorStr = fmt::format("{:04x}", vendor);
+  auto productStr = fmt::format("{:04x}", product);
 
   // scan file
   wpi::SmallString<128> lineBuf;
   bool foundVendor = false;
   for (;;) {
     auto line = is.getline(lineBuf, 4096);
-    if (is.has_error()) break;
+    if (is.has_error()) {
+      break;
+    }
 
-    if (line.empty()) continue;
+    if (line.empty()) {
+      continue;
+    }
 
     // look for vendor at start of line
-    if (line.startswith(vendorStr)) {
+    if (wpi::starts_with(line, vendorStr)) {
       foundVendor = true;
-      os << line.substr(5).trim() << ' ';
+      buf += wpi::trim(line.substr(5));
+      buf += ' ';
       continue;
     }
 
     if (foundVendor) {
       // next vendor, but didn't match product?
       if (line[0] != '\t') {
-        os << "Unknown";
-        return os.str();
+        buf += "Unknown";
+        return buf;
       }
 
       // look for product
-      if (line.substr(1).startswith(productStr)) {
-        os << line.substr(6).trim();
-        return os.str();
+      if (wpi::starts_with(line.substr(1), productStr)) {
+        buf += wpi::trim(line.substr(6));
+        return buf;
       }
     }
   }
 
-  return wpi::StringRef{};
+  return {};
 }
 
-wpi::StringRef GetUsbNameFromId(int vendor, int product,
-                                wpi::SmallVectorImpl<char>& buf) {
+std::string GetUsbNameFromId(int vendor, int product) {
   // try reading usb.ids
-  wpi::StringRef rv = GetUsbNameFromFile(vendor, product, buf);
-  if (!rv.empty()) return rv;
-
-  // Fall back to internal database
-  wpi::raw_svector_ostream os{buf};
-  switch (vendor) {
-    case 0x046d:
-      os << "Logitech, Inc. ";
-      switch (product) {
-        case 0x0802:
-          os << "Webcam C200";
-          break;
-        case 0x0804:
-          os << "Webcam C250";
-          break;
-        case 0x0805:
-          os << "Webcam C300";
-          break;
-        case 0x0807:
-          os << "Webcam B500";
-          break;
-        case 0x0808:
-          os << "Webcam C600";
-          break;
-        case 0x0809:
-          os << "Webcam Pro 9000";
-          break;
-        case 0x080a:
-          os << "Portable Webcam C905";
-          break;
-        case 0x080f:
-          os << "Webcam C120";
-          break;
-        case 0x0819:
-          os << "Webcam C210";
-          break;
-        case 0x081b:
-          os << "Webcam C310";
-          break;
-        case 0x081d:
-          os << "HD Webcam C510";
-          break;
-        case 0x0821:
-          os << "HD Webcam C910";
-          break;
-        case 0x0825:
-          os << "Webcam C270";
-          break;
-        case 0x0826:
-          os << "HD Webcam C525";
-          break;
-        case 0x0828:
-          os << "HD Webcam B990";
-          break;
-        case 0x082b:
-          os << "Webcam C170";
-          break;
-        case 0x082d:
-          os << "HD Pro Webcam C920";
-          break;
-        case 0x0836:
-          os << "B525 HD Webcam";
-          break;
-        case 0x0843:
-          os << "Webcam C930e";
-          break;
-      }
-      break;
+  std::string rv = GetUsbNameFromFile(vendor, product);
+  if (!rv.empty()) {
+    return rv;
   }
 
-  return os.str();
+  // Fall back to internal database
+  switch (vendor) {
+    case 0x046d: {
+      std::string_view productStr;
+      switch (product) {
+        case 0x0802:
+          productStr = "Webcam C200";
+          break;
+        case 0x0804:
+          productStr = "Webcam C250";
+          break;
+        case 0x0805:
+          productStr = "Webcam C300";
+          break;
+        case 0x0807:
+          productStr = "Webcam B500";
+          break;
+        case 0x0808:
+          productStr = "Webcam C600";
+          break;
+        case 0x0809:
+          productStr = "Webcam Pro 9000";
+          break;
+        case 0x080a:
+          productStr = "Portable Webcam C905";
+          break;
+        case 0x080f:
+          productStr = "Webcam C120";
+          break;
+        case 0x0819:
+          productStr = "Webcam C210";
+          break;
+        case 0x081b:
+          productStr = "Webcam C310";
+          break;
+        case 0x081d:
+          productStr = "HD Webcam C510";
+          break;
+        case 0x0821:
+          productStr = "HD Webcam C910";
+          break;
+        case 0x0825:
+          productStr = "Webcam C270";
+          break;
+        case 0x0826:
+          productStr = "HD Webcam C525";
+          break;
+        case 0x0828:
+          productStr = "HD Webcam B990";
+          break;
+        case 0x082b:
+          productStr = "Webcam C170";
+          break;
+        case 0x082d:
+          productStr = "HD Pro Webcam C920";
+          break;
+        case 0x0836:
+          productStr = "B525 HD Webcam";
+          break;
+        case 0x0843:
+          productStr = "Webcam C930e";
+          break;
+      }
+      return fmt::format("Logitech, Inc. {}", productStr);
+    }
+  }
+
+  return {};
 }
 
 int CheckedIoctl(int fd, unsigned long req, void* data,  // NOLINT(runtime/int)
                  const char* name, const char* file, int line, bool quiet) {
   int retval = ioctl(fd, req, data);
   if (!quiet && retval < 0) {
-    wpi::SmallString<64> localfile{file};
-    localfile.push_back('\0');
-    WPI_ERROR(Instance::GetInstance().logger,
-              "ioctl " << name << " failed at " << basename(localfile.data())
-                       << ":" << line << ": " << std::strerror(errno));
+    WPI_ERROR(Instance::GetInstance().logger, "ioctl {} failed at {}:{}: {}",
+              name, fs::path{file}.filename().string(), line,
+              std::strerror(errno));
   }
   return retval;
 }
diff --git a/cscore/src/main/native/linux/UsbUtil.h b/cscore/src/main/native/linux/UsbUtil.h
index 4f1d9d8..9f6f8f5 100644
--- a/cscore/src/main/native/linux/UsbUtil.h
+++ b/cscore/src/main/native/linux/UsbUtil.h
@@ -1,22 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_USBUTIL_H_
 #define CSCORE_USBUTIL_H_
 
-#include <stdint.h>
-
-#include <wpi/SmallVector.h>
-#include <wpi/StringRef.h>
+#include <string>
 
 namespace cs {
 
-wpi::StringRef GetUsbNameFromId(int vendor, int product,
-                                wpi::SmallVectorImpl<char>& buf);
+std::string GetUsbNameFromId(int vendor, int product);
 
 int CheckedIoctl(int fd, unsigned long req, void* data,  // NOLINT(runtime/int)
                  const char* name, const char* file, int line, bool quiet);
diff --git a/cscore/src/main/native/osx/NetworkListener.cpp b/cscore/src/main/native/osx/NetworkListener.cpp
index 0716789..3d77fba 100644
--- a/cscore/src/main/native/osx/NetworkListener.cpp
+++ b/cscore/src/main/native/osx/NetworkListener.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "NetworkListener.h"
 
@@ -13,7 +10,7 @@
 
 NetworkListener::NetworkListener(wpi::Logger& logger, Notifier& notifier) {}
 
-NetworkListener::~NetworkListener() {}
+NetworkListener::~NetworkListener() = default;
 
 void NetworkListener::Start() {}
 
diff --git a/cscore/src/main/native/osx/NetworkUtil.cpp b/cscore/src/main/native/osx/NetworkUtil.cpp
index 935c21f..0aaf535 100644
--- a/cscore/src/main/native/osx/NetworkUtil.cpp
+++ b/cscore/src/main/native/osx/NetworkUtil.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "cscore_cpp.h"
 
diff --git a/cscore/src/main/native/osx/UsbCameraImpl.cpp b/cscore/src/main/native/osx/UsbCameraImpl.cpp
index abefcc7..3b5d821 100644
--- a/cscore/src/main/native/osx/UsbCameraImpl.cpp
+++ b/cscore/src/main/native/osx/UsbCameraImpl.cpp
@@ -1,27 +1,24 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "cscore_cpp.h"
 
 namespace cs {
 
-CS_Source CreateUsbCameraDev(const wpi::Twine& name, int dev,
+CS_Source CreateUsbCameraDev(std::string_view name, int dev,
                              CS_Status* status) {
   *status = CS_INVALID_HANDLE;
   return 0;
 }
 
-CS_Source CreateUsbCameraPath(const wpi::Twine& name, const wpi::Twine& path,
+CS_Source CreateUsbCameraPath(std::string_view name, std::string_view path,
                               CS_Status* status) {
   *status = CS_INVALID_HANDLE;
   return 0;
 }
 
-void SetUsbCameraPath(CS_Source source, const wpi::Twine& path,
+void SetUsbCameraPath(CS_Source source, std::string_view path,
                       CS_Status* status) {
   *status = CS_INVALID_HANDLE;
 }
diff --git a/cscore/src/main/native/osx/UsbCameraListener.cpp b/cscore/src/main/native/osx/UsbCameraListener.cpp
new file mode 100644
index 0000000..b83663d
--- /dev/null
+++ b/cscore/src/main/native/osx/UsbCameraListener.cpp
@@ -0,0 +1,17 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "UsbCameraListener.h"
+
+using namespace cs;
+
+class UsbCameraListener::Impl {};
+
+UsbCameraListener::UsbCameraListener(wpi::Logger& logger, Notifier& notifier) {}
+
+UsbCameraListener::~UsbCameraListener() = default;
+
+void UsbCameraListener::Start() {}
+
+void UsbCameraListener::Stop() {}
diff --git a/cscore/src/main/native/windows/COMCreators.cpp b/cscore/src/main/native/windows/COMCreators.cpp
index 265f7d3..f9f5cae 100644
--- a/cscore/src/main/native/windows/COMCreators.cpp
+++ b/cscore/src/main/native/windows/COMCreators.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include <mfapi.h>
 #include <mfidl.h>
@@ -59,9 +56,13 @@
   return uCount;
 }
 
-STDMETHODIMP SourceReaderCB::OnEvent(DWORD, IMFMediaEvent*) { return S_OK; }
+STDMETHODIMP SourceReaderCB::OnEvent(DWORD, IMFMediaEvent*) {
+  return S_OK;
+}
 
-STDMETHODIMP SourceReaderCB::OnFlush(DWORD) { return S_OK; }
+STDMETHODIMP SourceReaderCB::OnFlush(DWORD) {
+  return S_OK;
+}
 
 void SourceReaderCB::NotifyError(HRESULT hr) {
   wprintf(L"Source Reader error: 0x%X\n", hr);
@@ -73,7 +74,8 @@
                                           IMFSample* pSample  // Can be NULL
 ) {
   auto source = m_source.lock();
-  if (!source) return S_OK;
+  if (!source)
+    return S_OK;
   if (SUCCEEDED(hrStatus)) {
     if (pSample) {
       // Prcoess sample
diff --git a/cscore/src/main/native/windows/COMCreators.h b/cscore/src/main/native/windows/COMCreators.h
index f89e6fd..84cb982 100644
--- a/cscore/src/main/native/windows/COMCreators.h
+++ b/cscore/src/main/native/windows/COMCreators.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #pragma once
 
@@ -42,7 +39,7 @@
 
  private:
   // Destructor is private. Caller should call Release.
-  virtual ~SourceReaderCB() {}
+  virtual ~SourceReaderCB() = default;
   void NotifyError(HRESULT hr);
 
   ULONG m_nRefCount;
diff --git a/cscore/src/main/native/windows/ComPtr.h b/cscore/src/main/native/windows/ComPtr.h
index 22a330d..2d9f241 100644
--- a/cscore/src/main/native/windows/ComPtr.h
+++ b/cscore/src/main/native/windows/ComPtr.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #pragma once
 
@@ -27,24 +24,24 @@
   template <typename T>
   friend class ComPtr;
 
-  ComPtr(std::nullptr_t = nullptr) noexcept {}  // NOLINT(runtime/explicit)
+  ComPtr(std::nullptr_t = nullptr) noexcept {}  // NOLINT
   ComPtr(const ComPtr& other) noexcept : m_ptr(other.m_ptr) {
     InternalAddRef();
   }
 
   template <typename T>
-  ComPtr(const ComPtr<T>& other) noexcept : m_ptr(other.m_ptr) {
+  ComPtr(const ComPtr<T>& other) noexcept : m_ptr(other.m_ptr) {  // NOLINT
     InternalAddRef();
   }
 
   template <typename T>
-  ComPtr(ComPtr<T>&& other) noexcept : m_ptr(other.m_ptr) {
+  ComPtr(ComPtr<T>&& other) noexcept : m_ptr(other.m_ptr) {  // NOLINT
     other.m_ptr = nullptr;
   }
 
   ~ComPtr() noexcept { InternalRelease(); }
 
-  ComPtr& operator=(const ComPtr& other) noexcept {
+  ComPtr& operator=(const ComPtr& other) noexcept {  // NOLINT
     InternalCopy(other.m_ptr);
     return *this;
   }
diff --git a/cscore/src/main/native/windows/NetworkListener.cpp b/cscore/src/main/native/windows/NetworkListener.cpp
index cd29e44..b4fbb1e 100644
--- a/cscore/src/main/native/windows/NetworkListener.cpp
+++ b/cscore/src/main/native/windows/NetworkListener.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "NetworkListener.h"
 
@@ -47,7 +44,9 @@
 NetworkListener::NetworkListener(wpi::Logger& logger, Notifier& notifier)
     : m_impl(std::make_unique<Impl>(logger, notifier)) {}
 
-NetworkListener::~NetworkListener() { Stop(); }
+NetworkListener::~NetworkListener() {
+  Stop();
+}
 
 void NetworkListener::Start() {
   NotifyIpInterfaceChange(AF_INET, OnInterfaceChange, &m_impl->m_notifier, true,
diff --git a/cscore/src/main/native/windows/NetworkUtil.cpp b/cscore/src/main/native/windows/NetworkUtil.cpp
index 151ba79..86b99bc 100644
--- a/cscore/src/main/native/windows/NetworkUtil.cpp
+++ b/cscore/src/main/native/windows/NetworkUtil.cpp
@@ -1,11 +1,9 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include <uv.h>
+#include <ws2tcpip.h>
 
 #include "cscore_cpp.h"
 
@@ -24,7 +22,9 @@
   char ip[50];
 
   for (int i = 0; i < counts; i++) {
-    if (adrs[i].is_internal) continue;
+    if (adrs[i].is_internal) {
+      continue;
+    }
     InetNtop(PF_INET, &(adrs[i].netmask.netmask4.sin_addr.s_addr), ip,
              sizeof(ip) - 1);
     ip[49] = '\0';
diff --git a/cscore/src/main/native/windows/UsbCameraImpl.cpp b/cscore/src/main/native/windows/UsbCameraImpl.cpp
index 784f5ad..1a3b230 100644
--- a/cscore/src/main/native/windows/UsbCameraImpl.cpp
+++ b/cscore/src/main/native/windows/UsbCameraImpl.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #define _WINSOCKAPI_
 #include "UsbCameraImpl.h"
@@ -29,8 +26,7 @@
 #include <opencv2/highgui/highgui.hpp>
 #include <opencv2/imgproc/imgproc.hpp>
 #include <wpi/MemAlloc.h>
-#include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
+#include <wpi/StringExtras.h>
 #include <wpi/timestamp.h>
 
 #include "COMCreators.h"
@@ -72,24 +68,26 @@
 
 namespace cs {
 
-UsbCameraImpl::UsbCameraImpl(const wpi::Twine& name, wpi::Logger& logger,
+UsbCameraImpl::UsbCameraImpl(std::string_view name, wpi::Logger& logger,
                              Notifier& notifier, Telemetry& telemetry,
-                             const wpi::Twine& path)
-    : SourceImpl{name, logger, notifier, telemetry}, m_path{path.str()} {
+                             std::string_view path)
+    : SourceImpl{name, logger, notifier, telemetry}, m_path{path} {
   std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
   m_widePath = utf8_conv.from_bytes(m_path.c_str());
   m_deviceId = -1;
   StartMessagePump();
 }
 
-UsbCameraImpl::UsbCameraImpl(const wpi::Twine& name, wpi::Logger& logger,
+UsbCameraImpl::UsbCameraImpl(std::string_view name, wpi::Logger& logger,
                              Notifier& notifier, Telemetry& telemetry,
                              int deviceId)
     : SourceImpl{name, logger, notifier, telemetry}, m_deviceId(deviceId) {
   StartMessagePump();
 }
 
-UsbCameraImpl::~UsbCameraImpl() { m_messagePump = nullptr; }
+UsbCameraImpl::~UsbCameraImpl() {
+  m_messagePump = nullptr;
+}
 
 void UsbCameraImpl::SetProperty(int property, int value, CS_Status* status) {
   Message msg{Message::kCmdSetProperty};
@@ -100,11 +98,11 @@
           SetCameraMessage, msg.kind, &msg);
   *status = result;
 }
-void UsbCameraImpl::SetStringProperty(int property, const wpi::Twine& value,
+void UsbCameraImpl::SetStringProperty(int property, std::string_view value,
                                       CS_Status* status) {
   Message msg{Message::kCmdSetPropertyStr};
   msg.data[0] = property;
-  msg.dataStr = value.str();
+  msg.dataStr = value;
   auto result =
       m_messagePump->SendWindowMessage<CS_Status, Message::Kind, Message*>(
           SetCameraMessage, msg.kind, &msg);
@@ -198,9 +196,9 @@
       SetCameraMessage, Message::kNumSinksEnabledChanged, nullptr);
 }
 
-void UsbCameraImpl::SetPath(const wpi::Twine& path, CS_Status* status) {
+void UsbCameraImpl::SetPath(std::string_view path, CS_Status* status) {
   Message msg{Message::kCmdSetPath};
-  msg.dataStr = path.str();
+  msg.dataStr = path;
   auto result =
       m_messagePump->SendWindowMessage<CS_Status, Message::Kind, Message*>(
           SetCameraMessage, msg.kind, &msg);
@@ -257,7 +255,8 @@
 }
 
 void UsbCameraImpl::DeviceDisconnect() {
-  if (m_connectVerbose) SINFO("Disconnected from " << m_path);
+  if (m_connectVerbose)
+    SINFO("Disconnected from {}", m_path);
   m_sourceReader.Reset();
   m_mediaSource.Reset();
   if (m_imageCallback) {
@@ -268,8 +267,9 @@
   SetConnected(false);
 }
 
-static bool IsPercentageProperty(wpi::StringRef name) {
-  if (name.startswith("raw_")) name = name.substr(4);
+static bool IsPercentageProperty(std::string_view name) {
+  if (wpi::starts_with(name, "raw_"))
+    name = name.substr(4);
   return name == "Brightness" || name == "Contrast" || name == "Saturation" ||
          name == "Hue" || name == "Sharpness" || name == "Gain" ||
          name == "Exposure";
@@ -277,43 +277,60 @@
 
 void UsbCameraImpl::ProcessFrame(IMFSample* videoSample,
                                  const VideoMode& mode) {
-  if (!videoSample) return;
+  if (!videoSample)
+    return;
 
   ComPtr<IMFMediaBuffer> buf;
 
   if (!SUCCEEDED(videoSample->ConvertToContiguousBuffer(buf.GetAddressOf()))) {
     DWORD bcnt = 0;
-    if (!SUCCEEDED(videoSample->GetBufferCount(&bcnt))) return;
-    if (bcnt == 0) return;
+    if (!SUCCEEDED(videoSample->GetBufferCount(&bcnt)))
+      return;
+    if (bcnt == 0)
+      return;
     if (!SUCCEEDED(videoSample->GetBufferByIndex(0, buf.GetAddressOf())))
       return;
   }
 
-  bool lock2d = false;
   BYTE* ptr = NULL;
   LONG pitch = 0;
-  DWORD maxsize = 0, cursize = 0;
+  DWORD length = 0;
 
-  // "For 2-D buffers, the Lock2D method is more efficient than the Lock
-  // method" see IMFMediaBuffer::Lock method documentation:
-  // https://msdn.microsoft.com/en-us/library/windows/desktop/bb970366(v=vs.85).aspx
-  ComPtr<IMF2DBuffer> buffer2d;
-  DWORD memLength2d = 0;
-  if (true) {
-    buffer2d = buf.As<IMF2DBuffer>();
-    if (buffer2d) {
-      buffer2d->GetContiguousLength(&memLength2d);
-      if (SUCCEEDED(buffer2d->Lock2D(&ptr, &pitch))) {
-        lock2d = true;
+  // First try to access using Lock2DSize, then try Lock2D, then fallback
+  // https://docs.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfmediabuffer-lock
+
+  ComPtr<IMF2DBuffer> buffer2d = buf.As<IMF2DBuffer>();
+  if (buffer2d) {
+    BYTE* scanline0 = nullptr;
+    HRESULT result;
+    ComPtr<IMF2DBuffer2> buffer2d2 = buf.As<IMF2DBuffer2>();
+    if (buffer2d2) {
+      BYTE* datastart;
+      result = buffer2d2->Lock2DSize(MF2DBuffer_LockFlags_Read, &scanline0,
+                                     &pitch, &datastart, &length);
+    } else {
+      result = buffer2d->Lock2D(&scanline0, &pitch);
+    }
+    if (SUCCEEDED(result)) {
+      BOOL isContiguous;
+      if (pitch > 0 && SUCCEEDED(buffer2d->IsContiguousFormat(&isContiguous)) &&
+          isContiguous &&
+          (length || SUCCEEDED(buffer2d->GetContiguousLength(&length)))) {
+        // Use the buffer pointer.
+        ptr = scanline0;
+      } else {
+        // Release the buffer and fall back to Lock().
+        buffer2d->Unlock2D();
       }
     }
   }
   if (ptr == NULL) {
-    if (!SUCCEEDED(buf->Lock(&ptr, &maxsize, &cursize))) {
+    buffer2d = nullptr;
+    DWORD maxsize = 0;
+    if (!SUCCEEDED(buf->Lock(&ptr, &maxsize, &length))) {
       return;
     }
   }
-  if (!ptr) return;
 
   cv::Mat tmpMat;
   std::unique_ptr<Image> dest;
@@ -323,8 +340,7 @@
     case cs::VideoMode::PixelFormat::kMJPEG: {
       // Special case
       PutFrame(VideoMode::kMJPEG, mode.width, mode.height,
-               wpi::StringRef(reinterpret_cast<char*>(ptr), cursize),
-               wpi::Now());
+               {reinterpret_cast<char*>(ptr), length}, wpi::Now());
       doFinalSet = false;
       break;
     }
@@ -355,10 +371,11 @@
     PutFrame(std::move(dest), wpi::Now());
   }
 
-  if (lock2d)
+  if (buffer2d) {
     buffer2d->Unlock2D();
-  else
+  } else {
     buf->Unlock();
+  }
 }
 
 LRESULT UsbCameraImpl::PumpMain(HWND hwnd, UINT uiMsg, WPARAM wParam,
@@ -454,16 +471,19 @@
 }
 
 bool UsbCameraImpl::DeviceConnect() {
-  if (m_mediaSource && m_sourceReader) return true;
+  if (m_mediaSource && m_sourceReader)
+    return true;
 
-  if (m_connectVerbose) SINFO("Connecting to USB camera on " << m_path);
+  if (m_connectVerbose)
+    SINFO("Connecting to USB camera on {}", m_path);
 
-  SDEBUG3("opening device");
+  SDEBUG3("{}", "opening device");
 
   const wchar_t* path = m_widePath.c_str();
   m_mediaSource = CreateVideoCaptureDevice(path);
 
-  if (!m_mediaSource) return false;
+  if (!m_mediaSource)
+    return false;
   m_imageCallback = CreateSourceReaderCB(shared_from_this(), m_mode);
 
   m_sourceReader =
@@ -484,13 +504,13 @@
   }
 
   if (!m_properties_cached) {
-    SDEBUG3("caching properties");
+    SDEBUG3("{}", "caching properties");
     DeviceCacheProperties();
     DeviceCacheVideoModes();
     DeviceCacheMode();
     m_properties_cached = true;
   } else {
-    SDEBUG3("restoring video mode");
+    SDEBUG3("{}", "restoring video mode");
     DeviceSetMode();
   }
 
@@ -504,7 +524,7 @@
 }
 
 std::unique_ptr<PropertyImpl> UsbCameraImpl::CreateEmptyProperty(
-    const wpi::Twine& name) const {
+    std::string_view name) const {
   return nullptr;
 }
 
@@ -513,7 +533,9 @@
   auto result = m_messagePump->SendWindowMessage<CS_Status>(
       WaitForStartupMessage, nullptr, nullptr);
   *status = result;
-  if (*status != CS_OK) return false;
+  if (*status != CS_OK) {
+    return false;
+  }
   if (!m_properties_cached) {
     *status = CS_SOURCE_IS_DISCONNECTED;
     return false;
@@ -522,7 +544,7 @@
 }
 
 template <typename TagProperty, typename IAM>
-void UsbCameraImpl::DeviceAddProperty(const wpi::Twine& name_, TagProperty tag,
+void UsbCameraImpl::DeviceAddProperty(std::string_view name_, TagProperty tag,
                                       IAM* pProcAmp) {
   // First see if properties exist
   bool isValid = false;
@@ -533,11 +555,11 @@
   }
 }
 
-template void UsbCameraImpl::DeviceAddProperty(const wpi::Twine& name_,
+template void UsbCameraImpl::DeviceAddProperty(std::string_view name_,
                                                tagVideoProcAmpProperty tag,
                                                IAMVideoProcAmp* pProcAmp);
 
-template void UsbCameraImpl::DeviceAddProperty(const wpi::Twine& name_,
+template void UsbCameraImpl::DeviceAddProperty(std::string_view name_,
                                                tagCameraControlProperty tag,
                                                IAMCameraControl* pProcAmp);
 
@@ -548,7 +570,8 @@
   DeviceAddProperty(#val, CameraControl_##val, pCamControl);
 
 void UsbCameraImpl::DeviceCacheProperties() {
-  if (!m_sourceReader) return;
+  if (!m_sourceReader)
+    return;
 
   IAMVideoProcAmp* pProcAmp = NULL;
 
@@ -645,7 +668,7 @@
   } else {
     // Read current raw value and set percentage from it
     if (!rawProp->DeviceGet(lock, pProcAmp))
-      SWARNING("failed to get property " << rawProp->name);
+      SWARNING("failed to get property {}", rawProp->name);
 
     if (perProp) {
       perProp->SetValue(RawToPercentage(*rawProp, rawProp->value));
@@ -656,7 +679,7 @@
   // Set value on device if user-configured
   if (rawProp->valueSet) {
     if (!rawProp->DeviceSet(lock, pProcAmp))
-      SWARNING("failed to set property " << rawProp->name);
+      SWARNING("failed to set property {}", rawProp->name);
   }
 
   // Update pointers since we released the lock
@@ -696,7 +719,8 @@
   }
 
   NotifyPropertyCreated(*rawIndex, *rawPropPtr);
-  if (perPropPtr && perIndex) NotifyPropertyCreated(*perIndex, *perPropPtr);
+  if (perPropPtr && perIndex)
+    NotifyPropertyCreated(*perIndex, *perPropPtr);
 }
 
 CS_StatusValue UsbCameraImpl::DeviceProcessCommand(
@@ -739,11 +763,12 @@
   bool setString = (msg.kind == Message::kCmdSetPropertyStr);
   int property = msg.data[0];
   int value = msg.data[1];
-  wpi::StringRef valueStr = msg.dataStr;
+  std::string_view valueStr = msg.dataStr;
 
   // Look up
   auto prop = static_cast<UsbCameraProperty*>(GetProperty(property));
-  if (!prop) return CS_INVALID_PROPERTY;
+  if (!prop)
+    return CS_INVALID_PROPERTY;
 
   // If setting before we get, guess initial type based on set
   if (prop->propKind == CS_PROP_NONE) {
@@ -774,7 +799,8 @@
 
   // Actually set the new value on the device (if possible)
   if (!prop->device) {
-    if (prop->id == kPropConnectVerboseId) m_connectVerbose = value;
+    if (prop->id == kPropConnectVerboseId)
+      m_connectVerbose = value;
   } else {
     if (!prop->DeviceSet(lock, m_sourceReader.Get())) {
       return CS_PROPERTY_WRITE_FAILED;
@@ -858,8 +884,12 @@
 }
 
 bool UsbCameraImpl::DeviceStreamOn() {
-  if (m_streaming) return false;
-  if (!m_deviceValid) return false;
+  if (m_streaming) {
+    return false;
+  }
+  if (!m_deviceValid) {
+    return false;
+  }
   m_streaming = true;
   m_sourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, NULL,
                              NULL, NULL);
@@ -872,9 +902,11 @@
 }
 
 void UsbCameraImpl::DeviceCacheMode() {
-  if (!m_sourceReader) return;
+  if (!m_sourceReader)
+    return;
 
-  if (m_windowsVideoModes.size() == 0) return;
+  if (m_windowsVideoModes.size() == 0)
+    return;
 
   if (!m_currentMode) {
     // First, see if our set mode is valid
@@ -939,7 +971,8 @@
 }
 
 void UsbCameraImpl::DeviceCacheVideoModes() {
-  if (!m_sourceReader) return;
+  if (!m_sourceReader)
+    return;
 
   std::vector<VideoMode> modes;
   m_windowsVideoModes.clear();
@@ -990,23 +1023,21 @@
   m_notifier.NotifySource(*this, CS_SOURCE_VIDEOMODES_UPDATED);
 }
 
-static void ParseVidAndPid(wpi::StringRef path, int* pid, int* vid) {
-  auto vidIndex = path.find_lower("vid_");
-  auto pidIndex = path.find_lower("pid_");
+static void ParseVidAndPid(std::string_view path, int* pid, int* vid) {
+  auto vidIndex = wpi::find_lower(path, "vid_");
+  auto pidIndex = wpi::find_lower(path, "pid_");
 
-  if (vidIndex != wpi::StringRef::npos) {
-    auto vidSlice = path.slice(vidIndex + 4, vidIndex + 8);
-    uint16_t val = 0;
-    if (!vidSlice.getAsInteger(16, val)) {
-      *vid = val;
+  if (vidIndex != std::string_view::npos) {
+    auto vidSlice = wpi::slice(path, vidIndex + 4, vidIndex + 8);
+    if (auto v = wpi::parse_integer<uint16_t>(vidSlice, 16)) {
+      *vid = v.value();
     }
   }
 
-  if (pidIndex != wpi::StringRef::npos) {
-    auto pidSlice = path.slice(pidIndex + 4, pidIndex + 8);
-    uint16_t val = 0;
-    if (!pidSlice.getAsInteger(16, val)) {
-      *pid = val;
+  if (pidIndex != std::string_view::npos) {
+    auto pidSlice = wpi::slice(path, pidIndex + 4, pidIndex + 8);
+    if (auto v = wpi::parse_integer<uint16_t>(pidSlice, 16)) {
+      *pid = v.value();
     }
   }
 }
@@ -1080,7 +1111,7 @@
   return retval;
 }
 
-CS_Source CreateUsbCameraDev(const wpi::Twine& name, int dev,
+CS_Source CreateUsbCameraDev(std::string_view name, int dev,
                              CS_Status* status) {
   // First check if device exists
   auto devices = cs::EnumerateUsbCameras(status);
@@ -1093,7 +1124,7 @@
   return inst.CreateSource(CS_SOURCE_USB, source);
 }
 
-CS_Source CreateUsbCameraPath(const wpi::Twine& name, const wpi::Twine& path,
+CS_Source CreateUsbCameraPath(std::string_view name, std::string_view path,
                               CS_Status* status) {
   auto& inst = Instance::GetInstance();
   auto source = std::make_shared<UsbCameraImpl>(
@@ -1101,7 +1132,7 @@
   return inst.CreateSource(CS_SOURCE_USB, source);
 }
 
-void SetUsbCameraPath(CS_Source source, const wpi::Twine& path,
+void SetUsbCameraPath(CS_Source source, std::string_view path,
                       CS_Status* status) {
   auto data = Instance::GetInstance().GetSource(source);
   if (!data || data->kind != CS_SOURCE_USB) {
diff --git a/cscore/src/main/native/windows/UsbCameraImpl.h b/cscore/src/main/native/windows/UsbCameraImpl.h
index 60a3080..ea6c104 100644
--- a/cscore/src/main/native/windows/UsbCameraImpl.h
+++ b/cscore/src/main/native/windows/UsbCameraImpl.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #ifndef CSCORE_USBCAMERAIMPL_H_
 #define CSCORE_USBCAMERAIMPL_H_
@@ -19,14 +16,13 @@
 #include <atomic>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <thread>
 #include <utility>
 #include <vector>
 
 #include <Dbt.h>
-#include <wpi/STLExtras.h>
 #include <wpi/SmallVector.h>
-#include <wpi/Twine.h>
 #include <wpi/condition_variable.h>
 #include <wpi/mutex.h>
 #include <wpi/raw_istream.h>
@@ -43,17 +39,17 @@
 class UsbCameraImpl : public SourceImpl,
                       public std::enable_shared_from_this<UsbCameraImpl> {
  public:
-  UsbCameraImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
-                Telemetry& telemetry, const wpi::Twine& path);
-  UsbCameraImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
+  UsbCameraImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
+                Telemetry& telemetry, std::string_view path);
+  UsbCameraImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
                 Telemetry& telemetry, int deviceId);
   ~UsbCameraImpl() override;
 
-  void Start();
+  void Start() override;
 
   // Property functions
   void SetProperty(int property, int value, CS_Status* status) override;
-  void SetStringProperty(int property, const wpi::Twine& value,
+  void SetStringProperty(int property, std::string_view value,
                          CS_Status* status) override;
 
   // Standard common camera properties
@@ -78,7 +74,7 @@
   void ProcessFrame(IMFSample* sample, const VideoMode& mode);
   void PostRequestNewFrame();
 
-  void SetPath(const wpi::Twine& path, CS_Status* status);
+  void SetPath(std::string_view path, CS_Status* status);
   std::string GetPath() const;
 
   // Messages passed to/from camera thread
@@ -110,7 +106,7 @@
 
  protected:
   std::unique_ptr<PropertyImpl> CreateEmptyProperty(
-      const wpi::Twine& name) const override;
+      std::string_view name) const override;
 
   // Cache properties.  Immediately successful if properties are already cached.
   // If they are not, tries to connect to the camera to do so; returns false and
@@ -135,7 +131,7 @@
   void DeviceCacheProperties();
   void DeviceCacheVideoModes();
   template <typename TagProperty, typename IAM>
-  void DeviceAddProperty(const wpi::Twine& name_, TagProperty tag,
+  void DeviceAddProperty(std::string_view name_, TagProperty tag,
                          IAM* pProcAmp);
 
   ComPtr<IMFMediaType> DeviceCheckModeValid(const VideoMode& toCheck);
diff --git a/cscore/src/main/native/windows/UsbCameraListener.cpp b/cscore/src/main/native/windows/UsbCameraListener.cpp
new file mode 100644
index 0000000..be33771
--- /dev/null
+++ b/cscore/src/main/native/windows/UsbCameraListener.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+#include "UsbCameraListener.h"
+
+#include "Notifier.h"
+#include "WindowsMessagePump.h"
+
+#include <dbt.h>  // NOLINT(build/include_order)
+
+#define IDT_TIMER1 1001
+
+using namespace cs;
+
+class UsbCameraListener::Impl {
+ public:
+  explicit Impl(Notifier& notifier) : m_notifier{notifier} {}
+
+  void Start() {
+    m_messagePump = std::make_unique<WindowsMessagePump>(
+        [this](HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
+          return this->PumpMain(hwnd, uiMsg, wParam, lParam);
+        });
+  }
+
+  void Stop() { m_messagePump = nullptr; }
+
+  LRESULT PumpMain(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uiMsg) {
+      case WM_CLOSE:
+        KillTimer(hwnd, IDT_TIMER1);
+        break;
+      case WM_TIMER:
+        if (wParam == IDT_TIMER1) {
+          KillTimer(hwnd, IDT_TIMER1);
+          m_notifier.NotifyUsbCamerasChanged();
+        }
+        break;
+      case WM_DEVICECHANGE:
+        PDEV_BROADCAST_HDR parameter =
+            reinterpret_cast<PDEV_BROADCAST_HDR>(lParam);
+        if (wParam == DBT_DEVICEARRIVAL &&
+            parameter->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
+          SetTimer(hwnd, IDT_TIMER1, 200, nullptr);
+        } else if (wParam == DBT_DEVICEREMOVECOMPLETE &&
+                   parameter->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
+          SetTimer(hwnd, IDT_TIMER1, 200, nullptr);
+        }
+        break;
+    }
+    return 0;
+  }
+
+  Notifier& m_notifier;
+  std::unique_ptr<cs::WindowsMessagePump> m_messagePump;
+};
+
+UsbCameraListener::UsbCameraListener(wpi::Logger& logger, Notifier& notifier)
+    : m_impl{std::make_unique<Impl>(notifier)} {}
+
+UsbCameraListener::~UsbCameraListener() {
+  Stop();
+}
+
+void UsbCameraListener::Start() {
+  m_impl->Start();
+}
+
+void UsbCameraListener::Stop() {
+  m_impl->Stop();
+}
diff --git a/cscore/src/main/native/windows/UsbCameraProperty.cpp b/cscore/src/main/native/windows/UsbCameraProperty.cpp
index ae22436..5ddeebf 100644
--- a/cscore/src/main/native/windows/UsbCameraProperty.cpp
+++ b/cscore/src/main/native/windows/UsbCameraProperty.cpp
@@ -1,20 +1,19 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "UsbCameraProperty.h"
 
+#include <fmt/format.h>
+
 #include "ComPtr.h"
 
 using namespace cs;
 
-UsbCameraProperty::UsbCameraProperty(const wpi::Twine& name_,
+UsbCameraProperty::UsbCameraProperty(std::string_view name_,
                                      tagVideoProcAmpProperty tag, bool autoProp,
                                      IAMVideoProcAmp* pProcAmp, bool* isValid)
-    : PropertyImpl{autoProp ? name_ + "_auto" : name_} {
+    : PropertyImpl{autoProp ? fmt::format("{}_auto", name_) : name_} {
   this->tagVideoProc = tag;
   this->isControlProperty = false;
   this->isAutoProp = autoProp;
@@ -41,7 +40,8 @@
 
 bool UsbCameraProperty::DeviceGet(std::unique_lock<wpi::mutex>& lock,
                                   IAMVideoProcAmp* pProcAmp) {
-  if (!pProcAmp) return true;
+  if (!pProcAmp)
+    return true;
 
   lock.unlock();
   long newValue = 0, paramFlag = 0;  // NOLINT(runtime/int)
@@ -60,7 +60,8 @@
 bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
                                   IAMVideoProcAmp* pProcAmp,
                                   int newValue) const {
-  if (!pProcAmp) return true;
+  if (!pProcAmp)
+    return true;
 
   lock.unlock();
   if (SUCCEEDED(
@@ -72,11 +73,11 @@
   return false;
 }
 
-UsbCameraProperty::UsbCameraProperty(const wpi::Twine& name_,
+UsbCameraProperty::UsbCameraProperty(std::string_view name_,
                                      tagCameraControlProperty tag,
                                      bool autoProp, IAMCameraControl* pProcAmp,
                                      bool* isValid)
-    : PropertyImpl{autoProp ? name_ + "_auto" : name_} {
+    : PropertyImpl{autoProp ? fmt::format("{}_auto", name_) : name_} {
   this->tagCameraControl = tag;
   this->isControlProperty = true;
   this->isAutoProp = autoProp;
@@ -103,7 +104,8 @@
 
 bool UsbCameraProperty::DeviceGet(std::unique_lock<wpi::mutex>& lock,
                                   IAMCameraControl* pProcAmp) {
-  if (!pProcAmp) return true;
+  if (!pProcAmp)
+    return true;
 
   lock.unlock();
   long newValue = 0, paramFlag = 0;  // NOLINT(runtime/int)
@@ -122,7 +124,8 @@
 bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
                                   IAMCameraControl* pProcAmp,
                                   int newValue) const {
-  if (!pProcAmp) return true;
+  if (!pProcAmp)
+    return true;
 
   lock.unlock();
   if (SUCCEEDED(pProcAmp->Set(tagCameraControl, newValue,
@@ -136,7 +139,8 @@
 
 bool UsbCameraProperty::DeviceGet(std::unique_lock<wpi::mutex>& lock,
                                   IMFSourceReader* sourceReader) {
-  if (!sourceReader) return true;
+  if (!sourceReader)
+    return true;
 
   if (isControlProperty) {
     ComPtr<IAMCameraControl> pProcAmp;
@@ -165,7 +169,8 @@
 bool UsbCameraProperty::DeviceSet(std::unique_lock<wpi::mutex>& lock,
                                   IMFSourceReader* sourceReader,
                                   int newValue) const {
-  if (!sourceReader) return true;
+  if (!sourceReader)
+    return true;
 
   if (isControlProperty) {
     ComPtr<IAMCameraControl> pProcAmp;
diff --git a/cscore/src/main/native/windows/UsbCameraProperty.h b/cscore/src/main/native/windows/UsbCameraProperty.h
index 11888e9..03cd375 100644
--- a/cscore/src/main/native/windows/UsbCameraProperty.h
+++ b/cscore/src/main/native/windows/UsbCameraProperty.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #pragma once
 
@@ -12,6 +9,7 @@
 #include <mfreadwrite.h>
 
 #include <memory>
+#include <string_view>
 
 #include <Dshow.h>
 #include <wpi/mutex.h>
@@ -24,19 +22,19 @@
 class UsbCameraProperty : public PropertyImpl {
  public:
   UsbCameraProperty() = default;
-  explicit UsbCameraProperty(const wpi::Twine& name_) : PropertyImpl{name_} {}
+  explicit UsbCameraProperty(std::string_view name_) : PropertyImpl{name_} {}
 
   // Software property constructor
-  UsbCameraProperty(const wpi::Twine& name_, unsigned id_,
-                    CS_PropertyKind kind_, int minimum_, int maximum_,
-                    int step_, int defaultValue_, int value_)
+  UsbCameraProperty(std::string_view name_, unsigned id_, CS_PropertyKind kind_,
+                    int minimum_, int maximum_, int step_, int defaultValue_,
+                    int value_)
       : PropertyImpl(name_, kind_, minimum_, maximum_, step_, defaultValue_,
                      value_),
         device{false},
         id{id_} {}
 
   // Normalized property constructor
-  UsbCameraProperty(const wpi::Twine& name_, int rawIndex_,
+  UsbCameraProperty(std::string_view name_, int rawIndex_,
                     const UsbCameraProperty& rawProp, int defaultValue_,
                     int value_)
       : PropertyImpl(name_, rawProp.propKind, 1, defaultValue_, value_),
@@ -50,10 +48,10 @@
     maximum = 100;
   }
 
-  UsbCameraProperty(const wpi::Twine& name_, tagVideoProcAmpProperty tag,
+  UsbCameraProperty(std::string_view name_, tagVideoProcAmpProperty tag,
                     bool autoProp, IAMVideoProcAmp* pProcAmp, bool* isValid);
 
-  UsbCameraProperty(const wpi::Twine& name_, tagCameraControlProperty tag,
+  UsbCameraProperty(std::string_view name_, tagCameraControlProperty tag,
                     bool autoProp, IAMCameraControl* pProcAmp, bool* isValid);
 
   bool DeviceGet(std::unique_lock<wpi::mutex>& lock,
diff --git a/cscore/src/main/native/windows/WindowsMessagePump.cpp b/cscore/src/main/native/windows/WindowsMessagePump.cpp
index 0206bd0..e0d78d7 100644
--- a/cscore/src/main/native/windows/WindowsMessagePump.cpp
+++ b/cscore/src/main/native/windows/WindowsMessagePump.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "WindowsMessagePump.h"
 
@@ -93,7 +90,7 @@
     std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)> callback) {
   m_callback = callback;
   auto handle = CreateEvent(NULL, true, false, NULL);
-  m_mainThread = std::thread([=]() { ThreadMain(handle); });
+  m_mainThread = std::thread([=] { ThreadMain(handle); });
   auto waitResult = WaitForSingleObject(handle, 1000);
   if (waitResult == WAIT_OBJECT_0) {
     CloseHandle(handle);
@@ -102,7 +99,8 @@
 
 WindowsMessagePump::~WindowsMessagePump() {
   auto res = SendMessage(hwnd, WM_CLOSE, NULL, NULL);
-  if (m_mainThread.joinable()) m_mainThread.join();
+  if (m_mainThread.joinable())
+    m_mainThread.join();
 }
 
 void WindowsMessagePump::ThreadMain(HANDLE eventHandle) {
diff --git a/cscore/src/main/native/windows/WindowsMessagePump.h b/cscore/src/main/native/windows/WindowsMessagePump.h
index 4638e4e..97a3f2d 100644
--- a/cscore/src/main/native/windows/WindowsMessagePump.h
+++ b/cscore/src/main/native/windows/WindowsMessagePump.h
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #pragma once
 
@@ -15,7 +12,7 @@
 namespace cs {
 class WindowsMessagePump {
  public:
-  WindowsMessagePump(
+  explicit WindowsMessagePump(
       std::function<LRESULT(HWND, UINT, WPARAM, LPARAM)> callback);
   ~WindowsMessagePump();
 
diff --git a/cscore/src/test/java/edu/wpi/cscore/JNITest.java b/cscore/src/test/java/edu/wpi/cscore/JNITest.java
deleted file mode 100644
index b3c1062..0000000
--- a/cscore/src/test/java/edu/wpi/cscore/JNITest.java
+++ /dev/null
@@ -1,18 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-package edu.wpi.cscore;
-
-import org.junit.jupiter.api.Test;
-
-class JNITest {
-  @Test
-  void jniLinkTest() {
-    // Test to verify that the JNI test link works correctly.
-    CameraServerJNI.getHostname();
-  }
-}
diff --git a/cscore/src/test/java/edu/wpi/first/cscore/JNITest.java b/cscore/src/test/java/edu/wpi/first/cscore/JNITest.java
new file mode 100644
index 0000000..40e2584
--- /dev/null
+++ b/cscore/src/test/java/edu/wpi/first/cscore/JNITest.java
@@ -0,0 +1,15 @@
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
+
+package edu.wpi.first.cscore;
+
+import org.junit.jupiter.api.Test;
+
+class JNITest {
+  @Test
+  void jniLinkTest() {
+    // Test to verify that the JNI test link works correctly.
+    CameraServerJNI.getHostname();
+  }
+}
diff --git a/cscore/src/test/java/edu/wpi/cscore/UsbCameraTest.java b/cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java
similarity index 67%
rename from cscore/src/test/java/edu/wpi/cscore/UsbCameraTest.java
rename to cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java
index bc7866d..f0a679f 100644
--- a/cscore/src/test/java/edu/wpi/cscore/UsbCameraTest.java
+++ b/cscore/src/test/java/edu/wpi/first/cscore/UsbCameraTest.java
@@ -1,31 +1,27 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
-package edu.wpi.cscore;
+package edu.wpi.first.cscore;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.time.Duration;
 import java.util.Arrays;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.EnabledOnOs;
 import org.junit.jupiter.api.condition.OS;
 
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
 class UsbCameraTest {
   @Nested
   @EnabledOnOs(OS.LINUX)
-  class ConnectVerbose {
+  static class ConnectVerbose {
     @Test
     void setConnectVerboseEnabledTest() {
       try (UsbCamera camera = new UsbCamera("Nonexistant Camera", getNonexistentCameraDev())) {
@@ -34,7 +30,8 @@
         CompletableFuture<String> result = new CompletableFuture<>();
         CameraServerJNI.setLogger((level, file, line, message) -> result.complete(message), 20);
 
-        assertTimeoutPreemptively(Duration.ofSeconds(5),
+        assertTimeoutPreemptively(
+            Duration.ofSeconds(5),
             () -> assertTrue(result.get().contains("Connecting to USB camera on ")));
       }
     }
@@ -47,15 +44,16 @@
         CompletableFuture<String> result = new CompletableFuture<>();
         CameraServerJNI.setLogger((level, file, line, message) -> result.complete(message), 20);
 
-        assertThrows(TimeoutException.class,
-            () -> result.get(3, TimeUnit.SECONDS));
+        assertThrows(TimeoutException.class, () -> result.get(3, TimeUnit.SECONDS));
       }
     }
   }
 
   private static int getNonexistentCameraDev() {
     return Arrays.stream(CameraServerJNI.enumerateUsbCameras())
-        .mapToInt(info -> info.dev)
-        .max().orElse(-1) + 20;
+            .mapToInt(info -> info.dev)
+            .max()
+            .orElse(-1)
+        + 20;
   }
 }
diff --git a/cscore/src/test/native/cpp/CameraSourceTest.cpp b/cscore/src/test/native/cpp/CameraSourceTest.cpp
index e09f5e6..4c860bf 100644
--- a/cscore/src/test/native/cpp/CameraSourceTest.cpp
+++ b/cscore/src/test/native/cpp/CameraSourceTest.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "cscore.h"
 #include "gtest/gtest.h"
@@ -12,11 +9,12 @@
 
 class CameraSourceTest : public ::testing::Test {
  protected:
-  CameraSourceTest() {}
+  CameraSourceTest() = default;
 };
 
 TEST_F(CameraSourceTest, HTTPCamera) {
   auto source = HttpCamera("axis", "http://localhost:8000");
+  cs::Shutdown();
 }
 
 }  // namespace cs
diff --git a/cscore/src/test/native/cpp/main.cpp b/cscore/src/test/native/cpp/main.cpp
index 1e5ecf0..09072ee 100644
--- a/cscore/src/test/native/cpp/main.cpp
+++ b/cscore/src/test/native/cpp/main.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// Copyright (c) FIRST and other WPILib contributors.
+// Open Source Software; you can modify and/or share it under the terms of
+// the WPILib BSD license file in the root directory of this project.
 
 #include "gtest/gtest.h"