mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
Compare commits
1672 Commits
v2018.3.2
...
v2020.1.1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
936627bd94 | ||
|
|
8e333c0aad | ||
|
|
d4430b765e | ||
|
|
75438ab2ce | ||
|
|
989df1b461 | ||
|
|
dbc33b61e1 | ||
|
|
79f8c5644a | ||
|
|
9440edf2b5 | ||
|
|
73a30182c3 | ||
|
|
36ea865edc | ||
|
|
cbe05e7e8a | ||
|
|
d04eb35465 | ||
|
|
02264db69c | ||
|
|
2a76c996eb | ||
|
|
a3820bbdfa | ||
|
|
a83fb47933 | ||
|
|
4b0ed910ee | ||
|
|
103c1b121c | ||
|
|
6635ea75ee | ||
|
|
cfe23c5cd0 | ||
|
|
4bde2654e2 | ||
|
|
4f034e6c14 | ||
|
|
acf960f729 | ||
|
|
2d3dac99f0 | ||
|
|
07c86e0cd5 | ||
|
|
46ad95512e | ||
|
|
5bce489b98 | ||
|
|
55af553acc | ||
|
|
c59f9cea5f | ||
|
|
3fc89c84d6 | ||
|
|
2c50937975 | ||
|
|
f3ad927f45 | ||
|
|
05c25deb7b | ||
|
|
d726591ce4 | ||
|
|
2ff694fa49 | ||
|
|
53816155ba | ||
|
|
a38f183a98 | ||
|
|
b3398dca39 | ||
|
|
2c311013d4 | ||
|
|
c10f2003c5 | ||
|
|
63cfa64fb3 | ||
|
|
2402c2bad7 | ||
|
|
f4eedf597f | ||
|
|
bb0b207d2f | ||
|
|
7bd69e591c | ||
|
|
ec9738245d | ||
|
|
46303a8221 | ||
|
|
d169d6be9e | ||
|
|
4e183eb104 | ||
|
|
84c185803d | ||
|
|
0e3b0f3da7 | ||
|
|
7f839b87ce | ||
|
|
45b766a5dc | ||
|
|
56d782b16c | ||
|
|
2b4894038e | ||
|
|
f97d16073a | ||
|
|
55a844a3e1 | ||
|
|
10deba8546 | ||
|
|
a9f0e46680 | ||
|
|
aa90645865 | ||
|
|
81c2c8a7de | ||
|
|
e8d6f8a2c1 | ||
|
|
1b266717a8 | ||
|
|
fb8f3bd06b | ||
|
|
846d8def00 | ||
|
|
d6ac6e512b | ||
|
|
2271570860 | ||
|
|
885744d7e1 | ||
|
|
366091fa87 | ||
|
|
c58b072c89 | ||
|
|
762c88adb8 | ||
|
|
af8ce568d1 | ||
|
|
b2c2934d05 | ||
|
|
cce26ec78e | ||
|
|
cb54602d49 | ||
|
|
9f740e5905 | ||
|
|
b23baf611a | ||
|
|
457f94ba26 | ||
|
|
fd612052f3 | ||
|
|
8858ec55c7 | ||
|
|
41efb8015d | ||
|
|
c93be1b2d5 | ||
|
|
680f8919ed | ||
|
|
c5812524f8 | ||
|
|
971303da8c | ||
|
|
50db77bf25 | ||
|
|
85d42c1993 | ||
|
|
2dfbb855d7 | ||
|
|
471f375a38 | ||
|
|
1d8c4d016f | ||
|
|
a5650b9439 | ||
|
|
904479ad43 | ||
|
|
86b666bba9 | ||
|
|
62f07c182c | ||
|
|
f405582f86 | ||
|
|
561cbbd144 | ||
|
|
84e2973aaa | ||
|
|
f49859ebfd | ||
|
|
bc59db5e6f | ||
|
|
dd928b4cbf | ||
|
|
3e0f7d0995 | ||
|
|
5ffe15d5ff | ||
|
|
516cbef2c4 | ||
|
|
9b6ffc201c | ||
|
|
ff8b8f0a8a | ||
|
|
0ca8d667d2 | ||
|
|
7112add67f | ||
|
|
761bc3ef85 | ||
|
|
1fb3011235 | ||
|
|
eb3e0c9c95 | ||
|
|
2250b7fbe3 | ||
|
|
c9f9feff1f | ||
|
|
d6b9c7e148 | ||
|
|
d10a1a7977 | ||
|
|
2bdb443255 | ||
|
|
4b2b21d247 | ||
|
|
8993ce5bf1 | ||
|
|
0f532a1174 | ||
|
|
f7ad363d86 | ||
|
|
9afea33403 | ||
|
|
d787b5d609 | ||
|
|
5dd0d1b7db | ||
|
|
07ac711b31 | ||
|
|
decfd858b8 | ||
|
|
076ed7770c | ||
|
|
a0be07c370 | ||
|
|
558c383088 | ||
|
|
1379735aff | ||
|
|
e3d86fee46 | ||
|
|
4cd8a56672 | ||
|
|
b2861f8948 | ||
|
|
98cc32703c | ||
|
|
fa06403000 | ||
|
|
e716c36b89 | ||
|
|
9fd2b5e3fa | ||
|
|
7e95010a29 | ||
|
|
3ebc5a6d3a | ||
|
|
fc98a79dbb | ||
|
|
fdc098267e | ||
|
|
a3dd84e854 | ||
|
|
a216b9e9ee | ||
|
|
8f386f6bb3 | ||
|
|
c07ac23532 | ||
|
|
f1d71da8a9 | ||
|
|
ef037457e5 | ||
|
|
76930250c0 | ||
|
|
1c246418f8 | ||
|
|
95a54a0f29 | ||
|
|
a4530243e1 | ||
|
|
09d00a6227 | ||
|
|
ba9b517427 | ||
|
|
6411bd79c6 | ||
|
|
810e58ea85 | ||
|
|
607d6c148a | ||
|
|
c9873e81b2 | ||
|
|
98d0706de8 | ||
|
|
fbe67c90c8 | ||
|
|
c67a488a09 | ||
|
|
8e93ce8929 | ||
|
|
c98ca7310f | ||
|
|
3b12276bc3 | ||
|
|
e6d348f382 | ||
|
|
df12fc2a86 | ||
|
|
39561751fc | ||
|
|
37d316aa09 | ||
|
|
dd43109596 | ||
|
|
823174f30a | ||
|
|
37c6952663 | ||
|
|
04c9b000ff | ||
|
|
ca3e71e214 | ||
|
|
d946d5a2bb | ||
|
|
8b1b9ac75a | ||
|
|
2f680ba990 | ||
|
|
a885db7d48 | ||
|
|
ee24101696 | ||
|
|
48fe54271a | ||
|
|
dff58c87f4 | ||
|
|
dde61aad32 | ||
|
|
0f6ef80ab2 | ||
|
|
e488861877 | ||
|
|
dffa1a5cba | ||
|
|
fe59d854d5 | ||
|
|
10731f3d6b | ||
|
|
89f7b72b6e | ||
|
|
85f2f87400 | ||
|
|
73ec940786 | ||
|
|
62be0392b6 | ||
|
|
24d31df55a | ||
|
|
841ef5d739 | ||
|
|
e582518bae | ||
|
|
8757bc471b | ||
|
|
ea9512977c | ||
|
|
9b798d228f | ||
|
|
804926fb5b | ||
|
|
118e9d29d5 | ||
|
|
c705953d77 | ||
|
|
852d1b9cad | ||
|
|
eedb3a1adc | ||
|
|
60dce66a4f | ||
|
|
9e19b29c31 | ||
|
|
2994250714 | ||
|
|
a6b0e9b856 | ||
|
|
3c2093119e | ||
|
|
5fe2eebceb | ||
|
|
4b1b92bb74 | ||
|
|
0fbb0d989e | ||
|
|
2dc94e6052 | ||
|
|
d9cb57a429 | ||
|
|
f7cfdd7cee | ||
|
|
b6d5d90d9d | ||
|
|
c7ab2baa6e | ||
|
|
0c45c5b7ea | ||
|
|
3dfb01d45b | ||
|
|
30e936837c | ||
|
|
311e2de4c1 | ||
|
|
c08fd6682f | ||
|
|
258bba0c2d | ||
|
|
372ca4f456 | ||
|
|
223d47af0e | ||
|
|
55cb683db4 | ||
|
|
ee8a33c568 | ||
|
|
61426d08de | ||
|
|
b630b63ef0 | ||
|
|
1d0c05d4f8 | ||
|
|
f07569df19 | ||
|
|
0120f31247 | ||
|
|
c2829ed98e | ||
|
|
221e66f46d | ||
|
|
738852e119 | ||
|
|
27b697b084 | ||
|
|
9e45373a74 | ||
|
|
eeb1025ac7 | ||
|
|
bc6f1e2469 | ||
|
|
bb48ae391e | ||
|
|
221011494d | ||
|
|
fb1239a2ad | ||
|
|
7de9477347 | ||
|
|
90957aeea4 | ||
|
|
47aae502a7 | ||
|
|
0bff98b5ec | ||
|
|
b52e40b80c | ||
|
|
4a00cd77bb | ||
|
|
e25e515f2e | ||
|
|
322ef9b967 | ||
|
|
d42ef5df02 | ||
|
|
f432f65bef | ||
|
|
1726b77ac5 | ||
|
|
620bec9cae | ||
|
|
7cd6e2e7fa | ||
|
|
7732836bd5 | ||
|
|
698edfda9d | ||
|
|
1c454b000f | ||
|
|
f42905b32e | ||
|
|
bdc822fad8 | ||
|
|
d3affb16bc | ||
|
|
2de3bf7f58 | ||
|
|
3cf4f38f5d | ||
|
|
4e0c10f488 | ||
|
|
3b06313243 | ||
|
|
6cd1c73efe | ||
|
|
063bbab6f5 | ||
|
|
aab4c494d6 | ||
|
|
bf46af2600 | ||
|
|
655763a9a7 | ||
|
|
a095ec2d8f | ||
|
|
12ab035aad | ||
|
|
99e4f7dd2c | ||
|
|
60c2f59051 | ||
|
|
d55ca191b8 | ||
|
|
e8b24717c7 | ||
|
|
182758c05b | ||
|
|
74f7ba04b0 | ||
|
|
997d4fdf47 | ||
|
|
76d9e26633 | ||
|
|
a230c814cb | ||
|
|
12cb77cd7c | ||
|
|
8a9822a96b | ||
|
|
a9371a7586 | ||
|
|
6992f5421f | ||
|
|
43696956d2 | ||
|
|
ae3fd5adac | ||
|
|
404666b298 | ||
|
|
1eb4c99d15 | ||
|
|
910b9f3af7 | ||
|
|
09d90b02fb | ||
|
|
0e1f9c2ed2 | ||
|
|
f156a00117 | ||
|
|
4a6087ed56 | ||
|
|
88a09dd13a | ||
|
|
7d19596367 | ||
|
|
bd05dfa1c7 | ||
|
|
05d6660a6b | ||
|
|
1349dd4bd8 | ||
|
|
fdf298b172 | ||
|
|
453a9047e4 | ||
|
|
e97e7a7611 | ||
|
|
308bdbe298 | ||
|
|
f889b45d59 | ||
|
|
444b899a9f | ||
|
|
f121ccff0d | ||
|
|
bc2c932f92 | ||
|
|
6bdd7ce506 | ||
|
|
c12d7729e3 | ||
|
|
3635116049 | ||
|
|
6105873cbe | ||
|
|
80f87ff8ad | ||
|
|
a2368a6199 | ||
|
|
ae3cb6c83b | ||
|
|
f0f196e5b3 | ||
|
|
7c35355d29 | ||
|
|
75cc09a9e4 | ||
|
|
0e2e180635 | ||
|
|
01d1322066 | ||
|
|
ceed1d74dc | ||
|
|
e1bf623997 | ||
|
|
d46ce13ffe | ||
|
|
300eeb330d | ||
|
|
d817001259 | ||
|
|
8ac4b113a5 | ||
|
|
f3864e9abb | ||
|
|
799c3ea8a6 | ||
|
|
8d95c38e39 | ||
|
|
a7f4e29b73 | ||
|
|
b88369f5e8 | ||
|
|
ce6f1d0588 | ||
|
|
f163216a4c | ||
|
|
c449ef1064 | ||
|
|
6593f4346e | ||
|
|
ce1367a115 | ||
|
|
0d7d880261 | ||
|
|
ca2acec88c | ||
|
|
3721463eb3 | ||
|
|
6e8f8be370 | ||
|
|
d84240d8e9 | ||
|
|
1823cb2b68 | ||
|
|
41596608cc | ||
|
|
0c3b488e18 | ||
|
|
7d7af287f6 | ||
|
|
4119622994 | ||
|
|
d528a77963 | ||
|
|
dab7e1b7a2 | ||
|
|
ab49345460 | ||
|
|
608d82423d | ||
|
|
e0e15eafeb | ||
|
|
0fb24538a7 | ||
|
|
d65547ea74 | ||
|
|
bfe15245a6 | ||
|
|
ff58c5156a | ||
|
|
6d4326a560 | ||
|
|
97ba195b88 | ||
|
|
3d546428ab | ||
|
|
b64dfacff3 | ||
|
|
dcbf02a1ec | ||
|
|
7e1ec28839 | ||
|
|
ef16317f8f | ||
|
|
26e8e587f9 | ||
|
|
0d0492bfcc | ||
|
|
3b33abfc7b | ||
|
|
99033481e0 | ||
|
|
b4901985b7 | ||
|
|
97edb6c68f | ||
|
|
73de3364b7 | ||
|
|
5551981b3f | ||
|
|
90572a3cc5 | ||
|
|
c405188052 | ||
|
|
bea0565ac9 | ||
|
|
0b03454366 | ||
|
|
489701cacc | ||
|
|
a769d56ec1 | ||
|
|
6f0c185a05 | ||
|
|
a60f312d19 | ||
|
|
acb786a791 | ||
|
|
df347e3d80 | ||
|
|
e4aa45f34b | ||
|
|
75cc3cda28 | ||
|
|
45f4472d42 | ||
|
|
69cb53b51b | ||
|
|
70a66fc943 | ||
|
|
9207d788ab | ||
|
|
ef3a31aa20 | ||
|
|
63775362fe | ||
|
|
55493b0c18 | ||
|
|
1696557c46 | ||
|
|
ecd376be4c | ||
|
|
f54c0f70f6 | ||
|
|
9bc998f4b0 | ||
|
|
43d188a429 | ||
|
|
563d5334c9 | ||
|
|
193b0a222c | ||
|
|
76f5d153fa | ||
|
|
19caeca990 | ||
|
|
0abae17653 | ||
|
|
81d10bc656 | ||
|
|
b51b86525d | ||
|
|
ace37c517e | ||
|
|
ac751d3224 | ||
|
|
7c9a3c4d77 | ||
|
|
8be693f55d | ||
|
|
622ae29dff | ||
|
|
e7c98feca2 | ||
|
|
28087424ec | ||
|
|
b6830638df | ||
|
|
fb557f49ea | ||
|
|
746f950a0b | ||
|
|
9a38a3e188 | ||
|
|
2e3e3a47b9 | ||
|
|
e27d6d7bb8 | ||
|
|
1dec0393a1 | ||
|
|
d03b020326 | ||
|
|
71e29b1d91 | ||
|
|
f0b0965f9b | ||
|
|
f774e47c80 | ||
|
|
761933a164 | ||
|
|
99e0f08a6f | ||
|
|
e89d5eb692 | ||
|
|
2501e11886 | ||
|
|
9174f23f36 | ||
|
|
9f6544fa87 | ||
|
|
9a1af132bf | ||
|
|
a8aacd3657 | ||
|
|
8ff81f5a2a | ||
|
|
349e273ecc | ||
|
|
0a2ab4f0d7 | ||
|
|
7c1a7332e1 | ||
|
|
172e438cd6 | ||
|
|
1a7a0db1ff | ||
|
|
11e5faf469 | ||
|
|
c7118f8ade | ||
|
|
7933d2cbe5 | ||
|
|
ce8c71b1f3 | ||
|
|
da9a575526 | ||
|
|
7068551a3e | ||
|
|
bd9484a2f4 | ||
|
|
b9fa3a4398 | ||
|
|
88b93c220e | ||
|
|
0a937bb5b9 | ||
|
|
613d5eda0d | ||
|
|
18c8cce6a7 | ||
|
|
36000ddb36 | ||
|
|
de6d6c9a5c | ||
|
|
6d99c0ac6c | ||
|
|
164e9a2c7d | ||
|
|
f3fb95af70 | ||
|
|
40a9fc44ff | ||
|
|
f0ac048645 | ||
|
|
81498e6af9 | ||
|
|
f1056efa01 | ||
|
|
54fbec27df | ||
|
|
fd82153456 | ||
|
|
7b471d8c62 | ||
|
|
175c6c1f01 | ||
|
|
ac7dfa5042 | ||
|
|
a732854866 | ||
|
|
6171856020 | ||
|
|
d5d744a390 | ||
|
|
8b1274d744 | ||
|
|
26c33a9a56 | ||
|
|
5fad2b1056 | ||
|
|
32ec07ee01 | ||
|
|
15c5a820bf | ||
|
|
e15fabd2e1 | ||
|
|
1aa8446725 | ||
|
|
b1965f74a8 | ||
|
|
0c58a0a705 | ||
|
|
467c9fd686 | ||
|
|
b505bbefd1 | ||
|
|
5c6b78ea2b | ||
|
|
f89274fb13 | ||
|
|
1137582a7a | ||
|
|
e26e3b6aa8 | ||
|
|
456d3e16a6 | ||
|
|
e210073044 | ||
|
|
0068b6aea3 | ||
|
|
12c92a822d | ||
|
|
d2a5aaafdd | ||
|
|
bedef476fd | ||
|
|
59386635e7 | ||
|
|
a846ed062f | ||
|
|
8b5dc53cc7 | ||
|
|
59700882f1 | ||
|
|
025af24523 | ||
|
|
c0ff6198b3 | ||
|
|
67b1c85315 | ||
|
|
0b113ad9ce | ||
|
|
c8482cd6d2 | ||
|
|
d6d5321828 | ||
|
|
8d91343bf5 | ||
|
|
488ba79379 | ||
|
|
de212a9dd0 | ||
|
|
8d8f120cc3 | ||
|
|
57490e0002 | ||
|
|
1de1900dbb | ||
|
|
4a3e43d4a7 | ||
|
|
9e37ee13de | ||
|
|
7b95c5341a | ||
|
|
5283726cce | ||
|
|
0b8f4b5e6c | ||
|
|
a5f7342fce | ||
|
|
6df7425440 | ||
|
|
d7b68f3f95 | ||
|
|
e28295fc7b | ||
|
|
6df500e726 | ||
|
|
83cfb8b19f | ||
|
|
82b25d0ec9 | ||
|
|
b44f27ddfa | ||
|
|
fa78f30e30 | ||
|
|
46ae19d082 | ||
|
|
77124a229e | ||
|
|
1462a5bd46 | ||
|
|
44099d9a21 | ||
|
|
c2ceebfb9c | ||
|
|
0a0d9245e2 | ||
|
|
7bd3f9f0bd | ||
|
|
4801ae2499 | ||
|
|
0e9172f9a7 | ||
|
|
6db5f80430 | ||
|
|
898076f698 | ||
|
|
195e101816 | ||
|
|
97a8f8f47b | ||
|
|
9408fd5176 | ||
|
|
00c2cd7dab | ||
|
|
cbb62fb98f | ||
|
|
a11fcb605d | ||
|
|
139b1720b2 | ||
|
|
011f0ff536 | ||
|
|
75a67202e5 | ||
|
|
186e1dc54b | ||
|
|
f2393feeef | ||
|
|
66e35128c9 | ||
|
|
6933fefe55 | ||
|
|
fedf828120 | ||
|
|
eb64ea9fc7 | ||
|
|
826ed7fe3c | ||
|
|
a2d314b0d9 | ||
|
|
6b37ca9f9a | ||
|
|
0614913f1a | ||
|
|
5fafaf6272 | ||
|
|
397a296e25 | ||
|
|
1d9ed8f458 | ||
|
|
bbb622aaa6 | ||
|
|
8cbe7a6257 | ||
|
|
932308b497 | ||
|
|
9398278250 | ||
|
|
c9a75a119a | ||
|
|
04ee8dbe79 | ||
|
|
7fd7192b16 | ||
|
|
63c1f80d60 | ||
|
|
d54c2665dc | ||
|
|
8aac46542d | ||
|
|
c78e1499d7 | ||
|
|
a34df5589e | ||
|
|
eb2c6e19f8 | ||
|
|
c25d48fd0c | ||
|
|
794403dcea | ||
|
|
d89b7dd412 | ||
|
|
31ced30c1e | ||
|
|
74a306d47a | ||
|
|
5bf5821138 | ||
|
|
eed28a5852 | ||
|
|
435e026c08 | ||
|
|
739267d36d | ||
|
|
85118a023d | ||
|
|
ae72c0b296 | ||
|
|
b72885b4f8 | ||
|
|
70b0d7cb03 | ||
|
|
053ca47d4e | ||
|
|
74efe5aafe | ||
|
|
fe5d7dd6ba | ||
|
|
0b5df467e1 | ||
|
|
80134164a4 | ||
|
|
76b26c2df5 | ||
|
|
f8635e8abf | ||
|
|
4029b5d84d | ||
|
|
4c527b9b08 | ||
|
|
caa03d23a3 | ||
|
|
297863b17a | ||
|
|
1992b67ee3 | ||
|
|
e2314f3528 | ||
|
|
340b26bada | ||
|
|
7f000fecc4 | ||
|
|
76c901ce78 | ||
|
|
57fc614074 | ||
|
|
89d15f061b | ||
|
|
f5b1028b5a | ||
|
|
ad3e2d7d3b | ||
|
|
3818a8b3b6 | ||
|
|
59e8b60267 | ||
|
|
de5d7d3c17 | ||
|
|
ebd41fe0bb | ||
|
|
70960b0251 | ||
|
|
c8afe9bc2f | ||
|
|
1ecaaafa6c | ||
|
|
33a01b3146 | ||
|
|
1d8456e2bf | ||
|
|
b5bacc09a7 | ||
|
|
876c650471 | ||
|
|
3eae079db4 | ||
|
|
122fdf48b2 | ||
|
|
d94f49b3ba | ||
|
|
39670fc9c0 | ||
|
|
6f0d50b9cb | ||
|
|
873b2ed13c | ||
|
|
321c144d21 | ||
|
|
13e1af259c | ||
|
|
9d7792ead0 | ||
|
|
6d93d3c250 | ||
|
|
156822dbc8 | ||
|
|
208f82d6f2 | ||
|
|
a818c7fd47 | ||
|
|
50b13d2f36 | ||
|
|
b7807bf9d2 | ||
|
|
ea7d11b1db | ||
|
|
212f378d08 | ||
|
|
2faba39b58 | ||
|
|
064989f2e4 | ||
|
|
6b1b4796c2 | ||
|
|
1ebb83e0f2 | ||
|
|
9108a93598 | ||
|
|
c7e97f45f5 | ||
|
|
5af85dd1bb | ||
|
|
b20158015c | ||
|
|
b1bb63f9a4 | ||
|
|
056e68f2ae | ||
|
|
f6e4df6a18 | ||
|
|
0cde67143a | ||
|
|
1f9645afe9 | ||
|
|
86285b427f | ||
|
|
e548a5f705 | ||
|
|
8eafe7f325 | ||
|
|
6aebba5452 | ||
|
|
664a3c2463 | ||
|
|
321dfaf0a2 | ||
|
|
8373e0361b | ||
|
|
8c680a26f8 | ||
|
|
d9971a705a | ||
|
|
85fe722f4c | ||
|
|
c04f463b78 | ||
|
|
307da3ad2d | ||
|
|
39f80730de | ||
|
|
35cfe0d92c | ||
|
|
8d218dbca4 | ||
|
|
938f835142 | ||
|
|
1dc55c03dc | ||
|
|
859b457c3d | ||
|
|
8958c4eabd | ||
|
|
7c9517ce5b | ||
|
|
5bf9720ccf | ||
|
|
d1587ed2c1 | ||
|
|
5fcb67aaf5 | ||
|
|
2e5fece594 | ||
|
|
863cfde394 | ||
|
|
c4728d291e | ||
|
|
fb45a5b314 | ||
|
|
86c1f8ae50 | ||
|
|
381c25c573 | ||
|
|
62d5301b1f | ||
|
|
40cc743cc7 | ||
|
|
ecfe95383c | ||
|
|
ba93f79d8b | ||
|
|
dcc2764844 | ||
|
|
cbaff52850 | ||
|
|
a2ecb1027a | ||
|
|
680aabbe7c | ||
|
|
55b0fe0082 | ||
|
|
8b8c3d5462 | ||
|
|
38a7786f22 | ||
|
|
df182f382e | ||
|
|
5cc7573574 | ||
|
|
17401e10f0 | ||
|
|
73439d8213 | ||
|
|
72a79aac53 | ||
|
|
c89678971c | ||
|
|
64b03704f8 | ||
|
|
630fc55bde | ||
|
|
f90e429bf9 | ||
|
|
2e0709f05b | ||
|
|
11d46713d1 | ||
|
|
ef442d775d | ||
|
|
3e6c3c3e98 | ||
|
|
8d57b73b41 | ||
|
|
d8c8643b52 | ||
|
|
adb6098353 | ||
|
|
938d5379e6 | ||
|
|
7cd15aa049 | ||
|
|
f8ed48af98 | ||
|
|
c274d1790f | ||
|
|
6699f86361 | ||
|
|
c2b1ed3edd | ||
|
|
2c27ad073a | ||
|
|
31bb55c319 | ||
|
|
dd4230d743 | ||
|
|
cff475c1fc | ||
|
|
d564e19ef3 | ||
|
|
1d6eb629ad | ||
|
|
406e18663d | ||
|
|
ab70220ecf | ||
|
|
560123ab7d | ||
|
|
4e1964156e | ||
|
|
5ff3d837b6 | ||
|
|
74d7107ac6 | ||
|
|
e21a246a4d | ||
|
|
59a8e9da57 | ||
|
|
795c60da01 | ||
|
|
f3db329115 | ||
|
|
f07799c67b | ||
|
|
eec4f53a65 | ||
|
|
01d8d0c795 | ||
|
|
6729a7d6b1 | ||
|
|
0babbf317c | ||
|
|
337e89cf6e | ||
|
|
1046371349 | ||
|
|
665a6e356a | ||
|
|
b7ea481bf9 | ||
|
|
7a34f5d17d | ||
|
|
e8d5759d95 | ||
|
|
eeae84c715 | ||
|
|
dab6f40b46 | ||
|
|
5c2c5ccd07 | ||
|
|
8cbfe35bd4 | ||
|
|
954f8c40f5 | ||
|
|
6a49173cea | ||
|
|
1043aef7f7 | ||
|
|
f7bcf53059 | ||
|
|
6a159c5bd2 | ||
|
|
a098814ea0 | ||
|
|
a28832e52f | ||
|
|
f84018af5f | ||
|
|
93859eb84f | ||
|
|
e7cf6bf7c5 | ||
|
|
a8fd88840d | ||
|
|
c84bd744c8 | ||
|
|
11b99a016a | ||
|
|
dfa46cbddd | ||
|
|
91151e33bb | ||
|
|
2ed9ae1652 | ||
|
|
fdfea35161 | ||
|
|
47783842e9 | ||
|
|
7f88cf768d | ||
|
|
cb2c9eb6d5 | ||
|
|
4a1e520758 | ||
|
|
b3aa659f93 | ||
|
|
4870d83ad1 | ||
|
|
7210a8fd28 | ||
|
|
0f947613a9 | ||
|
|
ea73c10cd8 | ||
|
|
6d3d52f923 | ||
|
|
dbd1f1781e | ||
|
|
96e9a6989c | ||
|
|
14228d82f3 | ||
|
|
040731447f | ||
|
|
5175829bab | ||
|
|
9d7293734a | ||
|
|
1e5ec362f7 | ||
|
|
7bb3e4efc3 | ||
|
|
67de595c85 | ||
|
|
82152e90fe | ||
|
|
1e7d439899 | ||
|
|
71d06a1a20 | ||
|
|
698feff2ff | ||
|
|
3ef9ffaf34 | ||
|
|
3025a182cc | ||
|
|
febc41c85d | ||
|
|
627ca6db75 | ||
|
|
c80b0de2c4 | ||
|
|
979984fa6b | ||
|
|
57e9fb33d2 | ||
|
|
4e9e7ec8f5 | ||
|
|
c69b8f00d0 | ||
|
|
0542b50f76 | ||
|
|
1077ef9fb7 | ||
|
|
4376c94dc1 | ||
|
|
19f7a5f108 | ||
|
|
4514ff8071 | ||
|
|
b66d72f5c2 | ||
|
|
c6f6b352fb | ||
|
|
882399c65e | ||
|
|
2287281066 | ||
|
|
cd4b7b6cc7 | ||
|
|
da5458a2d2 | ||
|
|
4e0ed79864 | ||
|
|
6767afd400 | ||
|
|
aa2de65bad | ||
|
|
877c7f51c1 | ||
|
|
912b74151f | ||
|
|
f73db4a49b | ||
|
|
cf828ca858 | ||
|
|
7847c69231 | ||
|
|
0e4a1c5dae | ||
|
|
551504e773 | ||
|
|
85e83f1bba | ||
|
|
7eac3fcbda | ||
|
|
e9b0b9d8f6 | ||
|
|
cad1b9413c | ||
|
|
3324bcc5ce | ||
|
|
20c8d29ae9 | ||
|
|
110726c5bf | ||
|
|
7db60f8e7c | ||
|
|
b3f1e74317 | ||
|
|
e301adb22b | ||
|
|
3438a17341 | ||
|
|
e4deda5ccb | ||
|
|
80618a2e64 | ||
|
|
e45b6e0f65 | ||
|
|
86d4899a54 | ||
|
|
9d8a508cd5 | ||
|
|
c9ead29f44 | ||
|
|
f77fd1eca9 | ||
|
|
5a5e753921 | ||
|
|
3e4e5261fe | ||
|
|
cf4afb6feb | ||
|
|
55fa1e5e76 | ||
|
|
c101655419 | ||
|
|
51165ba0aa | ||
|
|
f03b31f433 | ||
|
|
8e797a1a1d | ||
|
|
fd32350dc6 | ||
|
|
b9c8ebeffa | ||
|
|
ec12b0ffe2 | ||
|
|
7fd5947486 | ||
|
|
b68e1c5570 | ||
|
|
ded1beb949 | ||
|
|
a6c7789b5e | ||
|
|
73f8412b42 | ||
|
|
529d7f5fe3 | ||
|
|
a6c1e18aef | ||
|
|
8a37b81f4e | ||
|
|
f81b6fbcd6 | ||
|
|
223e61df2a | ||
|
|
303df626a2 | ||
|
|
9e8ad778dd | ||
|
|
1f18cc5416 | ||
|
|
e68a71022c | ||
|
|
e4a8bff70e | ||
|
|
10982e0275 | ||
|
|
8edc02b06d | ||
|
|
4b2aaee9ea | ||
|
|
9fdb33b6af | ||
|
|
3faecdb353 | ||
|
|
db96f41ad7 | ||
|
|
10fbf17d42 | ||
|
|
ef85809690 | ||
|
|
95bce5d656 | ||
|
|
cedbafeb28 | ||
|
|
7c1d2f4bc4 | ||
|
|
8099d6dbd7 | ||
|
|
8e01b68cf6 | ||
|
|
d707a07f84 | ||
|
|
5ab20bb27c | ||
|
|
8125a179fb | ||
|
|
041563f8ea | ||
|
|
c3f7c85f8a | ||
|
|
12b2efa489 | ||
|
|
5d403a7b49 | ||
|
|
3c88f94b43 | ||
|
|
06636a0e1c | ||
|
|
8416b4e42c | ||
|
|
1a0ed61f78 | ||
|
|
68501759fa | ||
|
|
dd85b1e519 | ||
|
|
2d3cf1bdb1 | ||
|
|
baa8021c79 | ||
|
|
133540f577 | ||
|
|
43c103c0ac | ||
|
|
162ac787b7 | ||
|
|
fa7d5bc023 | ||
|
|
9d45088127 | ||
|
|
7ef56de3f2 | ||
|
|
0d76b3f308 | ||
|
|
92c4c49b01 | ||
|
|
855df5d679 | ||
|
|
c8d9cc7e5b | ||
|
|
1c1fbf14cf | ||
|
|
9e4dc235d7 | ||
|
|
4bd8cf6f5c | ||
|
|
d9c754c30f | ||
|
|
ea028a3822 | ||
|
|
7d9e6b7e22 | ||
|
|
17b5cace5b | ||
|
|
2fa41b23b9 | ||
|
|
9f5f6111d4 | ||
|
|
0782164120 | ||
|
|
5439fe7b16 | ||
|
|
55111ac35f | ||
|
|
f0cc5d9ca8 | ||
|
|
ccfeab5ac9 | ||
|
|
7e011bda6f | ||
|
|
8418c39120 | ||
|
|
4b8ef57a99 | ||
|
|
d910b0b2a2 | ||
|
|
80c8de7d69 | ||
|
|
7f776deae2 | ||
|
|
8209ba8a00 | ||
|
|
25c8e873d0 | ||
|
|
e24db75f08 | ||
|
|
5df7463663 | ||
|
|
eb7331f2ab | ||
|
|
03bd0820cc | ||
|
|
63768166ea | ||
|
|
bd899a7a7c | ||
|
|
301442ee43 | ||
|
|
de9dd1180b | ||
|
|
436ed4d1e3 | ||
|
|
88aa273e55 | ||
|
|
d11d8409a8 | ||
|
|
9385d1b6d8 | ||
|
|
b90653f3e3 | ||
|
|
1243cf04ea | ||
|
|
4f5b5b1377 | ||
|
|
f43675e2bd | ||
|
|
417cf33f90 | ||
|
|
067b1f3ee0 | ||
|
|
3d2f41d081 | ||
|
|
49de28d3d0 | ||
|
|
c34cf11769 | ||
|
|
42facbb07e | ||
|
|
9f97cd61bf | ||
|
|
59133a7d93 | ||
|
|
b484cbba7c | ||
|
|
61e34621cc | ||
|
|
5e9575de66 | ||
|
|
c02d34dbf3 | ||
|
|
8f97637b71 | ||
|
|
878d3a6f4f | ||
|
|
ef25bbde75 | ||
|
|
8c3efa5926 | ||
|
|
e6656326a8 | ||
|
|
cf8cab850b | ||
|
|
db5dfa1746 | ||
|
|
13457d1bf4 | ||
|
|
8f8c4d3d95 | ||
|
|
d47bd1ecbc | ||
|
|
e9fcb5381a | ||
|
|
3e2631f49b | ||
|
|
3c3236c5d5 | ||
|
|
23462ec7df | ||
|
|
ac56b0a33e | ||
|
|
b55c604c0a | ||
|
|
a72f8f3bcd | ||
|
|
27c0405fc9 | ||
|
|
b9e80ecfdc | ||
|
|
a3adb38bef | ||
|
|
593ba37c43 | ||
|
|
4ed78a84ef | ||
|
|
e893662c0a | ||
|
|
5df78c520c | ||
|
|
f13f886886 | ||
|
|
5dd8e4dc7f | ||
|
|
883fd5b062 | ||
|
|
7ddbf20108 | ||
|
|
23135d7a5a | ||
|
|
b91ab0b44f | ||
|
|
9a2ec13ba4 | ||
|
|
78995f5cca | ||
|
|
f225c4773a | ||
|
|
bac4b3d5cb | ||
|
|
e3f99a4a22 | ||
|
|
df7d3261c9 | ||
|
|
95ad4783f1 | ||
|
|
b5b0899226 | ||
|
|
9a0a1baa6b | ||
|
|
6ad9f45d9a | ||
|
|
7ec223d445 | ||
|
|
ed9e837229 | ||
|
|
b9a08e8260 | ||
|
|
58931e1d30 | ||
|
|
976ca80056 | ||
|
|
95e5295666 | ||
|
|
4b16999fbc | ||
|
|
318d23ba1c | ||
|
|
9c4c7c08bf | ||
|
|
9016a9e8b8 | ||
|
|
4c8c41fdc0 | ||
|
|
205d3b1d04 | ||
|
|
1575fff07a | ||
|
|
8c7338f2ba | ||
|
|
459cc65b3f | ||
|
|
3d28275675 | ||
|
|
6f41b3cde7 | ||
|
|
9ffc09a11b | ||
|
|
ced2608afd | ||
|
|
adbca532c0 | ||
|
|
5e38d8f28a | ||
|
|
d6ef2c04a5 | ||
|
|
ce69783871 | ||
|
|
10b13da3da | ||
|
|
88afefe464 | ||
|
|
80abf6bf24 | ||
|
|
94359709a1 | ||
|
|
8501b7c9e2 | ||
|
|
0ce0855a6f | ||
|
|
e1dabbc2d5 | ||
|
|
c08a489e27 | ||
|
|
28a2ba4bf8 | ||
|
|
2b8b8e7403 | ||
|
|
bdaf60b2d6 | ||
|
|
7c2f994a66 | ||
|
|
bb9f5b7491 | ||
|
|
c091d74de4 | ||
|
|
19be09c361 | ||
|
|
57d053a8fb | ||
|
|
8c2a148ed1 | ||
|
|
0e43765c53 | ||
|
|
6fbaf57b99 | ||
|
|
12aee3e022 | ||
|
|
2df00647d5 | ||
|
|
498a8e2b7d | ||
|
|
99395273c7 | ||
|
|
4568156bdf | ||
|
|
ce7611562f | ||
|
|
81fd0eefac | ||
|
|
b5fd15e052 | ||
|
|
ecfc684174 | ||
|
|
4600ea135c | ||
|
|
3b82ba8945 | ||
|
|
c32fc57ce1 | ||
|
|
9a8f66e3e5 | ||
|
|
ef39713219 | ||
|
|
da68fea081 | ||
|
|
1f93a4ab4f | ||
|
|
7a587390ba | ||
|
|
378a145cf7 | ||
|
|
3625f11e08 | ||
|
|
1332ba3ad2 | ||
|
|
558b2ffa41 | ||
|
|
1315a3967d | ||
|
|
00b76d42e0 | ||
|
|
e7c4150c02 | ||
|
|
bc06c843c7 | ||
|
|
b8e9439d32 | ||
|
|
046d385a78 | ||
|
|
5caf75237b | ||
|
|
a19b1b9341 | ||
|
|
0b1e876dcf | ||
|
|
ae8c8ec230 | ||
|
|
ec8c0eb3c2 | ||
|
|
3d898dd8f7 | ||
|
|
df18e178ee | ||
|
|
71a6e08988 | ||
|
|
a05636d9a6 | ||
|
|
5eecbfd9bf | ||
|
|
65514b3028 | ||
|
|
881d55f858 | ||
|
|
cc20d9d0fe | ||
|
|
35aa544415 | ||
|
|
bad4ca4666 | ||
|
|
5fecc57e8a | ||
|
|
e1f4e3d2d7 | ||
|
|
5ace9e4189 | ||
|
|
9945459a41 | ||
|
|
22c11fad36 | ||
|
|
c6b527d452 | ||
|
|
8e9911d330 | ||
|
|
b245725941 | ||
|
|
154ae5dcbf | ||
|
|
bae2037086 | ||
|
|
c0bc8d7028 | ||
|
|
736b5ff424 | ||
|
|
29cd2b11be | ||
|
|
dcf773c3ef | ||
|
|
791cabbc26 | ||
|
|
3c7d8063f6 | ||
|
|
3381340eb5 | ||
|
|
6446b9ef10 | ||
|
|
6c19eb59b2 | ||
|
|
797d049f31 | ||
|
|
0bcafedebf | ||
|
|
7e0e8286eb | ||
|
|
5ae1162378 | ||
|
|
c80c4ae55c | ||
|
|
254b88bdbe | ||
|
|
2657d89178 | ||
|
|
38ec59f03a | ||
|
|
259cf1ff3a | ||
|
|
b12658afc2 | ||
|
|
c23880f829 | ||
|
|
41dd9e4f06 | ||
|
|
3d1f69075a | ||
|
|
dc94a3fac9 | ||
|
|
468cac543f | ||
|
|
d4b48216e8 | ||
|
|
f83ff41e47 | ||
|
|
891ce06312 | ||
|
|
0658ba6f77 | ||
|
|
29d8d1d74c | ||
|
|
8d2efb2838 | ||
|
|
e07a40a16d | ||
|
|
38a3eda6a7 | ||
|
|
6d1ab7606b | ||
|
|
77edf1e103 | ||
|
|
28c8678ea2 | ||
|
|
34acd9d47c | ||
|
|
05ca76ea99 | ||
|
|
d1065f0bd1 | ||
|
|
cf0ec7b9a9 | ||
|
|
46085824ae | ||
|
|
7067179b28 | ||
|
|
4828a69867 | ||
|
|
3fcc808e99 | ||
|
|
15cdd661a4 | ||
|
|
60d9f3de68 | ||
|
|
f87baaa4fc | ||
|
|
fecd8a448f | ||
|
|
fe4ef75cf6 | ||
|
|
87c7a9db54 | ||
|
|
fa2ce40084 | ||
|
|
4eac3fe9a0 | ||
|
|
d7efd62511 | ||
|
|
e10b399f51 | ||
|
|
39a8195386 | ||
|
|
43331419f6 | ||
|
|
c66a55d81a | ||
|
|
9a44a38141 | ||
|
|
5f69cb2a5b | ||
|
|
a5f63c3ae3 | ||
|
|
0053962182 | ||
|
|
c462d0b249 | ||
|
|
778edaeb28 | ||
|
|
273a395a2f | ||
|
|
aa49ebd47f | ||
|
|
c45384b91a | ||
|
|
88fdbc6d3e | ||
|
|
7ca6c5ef34 | ||
|
|
511d551546 | ||
|
|
7ea13f7e03 | ||
|
|
6754703ad1 | ||
|
|
89b8e5435f | ||
|
|
b0ab351f7f | ||
|
|
9b6f4ecd0d | ||
|
|
9142cbb820 | ||
|
|
1f6b386325 | ||
|
|
aad1266a94 | ||
|
|
06a40680aa | ||
|
|
cc2cbf810d | ||
|
|
cf9aa90321 | ||
|
|
d51f6c45e5 | ||
|
|
017ec83ce3 | ||
|
|
6641612de5 | ||
|
|
218718a063 | ||
|
|
353041535c | ||
|
|
e4234f5198 | ||
|
|
d81840d6c6 | ||
|
|
1d336996be | ||
|
|
86c43df8d1 | ||
|
|
5c1b7ecd17 | ||
|
|
011ac1fa22 | ||
|
|
b8e5258cf3 | ||
|
|
56179088bb | ||
|
|
780e9580b7 | ||
|
|
b775b01e0a | ||
|
|
0a8e0e9746 | ||
|
|
89805a44c1 | ||
|
|
9caa0af4d9 | ||
|
|
70531762b9 | ||
|
|
30f4ecd171 | ||
|
|
d56c3f9adf | ||
|
|
73a97c1774 | ||
|
|
8ec2b1d96f | ||
|
|
c858e0391d | ||
|
|
63c9af4578 | ||
|
|
473a87a76b | ||
|
|
4c6f6536b8 | ||
|
|
cb4d8a6555 | ||
|
|
417545d521 | ||
|
|
7f88bd15d1 | ||
|
|
7b3f6eeae2 | ||
|
|
f6df7cad9b | ||
|
|
15cb505163 | ||
|
|
cc1b94afd2 | ||
|
|
a7eca7d4bd | ||
|
|
9047c98e68 | ||
|
|
53d0789660 | ||
|
|
ee24a6f4fc | ||
|
|
70616c48e3 | ||
|
|
d4bbd5cc6f | ||
|
|
1affae956b | ||
|
|
7463e02080 | ||
|
|
d3ed26f7cc | ||
|
|
f711ced4ca | ||
|
|
1ec89fc4f3 | ||
|
|
a92b7298f9 | ||
|
|
de07b01a75 | ||
|
|
4c6c096c50 | ||
|
|
c2ae897b02 | ||
|
|
b2e1291973 | ||
|
|
94c2b65798 | ||
|
|
760d6a26d3 | ||
|
|
3a419768ca | ||
|
|
d8ee44349c | ||
|
|
ee42448504 | ||
|
|
d90cf843e8 | ||
|
|
f6b700ea97 | ||
|
|
a5fe605aae | ||
|
|
7818c3bda3 | ||
|
|
d5e5755ff4 | ||
|
|
8fbc23b1fa | ||
|
|
d05f0820b2 | ||
|
|
dad44cc928 | ||
|
|
2c80587d11 | ||
|
|
80eb056432 | ||
|
|
80e546b79f | ||
|
|
6eba04ed8e | ||
|
|
c606671d27 | ||
|
|
9bb37d5df0 | ||
|
|
e952236e1a | ||
|
|
9a3100b221 | ||
|
|
7e9754acff | ||
|
|
b78592d622 | ||
|
|
052f746c68 | ||
|
|
c4ceec145a | ||
|
|
775386d8b3 | ||
|
|
451c08ef7b | ||
|
|
52c8743b36 | ||
|
|
5c59b9aeb3 | ||
|
|
3e00dabd16 | ||
|
|
c5c069743b | ||
|
|
ba241cd7f9 | ||
|
|
ddb97bfafb | ||
|
|
e415ca66b6 | ||
|
|
85be299da1 | ||
|
|
9dd5bea7a5 | ||
|
|
e71abedefb | ||
|
|
3888d7726a | ||
|
|
fc48944b47 | ||
|
|
2150f5879b | ||
|
|
85156d15ca | ||
|
|
6943d14f93 | ||
|
|
ade4e87d6f | ||
|
|
8007a7b153 | ||
|
|
e8643600f2 | ||
|
|
cb7f1f6e3e | ||
|
|
af7132be82 | ||
|
|
ec080118f4 | ||
|
|
6bcc0e2d82 | ||
|
|
2fd81a7e33 | ||
|
|
2f99f81aa6 | ||
|
|
97f1f1c9c4 | ||
|
|
0fbb2e8a14 | ||
|
|
0158fd35f0 | ||
|
|
80b15b7fe5 | ||
|
|
2acca6eeb1 | ||
|
|
151c89fb5d | ||
|
|
4f22ac4100 | ||
|
|
7c1da2dfcd | ||
|
|
b5d32ec844 | ||
|
|
aec16a934f | ||
|
|
075155b431 | ||
|
|
c25c62e0af | ||
|
|
e653a228fa | ||
|
|
b2831347bc | ||
|
|
7845caa100 | ||
|
|
66d214c8a8 | ||
|
|
277cf2a08f | ||
|
|
7bf44e951c | ||
|
|
c0ce4270f0 | ||
|
|
d6e8de21ef | ||
|
|
1635cba827 | ||
|
|
a786470623 | ||
|
|
4164e670d2 | ||
|
|
062470ef68 | ||
|
|
0b80bd2b09 | ||
|
|
e1515299c2 | ||
|
|
bc99d341fb | ||
|
|
0f9f7309e3 | ||
|
|
e6244289ff | ||
|
|
30fbfe46e6 | ||
|
|
a73166a665 | ||
|
|
d66f65e376 | ||
|
|
b979fc2a67 | ||
|
|
58092c5190 | ||
|
|
6615a34e99 | ||
|
|
50a2612839 | ||
|
|
eb4350033b | ||
|
|
384ad57d21 | ||
|
|
4b516de183 | ||
|
|
c7d9ecbab3 | ||
|
|
b2795af2b8 | ||
|
|
6c6c087e30 | ||
|
|
3c77faaf61 | ||
|
|
6272244a73 | ||
|
|
a532518056 | ||
|
|
320db8df18 | ||
|
|
d1fb8cc209 | ||
|
|
7d33059c20 | ||
|
|
ed0f197d1b | ||
|
|
25ad7a6230 | ||
|
|
5fb31baea6 | ||
|
|
ffb384ebfc | ||
|
|
95098c5496 | ||
|
|
3862668420 | ||
|
|
5ac68f74d4 | ||
|
|
b8ad1de33c | ||
|
|
f80312b86b | ||
|
|
836dc7a880 | ||
|
|
7283293887 | ||
|
|
710bd586d5 | ||
|
|
cb4cc63221 | ||
|
|
838d8abf63 | ||
|
|
94bd629b80 | ||
|
|
73d6e98bf2 | ||
|
|
c90a8c586f | ||
|
|
9092b74f4e | ||
|
|
b3d28c7e3a | ||
|
|
ed92385469 | ||
|
|
1247976a34 | ||
|
|
880bc7db9f | ||
|
|
20f23e0e31 | ||
|
|
d9efcbc7a9 | ||
|
|
5e2a07d58a | ||
|
|
620836e1cb | ||
|
|
af2f54720d | ||
|
|
236ef199aa | ||
|
|
1ea5b21dcf | ||
|
|
e6054f543a | ||
|
|
2aaaed34f9 | ||
|
|
fef8f933d9 | ||
|
|
d8de5e4f19 | ||
|
|
554543c5d0 | ||
|
|
44821c3e3c | ||
|
|
8cc066ecc4 | ||
|
|
2540f102b0 | ||
|
|
8b3f4aa68c | ||
|
|
0537f9d0d7 | ||
|
|
4b0980fb86 | ||
|
|
65e4eeeb7c | ||
|
|
298dc54910 | ||
|
|
fb486e381f | ||
|
|
32001427d4 | ||
|
|
2a43813d14 | ||
|
|
b4c0583896 | ||
|
|
88b985be5d | ||
|
|
7528b6b8bf | ||
|
|
e25d9fc96a | ||
|
|
b1a3ded2fa | ||
|
|
bc6da8effa | ||
|
|
5d26b13553 | ||
|
|
0d7106450b | ||
|
|
60647a2f8c | ||
|
|
76ee093e92 | ||
|
|
9e6635ec13 | ||
|
|
db91e20ec7 | ||
|
|
3cd20d3ff2 | ||
|
|
c2642f39ed | ||
|
|
7182b9a6b5 | ||
|
|
ac9e42af36 | ||
|
|
790862db4b | ||
|
|
bb9988365f | ||
|
|
f10f8558eb | ||
|
|
26c27756af | ||
|
|
c76e60324b | ||
|
|
d98ceb60c3 | ||
|
|
2e050b0540 | ||
|
|
44ba8823d7 | ||
|
|
4c42712b24 | ||
|
|
6a0485a72c | ||
|
|
967400f181 | ||
|
|
a142cc48d3 | ||
|
|
3469f6733c | ||
|
|
2b6c6f280c | ||
|
|
aefeee39aa | ||
|
|
27101979fa | ||
|
|
033520d426 | ||
|
|
16e68c3480 | ||
|
|
94b6073f34 | ||
|
|
8fc2eee2b4 | ||
|
|
969af7b610 | ||
|
|
da66118331 | ||
|
|
424efca1bf | ||
|
|
4d7ea37d56 | ||
|
|
32a1beb772 | ||
|
|
d850b2fd76 | ||
|
|
9f5fe63aaa | ||
|
|
03ee425e5f | ||
|
|
9200b7c78c | ||
|
|
b793810e4a | ||
|
|
9df5f5e27a | ||
|
|
7fbd0e34d3 | ||
|
|
77cf3adf64 | ||
|
|
dd0e3e4abb | ||
|
|
d77b3d788e | ||
|
|
dff6e89b4b | ||
|
|
57b00d3b38 | ||
|
|
898952a2df | ||
|
|
202cb8bb1f | ||
|
|
4592c90e34 | ||
|
|
734e9a4461 | ||
|
|
90959defd9 | ||
|
|
51064f5e75 | ||
|
|
3e8afc14b0 | ||
|
|
424bf51a7b | ||
|
|
75358e6c45 | ||
|
|
e2879b7bf2 | ||
|
|
b80d2f6659 | ||
|
|
4c83259acb | ||
|
|
9c50e9f964 | ||
|
|
a3effbfb9f | ||
|
|
90ce262bb3 | ||
|
|
23448c8277 | ||
|
|
d9fa086ec0 | ||
|
|
3b91fac192 | ||
|
|
e42f9b0603 | ||
|
|
c5d456f3a6 | ||
|
|
6cbc219427 | ||
|
|
4b06e74a14 | ||
|
|
42f973ebe0 | ||
|
|
84e7d5906c | ||
|
|
51eb96903c | ||
|
|
6d8e796932 | ||
|
|
f7e603c7db | ||
|
|
30ad381b6c | ||
|
|
5181c4e5be | ||
|
|
a2ec638db8 | ||
|
|
953a2ce807 | ||
|
|
e1efb7364e | ||
|
|
123ba9c670 | ||
|
|
d3e63e0078 | ||
|
|
21e21d3b8b | ||
|
|
702b6de734 | ||
|
|
6233b3477b | ||
|
|
07942bf422 | ||
|
|
66f9f73cb3 | ||
|
|
2dd9eafa4f | ||
|
|
b1783cc1db | ||
|
|
c5c615b7d3 | ||
|
|
897420a5f4 | ||
|
|
550b04cff8 | ||
|
|
46e0ac1258 | ||
|
|
22d984a6bd | ||
|
|
8d6f96adb9 | ||
|
|
1f431754a9 | ||
|
|
40093df91f | ||
|
|
f3bfee149c | ||
|
|
9b9b41f40e | ||
|
|
a451fd3f04 | ||
|
|
7565207242 | ||
|
|
e516200e09 | ||
|
|
5beaf45773 | ||
|
|
1ca0157768 | ||
|
|
dbe4168d8d | ||
|
|
969916851c | ||
|
|
b00b4cb185 | ||
|
|
2b9e7c6af1 | ||
|
|
0d9aaa86c6 | ||
|
|
28b613d60c | ||
|
|
b971c741d7 | ||
|
|
ecadb117da | ||
|
|
4b7dfc0254 | ||
|
|
a990859db6 | ||
|
|
9c576b10d0 | ||
|
|
b28807d791 | ||
|
|
b488cdd6ff | ||
|
|
302cc064c6 | ||
|
|
6e4d7ca933 | ||
|
|
6c8a5935c9 | ||
|
|
86445b6670 | ||
|
|
330ba39939 | ||
|
|
0f4eecebe6 | ||
|
|
a3dbe9a800 | ||
|
|
822dc45834 | ||
|
|
b8a99690b6 | ||
|
|
48b1120c31 | ||
|
|
c27a1cec48 | ||
|
|
9d4a66a400 | ||
|
|
c846dff524 | ||
|
|
29f73cb5c1 | ||
|
|
a8836b7665 | ||
|
|
96bf5c24b5 | ||
|
|
ab9de550d9 | ||
|
|
9cb9c3beb0 | ||
|
|
2d1bc2f4c7 | ||
|
|
c6bed1f464 | ||
|
|
c41341c7e3 | ||
|
|
f0e31487f0 | ||
|
|
a34143ae75 | ||
|
|
ca9ce0f3a3 | ||
|
|
a5ccafd924 | ||
|
|
9458da0454 | ||
|
|
80247e98e5 | ||
|
|
293d005432 | ||
|
|
a395e3577f | ||
|
|
8d7cdeabbf | ||
|
|
e199e3571b | ||
|
|
e9618df1b5 | ||
|
|
c34ba968f1 | ||
|
|
0cbcfbc3f0 | ||
|
|
492463411d | ||
|
|
cd53feb193 | ||
|
|
00feb67064 | ||
|
|
b3eed38187 | ||
|
|
f683a5c63c | ||
|
|
7d409f071e | ||
|
|
4146db6fc8 | ||
|
|
a86f65db1e | ||
|
|
53a0531def | ||
|
|
7db00575c9 | ||
|
|
5b65bfb64d | ||
|
|
3b0eb56cf6 | ||
|
|
e9073a3cc0 | ||
|
|
538a19fd47 | ||
|
|
84ff80710c | ||
|
|
0dcaf56ed1 | ||
|
|
6703968f73 | ||
|
|
8ec65dbfc8 | ||
|
|
4670ef6ec5 | ||
|
|
35e6400174 | ||
|
|
67de7af7b2 | ||
|
|
3b207ad2c3 | ||
|
|
b5274a495e | ||
|
|
11508b77d1 | ||
|
|
0a10778697 | ||
|
|
ecbf76e94b | ||
|
|
6c0103639c | ||
|
|
c2eb4a766e | ||
|
|
06e8d835dd | ||
|
|
3a71acec52 | ||
|
|
a9af4589ac | ||
|
|
4356e313ec | ||
|
|
afcfdb13a0 | ||
|
|
3a570add4e | ||
|
|
ead125555c | ||
|
|
98ad6d1b43 | ||
|
|
978cdadda0 | ||
|
|
cde7782c21 | ||
|
|
138ebf5b4d | ||
|
|
a127bca0e4 | ||
|
|
83be99e78c | ||
|
|
5b5e3ae6aa | ||
|
|
f6576b18f7 | ||
|
|
9a621e9272 | ||
|
|
c01f2977ac | ||
|
|
683c53babc | ||
|
|
5823e3c780 | ||
|
|
a0d30ffef1 | ||
|
|
fb1b82e239 | ||
|
|
3bc5699ba1 | ||
|
|
18659257d3 | ||
|
|
3c7cb363ba | ||
|
|
787d39851b | ||
|
|
ae070aca70 | ||
|
|
63dd895e6d | ||
|
|
507b083e77 | ||
|
|
13bc05d9ec | ||
|
|
3f24b86875 | ||
|
|
1368f0ec8f | ||
|
|
196fcf791b | ||
|
|
6bccf528d7 | ||
|
|
1fc03803cd | ||
|
|
8fa0e6c914 | ||
|
|
6f940bcaaf | ||
|
|
79f732f239 | ||
|
|
593bc28446 | ||
|
|
9c204533e8 | ||
|
|
4c14f7823a | ||
|
|
4aa2d65bba | ||
|
|
bb5848a033 | ||
|
|
9f728b850e | ||
|
|
b4c65dc210 | ||
|
|
5df62ac172 | ||
|
|
0979c1c9ca | ||
|
|
8bbe5f9fdb | ||
|
|
3cd3d1691e | ||
|
|
6b2fb02bed | ||
|
|
8938a19810 | ||
|
|
158ae61811 | ||
|
|
98d45777c6 | ||
|
|
d059022071 | ||
|
|
c9260ea785 | ||
|
|
c9ca2f902e | ||
|
|
29691e0ac5 | ||
|
|
a3fcce891f | ||
|
|
8db016c223 | ||
|
|
b0802f3e26 | ||
|
|
e4731a4e4e | ||
|
|
67ae9e1ba7 | ||
|
|
d6afbc56c4 | ||
|
|
ffb54872c0 | ||
|
|
ead6b4960f | ||
|
|
21b7acc397 | ||
|
|
173111c64c | ||
|
|
cf18355fe2 | ||
|
|
1cc148848b | ||
|
|
ec54904347 | ||
|
|
0a18d2e57b | ||
|
|
c08e2ed8fc | ||
|
|
2437f06c7f | ||
|
|
9b7e265762 | ||
|
|
c4a7f6ec9b | ||
|
|
d05656b716 | ||
|
|
04789d9ae4 | ||
|
|
77acf1f35b | ||
|
|
412e8034de | ||
|
|
a6162ba990 | ||
|
|
7c51178608 | ||
|
|
899c489124 | ||
|
|
3062e1e740 | ||
|
|
854bfba7c9 | ||
|
|
1d4de091f9 | ||
|
|
440916cf2a | ||
|
|
3f4feb2f5c | ||
|
|
3025a7e51e | ||
|
|
fcbd2751ba | ||
|
|
56f1481c24 | ||
|
|
2ea20b8e81 | ||
|
|
9906116d23 | ||
|
|
555725a05b | ||
|
|
13cbf4e288 | ||
|
|
beb92e6cbf | ||
|
|
4d3fb3c497 | ||
|
|
dbd0c98306 | ||
|
|
53fb702512 | ||
|
|
e640708245 | ||
|
|
8fdaf61ef1 | ||
|
|
a5713252dc | ||
|
|
f5ec5e180d | ||
|
|
27e0f22c04 | ||
|
|
2016bcb37a | ||
|
|
1760d32019 | ||
|
|
9808c6e62b | ||
|
|
071b278b71 | ||
|
|
f4673f3123 | ||
|
|
8ee4c36e02 | ||
|
|
0f876d1989 | ||
|
|
eecf0deeec | ||
|
|
6aa32e8752 | ||
|
|
a55b6565b8 | ||
|
|
c1c0c8d418 | ||
|
|
1634773529 | ||
|
|
547e2ad72b | ||
|
|
ca90648118 | ||
|
|
958fbaa819 | ||
|
|
fa19a54ab7 | ||
|
|
c9c1b7e5d8 | ||
|
|
4d5e5c82d4 | ||
|
|
9bd4a5ecc3 | ||
|
|
09d6619a58 | ||
|
|
e7ba40dcfd | ||
|
|
ae42eee8e1 | ||
|
|
1e9b9b9a3e | ||
|
|
1225d3ef75 | ||
|
|
407ded6b8c | ||
|
|
c7b5266f9a | ||
|
|
5a0fccc9cf | ||
|
|
b66fb68f29 | ||
|
|
cb30bb5d70 | ||
|
|
40ebb9a844 | ||
|
|
b7a87bb6f9 | ||
|
|
e38cce46ad | ||
|
|
f6deafee22 | ||
|
|
8d6a0786c8 | ||
|
|
9ee3070667 | ||
|
|
05e084364a | ||
|
|
69e91244f4 | ||
|
|
e37a9d81f1 | ||
|
|
95d0736bb6 | ||
|
|
de4ba1aa35 | ||
|
|
d7ca3343bc | ||
|
|
cee77a3228 | ||
|
|
f5a82be9e5 | ||
|
|
2a1ccaff1e | ||
|
|
b195dfbe09 | ||
|
|
0aab4c27e1 | ||
|
|
42ad98567c | ||
|
|
c06e625413 | ||
|
|
1a48e75d43 | ||
|
|
a896a3ade5 | ||
|
|
62ef9e1c6b | ||
|
|
047cccda89 | ||
|
|
dbed3fea6f | ||
|
|
b88a9295bf | ||
|
|
c4d4679ed9 | ||
|
|
c2063f3a88 | ||
|
|
03bae15b96 | ||
|
|
842fb957a6 |
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,11 +1,8 @@
|
||||
# WPIlib Specific
|
||||
|
||||
dependency-reduced-pom.xml
|
||||
/wpilibj/src/shared/java/edu/wpi/first/wpilibj/util/WPILibVersion.java
|
||||
/wpilibc/shared/src/WPILibVersion.cpp
|
||||
/wpilibj/src/main/java/edu/wpi/first/wpilibj/util/WPILibVersion.java
|
||||
/wpilibc/src/main/native/cpp/WPILibVersion.cpp
|
||||
doxygen.log
|
||||
build*/
|
||||
|
||||
# Created by the jenkins test script
|
||||
test-reports
|
||||
@@ -15,6 +12,7 @@ test-reports
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
out/
|
||||
|
||||
# Created by http://www.gitignore.io
|
||||
|
||||
@@ -204,7 +202,6 @@ ipch/
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
@@ -217,3 +214,10 @@ ipch/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
|
||||
# compile_commands
|
||||
compile_commands.json
|
||||
|
||||
# clang configuration and clangd cache
|
||||
.clang
|
||||
.clangd/
|
||||
|
||||
38
.styleguide
38
.styleguide
@@ -9,33 +9,33 @@ cppSrcFileInclude {
|
||||
}
|
||||
|
||||
generatedFileExclude {
|
||||
gmock/
|
||||
gtest/
|
||||
ni-libraries/include/
|
||||
ni-libraries/lib/
|
||||
hal/src/main/native/athena/ctre/
|
||||
hal/src/main/native/athena/frccansae/
|
||||
hal/src/main/native/athena/visa/
|
||||
hal/src/main/native/include/ctre/
|
||||
UsageReporting\.h$
|
||||
FRCNetComm\.java$
|
||||
simulation/frc_gazebo_plugins/frcgazebo/
|
||||
simulation/gz_msgs/src/include/simulation/gz_msgs/msgs\.h$
|
||||
}
|
||||
|
||||
modifiableFileExclude {
|
||||
wpilibj/src/arm-linux-jni/
|
||||
wpilibj/src/main/native/cpp/
|
||||
\.patch$
|
||||
\.png$
|
||||
\.py$
|
||||
\.so$
|
||||
}
|
||||
|
||||
includeOtherLibs {
|
||||
^HAL/
|
||||
^llvm/
|
||||
^networktables/
|
||||
^opencv2/
|
||||
^support/
|
||||
repoRootNameOverride {
|
||||
wpilib
|
||||
}
|
||||
|
||||
includeProject {
|
||||
^ctre/
|
||||
includeOtherLibs {
|
||||
^cameraserver/
|
||||
^cscore
|
||||
^hal/
|
||||
^imgui
|
||||
^mockdata/
|
||||
^networktables/
|
||||
^ntcore
|
||||
^opencv2/
|
||||
^support/
|
||||
^units/
|
||||
^vision/
|
||||
^wpi/
|
||||
}
|
||||
|
||||
40
.travis.yml
40
.travis.yml
@@ -1,40 +0,0 @@
|
||||
sudo: true
|
||||
dist: trusty
|
||||
language: java
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- deadsnakes
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-6
|
||||
- python3.5
|
||||
|
||||
before_install:
|
||||
- sudo sh -c 'echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main" > /etc/apt/sources.list.d/llvm.list'
|
||||
- wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
- sudo apt-get update -q || true
|
||||
- sudo apt-get install clang-format-5.0 -y
|
||||
|
||||
install:
|
||||
- wget https://bootstrap.pypa.io/get-pip.py
|
||||
- sudo python3.5 get-pip.py
|
||||
- python3.5 -m pip install --user wpiformat
|
||||
- mkdir -p $HOME/latest-gcc-symlinks # see travis-ci/travis-ci#3668
|
||||
- ln -s /usr/bin/g++-6 $HOME/latest-gcc-symlinks/g++
|
||||
- ln -s /usr/bin/gcc-6 $HOME/latest-gcc-symlinks/gcc
|
||||
- export PATH=$HOME/latest-gcc-symlinks:$PATH
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
script:
|
||||
- python3.5 -m wpiformat -y 2018 -clang 5.0
|
||||
- git --no-pager diff --exit-code HEAD # Ensure formatter made no changes
|
||||
- ./gradlew --no-daemon --console=plain -PskipAthena :hal:halSimSharedLibrary :wpilibc:wpilibcSharedLibrary :wpilibj:wpilibJNISharedSharedLibrary :wpilibj:jar
|
||||
6
.wpilib/wpilib_preferences.json
Normal file
6
.wpilib/wpilib_preferences.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"enableCppIntellisense": true,
|
||||
"currentLanguage": "cpp",
|
||||
"projectYear": "2019",
|
||||
"teamNumber": 0
|
||||
}
|
||||
136
CMakeLists.txt
Normal file
136
CMakeLists.txt
Normal file
@@ -0,0 +1,136 @@
|
||||
# Disable in-source builds to prevent source tree corruption.
|
||||
if(" ${CMAKE_SOURCE_DIR}" STREQUAL " ${CMAKE_BINARY_DIR}")
|
||||
message(FATAL_ERROR "
|
||||
FATAL: In-source builds are not allowed.
|
||||
You should create a separate directory for build files.
|
||||
")
|
||||
endif()
|
||||
|
||||
|
||||
project(allwpilib)
|
||||
cmake_minimum_required(VERSION 3.3.0)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
INCLUDE(CPack)
|
||||
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND MSVC)
|
||||
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Default install dir on windows" FORCE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${CMAKE_BINARY_DIR}/jar)
|
||||
|
||||
# use, i.e. don't skip the full RPATH for the build tree
|
||||
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||
|
||||
# when building, don't use the install RPATH already
|
||||
# (but later on when installing)
|
||||
SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
|
||||
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/wpilib/lib")
|
||||
|
||||
# add the automatically determined parts of the RPATH
|
||||
# which point to directories outside the build tree to the install RPATH
|
||||
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
|
||||
# the RPATH to be used when installing, but only if it's not a system directory
|
||||
LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/wpilib/lib" isSystemDir)
|
||||
IF("${isSystemDir}" STREQUAL "-1")
|
||||
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/wpilib/lib")
|
||||
ENDIF("${isSystemDir}" STREQUAL "-1")
|
||||
|
||||
option(WITHOUT_JAVA "don't include java and JNI in the build" OFF)
|
||||
option(BUILD_SHARED_LIBS "build with shared libs (needed for JNI)" ON)
|
||||
option(WITHOUT_CSCORE "Don't build cscore (removes OpenCV requirement)" OFF)
|
||||
option(WITHOUT_ALLWPILIB "Don't build allwpilib (removes OpenCV requirement)" ON)
|
||||
option(WITH_TESTS "build unit tests (requires internet connection)" OFF)
|
||||
option(USE_EXTERNAL_HAL "Use a separately built HAL" OFF)
|
||||
set(EXTERNAL_HAL_FILE "" CACHE FILEPATH "Location to look for an external HAL CMake File")
|
||||
option(USE_VCPKG_LIBUV "Use vcpkg libuv" OFF)
|
||||
option(USE_VCPKG_EIGEN "Use vcpkg eigen" OFF)
|
||||
option(FLAT_INSTALL_WPILIB "Use a flat install directory" OFF)
|
||||
option(WITH_SIMULATION_MODULES "build simulation modules" OFF)
|
||||
|
||||
if (NOT WITHOUT_JAVA AND NOT BUILD_SHARED_LIBS)
|
||||
message(FATAL_ERROR "
|
||||
FATAL: Cannot build static libs with Java enabled.
|
||||
Static libs requires both BUILD_SHARED_LIBS=OFF and
|
||||
WITHOUT_JAVA=ON
|
||||
")
|
||||
endif()
|
||||
|
||||
set( wpilib_dest wpilib)
|
||||
set( include_dest wpilib/include )
|
||||
set( main_lib_dest wpilib/lib )
|
||||
set( java_lib_dest wpilib/java )
|
||||
set( jni_lib_dest wpilib/jni )
|
||||
|
||||
if (MSVC OR FLAT_INSTALL_WPILIB)
|
||||
set (wpilib_config_dir ${wpilib_dest})
|
||||
else()
|
||||
set (wpilib_config_dir share/wpilib)
|
||||
endif()
|
||||
|
||||
if (USE_VCPKG_LIBUV)
|
||||
set (LIBUV_VCPKG_REPLACE "find_package(unofficial-libuv CONFIG)")
|
||||
endif()
|
||||
|
||||
if (USE_VCPKG_EIGEN)
|
||||
set (EIGEN_VCPKG_REPLACE "find_package(Eigen3 CONFIG)")
|
||||
endif()
|
||||
|
||||
if (MSVC OR FLAT_INSTALL_WPILIB)
|
||||
set(WPIUTIL_DEP_REPLACE "include($\{SELF_DIR\}/wpiutil-config.cmake)")
|
||||
set(NTCORE_DEP_REPLACE "include($\{SELF_DIR\}/ntcore-config.cmake)")
|
||||
set(CSCORE_DEP_REPLACE_IMPL "include(\${SELF_DIR}/cscore-config.cmake)")
|
||||
set(CAMERASERVER_DEP_REPLACE_IMPL "include(\${SELF_DIR}/cameraserver-config.cmake)")
|
||||
set(HAL_DEP_REPLACE_IMPL "include(\${SELF_DIR}/hal-config.cmake)")
|
||||
set(WPILIBC_DEP_REPLACE_IMPL "include(\${SELF_DIR}/wpilibc-config.cmake)")
|
||||
else()
|
||||
set(WPIUTIL_DEP_REPLACE "find_dependency(wpiutil)")
|
||||
set(NTCORE_DEP_REPLACE "find_dependency(ntcore)")
|
||||
set(CSCORE_DEP_REPLACE_IMPL "find_dependency(cscore)")
|
||||
set(CAMERASERVER_DEP_REPLACE_IMPL "find_dependency(cameraserver)")
|
||||
set(HAL_DEP_REPLACE_IMPL "find_dependency(hal)")
|
||||
set(WPILIBC_DEP_REPLACE_IMPL "find_dependency(wpilibc)")
|
||||
endif()
|
||||
|
||||
set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)")
|
||||
set(SELF_DIR "$\{SELF_DIR\}")
|
||||
|
||||
if (WITH_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(googletest)
|
||||
include(GoogleTest)
|
||||
endif()
|
||||
|
||||
add_subdirectory(wpiutil)
|
||||
add_subdirectory(ntcore)
|
||||
|
||||
if (NOT WITHOUT_CSCORE)
|
||||
set(CSCORE_DEP_REPLACE ${CSCORE_DEP_REPLACE_IMPL})
|
||||
set(CAMERASERVER_DEP_REPLACE ${CAMERASERVER_DEP_REPLACE_IMPL})
|
||||
add_subdirectory(cscore)
|
||||
add_subdirectory(cameraserver)
|
||||
if (NOT WITHOUT_ALLWPILIB)
|
||||
set(HAL_DEP_REPLACE ${HAL_DEP_REPLACE_IMPL})
|
||||
set(WPILIBC_DEP_REPLACE ${WPILIBC_DEP_REPLACE_IMPL})
|
||||
add_subdirectory(hal)
|
||||
add_subdirectory(wpilibj)
|
||||
add_subdirectory(wpilibc)
|
||||
add_subdirectory(myRobot)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WITH_SIMULATION_MODULES AND NOT USE_EXTERNAL_HAL)
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(simulation)
|
||||
endif()
|
||||
|
||||
configure_file(wpilib-config.cmake.in ${CMAKE_BINARY_DIR}/wpilib-config.cmake )
|
||||
install(FILES ${CMAKE_BINARY_DIR}/wpilib-config.cmake DESTINATION ${wpilib_config_dir})
|
||||
@@ -20,7 +20,7 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
|
||||
- Substantial changes often need to have corresponding LabVIEW changes. To do this, we will work with NI on these large changes.
|
||||
- Changes should have tests.
|
||||
- Code should be well documented.
|
||||
- This often involves ScreenSteps. To add content to ScreenSteps, we will work with you to get the appropriate articles written.
|
||||
- This involves writing tutorials and/or usage guides for your submitted feature. These articles are then hosted on the [WPILib](https://docs.wpilib.org/) documentation website. See the [frc-docs repository](https://github.com/wpilibsuite/frc-docs) for more information.
|
||||
|
||||
## What to Contribute
|
||||
|
||||
@@ -37,7 +37,7 @@ So you want to contribute your changes back to WPILib. Great! We have a few cont
|
||||
|
||||
## Coding Guidelines
|
||||
|
||||
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system.
|
||||
WPILib uses modified Google style guides for both C++ and Java, which can be found in the [styleguide repository](https://github.com/wpilibsuite/styleguide). Autoformatters are available for many popular editors at https://github.com/google/styleguide. Running wpiformat is required for all contributions and is enforced by our continuous integration system. We currently use clang-format 5.0 with wpiformat.
|
||||
|
||||
While the library should be fully formatted according to the styles, additional elements of the style guide were not followed when the library was initially created. All new code should follow the guidelines. If you are looking for some easy ramp-up tasks, finding areas that don't follow the style guide and fixing them is very welcome.
|
||||
|
||||
@@ -53,4 +53,4 @@ When you first submit changes, Travis-CI will attempt to run `./gradlew check` o
|
||||
|
||||
## Licensing
|
||||
|
||||
By contributing to WPILib, you agree that your code will be distributed with WPILib, and licensed under the license for the WPILib project. You should not contribute code that you do not have permission to relicense in this manner. This includes code that is licensed under the GPL that you do not have permission to relicense, as WPILib is not released under a copyleft license. Our license is the 3-clause BSD license, which you can find [here](license.txt).
|
||||
By contributing to WPILib, you agree that your code will be distributed with WPILib, and licensed under the license for the WPILib project. You should not contribute code that you do not have permission to relicense in this manner. This includes code that is licensed under the GPL that you do not have permission to relicense, as WPILib is not released under a copyleft license. Our license is the 3-clause BSD license, which you can find [here](LICENSE.txt).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2017 FIRST
|
||||
Copyright (c) 2009-2018 FIRST
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
102
MavenArtifacts.md
Normal file
102
MavenArtifacts.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# WPILib Maven Artifacts
|
||||
|
||||
WPILib publishes its built artifacts to our Maven server for use by downstream projects. This document explains these locations, and the meanings of artifact names, classifiers, and versions.
|
||||
|
||||
## Repositories
|
||||
We provide two repositories. These repositories are:
|
||||
|
||||
* (Release) https://first.wpi.edu/FRC/roborio/maven/release/
|
||||
* (Development) https://first.wpi.edu/FRC/roborio/maven/development/
|
||||
|
||||
The release repository is where official WPILib releases are pushed.
|
||||
The development repository is where development releases of every commit to [master](https://github.com/wpilibsuite/allwpilib/tree/master) is pushed.
|
||||
|
||||
## Artifact classifiers
|
||||
We provide two base types of artifacts.
|
||||
|
||||
The first types are Java artifacts. These are usually published as `jar` files. Usually, the actual jar file is published with no classifier. The sources are published with the `-sources` classifier, and the javadocs are published with the `-javadoc` classifier.
|
||||
|
||||
The second types are native artifacts. These are usually published as `zip` files (except for the `JNI` artifact types, which are `jar` files. See below for information on this). The `-sources` and `-headers` classifiers contain the sources and headers respecively for the library. Each artifact also contains a classifier for each platform we publish. This platform is in the format `{os}{arch}`. The platform artifact only contains the binaries for a specific platform. In addition, we provide a `-all` classifier. This classifer combines all of the platform artifacts into a single artifact. This is useful for tools that cannot determine what version to use during builds. However, we recommend using the platform specific classifier when possible. Note that the binary artifacts never contain the headers, you always need the `-headers` classifier to get those.
|
||||
|
||||
## Artifact Names
|
||||
|
||||
WPILib builds four different types of artifacts.
|
||||
|
||||
##### C++ Only Libraries
|
||||
When we publish C++ only libraries, they are published with the base artifact name as their artifact name, with a `-cpp` extension. All dependencies for the library are linked as shared libraries to the binary.
|
||||
|
||||
|
||||
Example:
|
||||
```
|
||||
edu.wpi.first.wpilibc:wpilibc-cpp:version:classifier@zip
|
||||
```
|
||||
|
||||
#### Java Only Libraries
|
||||
When we publish Java only libraries, they are published with the base artifact name as their artifact name, with a `-java` extension.
|
||||
|
||||
Example:
|
||||
```
|
||||
edu.wpi.first.wpilibj:wpilibj-java:version
|
||||
```
|
||||
|
||||
#### C++/Java Libraries without JNI
|
||||
For libraries that are both C++ and Java, but without a JNI component, the C++ component is published with the `basename-cpp` artifact name, and the Java component is published with the `basename-java` artifact name.
|
||||
|
||||
Example:
|
||||
```
|
||||
edu.wpi.first.wpiutil:wpiutil-cpp:version:classifier@zip (C++)
|
||||
edu.wpi.first.wpiutil:wpiutil-java:version (Java)
|
||||
```
|
||||
|
||||
#### C++/Java Libraries with JNI
|
||||
For libraries that are both C++ and Java with a JNI component there are three different artifact names. For Java, the component is published as `basename-java`. For C++, the `basename-cpp` artifact contains the C++ artifacts with all dependencies linked as shared libraries to the binary. These binaries DO contain the JNI entry points. The `basename-jni` artifact contains identical C++ binaries to the `-cpp` artifact, however all of its dependencies are statically linked, and only the JNI and C entry points are exported.
|
||||
|
||||
The `-jni` artifact should only be used in cases where you want to create a self contained Java application where the native artifacts are embedded in the jar. Note in an extraction scenario, extending off of the library is never supported, which is why the C++ entry points are not exposed. The name of the library is randomly generated during extraction. For pretty much all cases, and if you ever want to extend from a native library, you should use the `-cpp` artifacts. GradleRIO uses the `-cpp` artifacts for all platforms, even desktop, for this reason.
|
||||
|
||||
Example:
|
||||
```
|
||||
edu.wpi.first.ntcore:ntcore-cpp:version:classifier@zip (C++)
|
||||
edu.wpi.first.ntcore:ntcore-jni:version:classifier (JNI jar library)
|
||||
edu.wpi.first.ntcore:ntcore-java:version (Java)
|
||||
```
|
||||
|
||||
## Provided Artifacts
|
||||
This repository provides the following artifacts. Below each artifact is its dependencies. Note if ever using the `-jni` artifacts, no dependencies are needed for native binaries.
|
||||
|
||||
For C++, if building with static dependencies, the listed order should be the link order in your linker.
|
||||
|
||||
All artifacts are based at `edu.wpi.first.artifactname` in the repository.
|
||||
|
||||
* wpiutil
|
||||
|
||||
* hal
|
||||
* wpiutil
|
||||
|
||||
* ntcore
|
||||
* wpiutil
|
||||
|
||||
* cscore
|
||||
* opencv
|
||||
* wpiutil
|
||||
|
||||
* cameraserver
|
||||
* ntcore
|
||||
* cscore
|
||||
* opencv
|
||||
* wpiutil
|
||||
|
||||
|
||||
* wpilibj
|
||||
* hal
|
||||
* cameraserver
|
||||
* ntcore
|
||||
* cscore
|
||||
* wpiutil
|
||||
|
||||
|
||||
* wpilibc
|
||||
* hal
|
||||
* cameraserver
|
||||
* ntcore
|
||||
* cscore
|
||||
* wpiutil
|
||||
138
README-CMAKE.md
Normal file
138
README-CMAKE.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# WPILib CMake Support
|
||||
|
||||
WPILib is normally built with Gradle, however for some systems, such a linux based coprocessors, Gradle doesn't work correctly, especially if cscore is needed, which requires OpenCV. We provide the CMake build for these cases. Although it is supported on Windows, these docs will only go over linux builds.
|
||||
|
||||
## Libraries that get built
|
||||
* wpiutil
|
||||
* ntcore
|
||||
* cscore
|
||||
* cameraserver
|
||||
* hal
|
||||
* wpilib
|
||||
|
||||
By default, all libraries except for the HAL and WPILib get built with a default cmake setup. The libraries are built as shared libraries, and include the JNI libraries as well as building the Java jars.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The most common prerequisite is going to be OpenCV. OpenCV needs to be findable by cmake. On systems like the Jetson, this is installed by default. Otherwise, you will need to build cmake from source and install it.
|
||||
|
||||
In addition, if you want JNI and Java, you will need a JDK of at least version 8 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the jdk directory.
|
||||
|
||||
## Build Options
|
||||
|
||||
The following build options are available:
|
||||
|
||||
* WITHOUT_JAVA (OFF Default)
|
||||
* Enabling this option will disable Java and JNI builds. If this is off, `BUILD_SHARED_LIBS` must be on. Otherwise cmake will error.
|
||||
* BUILD_SHARED_LIBS (ON Default)
|
||||
* Disabling this option will cause cmake to build static libraries instead of shared libraries. If this is off, `WITHOUT_JAVA` must be on. Otherwise cmake will error.
|
||||
* WITHOUT_CSCORE (OFF Default)
|
||||
* Enabling this option will cause cscore to not be built. This will also implicitly disable cameraserver, the hal and wpilib as well, irrespective of their specific options. If this is on, the opencv build requirement is removed.
|
||||
* WITHOUT_ALLWPILIB (ON Default)
|
||||
* Disabling this option will build the hal and wpilib during the build. The HAL is the simulator hal, unless the external hal options are used. The cmake build has no capability to build for the RoboRIO.
|
||||
* USE_EXTERNAL_HAL (OFF Default)
|
||||
* TODO
|
||||
* EXTERNAL_HAL_FILE
|
||||
* TODO
|
||||
|
||||
## Build Setup
|
||||
|
||||
The WPILib CMake build does not allow in source builds. Because the `build` directory is used by gradle, we recommend a `buildcmake` directory in the root. This folder is included in the gitignore.
|
||||
|
||||
Once you have a build folder, run cmake configuration in that build directory with the following command.
|
||||
|
||||
```
|
||||
cmake path/to/allwpilib/root
|
||||
```
|
||||
|
||||
If you want to change any of the options, add `-DOPTIONHERE=VALUE` to the cmake command. This will check for any dependencies. If everything works properly this will succeed. If not, please check out the troubleshooting section for help.
|
||||
|
||||
If you want, you can also use `ccmake` in order to visually set these properties as well.
|
||||
https://cmake.org/cmake/help/v3.0/manual/ccmake.1.html
|
||||
Here is the link to the documentation for that program.
|
||||
|
||||
## Building
|
||||
|
||||
Once you have cmake setup. run `make` from the directory you configured cmake in. This will build all libraries possible. If you have a multicore system, we recommend running make with multiple jobs. The usual rule of thumb is 1.5x the number of cores you have. To run a multiple job build, run the following command with x being the number of jobs you want.
|
||||
|
||||
```
|
||||
make -jx
|
||||
```
|
||||
|
||||
## Installing
|
||||
|
||||
After build, the easiest way to use the libraries is to install them. Run the following command to install the libraries. This will install them so that they can be used from external cmake projects.
|
||||
|
||||
```
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Using the installed libraries for C++.
|
||||
|
||||
Using the libraries from C++ is the easiest way to use the built libraries.
|
||||
|
||||
To do so, create a new folder to contain your project. Add the following code below to a `CMakeLists.txt` file in that directory.
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(vision_app) # Project Name Here
|
||||
|
||||
find_package(wpilib REQUIRED)
|
||||
|
||||
add_executable(my_vision_app main.cpp) # exectuable name as first parameter
|
||||
target_link_libraries(my_vision_app cameraserver ntcore cscore wpiutil)
|
||||
```
|
||||
|
||||
If you are using them, `wpilibc` and `hal` should be added before the `cameraserver` declaration in the `target_link_libraries` function.
|
||||
|
||||
Add a `main.cpp` file to contain your code, and create a build folder. Move into the build folder, and run
|
||||
|
||||
```
|
||||
cmake /path/to/folder/containing/CMakeLists
|
||||
```
|
||||
|
||||
After that, run `make`. That will create your executable. Then you should be able to run `./my_vision_app` to run your application.
|
||||
|
||||
|
||||
## Using the installed libraries for Java
|
||||
TODO
|
||||
|
||||
## Troubleshooting
|
||||
Below are some common issues that are run into when building.
|
||||
|
||||
#### Missing OpenCV
|
||||
|
||||
If you are missing OpenCV, you will get an error message similar to this.
|
||||
|
||||
```
|
||||
CMake Error at cscore/CMakeLists.txt:3 (find_package):
|
||||
By not providing "FindOpenCV.cmake" in CMAKE_MODULE_PATH this project has
|
||||
asked CMake to find a package configuration file provided by "OpenCV", but
|
||||
CMake did not find one.
|
||||
|
||||
Could not find a package configuration file provided by "OpenCV" with any
|
||||
of the following names:
|
||||
|
||||
OpenCVConfig.cmake
|
||||
opencv-config.cmake
|
||||
|
||||
Add the installation prefix of "OpenCV" to CMAKE_PREFIX_PATH or set
|
||||
"OpenCV_DIR" to a directory containing one of the above files. If "OpenCV"
|
||||
provides a separate development package or SDK, be sure it has been
|
||||
installed.
|
||||
```
|
||||
|
||||
If you get that, you need make sure opencv was installed, and then reattempt to configure. If that doesn't work, set the `OpenCV_DIR` variable to the directory where you built OpenCV.
|
||||
|
||||
#### Missing Java
|
||||
|
||||
If you are missing Java, you will get a message like the following.
|
||||
```
|
||||
CMake Error at /usr/share/cmake-3.5/Modules/FindPackageHandleStandardArgs.cmake:148 (message):
|
||||
Could NOT find Java (missing: Java_JAVA_EXECUTABLE Java_JAR_EXECUTABLE
|
||||
Java_JAVAC_EXECUTABLE Java_JAVAH_EXECUTABLE Java_JAVADOC_EXECUTABLE)
|
||||
```
|
||||
|
||||
If this happens, make sure you have a JDK of at least version 8 installed, and that your JAVA_HOME variable is set properly to point to the JDK.
|
||||
|
||||
In addition, if you do not need Java, you can disable it with `-DWITHOUT_JAVA=ON`.
|
||||
25
README.md
25
README.md
@@ -1,6 +1,6 @@
|
||||
# WPILib Project
|
||||
|
||||
[](https://travis-ci.org/wpilibsuite/allwpilib)
|
||||
[](https://dev.azure.com/wpilib/wpilib/_build/latest?definitionId=1)
|
||||
|
||||
Welcome to the WPILib project. This repository contains the HAL, WPILibJ, and WPILibC projects. These are the core libraries for creating robot programs for the roboRIO.
|
||||
|
||||
@@ -15,7 +15,7 @@ Welcome to the WPILib project. This repository contains the HAL, WPILibJ, and WP
|
||||
|
||||
## WPILib Mission
|
||||
|
||||
The WPILib Mission is to enable FIRST Robotics teams to focus on writing game-specific software rather than focussing on hardware details - "raise the floor, don't lower the ceiling". We work to enable teams with limited programming knowledge and/or mentor experience to be as successful as possible, while not hampering the abilities of teams with more advanced programming capabilities. We support Kit of Parts control system components directly in the library. We also strive to keep parity between major features of each language (Java, C++, and NI's LabVIEW), so that teams aren't at a disadvantage for choosing a specific programming language. WPILib is an open source project, licensed under the BSD 3-clause license. You can find a copy of the license [here](license.txt).
|
||||
The WPILib Mission is to enable FIRST Robotics teams to focus on writing game-specific software rather than focusing on hardware details - "raise the floor, don't lower the ceiling". We work to enable teams with limited programming knowledge and/or mentor experience to be as successful as possible, while not hampering the abilities of teams with more advanced programming capabilities. We support Kit of Parts control system components directly in the library. We also strive to keep parity between major features of each language (Java, C++, and NI's LabVIEW), so that teams aren't at a disadvantage for choosing a specific programming language. WPILib is an open source project, licensed under the BSD 3-clause license. You can find a copy of the license [here](LICENSE.txt).
|
||||
|
||||
# Building WPILib
|
||||
|
||||
@@ -24,11 +24,11 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
|
||||
## Requirements
|
||||
|
||||
- A C++ compiler
|
||||
- On Linux, gcc works fine
|
||||
- On Windows, you need Visual Studio 2015 (the free community edition works fine).
|
||||
- On Linux, GCC works fine
|
||||
- On Windows, you need Visual Studio 2019 (the free community edition works fine).
|
||||
Make sure to select the C++ Programming Language for installation
|
||||
- [ARM Compiler Toolchain](http://first.wpi.edu/FRC/roborio/toolchains/)
|
||||
* Note that for 2017-2018 and beyond, you will need version 5 or greater of gcc
|
||||
- [ARM Compiler Toolchain](https://github.com/wpilibsuite/toolchain-builder/releases)
|
||||
* Note that for 2020 and beyond, you should use version 7 or greater of GCC
|
||||
- Doxygen (Only required if you want to build the C++ documentation)
|
||||
|
||||
## Setup
|
||||
@@ -79,23 +79,18 @@ There are a few tasks other than `build` available. To see them, run the meta-ta
|
||||
|
||||
wpiformat can be executed anywhere in the repository via `py -3 -m wpiformat` on Windows or `python3 -m wpiformat` on other platforms.
|
||||
|
||||
CMake is also supported for building. See [README-CMAKE.md](README-CMAKE.md).
|
||||
|
||||
## Publishing
|
||||
|
||||
If you are building to test with the Eclipse plugins or just want to export the build as a Maven-style dependency, simply run the `publish` task. This task will publish all available packages to ~/releases/maven/development. If you need to publish the project to a different repo, you can specify it with `-Prepo=repo_name`. Valid options are:
|
||||
If you are building to test with other dependencies or just want to export the build as a Maven-style dependency, simply run the `publish` task. This task will publish all available packages to ~/releases/maven/development. If you need to publish the project to a different repo, you can specify it with `-Prepo=repo_name`. Valid options are:
|
||||
|
||||
- development - The default repo.
|
||||
- beta - Publishes to ~/releases/maven/beta.
|
||||
- stable - Publishes to ~/releases/maven/stable.
|
||||
- release - Publishes to ~/releases/maven/release.
|
||||
|
||||
The following maven targets a published by this task:
|
||||
|
||||
- edu.wpi.first.wpilib.cmake:cpp-root:1.0.0 - roboRIO C++
|
||||
- edu.wpi.first.wpilibc.simulation:WPILibCSim:0.1.0 - Simulation C++
|
||||
- edu.wpi.first.wpilibj:wpilibJavaFinal:0.1.0-SNAPSHOT - roboRIO Java
|
||||
- edu.wpi.first.wpilibj:wpilibJavaSim:0.1.0-SNAPSHOT - Simulation Java
|
||||
- edu.wpi.first.wpilibj.simulation:SimDS:0.1.0-SNAPSHOT - The driverstation for controlling simulation.
|
||||
- org.gazebosim:JavaGazebo:0.1.0-SNAPSHOT - Gazebo protocol for Java.
|
||||
The maven artifacts are described in [MavenArtifacts.md](MavenArtifacts.md)
|
||||
|
||||
## Structure and Organization
|
||||
|
||||
|
||||
808
ThirdPartyNotices.txt
Normal file
808
ThirdPartyNotices.txt
Normal file
@@ -0,0 +1,808 @@
|
||||
==============================================================================
|
||||
Copyrights and Licenses for Third Party Software Distributed with WPILib
|
||||
==============================================================================
|
||||
The WPILib software contains code written by third parties. The copyrights,
|
||||
license, and restrictions which apply to each piece of software is included
|
||||
later in this file and/or inside of the individual applicable source files.
|
||||
|
||||
The disclaimer of warranty in the WPILib license above applies to all code in
|
||||
WPILib, and nothing in any of the other licenses gives permission to use the
|
||||
names of FIRST nor the names of the WPILib contributors to endorse or promote
|
||||
products derived from this software.
|
||||
|
||||
The following pieces of software have additional or alternate copyrights,
|
||||
licenses, and/or restrictions:
|
||||
|
||||
Program Locations
|
||||
------- ---------
|
||||
RoboRIO Libraries ni-libraries
|
||||
Google Test gtest
|
||||
LLVM wpiutil/src/main/native/include/wpi/{various files}
|
||||
wpiutil/src/main/native/cpp/llvm/
|
||||
wpiutil/src/main/native/cpp/leb128.cpp
|
||||
wpiutil/src/test/native/cpp/leb128Test.cpp
|
||||
JSON for Modern C++ wpiutil/src/main/native/include/wpi/json.h
|
||||
wpiutil/src/main/native/cpp/json_*.cpp
|
||||
wpiutil/src/test/native/cpp/json/
|
||||
libuv wpiutil/src/main/native/include/uv.h
|
||||
wpiutil/src/main/native/include/uv/
|
||||
wpiutil/src/main/native/libuv/
|
||||
sigslot wpiutil/src/main/native/include/wpi/Signal.h
|
||||
wpiutil/src/test/native/cpp/sigslot/
|
||||
tcpsockets wpiutil/src/main/native/cpp/TCP{Stream,Connector,Acceptor}.cpp
|
||||
wpiutil/src/main/native/include/wpi/TCP*.h
|
||||
Bootstrap wpiutil/src/main/native/resources/bootstrap-*
|
||||
CoreUI wpiutil/src/main/native/resources/coreui-*
|
||||
Feather Icons wpiutil/src/main/native/resources/feather-*
|
||||
jQuery wpiutil/src/main/native/resources/jquery-*
|
||||
popper.js wpiutil/src/main/native/resources/popper-*
|
||||
units wpiutil/src/main/native/include/units/units.h
|
||||
Eigen wpiutil/src/main/native/eigeninclude/
|
||||
StackWalker wpiutil/src/main/native/windows/StackWalker.*
|
||||
Team 254 Library wpilibj/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java
|
||||
wpilibj/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java
|
||||
wpilibc/src/main/native/include/spline/SplineParameterizer.h
|
||||
wpilibc/src/main/native/include/trajectory/TrajectoryParameterizer.h
|
||||
wpilibc/src/main/native/cpp/trajectory/TrajectoryParameterizer.cpp
|
||||
|
||||
|
||||
==============================================================================
|
||||
Google Test License
|
||||
==============================================================================
|
||||
Copyright 2008, Google Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
==============================================================================
|
||||
LLVM Release License
|
||||
==============================================================================
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2003-2017 University of Illinois at Urbana-Champaign.
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
University of Illinois at Urbana-Champaign
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois at
|
||||
Urbana-Champaign, nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this Software without specific
|
||||
prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
==============================================================================
|
||||
JSON for Modern C++ License
|
||||
==============================================================================
|
||||
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++
|
||||
| | |__ | | | | | | version 2.1.1
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
==============================================================================
|
||||
libuv License
|
||||
==============================================================================
|
||||
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
|
||||
|
||||
==============================================================================
|
||||
sigslot License
|
||||
==============================================================================
|
||||
Sigslot, a signal-slot library
|
||||
|
||||
https://github.com/palacaze/sigslot
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Pierre-Antoine Lacaze
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
==============================================================================
|
||||
tcpsockets License
|
||||
==============================================================================
|
||||
Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
==============================================================================
|
||||
Bootstrap License
|
||||
==============================================================================
|
||||
Copyright (c) 2011-2018 Twitter, Inc.
|
||||
Copyright (c) 2011-2018 The Bootstrap Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
==============================================================================
|
||||
CoreUI License
|
||||
==============================================================================
|
||||
Copyright (c) 2018 creativeLabs tukasz Holeczek.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
==============================================================================
|
||||
Feather Icons License
|
||||
==============================================================================
|
||||
Copyright (c) 2013-2017 Cole Bemis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
==============================================================================
|
||||
jQuery License
|
||||
==============================================================================
|
||||
Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
==============================================================================
|
||||
popper.js License
|
||||
==============================================================================
|
||||
Copyright (c) 2016 Federico Zivolo and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
=============
|
||||
units License
|
||||
=============
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Nic Holthaus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
=============
|
||||
Eigen license
|
||||
=============
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
|
||||
===================
|
||||
StackWalker License
|
||||
===================
|
||||
Copyright (c) 2005-2013, Jochen Kalmbach
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
Neither the name of Jochen Kalmbach nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
================
|
||||
Team 254 Library
|
||||
================
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Team 254
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
19
azure-pipelines-testbench.yaml
Normal file
19
azure-pipelines-testbench.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
# Starter pipeline
|
||||
# Start with a minimal pipeline that you can customize to build and deploy your code.
|
||||
# Add steps that build, run tests, deploy, and more:
|
||||
# https://aka.ms/yaml
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- script: echo Hello, world!
|
||||
displayName: 'Run a one-line script'
|
||||
|
||||
- script: |
|
||||
echo Add other tasks to build, test, and deploy your project.
|
||||
echo See https://aka.ms/yaml
|
||||
displayName: 'Run a multi-line script'
|
||||
417
azure-pipelines.yml
Normal file
417
azure-pipelines.yml
Normal file
@@ -0,0 +1,417 @@
|
||||
# Gradle
|
||||
# Build your Java projects and run tests with Gradle using a Gradle wrapper script.
|
||||
# Add steps that analyze code, save build artifacts, deploy, and more:
|
||||
# https://docs.microsoft.com/vsts/pipelines/languages/java
|
||||
|
||||
resources:
|
||||
containers:
|
||||
- container: wpilib2020
|
||||
image: wpilib/roborio-cross-ubuntu:2020-18.04
|
||||
- container: raspbian
|
||||
image: wpilib/raspbian-cross-ubuntu:10-18.04
|
||||
- container: aarch64
|
||||
image: wpilib/aarch64-cross-ubuntu:bionic-18.04
|
||||
- container: ubuntu
|
||||
image: wpilib/ubuntu-base:18.04
|
||||
|
||||
variables:
|
||||
- group: Artifactory-Package-Publish
|
||||
|
||||
trigger:
|
||||
batch: true
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
|
||||
stages:
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: Linux_Arm
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
|
||||
container: wpilib2020
|
||||
|
||||
timeoutInMinutes: 0
|
||||
|
||||
steps:
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
publishJUnitResults: false
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-Ponlylinuxathena -PbuildServer'
|
||||
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
publishJUnitResults: false
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-Ponlylinuxathena -PreleaseMode -PbuildServer'
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Athena'
|
||||
targetPath: 'build/allOutputs'
|
||||
|
||||
- job: Linux_Raspbian
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
|
||||
container: raspbian
|
||||
|
||||
timeoutInMinutes: 0
|
||||
|
||||
steps:
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-Ponlylinuxraspbian -PbuildServer'
|
||||
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-Ponlylinuxraspbian -PreleaseMode -PbuildServer'
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Raspbian'
|
||||
targetPath: 'build/allOutputs'
|
||||
|
||||
- job: Linux_Aarch64
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
|
||||
container: aarch64
|
||||
|
||||
timeoutInMinutes: 0
|
||||
|
||||
steps:
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-Ponlylinuxaarch64bionic -PbuildServer'
|
||||
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-Ponlylinuxaarch64bionic -PreleaseMode -PbuildServer'
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Aarch64'
|
||||
targetPath: 'build/allOutputs'
|
||||
|
||||
- job: Linux
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
|
||||
container: ubuntu
|
||||
|
||||
timeoutInMinutes: 0
|
||||
|
||||
steps:
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-PbuildServer'
|
||||
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-PreleaseMode -PbuildServer'
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Linux'
|
||||
targetPath: 'build/allOutputs'
|
||||
|
||||
- job: Styleguide
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
|
||||
container: ubuntu
|
||||
|
||||
timeoutInMinutes: 0
|
||||
|
||||
steps:
|
||||
- script: |
|
||||
sudo pip3 install wpiformat
|
||||
displayName: 'Install wpiformat'
|
||||
- script: |
|
||||
git checkout -b master
|
||||
wpiformat -clang 6.0
|
||||
displayName: 'Run wpiformat'
|
||||
- script: |
|
||||
# Ensure formatter made no changes
|
||||
git --no-pager diff --exit-code HEAD
|
||||
displayName: 'Check wpiformat Output'
|
||||
|
||||
- job: CMakeBuild
|
||||
pool:
|
||||
vmImage: 'Ubuntu 16.04'
|
||||
|
||||
container: wpilib2020
|
||||
|
||||
timeoutInMinutes: 0
|
||||
|
||||
steps:
|
||||
- task: CMake@1
|
||||
inputs:
|
||||
cmakeArgs: '-DWITHOUT_ALLWPILIB=OFF ..'
|
||||
- script: |
|
||||
make -j3
|
||||
workingDirectory: 'build'
|
||||
displayName: 'Build'
|
||||
|
||||
- job: Windows_64_Bit
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
|
||||
timeoutInMinutes: 0
|
||||
steps:
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
jdkVersionOption: '1.11'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-PskipPMD -PbuildServer'
|
||||
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
jdkVersionOption: '1.11'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-PskipPMD -PreleaseMode -PbuildServer'
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Win64'
|
||||
targetPath: 'build/allOutputs'
|
||||
|
||||
- job: Windows_32_Bit
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
|
||||
timeoutInMinutes: 0
|
||||
steps:
|
||||
- powershell: |
|
||||
mkdir build
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
wget "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.4%2B11/OpenJDK11U-jdk_x86-32_windows_hotspot_11.0.4_11.zip" -O "build\jdk.zip"
|
||||
displayName: 'Download JDK'
|
||||
- task: JavaToolInstaller@0
|
||||
inputs:
|
||||
jdkSourceOption: localDirectory
|
||||
jdkFile: 'build/jdk.zip'
|
||||
jdkDestinationDirectory: 'build/jdkinst'
|
||||
jdkArchitectureOption: x86
|
||||
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx1024m'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-PskipPMD -PbuildServer'
|
||||
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx1024m'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-PskipPMD -PreleaseMode -PbuildServer'
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Win32'
|
||||
targetPath: 'build/allOutputs'
|
||||
|
||||
- job: Mac
|
||||
pool:
|
||||
vmImage: 'macOS-10.14'
|
||||
|
||||
timeoutInMinutes: 0
|
||||
steps:
|
||||
- script: |
|
||||
mkdir build
|
||||
export JAVA_HOME=`/usr/libexec/java_home -v 11`
|
||||
displayName: 'Setup JDK'
|
||||
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v')))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
jdkVersionOption: '1.11'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-PbuildServer'
|
||||
|
||||
- task: Gradle@2
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
||||
inputs:
|
||||
workingDirectory: ''
|
||||
gradleWrapperFile: 'gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
jdkVersionOption: '1.11'
|
||||
publishJUnitResults: true
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
tasks: 'build'
|
||||
options: '-PreleaseMode -PbuildServer'
|
||||
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Mac'
|
||||
targetPath: 'build/allOutputs'
|
||||
|
||||
- stage: Combine
|
||||
jobs:
|
||||
- job: CombineJob
|
||||
pool:
|
||||
vmImage: 'macOS-10.14'
|
||||
|
||||
timeoutInMinutes: 0
|
||||
|
||||
steps:
|
||||
- checkout: none
|
||||
- script: |
|
||||
git clone https://github.com/wpilibsuite/build-tools
|
||||
displayName: 'Clone Combiner'
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Mac'
|
||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Win32'
|
||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Win64'
|
||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Linux'
|
||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Raspbian'
|
||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Athena'
|
||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
||||
- task: DownloadPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Aarch64'
|
||||
targetPath: 'build-tools/combiner/products/build/allOutputs'
|
||||
|
||||
# PR Builds
|
||||
- task: Gradle@2
|
||||
inputs:
|
||||
workingDirectory: 'build-tools/combiner'
|
||||
gradleWrapperFile: 'build-tools/combiner/gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
tasks: 'publish '
|
||||
options: '-Pallwpilib'
|
||||
condition: and(succeeded(), and(ne(variables['Build.SourceBranch'], 'refs/heads/master'), not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))))
|
||||
|
||||
# Master Builds
|
||||
- task: Gradle@2
|
||||
inputs:
|
||||
workingDirectory: 'build-tools/combiner'
|
||||
gradleWrapperFile: 'build-tools/combiner/gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
tasks: 'publish '
|
||||
options: '-Pallwpilib'
|
||||
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
env:
|
||||
RUN_AZURE_ARTIFACTORY_RELEASE: 'TRUE'
|
||||
ARTIFACTORY_PUBLISH_USERNAME: $(PublishUserName)
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: $(PublishPassword)
|
||||
|
||||
# Tagged Builds
|
||||
- task: Gradle@2
|
||||
inputs:
|
||||
workingDirectory: 'build-tools/combiner'
|
||||
gradleWrapperFile: 'build-tools/combiner/gradlew'
|
||||
gradleOptions: '-Xmx3072m'
|
||||
tasks: 'publish '
|
||||
options: '-Pallwpilib -PreleaseRepoPublish'
|
||||
condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
||||
env:
|
||||
RUN_AZURE_ARTIFACTORY_RELEASE: 'TRUE'
|
||||
ARTIFACTORY_PUBLISH_USERNAME: $(PublishUserName)
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: $(PublishPassword)
|
||||
|
||||
- script: |
|
||||
echo "##vso[task.setvariable variable=UserHome]$HOME"
|
||||
displayName: 'Set Home Variable'
|
||||
- task: PublishPipelineArtifact@0
|
||||
inputs:
|
||||
artifactName: 'Maven'
|
||||
targetPath: $(UserHome)/releases
|
||||
203
build.gradle
203
build.gradle
@@ -1,149 +1,54 @@
|
||||
import edu.wpi.first.nativeutils.NativeUtils
|
||||
import edu.wpi.first.nativeutils.tasks.JNIHeaders
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'gradle.plugin.edu.wpi.first:native-utils:1.5.1'
|
||||
}
|
||||
}
|
||||
import edu.wpi.first.toolchain.*
|
||||
|
||||
plugins {
|
||||
id 'net.ltgt.errorprone' version '0.0.10'
|
||||
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '2.0'
|
||||
id 'base'
|
||||
id 'edu.wpi.first.wpilib.versioning.WPILibVersioningPlugin' version '4.0.1'
|
||||
id 'edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin' version '2020.1'
|
||||
id 'edu.wpi.first.NativeUtils' apply false
|
||||
id 'edu.wpi.first.GradleJni' version '0.9.1'
|
||||
id 'edu.wpi.first.GradleVsCode' version '0.9.4'
|
||||
id 'idea'
|
||||
id 'visual-studio'
|
||||
id 'com.gradle.build-scan' version '2.3'
|
||||
id 'net.ltgt.errorprone' version '0.6' apply false
|
||||
id 'com.github.johnrengelman.shadow' version '4.0.3' apply false
|
||||
}
|
||||
|
||||
ext.licenseFile = file("$rootDir/LICENSE.txt")
|
||||
|
||||
ext.getJNIHeadersClass = {
|
||||
return JNIHeaders
|
||||
if (project.hasProperty('buildServer')) {
|
||||
wpilibVersioning.buildServerMode = true
|
||||
}
|
||||
|
||||
ext.getClassifier = { binary->
|
||||
return NativeUtils.getClassifier(binary)
|
||||
if (project.hasProperty('releaseMode')) {
|
||||
wpilibVersioning.releaseMode = true
|
||||
}
|
||||
|
||||
ext.getPlatformPath = { binary->
|
||||
return NativeUtils.getPlatformPath(binary)
|
||||
}
|
||||
|
||||
ext.createComponentZipTasks = { components, name, base, type, project, func ->
|
||||
def configMap = [:]
|
||||
components.each {
|
||||
if (it in NativeLibrarySpec && it.name == name) {
|
||||
it.binaries.each {
|
||||
def target = getClassifier(it)
|
||||
if (configMap.containsKey(target)) {
|
||||
configMap.get(target).add(it)
|
||||
} else {
|
||||
configMap.put(target, [])
|
||||
configMap.get(target).add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
def taskList = []
|
||||
def outputsFolder = file("$project.buildDir/outputs")
|
||||
def baseN = base + name
|
||||
configMap.each { key, value ->
|
||||
def task = project.tasks.create(baseN + "-${key}", type) {
|
||||
description = 'Creates component archive for platform ' + key
|
||||
destinationDir = outputsFolder
|
||||
classifier = key
|
||||
baseName = baseN + '-classifier'
|
||||
duplicatesStrategy = 'exclude'
|
||||
|
||||
from(licenseFile) {
|
||||
into '/'
|
||||
}
|
||||
|
||||
func(it, value)
|
||||
}
|
||||
taskList.add(task)
|
||||
|
||||
project.build.dependsOn task
|
||||
|
||||
project.artifacts {
|
||||
task
|
||||
}
|
||||
}
|
||||
return taskList
|
||||
}
|
||||
|
||||
ext.createAllCombined = { list, name, base, type, project ->
|
||||
def outputsFolder = file("$project.buildDir/outputs")
|
||||
def baseN = base + name
|
||||
def task = project.tasks.create(baseN + '-all', type) {
|
||||
description = 'Creates component archive for all classifiers'
|
||||
destinationDir = outputsFolder
|
||||
classifier = 'all'
|
||||
baseName = baseN + '-classifier'
|
||||
duplicatesStrategy = 'exclude'
|
||||
|
||||
list.each {
|
||||
it.outputs.files.each {
|
||||
from project.zipTree(it)
|
||||
}
|
||||
dependsOn it
|
||||
}
|
||||
}
|
||||
|
||||
project.build.dependsOn task
|
||||
|
||||
project.artifacts {
|
||||
task
|
||||
}
|
||||
|
||||
return task
|
||||
|
||||
}
|
||||
|
||||
ext.includeStandardZipFormat = { task, value ->
|
||||
value.each { binary->
|
||||
if (binary.buildable) {
|
||||
if (binary instanceof SharedLibraryBinarySpec) {
|
||||
task.dependsOn binary.buildTask
|
||||
task.from(new File(binary.sharedLibraryFile.absolutePath + ".debug")) {
|
||||
into getPlatformPath(binary) + '/shared'
|
||||
}
|
||||
task.from (binary.sharedLibraryFile) {
|
||||
into getPlatformPath(binary) + '/shared'
|
||||
}
|
||||
task.from (binary.sharedLibraryLinkFile) {
|
||||
into getPlatformPath(binary) + '/shared'
|
||||
}
|
||||
} else if (binary instanceof StaticLibraryBinarySpec) {
|
||||
task.dependsOn binary.buildTask
|
||||
task.from (binary.staticLibraryFile) {
|
||||
into getPlatformPath(binary) + '/static'
|
||||
}
|
||||
}
|
||||
}
|
||||
if (project.hasProperty('releaseMode')) {
|
||||
wpilibRepositories.addAllReleaseRepositories(it)
|
||||
} else {
|
||||
wpilibRepositories.addAllDevelopmentRepositories(it)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the WPILibVersioningPlugin is setup by setting the release type, if releaseType wasn't
|
||||
// already specified on the command line
|
||||
if (!hasProperty('releaseType')) {
|
||||
WPILibVersion {
|
||||
releaseType = 'dev'
|
||||
}
|
||||
buildScan {
|
||||
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
|
||||
termsOfServiceAgree = 'yes'
|
||||
|
||||
publishAlways()
|
||||
}
|
||||
|
||||
def pubVersion
|
||||
ext.licenseFile = files("$rootDir/LICENSE.txt", "$rootDir/ThirdPartyNotices.txt")
|
||||
|
||||
if (project.hasProperty("publishVersion")) {
|
||||
pubVersion = project.publishVersion
|
||||
} else {
|
||||
pubVersion = WPILibVersion.version
|
||||
wpilibVersioning.version.set(project.publishVersion)
|
||||
}
|
||||
|
||||
def outputsFolder = file("$buildDir/outputs")
|
||||
wpilibVersioning.version.finalizeValue()
|
||||
|
||||
def outputsFolder = file("$buildDir/allOutputs")
|
||||
|
||||
def versionFile = file("$outputsFolder/version.txt")
|
||||
|
||||
@@ -158,31 +63,38 @@ task outputVersions() {
|
||||
}
|
||||
|
||||
doLast {
|
||||
versionFile.write pubVersion
|
||||
versionFile.write wpilibVersioning.version.get()
|
||||
}
|
||||
}
|
||||
|
||||
task build() {}
|
||||
task libraryBuild() {}
|
||||
|
||||
build.dependsOn outputVersions
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete buildDir
|
||||
task copyAllOutputs(type: Copy) {
|
||||
destinationDir outputsFolder
|
||||
}
|
||||
|
||||
build.dependsOn copyAllOutputs
|
||||
copyAllOutputs.dependsOn outputVersions
|
||||
|
||||
ext.addTaskToCopyAllOutputs = { task ->
|
||||
copyAllOutputs.dependsOn task
|
||||
copyAllOutputs.inputs.file task.archivePath
|
||||
copyAllOutputs.from task.archivePath
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'checkstyle'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
def subproj = it
|
||||
|
||||
plugins.withType(NativeComponentPlugin) {
|
||||
subproj.apply plugin: MultiBuilds
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "8.1"
|
||||
configFile = new File(rootDir, "styleguide/checkstyle.xml")
|
||||
}
|
||||
apply from: "${rootDir}/shared/java/javastyle.gradle"
|
||||
|
||||
// Disables doclint in java 8.
|
||||
if (JavaVersion.current().isJava8Compatible()) {
|
||||
@@ -190,13 +102,12 @@ subprojects {
|
||||
options.addStringOption('Xdoclint:none', '-quiet')
|
||||
}
|
||||
}
|
||||
ext.setupWpilibRepo = { publishing ->
|
||||
publishing.repositories.maven {
|
||||
url = WPILibVersion.mavenLocalUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '4.1'
|
||||
ext.getCurrentArch = {
|
||||
return NativePlatforms.desktop
|
||||
}
|
||||
|
||||
wrapper {
|
||||
gradleVersion = '5.4.1'
|
||||
}
|
||||
|
||||
9
buildSrc/build.gradle
Normal file
9
buildSrc/build.gradle
Normal file
@@ -0,0 +1,9 @@
|
||||
repositories {
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
compile "edu.wpi.first:native-utils:2020.1.4"
|
||||
}
|
||||
70
buildSrc/src/main/groovy/DisableBuildingGTest.groovy
Normal file
70
buildSrc/src/main/groovy/DisableBuildingGTest.groovy
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.language.base.internal.ProjectLayout;
|
||||
import org.gradle.language.base.plugins.ComponentModelBasePlugin;
|
||||
import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask;
|
||||
import org.gradle.model.ModelMap;
|
||||
import edu.wpi.first.toolchain.ToolchainExtension
|
||||
import org.gradle.model.Mutate;
|
||||
import org.gradle.api.plugins.ExtensionContainer;
|
||||
import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec;
|
||||
import org.gradle.model.RuleSource;
|
||||
import org.gradle.model.Validate;
|
||||
import org.gradle.nativeplatform.NativeExecutableBinarySpec
|
||||
import org.gradle.nativeplatform.NativeBinarySpec;
|
||||
import org.gradle.nativeplatform.NativeComponentSpec;
|
||||
import org.gradle.nativeplatform.NativeLibrarySpec;
|
||||
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
|
||||
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
|
||||
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
|
||||
import org.gradle.nativeplatform.toolchain.NativeToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
|
||||
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
|
||||
import org.gradle.nativeplatform.toolchain.internal.ToolType;
|
||||
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
|
||||
import org.gradle.platform.base.BinarySpec;
|
||||
import org.gradle.platform.base.ComponentSpec;
|
||||
import org.gradle.platform.base.ComponentSpecContainer;
|
||||
import org.gradle.platform.base.BinaryContainer;
|
||||
import org.gradle.platform.base.ComponentType;
|
||||
import org.gradle.platform.base.TypeBuilder;
|
||||
import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
|
||||
import groovy.transform.CompileStatic;
|
||||
import groovy.transform.CompileDynamic
|
||||
import org.gradle.nativeplatform.BuildTypeContainer
|
||||
|
||||
@CompileStatic
|
||||
class DisableBuildingGTest implements Plugin<Project> {
|
||||
@CompileStatic
|
||||
public void apply(Project project) {
|
||||
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static class Rules extends RuleSource {
|
||||
@CompileDynamic
|
||||
private static void setBuildableFalseDynamically(NativeBinarySpec binary) {
|
||||
binary.buildable = false
|
||||
}
|
||||
|
||||
@Validate
|
||||
@CompileStatic
|
||||
// TODO: Move this to tc plugin
|
||||
void disableCrossTests(BinaryContainer binaries, ExtensionContainer extContainer) {
|
||||
final ToolchainExtension ext = extContainer.getByType(ToolchainExtension.class);
|
||||
|
||||
for (GoogleTestTestSuiteBinarySpec binary : binaries.withType(GoogleTestTestSuiteBinarySpec.class)) {
|
||||
if (ext.getCrossCompilers().findByName(binary.getTargetPlatform().getName()) != null) {
|
||||
setBuildableFalseDynamically(binary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
buildSrc/src/main/groovy/ExtraTasks.groovy
Normal file
88
buildSrc/src/main/groovy/ExtraTasks.groovy
Normal file
@@ -0,0 +1,88 @@
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.language.base.internal.ProjectLayout;
|
||||
import org.gradle.language.base.plugins.ComponentModelBasePlugin;
|
||||
import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask;
|
||||
import org.gradle.model.ModelMap;
|
||||
import org.gradle.model.Mutate;
|
||||
import org.gradle.model.RuleSource;
|
||||
import org.gradle.model.Validate;
|
||||
import org.gradle.nativeplatform.NativeBinarySpec;
|
||||
import org.gradle.nativeplatform.NativeComponentSpec;
|
||||
import org.gradle.nativeplatform.NativeLibrarySpec;
|
||||
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
|
||||
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
|
||||
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
|
||||
import org.gradle.nativeplatform.toolchain.NativeToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
|
||||
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
|
||||
import org.gradle.nativeplatform.toolchain.internal.ToolType;
|
||||
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
|
||||
import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec;
|
||||
import org.gradle.platform.base.BinarySpec;
|
||||
import org.gradle.platform.base.ComponentSpec;
|
||||
import org.gradle.platform.base.ComponentSpecContainer;
|
||||
import org.gradle.platform.base.ComponentType;
|
||||
import org.gradle.platform.base.TypeBuilder;
|
||||
import org.gradle.nativeplatform.test.tasks.RunTestExecutable;
|
||||
import org.gradle.platform.base.BinaryContainer;
|
||||
import groovy.transform.CompileStatic;
|
||||
|
||||
@CompileStatic
|
||||
class ExtraTasks implements Plugin<Project> {
|
||||
@CompileStatic
|
||||
public void apply(Project project) {
|
||||
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static class Rules extends RuleSource {
|
||||
@Mutate
|
||||
@CompileStatic
|
||||
void createNativeCompileTask(ModelMap<Task> tasks, BinaryContainer binaries) {
|
||||
tasks.create('compileCpp', Task) { oTask ->
|
||||
def task = (Task) oTask
|
||||
task.group = 'build'
|
||||
task.description = 'Uber task to compile all native code for this project'
|
||||
binaries.each { binary ->
|
||||
if (binary instanceof NativeBinarySpec && binary.buildable) {
|
||||
binary.tasks.withType(AbstractNativeSourceCompileTask) { compileTask ->
|
||||
task.dependsOn compileTask
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Mutate
|
||||
@CompileStatic
|
||||
void createNativeTestTask(ModelMap<Task> tasks, BinaryContainer binaries) {
|
||||
tasks.create('testCpp', Task) { oTask ->
|
||||
def task = (Task) oTask
|
||||
task.group = 'build'
|
||||
task.description = 'Uber task to run all native tests for project'
|
||||
binaries.each { binary ->
|
||||
if (binary instanceof GoogleTestTestSuiteBinarySpec && binary.buildable) {
|
||||
binary.tasks.withType(RunTestExecutable) { testTask ->
|
||||
task.dependsOn testTask
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
buildSrc/src/main/groovy/MultiBuilds.groovy
Normal file
74
buildSrc/src/main/groovy/MultiBuilds.groovy
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.language.base.internal.ProjectLayout;
|
||||
import org.gradle.language.base.plugins.ComponentModelBasePlugin;
|
||||
import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask;
|
||||
import org.gradle.model.ModelMap;
|
||||
import edu.wpi.first.toolchain.ToolchainExtension
|
||||
import org.gradle.model.Mutate;
|
||||
import org.gradle.api.plugins.ExtensionContainer;
|
||||
import org.gradle.nativeplatform.test.googletest.GoogleTestTestSuiteBinarySpec;
|
||||
import org.gradle.model.RuleSource;
|
||||
import org.gradle.model.Validate;
|
||||
import org.gradle.nativeplatform.test.tasks.RunTestExecutable
|
||||
import org.gradle.nativeplatform.NativeExecutableBinarySpec
|
||||
import org.gradle.nativeplatform.NativeBinarySpec;
|
||||
import org.gradle.nativeplatform.NativeComponentSpec;
|
||||
import org.gradle.nativeplatform.NativeLibrarySpec;
|
||||
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
|
||||
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
|
||||
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
|
||||
import org.gradle.nativeplatform.toolchain.NativeToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
|
||||
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
|
||||
import org.gradle.nativeplatform.toolchain.internal.ToolType;
|
||||
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
|
||||
import org.gradle.platform.base.BinarySpec;
|
||||
import org.gradle.platform.base.ComponentSpec;
|
||||
import org.gradle.platform.base.ComponentSpecContainer;
|
||||
import org.gradle.platform.base.BinaryContainer;
|
||||
import org.gradle.platform.base.ComponentType;
|
||||
import org.gradle.platform.base.TypeBuilder;
|
||||
import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
|
||||
import groovy.transform.CompileStatic;
|
||||
import groovy.transform.CompileDynamic
|
||||
import org.gradle.nativeplatform.BuildTypeContainer
|
||||
|
||||
@CompileStatic
|
||||
class MultiBuilds implements Plugin<Project> {
|
||||
@CompileStatic
|
||||
public void apply(Project project) {
|
||||
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static class Rules extends RuleSource {
|
||||
@CompileDynamic
|
||||
private static void setBuildableFalseDynamically(NativeBinarySpec binary) {
|
||||
binary.buildable = false
|
||||
}
|
||||
|
||||
|
||||
@Mutate
|
||||
@CompileStatic
|
||||
void disableReleaseGoogleTest(BinaryContainer binaries, ProjectLayout projectLayout) {
|
||||
def project = (Project) projectLayout.projectIdentifier
|
||||
if (project.hasProperty('testRelease')) {
|
||||
return
|
||||
}
|
||||
binaries.withType(GoogleTestTestSuiteBinarySpec) { oSpec ->
|
||||
GoogleTestTestSuiteBinarySpec spec = (GoogleTestTestSuiteBinarySpec) oSpec
|
||||
if (spec.buildType.name == 'release') {
|
||||
Rules.setBuildableFalseDynamically(spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
buildSrc/src/main/groovy/SingleNativeBuild.groovy
Normal file
133
buildSrc/src/main/groovy/SingleNativeBuild.groovy
Normal file
@@ -0,0 +1,133 @@
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.language.base.internal.ProjectLayout;
|
||||
import org.gradle.language.base.plugins.ComponentModelBasePlugin;
|
||||
import org.gradle.language.nativeplatform.tasks.AbstractNativeSourceCompileTask;
|
||||
import org.gradle.model.ModelMap;
|
||||
import org.gradle.model.Mutate;
|
||||
import org.gradle.model.RuleSource;
|
||||
import org.gradle.model.Validate;
|
||||
import org.gradle.nativeplatform.NativeBinarySpec;
|
||||
import org.gradle.nativeplatform.NativeComponentSpec;
|
||||
import org.gradle.nativeplatform.NativeLibrarySpec;
|
||||
import org.gradle.nativeplatform.SharedLibraryBinarySpec;
|
||||
import org.gradle.nativeplatform.StaticLibraryBinarySpec;
|
||||
import org.gradle.nativeplatform.platform.internal.NativePlatformInternal;
|
||||
import org.gradle.nativeplatform.toolchain.NativeToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
|
||||
import org.gradle.nativeplatform.toolchain.internal.PlatformToolProvider;
|
||||
import org.gradle.nativeplatform.toolchain.internal.ToolType;
|
||||
import org.gradle.nativeplatform.toolchain.internal.gcc.AbstractGccCompatibleToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.internal.msvcpp.VisualCppToolChain;
|
||||
import org.gradle.nativeplatform.toolchain.internal.tools.ToolRegistry;
|
||||
import org.gradle.platform.base.BinarySpec;
|
||||
import org.gradle.platform.base.ComponentSpec;
|
||||
import org.gradle.platform.base.ComponentSpecContainer;
|
||||
import org.gradle.platform.base.BinaryContainer;
|
||||
import org.gradle.platform.base.ComponentType;
|
||||
import org.gradle.platform.base.TypeBuilder;
|
||||
import org.gradle.nativeplatform.tasks.ObjectFilesToBinary;
|
||||
import groovy.transform.CompileStatic;
|
||||
import edu.wpi.first.nativeutils.tasks.ExportsGenerationTask
|
||||
|
||||
@CompileStatic
|
||||
class SingleNativeBuild implements Plugin<Project> {
|
||||
@CompileStatic
|
||||
public void apply(Project project) {
|
||||
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static class Rules extends RuleSource {
|
||||
@Mutate
|
||||
@CompileStatic
|
||||
void removeMacSystemIncludes(ModelMap<Task> tasks, BinaryContainer binaries) {
|
||||
binaries.each {
|
||||
if (!(it instanceof NativeBinarySpec)) {
|
||||
return
|
||||
}
|
||||
NativeBinarySpec nativeBin = (NativeBinarySpec)it
|
||||
if (nativeBin.targetPlatform.operatingSystem.isMacOsX()) {
|
||||
nativeBin.tasks.withType(AbstractNativeSourceCompileTask) { AbstractNativeSourceCompileTask compileTask->
|
||||
compileTask.getSystemIncludes().setFrom()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Mutate
|
||||
@CompileStatic
|
||||
void setupSingleNativeBuild(ModelMap<Task> tasks, ComponentSpecContainer components, BinaryContainer binaryContainer, ProjectLayout projectLayout) {
|
||||
Project project = (Project) projectLayout.projectIdentifier;
|
||||
|
||||
def nativeName = project.extensions.extraProperties.get('nativeName')
|
||||
|
||||
NativeLibrarySpec base = null
|
||||
def subs = []
|
||||
components.each { component ->
|
||||
if (component.name == "${nativeName}Base") {
|
||||
base = (NativeLibrarySpec) component
|
||||
} else if (component.name == "${nativeName}" || component.name == "${nativeName}JNI") {
|
||||
subs << component
|
||||
}
|
||||
}
|
||||
subs.each {
|
||||
((NativeLibrarySpec) it).binaries.each { oBinary ->
|
||||
if (oBinary.buildable == false) {
|
||||
return
|
||||
}
|
||||
NativeBinarySpec binary = (NativeBinarySpec) oBinary
|
||||
NativeBinarySpec baseBin = null
|
||||
base.binaries.each { oTmpBaseBin ->
|
||||
if (oTmpBaseBin.buildable == false) {
|
||||
return
|
||||
}
|
||||
def tmpBaseBin = (NativeBinarySpec) oTmpBaseBin
|
||||
if (tmpBaseBin.targetPlatform.name == binary.targetPlatform.name &&
|
||||
tmpBaseBin.buildType == binary.buildType) {
|
||||
baseBin = tmpBaseBin
|
||||
}
|
||||
}
|
||||
baseBin.tasks.withType(AbstractNativeSourceCompileTask) { oCompileTask ->
|
||||
def compileTask = (AbstractNativeSourceCompileTask) oCompileTask
|
||||
if (binary instanceof SharedLibraryBinarySpec) {
|
||||
def sBinary = (SharedLibraryBinarySpec) binary
|
||||
ObjectFilesToBinary link = (ObjectFilesToBinary) sBinary.tasks.link
|
||||
ExportsGenerationTask exportsTask = binary.tasks.withType(ExportsGenerationTask)[0]
|
||||
if (exportsTask != null) {
|
||||
exportsTask.dependsOn compileTask
|
||||
}
|
||||
link.dependsOn compileTask
|
||||
link.inputs.dir compileTask.objectFileDir
|
||||
def tree = project.fileTree(compileTask.objectFileDir)
|
||||
tree.include '**/*.o'
|
||||
tree.include '**/*.obj'
|
||||
link.source tree
|
||||
} else if (binary instanceof StaticLibraryBinarySpec) {
|
||||
def sBinary = (StaticLibraryBinarySpec) binary
|
||||
ObjectFilesToBinary assemble = (ObjectFilesToBinary) sBinary.tasks.createStaticLib
|
||||
assemble.dependsOn compileTask
|
||||
assemble.inputs.dir compileTask.objectFileDir
|
||||
def tree = project.fileTree(compileTask.objectFileDir)
|
||||
tree.include '**/*.o'
|
||||
tree.include '**/*.obj'
|
||||
assemble.source tree
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
cameraserver/.styleguide
Normal file
28
cameraserver/.styleguide
Normal file
@@ -0,0 +1,28 @@
|
||||
cppHeaderFileInclude {
|
||||
\.h$
|
||||
\.inc$
|
||||
}
|
||||
|
||||
cppSrcFileInclude {
|
||||
\.cpp$
|
||||
}
|
||||
|
||||
modifiableFileExclude {
|
||||
\.so$
|
||||
}
|
||||
|
||||
repoRootNameOverride {
|
||||
cameraserver
|
||||
}
|
||||
|
||||
includeOtherLibs {
|
||||
^HAL/
|
||||
^networktables/
|
||||
^opencv2/
|
||||
^support/
|
||||
^wpi/
|
||||
}
|
||||
|
||||
includeGuardRoots {
|
||||
cameraserver/src/main/native/include/
|
||||
}
|
||||
70
cameraserver/CMakeLists.txt
Normal file
70
cameraserver/CMakeLists.txt
Normal file
@@ -0,0 +1,70 @@
|
||||
project(cameraserver)
|
||||
|
||||
include(CompileWarnings)
|
||||
include(AddTest)
|
||||
|
||||
find_package( OpenCV REQUIRED )
|
||||
|
||||
# Java bindings
|
||||
if (NOT WITHOUT_JAVA)
|
||||
find_package(Java REQUIRED)
|
||||
include(UseJava)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS "-Xlint:unchecked")
|
||||
|
||||
#find java files, copy them locally
|
||||
|
||||
set(OPENCV_JAVA_INSTALL_DIR ${OpenCV_INSTALL_PATH}/share/OpenCV/java/)
|
||||
|
||||
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)
|
||||
|
||||
file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
|
||||
|
||||
add_jar(cameraserver_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar cscore_jar ntcore_jar ${OPENCV_JAR_FILE} OUTPUT_NAME cameraserver)
|
||||
|
||||
get_property(CAMERASERVER_JAR_FILE TARGET cameraserver_jar PROPERTY JAR_FILE)
|
||||
install(FILES ${CAMERASERVER_JAR_FILE} DESTINATION "${java_lib_dest}")
|
||||
|
||||
set_property(TARGET cameraserver_jar PROPERTY FOLDER "java")
|
||||
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE
|
||||
cameraserver_native_src src/main/native/cpp/*.cpp)
|
||||
add_library(cameraserver ${cameraserver_native_src})
|
||||
set_target_properties(cameraserver PROPERTIES DEBUG_POSTFIX "d")
|
||||
target_include_directories(cameraserver PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
|
||||
$<INSTALL_INTERFACE:${include_dest}/cameraserver>)
|
||||
wpilib_target_warnings(cameraserver)
|
||||
target_link_libraries(cameraserver PUBLIC ntcore cscore wpiutil ${OpenCV_LIBS})
|
||||
|
||||
set_property(TARGET cameraserver PROPERTY FOLDER "libraries")
|
||||
|
||||
install(TARGETS cameraserver EXPORT cameraserver DESTINATION "${main_lib_dest}")
|
||||
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/cameraserver")
|
||||
|
||||
if (NOT WITHOUT_JAVA AND MSVC)
|
||||
install(TARGETS cameraserver RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
|
||||
endif()
|
||||
|
||||
if (MSVC OR FLAT_INSTALL_WPILIB)
|
||||
set (cameraserver_config_dir ${wpilib_dest})
|
||||
else()
|
||||
set (cameraserver_config_dir share/cameraserver)
|
||||
endif()
|
||||
|
||||
configure_file(cameraserver-config.cmake.in ${CMAKE_BINARY_DIR}/cameraserver-config.cmake )
|
||||
install(FILES ${CMAKE_BINARY_DIR}/cameraserver-config.cmake DESTINATION ${cameraserver_config_dir})
|
||||
install(EXPORT cameraserver DESTINATION ${cameraserver_config_dir})
|
||||
|
||||
file(GLOB multiCameraServer_src multiCameraServer/src/main/native/cpp/*.cpp)
|
||||
add_executable(multiCameraServer ${multiCameraServer_src})
|
||||
wpilib_target_warnings(multiCameraServer)
|
||||
target_link_libraries(multiCameraServer cameraserver)
|
||||
|
||||
set_property(TARGET multiCameraServer PROPERTY FOLDER "examples")
|
||||
|
||||
if (WITH_TESTS)
|
||||
wpilib_add_test(cameraserver src/test/native/cpp)
|
||||
target_link_libraries(cameraserver_test cameraserver gtest)
|
||||
endif()
|
||||
77
cameraserver/build.gradle
Normal file
77
cameraserver/build.gradle
Normal file
@@ -0,0 +1,77 @@
|
||||
ext {
|
||||
nativeName = 'cameraserver'
|
||||
devMain = 'edu.wpi.first.cameraserver.DevMain'
|
||||
}
|
||||
|
||||
evaluationDependsOn(':ntcore')
|
||||
evaluationDependsOn(':cscore')
|
||||
evaluationDependsOn(':hal')
|
||||
|
||||
apply from: "${rootDir}/shared/javacpp/setupBuild.gradle"
|
||||
|
||||
dependencies {
|
||||
compile project(':wpiutil')
|
||||
compile project(':ntcore')
|
||||
compile project(':cscore')
|
||||
devCompile project(':wpiutil')
|
||||
devCompile project(':ntcore')
|
||||
devCompile project(':cscore')
|
||||
}
|
||||
|
||||
ext {
|
||||
sharedCvConfigs = [cameraserver : [],
|
||||
cameraserverBase: [],
|
||||
cameraserverDev : [],
|
||||
cameraserverTest: []]
|
||||
staticCvConfigs = [:]
|
||||
useJava = true
|
||||
useCpp = true
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/shared/opencv.gradle"
|
||||
|
||||
nativeUtils.exportsConfigs {
|
||||
cameraserver {
|
||||
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']
|
||||
}
|
||||
}
|
||||
|
||||
model {
|
||||
components {}
|
||||
binaries {
|
||||
all {
|
||||
if (!it.buildable || !(it instanceof NativeBinarySpec)) {
|
||||
return
|
||||
}
|
||||
lib project: ':ntcore', library: 'ntcore', linkage: 'shared'
|
||||
lib project: ':cscore', library: 'cscore', linkage: 'shared'
|
||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
|
||||
}
|
||||
}
|
||||
tasks {
|
||||
def c = $.components
|
||||
def found = false
|
||||
def systemArch = getCurrentArch()
|
||||
c.each {
|
||||
if (it in NativeExecutableSpec && it.name == "${nativeName}Dev") {
|
||||
it.binaries.each {
|
||||
if (!found) {
|
||||
def arch = it.targetPlatform.name
|
||||
if (arch == systemArch) {
|
||||
def filePath = it.tasks.install.installDirectory.get().toString() + File.separatorChar + 'lib'
|
||||
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
cameraserver/cameraserver-config.cmake.in
Normal file
8
cameraserver/cameraserver-config.cmake.in
Normal file
@@ -0,0 +1,8 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
@FILENAME_DEP_REPLACE@
|
||||
@WPIUTIL_DEP_REPLACE@
|
||||
@NTCORE_DEP_REPLACE@
|
||||
@CSCORE_DEP_REPLACE@
|
||||
find_dependency(OpenCV)
|
||||
|
||||
include(${SELF_DIR}/cameraserver.cmake)
|
||||
62
cameraserver/multiCameraServer/build.gradle
Normal file
62
cameraserver/multiCameraServer/build.gradle
Normal file
@@ -0,0 +1,62 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id 'cpp'
|
||||
id 'visual-studio'
|
||||
}
|
||||
|
||||
apply plugin: 'edu.wpi.first.NativeUtils'
|
||||
|
||||
apply from: "${rootDir}/shared/config.gradle"
|
||||
|
||||
ext {
|
||||
staticCvConfigs = [multiCameraServerCpp: []]
|
||||
useJava = true
|
||||
useCpp = true
|
||||
skipDev = true
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/shared/opencv.gradle"
|
||||
|
||||
mainClassName = 'Main'
|
||||
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.google.code.gson:gson:2.8.5'
|
||||
|
||||
compile project(':wpiutil')
|
||||
compile project(':ntcore')
|
||||
compile project(':cscore')
|
||||
compile project(':cameraserver')
|
||||
}
|
||||
|
||||
model {
|
||||
components {
|
||||
multiCameraServerCpp(NativeExecutableSpec) {
|
||||
targetBuildTypes 'release'
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
srcDirs = ['src/main/native/cpp']
|
||||
includes = ['**/*.cpp']
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs = ['src/main/native/include']
|
||||
includes = ['**/*.h']
|
||||
}
|
||||
}
|
||||
}
|
||||
binaries.all { binary ->
|
||||
lib project: ':cameraserver', library: 'cameraserver', linkage: 'static'
|
||||
lib project: ':ntcore', library: 'ntcore', linkage: 'static'
|
||||
lib project: ':cscore', library: 'cscore', linkage: 'static'
|
||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'static'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
211
cameraserver/multiCameraServer/src/main/java/Main.java
Normal file
211
cameraserver/multiCameraServer/src/main/java/Main.java
Normal file
@@ -0,0 +1,211 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import edu.wpi.cscore.VideoSource;
|
||||
import edu.wpi.first.cameraserver.CameraServer;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
|
||||
/*
|
||||
JSON format:
|
||||
{
|
||||
"team": <team number>,
|
||||
"ntmode": <"client" or "server", "client" if unspecified>
|
||||
"cameras": [
|
||||
{
|
||||
"name": <camera name>
|
||||
"path": <path, e.g. "/dev/video0">
|
||||
"pixel format": <"MJPEG", "YUYV", etc> // optional
|
||||
"width": <video mode width> // optional
|
||||
"height": <video mode height> // optional
|
||||
"fps": <video mode fps> // optional
|
||||
"brightness": <percentage brightness> // optional
|
||||
"white balance": <"auto", "hold", value> // optional
|
||||
"exposure": <"auto", "hold", value> // optional
|
||||
"properties": [ // optional
|
||||
{
|
||||
"name": <property name>
|
||||
"value": <property value>
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
public final class Main {
|
||||
private static String configFile = "/boot/frc.json";
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public static class CameraConfig {
|
||||
public String name;
|
||||
public String path;
|
||||
public JsonObject config;
|
||||
}
|
||||
|
||||
public static int team;
|
||||
public static boolean server;
|
||||
public static List<CameraConfig> cameras = new ArrayList<>();
|
||||
|
||||
private Main() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report parse error.
|
||||
*/
|
||||
public static void parseError(String str) {
|
||||
System.err.println("config error in '" + configFile + "': " + str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read single camera configuration.
|
||||
*/
|
||||
public static boolean readCameraConfig(JsonObject config) {
|
||||
CameraConfig cam = new CameraConfig();
|
||||
|
||||
// name
|
||||
JsonElement nameElement = config.get("name");
|
||||
if (nameElement == null) {
|
||||
parseError("could not read camera name");
|
||||
return false;
|
||||
}
|
||||
cam.name = nameElement.getAsString();
|
||||
|
||||
// path
|
||||
JsonElement pathElement = config.get("path");
|
||||
if (pathElement == null) {
|
||||
parseError("camera '" + cam.name + "': could not read path");
|
||||
return false;
|
||||
}
|
||||
cam.path = pathElement.getAsString();
|
||||
|
||||
cam.config = config;
|
||||
|
||||
cameras.add(cam);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read configuration file.
|
||||
*/
|
||||
@SuppressWarnings("PMD.CyclomaticComplexity")
|
||||
public static boolean readConfig() {
|
||||
// parse file
|
||||
JsonElement top;
|
||||
try {
|
||||
top = new JsonParser().parse(Files.newBufferedReader(Paths.get(configFile)));
|
||||
} catch (IOException ex) {
|
||||
System.err.println("could not open '" + configFile + "': " + ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
// top level must be an object
|
||||
if (!top.isJsonObject()) {
|
||||
parseError("must be JSON object");
|
||||
return false;
|
||||
}
|
||||
JsonObject obj = top.getAsJsonObject();
|
||||
|
||||
// team number
|
||||
JsonElement teamElement = obj.get("team");
|
||||
if (teamElement == null) {
|
||||
parseError("could not read team number");
|
||||
return false;
|
||||
}
|
||||
team = teamElement.getAsInt();
|
||||
|
||||
// ntmode (optional)
|
||||
if (obj.has("ntmode")) {
|
||||
String str = obj.get("ntmode").getAsString();
|
||||
if ("client".equalsIgnoreCase(str)) {
|
||||
server = false;
|
||||
} else if ("server".equalsIgnoreCase(str)) {
|
||||
server = true;
|
||||
} else {
|
||||
parseError("could not understand ntmode value '" + str + "'");
|
||||
}
|
||||
}
|
||||
|
||||
// cameras
|
||||
JsonElement camerasElement = obj.get("cameras");
|
||||
if (camerasElement == null) {
|
||||
parseError("could not read cameras");
|
||||
return false;
|
||||
}
|
||||
JsonArray cameras = camerasElement.getAsJsonArray();
|
||||
for (JsonElement camera : cameras) {
|
||||
if (!readCameraConfig(camera.getAsJsonObject())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start running the camera.
|
||||
*/
|
||||
public static void startCamera(CameraConfig config) {
|
||||
System.out.println("Starting camera '" + config.name + "' on " + config.path);
|
||||
VideoSource camera = CameraServer.getInstance().startAutomaticCapture(
|
||||
config.name, config.path);
|
||||
|
||||
Gson gson = new GsonBuilder().create();
|
||||
|
||||
camera.setConfigJson(gson.toJson(config.config));
|
||||
}
|
||||
|
||||
/**
|
||||
* Main.
|
||||
*/
|
||||
public static void main(String... args) {
|
||||
if (args.length > 0) {
|
||||
configFile = args[0];
|
||||
}
|
||||
|
||||
// read configuration
|
||||
if (!readConfig()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start NetworkTables
|
||||
NetworkTableInstance ntinst = NetworkTableInstance.getDefault();
|
||||
if (server) {
|
||||
System.out.println("Setting up NetworkTables server");
|
||||
ntinst.startServer();
|
||||
} else {
|
||||
System.out.println("Setting up NetworkTables client for team " + team);
|
||||
ntinst.startClientTeam(team);
|
||||
}
|
||||
|
||||
// start cameras
|
||||
for (CameraConfig camera : cameras) {
|
||||
startCamera(camera);
|
||||
}
|
||||
|
||||
// loop forever
|
||||
for (;;) {
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException ex) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
190
cameraserver/multiCameraServer/src/main/native/cpp/main.cpp
Normal file
190
cameraserver/multiCameraServer/src/main/native/cpp/main.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <wpi/StringRef.h>
|
||||
#include <wpi/json.h>
|
||||
#include <wpi/raw_istream.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "cameraserver/CameraServer.h"
|
||||
|
||||
/*
|
||||
JSON format:
|
||||
{
|
||||
"team": <team number>,
|
||||
"ntmode": <"client" or "server", "client" if unspecified>
|
||||
"cameras": [
|
||||
{
|
||||
"name": <camera name>
|
||||
"path": <path, e.g. "/dev/video0">
|
||||
"pixel format": <"MJPEG", "YUYV", etc> // optional
|
||||
"width": <video mode width> // optional
|
||||
"height": <video mode height> // optional
|
||||
"fps": <video mode fps> // optional
|
||||
"brightness": <percentage brightness> // optional
|
||||
"white balance": <"auto", "hold", value> // optional
|
||||
"exposure": <"auto", "hold", value> // optional
|
||||
"properties": [ // optional
|
||||
{
|
||||
"name": <property name>
|
||||
"value": <property value>
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
#ifdef __RASPBIAN__
|
||||
static const char* configFile = "/boot/frc.json";
|
||||
#else
|
||||
static const char* configFile = "frc.json";
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
unsigned int team;
|
||||
bool server = false;
|
||||
|
||||
struct CameraConfig {
|
||||
std::string name;
|
||||
std::string path;
|
||||
wpi::json config;
|
||||
};
|
||||
|
||||
std::vector<CameraConfig> cameras;
|
||||
|
||||
wpi::raw_ostream& ParseError() {
|
||||
return wpi::errs() << "config error in '" << configFile << "': ";
|
||||
}
|
||||
|
||||
bool ReadCameraConfig(const wpi::json& config) {
|
||||
CameraConfig c;
|
||||
|
||||
// name
|
||||
try {
|
||||
c.name = config.at("name").get<std::string>();
|
||||
} catch (const wpi::json::exception& e) {
|
||||
ParseError() << "could not read camera name: " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
// path
|
||||
try {
|
||||
c.path = config.at("path").get<std::string>();
|
||||
} catch (const wpi::json::exception& e) {
|
||||
ParseError() << "camera '" << c.name
|
||||
<< "': could not read path: " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
c.config = config;
|
||||
|
||||
cameras.emplace_back(std::move(c));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadConfig() {
|
||||
// open config file
|
||||
std::error_code ec;
|
||||
wpi::raw_fd_istream is(configFile, ec);
|
||||
if (ec) {
|
||||
wpi::errs() << "could not open '" << configFile << "': " << ec.message()
|
||||
<< '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse file
|
||||
wpi::json j;
|
||||
try {
|
||||
j = wpi::json::parse(is);
|
||||
} catch (const wpi::json::parse_error& e) {
|
||||
ParseError() << "byte " << e.byte << ": " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
// top level must be an object
|
||||
if (!j.is_object()) {
|
||||
ParseError() << "must be JSON object\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// team number
|
||||
try {
|
||||
team = j.at("team").get<unsigned int>();
|
||||
} catch (const wpi::json::exception& e) {
|
||||
ParseError() << "could not read team number: " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
// ntmode (optional)
|
||||
if (j.count("ntmode") != 0) {
|
||||
try {
|
||||
auto str = j.at("ntmode").get<std::string>();
|
||||
wpi::StringRef s(str);
|
||||
if (s.equals_lower("client")) {
|
||||
server = false;
|
||||
} else if (s.equals_lower("server")) {
|
||||
server = true;
|
||||
} else {
|
||||
ParseError() << "could not understand ntmode value '" << str << "'\n";
|
||||
}
|
||||
} catch (const wpi::json::exception& e) {
|
||||
ParseError() << "could not read ntmode: " << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// cameras
|
||||
try {
|
||||
for (auto&& camera : j.at("cameras")) {
|
||||
if (!ReadCameraConfig(camera)) return false;
|
||||
}
|
||||
} catch (const wpi::json::exception& e) {
|
||||
ParseError() << "could not read cameras: " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartCamera(const CameraConfig& config) {
|
||||
wpi::outs() << "Starting camera '" << config.name << "' on " << config.path
|
||||
<< '\n';
|
||||
auto camera = frc::CameraServer::GetInstance()->StartAutomaticCapture(
|
||||
config.name, config.path);
|
||||
|
||||
camera.SetConfigJson(config.config);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc >= 2) configFile = argv[1];
|
||||
|
||||
// read configuration
|
||||
if (!ReadConfig()) return EXIT_FAILURE;
|
||||
|
||||
// start NetworkTables
|
||||
auto ntinst = nt::NetworkTableInstance::GetDefault();
|
||||
if (server) {
|
||||
wpi::outs() << "Setting up NetworkTables server\n";
|
||||
ntinst.StartServer();
|
||||
} else {
|
||||
wpi::outs() << "Setting up NetworkTables client for team " << team << '\n';
|
||||
ntinst.StartClientTeam(team);
|
||||
}
|
||||
|
||||
// start cameras
|
||||
for (auto&& camera : cameras) StartCamera(camera);
|
||||
|
||||
// loop forever
|
||||
for (;;) std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.cameraserver;
|
||||
|
||||
public final class DevMain {
|
||||
public static void main(String[] args) {
|
||||
|
||||
}
|
||||
|
||||
private DevMain() {
|
||||
}
|
||||
}
|
||||
8
cameraserver/src/dev/native/cpp/main.cpp
Normal file
8
cameraserver/src/dev/native/cpp/main.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
int main() {}
|
||||
@@ -0,0 +1,802 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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.first.cameraserver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import edu.wpi.cscore.AxisCamera;
|
||||
import edu.wpi.cscore.CameraServerJNI;
|
||||
import edu.wpi.cscore.CvSink;
|
||||
import edu.wpi.cscore.CvSource;
|
||||
import edu.wpi.cscore.MjpegServer;
|
||||
import edu.wpi.cscore.UsbCamera;
|
||||
import edu.wpi.cscore.VideoEvent;
|
||||
import edu.wpi.cscore.VideoException;
|
||||
import edu.wpi.cscore.VideoListener;
|
||||
import edu.wpi.cscore.VideoMode;
|
||||
import edu.wpi.cscore.VideoMode.PixelFormat;
|
||||
import edu.wpi.cscore.VideoProperty;
|
||||
import edu.wpi.cscore.VideoSink;
|
||||
import edu.wpi.cscore.VideoSource;
|
||||
import edu.wpi.first.networktables.EntryListenerFlags;
|
||||
import edu.wpi.first.networktables.NetworkTable;
|
||||
import edu.wpi.first.networktables.NetworkTableEntry;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
|
||||
/**
|
||||
* Singleton class for creating and keeping camera servers.
|
||||
* Also publishes camera information to NetworkTables.
|
||||
*/
|
||||
@SuppressWarnings("PMD.TooManyMethods")
|
||||
public final class CameraServer {
|
||||
public static final int kBasePort = 1181;
|
||||
|
||||
@Deprecated
|
||||
public static final int kSize640x480 = 0;
|
||||
@Deprecated
|
||||
public static final int kSize320x240 = 1;
|
||||
@Deprecated
|
||||
public static final int kSize160x120 = 2;
|
||||
|
||||
private static final String kPublishName = "/CameraPublisher";
|
||||
private static CameraServer server;
|
||||
|
||||
/**
|
||||
* Get the CameraServer instance.
|
||||
*/
|
||||
public static synchronized CameraServer getInstance() {
|
||||
if (server == null) {
|
||||
server = new CameraServer();
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
private final AtomicInteger m_defaultUsbDevice;
|
||||
private String m_primarySourceName;
|
||||
private final Map<String, VideoSource> m_sources;
|
||||
private final Map<String, VideoSink> m_sinks;
|
||||
private final Map<Integer, NetworkTable> m_tables; // indexed by source handle
|
||||
// source handle indexed by sink handle
|
||||
private final Map<Integer, Integer> m_fixedSources;
|
||||
private final NetworkTable m_publishTable;
|
||||
private final VideoListener m_videoListener; //NOPMD
|
||||
private final int m_tableListener; //NOPMD
|
||||
private int m_nextPort;
|
||||
private String[] m_addresses;
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String makeSourceValue(int source) {
|
||||
switch (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))) {
|
||||
case kUsb:
|
||||
return "usb:" + CameraServerJNI.getUsbCameraPath(source);
|
||||
case kHttp: {
|
||||
String[] urls = CameraServerJNI.getHttpCameraUrls(source);
|
||||
if (urls.length > 0) {
|
||||
return "ip:" + urls[0];
|
||||
} else {
|
||||
return "ip:";
|
||||
}
|
||||
}
|
||||
case kCv:
|
||||
return "cv:";
|
||||
default:
|
||||
return "unknown:";
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String makeStreamValue(String address, int port) {
|
||||
return "mjpg:http://" + address + ":" + port + "/?action=stream";
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
|
||||
private synchronized String[] getSinkStreamValues(int sink) {
|
||||
// Ignore all but MjpegServer
|
||||
if (VideoSink.getKindFromInt(CameraServerJNI.getSinkKind(sink)) != VideoSink.Kind.kMjpeg) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
// Get port
|
||||
int port = CameraServerJNI.getMjpegServerPort(sink);
|
||||
|
||||
// Generate values
|
||||
ArrayList<String> values = new ArrayList<>(m_addresses.length + 1);
|
||||
String listenAddress = CameraServerJNI.getMjpegServerListenAddress(sink);
|
||||
if (!listenAddress.isEmpty()) {
|
||||
// If a listen address is specified, only use that
|
||||
values.add(makeStreamValue(listenAddress, port));
|
||||
} else {
|
||||
// Otherwise generate for hostname and all interface addresses
|
||||
values.add(makeStreamValue(CameraServerJNI.getHostname() + ".local", port));
|
||||
for (String addr : m_addresses) {
|
||||
if ("127.0.0.1".equals(addr)) {
|
||||
continue; // ignore localhost
|
||||
}
|
||||
values.add(makeStreamValue(addr, port));
|
||||
}
|
||||
}
|
||||
|
||||
return values.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP"})
|
||||
private synchronized String[] getSourceStreamValues(int source) {
|
||||
// Ignore all but HttpCamera
|
||||
if (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))
|
||||
!= VideoSource.Kind.kHttp) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
// Generate values
|
||||
String[] values = CameraServerJNI.getHttpCameraUrls(source);
|
||||
for (int j = 0; j < values.length; j++) {
|
||||
values[j] = "mjpg:" + values[j];
|
||||
}
|
||||
|
||||
if (CameraServerSharedStore.getCameraServerShared().isRoboRIO()) {
|
||||
// Look to see if we have a passthrough server for this source
|
||||
// Only do this on the roboRIO
|
||||
for (VideoSink i : m_sinks.values()) {
|
||||
int sink = i.getHandle();
|
||||
int sinkSource = CameraServerJNI.getSinkSource(sink);
|
||||
if (source == sinkSource
|
||||
&& VideoSink.getKindFromInt(CameraServerJNI.getSinkKind(sink))
|
||||
== VideoSink.Kind.kMjpeg) {
|
||||
// Add USB-only passthrough
|
||||
String[] finalValues = Arrays.copyOf(values, values.length + 1);
|
||||
int port = CameraServerJNI.getMjpegServerPort(sink);
|
||||
finalValues[values.length] = makeStreamValue("172.22.11.2", port);
|
||||
return finalValues;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.AvoidUsingHardCodedIP", "PMD.CyclomaticComplexity"})
|
||||
private synchronized void updateStreamValues() {
|
||||
// Over all the sinks...
|
||||
for (VideoSink i : m_sinks.values()) {
|
||||
int sink = i.getHandle();
|
||||
|
||||
// Get the source's subtable (if none exists, we're done)
|
||||
int source = Objects.requireNonNullElseGet(m_fixedSources.get(sink),
|
||||
() -> CameraServerJNI.getSinkSource(sink));
|
||||
|
||||
if (source == 0) {
|
||||
continue;
|
||||
}
|
||||
NetworkTable table = m_tables.get(source);
|
||||
if (table != null) {
|
||||
// Don't set stream values if this is a HttpCamera passthrough
|
||||
if (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))
|
||||
== VideoSource.Kind.kHttp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set table value
|
||||
String[] values = getSinkStreamValues(sink);
|
||||
if (values.length > 0) {
|
||||
table.getEntry("streams").setStringArray(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Over all the sources...
|
||||
for (VideoSource i : m_sources.values()) {
|
||||
int source = i.getHandle();
|
||||
|
||||
// Get the source's subtable (if none exists, we're done)
|
||||
NetworkTable table = m_tables.get(source);
|
||||
if (table != null) {
|
||||
// Set table value
|
||||
String[] values = getSourceStreamValues(source);
|
||||
if (values.length > 0) {
|
||||
table.getEntry("streams").setStringArray(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String pixelFormatToString(PixelFormat pixelFormat) {
|
||||
switch (pixelFormat) {
|
||||
case kMJPEG:
|
||||
return "MJPEG";
|
||||
case kYUYV:
|
||||
return "YUYV";
|
||||
case kRGB565:
|
||||
return "RGB565";
|
||||
case kBGR:
|
||||
return "BGR";
|
||||
case kGray:
|
||||
return "Gray";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide string description of video mode.
|
||||
/// The returned string is "{width}x{height} {format} {fps} fps".
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String videoModeToString(VideoMode mode) {
|
||||
return mode.width + "x" + mode.height + " " + pixelFormatToString(mode.pixelFormat)
|
||||
+ " " + mode.fps + " fps";
|
||||
}
|
||||
|
||||
@SuppressWarnings("JavadocMethod")
|
||||
private static String[] getSourceModeValues(int sourceHandle) {
|
||||
VideoMode[] modes = CameraServerJNI.enumerateSourceVideoModes(sourceHandle);
|
||||
String[] modeStrings = new String[modes.length];
|
||||
for (int i = 0; i < modes.length; i++) {
|
||||
modeStrings[i] = videoModeToString(modes[i]);
|
||||
}
|
||||
return modeStrings;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.CyclomaticComplexity"})
|
||||
private static void putSourcePropertyValue(NetworkTable table, VideoEvent event, boolean isNew) {
|
||||
String name;
|
||||
String infoName;
|
||||
if (event.name.startsWith("raw_")) {
|
||||
name = "RawProperty/" + event.name;
|
||||
infoName = "RawPropertyInfo/" + event.name;
|
||||
} else {
|
||||
name = "Property/" + event.name;
|
||||
infoName = "PropertyInfo/" + event.name;
|
||||
}
|
||||
|
||||
NetworkTableEntry entry = table.getEntry(name);
|
||||
try {
|
||||
switch (event.propertyKind) {
|
||||
case kBoolean:
|
||||
if (isNew) {
|
||||
entry.setDefaultBoolean(event.value != 0);
|
||||
} else {
|
||||
entry.setBoolean(event.value != 0);
|
||||
}
|
||||
break;
|
||||
case kInteger:
|
||||
case kEnum:
|
||||
if (isNew) {
|
||||
entry.setDefaultDouble(event.value);
|
||||
table.getEntry(infoName + "/min").setDouble(
|
||||
CameraServerJNI.getPropertyMin(event.propertyHandle));
|
||||
table.getEntry(infoName + "/max").setDouble(
|
||||
CameraServerJNI.getPropertyMax(event.propertyHandle));
|
||||
table.getEntry(infoName + "/step").setDouble(
|
||||
CameraServerJNI.getPropertyStep(event.propertyHandle));
|
||||
table.getEntry(infoName + "/default").setDouble(
|
||||
CameraServerJNI.getPropertyDefault(event.propertyHandle));
|
||||
} else {
|
||||
entry.setDouble(event.value);
|
||||
}
|
||||
break;
|
||||
case kString:
|
||||
if (isNew) {
|
||||
entry.setDefaultString(event.valueStr);
|
||||
} else {
|
||||
entry.setString(event.valueStr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (VideoException ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavadocMethod", "PMD.UnusedLocalVariable", "PMD.ExcessiveMethodLength",
|
||||
"PMD.NPathComplexity"})
|
||||
private CameraServer() {
|
||||
m_defaultUsbDevice = new AtomicInteger();
|
||||
m_sources = new HashMap<>();
|
||||
m_sinks = new HashMap<>();
|
||||
m_fixedSources = new HashMap<>();
|
||||
m_tables = new HashMap<>();
|
||||
m_publishTable = NetworkTableInstance.getDefault().getTable(kPublishName);
|
||||
m_nextPort = kBasePort;
|
||||
m_addresses = new String[0];
|
||||
|
||||
// We publish sources to NetworkTables using the following structure:
|
||||
// "/CameraPublisher/{Source.Name}/" - root
|
||||
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
|
||||
// - "streams" (string array): URLs that can be used to stream data
|
||||
// - "description" (string): Description of the source
|
||||
// - "connected" (boolean): Whether source is connected
|
||||
// - "mode" (string): Current video mode
|
||||
// - "modes" (string array): Available video modes
|
||||
// - "Property/{Property}" - Property values
|
||||
// - "PropertyInfo/{Property}" - Property supporting information
|
||||
|
||||
// Listener for video events
|
||||
m_videoListener = new VideoListener(event -> {
|
||||
switch (event.kind) {
|
||||
case kSourceCreated: {
|
||||
// Create subtable for the camera
|
||||
NetworkTable table = m_publishTable.getSubTable(event.name);
|
||||
m_tables.put(event.sourceHandle, table);
|
||||
table.getEntry("source").setString(makeSourceValue(event.sourceHandle));
|
||||
table.getEntry("description").setString(
|
||||
CameraServerJNI.getSourceDescription(event.sourceHandle));
|
||||
table.getEntry("connected").setBoolean(
|
||||
CameraServerJNI.isSourceConnected(event.sourceHandle));
|
||||
table.getEntry("streams").setStringArray(getSourceStreamValues(event.sourceHandle));
|
||||
try {
|
||||
VideoMode mode = CameraServerJNI.getSourceVideoMode(event.sourceHandle);
|
||||
table.getEntry("mode").setDefaultString(videoModeToString(mode));
|
||||
table.getEntry("modes").setStringArray(getSourceModeValues(event.sourceHandle));
|
||||
} catch (VideoException ignored) {
|
||||
// Do nothing. Let the other event handlers update this if there is an error.
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceDestroyed: {
|
||||
NetworkTable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
table.getEntry("source").setString("");
|
||||
table.getEntry("streams").setStringArray(new String[0]);
|
||||
table.getEntry("modes").setStringArray(new String[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceConnected: {
|
||||
NetworkTable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
// update the description too (as it may have changed)
|
||||
table.getEntry("description").setString(
|
||||
CameraServerJNI.getSourceDescription(event.sourceHandle));
|
||||
table.getEntry("connected").setBoolean(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceDisconnected: {
|
||||
NetworkTable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
table.getEntry("connected").setBoolean(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceVideoModesUpdated: {
|
||||
NetworkTable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
table.getEntry("modes").setStringArray(getSourceModeValues(event.sourceHandle));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceVideoModeChanged: {
|
||||
NetworkTable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
table.getEntry("mode").setString(videoModeToString(event.mode));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourcePropertyCreated: {
|
||||
NetworkTable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
putSourcePropertyValue(table, event, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourcePropertyValueUpdated: {
|
||||
NetworkTable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
putSourcePropertyValue(table, event, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourcePropertyChoicesUpdated: {
|
||||
NetworkTable table = m_tables.get(event.sourceHandle);
|
||||
if (table != null) {
|
||||
try {
|
||||
String[] choices = CameraServerJNI.getEnumPropertyChoices(event.propertyHandle);
|
||||
table.getEntry("PropertyInfo/" + event.name + "/choices").setStringArray(choices);
|
||||
} catch (VideoException ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSinkSourceChanged:
|
||||
case kSinkCreated:
|
||||
case kSinkDestroyed:
|
||||
case kNetworkInterfacesChanged: {
|
||||
m_addresses = CameraServerJNI.getNetworkInterfaces();
|
||||
updateStreamValues();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, 0x4fff, true);
|
||||
|
||||
// Listener for NetworkTable events
|
||||
// We don't currently support changing settings via NT due to
|
||||
// synchronization issues, so just update to current setting if someone
|
||||
// else tries to change it.
|
||||
m_tableListener = NetworkTableInstance.getDefault().addEntryListener(kPublishName + "/",
|
||||
event -> {
|
||||
String relativeKey = event.name.substring(kPublishName.length() + 1);
|
||||
|
||||
// get source (sourceName/...)
|
||||
int subKeyIndex = relativeKey.indexOf('/');
|
||||
if (subKeyIndex == -1) {
|
||||
return;
|
||||
}
|
||||
String sourceName = relativeKey.substring(0, subKeyIndex);
|
||||
VideoSource source = m_sources.get(sourceName);
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get subkey
|
||||
relativeKey = relativeKey.substring(subKeyIndex + 1);
|
||||
|
||||
// handle standard names
|
||||
String propName;
|
||||
if ("mode".equals(relativeKey)) {
|
||||
// reset to current mode
|
||||
event.getEntry().setString(videoModeToString(source.getVideoMode()));
|
||||
return;
|
||||
} else if (relativeKey.startsWith("Property/")) {
|
||||
propName = relativeKey.substring(9);
|
||||
} else if (relativeKey.startsWith("RawProperty/")) {
|
||||
propName = relativeKey.substring(12);
|
||||
} else {
|
||||
return; // ignore
|
||||
}
|
||||
|
||||
// everything else is a property
|
||||
VideoProperty property = source.getProperty(propName);
|
||||
switch (property.getKind()) {
|
||||
case kNone:
|
||||
return;
|
||||
case kBoolean:
|
||||
// reset to current setting
|
||||
event.getEntry().setBoolean(property.get() != 0);
|
||||
return;
|
||||
case kInteger:
|
||||
case kEnum:
|
||||
// reset to current setting
|
||||
event.getEntry().setDouble(property.get());
|
||||
return;
|
||||
case kString:
|
||||
// reset to current setting
|
||||
event.getEntry().setString(property.getString());
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}, EntryListenerFlags.kImmediate | EntryListenerFlags.kUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* <p>You should call this method to see a camera feed on the dashboard.
|
||||
* If you also want to perform vision processing on the roboRIO, use
|
||||
* getVideo() to get access to the camera images.
|
||||
*
|
||||
* <p>The first time this overload is called, it calls
|
||||
* {@link #startAutomaticCapture(int)} with device 0, creating a camera
|
||||
* named "USB Camera 0". Subsequent calls increment the device number
|
||||
* (e.g. 1, 2, etc).
|
||||
*/
|
||||
public UsbCamera startAutomaticCapture() {
|
||||
UsbCamera camera = startAutomaticCapture(m_defaultUsbDevice.getAndIncrement());
|
||||
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* <p>This overload calls {@link #startAutomaticCapture(String, int)} with
|
||||
* a name of "USB Camera {dev}".
|
||||
*
|
||||
* @param dev The device number of the camera interface
|
||||
*/
|
||||
public UsbCamera startAutomaticCapture(int dev) {
|
||||
UsbCamera camera = new UsbCamera("USB Camera " + dev, dev);
|
||||
startAutomaticCapture(camera);
|
||||
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param dev The device number of the camera interface
|
||||
*/
|
||||
public UsbCamera startAutomaticCapture(String name, int dev) {
|
||||
UsbCamera camera = new UsbCamera(name, dev);
|
||||
startAutomaticCapture(camera);
|
||||
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param path The device path (e.g. "/dev/video0") of the camera
|
||||
*/
|
||||
public UsbCamera startAutomaticCapture(String name, String path) {
|
||||
UsbCamera camera = new UsbCamera(name, path);
|
||||
startAutomaticCapture(camera);
|
||||
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard from
|
||||
* an existing camera.
|
||||
*
|
||||
* @param camera Camera
|
||||
*/
|
||||
public MjpegServer startAutomaticCapture(VideoSource camera) {
|
||||
addCamera(camera);
|
||||
MjpegServer server = addServer("serve_" + camera.getName());
|
||||
server.setSource(camera);
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* <p>This overload calls {@link #addAxisCamera(String, String)} with
|
||||
* name "Axis Camera".
|
||||
*
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
public AxisCamera addAxisCamera(String host) {
|
||||
return addAxisCamera("Axis Camera", host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* <p>This overload calls {@link #addAxisCamera(String, String[])} with
|
||||
* name "Axis Camera".
|
||||
*
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
*/
|
||||
public AxisCamera addAxisCamera(String[] hosts) {
|
||||
return addAxisCamera("Axis Camera", hosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
public AxisCamera addAxisCamera(String name, String host) {
|
||||
AxisCamera camera = new AxisCamera(name, host);
|
||||
// Create a passthrough MJPEG server for USB access
|
||||
startAutomaticCapture(camera);
|
||||
CameraServerSharedStore.getCameraServerShared().reportAxisCamera(camera.getHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
*/
|
||||
public AxisCamera addAxisCamera(String name, String[] hosts) {
|
||||
AxisCamera camera = new AxisCamera(name, hosts);
|
||||
// Create a passthrough MJPEG server for USB access
|
||||
startAutomaticCapture(camera);
|
||||
CameraServerSharedStore.getCameraServerShared().reportAxisCamera(camera.getHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a virtual camera for switching between two streams. Unlike the
|
||||
* other addCamera methods, this returns a VideoSink rather than a
|
||||
* VideoSource. Calling setSource() on the returned object can be used
|
||||
* to switch the actual source of the stream.
|
||||
*/
|
||||
public MjpegServer addSwitchedCamera(String name) {
|
||||
// create a dummy CvSource
|
||||
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, 160, 120, 30);
|
||||
MjpegServer server = startAutomaticCapture(source);
|
||||
synchronized (this) {
|
||||
m_fixedSources.put(server.getHandle(), source.getHandle());
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the primary camera feed. This allows you to
|
||||
* get images from the camera for image processing on the roboRIO.
|
||||
*
|
||||
* <p>This is only valid to call after a camera feed has been added
|
||||
* with startAutomaticCapture() or addServer().
|
||||
*/
|
||||
public CvSink getVideo() {
|
||||
VideoSource source;
|
||||
synchronized (this) {
|
||||
if (m_primarySourceName == null) {
|
||||
throw new VideoException("no camera available");
|
||||
}
|
||||
source = m_sources.get(m_primarySourceName);
|
||||
}
|
||||
if (source == null) {
|
||||
throw new VideoException("no camera available");
|
||||
}
|
||||
return getVideo(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the specified camera. This allows you to get
|
||||
* images from the camera for image processing on the roboRIO.
|
||||
*
|
||||
* @param camera Camera (e.g. as returned by startAutomaticCapture).
|
||||
*/
|
||||
public CvSink getVideo(VideoSource camera) {
|
||||
String name = "opencv_" + camera.getName();
|
||||
|
||||
synchronized (this) {
|
||||
VideoSink sink = m_sinks.get(name);
|
||||
if (sink != null) {
|
||||
VideoSink.Kind kind = sink.getKind();
|
||||
if (kind != VideoSink.Kind.kCv) {
|
||||
throw new VideoException("expected OpenCV sink, but got " + kind);
|
||||
}
|
||||
return (CvSink) sink;
|
||||
}
|
||||
}
|
||||
|
||||
CvSink newsink = new CvSink(name);
|
||||
newsink.setSource(camera);
|
||||
addServer(newsink);
|
||||
return newsink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the specified camera. This allows you to get
|
||||
* images from the camera for image processing on the roboRIO.
|
||||
*
|
||||
* @param name Camera name
|
||||
*/
|
||||
public CvSink getVideo(String name) {
|
||||
VideoSource source;
|
||||
synchronized (this) {
|
||||
source = m_sources.get(name);
|
||||
if (source == null) {
|
||||
throw new VideoException("could not find camera " + name);
|
||||
}
|
||||
}
|
||||
return getVideo(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MJPEG stream with OpenCV input. This can be called to pass custom
|
||||
* annotated images to the dashboard.
|
||||
*
|
||||
* @param name Name to give the stream
|
||||
* @param width Width of the image being sent
|
||||
* @param height Height of the image being sent
|
||||
*/
|
||||
public CvSource putVideo(String name, int width, int height) {
|
||||
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, width, height, 30);
|
||||
startAutomaticCapture(source);
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a MJPEG server at the next available port.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
public MjpegServer addServer(String name) {
|
||||
int port;
|
||||
synchronized (this) {
|
||||
port = m_nextPort;
|
||||
m_nextPort++;
|
||||
}
|
||||
return addServer(name, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a MJPEG server.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
public MjpegServer addServer(String name, int port) {
|
||||
MjpegServer server = new MjpegServer(name, port);
|
||||
addServer(server);
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an already created server.
|
||||
*
|
||||
* @param server Server
|
||||
*/
|
||||
public void addServer(VideoSink server) {
|
||||
synchronized (this) {
|
||||
m_sinks.put(server.getName(), server);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a server by name.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
public void removeServer(String name) {
|
||||
synchronized (this) {
|
||||
m_sinks.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server for the primary camera feed.
|
||||
*
|
||||
* <p>This is only valid to call after a camera feed has been added
|
||||
* with startAutomaticCapture() or addServer().
|
||||
*/
|
||||
public VideoSink getServer() {
|
||||
synchronized (this) {
|
||||
if (m_primarySourceName == null) {
|
||||
throw new VideoException("no camera available");
|
||||
}
|
||||
return getServer("serve_" + m_primarySourceName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a server by name.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
public VideoSink getServer(String name) {
|
||||
synchronized (this) {
|
||||
return m_sinks.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an already created camera.
|
||||
*
|
||||
* @param camera Camera
|
||||
*/
|
||||
public void addCamera(VideoSource camera) {
|
||||
String name = camera.getName();
|
||||
synchronized (this) {
|
||||
if (m_primarySourceName == null) {
|
||||
m_primarySourceName = name;
|
||||
}
|
||||
m_sources.put(name, camera);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a camera by name.
|
||||
*
|
||||
* @param name Camera name
|
||||
*/
|
||||
public void removeCamera(String name) {
|
||||
synchronized (this) {
|
||||
m_sources.remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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.first.cameraserver;
|
||||
|
||||
|
||||
public interface CameraServerShared {
|
||||
/**
|
||||
* get the main thread id func.
|
||||
*
|
||||
* @return the robotMainThreadId
|
||||
*/
|
||||
Long getRobotMainThreadId();
|
||||
|
||||
/**
|
||||
* Report an error to the driver station.
|
||||
*
|
||||
* @param error the error to set
|
||||
*/
|
||||
void reportDriverStationError(String error);
|
||||
|
||||
/**
|
||||
* Report an video server usage.
|
||||
*
|
||||
* @param id the usage id
|
||||
*/
|
||||
void reportVideoServer(int id);
|
||||
|
||||
/**
|
||||
* Report a usb camera usage.
|
||||
*
|
||||
* @param id the usage id
|
||||
*/
|
||||
void reportUsbCamera(int id);
|
||||
|
||||
/**
|
||||
* Report an axis camera usage.
|
||||
*
|
||||
* @param id the usage id
|
||||
*/
|
||||
void reportAxisCamera(int id);
|
||||
|
||||
/**
|
||||
* Get if running on a roboRIO.
|
||||
*
|
||||
* @return true if on roboRIO
|
||||
*/
|
||||
default boolean isRoboRIO() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.cameraserver;
|
||||
|
||||
public final class CameraServerSharedStore {
|
||||
private static CameraServerShared cameraServerShared;
|
||||
|
||||
private CameraServerSharedStore() {
|
||||
}
|
||||
|
||||
/**
|
||||
* get the CameraServerShared object.
|
||||
*/
|
||||
public static synchronized CameraServerShared getCameraServerShared() {
|
||||
if (cameraServerShared == null) {
|
||||
cameraServerShared = new CameraServerShared() {
|
||||
|
||||
@Override
|
||||
public void reportVideoServer(int id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportUsbCamera(int id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportDriverStationError(String error) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportAxisCamera(int id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getRobotMainThreadId() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
return cameraServerShared;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the CameraServerShared object.
|
||||
*/
|
||||
public static synchronized void setCameraServerShared(CameraServerShared shared) {
|
||||
cameraServerShared = shared;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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.first.vision;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
/**
|
||||
* A vision pipeline is responsible for running a group of
|
||||
* OpenCV algorithms to extract data from an image.
|
||||
*
|
||||
* @see VisionRunner
|
||||
* @see VisionThread
|
||||
*/
|
||||
public interface VisionPipeline {
|
||||
/**
|
||||
* Processes the image input and sets the result objects.
|
||||
* Implementations should make these objects accessible.
|
||||
*/
|
||||
void process(Mat image);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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.first.vision;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import edu.wpi.cscore.CvSink;
|
||||
import edu.wpi.cscore.VideoSource;
|
||||
import edu.wpi.first.cameraserver.CameraServerSharedStore;
|
||||
|
||||
/**
|
||||
* A vision runner is a convenient wrapper object to make it easy to run vision pipelines
|
||||
* from robot code. The easiest way to use this is to run it in a {@link VisionThread}
|
||||
* and use the listener to take snapshots of the pipeline's outputs.
|
||||
*
|
||||
* @see VisionPipeline
|
||||
* @see VisionThread
|
||||
* @see <a href="package-summary.html">vision</a>
|
||||
*/
|
||||
public class VisionRunner<P extends VisionPipeline> {
|
||||
private final CvSink m_cvSink = new CvSink("VisionRunner CvSink");
|
||||
private final P m_pipeline;
|
||||
private final Mat m_image = new Mat();
|
||||
private final Listener<? super P> m_listener;
|
||||
private volatile boolean m_enabled = true;
|
||||
|
||||
/**
|
||||
* Listener interface for a callback that should run after a pipeline has processed its input.
|
||||
*
|
||||
* @param <P> the type of the pipeline this listener is for
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Listener<P extends VisionPipeline> {
|
||||
/**
|
||||
* Called when the pipeline has run. This shouldn't take much time to run because it will delay
|
||||
* later calls to the pipeline's {@link VisionPipeline#process process} method. Copying the
|
||||
* outputs and code that uses the copies should be <i>synchronized</i> on the same mutex to
|
||||
* prevent multiple threads from reading and writing to the same memory at the same time.
|
||||
*
|
||||
* @param pipeline the vision pipeline that ran
|
||||
*/
|
||||
void copyPipelineOutputs(P pipeline);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new vision runner. It will take images from the {@code videoSource}, send them to
|
||||
* the {@code pipeline}, and call the {@code listener} when the pipeline has finished to alert
|
||||
* user code when it is safe to access the pipeline's outputs.
|
||||
*
|
||||
* @param videoSource the video source to use to supply images for the pipeline
|
||||
* @param pipeline the vision pipeline to run
|
||||
* @param listener a function to call after the pipeline has finished running
|
||||
*/
|
||||
public VisionRunner(VideoSource videoSource, P pipeline, Listener<? super P> listener) {
|
||||
this.m_pipeline = pipeline;
|
||||
this.m_listener = listener;
|
||||
m_cvSink.setSource(videoSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the pipeline one time, giving it the next image from the video source specified
|
||||
* in the constructor. This will block until the source either has an image or throws an error.
|
||||
* If the source successfully supplied a frame, the pipeline's image input will be set,
|
||||
* the pipeline will run, and the listener specified in the constructor will be called to notify
|
||||
* it that the pipeline ran.
|
||||
*
|
||||
* <p>This method is exposed to allow teams to add additional functionality or have their own
|
||||
* ways to run the pipeline. Most teams, however, should just use {@link #runForever} in its own
|
||||
* thread using a {@link VisionThread}.</p>
|
||||
*/
|
||||
public void runOnce() {
|
||||
Long id = CameraServerSharedStore.getCameraServerShared().getRobotMainThreadId();
|
||||
|
||||
if (id != null && Thread.currentThread().getId() == id) {
|
||||
throw new IllegalStateException(
|
||||
"VisionRunner.runOnce() cannot be called from the main robot thread");
|
||||
}
|
||||
runOnceInternal();
|
||||
}
|
||||
|
||||
private void runOnceInternal() {
|
||||
long frameTime = m_cvSink.grabFrame(m_image);
|
||||
if (frameTime == 0) {
|
||||
// There was an error, report it
|
||||
String error = m_cvSink.getError();
|
||||
CameraServerSharedStore.getCameraServerShared().reportDriverStationError(error);
|
||||
} else {
|
||||
// No errors, process the image
|
||||
m_pipeline.process(m_image);
|
||||
m_listener.copyPipelineOutputs(m_pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method that calls {@link #runOnce()} in an infinite loop. This must
|
||||
* be run in a dedicated thread, and cannot be used in the main robot thread because
|
||||
* it will freeze the robot program.
|
||||
*
|
||||
* <p><strong>Do not call this method directly from the main thread.</strong></p>
|
||||
*
|
||||
* @throws IllegalStateException if this is called from the main robot thread
|
||||
* @see VisionThread
|
||||
*/
|
||||
public void runForever() {
|
||||
Long id = CameraServerSharedStore.getCameraServerShared().getRobotMainThreadId();
|
||||
|
||||
if (id != null && Thread.currentThread().getId() == id) {
|
||||
throw new IllegalStateException(
|
||||
"VisionRunner.runForever() cannot be called from the main robot thread");
|
||||
}
|
||||
while (m_enabled && !Thread.interrupted()) {
|
||||
runOnceInternal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a RunForever() loop.
|
||||
*/
|
||||
public void stop() {
|
||||
m_enabled = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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.first.vision;
|
||||
|
||||
import edu.wpi.cscore.VideoSource;
|
||||
|
||||
/**
|
||||
* A vision thread is a special thread that runs a vision pipeline. It is a <i>daemon</i> thread;
|
||||
* it does not prevent the program from exiting when all other non-daemon threads
|
||||
* have finished running.
|
||||
*
|
||||
* @see VisionPipeline
|
||||
* @see VisionRunner
|
||||
* @see Thread#setDaemon(boolean)
|
||||
*/
|
||||
public class VisionThread extends Thread {
|
||||
/**
|
||||
* Creates a vision thread that continuously runs a {@link VisionPipeline}.
|
||||
*
|
||||
* @param visionRunner the runner for a vision pipeline
|
||||
*/
|
||||
public VisionThread(VisionRunner<?> visionRunner) {
|
||||
super(visionRunner::runForever, "WPILib Vision Thread");
|
||||
setDaemon(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new vision thread that continuously runs the given vision pipeline. This is
|
||||
* equivalent to {@code new VisionThread(new VisionRunner<>(videoSource, pipeline, listener))}.
|
||||
*
|
||||
* @param videoSource the source for images the pipeline should process
|
||||
* @param pipeline the pipeline to run
|
||||
* @param listener the listener to copy outputs from the pipeline after it runs
|
||||
* @param <P> the type of the pipeline
|
||||
*/
|
||||
public <P extends VisionPipeline> VisionThread(VideoSource videoSource,
|
||||
P pipeline,
|
||||
VisionRunner.Listener<? super P> listener) {
|
||||
this(new VisionRunner<>(videoSource, pipeline, listener));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Classes in the {@code edu.wpi.first.vision} package are designed to
|
||||
* simplify using OpenCV vision processing code from a robot program.
|
||||
*
|
||||
* <p>An example use case for grabbing a yellow tote from 2015 in autonomous:
|
||||
* <br>
|
||||
* <pre><code>
|
||||
* public class Robot extends IterativeRobot
|
||||
* implements VisionRunner.Listener<MyFindTotePipeline> {
|
||||
*
|
||||
* // A USB camera connected to the roboRIO.
|
||||
* private {@link edu.wpi.cscore.VideoSource VideoSource} usbCamera;
|
||||
*
|
||||
* // A vision pipeline. This could be handwritten or generated by GRIP.
|
||||
* // This has to implement {@link edu.wpi.first.vision.VisionPipeline}.
|
||||
* // For this example, assume that it's perfect and will always see the tote.
|
||||
* private MyFindTotePipeline findTotePipeline;
|
||||
* private {@link edu.wpi.first.vision.VisionThread} findToteThread;
|
||||
*
|
||||
* // The object to synchronize on to make sure the vision thread doesn't
|
||||
* // write to variables the main thread is using.
|
||||
* private final Object visionLock = new Object();
|
||||
*
|
||||
* // The pipeline outputs we want
|
||||
* private boolean pipelineRan = false; // lets us know when the pipeline has actually run
|
||||
* private double angleToTote = 0;
|
||||
* private double distanceToTote = 0;
|
||||
*
|
||||
* {@literal @}Override
|
||||
* public void {@link edu.wpi.first.vision.VisionRunner.Listener#copyPipelineOutputs
|
||||
* copyPipelineOutputs(MyFindTotePipeline pipeline)} {
|
||||
* synchronized (visionLock) {
|
||||
* // Take a snapshot of the pipeline's output because
|
||||
* // it may have changed the next time this method is called!
|
||||
* this.pipelineRan = true;
|
||||
* this.angleToTote = pipeline.getAngleToTote();
|
||||
* this.distanceToTote = pipeline.getDistanceToTote();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* {@literal @}Override
|
||||
* public void robotInit() {
|
||||
* usbCamera = CameraServer.getInstance().startAutomaticCapture(0);
|
||||
* findTotePipeline = new MyFindTotePipeline();
|
||||
* findToteThread = new VisionThread(usbCamera, findTotePipeline, this);
|
||||
* }
|
||||
*
|
||||
* {@literal @}Override
|
||||
* public void autonomousInit() {
|
||||
* findToteThread.start();
|
||||
* }
|
||||
*
|
||||
* {@literal @}Override
|
||||
* public void autonomousPeriodic() {
|
||||
* double angle;
|
||||
* double distance;
|
||||
* synchronized (visionLock) {
|
||||
* if (!pipelineRan) {
|
||||
* // Wait until the pipeline has run
|
||||
* return;
|
||||
* }
|
||||
* // Copy the outputs to make sure they're all from the same run
|
||||
* angle = this.angleToTote;
|
||||
* distance = this.distanceToTote;
|
||||
* }
|
||||
* if (!aimedAtTote()) {
|
||||
* turnToAngle(angle);
|
||||
* } else if (!droveToTote()) {
|
||||
* driveDistance(distance);
|
||||
* } else if (!grabbedTote()) {
|
||||
* grabTote();
|
||||
* } else {
|
||||
* // Tote was grabbed and we're done!
|
||||
* return;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </code></pre>
|
||||
*/
|
||||
package edu.wpi.first.vision;
|
||||
709
cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp
Normal file
709
cameraserver/src/main/native/cpp/cameraserver/CameraServer.cpp
Normal file
@@ -0,0 +1,709 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "cameraserver/CameraServer.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include <networktables/NetworkTable.h>
|
||||
#include <networktables/NetworkTableInstance.h>
|
||||
#include <wpi/DenseMap.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringMap.h>
|
||||
#include <wpi/mutex.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "cameraserver/CameraServerShared.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
static constexpr char const* kPublishName = "/CameraPublisher";
|
||||
|
||||
struct CameraServer::Impl {
|
||||
Impl();
|
||||
std::shared_ptr<nt::NetworkTable> GetSourceTable(CS_Source source);
|
||||
std::vector<std::string> GetSinkStreamValues(CS_Sink sink);
|
||||
std::vector<std::string> GetSourceStreamValues(CS_Source source);
|
||||
void UpdateStreamValues();
|
||||
|
||||
wpi::mutex m_mutex;
|
||||
std::atomic<int> m_defaultUsbDevice{0};
|
||||
std::string m_primarySourceName;
|
||||
wpi::StringMap<cs::VideoSource> m_sources;
|
||||
wpi::StringMap<cs::VideoSink> m_sinks;
|
||||
wpi::DenseMap<CS_Sink, CS_Source> m_fixedSources;
|
||||
wpi::DenseMap<CS_Source, std::shared_ptr<nt::NetworkTable>> m_tables;
|
||||
std::shared_ptr<nt::NetworkTable> m_publishTable;
|
||||
cs::VideoListener m_videoListener;
|
||||
int m_tableListener;
|
||||
int m_nextPort;
|
||||
std::vector<std::string> m_addresses;
|
||||
};
|
||||
|
||||
CameraServer* CameraServer::GetInstance() {
|
||||
static CameraServer instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
static wpi::StringRef MakeSourceValue(CS_Source source,
|
||||
wpi::SmallVectorImpl<char>& buf) {
|
||||
CS_Status status = 0;
|
||||
buf.clear();
|
||||
switch (cs::GetSourceKind(source, &status)) {
|
||||
case CS_SOURCE_USB: {
|
||||
wpi::StringRef prefix{"usb:"};
|
||||
buf.append(prefix.begin(), prefix.end());
|
||||
auto path = cs::GetUsbCameraPath(source, &status);
|
||||
buf.append(path.begin(), path.end());
|
||||
break;
|
||||
}
|
||||
case CS_SOURCE_HTTP: {
|
||||
wpi::StringRef prefix{"ip:"};
|
||||
buf.append(prefix.begin(), prefix.end());
|
||||
auto urls = cs::GetHttpCameraUrls(source, &status);
|
||||
if (!urls.empty()) buf.append(urls[0].begin(), urls[0].end());
|
||||
break;
|
||||
}
|
||||
case CS_SOURCE_CV:
|
||||
return "cv:";
|
||||
default:
|
||||
return "unknown:";
|
||||
}
|
||||
|
||||
return wpi::StringRef{buf.begin(), buf.size()};
|
||||
}
|
||||
|
||||
static std::string MakeStreamValue(const wpi::Twine& address, int port) {
|
||||
return ("mjpg:http://" + address + wpi::Twine(':') + wpi::Twine(port) +
|
||||
"/?action=stream")
|
||||
.str();
|
||||
}
|
||||
|
||||
std::shared_ptr<nt::NetworkTable> CameraServer::Impl::GetSourceTable(
|
||||
CS_Source source) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
return m_tables.lookup(source);
|
||||
}
|
||||
|
||||
std::vector<std::string> CameraServer::Impl::GetSinkStreamValues(CS_Sink sink) {
|
||||
CS_Status status = 0;
|
||||
|
||||
// Ignore all but MjpegServer
|
||||
if (cs::GetSinkKind(sink, &status) != CS_SINK_MJPEG)
|
||||
return std::vector<std::string>{};
|
||||
|
||||
// Get port
|
||||
int port = cs::GetMjpegServerPort(sink, &status);
|
||||
|
||||
// Generate values
|
||||
std::vector<std::string> values;
|
||||
auto listenAddress = cs::GetMjpegServerListenAddress(sink, &status);
|
||||
if (!listenAddress.empty()) {
|
||||
// If a listen address is specified, only use that
|
||||
values.emplace_back(MakeStreamValue(listenAddress, port));
|
||||
} else {
|
||||
// Otherwise generate for hostname and all interface addresses
|
||||
values.emplace_back(MakeStreamValue(cs::GetHostname() + ".local", port));
|
||||
|
||||
for (const auto& addr : m_addresses) {
|
||||
if (addr == "127.0.0.1") continue; // ignore localhost
|
||||
values.emplace_back(MakeStreamValue(addr, port));
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
std::vector<std::string> CameraServer::Impl::GetSourceStreamValues(
|
||||
CS_Source source) {
|
||||
CS_Status status = 0;
|
||||
|
||||
// Ignore all but HttpCamera
|
||||
if (cs::GetSourceKind(source, &status) != CS_SOURCE_HTTP)
|
||||
return std::vector<std::string>{};
|
||||
|
||||
// Generate values
|
||||
auto values = cs::GetHttpCameraUrls(source, &status);
|
||||
for (auto& value : values) value = "mjpg:" + value;
|
||||
|
||||
#ifdef __FRC_ROBORIO__
|
||||
// Look to see if we have a passthrough server for this source
|
||||
// Only do this on the roboRIO
|
||||
for (const auto& i : m_sinks) {
|
||||
CS_Sink sink = i.second.GetHandle();
|
||||
CS_Source sinkSource = cs::GetSinkSource(sink, &status);
|
||||
if (source == sinkSource &&
|
||||
cs::GetSinkKind(sink, &status) == CS_SINK_MJPEG) {
|
||||
// Add USB-only passthrough
|
||||
int port = cs::GetMjpegServerPort(sink, &status);
|
||||
values.emplace_back(MakeStreamValue("172.22.11.2", port));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set table value
|
||||
return values;
|
||||
}
|
||||
|
||||
void CameraServer::Impl::UpdateStreamValues() {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
// Over all the sinks...
|
||||
for (const auto& i : m_sinks) {
|
||||
CS_Status status = 0;
|
||||
CS_Sink sink = i.second.GetHandle();
|
||||
|
||||
// Get the source's subtable (if none exists, we're done)
|
||||
CS_Source source = m_fixedSources.lookup(sink);
|
||||
if (source == 0) source = cs::GetSinkSource(sink, &status);
|
||||
if (source == 0) continue;
|
||||
auto table = m_tables.lookup(source);
|
||||
if (table) {
|
||||
// Don't set stream values if this is a HttpCamera passthrough
|
||||
if (cs::GetSourceKind(source, &status) == CS_SOURCE_HTTP) continue;
|
||||
|
||||
// Set table value
|
||||
auto values = GetSinkStreamValues(sink);
|
||||
if (!values.empty()) table->GetEntry("streams").SetStringArray(values);
|
||||
}
|
||||
}
|
||||
|
||||
// Over all the sources...
|
||||
for (const auto& i : m_sources) {
|
||||
CS_Source source = i.second.GetHandle();
|
||||
|
||||
// Get the source's subtable (if none exists, we're done)
|
||||
auto table = m_tables.lookup(source);
|
||||
if (table) {
|
||||
// Set table value
|
||||
auto values = GetSourceStreamValues(source);
|
||||
if (!values.empty()) table->GetEntry("streams").SetStringArray(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string PixelFormatToString(int pixelFormat) {
|
||||
switch (pixelFormat) {
|
||||
case cs::VideoMode::PixelFormat::kMJPEG:
|
||||
return "MJPEG";
|
||||
case cs::VideoMode::PixelFormat::kYUYV:
|
||||
return "YUYV";
|
||||
case cs::VideoMode::PixelFormat::kRGB565:
|
||||
return "RGB565";
|
||||
case cs::VideoMode::PixelFormat::kBGR:
|
||||
return "BGR";
|
||||
case cs::VideoMode::PixelFormat::kGray:
|
||||
return "Gray";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string VideoModeToString(const cs::VideoMode& mode) {
|
||||
std::string rv;
|
||||
wpi::raw_string_ostream oss{rv};
|
||||
oss << mode.width << "x" << mode.height;
|
||||
oss << " " << PixelFormatToString(mode.pixelFormat) << " ";
|
||||
oss << mode.fps << " fps";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static std::vector<std::string> GetSourceModeValues(int source) {
|
||||
std::vector<std::string> rv;
|
||||
CS_Status status = 0;
|
||||
for (const auto& mode : cs::EnumerateSourceVideoModes(source, &status))
|
||||
rv.emplace_back(VideoModeToString(mode));
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void PutSourcePropertyValue(nt::NetworkTable* table,
|
||||
const cs::VideoEvent& event, bool isNew) {
|
||||
wpi::SmallString<64> name;
|
||||
wpi::SmallString<64> infoName;
|
||||
if (wpi::StringRef{event.name}.startswith("raw_")) {
|
||||
name = "RawProperty/";
|
||||
name += event.name;
|
||||
infoName = "RawPropertyInfo/";
|
||||
infoName += event.name;
|
||||
} else {
|
||||
name = "Property/";
|
||||
name += event.name;
|
||||
infoName = "PropertyInfo/";
|
||||
infoName += event.name;
|
||||
}
|
||||
|
||||
wpi::SmallString<64> buf;
|
||||
CS_Status status = 0;
|
||||
nt::NetworkTableEntry entry = table->GetEntry(name);
|
||||
switch (event.propertyKind) {
|
||||
case CS_PROP_BOOLEAN:
|
||||
if (isNew)
|
||||
entry.SetDefaultBoolean(event.value != 0);
|
||||
else
|
||||
entry.SetBoolean(event.value != 0);
|
||||
break;
|
||||
case CS_PROP_INTEGER:
|
||||
case CS_PROP_ENUM:
|
||||
if (isNew) {
|
||||
entry.SetDefaultDouble(event.value);
|
||||
table->GetEntry(infoName + "/min")
|
||||
.SetDouble(cs::GetPropertyMin(event.propertyHandle, &status));
|
||||
table->GetEntry(infoName + "/max")
|
||||
.SetDouble(cs::GetPropertyMax(event.propertyHandle, &status));
|
||||
table->GetEntry(infoName + "/step")
|
||||
.SetDouble(cs::GetPropertyStep(event.propertyHandle, &status));
|
||||
table->GetEntry(infoName + "/default")
|
||||
.SetDouble(cs::GetPropertyDefault(event.propertyHandle, &status));
|
||||
} else {
|
||||
entry.SetDouble(event.value);
|
||||
}
|
||||
break;
|
||||
case CS_PROP_STRING:
|
||||
if (isNew)
|
||||
entry.SetDefaultString(event.valueStr);
|
||||
else
|
||||
entry.SetString(event.valueStr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CameraServer::Impl::Impl()
|
||||
: m_publishTable{nt::NetworkTableInstance::GetDefault().GetTable(
|
||||
kPublishName)},
|
||||
m_nextPort(kBasePort) {
|
||||
// We publish sources to NetworkTables using the following structure:
|
||||
// "/CameraPublisher/{Source.Name}/" - root
|
||||
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
|
||||
// - "streams" (string array): URLs that can be used to stream data
|
||||
// - "description" (string): Description of the source
|
||||
// - "connected" (boolean): Whether source is connected
|
||||
// - "mode" (string): Current video mode
|
||||
// - "modes" (string array): Available video modes
|
||||
// - "Property/{Property}" - Property values
|
||||
// - "PropertyInfo/{Property}" - Property supporting information
|
||||
|
||||
// Listener for video events
|
||||
m_videoListener = cs::VideoListener{
|
||||
[=](const cs::VideoEvent& event) {
|
||||
CS_Status status = 0;
|
||||
switch (event.kind) {
|
||||
case cs::VideoEvent::kSourceCreated: {
|
||||
// Create subtable for the camera
|
||||
auto table = m_publishTable->GetSubTable(event.name);
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_tables.insert(std::make_pair(event.sourceHandle, table));
|
||||
}
|
||||
wpi::SmallString<64> buf;
|
||||
table->GetEntry("source").SetString(
|
||||
MakeSourceValue(event.sourceHandle, buf));
|
||||
wpi::SmallString<64> descBuf;
|
||||
table->GetEntry("description")
|
||||
.SetString(cs::GetSourceDescription(event.sourceHandle, descBuf,
|
||||
&status));
|
||||
table->GetEntry("connected")
|
||||
.SetBoolean(cs::IsSourceConnected(event.sourceHandle, &status));
|
||||
table->GetEntry("streams").SetStringArray(
|
||||
GetSourceStreamValues(event.sourceHandle));
|
||||
auto mode = cs::GetSourceVideoMode(event.sourceHandle, &status);
|
||||
table->GetEntry("mode").SetDefaultString(VideoModeToString(mode));
|
||||
table->GetEntry("modes").SetStringArray(
|
||||
GetSourceModeValues(event.sourceHandle));
|
||||
break;
|
||||
}
|
||||
case cs::VideoEvent::kSourceDestroyed: {
|
||||
auto table = GetSourceTable(event.sourceHandle);
|
||||
if (table) {
|
||||
table->GetEntry("source").SetString("");
|
||||
table->GetEntry("streams").SetStringArray(
|
||||
std::vector<std::string>{});
|
||||
table->GetEntry("modes").SetStringArray(
|
||||
std::vector<std::string>{});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cs::VideoEvent::kSourceConnected: {
|
||||
auto table = GetSourceTable(event.sourceHandle);
|
||||
if (table) {
|
||||
// update the description too (as it may have changed)
|
||||
wpi::SmallString<64> descBuf;
|
||||
table->GetEntry("description")
|
||||
.SetString(cs::GetSourceDescription(event.sourceHandle,
|
||||
descBuf, &status));
|
||||
table->GetEntry("connected").SetBoolean(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cs::VideoEvent::kSourceDisconnected: {
|
||||
auto table = GetSourceTable(event.sourceHandle);
|
||||
if (table) table->GetEntry("connected").SetBoolean(false);
|
||||
break;
|
||||
}
|
||||
case cs::VideoEvent::kSourceVideoModesUpdated: {
|
||||
auto table = GetSourceTable(event.sourceHandle);
|
||||
if (table)
|
||||
table->GetEntry("modes").SetStringArray(
|
||||
GetSourceModeValues(event.sourceHandle));
|
||||
break;
|
||||
}
|
||||
case cs::VideoEvent::kSourceVideoModeChanged: {
|
||||
auto table = GetSourceTable(event.sourceHandle);
|
||||
if (table)
|
||||
table->GetEntry("mode").SetString(VideoModeToString(event.mode));
|
||||
break;
|
||||
}
|
||||
case cs::VideoEvent::kSourcePropertyCreated: {
|
||||
auto table = GetSourceTable(event.sourceHandle);
|
||||
if (table) PutSourcePropertyValue(table.get(), event, true);
|
||||
break;
|
||||
}
|
||||
case cs::VideoEvent::kSourcePropertyValueUpdated: {
|
||||
auto table = GetSourceTable(event.sourceHandle);
|
||||
if (table) PutSourcePropertyValue(table.get(), event, false);
|
||||
break;
|
||||
}
|
||||
case cs::VideoEvent::kSourcePropertyChoicesUpdated: {
|
||||
auto table = GetSourceTable(event.sourceHandle);
|
||||
if (table) {
|
||||
wpi::SmallString<64> name{"PropertyInfo/"};
|
||||
name += event.name;
|
||||
name += "/choices";
|
||||
auto choices =
|
||||
cs::GetEnumPropertyChoices(event.propertyHandle, &status);
|
||||
table->GetEntry(name).SetStringArray(choices);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cs::VideoEvent::kSinkSourceChanged:
|
||||
case cs::VideoEvent::kSinkCreated:
|
||||
case cs::VideoEvent::kSinkDestroyed:
|
||||
case cs::VideoEvent::kNetworkInterfacesChanged: {
|
||||
m_addresses = cs::GetNetworkInterfaces();
|
||||
UpdateStreamValues();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
0x4fff, true};
|
||||
|
||||
// Listener for NetworkTable events
|
||||
// We don't currently support changing settings via NT due to
|
||||
// synchronization issues, so just update to current setting if someone
|
||||
// else tries to change it.
|
||||
wpi::SmallString<64> buf;
|
||||
m_tableListener = nt::NetworkTableInstance::GetDefault().AddEntryListener(
|
||||
kPublishName + wpi::Twine('/'),
|
||||
[=](const nt::EntryNotification& event) {
|
||||
wpi::StringRef relativeKey =
|
||||
event.name.substr(wpi::StringRef(kPublishName).size() + 1);
|
||||
|
||||
// get source (sourceName/...)
|
||||
auto subKeyIndex = relativeKey.find('/');
|
||||
if (subKeyIndex == wpi::StringRef::npos) return;
|
||||
wpi::StringRef sourceName = relativeKey.slice(0, subKeyIndex);
|
||||
auto sourceIt = m_sources.find(sourceName);
|
||||
if (sourceIt == m_sources.end()) return;
|
||||
|
||||
// get subkey
|
||||
relativeKey = relativeKey.substr(subKeyIndex + 1);
|
||||
|
||||
// handle standard names
|
||||
wpi::StringRef propName;
|
||||
nt::NetworkTableEntry entry{event.entry};
|
||||
if (relativeKey == "mode") {
|
||||
// reset to current mode
|
||||
entry.SetString(VideoModeToString(sourceIt->second.GetVideoMode()));
|
||||
return;
|
||||
} else if (relativeKey.startswith("Property/")) {
|
||||
propName = relativeKey.substr(9);
|
||||
} else if (relativeKey.startswith("RawProperty/")) {
|
||||
propName = relativeKey.substr(12);
|
||||
} else {
|
||||
return; // ignore
|
||||
}
|
||||
|
||||
// everything else is a property
|
||||
auto property = sourceIt->second.GetProperty(propName);
|
||||
switch (property.GetKind()) {
|
||||
case cs::VideoProperty::kNone:
|
||||
return;
|
||||
case cs::VideoProperty::kBoolean:
|
||||
entry.SetBoolean(property.Get() != 0);
|
||||
return;
|
||||
case cs::VideoProperty::kInteger:
|
||||
case cs::VideoProperty::kEnum:
|
||||
entry.SetDouble(property.Get());
|
||||
return;
|
||||
case cs::VideoProperty::kString:
|
||||
entry.SetString(property.GetString());
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
NT_NOTIFY_IMMEDIATE | NT_NOTIFY_UPDATE);
|
||||
}
|
||||
|
||||
CameraServer::CameraServer() : m_impl(new Impl) {}
|
||||
|
||||
CameraServer::~CameraServer() {}
|
||||
|
||||
cs::UsbCamera CameraServer::StartAutomaticCapture() {
|
||||
cs::UsbCamera camera = StartAutomaticCapture(m_impl->m_defaultUsbDevice++);
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->ReportUsbCamera(camera.GetHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
cs::UsbCamera CameraServer::StartAutomaticCapture(int dev) {
|
||||
cs::UsbCamera camera{"USB Camera " + wpi::Twine(dev), dev};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->ReportUsbCamera(camera.GetHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
|
||||
int dev) {
|
||||
cs::UsbCamera camera{name, dev};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->ReportUsbCamera(camera.GetHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
cs::UsbCamera CameraServer::StartAutomaticCapture(const wpi::Twine& name,
|
||||
const wpi::Twine& path) {
|
||||
cs::UsbCamera camera{name, path};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->ReportUsbCamera(camera.GetHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& host) {
|
||||
return AddAxisCamera("Axis Camera", host);
|
||||
}
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(const char* host) {
|
||||
return AddAxisCamera("Axis Camera", host);
|
||||
}
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(const std::string& host) {
|
||||
return AddAxisCamera("Axis Camera", host);
|
||||
}
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(wpi::ArrayRef<std::string> hosts) {
|
||||
return AddAxisCamera("Axis Camera", hosts);
|
||||
}
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
||||
const wpi::Twine& host) {
|
||||
cs::AxisCamera camera{name, host};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->ReportAxisCamera(camera.GetHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
||||
const char* host) {
|
||||
cs::AxisCamera camera{name, host};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->ReportAxisCamera(camera.GetHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
||||
const std::string& host) {
|
||||
cs::AxisCamera camera{name, host};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->ReportAxisCamera(camera.GetHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(const wpi::Twine& name,
|
||||
wpi::ArrayRef<std::string> hosts) {
|
||||
cs::AxisCamera camera{name, hosts};
|
||||
StartAutomaticCapture(camera);
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->ReportAxisCamera(camera.GetHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
cs::MjpegServer CameraServer::AddSwitchedCamera(const wpi::Twine& name) {
|
||||
// create a dummy CvSource
|
||||
cs::CvSource source{name, cs::VideoMode::PixelFormat::kMJPEG, 160, 120, 30};
|
||||
cs::MjpegServer server = StartAutomaticCapture(source);
|
||||
m_impl->m_fixedSources[server.GetHandle()] = source.GetHandle();
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
cs::MjpegServer CameraServer::StartAutomaticCapture(
|
||||
const cs::VideoSource& camera) {
|
||||
AddCamera(camera);
|
||||
auto server = AddServer(wpi::Twine("serve_") + camera.GetName());
|
||||
server.SetSource(camera);
|
||||
return server;
|
||||
}
|
||||
|
||||
cs::CvSink CameraServer::GetVideo() {
|
||||
cs::VideoSource source;
|
||||
{
|
||||
auto csShared = GetCameraServerShared();
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
if (m_impl->m_primarySourceName.empty()) {
|
||||
csShared->SetCameraServerError("no camera available");
|
||||
return cs::CvSink{};
|
||||
}
|
||||
auto it = m_impl->m_sources.find(m_impl->m_primarySourceName);
|
||||
if (it == m_impl->m_sources.end()) {
|
||||
csShared->SetCameraServerError("no camera available");
|
||||
return cs::CvSink{};
|
||||
}
|
||||
source = it->second;
|
||||
}
|
||||
return GetVideo(std::move(source));
|
||||
}
|
||||
|
||||
cs::CvSink CameraServer::GetVideo(const cs::VideoSource& camera) {
|
||||
wpi::SmallString<64> name{"opencv_"};
|
||||
name += camera.GetName();
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
auto it = m_impl->m_sinks.find(name);
|
||||
if (it != m_impl->m_sinks.end()) {
|
||||
auto kind = it->second.GetKind();
|
||||
if (kind != cs::VideoSink::kCv) {
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->SetCameraServerError("expected OpenCV sink, but got " +
|
||||
wpi::Twine(kind));
|
||||
return cs::CvSink{};
|
||||
}
|
||||
return *static_cast<cs::CvSink*>(&it->second);
|
||||
}
|
||||
}
|
||||
|
||||
cs::CvSink newsink{name};
|
||||
newsink.SetSource(camera);
|
||||
AddServer(newsink);
|
||||
return newsink;
|
||||
}
|
||||
|
||||
cs::CvSink CameraServer::GetVideo(const wpi::Twine& name) {
|
||||
wpi::SmallString<64> nameBuf;
|
||||
wpi::StringRef nameStr = name.toStringRef(nameBuf);
|
||||
cs::VideoSource source;
|
||||
{
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
auto it = m_impl->m_sources.find(nameStr);
|
||||
if (it == m_impl->m_sources.end()) {
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->SetCameraServerError("could not find camera " + nameStr);
|
||||
return cs::CvSink{};
|
||||
}
|
||||
source = it->second;
|
||||
}
|
||||
return GetVideo(source);
|
||||
}
|
||||
|
||||
cs::CvSource CameraServer::PutVideo(const wpi::Twine& name, int width,
|
||||
int height) {
|
||||
cs::CvSource source{name, cs::VideoMode::kMJPEG, width, height, 30};
|
||||
StartAutomaticCapture(source);
|
||||
return source;
|
||||
}
|
||||
|
||||
cs::MjpegServer CameraServer::AddServer(const wpi::Twine& name) {
|
||||
int port;
|
||||
{
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
port = m_impl->m_nextPort++;
|
||||
}
|
||||
return AddServer(name, port);
|
||||
}
|
||||
|
||||
cs::MjpegServer CameraServer::AddServer(const wpi::Twine& name, int port) {
|
||||
cs::MjpegServer server{name, port};
|
||||
AddServer(server);
|
||||
return server;
|
||||
}
|
||||
|
||||
void CameraServer::AddServer(const cs::VideoSink& server) {
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
m_impl->m_sinks.try_emplace(server.GetName(), server);
|
||||
}
|
||||
|
||||
void CameraServer::RemoveServer(const wpi::Twine& name) {
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
wpi::SmallString<64> nameBuf;
|
||||
m_impl->m_sinks.erase(name.toStringRef(nameBuf));
|
||||
}
|
||||
|
||||
cs::VideoSink CameraServer::GetServer() {
|
||||
wpi::SmallString<64> name;
|
||||
{
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
if (m_impl->m_primarySourceName.empty()) {
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->SetCameraServerError("no camera available");
|
||||
return cs::VideoSink{};
|
||||
}
|
||||
name = "serve_";
|
||||
name += m_impl->m_primarySourceName;
|
||||
}
|
||||
return GetServer(name);
|
||||
}
|
||||
|
||||
cs::VideoSink CameraServer::GetServer(const wpi::Twine& name) {
|
||||
wpi::SmallString<64> nameBuf;
|
||||
wpi::StringRef nameStr = name.toStringRef(nameBuf);
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
auto it = m_impl->m_sinks.find(nameStr);
|
||||
if (it == m_impl->m_sinks.end()) {
|
||||
auto csShared = GetCameraServerShared();
|
||||
csShared->SetCameraServerError("could not find server " + nameStr);
|
||||
return cs::VideoSink{};
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void CameraServer::AddCamera(const cs::VideoSource& camera) {
|
||||
std::string name = camera.GetName();
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
if (m_impl->m_primarySourceName.empty()) m_impl->m_primarySourceName = name;
|
||||
m_impl->m_sources.try_emplace(name, camera);
|
||||
}
|
||||
|
||||
void CameraServer::RemoveCamera(const wpi::Twine& name) {
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
wpi::SmallString<64> nameBuf;
|
||||
m_impl->m_sources.erase(name.toStringRef(nameBuf));
|
||||
}
|
||||
|
||||
void CameraServer::SetSize(int size) {
|
||||
std::scoped_lock lock(m_impl->m_mutex);
|
||||
if (m_impl->m_primarySourceName.empty()) return;
|
||||
auto it = m_impl->m_sources.find(m_impl->m_primarySourceName);
|
||||
if (it == m_impl->m_sources.end()) return;
|
||||
if (size == kSize160x120)
|
||||
it->second.SetResolution(160, 120);
|
||||
else if (size == kSize320x240)
|
||||
it->second.SetResolution(320, 240);
|
||||
else if (size == kSize640x480)
|
||||
it->second.SetResolution(640, 480);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "cameraserver/CameraServerShared.h"
|
||||
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
namespace {
|
||||
class DefaultCameraServerShared : public frc::CameraServerShared {
|
||||
public:
|
||||
void ReportUsbCamera(int id) override {}
|
||||
void ReportAxisCamera(int id) override {}
|
||||
void ReportVideoServer(int id) override {}
|
||||
void SetCameraServerError(const wpi::Twine& error) override {}
|
||||
void SetVisionRunnerError(const wpi::Twine& error) override {}
|
||||
void ReportDriverStationError(const wpi::Twine& error) override {}
|
||||
std::pair<std::thread::id, bool> GetRobotMainThreadId() const override {
|
||||
return std::make_pair(std::thread::id(), false);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static std::unique_ptr<frc::CameraServerShared> cameraServerShared = nullptr;
|
||||
static wpi::mutex setLock;
|
||||
|
||||
namespace frc {
|
||||
CameraServerShared* GetCameraServerShared() {
|
||||
std::unique_lock lock(setLock);
|
||||
if (!cameraServerShared) {
|
||||
cameraServerShared = std::make_unique<DefaultCameraServerShared>();
|
||||
}
|
||||
return cameraServerShared.get();
|
||||
}
|
||||
} // namespace frc
|
||||
|
||||
extern "C" {
|
||||
void CameraServer_SetCameraServerShared(frc::CameraServerShared* shared) {
|
||||
std::unique_lock lock(setLock);
|
||||
cameraServerShared.reset(shared);
|
||||
}
|
||||
} // extern "C"
|
||||
59
cameraserver/src/main/native/cpp/vision/VisionRunner.cpp
Normal file
59
cameraserver/src/main/native/cpp/vision/VisionRunner.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "vision/VisionRunner.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <opencv2/core/mat.hpp>
|
||||
|
||||
#include "cameraserver/CameraServerShared.h"
|
||||
|
||||
using namespace frc;
|
||||
|
||||
VisionRunnerBase::VisionRunnerBase(cs::VideoSource videoSource)
|
||||
: m_image(std::make_unique<cv::Mat>()),
|
||||
m_cvSink("VisionRunner CvSink"),
|
||||
m_enabled(true) {
|
||||
m_cvSink.SetSource(videoSource);
|
||||
}
|
||||
|
||||
// Located here and not in header due to cv::Mat forward declaration.
|
||||
VisionRunnerBase::~VisionRunnerBase() {}
|
||||
|
||||
void VisionRunnerBase::RunOnce() {
|
||||
auto csShared = frc::GetCameraServerShared();
|
||||
auto res = csShared->GetRobotMainThreadId();
|
||||
if (res.second && (std::this_thread::get_id() == res.first)) {
|
||||
csShared->SetVisionRunnerError(
|
||||
"VisionRunner::RunOnce() cannot be called from the main robot thread");
|
||||
return;
|
||||
}
|
||||
auto frameTime = m_cvSink.GrabFrame(*m_image);
|
||||
if (frameTime == 0) {
|
||||
auto error = m_cvSink.GetError();
|
||||
csShared->ReportDriverStationError(error);
|
||||
} else {
|
||||
DoProcess(*m_image);
|
||||
}
|
||||
}
|
||||
|
||||
void VisionRunnerBase::RunForever() {
|
||||
auto csShared = frc::GetCameraServerShared();
|
||||
auto res = csShared->GetRobotMainThreadId();
|
||||
if (res.second && (std::this_thread::get_id() == res.first)) {
|
||||
csShared->SetVisionRunnerError(
|
||||
"VisionRunner::RunForever() cannot be called from the main robot "
|
||||
"thread");
|
||||
return;
|
||||
}
|
||||
while (m_enabled) {
|
||||
RunOnce();
|
||||
}
|
||||
}
|
||||
|
||||
void VisionRunnerBase::Stop() { m_enabled = false; }
|
||||
298
cameraserver/src/main/native/include/cameraserver/CameraServer.h
Normal file
298
cameraserver/src/main/native/include/cameraserver/CameraServer.h
Normal file
@@ -0,0 +1,298 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2014-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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <wpi/ArrayRef.h>
|
||||
#include <wpi/Twine.h>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
/**
|
||||
* Singleton class for creating and keeping camera servers.
|
||||
*
|
||||
* Also publishes camera information to NetworkTables.
|
||||
*/
|
||||
class CameraServer {
|
||||
public:
|
||||
static constexpr uint16_t kBasePort = 1181;
|
||||
static constexpr int kSize640x480 = 0;
|
||||
static constexpr int kSize320x240 = 1;
|
||||
static constexpr int kSize160x120 = 2;
|
||||
|
||||
/**
|
||||
* Get the CameraServer instance.
|
||||
*/
|
||||
static CameraServer* GetInstance();
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* You should call this method to see a camera feed on the dashboard. If you
|
||||
* also want to perform vision processing on the roboRIO, use getVideo() to
|
||||
* get access to the camera images.
|
||||
*
|
||||
* The first time this overload is called, it calls StartAutomaticCapture()
|
||||
* with device 0, creating a camera named "USB Camera 0". Subsequent calls
|
||||
* increment the device number (e.g. 1, 2, etc).
|
||||
*/
|
||||
cs::UsbCamera StartAutomaticCapture();
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* This overload calls StartAutomaticCapture() with a name of "USB Camera
|
||||
* {dev}".
|
||||
*
|
||||
* @param dev The device number of the camera interface
|
||||
*/
|
||||
cs::UsbCamera StartAutomaticCapture(int dev);
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param dev The device number of the camera interface
|
||||
*/
|
||||
cs::UsbCamera StartAutomaticCapture(const wpi::Twine& name, int dev);
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param path The device path (e.g. "/dev/video0") of the camera
|
||||
*/
|
||||
cs::UsbCamera StartAutomaticCapture(const wpi::Twine& name,
|
||||
const wpi::Twine& path);
|
||||
|
||||
/**
|
||||
* Start automatically capturing images to send to the dashboard from
|
||||
* an existing camera.
|
||||
*
|
||||
* @param camera Camera
|
||||
*/
|
||||
cs::MjpegServer StartAutomaticCapture(const cs::VideoSource& camera);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* This overload calls AddAxisCamera() with name "Axis Camera".
|
||||
*
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
cs::AxisCamera AddAxisCamera(const wpi::Twine& host);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* This overload calls AddAxisCamera() with name "Axis Camera".
|
||||
*
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
cs::AxisCamera AddAxisCamera(const char* host);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* This overload calls AddAxisCamera() with name "Axis Camera".
|
||||
*
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
cs::AxisCamera AddAxisCamera(const std::string& host);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* This overload calls AddAxisCamera() with name "Axis Camera".
|
||||
*
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
*/
|
||||
cs::AxisCamera AddAxisCamera(wpi::ArrayRef<std::string> hosts);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* This overload calls AddAxisCamera() with name "Axis Camera".
|
||||
*
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
*/
|
||||
template <typename T>
|
||||
cs::AxisCamera AddAxisCamera(std::initializer_list<T> hosts);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
cs::AxisCamera AddAxisCamera(const wpi::Twine& name, const wpi::Twine& host);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
cs::AxisCamera AddAxisCamera(const wpi::Twine& name, const char* host);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
*/
|
||||
cs::AxisCamera AddAxisCamera(const wpi::Twine& name, const std::string& host);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
*/
|
||||
cs::AxisCamera AddAxisCamera(const wpi::Twine& name,
|
||||
wpi::ArrayRef<std::string> hosts);
|
||||
|
||||
/**
|
||||
* Adds an Axis IP camera.
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
*/
|
||||
template <typename T>
|
||||
cs::AxisCamera AddAxisCamera(const wpi::Twine& name,
|
||||
std::initializer_list<T> hosts);
|
||||
|
||||
/**
|
||||
* Adds a virtual camera for switching between two streams. Unlike the
|
||||
* other addCamera methods, this returns a VideoSink rather than a
|
||||
* VideoSource. Calling SetSource() on the returned object can be used
|
||||
* to switch the actual source of the stream.
|
||||
*/
|
||||
cs::MjpegServer AddSwitchedCamera(const wpi::Twine& name);
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the primary camera feed. This allows you to
|
||||
* get images from the camera for image processing on the roboRIO.
|
||||
*
|
||||
* <p>This is only valid to call after a camera feed has been added
|
||||
* with startAutomaticCapture() or addServer().
|
||||
*/
|
||||
cs::CvSink GetVideo();
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the specified camera. This allows you to get
|
||||
* images from the camera for image processing on the roboRIO.
|
||||
*
|
||||
* @param camera Camera (e.g. as returned by startAutomaticCapture).
|
||||
*/
|
||||
cs::CvSink GetVideo(const cs::VideoSource& camera);
|
||||
|
||||
/**
|
||||
* Get OpenCV access to the specified camera. This allows you to get
|
||||
* images from the camera for image processing on the roboRIO.
|
||||
*
|
||||
* @param name Camera name
|
||||
*/
|
||||
cs::CvSink GetVideo(const wpi::Twine& name);
|
||||
|
||||
/**
|
||||
* Create a MJPEG stream with OpenCV input. This can be called to pass custom
|
||||
* annotated images to the dashboard.
|
||||
*
|
||||
* @param name Name to give the stream
|
||||
* @param width Width of the image being sent
|
||||
* @param height Height of the image being sent
|
||||
*/
|
||||
cs::CvSource PutVideo(const wpi::Twine& name, int width, int height);
|
||||
|
||||
/**
|
||||
* Adds a MJPEG server at the next available port.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
cs::MjpegServer AddServer(const wpi::Twine& name);
|
||||
|
||||
/**
|
||||
* Adds a MJPEG server.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
cs::MjpegServer AddServer(const wpi::Twine& name, int port);
|
||||
|
||||
/**
|
||||
* Adds an already created server.
|
||||
*
|
||||
* @param server Server
|
||||
*/
|
||||
void AddServer(const cs::VideoSink& server);
|
||||
|
||||
/**
|
||||
* Removes a server by name.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
void RemoveServer(const wpi::Twine& name);
|
||||
|
||||
/**
|
||||
* Get server for the primary camera feed.
|
||||
*
|
||||
* This is only valid to call after a camera feed has been added with
|
||||
* StartAutomaticCapture() or AddServer().
|
||||
*/
|
||||
cs::VideoSink GetServer();
|
||||
|
||||
/**
|
||||
* Gets a server by name.
|
||||
*
|
||||
* @param name Server name
|
||||
*/
|
||||
cs::VideoSink GetServer(const wpi::Twine& name);
|
||||
|
||||
/**
|
||||
* Adds an already created camera.
|
||||
*
|
||||
* @param camera Camera
|
||||
*/
|
||||
void AddCamera(const cs::VideoSource& camera);
|
||||
|
||||
/**
|
||||
* Removes a camera by name.
|
||||
*
|
||||
* @param name Camera name
|
||||
*/
|
||||
void RemoveCamera(const wpi::Twine& name);
|
||||
|
||||
/**
|
||||
* Sets the size of the image to use. Use the public kSize constants to set
|
||||
* the correct mode, or set it directly on a camera and call the appropriate
|
||||
* StartAutomaticCapture method.
|
||||
*
|
||||
* @deprecated Use SetResolution on the UsbCamera returned by
|
||||
* StartAutomaticCapture() instead.
|
||||
* @param size The size to use
|
||||
*/
|
||||
void SetSize(int size);
|
||||
|
||||
private:
|
||||
CameraServer();
|
||||
~CameraServer();
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // namespace frc
|
||||
|
||||
#include "cameraserver/CameraServer.inc"
|
||||
@@ -20,7 +20,7 @@ inline cs::AxisCamera CameraServer::AddAxisCamera(
|
||||
|
||||
template <typename T>
|
||||
inline cs::AxisCamera CameraServer::AddAxisCamera(
|
||||
llvm::StringRef name, std::initializer_list<T> hosts) {
|
||||
const wpi::Twine& name, std::initializer_list<T> hosts) {
|
||||
std::vector<std::string> vec;
|
||||
vec.reserve(hosts.size());
|
||||
for (const auto& host : hosts) vec.emplace_back(host);
|
||||
@@ -0,0 +1,35 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <wpi/Twine.h>
|
||||
|
||||
namespace frc {
|
||||
class CameraServerShared {
|
||||
public:
|
||||
virtual ~CameraServerShared() = default;
|
||||
virtual void ReportUsbCamera(int id) = 0;
|
||||
virtual void ReportAxisCamera(int id) = 0;
|
||||
virtual void ReportVideoServer(int id) = 0;
|
||||
virtual void SetCameraServerError(const wpi::Twine& error) = 0;
|
||||
virtual void SetVisionRunnerError(const wpi::Twine& error) = 0;
|
||||
virtual void ReportDriverStationError(const wpi::Twine& error) = 0;
|
||||
virtual std::pair<std::thread::id, bool> GetRobotMainThreadId() const = 0;
|
||||
};
|
||||
|
||||
CameraServerShared* GetCameraServerShared();
|
||||
} // namespace frc
|
||||
|
||||
extern "C" {
|
||||
// Takes ownership
|
||||
void CameraServer_SetCameraServerShared(frc::CameraServerShared* shared);
|
||||
} // extern "C"
|
||||
100
cameraserver/src/main/native/include/vision/VisionRunner.h
Normal file
100
cameraserver/src/main/native/include/vision/VisionRunner.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
#include "vision/VisionPipeline.h"
|
||||
|
||||
namespace frc {
|
||||
|
||||
/**
|
||||
* Non-template base class for VisionRunner.
|
||||
*/
|
||||
class VisionRunnerBase {
|
||||
public:
|
||||
/**
|
||||
* Creates a new vision runner. It will take images from the {@code
|
||||
* videoSource}, and call the virtual DoProcess() method.
|
||||
*
|
||||
* @param videoSource the video source to use to supply images for the
|
||||
* pipeline
|
||||
*/
|
||||
explicit VisionRunnerBase(cs::VideoSource videoSource);
|
||||
|
||||
~VisionRunnerBase();
|
||||
|
||||
VisionRunnerBase(const VisionRunnerBase&) = delete;
|
||||
VisionRunnerBase& operator=(const VisionRunnerBase&) = delete;
|
||||
|
||||
/**
|
||||
* Runs the pipeline one time, giving it the next image from the video source
|
||||
* specified in the constructor. This will block until the source either has
|
||||
* an image or throws an error. If the source successfully supplied a frame,
|
||||
* the pipeline's image input will be set, the pipeline will run, and the
|
||||
* listener specified in the constructor will be called to notify it that the
|
||||
* pipeline ran. This must be run in a dedicated thread, and cannot be used in
|
||||
* the main robot thread because it will freeze the robot program.
|
||||
*
|
||||
* <p>This method is exposed to allow teams to add additional functionality or
|
||||
* have their own ways to run the pipeline. Most teams, however, should just
|
||||
* use {@link #runForever} in its own thread using a std::thread.</p>
|
||||
*/
|
||||
void RunOnce();
|
||||
|
||||
/**
|
||||
* A convenience method that calls {@link #runOnce()} in an infinite loop.
|
||||
* This must be run in a dedicated thread, and cannot be used in the main
|
||||
* robot thread because it will freeze the robot program.
|
||||
*
|
||||
* <strong>Do not call this method directly from the main thread.</strong>
|
||||
*/
|
||||
void RunForever();
|
||||
|
||||
/**
|
||||
* Stop a RunForever() loop.
|
||||
*/
|
||||
void Stop();
|
||||
|
||||
protected:
|
||||
virtual void DoProcess(cv::Mat& image) = 0;
|
||||
|
||||
private:
|
||||
std::unique_ptr<cv::Mat> m_image;
|
||||
cs::CvSink m_cvSink;
|
||||
std::atomic_bool m_enabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* A vision runner is a convenient wrapper object to make it easy to run vision
|
||||
* pipelines from robot code. The easiest way to use this is to run it in a
|
||||
* std::thread and use the listener to take snapshots of the pipeline's outputs.
|
||||
*
|
||||
* @see VisionPipeline
|
||||
*/
|
||||
template <typename T>
|
||||
class VisionRunner : public VisionRunnerBase {
|
||||
public:
|
||||
VisionRunner(cs::VideoSource videoSource, T* pipeline,
|
||||
std::function<void(T&)> listener);
|
||||
virtual ~VisionRunner() = default;
|
||||
|
||||
protected:
|
||||
void DoProcess(cv::Mat& image) override;
|
||||
|
||||
private:
|
||||
T* m_pipeline;
|
||||
std::function<void(T&)> m_listener;
|
||||
};
|
||||
} // namespace frc
|
||||
|
||||
#include "VisionRunner.inc"
|
||||
8
cameraserver/src/test/native/cpp/main.cpp
Normal file
8
cameraserver/src/test/native/cpp/main.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
int main() { return 0; }
|
||||
14
cmake/modules/AddTest.cmake
Normal file
14
cmake/modules/AddTest.cmake
Normal file
@@ -0,0 +1,14 @@
|
||||
include(CompileWarnings)
|
||||
|
||||
macro(wpilib_add_test name srcdir)
|
||||
file(GLOB_RECURSE test_src ${srcdir}/*.cpp)
|
||||
add_executable(${name}_test ${test_src})
|
||||
wpilib_target_warnings(${name}_test)
|
||||
if (BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(${name}_test PRIVATE -DGTEST_LINKED_AS_SHARED_LIBRARY)
|
||||
endif()
|
||||
if (MSVC)
|
||||
target_compile_options(${name}_test PRIVATE /wd4251 /wd4101)
|
||||
endif()
|
||||
add_test(NAME ${name} COMMAND ${name}_test)
|
||||
endmacro()
|
||||
7
cmake/modules/CompileWarnings.cmake
Normal file
7
cmake/modules/CompileWarnings.cmake
Normal file
@@ -0,0 +1,7 @@
|
||||
macro(wpilib_target_warnings target)
|
||||
if(NOT MSVC)
|
||||
target_compile_options(${target} PRIVATE -Wall -pedantic -Wextra -Werror -Wno-unused-parameter -Wno-error=deprecated-declarations)
|
||||
else()
|
||||
target_compile_options(${target} PRIVATE /wd4244 /wd4267 /wd4146 /WX /wd4996)
|
||||
endif()
|
||||
endmacro()
|
||||
26
cmake/modules/GenResources.cmake
Normal file
26
cmake/modules/GenResources.cmake
Normal file
@@ -0,0 +1,26 @@
|
||||
set(SCRIPTS_DIR "${CMAKE_CURRENT_LIST_DIR}/../scripts")
|
||||
MACRO(GENERATE_RESOURCES inputDir outputDir prefix namespace outputFiles)
|
||||
FILE(GLOB inputFiles ${inputDir}/*)
|
||||
SET(${outputFiles})
|
||||
FOREACH(input ${inputFiles})
|
||||
GET_FILENAME_COMPONENT(inputBase ${input} NAME)
|
||||
IF("${inputBase}" MATCHES "^\\.")
|
||||
CONTINUE()
|
||||
ENDIF()
|
||||
SET(output "${outputDir}/${inputBase}.cpp")
|
||||
LIST(APPEND ${outputFiles} "${output}")
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ${output}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
"-Dinput=${input}"
|
||||
"-Doutput=${output}"
|
||||
"-Dprefix=${prefix}"
|
||||
"-Dnamespace=${namespace}"
|
||||
-P "${SCRIPTS_DIR}/GenResource.cmake"
|
||||
MAIN_DEPENDENCY ${input}
|
||||
DEPENDS ${SCRIPTS_DIR}/GenResource.cmake
|
||||
VERBATIM
|
||||
)
|
||||
ENDFOREACH()
|
||||
ENDMACRO()
|
||||
17
cmake/modules/SubDirList.cmake
Normal file
17
cmake/modules/SubDirList.cmake
Normal file
@@ -0,0 +1,17 @@
|
||||
MACRO(SUBDIR_LIST result curdir)
|
||||
FILE(GLOB children RELATIVE ${curdir} ${curdir}/*)
|
||||
SET(dirlist "")
|
||||
FOREACH(child ${children})
|
||||
IF(IS_DIRECTORY ${curdir}/${child})
|
||||
LIST(APPEND dirlist ${child})
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
SET(${result} ${dirlist})
|
||||
ENDMACRO()
|
||||
|
||||
MACRO(ADD_ALL_SUBDIRECTORIES curdir)
|
||||
SUBDIR_LIST (_SUBPROJECTS ${curdir})
|
||||
FOREACH (dir ${_SUBPROJECTS})
|
||||
ADD_SUBDIRECTORY (${dir})
|
||||
ENDFOREACH ()
|
||||
ENDMACRO()
|
||||
23
cmake/scripts/GenResource.cmake
Normal file
23
cmake/scripts/GenResource.cmake
Normal file
@@ -0,0 +1,23 @@
|
||||
# Parameters: input output prefix namespace
|
||||
FILE(READ ${input} fileHex HEX)
|
||||
STRING(LENGTH "${fileHex}" fileHexSize)
|
||||
MATH(EXPR fileSize "${fileHexSize} / 2")
|
||||
|
||||
GET_FILENAME_COMPONENT(inputBase ${input} NAME)
|
||||
STRING(REGEX REPLACE "[^a-zA-Z0-9]" "_" funcName "${inputBase}")
|
||||
SET(funcName "GetResource_${funcName}")
|
||||
|
||||
FILE(WRITE "${output}" "#include <stddef.h>\n#include <wpi/StringRef.h>\nextern \"C\" {\nstatic const unsigned char contents[] = {")
|
||||
|
||||
STRING(REGEX MATCHALL ".." outputData "${fileHex}")
|
||||
STRING(REGEX REPLACE ";" ", 0x" outputData "${outputData}")
|
||||
FILE(APPEND "${output}" " 0x${outputData} };\n")
|
||||
FILE(APPEND "${output}" "const unsigned char* ${prefix}${funcName}(size_t* len) {\n *len = ${fileSize};\n return contents;\n}\n}\n")
|
||||
|
||||
IF(NOT namespace STREQUAL "")
|
||||
FILE(APPEND "${output}" "namespace ${namespace} {\n")
|
||||
ENDIF()
|
||||
FILE(APPEND "${output}" "wpi::StringRef ${funcName}() {\n return wpi::StringRef(reinterpret_cast<const char*>(contents), ${fileSize});\n}\n")
|
||||
IF(NOT namespace STREQUAL "")
|
||||
FILE(APPEND "${output}" "}\n")
|
||||
ENDIF()
|
||||
4
cmake/toolchains/arm-frc-gnueabi.toolchain.cmake
Normal file
4
cmake/toolchains/arm-frc-gnueabi.toolchain.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
|
||||
set(GNU_MACHINE "arm-frc2020-linux-gnueabi" CACHE STRING "GNU compiler triple")
|
||||
set(SOFTFP yes)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/arm.toolchain.cmake")
|
||||
98
cmake/toolchains/arm-pi-gnueabihf.toolchain.cmake
Normal file
98
cmake/toolchains/arm-pi-gnueabihf.toolchain.cmake
Normal file
@@ -0,0 +1,98 @@
|
||||
set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
|
||||
set(GNU_MACHINE "arm-raspbian10-linux-gnueabi" CACHE STRING "GNU compiler triple")
|
||||
|
||||
if(COMMAND toolchain_save_config)
|
||||
return() # prevent recursive call
|
||||
endif()
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
else()
|
||||
#message("CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/opencv/platforms/linux/gnu.toolchain.cmake")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm AND NOT ARM_IGNORE_FP)
|
||||
set(FLOAT_ABI_SUFFIX "")
|
||||
if(NOT SOFTFP)
|
||||
set(FLOAT_ABI_SUFFIX "hf")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT "x${GCC_COMPILER_VERSION}" STREQUAL "x")
|
||||
set(__GCC_VER_SUFFIX "-${GCC_COMPILER_VERSION}")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER)
|
||||
find_program(CMAKE_C_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-gcc${__GCC_VER_SUFFIX})
|
||||
else()
|
||||
#message(WARNING "CMAKE_C_COMPILER=${CMAKE_C_COMPILER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_CXX_COMPILER)
|
||||
find_program(CMAKE_CXX_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-g++${__GCC_VER_SUFFIX})
|
||||
else()
|
||||
#message(WARNING "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_LINKER)
|
||||
find_program(CMAKE_LINKER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld)
|
||||
else()
|
||||
#message(WARNING "CMAKE_LINKER=${CMAKE_LINKER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_AR)
|
||||
find_program(CMAKE_AR NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar)
|
||||
else()
|
||||
#message(WARNING "CMAKE_AR=${CMAKE_AR} is defined")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED ARM_LINUX_SYSROOT AND DEFINED GNU_MACHINE)
|
||||
set(ARM_LINUX_SYSROOT /usr/${GNU_MACHINE}${FLOAT_ABI_SUFFIX})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_FLAGS)
|
||||
set(CMAKE_CXX_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_C_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,nocopyreloc")
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
|
||||
set(ARM_LINKER_FLAGS "-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
||||
set(ARM_LINKER_FLAGS "-Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
|
||||
endif()
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
else()
|
||||
#message(WARNING "CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}' is defined")
|
||||
endif()
|
||||
|
||||
if(USE_NEON)
|
||||
message(WARNING "You use obsolete variable USE_NEON to enable NEON instruction set. Use -DENABLE_NEON=ON instead." )
|
||||
set(ENABLE_NEON TRUE)
|
||||
elseif(USE_VFPV3)
|
||||
message(WARNING "You use obsolete variable USE_VFPV3 to enable VFPV3 instruction set. Use -DENABLE_VFPV3=ON instead." )
|
||||
set(ENABLE_VFPV3 TRUE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${ARM_LINUX_SYSROOT})
|
||||
|
||||
if(EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
endif()
|
||||
|
||||
set(TOOLCHAIN_CONFIG_VARS ${TOOLCHAIN_CONFIG_VARS}
|
||||
ARM_LINUX_SYSROOT
|
||||
ENABLE_NEON
|
||||
ENABLE_VFPV3
|
||||
CUDA_TOOLKIT_ROOT_DIR
|
||||
)
|
||||
toolchain_save_config()
|
||||
97
cmake/toolchains/arm.toolchain.cmake
Normal file
97
cmake/toolchains/arm.toolchain.cmake
Normal file
@@ -0,0 +1,97 @@
|
||||
if(COMMAND toolchain_save_config)
|
||||
return() # prevent recursive call
|
||||
endif()
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
|
||||
set(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
else()
|
||||
#message("CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/gnu.toolchain.cmake")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm AND NOT ARM_IGNORE_FP)
|
||||
set(FLOAT_ABI_SUFFIX "")
|
||||
if(NOT SOFTFP)
|
||||
set(FLOAT_ABI_SUFFIX "hf")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT "x${GCC_COMPILER_VERSION}" STREQUAL "x")
|
||||
set(__GCC_VER_SUFFIX "-${GCC_COMPILER_VERSION}")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_C_COMPILER)
|
||||
find_program(CMAKE_C_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-gcc${__GCC_VER_SUFFIX})
|
||||
else()
|
||||
#message(WARNING "CMAKE_C_COMPILER=${CMAKE_C_COMPILER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_CXX_COMPILER)
|
||||
find_program(CMAKE_CXX_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-g++${__GCC_VER_SUFFIX})
|
||||
else()
|
||||
#message(WARNING "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_LINKER)
|
||||
find_program(CMAKE_LINKER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld)
|
||||
else()
|
||||
#message(WARNING "CMAKE_LINKER=${CMAKE_LINKER} is defined")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_AR)
|
||||
find_program(CMAKE_AR NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar)
|
||||
else()
|
||||
#message(WARNING "CMAKE_AR=${CMAKE_AR} is defined")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED ARM_LINUX_SYSROOT AND DEFINED GNU_MACHINE)
|
||||
set(ARM_LINUX_SYSROOT /usr/${GNU_MACHINE}${FLOAT_ABI_SUFFIX})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_FLAGS)
|
||||
set(CMAKE_CXX_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_C_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "" CACHE INTERNAL "")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
|
||||
set(CMAKE_CXX_FLAGS "-mthumb ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "-mthumb ${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,nocopyreloc")
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
|
||||
set(ARM_LINKER_FLAGS "-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
|
||||
set(ARM_LINKER_FLAGS "-Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
|
||||
endif()
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
else()
|
||||
#message(WARNING "CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}' is defined")
|
||||
endif()
|
||||
|
||||
if(USE_NEON)
|
||||
message(WARNING "You use obsolete variable USE_NEON to enable NEON instruction set. Use -DENABLE_NEON=ON instead." )
|
||||
set(ENABLE_NEON TRUE)
|
||||
elseif(USE_VFPV3)
|
||||
message(WARNING "You use obsolete variable USE_VFPV3 to enable VFPV3 instruction set. Use -DENABLE_VFPV3=ON instead." )
|
||||
set(ENABLE_VFPV3 TRUE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${ARM_LINUX_SYSROOT})
|
||||
|
||||
if(EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${CUDA_TOOLKIT_ROOT_DIR})
|
||||
endif()
|
||||
|
||||
set(TOOLCHAIN_CONFIG_VARS ${TOOLCHAIN_CONFIG_VARS}
|
||||
ARM_LINUX_SYSROOT
|
||||
ENABLE_NEON
|
||||
ENABLE_VFPV3
|
||||
CUDA_TOOLKIT_ROOT_DIR
|
||||
)
|
||||
toolchain_save_config()
|
||||
134
cmake/toolchains/gnu.toolchain.cmake
Normal file
134
cmake/toolchains/gnu.toolchain.cmake
Normal file
@@ -0,0 +1,134 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
# load settings in case of "try compile"
|
||||
set(TOOLCHAIN_CONFIG_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain.config.cmake")
|
||||
get_property(__IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
|
||||
if(__IN_TRY_COMPILE)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/../toolchain.config.cmake" OPTIONAL) # CMAKE_BINARY_DIR is different
|
||||
macro(toolchain_save_config)
|
||||
# nothing
|
||||
endmacro()
|
||||
else()
|
||||
macro(toolchain_save_config)
|
||||
set(__config "#message(\"Load TOOLCHAIN config...\")\n")
|
||||
get_cmake_property(__variableNames VARIABLES)
|
||||
set(__vars_list ${ARGN})
|
||||
list(APPEND __vars_list
|
||||
${TOOLCHAIN_CONFIG_VARS}
|
||||
CMAKE_SYSTEM_NAME
|
||||
CMAKE_SYSTEM_VERSION
|
||||
CMAKE_SYSTEM_PROCESSOR
|
||||
CMAKE_C_COMPILER
|
||||
CMAKE_CXX_COMPILER
|
||||
CMAKE_C_FLAGS
|
||||
CMAKE_CXX_FLAGS
|
||||
CMAKE_SHARED_LINKER_FLAGS
|
||||
CMAKE_MODULE_LINKER_FLAGS
|
||||
CMAKE_EXE_LINKER_FLAGS
|
||||
CMAKE_SKIP_RPATH
|
||||
CMAKE_FIND_ROOT_PATH
|
||||
GCC_COMPILER_VERSION
|
||||
)
|
||||
foreach(__var ${__variableNames})
|
||||
foreach(_v ${__vars_list})
|
||||
if("x${__var}" STREQUAL "x${_v}")
|
||||
if(${__var} MATCHES " ")
|
||||
set(__config "${__config}set(${__var} \"${${__var}}\")\n")
|
||||
else()
|
||||
set(__config "${__config}set(${__var} ${${__var}})\n")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
if(EXISTS "${TOOLCHAIN_CONFIG_FILE}")
|
||||
file(READ "${TOOLCHAIN_CONFIG_FILE}" __config_old)
|
||||
endif()
|
||||
if("${__config_old}" STREQUAL "${__config}")
|
||||
# nothing
|
||||
else()
|
||||
#message("Update TOOLCHAIN config: ${__config}")
|
||||
file(WRITE "${TOOLCHAIN_CONFIG_FILE}" "${__config}")
|
||||
endif()
|
||||
unset(__config)
|
||||
unset(__config_old)
|
||||
unset(__vars_list)
|
||||
unset(__variableNames)
|
||||
endmacro()
|
||||
endif() # IN_TRY_COMPILE
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
endif()
|
||||
|
||||
macro(__cmake_find_root_save_and_reset)
|
||||
foreach(v
|
||||
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
|
||||
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
|
||||
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
|
||||
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
|
||||
)
|
||||
set(__save_${v} ${${v}})
|
||||
set(${v} NEVER)
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
macro(__cmake_find_root_restore)
|
||||
foreach(v
|
||||
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY
|
||||
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
|
||||
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
|
||||
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
|
||||
)
|
||||
set(${v} ${__save_${v}})
|
||||
unset(__save_${v})
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
|
||||
# macro to find programs on the host OS
|
||||
macro(find_host_program)
|
||||
__cmake_find_root_save_and_reset()
|
||||
if(CMAKE_HOST_WIN32)
|
||||
SET(WIN32 1)
|
||||
SET(UNIX)
|
||||
elseif(CMAKE_HOST_APPLE)
|
||||
SET(APPLE 1)
|
||||
SET(UNIX)
|
||||
endif()
|
||||
find_program(${ARGN})
|
||||
SET(WIN32)
|
||||
SET(APPLE)
|
||||
SET(UNIX 1)
|
||||
__cmake_find_root_restore()
|
||||
endmacro()
|
||||
|
||||
# macro to find packages on the host OS
|
||||
macro(find_host_package)
|
||||
__cmake_find_root_save_and_reset()
|
||||
if(CMAKE_HOST_WIN32)
|
||||
SET(WIN32 1)
|
||||
SET(UNIX)
|
||||
elseif(CMAKE_HOST_APPLE)
|
||||
SET(APPLE 1)
|
||||
SET(UNIX)
|
||||
endif()
|
||||
find_package(${ARGN})
|
||||
SET(WIN32)
|
||||
SET(APPLE)
|
||||
SET(UNIX 1)
|
||||
__cmake_find_root_restore()
|
||||
endmacro()
|
||||
|
||||
set(CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries.")
|
||||
235
config.gradle
235
config.gradle
@@ -1,235 +0,0 @@
|
||||
import edu.wpi.first.nativeutils.*
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
def windowsCompilerArgs = ['/EHsc', '/DNOMINMAX', '/Zi', '/FS', '/Zc:inline', '/MT']
|
||||
def windowsReleaseCompilerArgs = ['/O2']
|
||||
def windowsLinkerArgs = [ '/DEBUG:FULL' ]
|
||||
def windowsReleaseLinkerArgs = [ '/OPT:REF', '/OPT:ICF' ]
|
||||
|
||||
def linuxCompilerArgs = ['-std=c++11', '-Wformat=2', '-Wall', '-Wextra', '-Werror', '-pedantic', '-Wno-psabi', '-g',
|
||||
'-Wno-unused-parameter', '-fPIC', '-rdynamic', '-Wno-error=deprecated-declarations', '-pthread']
|
||||
def linuxLinkerArgs = ['-rdynamic', '-pthread']
|
||||
def linuxReleaseCompilerArgs = ['-Og']
|
||||
def linuxDebugCompilerArgs = ['-O0']
|
||||
def linux32BitArg = '-m32'
|
||||
|
||||
def macCompilerArgs = ['-std=c++11', '-Wall', '-Wextra', '-Werror', '-pedantic-errors', '-fPIC', '-g',
|
||||
'-Wno-unused-parameter', '-Wno-missing-field-initializers', '-Wno-unused-private-field',
|
||||
'-Wno-unused-const-variable', '-pthread']
|
||||
def macReleaseCompilerArgs = ['-O2']
|
||||
def macDebugCompilerArgs = ['-O0']
|
||||
def mac32BitArg = '-m32'
|
||||
|
||||
def buildAll = project.hasProperty('buildAll')
|
||||
|
||||
def windows64PlatformDetect = {
|
||||
def arch = System.getProperty("os.arch")
|
||||
def isWin = OperatingSystem.current().isWindows()
|
||||
if (buildAll) {
|
||||
return isWin
|
||||
} else {
|
||||
return isWin && arch == 'amd64'
|
||||
}
|
||||
}
|
||||
|
||||
def windows32PlatformDetect = {
|
||||
def arch = System.getProperty("os.arch")
|
||||
def isWin = OperatingSystem.current().isWindows()
|
||||
if (buildAll) {
|
||||
return isWin
|
||||
} else {
|
||||
return isWin && arch == 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
def linux32IntelPlatformDetect = {
|
||||
def arch = System.getProperty("os.arch")
|
||||
def isLinux = OperatingSystem.current().isLinux()
|
||||
def isIntel = (arch == 'amd64' || arch == 'i386')
|
||||
if (buildAll) {
|
||||
return isLinux && isIntel
|
||||
} else {
|
||||
return isLinux && arch == 'i386'
|
||||
}
|
||||
}
|
||||
|
||||
def linux64IntelPlatformDetect = {
|
||||
def arch = System.getProperty("os.arch")
|
||||
def isLinux = OperatingSystem.current().isLinux()
|
||||
def isIntel = (arch == 'amd64' || arch == 'i386')
|
||||
if (buildAll) {
|
||||
return isLinux && isIntel
|
||||
} else {
|
||||
return isLinux && arch == 'amd64'
|
||||
}
|
||||
}
|
||||
|
||||
def linuxArmPlatformDetect = {
|
||||
def arch = System.getProperty("os.arch")
|
||||
def isIntel = (arch == 'amd64' || arch == 'i386')
|
||||
return OperatingSystem.current().isLinux() && !isIntel
|
||||
}
|
||||
|
||||
def mac64PlatformDetect = {
|
||||
def arch = System.getProperty("os.arch")
|
||||
def isMac = OperatingSystem.current().isMacOsX()
|
||||
if (buildAll) {
|
||||
return isMac
|
||||
} else {
|
||||
return isMac && arch == 'x86_64'
|
||||
}
|
||||
}
|
||||
|
||||
def mac32PlatformDetect = {
|
||||
def arch = System.getProperty("os.arch")
|
||||
def isMac = OperatingSystem.current().isMacOsX()
|
||||
if (buildAll) {
|
||||
return isMac
|
||||
} else {
|
||||
return isMac && arch == 'x86'
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.hasProperty('skipAthena')) {
|
||||
model {
|
||||
buildConfigs {
|
||||
roboRio(CrossBuildConfig) {
|
||||
architecture = 'athena'
|
||||
operatingSystem = 'linux'
|
||||
toolChainPrefix = 'arm-frc-linux-gnueabi-'
|
||||
compilerArgs = linuxCompilerArgs
|
||||
linkerArgs = linuxLinkerArgs
|
||||
debugCompilerArgs = linuxDebugCompilerArgs
|
||||
releaseCompilerArgs = linuxReleaseCompilerArgs
|
||||
releaseStripBinaries = true
|
||||
compilerFamily = 'Gcc'
|
||||
exclude << 'halSim'
|
||||
exclude << 'halSimStaticDeps'
|
||||
exclude << 'halSimTestingBase'
|
||||
exclude << 'wpilibcTestingBase'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!project.hasProperty('onlyAthena')) {
|
||||
model {
|
||||
buildConfigs {
|
||||
winX86(BuildConfig) {
|
||||
architecture = 'x86'
|
||||
operatingSystem = 'windows'
|
||||
compilerArgs = windowsCompilerArgs
|
||||
linkerArgs = windowsLinkerArgs
|
||||
releaseCompilerArgs = windowsReleaseCompilerArgs
|
||||
releaseLinkerArgs = windowsReleaseLinkerArgs
|
||||
compilerFamily = 'VisualCpp'
|
||||
detectPlatform = windows32PlatformDetect
|
||||
exclude << 'halAthena'
|
||||
}
|
||||
winX64(BuildConfig) {
|
||||
architecture = 'x86-64'
|
||||
operatingSystem = 'windows'
|
||||
compilerArgs = windowsCompilerArgs
|
||||
linkerArgs = windowsLinkerArgs
|
||||
releaseCompilerArgs = windowsReleaseCompilerArgs
|
||||
releaseLinkerArgs = windowsReleaseLinkerArgs
|
||||
compilerFamily = 'VisualCpp'
|
||||
detectPlatform = windows64PlatformDetect
|
||||
exclude << 'halAthena'
|
||||
}
|
||||
/* Disable 32 bit linux until we can figure out jenkins
|
||||
linuxX86(BuildConfig) {
|
||||
architecture = 'x86'
|
||||
operatingSystem = 'linux'
|
||||
compilerArgs = linuxCompilerArgs
|
||||
compilerArgs << linux32BitArg
|
||||
linkerArgs = linuxLinkerArgs
|
||||
linkerArgs << linux32BitArg
|
||||
debugCompilerArgs = linuxDebugCompilerArgs
|
||||
releaseCompilerArgs = linuxReleaseCompilerArgs
|
||||
releaseStripBinaries = true
|
||||
compilerFamily = 'Gcc'
|
||||
detectPlatform = linux32IntelPlatformDetect
|
||||
exclude << 'halAthena'
|
||||
}
|
||||
*/
|
||||
linuxX64(BuildConfig) {
|
||||
architecture = 'x86-64'
|
||||
operatingSystem = 'linux'
|
||||
compilerArgs = linuxCompilerArgs
|
||||
linkerArgs = linuxLinkerArgs
|
||||
debugCompilerArgs = linuxDebugCompilerArgs
|
||||
releaseCompilerArgs = linuxReleaseCompilerArgs
|
||||
releaseStripBinaries = true
|
||||
compilerFamily = 'Gcc'
|
||||
detectPlatform = linux64IntelPlatformDetect
|
||||
exclude << 'halAthena'
|
||||
}
|
||||
/* 32 bit Mac OS X not supported by OpenCV.
|
||||
* If support is ever added, will add this back in
|
||||
macX86(BuildConfig) {
|
||||
architecture = 'x86'
|
||||
operatingSystem = 'osx'
|
||||
compilerArgs = macCompilerArgs
|
||||
compilerArgs << mac32BitArg
|
||||
linkerArgs << mac32BitArg
|
||||
debugCompilerArgs = macDebugCompilerArgs
|
||||
releaseCompilerArgs = macReleaseCompilerArgs
|
||||
releaseStripBinaries = true
|
||||
compilerFamily = 'Clang'
|
||||
detectPlatform = mac32PlatformDetect
|
||||
exclude << 'halAthena'
|
||||
}
|
||||
*/
|
||||
macX64(BuildConfig) {
|
||||
architecture = 'x86-64'
|
||||
operatingSystem = 'osx'
|
||||
compilerArgs = macCompilerArgs
|
||||
debugCompilerArgs = macDebugCompilerArgs
|
||||
releaseCompilerArgs = macReleaseCompilerArgs
|
||||
releaseStripBinaries = true
|
||||
compilerFamily = 'Clang'
|
||||
detectPlatform = mac64PlatformDetect
|
||||
exclude << 'halAthena'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (project.hasProperty('linuxCross')) {
|
||||
model {
|
||||
buildConfigs {
|
||||
linuxArm(CrossBuildConfig) {
|
||||
architecture = 'nativearm'
|
||||
operatingSystem = 'linux'
|
||||
toolChainPrefix = 'PLEASE_PROVIDE_A_COMPILER_NAME'
|
||||
compilerArgs = linuxCompilerArgs
|
||||
linkerArgs = linuxLinkerArgs
|
||||
debugCompilerArgs = linuxDebugCompilerArgs
|
||||
releaseCompilerArgs = linuxReleaseCompilerArgs
|
||||
releaseStripBinaries = true
|
||||
skipByDefault = true
|
||||
compilerFamily = 'Gcc'
|
||||
exclude << 'gmock'
|
||||
exclude << 'halAthena'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
model {
|
||||
buildConfigs {
|
||||
linuxArm(BuildConfig) {
|
||||
architecture = 'nativearm'
|
||||
operatingSystem = 'linux'
|
||||
compilerArgs = linuxCompilerArgs
|
||||
linkerArgs = linuxLinkerArgs
|
||||
debugCompilerArgs = linuxDebugCompilerArgs
|
||||
releaseCompilerArgs = linuxReleaseCompilerArgs
|
||||
releaseStripBinaries = true
|
||||
compilerFamily = 'Gcc'
|
||||
detectPlatform = linuxArmPlatformDetect
|
||||
exclude << 'halAthena'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
cscore/.styleguide
Normal file
37
cscore/.styleguide
Normal file
@@ -0,0 +1,37 @@
|
||||
cHeaderFileInclude {
|
||||
_c\.h$
|
||||
}
|
||||
|
||||
cppHeaderFileInclude {
|
||||
(?<!_c)\.h$
|
||||
\.hpp$
|
||||
\.inc$
|
||||
}
|
||||
|
||||
cppSrcFileInclude {
|
||||
\.cpp$
|
||||
}
|
||||
|
||||
licenseUpdateExclude {
|
||||
src/main/native/cpp/default_init_allocator\.h$
|
||||
}
|
||||
|
||||
includeGuardRoots {
|
||||
cscore/src/main/native/cpp/
|
||||
cscore/src/main/native/include/
|
||||
cscore/src/main/native/linux/
|
||||
cscore/src/main/native/osx/
|
||||
cscore/src/main/native/windows/
|
||||
cscore/src/main/test/native/cpp/
|
||||
}
|
||||
|
||||
repoRootNameOverride {
|
||||
cscore
|
||||
}
|
||||
|
||||
includeOtherLibs {
|
||||
^opencv2/
|
||||
^support/
|
||||
^tcpsockets/
|
||||
^wpi/
|
||||
}
|
||||
134
cscore/CMakeLists.txt
Normal file
134
cscore/CMakeLists.txt
Normal file
@@ -0,0 +1,134 @@
|
||||
project(cscore)
|
||||
|
||||
include(SubDirList)
|
||||
include(CompileWarnings)
|
||||
include(AddTest)
|
||||
|
||||
find_package( OpenCV REQUIRED )
|
||||
|
||||
file(GLOB
|
||||
cscore_native_src src/main/native/cpp/*.cpp)
|
||||
file(GLOB cscore_linux_src src/main/native/linux/*.cpp)
|
||||
file(GLOB cscore_osx_src src/main/native/osx/*.cpp)
|
||||
file(GLOB cscore_windows_src src/main/native/windows/*.cpp)
|
||||
|
||||
add_library(cscore ${cscore_native_src})
|
||||
set_target_properties(cscore PROPERTIES DEBUG_POSTFIX "d")
|
||||
|
||||
if(NOT MSVC)
|
||||
if (APPLE)
|
||||
target_sources(cscore PRIVATE ${cscore_osx_src})
|
||||
else()
|
||||
target_sources(cscore PRIVATE ${cscore_linux_src})
|
||||
endif()
|
||||
else()
|
||||
target_sources(cscore PRIVATE ${cscore_windows_src})
|
||||
target_compile_definitions(cscore PUBLIC -DNOMINMAX)
|
||||
target_compile_definitions(cscore PRIVATE -D_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
target_include_directories(cscore PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/include>
|
||||
$<INSTALL_INTERFACE:${include_dest}/cscore>)
|
||||
target_include_directories(cscore PRIVATE src/main/native/cpp)
|
||||
wpilib_target_warnings(cscore)
|
||||
target_link_libraries(cscore PUBLIC wpiutil ${OpenCV_LIBS})
|
||||
|
||||
set_property(TARGET cscore PROPERTY FOLDER "libraries")
|
||||
|
||||
install(TARGETS cscore EXPORT cscore DESTINATION "${main_lib_dest}")
|
||||
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/cscore")
|
||||
|
||||
if (MSVC OR FLAT_INSTALL_WPILIB)
|
||||
set (cscore_config_dir ${wpilib_dest})
|
||||
else()
|
||||
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})
|
||||
install(EXPORT cscore DESTINATION ${cscore_config_dir})
|
||||
|
||||
SUBDIR_LIST(cscore_examples "${CMAKE_CURRENT_SOURCE_DIR}/examples")
|
||||
foreach(example ${cscore_examples})
|
||||
file(GLOB cscore_example_src examples/${example}/*.cpp)
|
||||
if(cscore_example_src)
|
||||
add_executable(cscore_${example} ${cscore_example_src})
|
||||
wpilib_target_warnings(cscore_${example})
|
||||
target_link_libraries(cscore_${example} cscore)
|
||||
set_property(TARGET cscore_${example} PROPERTY FOLDER "examples")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Java bindings
|
||||
if (NOT WITHOUT_JAVA)
|
||||
find_package(Java REQUIRED)
|
||||
find_package(JNI REQUIRED)
|
||||
include(UseJava)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS "-Xlint:unchecked")
|
||||
|
||||
#find java files, copy them locally
|
||||
|
||||
set(OPENCV_JAVA_INSTALL_DIR ${OpenCV_INSTALL_PATH}/share/OpenCV/java/)
|
||||
|
||||
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_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)
|
||||
|
||||
file(GLOB
|
||||
cscore_jni_src src/main/native/cpp/jni/CameraServerJNI.cpp)
|
||||
|
||||
file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
|
||||
set(CMAKE_JNI_TARGET true)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS "-h" "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
|
||||
add_jar(cscore_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar ${OPENCV_JAR_FILE} OUTPUT_NAME cscore)
|
||||
else()
|
||||
add_jar(cscore_jar ${JAVA_SOURCES} INCLUDE_JARS wpiutil_jar ${OPENCV_JAR_FILE} OUTPUT_NAME cscore GENERATE_NATIVE_HEADERS cscore_jni_headers)
|
||||
endif()
|
||||
|
||||
get_property(CSCORE_JAR_FILE TARGET cscore_jar PROPERTY JAR_FILE)
|
||||
install(FILES ${CSCORE_JAR_FILE} DESTINATION "${java_lib_dest}")
|
||||
install(FILES ${OPENCV_JAR_FILE} DESTINATION "${java_lib_dest}")
|
||||
|
||||
if (MSVC)
|
||||
install(FILES ${OPENCV_JNI_FILE} DESTINATION "${jni_lib_dest}")
|
||||
|
||||
foreach(cvFile ${OpenCV_LIBS})
|
||||
find_file(${cvFile}Loc NAMES ${cvFile}${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)
|
||||
install(FILES ${${cvFile}Loc} DESTINATION "${jni_lib_dest}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set_property(TARGET cscore_jar PROPERTY FOLDER "java")
|
||||
|
||||
add_library(cscorejni ${cscore_jni_src})
|
||||
wpilib_target_warnings(cscorejni)
|
||||
target_link_libraries(cscorejni PUBLIC cscore wpiutil ${OpenCV_LIBS})
|
||||
|
||||
set_property(TARGET cscorejni PROPERTY FOLDER "libraries")
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.11.0")
|
||||
target_include_directories(cscorejni PRIVATE ${JNI_INCLUDE_DIRS})
|
||||
target_include_directories(cscorejni PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/jniheaders")
|
||||
else()
|
||||
target_link_libraries(cscorejni PRIVATE cscore_jni_headers)
|
||||
endif()
|
||||
add_dependencies(cscorejni cscore_jar)
|
||||
|
||||
if (MSVC)
|
||||
install(TARGETS cscorejni RUNTIME DESTINATION "${jni_lib_dest}" COMPONENT Runtime)
|
||||
endif()
|
||||
|
||||
install(TARGETS cscorejni EXPORT cscorejni DESTINATION "${main_lib_dest}")
|
||||
|
||||
endif()
|
||||
|
||||
if (WITH_TESTS)
|
||||
wpilib_add_test(cscore src/test/native/cpp)
|
||||
target_link_libraries(cscore_test cscore gmock)
|
||||
endif()
|
||||
136
cscore/build.gradle
Normal file
136
cscore/build.gradle
Normal file
@@ -0,0 +1,136 @@
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
ext {
|
||||
nativeName = 'cscore'
|
||||
devMain = 'edu.wpi.cscore.DevMain'
|
||||
}
|
||||
|
||||
// Removed because having the objective-cpp plugin added breaks
|
||||
// embedded tools and its toolchain check. It causes an obj-cpp
|
||||
// source set to be added to all binaries, even cross binaries
|
||||
// with no support.
|
||||
// if (OperatingSystem.current().isMacOsX()) {
|
||||
// apply plugin: 'objective-cpp'
|
||||
// }
|
||||
|
||||
apply from: "${rootDir}/shared/jni/setupBuild.gradle"
|
||||
|
||||
ext {
|
||||
sharedCvConfigs = [cscore : [],
|
||||
cscoreBase: [],
|
||||
cscoreDev : [],
|
||||
cscoreTest: []]
|
||||
staticCvConfigs = [cscoreJNI: []]
|
||||
useJava = true
|
||||
useCpp = true
|
||||
splitSetup = {
|
||||
if (it.targetPlatform.operatingSystem.isMacOsX()) {
|
||||
it.sources {
|
||||
// macObjCpp(ObjectiveCppSourceSet) {
|
||||
// source {
|
||||
// srcDirs = ['src/main/native/objcpp']
|
||||
// include '**/*.mm'
|
||||
// }
|
||||
// exportedHeaders {
|
||||
// srcDirs 'src/main/native/include'
|
||||
// include '**/*.h'
|
||||
// }
|
||||
// }
|
||||
cscoreMacCpp(CppSourceSet) {
|
||||
source {
|
||||
srcDirs 'src/main/native/osx'
|
||||
include '**/*.cpp'
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include', 'src/main/native/cpp'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (it.targetPlatform.operatingSystem.isLinux()) {
|
||||
it.sources {
|
||||
cscoreLinuxCpp(CppSourceSet) {
|
||||
source {
|
||||
srcDirs 'src/main/native/linux'
|
||||
include '**/*.cpp'
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include', 'src/main/native/cpp'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (it.targetPlatform.operatingSystem.isWindows()) {
|
||||
it.sources {
|
||||
cscoreWindowsCpp(CppSourceSet) {
|
||||
source {
|
||||
srcDirs 'src/main/native/windows'
|
||||
include '**/*.cpp'
|
||||
}
|
||||
exportedHeaders {
|
||||
srcDirs 'src/main/native/include', 'src/main/native/cpp'
|
||||
include '**/*.h'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def examplesMap = [:];
|
||||
|
||||
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, [])
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/shared/opencv.gradle"
|
||||
|
||||
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']
|
||||
}
|
||||
cscoreJNI {
|
||||
x86SymbolFilter = { symbols ->
|
||||
symbols.removeIf({ !it.startsWith('CS_') })
|
||||
}
|
||||
x64SymbolFilter = { symbols ->
|
||||
symbols.removeIf({ !it.startsWith('CS_') })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model {
|
||||
components {
|
||||
examplesMap.each { key, value ->
|
||||
"${key}"(NativeExecutableSpec) {
|
||||
targetBuildTypes 'debug'
|
||||
binaries.all {
|
||||
lib project: ':wpiutil', library: 'wpiutil', linkage: 'shared'
|
||||
lib library: 'cscore', linkage: 'shared'
|
||||
}
|
||||
sources {
|
||||
cpp {
|
||||
source {
|
||||
srcDirs 'examples/' + "${key}"
|
||||
include '**/*.cpp'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
cscore/cscore-config.cmake.in
Normal file
6
cscore/cscore-config.cmake.in
Normal file
@@ -0,0 +1,6 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
@FILENAME_DEP_REPLACE@
|
||||
@WPIUTIL_DEP_REPLACE@
|
||||
find_dependency(OpenCV)
|
||||
|
||||
include(${SELF_DIR}/cscore.cmake)
|
||||
84
cscore/examples/enum_usb/enum_usb.cpp
Normal file
84
cscore/examples/enum_usb/enum_usb.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/raw_ostream.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";
|
||||
if (!caminfo.otherPaths.empty()) {
|
||||
wpi::outs() << "Other device paths:\n";
|
||||
for (auto&& path : caminfo.otherPaths)
|
||||
wpi::outs() << " " << path << '\n';
|
||||
}
|
||||
|
||||
cs::UsbCamera camera{"usbcam", caminfo.dev};
|
||||
|
||||
wpi::outs() << "Properties:\n";
|
||||
for (const auto& prop : camera.EnumerateProperties()) {
|
||||
wpi::outs() << " " << prop.GetName();
|
||||
switch (prop.GetKind()) {
|
||||
case cs::VideoProperty::kBoolean:
|
||||
wpi::outs() << " (bool): "
|
||||
<< "value=" << prop.Get()
|
||||
<< " default=" << 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();
|
||||
break;
|
||||
case cs::VideoProperty::kString:
|
||||
wpi::outs() << " (string): " << prop.GetString(buf);
|
||||
break;
|
||||
case cs::VideoProperty::kEnum: {
|
||||
wpi::outs() << " (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];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
wpi::outs() << '\n';
|
||||
}
|
||||
|
||||
wpi::outs() << "Video Modes:\n";
|
||||
for (const auto& mode : camera.EnumerateVideoModes()) {
|
||||
wpi::outs() << " PixelFormat:";
|
||||
switch (mode.pixelFormat) {
|
||||
case cs::VideoMode::kMJPEG:
|
||||
wpi::outs() << "MJPEG";
|
||||
break;
|
||||
case cs::VideoMode::kYUYV:
|
||||
wpi::outs() << "YUYV";
|
||||
break;
|
||||
case cs::VideoMode::kRGB565:
|
||||
wpi::outs() << "RGB565";
|
||||
break;
|
||||
default:
|
||||
wpi::outs() << "Unknown";
|
||||
break;
|
||||
}
|
||||
wpi::outs() << " Width:" << mode.width;
|
||||
wpi::outs() << " Height:" << mode.height;
|
||||
wpi::outs() << " FPS:" << mode.fps << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
38
cscore/examples/httpcvstream/httpcvstream.cpp
Normal file
38
cscore/examples/httpcvstream/httpcvstream.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
|
||||
int main() {
|
||||
cs::HttpCamera camera{"httpcam", "http://localhost:8081/?action=stream"};
|
||||
camera.SetVideoMode(cs::VideoMode::kMJPEG, 320, 240, 30);
|
||||
cs::CvSink cvsink{"cvsink"};
|
||||
cvsink.SetSource(camera);
|
||||
cs::CvSource cvsource{"cvsource", cs::VideoMode::kMJPEG, 320, 240, 30};
|
||||
cs::MjpegServer cvMjpegServer{"cvhttpserver", 8083};
|
||||
cvMjpegServer.SetSource(cvsource);
|
||||
|
||||
cv::Mat test;
|
||||
cv::Mat flip;
|
||||
for (;;) {
|
||||
uint64_t time = cvsink.GrabFrame(test);
|
||||
if (time == 0) {
|
||||
std::cout << "error: " << cvsink.GetError() << std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << "got frame at time " << time << " size " << test.size()
|
||||
<< std::endl;
|
||||
cv::flip(test, flip, 0);
|
||||
cvsource.PutFrame(flip);
|
||||
}
|
||||
}
|
||||
104
cscore/examples/settings/settings.cpp
Normal file
104
cscore/examples/settings/settings.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/raw_ostream.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";
|
||||
return 1;
|
||||
}
|
||||
|
||||
int id;
|
||||
if (wpi::StringRef{argv[1]}.getAsInteger(10, id)) {
|
||||
wpi::errs() << "Expected number for camera\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
cs::UsbCamera camera{"usbcam", id};
|
||||
|
||||
// Set prior to connect
|
||||
int arg = 2;
|
||||
wpi::StringRef propName;
|
||||
for (; arg < argc && wpi::StringRef{argv[arg]} != "--"; ++arg) {
|
||||
if (propName.empty()) {
|
||||
propName = argv[arg];
|
||||
} else {
|
||||
wpi::StringRef propVal{argv[arg]};
|
||||
int intVal;
|
||||
if (propVal.getAsInteger(10, intVal))
|
||||
camera.GetProperty(propName).SetString(propVal);
|
||||
else
|
||||
camera.GetProperty(propName).Set(intVal);
|
||||
propName = wpi::StringRef{};
|
||||
}
|
||||
}
|
||||
if (arg < argc && wpi::StringRef{argv[arg]} == "--") ++arg;
|
||||
|
||||
// Wait to connect
|
||||
while (!camera.IsConnected())
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
// Set rest
|
||||
propName = wpi::StringRef{};
|
||||
for (; arg < argc; ++arg) {
|
||||
if (propName.empty()) {
|
||||
propName = argv[arg];
|
||||
} else {
|
||||
wpi::StringRef propVal{argv[arg]};
|
||||
int intVal;
|
||||
if (propVal.getAsInteger(10, intVal))
|
||||
camera.GetProperty(propName).SetString(propVal);
|
||||
else
|
||||
camera.GetProperty(propName).Set(intVal);
|
||||
propName = wpi::StringRef{};
|
||||
}
|
||||
}
|
||||
|
||||
// Print settings
|
||||
wpi::SmallString<64> buf;
|
||||
wpi::outs() << "Properties:\n";
|
||||
for (const auto& prop : camera.EnumerateProperties()) {
|
||||
wpi::outs() << " " << prop.GetName();
|
||||
switch (prop.GetKind()) {
|
||||
case cs::VideoProperty::kBoolean:
|
||||
wpi::outs() << " (bool): "
|
||||
<< "value=" << prop.Get()
|
||||
<< " default=" << 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();
|
||||
break;
|
||||
case cs::VideoProperty::kString:
|
||||
wpi::outs() << " (string): " << prop.GetString(buf);
|
||||
break;
|
||||
case cs::VideoProperty::kEnum: {
|
||||
wpi::outs() << " (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];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
wpi::outs() << '\n';
|
||||
}
|
||||
}
|
||||
40
cscore/examples/usbcvstream/usbcvstream.cpp
Normal file
40
cscore/examples/usbcvstream/usbcvstream.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
|
||||
int main() {
|
||||
cs::UsbCamera camera{"usbcam", 0};
|
||||
camera.SetVideoMode(cs::VideoMode::kMJPEG, 320, 240, 30);
|
||||
cs::MjpegServer mjpegServer{"httpserver", 8081};
|
||||
mjpegServer.SetSource(camera);
|
||||
cs::CvSink cvsink{"cvsink"};
|
||||
cvsink.SetSource(camera);
|
||||
cs::CvSource cvsource{"cvsource", cs::VideoMode::kMJPEG, 320, 240, 30};
|
||||
cs::MjpegServer cvMjpegServer{"cvhttpserver", 8082};
|
||||
cvMjpegServer.SetSource(cvsource);
|
||||
|
||||
cv::Mat test;
|
||||
cv::Mat flip;
|
||||
for (;;) {
|
||||
uint64_t time = cvsink.GrabFrame(test);
|
||||
if (time == 0) {
|
||||
std::cout << "error: " << cvsink.GetError() << std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << "got frame at time " << time << " size " << test.size()
|
||||
<< std::endl;
|
||||
cv::flip(test, flip, 0);
|
||||
cvsource.PutFrame(flip);
|
||||
}
|
||||
}
|
||||
35
cscore/examples/usbstream/usbstream.cpp
Normal file
35
cscore/examples/usbstream/usbstream.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <wpi/raw_ostream.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';
|
||||
cs::UsbCamera camera{"usbcam", 0};
|
||||
camera.SetVideoMode(cs::VideoMode::kMJPEG, 320, 240, 30);
|
||||
cs::MjpegServer mjpegServer{"httpserver", 8081};
|
||||
mjpegServer.SetSource(camera);
|
||||
|
||||
CS_Status status = 0;
|
||||
cs::AddListener(
|
||||
[&](const cs::RawEvent& event) {
|
||||
wpi::outs() << "FPS=" << camera.GetActualFPS()
|
||||
<< " MBPS=" << (camera.GetActualDataRate() / 1000000.0)
|
||||
<< '\n';
|
||||
},
|
||||
cs::RawEvent::kTelemetryUpdated, false, &status);
|
||||
cs::SetTelemetryPeriod(1.0);
|
||||
|
||||
std::getchar();
|
||||
}
|
||||
96
cscore/java-examples/RawCVMatSink.java
Normal file
96
cscore/java-examples/RawCVMatSink.java
Normal file
@@ -0,0 +1,96 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
public class RawCVMatSink extends ImageSink {
|
||||
RawFrame frame = new RawFrame();
|
||||
Mat tmpMat;
|
||||
ByteBuffer origByteBuffer;
|
||||
int width;
|
||||
int height;
|
||||
int pixelFormat;
|
||||
int bgrValue = PixelFormat.kBGR.getValue();
|
||||
|
||||
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;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 RawCVMatSink(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 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) {
|
||||
frame.setWidth(0);
|
||||
frame.setHeight(0);
|
||||
frame.setPixelFormat(bgrValue);
|
||||
long rv = CameraServerJNI.grabSinkFrameTimeout(m_handle, frame, timeout);
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
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.copyTo(image);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
59
cscore/java-examples/RawCVMatSource.java
Normal file
59
cscore/java-examples/RawCVMatSource.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import edu.wpi.cscore.VideoMode.PixelFormat;
|
||||
|
||||
public class RawCVMatSource extends ImageSource {
|
||||
/**
|
||||
* Create an OpenCV source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*
|
||||
* @param image OpenCV image
|
||||
*/
|
||||
public void putFrame(Mat image) {
|
||||
int channels = image.channels();
|
||||
if (channels != 1 && channels != 3) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
24
cscore/src/dev/java/edu/wpi/cscore/DevMain.java
Normal file
24
cscore/src/dev/java/edu/wpi/cscore/DevMain.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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() {
|
||||
}
|
||||
}
|
||||
12
cscore/src/dev/native/cpp/main.cpp
Normal file
12
cscore/src/dev/native/cpp/main.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "cscore.h"
|
||||
|
||||
int main() { std::cout << cs::GetHostname() << std::endl; }
|
||||
45
cscore/src/main/java/edu/wpi/cscore/AxisCamera.java
Normal file
45
cscore/src/main/java/edu/wpi/cscore/AxisCamera.java
Normal file
@@ -0,0 +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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.cscore;
|
||||
|
||||
/**
|
||||
* 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";
|
||||
}
|
||||
|
||||
private static String[] hostToUrl(String[] hosts) {
|
||||
String[] urls = new String[hosts.length];
|
||||
for (int i = 0; i < hosts.length; i++) {
|
||||
urls[i] = hostToUrl(hosts[i]);
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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")
|
||||
*/
|
||||
public AxisCamera(String name, String host) {
|
||||
super(name, hostToUrl(host), HttpCameraKind.kAxis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for an Axis IP camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
*/
|
||||
public AxisCamera(String name, String[] hosts) {
|
||||
super(name, hostToUrl(hosts), HttpCameraKind.kAxis);
|
||||
}
|
||||
}
|
||||
72
cscore/src/main/java/edu/wpi/cscore/CameraServerCvJNI.java
Normal file
72
cscore/src/main/java/edu/wpi/cscore/CameraServerCvJNI.java
Normal file
@@ -0,0 +1,72 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
static RuntimeLoader<Core> loader = null;
|
||||
|
||||
public static class Helper {
|
||||
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||
|
||||
public static boolean getExtractOnStaticLoad() {
|
||||
return extractOnStaticLoad.get();
|
||||
}
|
||||
|
||||
public static void setExtractOnStaticLoad(boolean load) {
|
||||
extractOnStaticLoad.set(load);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
String opencvName = Core.NATIVE_LIBRARY_NAME;
|
||||
if (Helper.getExtractOnStaticLoad()) {
|
||||
try {
|
||||
CameraServerJNI.forceLoad();
|
||||
loader = new RuntimeLoader<>(opencvName, RuntimeLoader.getDefaultExtractionRoot(), Core.class);
|
||||
loader.loadLibraryHashed();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
libraryLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force load the library.
|
||||
*/
|
||||
public static synchronized void forceLoad() throws IOException {
|
||||
if (libraryLoaded) {
|
||||
return;
|
||||
}
|
||||
CameraServerJNI.forceLoad();
|
||||
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 void putSourceFrame(int source, long imageNativeObj);
|
||||
|
||||
public static native int createCvSink(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);
|
||||
}
|
||||
255
cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
Normal file
255
cscore/src/main/java/edu/wpi/cscore/CameraServerJNI.java
Normal file
@@ -0,0 +1,255 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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.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;
|
||||
|
||||
static RuntimeLoader<CameraServerJNI> loader = null;
|
||||
|
||||
public static class Helper {
|
||||
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||
|
||||
public static boolean getExtractOnStaticLoad() {
|
||||
return extractOnStaticLoad.get();
|
||||
}
|
||||
|
||||
public static void setExtractOnStaticLoad(boolean load) {
|
||||
extractOnStaticLoad.set(load);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
if (Helper.getExtractOnStaticLoad()) {
|
||||
try {
|
||||
loader = new RuntimeLoader<>("cscorejni", RuntimeLoader.getDefaultExtractionRoot(), CameraServerJNI.class);
|
||||
loader.loadLibrary();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
libraryLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force load the library.
|
||||
*/
|
||||
public static synchronized void forceLoad() throws IOException {
|
||||
if (libraryLoaded) {
|
||||
return;
|
||||
}
|
||||
loader = new RuntimeLoader<>("cscorejni", RuntimeLoader.getDefaultExtractionRoot(), CameraServerJNI.class);
|
||||
loader.loadLibrary();
|
||||
libraryLoaded = true;
|
||||
}
|
||||
|
||||
//
|
||||
// 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);
|
||||
|
||||
//
|
||||
// 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 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 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 void putRawSourceFrame(int source, RawFrame raw) {
|
||||
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);
|
||||
|
||||
//
|
||||
// Sink Creation Functions
|
||||
//
|
||||
public static native int createMjpegServer(String name, String listenAddress, int port);
|
||||
|
||||
public static native int createRawSink(String name);
|
||||
|
||||
//
|
||||
// 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);
|
||||
|
||||
//
|
||||
// Image Sink Functions
|
||||
//
|
||||
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);
|
||||
|
||||
public static long grabSinkFrame(int sink, RawFrame rawFrame) {
|
||||
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);
|
||||
}
|
||||
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 void removeListener(int handle);
|
||||
|
||||
//
|
||||
// Telemetry Functions
|
||||
//
|
||||
public enum TelemetryKind {
|
||||
kSourceBytesReceived(1),
|
||||
kSourceFramesReceived(2);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
private final int value;
|
||||
|
||||
TelemetryKind(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
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());
|
||||
}
|
||||
|
||||
//
|
||||
// Logging Functions
|
||||
//
|
||||
@FunctionalInterface
|
||||
public interface LoggerFunction {
|
||||
void apply(int level, String file, int line, String msg);
|
||||
}
|
||||
public static native void setLogger(LoggerFunction func, int minLevel);
|
||||
|
||||
//
|
||||
// Utility Functions
|
||||
//
|
||||
public static native UsbCameraInfo[] enumerateUsbCameras();
|
||||
|
||||
public static native int[] enumerateSources();
|
||||
|
||||
public static native int[] enumerateSinks();
|
||||
|
||||
public static native String getHostname();
|
||||
|
||||
public static native String[] getNetworkInterfaces();
|
||||
|
||||
public static native long allocateRawFrame();
|
||||
|
||||
public static native void freeRawFrame(long frame);
|
||||
}
|
||||
77
cscore/src/main/java/edu/wpi/cscore/CvSink.java
Normal file
77
cscore/src/main/java/edu/wpi/cscore/CvSink.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
59
cscore/src/main/java/edu/wpi/cscore/CvSource.java
Normal file
59
cscore/src/main/java/edu/wpi/cscore/CvSource.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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);
|
||||
}
|
||||
|
||||
}
|
||||
109
cscore/src/main/java/edu/wpi/cscore/HttpCamera.java
Normal file
109
cscore/src/main/java/edu/wpi/cscore/HttpCamera.java
Normal file
@@ -0,0 +1,109 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
private final int value;
|
||||
|
||||
HttpCameraKind(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 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for a MJPEG-over-HTTP (IP) camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg")
|
||||
*/
|
||||
public HttpCamera(String name, String url) {
|
||||
super(CameraServerJNI.createHttpCamera(name, url, HttpCameraKind.kUnknown.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for a MJPEG-over-HTTP (IP) camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param url Camera URL (e.g. "http://10.x.y.11/video/stream.mjpg")
|
||||
* @param kind Camera kind (e.g. kAxis)
|
||||
*/
|
||||
public HttpCamera(String name, String url, HttpCameraKind kind) {
|
||||
super(CameraServerJNI.createHttpCamera(name, url, kind.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for a MJPEG-over-HTTP (IP) camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param urls Array of Camera URLs
|
||||
*/
|
||||
public HttpCamera(String name, String[] urls) {
|
||||
super(CameraServerJNI.createHttpCameraMulti(name, urls, HttpCameraKind.kUnknown.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for a MJPEG-over-HTTP (IP) camera.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param urls Array of Camera URLs
|
||||
* @param kind Camera kind (e.g. kAxis)
|
||||
*/
|
||||
public HttpCamera(String name, String[] urls, HttpCameraKind kind) {
|
||||
super(CameraServerJNI.createHttpCameraMulti(name, urls, kind.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the kind of HTTP camera.
|
||||
*
|
||||
* <p>Autodetection can result in returning a different value than the camera
|
||||
* was created with.
|
||||
*/
|
||||
public HttpCameraKind getHttpCameraKind() {
|
||||
return getHttpCameraKindFromInt(CameraServerJNI.getHttpCameraKind(m_handle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the URLs used to connect to the camera.
|
||||
*/
|
||||
public void setUrls(String[] urls) {
|
||||
CameraServerJNI.setHttpCameraUrls(m_handle, urls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URLs used to connect to the camera.
|
||||
*/
|
||||
public String[] getUrls() {
|
||||
return CameraServerJNI.getHttpCameraUrls(m_handle);
|
||||
}
|
||||
}
|
||||
41
cscore/src/main/java/edu/wpi/cscore/ImageSink.java
Normal file
41
cscore/src/main/java/edu/wpi/cscore/ImageSink.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
162
cscore/src/main/java/edu/wpi/cscore/ImageSource.java
Normal file
162
cscore/src/main/java/edu/wpi/cscore/ImageSource.java
Normal file
@@ -0,0 +1,162 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
104
cscore/src/main/java/edu/wpi/cscore/MjpegServer.java
Normal file
104
cscore/src/main/java/edu/wpi/cscore/MjpegServer.java
Normal file
@@ -0,0 +1,104 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
/**
|
||||
* A sink that acts as a MJPEG-over-HTTP network server.
|
||||
*/
|
||||
public class MjpegServer extends VideoSink {
|
||||
/**
|
||||
* Create a MJPEG-over-HTTP server sink.
|
||||
*
|
||||
* @param name Sink name (arbitrary unique identifier)
|
||||
* @param listenAddress TCP listen address (empty string for all addresses)
|
||||
* @param port TCP port number
|
||||
*/
|
||||
public MjpegServer(String name, String listenAddress, int port) {
|
||||
super(CameraServerJNI.createMjpegServer(name, listenAddress, port));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MJPEG-over-HTTP server sink.
|
||||
*
|
||||
* @param name Sink name (arbitrary unique identifier)
|
||||
* @param port TCP port number
|
||||
*/
|
||||
public MjpegServer(String name, int port) {
|
||||
this(name, "", port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the listen address of the server.
|
||||
*/
|
||||
public String getListenAddress() {
|
||||
return CameraServerJNI.getMjpegServerListenAddress(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port number of the server.
|
||||
*/
|
||||
public int getPort() {
|
||||
return CameraServerJNI.getMjpegServerPort(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>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
|
||||
*/
|
||||
public void setResolution(int width, int height) {
|
||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "width"), width);
|
||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "height"), height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stream frames per second (FPS) for clients that don't specify it.
|
||||
*
|
||||
* <p>It is not necessary to set this if it is the same as the source FPS.
|
||||
*
|
||||
* @param fps FPS, 0 for unspecified
|
||||
*/
|
||||
public void setFPS(int fps) {
|
||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "fps"), fps);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param quality JPEG compression quality (0-100), -1 for unspecified
|
||||
*/
|
||||
public void setCompression(int 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.
|
||||
*
|
||||
* @param quality JPEG compression quality (0-100)
|
||||
*/
|
||||
public void setDefaultCompression(int quality) {
|
||||
CameraServerJNI.setProperty(CameraServerJNI.getSinkProperty(m_handle, "default_compression"),
|
||||
quality);
|
||||
}
|
||||
}
|
||||
66
cscore/src/main/java/edu/wpi/cscore/UsbCamera.java
Normal file
66
cscore/src/main/java/edu/wpi/cscore/UsbCamera.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
/**
|
||||
* A source that represents a USB camera.
|
||||
*/
|
||||
public class UsbCamera extends VideoCamera {
|
||||
/**
|
||||
* Create a source for a USB camera based on device number.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param dev Device number (e.g. 0 for /dev/video0)
|
||||
*/
|
||||
public UsbCamera(String name, int dev) {
|
||||
super(CameraServerJNI.createUsbCameraDev(name, dev));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a source for a USB camera based on device path.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param path Path to device (e.g. "/dev/video0" on Linux)
|
||||
*/
|
||||
public UsbCamera(String name, String path) {
|
||||
super(CameraServerJNI.createUsbCameraPath(name, path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate USB cameras on the local system.
|
||||
*
|
||||
* @return Vector of USB camera information (one for each camera)
|
||||
*/
|
||||
public static UsbCameraInfo[] enumerateUsbCameras() {
|
||||
return CameraServerJNI.enumerateUsbCameras();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the device.
|
||||
*/
|
||||
public String getPath() {
|
||||
return CameraServerJNI.getUsbCameraPath(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full camera information for the device.
|
||||
*/
|
||||
public UsbCameraInfo getInfo() {
|
||||
return CameraServerJNI.getUsbCameraInfo(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set how verbose the camera connection messages are.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
70
cscore/src/main/java/edu/wpi/cscore/UsbCameraInfo.java
Normal file
70
cscore/src/main/java/edu/wpi/cscore/UsbCameraInfo.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
}
|
||||
81
cscore/src/main/java/edu/wpi/cscore/VideoCamera.java
Normal file
81
cscore/src/main/java/edu/wpi/cscore/VideoCamera.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
/**
|
||||
* A source that represents a video camera.
|
||||
*/
|
||||
public class VideoCamera extends VideoSource {
|
||||
public static class WhiteBalance {
|
||||
public static final int kFixedIndoor = 3000;
|
||||
public static final int kFixedOutdoor1 = 4000;
|
||||
public static final int kFixedOutdoor2 = 5000;
|
||||
public static final int kFixedFluorescent1 = 5100;
|
||||
public static final int kFixedFlourescent2 = 5200;
|
||||
}
|
||||
|
||||
protected VideoCamera(int handle) {
|
||||
super(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the brightness, as a percentage (0-100).
|
||||
*/
|
||||
public synchronized void setBrightness(int brightness) {
|
||||
CameraServerJNI.setCameraBrightness(m_handle, brightness);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the brightness, as a percentage (0-100).
|
||||
*/
|
||||
public synchronized int getBrightness() {
|
||||
return CameraServerJNI.getCameraBrightness(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the white balance to auto.
|
||||
*/
|
||||
public synchronized void setWhiteBalanceAuto() {
|
||||
CameraServerJNI.setCameraWhiteBalanceAuto(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public synchronized void setWhiteBalanceManual(int value) {
|
||||
CameraServerJNI.setCameraWhiteBalanceManual(m_handle, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exposure to auto aperture.
|
||||
*/
|
||||
public synchronized void setExposureAuto() {
|
||||
CameraServerJNI.setCameraExposureAuto(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exposure to hold current.
|
||||
*/
|
||||
public synchronized void setExposureHoldCurrent() {
|
||||
CameraServerJNI.setCameraExposureHoldCurrent(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exposure to manual, as a percentage (0-100).
|
||||
*/
|
||||
public synchronized void setExposureManual(int value) {
|
||||
CameraServerJNI.setCameraExposureManual(m_handle, value);
|
||||
}
|
||||
}
|
||||
133
cscore/src/main/java/edu/wpi/cscore/VideoEvent.java
Normal file
133
cscore/src/main/java/edu/wpi/cscore/VideoEvent.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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);
|
||||
}
|
||||
|
||||
}
|
||||
24
cscore/src/main/java/edu/wpi/cscore/VideoException.java
Normal file
24
cscore/src/main/java/edu/wpi/cscore/VideoException.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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() + "]";
|
||||
}
|
||||
}
|
||||
43
cscore/src/main/java/edu/wpi/cscore/VideoListener.java
Normal file
43
cscore/src/main/java/edu/wpi/cscore/VideoListener.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
}
|
||||
78
cscore/src/main/java/edu/wpi/cscore/VideoMode.java
Normal file
78
cscore/src/main/java/edu/wpi/cscore/VideoMode.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
}
|
||||
124
cscore/src/main/java/edu/wpi/cscore/VideoProperty.java
Normal file
124
cscore/src/main/java/edu/wpi/cscore/VideoProperty.java
Normal file
@@ -0,0 +1,124 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
/**
|
||||
* A source or sink property.
|
||||
*/
|
||||
public class VideoProperty {
|
||||
public enum Kind {
|
||||
kNone(0), kBoolean(1), kInteger(2), kString(4), kEnum(8);
|
||||
|
||||
@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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return CameraServerJNI.getPropertyName(m_handle);
|
||||
}
|
||||
|
||||
public Kind getKind() {
|
||||
return m_kind;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return m_kind != Kind.kNone;
|
||||
}
|
||||
|
||||
// Kind checkers
|
||||
public boolean isBoolean() {
|
||||
return m_kind == Kind.kBoolean;
|
||||
}
|
||||
|
||||
public boolean isInteger() {
|
||||
return m_kind == Kind.kInteger;
|
||||
}
|
||||
|
||||
public boolean isString() {
|
||||
return m_kind == Kind.kString;
|
||||
}
|
||||
|
||||
public boolean isEnum() {
|
||||
return m_kind == Kind.kEnum;
|
||||
}
|
||||
|
||||
public int get() {
|
||||
return CameraServerJNI.getProperty(m_handle);
|
||||
}
|
||||
|
||||
public void set(int value) {
|
||||
CameraServerJNI.setProperty(m_handle, value);
|
||||
}
|
||||
|
||||
public int getMin() {
|
||||
return CameraServerJNI.getPropertyMin(m_handle);
|
||||
}
|
||||
|
||||
public int getMax() {
|
||||
return CameraServerJNI.getPropertyMax(m_handle);
|
||||
}
|
||||
|
||||
public int getStep() {
|
||||
return CameraServerJNI.getPropertyStep(m_handle);
|
||||
}
|
||||
|
||||
public int getDefault() {
|
||||
return CameraServerJNI.getPropertyDefault(m_handle);
|
||||
}
|
||||
|
||||
// String-specific functions
|
||||
public String getString() {
|
||||
return CameraServerJNI.getStringProperty(m_handle);
|
||||
}
|
||||
|
||||
public void setString(String value) {
|
||||
CameraServerJNI.setStringProperty(m_handle, value);
|
||||
}
|
||||
|
||||
// Enum-specific functions
|
||||
public String[] getChoices() {
|
||||
return CameraServerJNI.getEnumPropertyChoices(m_handle);
|
||||
}
|
||||
|
||||
VideoProperty(int handle) {
|
||||
m_handle = handle;
|
||||
m_kind = getKindFromInt(CameraServerJNI.getPropertyKind(handle));
|
||||
}
|
||||
|
||||
VideoProperty(int handle, Kind kind) {
|
||||
m_handle = handle;
|
||||
m_kind = kind;
|
||||
}
|
||||
|
||||
int m_handle;
|
||||
private Kind m_kind;
|
||||
}
|
||||
217
cscore/src/main/java/edu/wpi/cscore/VideoSink.java
Normal file
217
cscore/src/main/java/edu/wpi/cscore/VideoSink.java
Normal file
@@ -0,0 +1,217 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@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
|
||||
*/
|
||||
public static Kind getKindFromInt(int kind) {
|
||||
switch (kind) {
|
||||
case 2: return Kind.kMjpeg;
|
||||
case 4: return Kind.kCv;
|
||||
default: return Kind.kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
protected VideoSink(int handle) {
|
||||
m_handle = handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
CameraServerJNI.releaseSink(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
VideoSink sink = (VideoSink) other;
|
||||
return m_handle == sink.m_handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 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.
|
||||
*/
|
||||
public String getName() {
|
||||
return CameraServerJNI.getSinkName(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sink description. This is sink-kind specific.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return CameraServerJNI.getSinkDescription(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property of the sink.
|
||||
*
|
||||
* @param name Property name
|
||||
* @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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all properties of this sink.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public VideoProperty[] enumerateProperties() {
|
||||
int[] handles = CameraServerJNI.enumerateSinkProperties(m_handle);
|
||||
VideoProperty[] rv = new VideoProperty[handles.length];
|
||||
for (int i = 0; i < handles.length; i++) {
|
||||
rv[i] = new VideoProperty(handles[i]);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties from a JSON configuration string.
|
||||
*
|
||||
* <p>The format of the JSON input is:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "properties": [
|
||||
* {
|
||||
* "name": property name
|
||||
* "value": property value
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param config configuration
|
||||
* @return True if set successfully
|
||||
*/
|
||||
public boolean setConfigJson(String config) {
|
||||
return CameraServerJNI.setSinkConfigJson(m_handle, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON configuration string.
|
||||
*
|
||||
* @return JSON configuration string
|
||||
*/
|
||||
public String getConfigJson() {
|
||||
return CameraServerJNI.getSinkConfigJson(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void setSource(VideoSource source) {
|
||||
if (source == null) {
|
||||
CameraServerJNI.setSinkSource(m_handle, 0);
|
||||
} else {
|
||||
CameraServerJNI.setSinkSource(m_handle, source.m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connected source.
|
||||
*
|
||||
* @return Connected source; nullptr if no source connected.
|
||||
*/
|
||||
public VideoSource getSource() {
|
||||
// While VideoSource.close() will call releaseSource(), getSinkSource()
|
||||
// increments the internal reference count so this is okay to do.
|
||||
return new VideoSource(CameraServerJNI.getSinkSource(m_handle));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
public VideoProperty getSourceProperty(String name) {
|
||||
return new VideoProperty(
|
||||
CameraServerJNI.getSinkSourceProperty(m_handle, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all existing sinks.
|
||||
*
|
||||
* @return Vector of sinks.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public static VideoSink[] enumerateSinks() {
|
||||
int[] handles = CameraServerJNI.enumerateSinks();
|
||||
VideoSink[] rv = new VideoSink[handles.length];
|
||||
for (int i = 0; i < handles.length; i++) {
|
||||
rv[i] = new VideoSink(handles[i]);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
protected int m_handle;
|
||||
}
|
||||
371
cscore/src/main/java/edu/wpi/cscore/VideoSource.java
Normal file
371
cscore/src/main/java/edu/wpi/cscore/VideoSource.java
Normal file
@@ -0,0 +1,371 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
private final int value;
|
||||
|
||||
Kind(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection strategy.
|
||||
*/
|
||||
public enum ConnectionStrategy {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
kKeepOpen(1),
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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 1: return Kind.kUsb;
|
||||
case 2: return Kind.kHttp;
|
||||
case 4: return Kind.kCv;
|
||||
default: return Kind.kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
protected VideoSource(int handle) {
|
||||
m_handle = handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (m_handle != 0) {
|
||||
CameraServerJNI.releaseSource(m_handle);
|
||||
}
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
public int getHandle() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
VideoSource source = (VideoSource) other;
|
||||
return m_handle == source.m_handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 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.
|
||||
*/
|
||||
public String getName() {
|
||||
return CameraServerJNI.getSourceName(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source description. This is source-kind specific.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return CameraServerJNI.getSourceDescription(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last time a frame was captured.
|
||||
* @return Time in 1 us increments.
|
||||
*/
|
||||
public long getLastFrameTime() {
|
||||
return CameraServerJNI.getSourceLastFrameTime(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param strategy connection strategy (auto, keep open, or force close)
|
||||
*/
|
||||
public void setConnectionStrategy(ConnectionStrategy strategy) {
|
||||
CameraServerJNI.setSourceConnectionStrategy(m_handle, strategy.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 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.
|
||||
*
|
||||
* @return True if enabled, false otherwise.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return CameraServerJNI.isSourceEnabled(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property.
|
||||
*
|
||||
* @param name Property name
|
||||
* @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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all properties of this source.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public VideoProperty[] enumerateProperties() {
|
||||
int[] handles = CameraServerJNI.enumerateSourceProperties(m_handle);
|
||||
VideoProperty[] rv = new VideoProperty[handles.length];
|
||||
for (int i = 0; i < handles.length; i++) {
|
||||
rv[i] = new VideoProperty(handles[i]);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current video mode.
|
||||
*/
|
||||
public VideoMode getVideoMode() {
|
||||
return CameraServerJNI.getSourceVideoMode(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the video mode.
|
||||
* @param mode Video mode
|
||||
*/
|
||||
public boolean setVideoMode(VideoMode mode) {
|
||||
return CameraServerJNI.setSourceVideoMode(m_handle,
|
||||
mode.pixelFormat.getValue(),
|
||||
mode.width,
|
||||
mode.height,
|
||||
mode.fps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the video mode.
|
||||
*
|
||||
* @param pixelFormat desired pixel format
|
||||
* @param width desired width
|
||||
* @param height desired height
|
||||
* @param fps desired FPS
|
||||
* @return True if set successfully
|
||||
*/
|
||||
public boolean setVideoMode(VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
|
||||
return CameraServerJNI.setSourceVideoMode(m_handle, pixelFormat.getValue(), width, height, fps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pixel format.
|
||||
*
|
||||
* @param pixelFormat desired pixel format
|
||||
* @return True if set successfully
|
||||
*/
|
||||
public boolean setPixelFormat(VideoMode.PixelFormat pixelFormat) {
|
||||
return CameraServerJNI.setSourcePixelFormat(m_handle, pixelFormat.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resolution.
|
||||
*
|
||||
* @param width desired width
|
||||
* @param height desired height
|
||||
* @return True if set successfully
|
||||
*/
|
||||
public boolean setResolution(int width, int height) {
|
||||
return CameraServerJNI.setSourceResolution(m_handle, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frames per second (FPS).
|
||||
*
|
||||
* @param fps desired FPS
|
||||
* @return True if set successfully
|
||||
*/
|
||||
public boolean setFPS(int fps) {
|
||||
return CameraServerJNI.setSourceFPS(m_handle, fps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set video mode and properties from a JSON configuration string.
|
||||
*
|
||||
* <p>The format of the JSON input is:
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "pixel format": "MJPEG", "YUYV", etc
|
||||
* "width": video mode width
|
||||
* "height": video mode height
|
||||
* "fps": video mode fps
|
||||
* "brightness": percentage brightness
|
||||
* "white balance": "auto", "hold", or value
|
||||
* "exposure": "auto", "hold", or value
|
||||
* "properties": [
|
||||
* {
|
||||
* "name": property name
|
||||
* "value": property value
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param config configuration
|
||||
* @return True if set successfully
|
||||
*/
|
||||
public boolean setConfigJson(String config) {
|
||||
return CameraServerJNI.setSourceConfigJson(m_handle, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON configuration string.
|
||||
*
|
||||
* @return JSON configuration string
|
||||
*/
|
||||
public String getConfigJson() {
|
||||
return CameraServerJNI.getSourceConfigJson(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual FPS.
|
||||
*
|
||||
* <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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
* @return Data rate averaged over the telemetry period.
|
||||
*/
|
||||
public double getActualDataRate() {
|
||||
return CameraServerJNI.getTelemetryAverageValue(m_handle,
|
||||
CameraServerJNI.TelemetryKind.kSourceBytesReceived);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all known video modes for this source.
|
||||
*/
|
||||
public VideoMode[] enumerateVideoModes() {
|
||||
return CameraServerJNI.enumerateSourceVideoModes(m_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all sinks connected to this source.
|
||||
*
|
||||
* @return Vector of sinks.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public VideoSink[] enumerateSinks() {
|
||||
int[] handles = CameraServerJNI.enumerateSourceSinks(m_handle);
|
||||
VideoSink[] rv = new VideoSink[handles.length];
|
||||
for (int i = 0; i < handles.length; i++) {
|
||||
rv[i] = new VideoSink(handles[i]);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all existing sources.
|
||||
*
|
||||
* @return Vector of sources.
|
||||
*/
|
||||
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
|
||||
public static VideoSource[] enumerateSources() {
|
||||
int[] handles = CameraServerJNI.enumerateSources();
|
||||
VideoSource[] rv = new VideoSource[handles.length];
|
||||
for (int i = 0; i < handles.length; i++) {
|
||||
rv[i] = new VideoSource(handles[i]);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
protected int m_handle;
|
||||
}
|
||||
130
cscore/src/main/java/edu/wpi/cscore/raw/RawFrame.java
Normal file
130
cscore/src/main/java/edu/wpi/cscore/raw/RawFrame.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
68
cscore/src/main/java/edu/wpi/cscore/raw/RawSink.java
Normal file
68
cscore/src/main/java/edu/wpi/cscore/raw/RawSink.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
85
cscore/src/main/java/edu/wpi/cscore/raw/RawSource.java
Normal file
85
cscore/src/main/java/edu/wpi/cscore/raw/RawSource.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
109
cscore/src/main/native/cpp/ConfigurableSourceImpl.cpp
Normal file
109
cscore/src/main/native/cpp/ConfigurableSourceImpl.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "ConfigurableSourceImpl.h"
|
||||
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "Instance.h"
|
||||
#include "Log.h"
|
||||
#include "Notifier.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
ConfigurableSourceImpl::ConfigurableSourceImpl(const wpi::Twine& name,
|
||||
wpi::Logger& logger,
|
||||
Notifier& notifier,
|
||||
Telemetry& telemetry,
|
||||
const VideoMode& mode)
|
||||
: SourceImpl{name, logger, notifier, telemetry} {
|
||||
m_mode = mode;
|
||||
m_videoModes.push_back(m_mode);
|
||||
}
|
||||
|
||||
ConfigurableSourceImpl::~ConfigurableSourceImpl() {}
|
||||
|
||||
void ConfigurableSourceImpl::Start() {
|
||||
m_notifier.NotifySource(*this, CS_SOURCE_CONNECTED);
|
||||
m_notifier.NotifySource(*this, CS_SOURCE_VIDEOMODES_UPDATED);
|
||||
m_notifier.NotifySourceVideoMode(*this, m_mode);
|
||||
}
|
||||
|
||||
bool ConfigurableSourceImpl::SetVideoMode(const VideoMode& mode,
|
||||
CS_Status* status) {
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_mode = mode;
|
||||
m_videoModes[0] = mode;
|
||||
}
|
||||
m_notifier.NotifySourceVideoMode(*this, mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigurableSourceImpl::NumSinksChanged() {
|
||||
// ignore
|
||||
}
|
||||
|
||||
void ConfigurableSourceImpl::NumSinksEnabledChanged() {
|
||||
// ignore
|
||||
}
|
||||
|
||||
void ConfigurableSourceImpl::NotifyError(const wpi::Twine& msg) {
|
||||
PutError(msg, wpi::Now());
|
||||
}
|
||||
|
||||
int ConfigurableSourceImpl::CreateProperty(const wpi::Twine& name,
|
||||
CS_PropertyKind kind, int minimum,
|
||||
int maximum, int step,
|
||||
int defaultValue, int value) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
int ndx = CreateOrUpdateProperty(name,
|
||||
[=] {
|
||||
return std::make_unique<PropertyImpl>(
|
||||
name, kind, minimum, maximum, step,
|
||||
defaultValue, value);
|
||||
},
|
||||
[&](PropertyImpl& prop) {
|
||||
// update all but value
|
||||
prop.propKind = kind;
|
||||
prop.minimum = minimum;
|
||||
prop.maximum = maximum;
|
||||
prop.step = step;
|
||||
prop.defaultValue = defaultValue;
|
||||
value = prop.value;
|
||||
});
|
||||
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name, ndx,
|
||||
kind, value, wpi::Twine{});
|
||||
return ndx;
|
||||
}
|
||||
|
||||
int ConfigurableSourceImpl::CreateProperty(
|
||||
const wpi::Twine& name, CS_PropertyKind kind, int minimum, int maximum,
|
||||
int step, int defaultValue, int value,
|
||||
std::function<void(CS_Property property)> onChange) {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ConfigurableSourceImpl::SetEnumPropertyChoices(
|
||||
int property, wpi::ArrayRef<std::string> choices, CS_Status* status) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
auto prop = GetProperty(property);
|
||||
if (!prop) {
|
||||
*status = CS_INVALID_PROPERTY;
|
||||
return;
|
||||
}
|
||||
if (prop->propKind != CS_PROP_ENUM) {
|
||||
*status = CS_WRONG_PROPERTY_TYPE;
|
||||
return;
|
||||
}
|
||||
prop->enumChoices = choices;
|
||||
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
|
||||
prop->name, property, CS_PROP_ENUM,
|
||||
prop->value, wpi::Twine{});
|
||||
}
|
||||
56
cscore/src/main/native/cpp/ConfigurableSourceImpl.h
Normal file
56
cscore/src/main/native/cpp/ConfigurableSourceImpl.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CSCORE_CONFIGURABLESOURCEIMPL_H_
|
||||
#define CSCORE_CONFIGURABLESOURCEIMPL_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/ArrayRef.h>
|
||||
#include <wpi/Twine.h>
|
||||
|
||||
#include "SourceImpl.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class ConfigurableSourceImpl : public SourceImpl {
|
||||
protected:
|
||||
ConfigurableSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
|
||||
Notifier& notifier, Telemetry& telemetry,
|
||||
const VideoMode& mode);
|
||||
|
||||
public:
|
||||
~ConfigurableSourceImpl() override;
|
||||
|
||||
void Start() override;
|
||||
|
||||
bool SetVideoMode(const VideoMode& mode, CS_Status* status) override;
|
||||
|
||||
void NumSinksChanged() override;
|
||||
void NumSinksEnabledChanged() override;
|
||||
|
||||
// OpenCV-specific functions
|
||||
void NotifyError(const wpi::Twine& msg);
|
||||
int CreateProperty(const wpi::Twine& 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 maximum, int step, int defaultValue, int value,
|
||||
std::function<void(CS_Property property)> onChange);
|
||||
void SetEnumPropertyChoices(int property, wpi::ArrayRef<std::string> choices,
|
||||
CS_Status* status);
|
||||
|
||||
private:
|
||||
std::atomic_bool m_connected{true};
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_CONFIGURABLESOURCEIMPL_H_
|
||||
254
cscore/src/main/native/cpp/CvSinkImpl.cpp
Normal file
254
cscore/src/main/native/cpp/CvSinkImpl.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "CvSinkImpl.h"
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
#include <wpi/SmallString.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "Instance.h"
|
||||
#include "Log.h"
|
||||
#include "Notifier.h"
|
||||
#include "c_util.h"
|
||||
#include "cscore_cpp.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
CvSinkImpl::CvSinkImpl(const wpi::Twine& 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,
|
||||
Notifier& notifier, Telemetry& telemetry,
|
||||
std::function<void(uint64_t time)> processFrame)
|
||||
: SinkImpl{name, logger, notifier, telemetry} {}
|
||||
|
||||
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();
|
||||
|
||||
// join thread
|
||||
if (m_thread.joinable()) m_thread.join();
|
||||
}
|
||||
|
||||
uint64_t CvSinkImpl::GrabFrame(cv::Mat& image) {
|
||||
SetEnabled(true);
|
||||
|
||||
auto source = GetSource();
|
||||
if (!source) {
|
||||
// Source disconnected; sleep for one second
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto frame = source->GetNextFrame(); // blocks
|
||||
if (!frame) {
|
||||
// Bad frame; sleep for 20 ms so we don't consume all processor time.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
return 0; // signal error
|
||||
}
|
||||
|
||||
if (!frame.GetCv(image)) {
|
||||
// Shouldn't happen, but just in case...
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return frame.GetTime();
|
||||
}
|
||||
|
||||
uint64_t CvSinkImpl::GrabFrame(cv::Mat& image, double timeout) {
|
||||
SetEnabled(true);
|
||||
|
||||
auto source = GetSource();
|
||||
if (!source) {
|
||||
// Source disconnected; sleep for one second
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto frame = source->GetNextFrame(timeout); // blocks
|
||||
if (!frame) {
|
||||
// Bad frame; sleep for 20 ms so we don't consume all processor time.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
return 0; // signal error
|
||||
}
|
||||
|
||||
if (!frame.GetCv(image)) {
|
||||
// Shouldn't happen, but just in case...
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return frame.GetTime();
|
||||
}
|
||||
|
||||
// Send HTTP response and a stream of JPG-frames
|
||||
void CvSinkImpl::ThreadMain() {
|
||||
Enable();
|
||||
while (m_active) {
|
||||
auto source = GetSource();
|
||||
if (!source) {
|
||||
// Source disconnected; sleep for one second
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
continue;
|
||||
}
|
||||
SDEBUG4("waiting for frame");
|
||||
Frame frame = source->GetNextFrame(); // blocks
|
||||
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));
|
||||
continue;
|
||||
}
|
||||
// TODO m_processFrame();
|
||||
}
|
||||
Disable();
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
|
||||
CS_Sink CreateCvSink(const wpi::Twine& 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,
|
||||
std::function<void(uint64_t time)> processFrame,
|
||||
CS_Status* status) {
|
||||
auto& inst = Instance::GetInstance();
|
||||
return inst.CreateSink(
|
||||
CS_SINK_CV, std::make_shared<CvSinkImpl>(name, inst.logger, inst.notifier,
|
||||
inst.telemetry, processFrame));
|
||||
}
|
||||
|
||||
static constexpr unsigned SinkMask = CS_SINK_CV | CS_SINK_RAW;
|
||||
|
||||
void SetSinkDescription(CS_Sink sink, const wpi::Twine& description,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || (data->kind & SinkMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<CvSinkImpl&>(*data->sink).SetDescription(description);
|
||||
}
|
||||
|
||||
uint64_t GrabSinkFrame(CS_Sink sink, cv::Mat& image, CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || data->kind != CS_SINK_CV) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return 0;
|
||||
}
|
||||
return static_cast<CvSinkImpl&>(*data->sink).GrabFrame(image);
|
||||
}
|
||||
|
||||
uint64_t GrabSinkFrameTimeout(CS_Sink sink, cv::Mat& image, double timeout,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || data->kind != CS_SINK_CV) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return 0;
|
||||
}
|
||||
return static_cast<CvSinkImpl&>(*data->sink).GrabFrame(image, timeout);
|
||||
}
|
||||
|
||||
std::string GetSinkError(CS_Sink sink, CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || (data->kind & SinkMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return std::string{};
|
||||
}
|
||||
return static_cast<CvSinkImpl&>(*data->sink).GetError();
|
||||
}
|
||||
|
||||
wpi::StringRef 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 static_cast<CvSinkImpl&>(*data->sink).GetError(buf);
|
||||
}
|
||||
|
||||
void SetSinkEnabled(CS_Sink sink, bool enabled, CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || (data->kind & SinkMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<CvSinkImpl&>(*data->sink).SetEnabled(enabled);
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
extern "C" {
|
||||
|
||||
CS_Sink CS_CreateCvSink(const char* name, CS_Status* status) {
|
||||
return cs::CreateCvSink(name, status);
|
||||
}
|
||||
|
||||
CS_Sink CS_CreateCvSinkCallback(const char* name, void* data,
|
||||
void (*processFrame)(void* data, uint64_t time),
|
||||
CS_Status* status) {
|
||||
return cs::CreateCvSinkCallback(
|
||||
name, [=](uint64_t time) { processFrame(data, time); }, status);
|
||||
}
|
||||
|
||||
void CS_SetSinkDescription(CS_Sink sink, const char* description,
|
||||
CS_Status* status) {
|
||||
return cs::SetSinkDescription(sink, description, status);
|
||||
}
|
||||
|
||||
#if CV_VERSION_MAJOR < 4
|
||||
uint64_t CS_GrabSinkFrame(CS_Sink sink, struct CvMat* image,
|
||||
CS_Status* status) {
|
||||
auto mat = cv::cvarrToMat(image);
|
||||
return cs::GrabSinkFrame(sink, mat, status);
|
||||
}
|
||||
|
||||
uint64_t CS_GrabSinkFrameTimeout(CS_Sink sink, struct CvMat* image,
|
||||
double timeout, CS_Status* status) {
|
||||
auto mat = cv::cvarrToMat(image);
|
||||
return cs::GrabSinkFrameTimeout(sink, mat, timeout, status);
|
||||
}
|
||||
#endif // CV_VERSION_MAJOR < 4
|
||||
|
||||
uint64_t CS_GrabSinkFrameCpp(CS_Sink sink, cv::Mat* image, CS_Status* status) {
|
||||
return cs::GrabSinkFrame(sink, *image, status);
|
||||
}
|
||||
|
||||
uint64_t CS_GrabSinkFrameTimeoutCpp(CS_Sink sink, cv::Mat* image,
|
||||
double timeout, CS_Status* status) {
|
||||
return cs::GrabSinkFrameTimeout(sink, *image, timeout, status);
|
||||
}
|
||||
|
||||
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;
|
||||
return cs::ConvertToC(str);
|
||||
}
|
||||
|
||||
void CS_SetSinkEnabled(CS_Sink sink, CS_Bool enabled, CS_Status* status) {
|
||||
return cs::SetSinkEnabled(sink, enabled, status);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
52
cscore/src/main/native/cpp/CvSinkImpl.h
Normal file
52
cscore/src/main/native/cpp/CvSinkImpl.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CSCORE_CVSINKIMPL_H_
|
||||
#define CSCORE_CVSINKIMPL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <wpi/Twine.h>
|
||||
#include <wpi/condition_variable.h>
|
||||
|
||||
#include "Frame.h"
|
||||
#include "SinkImpl.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class SourceImpl;
|
||||
|
||||
class CvSinkImpl : public SinkImpl {
|
||||
public:
|
||||
CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
|
||||
Telemetry& telemetry);
|
||||
CvSinkImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
|
||||
Telemetry& telemetry,
|
||||
std::function<void(uint64_t time)> processFrame);
|
||||
~CvSinkImpl() override;
|
||||
|
||||
void Stop();
|
||||
|
||||
uint64_t GrabFrame(cv::Mat& image);
|
||||
uint64_t GrabFrame(cv::Mat& image, double timeout);
|
||||
|
||||
private:
|
||||
void ThreadMain();
|
||||
|
||||
std::atomic_bool m_active; // set to false to terminate threads
|
||||
std::thread m_thread;
|
||||
std::function<void(uint64_t time)> m_processFrame;
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_CVSINKIMPL_H_
|
||||
234
cscore/src/main/native/cpp/CvSourceImpl.cpp
Normal file
234
cscore/src/main/native/cpp/CvSourceImpl.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#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"
|
||||
#include "Instance.h"
|
||||
#include "Log.h"
|
||||
#include "Notifier.h"
|
||||
#include "c_util.h"
|
||||
#include "cscore_cpp.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
CvSourceImpl::CvSourceImpl(const wpi::Twine& name, wpi::Logger& logger,
|
||||
Notifier& notifier, Telemetry& telemetry,
|
||||
const VideoMode& mode)
|
||||
: ConfigurableSourceImpl{name, logger, notifier, telemetry, mode} {}
|
||||
|
||||
CvSourceImpl::~CvSourceImpl() {}
|
||||
|
||||
void CvSourceImpl::PutFrame(cv::Mat& image) {
|
||||
// We only support 8-bit images; convert if necessary.
|
||||
cv::Mat finalImage;
|
||||
if (image.depth() == CV_8U)
|
||||
finalImage = image;
|
||||
else
|
||||
image.convertTo(finalImage, CV_8U);
|
||||
|
||||
std::unique_ptr<Image> dest;
|
||||
switch (image.channels()) {
|
||||
case 1:
|
||||
dest =
|
||||
AllocImage(VideoMode::kGray, image.cols, image.rows, image.total());
|
||||
finalImage.copyTo(dest->AsMat());
|
||||
break;
|
||||
case 3:
|
||||
dest = AllocImage(VideoMode::kBGR, image.cols, image.rows,
|
||||
image.total() * 3);
|
||||
finalImage.copyTo(dest->AsMat());
|
||||
break;
|
||||
case 4:
|
||||
dest = AllocImage(VideoMode::kBGR, image.cols, image.rows,
|
||||
image.total() * 3);
|
||||
cv::cvtColor(finalImage, dest->AsMat(), cv::COLOR_BGRA2BGR);
|
||||
break;
|
||||
default:
|
||||
SERROR("PutFrame: " << image.channels()
|
||||
<< "-channel images not supported");
|
||||
return;
|
||||
}
|
||||
SourceImpl::PutFrame(std::move(dest), wpi::Now());
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
|
||||
CS_Source CreateCvSource(const wpi::Twine& name, const VideoMode& mode,
|
||||
CS_Status* status) {
|
||||
auto& inst = Instance::GetInstance();
|
||||
return inst.CreateSource(CS_SOURCE_CV, std::make_shared<CvSourceImpl>(
|
||||
name, inst.logger, inst.notifier,
|
||||
inst.telemetry, mode));
|
||||
}
|
||||
|
||||
void PutSourceFrame(CS_Source source, cv::Mat& image, CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || data->kind != CS_SOURCE_CV) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<CvSourceImpl&>(*data->source).PutFrame(image);
|
||||
}
|
||||
|
||||
static constexpr unsigned SourceMask = CS_SINK_CV | CS_SINK_RAW;
|
||||
|
||||
void NotifySourceError(CS_Source source, const wpi::Twine& msg,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<CvSourceImpl&>(*data->source).NotifyError(msg);
|
||||
}
|
||||
|
||||
void SetSourceConnected(CS_Source source, bool connected, CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<CvSourceImpl&>(*data->source).SetConnected(connected);
|
||||
}
|
||||
|
||||
void SetSourceDescription(CS_Source source, const wpi::Twine& description,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<CvSourceImpl&>(*data->source).SetDescription(description);
|
||||
}
|
||||
|
||||
CS_Property CreateSourceProperty(CS_Source source, const wpi::Twine& name,
|
||||
CS_PropertyKind kind, int minimum, int maximum,
|
||||
int step, int defaultValue, int value,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return -1;
|
||||
}
|
||||
int property = static_cast<CvSourceImpl&>(*data->source)
|
||||
.CreateProperty(name, kind, minimum, maximum, step,
|
||||
defaultValue, value);
|
||||
return Handle{source, property, Handle::kProperty};
|
||||
}
|
||||
|
||||
CS_Property CreateSourcePropertyCallback(
|
||||
CS_Source source, const wpi::Twine& 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);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return -1;
|
||||
}
|
||||
int property = static_cast<CvSourceImpl&>(*data->source)
|
||||
.CreateProperty(name, kind, minimum, maximum, step,
|
||||
defaultValue, value, onChange);
|
||||
return Handle{source, property, Handle::kProperty};
|
||||
}
|
||||
|
||||
void SetSourceEnumPropertyChoices(CS_Source source, CS_Property property,
|
||||
wpi::ArrayRef<std::string> choices,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get property index; also validate the source owns this property
|
||||
Handle handle{property};
|
||||
int i = handle.GetParentIndex();
|
||||
if (i < 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
auto data2 = Instance::GetInstance().GetSource(Handle{i, Handle::kSource});
|
||||
if (!data2 || data->source.get() != data2->source.get()) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
int propertyIndex = handle.GetIndex();
|
||||
static_cast<CvSourceImpl&>(*data->source)
|
||||
.SetEnumPropertyChoices(propertyIndex, choices, status);
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
extern "C" {
|
||||
|
||||
CS_Source CS_CreateCvSource(const char* name, const CS_VideoMode* mode,
|
||||
CS_Status* status) {
|
||||
return cs::CreateCvSource(name, static_cast<const cs::VideoMode&>(*mode),
|
||||
status);
|
||||
}
|
||||
|
||||
#if CV_VERSION_MAJOR < 4
|
||||
void CS_PutSourceFrame(CS_Source source, struct CvMat* image,
|
||||
CS_Status* status) {
|
||||
auto mat = cv::cvarrToMat(image);
|
||||
return cs::PutSourceFrame(source, mat, status);
|
||||
}
|
||||
#endif // CV_VERSION_MAJOR < 4
|
||||
|
||||
void CS_PutSourceFrameCpp(CS_Source source, cv::Mat* image, CS_Status* status) {
|
||||
return cs::PutSourceFrame(source, *image, status);
|
||||
}
|
||||
|
||||
void CS_NotifySourceError(CS_Source source, const char* msg,
|
||||
CS_Status* status) {
|
||||
return cs::NotifySourceError(source, msg, status);
|
||||
}
|
||||
|
||||
void CS_SetSourceConnected(CS_Source source, CS_Bool connected,
|
||||
CS_Status* status) {
|
||||
return cs::SetSourceConnected(source, connected, status);
|
||||
}
|
||||
|
||||
void CS_SetSourceDescription(CS_Source source, const char* description,
|
||||
CS_Status* status) {
|
||||
return cs::SetSourceDescription(source, description, status);
|
||||
}
|
||||
|
||||
CS_Property CS_CreateSourceProperty(CS_Source source, const char* name,
|
||||
enum CS_PropertyKind kind, int minimum,
|
||||
int maximum, int step, int defaultValue,
|
||||
int value, CS_Status* status) {
|
||||
return cs::CreateSourceProperty(source, name, kind, minimum, maximum, step,
|
||||
defaultValue, value, status);
|
||||
}
|
||||
|
||||
CS_Property CS_CreateSourcePropertyCallback(
|
||||
CS_Source source, const char* name, enum CS_PropertyKind kind, int minimum,
|
||||
int maximum, int step, int defaultValue, int value, void* data,
|
||||
void (*onChange)(void* data, CS_Property property), CS_Status* status) {
|
||||
return cs::CreateSourcePropertyCallback(
|
||||
source, name, kind, minimum, maximum, step, defaultValue, value,
|
||||
[=](CS_Property property) { onChange(data, property); }, status);
|
||||
}
|
||||
|
||||
void CS_SetSourceEnumPropertyChoices(CS_Source source, CS_Property property,
|
||||
const char** choices, int count,
|
||||
CS_Status* status) {
|
||||
wpi::SmallVector<std::string, 8> vec;
|
||||
vec.reserve(count);
|
||||
for (int i = 0; i < count; ++i) vec.push_back(choices[i]);
|
||||
return cs::SetSourceEnumPropertyChoices(source, property, vec, status);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
41
cscore/src/main/native/cpp/CvSourceImpl.h
Normal file
41
cscore/src/main/native/cpp/CvSourceImpl.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CSCORE_CVSOURCEIMPL_H_
|
||||
#define CSCORE_CVSOURCEIMPL_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <wpi/ArrayRef.h>
|
||||
#include <wpi/Twine.h>
|
||||
|
||||
#include "ConfigurableSourceImpl.h"
|
||||
#include "SourceImpl.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class CvSourceImpl : public ConfigurableSourceImpl {
|
||||
public:
|
||||
CvSourceImpl(const wpi::Twine& name, wpi::Logger& logger, Notifier& notifier,
|
||||
Telemetry& telemetry, const VideoMode& mode);
|
||||
~CvSourceImpl() override;
|
||||
|
||||
// OpenCV-specific functions
|
||||
void PutFrame(cv::Mat& image);
|
||||
|
||||
private:
|
||||
std::atomic_bool m_connected{true};
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_CVSOURCEIMPL_H_
|
||||
499
cscore/src/main/native/cpp/Frame.cpp
Normal file
499
cscore/src/main/native/cpp/Frame.cpp
Normal file
@@ -0,0 +1,499 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "Frame.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
|
||||
#include "Instance.h"
|
||||
#include "Log.h"
|
||||
#include "SourceImpl.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
Frame::Frame(SourceImpl& source, const wpi::Twine& error, Time time)
|
||||
: m_impl{source.AllocFrameImpl().release()} {
|
||||
m_impl->refcount = 1;
|
||||
m_impl->error = error.str();
|
||||
m_impl->time = time;
|
||||
}
|
||||
|
||||
Frame::Frame(SourceImpl& source, std::unique_ptr<Image> image, Time time)
|
||||
: m_impl{source.AllocFrameImpl().release()} {
|
||||
m_impl->refcount = 1;
|
||||
m_impl->error.resize(0);
|
||||
m_impl->time = time;
|
||||
m_impl->images.push_back(image.release());
|
||||
}
|
||||
|
||||
Image* Frame::GetNearestImage(int width, int height) const {
|
||||
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))))
|
||||
found = i;
|
||||
}
|
||||
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) return found;
|
||||
|
||||
// Shouldn't reach this, but just in case...
|
||||
return m_impl->images.empty() ? nullptr : m_impl->images[0];
|
||||
}
|
||||
|
||||
Image* Frame::GetNearestImage(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
int jpegQuality) const {
|
||||
if (!m_impl) return nullptr;
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
Image* found = nullptr;
|
||||
|
||||
// We want the smallest image at least width/height (or the next largest),
|
||||
// but the primary search order is in order of conversion cost.
|
||||
// If we don't find exactly what we want, we prefer non-JPEG source images
|
||||
// (because JPEG source images require a decompression step).
|
||||
// While the searching takes a little time, it pales in comparison to the
|
||||
// image processing to come, so it's worth spending a little extra time
|
||||
// looking for the most efficient conversion.
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->Is(width, height) && i->pixelFormat != VideoMode::kMJPEG) return i;
|
||||
}
|
||||
|
||||
// 3) Different width, height, same pixelFormat (only if non-JPEG) (resample)
|
||||
if (pixelFormat != VideoMode::kMJPEG) {
|
||||
// 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;
|
||||
}
|
||||
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))))
|
||||
found = i;
|
||||
}
|
||||
if (found) return found;
|
||||
}
|
||||
|
||||
// 4) Different width, height, different (but non-JPEG) pixelFormat
|
||||
// (color conversion + resample)
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (found) return found;
|
||||
|
||||
// 5) Same width, height, JPEG pixelFormat (decompression). As there may be
|
||||
// multiple JPEG images, find the highest quality one.
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->Is(width, height, VideoMode::kMJPEG) &&
|
||||
(!found || i->jpegQuality > found->jpegQuality)) {
|
||||
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 (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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (found) return found;
|
||||
|
||||
// Shouldn't reach this, but just in case...
|
||||
return m_impl->images.empty() ? nullptr : m_impl->images[0];
|
||||
}
|
||||
|
||||
Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality) {
|
||||
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
|
||||
// anything else with it. Note that if the destination format is JPEG, we
|
||||
// still need to do this (unless it was already a JPEG, in which case we
|
||||
// would have returned above).
|
||||
if (cur->pixelFormat == VideoMode::kMJPEG) {
|
||||
cur = ConvertMJPEGToBGR(cur);
|
||||
if (pixelFormat == VideoMode::kBGR) return cur;
|
||||
}
|
||||
|
||||
// Color convert
|
||||
switch (pixelFormat) {
|
||||
case VideoMode::kRGB565:
|
||||
// If source is YUYV or Gray, need to convert to BGR first
|
||||
if (cur->pixelFormat == VideoMode::kYUYV) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR))
|
||||
cur = newImage;
|
||||
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))
|
||||
cur = newImage;
|
||||
else
|
||||
cur = ConvertGrayToBGR(cur);
|
||||
}
|
||||
return ConvertBGRToRGB565(cur);
|
||||
case VideoMode::kGray:
|
||||
// If source is YUYV or RGB565, need to convert to BGR first
|
||||
if (cur->pixelFormat == VideoMode::kYUYV) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR))
|
||||
cur = newImage;
|
||||
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))
|
||||
cur = newImage;
|
||||
else
|
||||
cur = ConvertRGB565ToBGR(cur);
|
||||
}
|
||||
return ConvertBGRToGray(cur);
|
||||
case VideoMode::kBGR:
|
||||
case VideoMode::kMJPEG:
|
||||
if (cur->pixelFormat == VideoMode::kYUYV) {
|
||||
cur = ConvertYUYVToBGR(cur);
|
||||
} else if (cur->pixelFormat == VideoMode::kRGB565) {
|
||||
cur = ConvertRGB565ToBGR(cur);
|
||||
} else if (cur->pixelFormat == VideoMode::kGray) {
|
||||
if (pixelFormat == VideoMode::kBGR)
|
||||
return ConvertGrayToBGR(cur);
|
||||
else
|
||||
return ConvertGrayToMJPEG(cur, defaultJpegQuality);
|
||||
}
|
||||
break;
|
||||
case VideoMode::kYUYV:
|
||||
default:
|
||||
return nullptr; // Unsupported
|
||||
}
|
||||
|
||||
// Compress if destination is JPEG
|
||||
if (pixelFormat == VideoMode::kMJPEG)
|
||||
cur = ConvertBGRToMJPEG(cur, defaultJpegQuality);
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertMJPEGToBGR(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kMJPEG) return nullptr;
|
||||
|
||||
// Allocate an BGR image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
|
||||
// Decode
|
||||
cv::Mat newMat = newImage->AsMat();
|
||||
cv::imdecode(image->AsInputArray(), cv::IMREAD_COLOR, &newMat);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
if (m_impl) {
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
m_impl->images.push_back(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertMJPEGToGray(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kMJPEG) return nullptr;
|
||||
|
||||
// Allocate an grayscale image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height,
|
||||
image->width * image->height);
|
||||
|
||||
// Decode
|
||||
cv::Mat newMat = newImage->AsMat();
|
||||
cv::imdecode(image->AsInputArray(), cv::IMREAD_GRAYSCALE, &newMat);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
if (m_impl) {
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
m_impl->images.push_back(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertYUYVToBGR(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kYUYV) return nullptr;
|
||||
|
||||
// Allocate a BGR image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_YUV2BGR_YUYV);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
if (m_impl) {
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
m_impl->images.push_back(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertBGRToRGB565(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kBGR) return nullptr;
|
||||
|
||||
// Allocate a RGB565 image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kRGB565, image->width, image->height,
|
||||
image->width * image->height * 2);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_RGB2BGR565);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
if (m_impl) {
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
m_impl->images.push_back(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertRGB565ToBGR(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kRGB565) return nullptr;
|
||||
|
||||
// Allocate a BGR image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR5652RGB);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
if (m_impl) {
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
m_impl->images.push_back(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertBGRToGray(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kBGR) return nullptr;
|
||||
|
||||
// Allocate a Grayscale image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kGray, image->width, image->height,
|
||||
image->width * image->height);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR2GRAY);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
if (m_impl) {
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
m_impl->images.push_back(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertGrayToBGR(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kGray) return nullptr;
|
||||
|
||||
// Allocate a BGR image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGR, image->width, image->height,
|
||||
image->width * image->height * 3);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_GRAY2BGR);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
if (m_impl) {
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
m_impl->images.push_back(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertBGRToMJPEG(Image* image, int quality) {
|
||||
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
|
||||
// will be; while the destination will automatically grow, doing so will
|
||||
// cause an extra malloc, so we don't want to be too conservative here.
|
||||
// Per Wikipedia, Q=100 on a sample image results in 8.25 bits per pixel,
|
||||
// this is a little bit more conservative in assuming 50% space savings over
|
||||
// the equivalent BGR image.
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kMJPEG, image->width, image->height,
|
||||
image->width * image->height * 1.5);
|
||||
|
||||
// Compress
|
||||
if (m_impl->compressionParams.empty()) {
|
||||
m_impl->compressionParams.push_back(cv::IMWRITE_JPEG_QUALITY);
|
||||
m_impl->compressionParams.push_back(quality);
|
||||
} else {
|
||||
m_impl->compressionParams[1] = quality;
|
||||
}
|
||||
cv::imencode(".jpg", image->AsMat(), newImage->vec(),
|
||||
m_impl->compressionParams);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
m_impl->images.push_back(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertGrayToMJPEG(Image* image, int quality) {
|
||||
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
|
||||
// will be; while the destination will automatically grow, doing so will
|
||||
// cause an extra malloc, so we don't want to be too conservative here.
|
||||
// Per Wikipedia, Q=100 on a sample image results in 8.25 bits per pixel,
|
||||
// this is a little bit more conservative in assuming 25% space savings over
|
||||
// the equivalent grayscale image.
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kMJPEG, image->width, image->height,
|
||||
image->width * image->height * 0.75);
|
||||
|
||||
// Compress
|
||||
if (m_impl->compressionParams.empty()) {
|
||||
m_impl->compressionParams.push_back(cv::IMWRITE_JPEG_QUALITY);
|
||||
m_impl->compressionParams.push_back(quality);
|
||||
} else {
|
||||
m_impl->compressionParams[1] = quality;
|
||||
}
|
||||
cv::imencode(".jpg", image->AsMat(), newImage->vec(),
|
||||
m_impl->compressionParams);
|
||||
|
||||
// Save the result
|
||||
Image* rv = newImage.release();
|
||||
m_impl->images.push_back(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::GetImageImpl(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality) {
|
||||
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))
|
||||
return cur;
|
||||
|
||||
WPI_DEBUG4(Instance::GetInstance().logger,
|
||||
"converting image from " << cur->width << "x" << cur->height
|
||||
<< " type " << cur->pixelFormat << " to "
|
||||
<< width << "x" << height << " type "
|
||||
<< 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);
|
||||
|
||||
// Resize
|
||||
if (!cur->Is(width, height)) {
|
||||
// Allocate an image.
|
||||
auto newImage = m_impl->source.AllocImage(
|
||||
cur->pixelFormat, width, height,
|
||||
width * height * (cur->size() / (cur->width * cur->height)));
|
||||
|
||||
// Resize
|
||||
cv::Mat newMat = newImage->AsMat();
|
||||
cv::resize(cur->AsMat(), newMat, newMat.size(), 0, 0);
|
||||
|
||||
// Save the result
|
||||
cur = newImage.release();
|
||||
m_impl->images.push_back(cur);
|
||||
}
|
||||
|
||||
// Convert to output format
|
||||
return ConvertImpl(cur, pixelFormat, requiredJpegQuality, defaultJpegQuality);
|
||||
}
|
||||
|
||||
bool Frame::GetCv(cv::Mat& image, int width, int height) {
|
||||
Image* rawImage = GetImage(width, height, VideoMode::kBGR);
|
||||
if (!rawImage) return false;
|
||||
rawImage->AsMat().copyTo(image);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Frame::ReleaseFrame() {
|
||||
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;
|
||||
}
|
||||
200
cscore/src/main/native/cpp/Frame.h
Normal file
200
cscore/src/main/native/cpp/Frame.h
Normal file
@@ -0,0 +1,200 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CSCORE_FRAME_H_
|
||||
#define CSCORE_FRAME_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/Twine.h>
|
||||
#include <wpi/mutex.h>
|
||||
|
||||
#include "Image.h"
|
||||
#include "cscore_cpp.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class SourceImpl;
|
||||
|
||||
class Frame {
|
||||
friend class SourceImpl;
|
||||
|
||||
public:
|
||||
using Time = uint64_t;
|
||||
|
||||
private:
|
||||
struct Impl {
|
||||
explicit Impl(SourceImpl& source_) : source(source_) {}
|
||||
|
||||
wpi::recursive_mutex mutex;
|
||||
std::atomic_int refcount{0};
|
||||
Time time{0};
|
||||
SourceImpl& source;
|
||||
std::string error;
|
||||
wpi::SmallVector<Image*, 4> images;
|
||||
std::vector<int> compressionParams;
|
||||
};
|
||||
|
||||
public:
|
||||
Frame() noexcept : m_impl{nullptr} {}
|
||||
|
||||
Frame(SourceImpl& source, const wpi::Twine& 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;
|
||||
}
|
||||
|
||||
Frame(Frame&& other) noexcept : Frame() { swap(*this, other); }
|
||||
|
||||
~Frame() { DecRef(); }
|
||||
|
||||
Frame& operator=(Frame other) noexcept {
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return m_impl && m_impl->error.empty(); }
|
||||
|
||||
friend void swap(Frame& first, Frame& second) noexcept {
|
||||
using std::swap;
|
||||
swap(first.m_impl, second.m_impl);
|
||||
}
|
||||
|
||||
Time GetTime() const { return m_impl ? m_impl->time : 0; }
|
||||
|
||||
wpi::StringRef GetError() const {
|
||||
if (!m_impl) return wpi::StringRef{};
|
||||
return m_impl->error;
|
||||
}
|
||||
|
||||
int GetOriginalWidth() const {
|
||||
if (!m_impl) return 0;
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
if (m_impl->images.empty()) return 0;
|
||||
return m_impl->images[0]->width;
|
||||
}
|
||||
|
||||
int GetOriginalHeight() const {
|
||||
if (!m_impl) return 0;
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
if (m_impl->images.empty()) return 0;
|
||||
return m_impl->images[0]->height;
|
||||
}
|
||||
|
||||
int GetOriginalPixelFormat() const {
|
||||
if (!m_impl) return 0;
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
if (m_impl->images.empty()) return 0;
|
||||
return m_impl->images[0]->pixelFormat;
|
||||
}
|
||||
|
||||
int GetOriginalJpegQuality() const {
|
||||
if (!m_impl) return 0;
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
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;
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
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;
|
||||
std::scoped_lock lock(m_impl->mutex);
|
||||
for (auto i : m_impl->images) {
|
||||
if (i->Is(width, height)) return i;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Image* GetExistingImage(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat) const {
|
||||
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;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Image* GetExistingImage(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
int jpegQuality) const {
|
||||
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;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Image* GetNearestImage(int width, int height) const;
|
||||
Image* GetNearestImage(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
int jpegQuality = -1) const;
|
||||
|
||||
Image* Convert(Image* image, VideoMode::PixelFormat pixelFormat) {
|
||||
if (pixelFormat == VideoMode::kMJPEG) return nullptr;
|
||||
return ConvertImpl(image, pixelFormat, -1, 80);
|
||||
}
|
||||
Image* ConvertToMJPEG(Image* image, int requiredQuality,
|
||||
int defaultQuality = 80) {
|
||||
return ConvertImpl(image, VideoMode::kMJPEG, requiredQuality,
|
||||
defaultQuality);
|
||||
}
|
||||
Image* ConvertMJPEGToBGR(Image* image);
|
||||
Image* ConvertMJPEGToGray(Image* image);
|
||||
Image* ConvertYUYVToBGR(Image* image);
|
||||
Image* ConvertBGRToRGB565(Image* image);
|
||||
Image* ConvertRGB565ToBGR(Image* image);
|
||||
Image* ConvertBGRToGray(Image* image);
|
||||
Image* ConvertGrayToBGR(Image* image);
|
||||
Image* ConvertBGRToMJPEG(Image* image, int quality);
|
||||
Image* ConvertGrayToMJPEG(Image* image, int quality);
|
||||
|
||||
Image* GetImage(int width, int height, VideoMode::PixelFormat pixelFormat) {
|
||||
if (pixelFormat == VideoMode::kMJPEG) return nullptr;
|
||||
return GetImageImpl(width, height, pixelFormat, -1, 80);
|
||||
}
|
||||
Image* GetImageMJPEG(int width, int height, int requiredQuality,
|
||||
int defaultQuality = 80) {
|
||||
return GetImageImpl(width, height, VideoMode::kMJPEG, requiredQuality,
|
||||
defaultQuality);
|
||||
}
|
||||
|
||||
bool GetCv(cv::Mat& image) {
|
||||
return GetCv(image, GetOriginalWidth(), GetOriginalHeight());
|
||||
}
|
||||
bool GetCv(cv::Mat& image, int width, int height);
|
||||
|
||||
private:
|
||||
Image* ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality);
|
||||
Image* GetImageImpl(int width, int height, VideoMode::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality);
|
||||
void DecRef() {
|
||||
if (m_impl && --(m_impl->refcount) == 0) ReleaseFrame();
|
||||
}
|
||||
void ReleaseFrame();
|
||||
|
||||
Impl* m_impl;
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_FRAME_H_
|
||||
69
cscore/src/main/native/cpp/Handle.h
Normal file
69
cscore/src/main/native/cpp/Handle.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CSCORE_HANDLE_H_
|
||||
#define CSCORE_HANDLE_H_
|
||||
|
||||
#include "cscore_c.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
// Handle data layout:
|
||||
// Bits 0-15: Handle index
|
||||
// Bits 16-23: Parent index (property only)
|
||||
// Bits 24-30: Type
|
||||
|
||||
class Handle {
|
||||
public:
|
||||
enum Type {
|
||||
kUndefined = 0,
|
||||
kProperty = 0x40,
|
||||
kSource,
|
||||
kSink,
|
||||
kListener,
|
||||
kSinkProperty
|
||||
};
|
||||
enum { kIndexMax = 0xffff };
|
||||
|
||||
Handle(CS_Handle handle) : m_handle(handle) {} // NOLINT
|
||||
operator CS_Handle() const { return m_handle; }
|
||||
|
||||
Handle(int index, Type type) {
|
||||
if (index < 0) {
|
||||
m_handle = 0;
|
||||
return;
|
||||
}
|
||||
m_handle = ((static_cast<int>(type) & 0x7f) << 24) | (index & 0xffff);
|
||||
}
|
||||
Handle(int index, int property, Type type) {
|
||||
if (index < 0 || property < 0) {
|
||||
m_handle = 0;
|
||||
return;
|
||||
}
|
||||
m_handle = ((static_cast<int>(type) & 0x7f) << 24) |
|
||||
((index & 0xff) << 16) | (property & 0xffff);
|
||||
}
|
||||
|
||||
int GetIndex() const { return static_cast<int>(m_handle) & 0xffff; }
|
||||
Type GetType() const {
|
||||
return static_cast<Type>((static_cast<int>(m_handle) >> 24) & 0xff);
|
||||
}
|
||||
bool IsType(Type type) const { return type == GetType(); }
|
||||
int GetTypedIndex(Type type) const { return IsType(type) ? GetIndex() : -1; }
|
||||
int GetParentIndex() const {
|
||||
return (IsType(Handle::kProperty) || IsType(Handle::kSinkProperty))
|
||||
? (static_cast<int>(m_handle) >> 16) & 0xff
|
||||
: -1;
|
||||
}
|
||||
|
||||
private:
|
||||
CS_Handle m_handle;
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_HANDLE_H_
|
||||
625
cscore/src/main/native/cpp/HttpCameraImpl.cpp
Normal file
625
cscore/src/main/native/cpp/HttpCameraImpl.cpp
Normal file
@@ -0,0 +1,625 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* 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. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "HttpCameraImpl.h"
|
||||
|
||||
#include <wpi/MemAlloc.h>
|
||||
#include <wpi/TCPConnector.h>
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
#include "Handle.h"
|
||||
#include "Instance.h"
|
||||
#include "JpegUtil.h"
|
||||
#include "Log.h"
|
||||
#include "Notifier.h"
|
||||
#include "Telemetry.h"
|
||||
#include "c_util.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
HttpCameraImpl::HttpCameraImpl(const wpi::Twine& name, CS_HttpCameraKind kind,
|
||||
wpi::Logger& logger, Notifier& notifier,
|
||||
Telemetry& telemetry)
|
||||
: SourceImpl{name, logger, notifier, telemetry}, m_kind{kind} {}
|
||||
|
||||
HttpCameraImpl::~HttpCameraImpl() {
|
||||
m_active = false;
|
||||
|
||||
// force wakeup of monitor thread
|
||||
m_monitorCond.notify_one();
|
||||
|
||||
// join monitor thread
|
||||
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();
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
// force wakeup of settings thread
|
||||
m_settingsCond.notify_one();
|
||||
|
||||
// join settings thread
|
||||
if (m_settingsThread.joinable()) m_settingsThread.join();
|
||||
}
|
||||
|
||||
void HttpCameraImpl::Start() {
|
||||
// Kick off the stream and settings threads
|
||||
m_streamThread = std::thread(&HttpCameraImpl::StreamThreadMain, this);
|
||||
m_settingsThread = std::thread(&HttpCameraImpl::SettingsThreadMain, this);
|
||||
m_monitorThread = std::thread(&HttpCameraImpl::MonitorThreadMain, this);
|
||||
}
|
||||
|
||||
void HttpCameraImpl::MonitorThreadMain() {
|
||||
while (m_active) {
|
||||
std::unique_lock lock(m_mutex);
|
||||
// sleep for 1 second between checks
|
||||
m_monitorCond.wait_for(lock, std::chrono::seconds(1),
|
||||
[=] { return !m_active; });
|
||||
|
||||
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");
|
||||
m_streamConn->stream->close();
|
||||
}
|
||||
|
||||
// reset the frame counter
|
||||
m_frameCount = 0;
|
||||
}
|
||||
|
||||
SDEBUG("Monitor Thread exiting");
|
||||
}
|
||||
|
||||
void HttpCameraImpl::StreamThreadMain() {
|
||||
while (m_active) {
|
||||
SetConnected(false);
|
||||
|
||||
// sleep between retries
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
|
||||
// disconnect if not enabled
|
||||
if (!IsEnabled()) {
|
||||
std::unique_lock lock(m_mutex);
|
||||
if (m_streamConn) m_streamConn->stream->close();
|
||||
// Wait for enable
|
||||
m_sinkEnabledCond.wait(lock, [=] { return !m_active || IsEnabled(); });
|
||||
if (!m_active) return;
|
||||
}
|
||||
|
||||
// connect
|
||||
wpi::SmallString<64> boundary;
|
||||
wpi::HttpConnection* conn = DeviceStreamConnect(boundary);
|
||||
|
||||
if (!m_active) break;
|
||||
|
||||
// keep retrying
|
||||
if (!conn) continue;
|
||||
|
||||
// update connected since we're actually connected
|
||||
SetConnected(true);
|
||||
|
||||
// stream
|
||||
DeviceStream(conn->is, boundary);
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
m_streamConn = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SDEBUG("Camera Thread exiting");
|
||||
SetConnected(false);
|
||||
}
|
||||
|
||||
wpi::HttpConnection* HttpCameraImpl::DeviceStreamConnect(
|
||||
wpi::SmallVectorImpl<char>& boundary) {
|
||||
// Build the request
|
||||
wpi::HttpRequest req;
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
if (m_locations.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;
|
||||
req = wpi::HttpRequest{m_locations[m_nextLocation++], m_streamSettings};
|
||||
m_streamSettingsUpdated = false;
|
||||
}
|
||||
|
||||
// Try to connect
|
||||
auto stream =
|
||||
wpi::TCPConnector::connect(req.host.c_str(), req.port, m_logger, 1);
|
||||
|
||||
if (!m_active || !stream) return nullptr;
|
||||
|
||||
auto connPtr = std::make_unique<wpi::HttpConnection>(std::move(stream), 1);
|
||||
wpi::HttpConnection* conn = connPtr.get();
|
||||
|
||||
// update m_streamConn
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_frameCount = 1; // avoid a race with monitor thread
|
||||
m_streamConn = std::move(connPtr);
|
||||
}
|
||||
|
||||
std::string warn;
|
||||
if (!conn->Handshake(req, &warn)) {
|
||||
SWARNING(GetName() << ": " << 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();
|
||||
if (mediaType != "multipart/x-mixed-replace") {
|
||||
SWARNING("\"" << req.host << "\": unrecognized Content-Type \"" << mediaType
|
||||
<< "\"");
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_streamConn = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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("--")) {
|
||||
value = value.substr(2);
|
||||
}
|
||||
boundary.append(value.begin(), value.end());
|
||||
}
|
||||
}
|
||||
|
||||
if (boundary.empty()) {
|
||||
SWARNING("\"" << req.host
|
||||
<< "\": empty multi-part boundary or no Content-Type");
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_streamConn = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
void HttpCameraImpl::DeviceStream(wpi::raw_istream& is,
|
||||
wpi::StringRef boundary) {
|
||||
// Stored here so we reuse it from frame to frame
|
||||
std::string imageBuf;
|
||||
|
||||
// keep track of number of bad images received; if we receive 3 bad images
|
||||
// in a row, we reconnect
|
||||
int numErrors = 0;
|
||||
|
||||
// streaming loop
|
||||
while (m_active && !is.has_error() && IsEnabled() && numErrors < 3 &&
|
||||
!m_streamSettingsUpdated) {
|
||||
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 (eol[0] != '\n') {
|
||||
is.read(eol + 1, 1);
|
||||
if (!m_active || is.has_error()) break;
|
||||
// End-of-stream is indicated with trailing --
|
||||
if (eol[0] == '-' && eol[1] == '-') break;
|
||||
}
|
||||
|
||||
if (!DeviceStreamFrame(is, imageBuf))
|
||||
++numErrors;
|
||||
else
|
||||
numErrors = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpCameraImpl::DeviceStreamFrame(wpi::raw_istream& is,
|
||||
std::string& imageBuf) {
|
||||
// Read the headers
|
||||
wpi::SmallString<64> contentTypeBuf;
|
||||
wpi::SmallString<64> contentLengthBuf;
|
||||
if (!ParseHttpHeaders(is, &contentTypeBuf, &contentLengthBuf)) {
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int contentLength = 0;
|
||||
if (contentLengthBuf.str().getAsInteger(10, contentLength)) {
|
||||
// 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");
|
||||
PutError("did not receive a JPEG image", wpi::Now());
|
||||
return false;
|
||||
}
|
||||
PutFrame(VideoMode::PixelFormat::kMJPEG, width, height, imageBuf,
|
||||
wpi::Now());
|
||||
++m_frameCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We know how big it is! Just get a frame of the right size and read
|
||||
// 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;
|
||||
int width, height;
|
||||
if (!GetJpegSize(image->str(), &width, &height)) {
|
||||
SWARNING("did not receive a JPEG image");
|
||||
PutError("did not receive a JPEG image", wpi::Now());
|
||||
return false;
|
||||
}
|
||||
image->width = width;
|
||||
image->height = height;
|
||||
PutFrame(std::move(image), wpi::Now());
|
||||
++m_frameCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SettingsThreadMain() {
|
||||
for (;;) {
|
||||
wpi::HttpRequest req;
|
||||
{
|
||||
std::unique_lock lock(m_mutex);
|
||||
m_settingsCond.wait(lock, [=] {
|
||||
return !m_active || (m_prefLocation != -1 && !m_settings.empty());
|
||||
});
|
||||
if (!m_active) break;
|
||||
|
||||
// Build the request
|
||||
req = wpi::HttpRequest{m_locations[m_prefLocation], m_settings};
|
||||
}
|
||||
|
||||
DeviceSendSettings(req);
|
||||
}
|
||||
|
||||
SDEBUG("Settings Thread exiting");
|
||||
}
|
||||
|
||||
void HttpCameraImpl::DeviceSendSettings(wpi::HttpRequest& req) {
|
||||
// Try to connect
|
||||
auto stream =
|
||||
wpi::TCPConnector::connect(req.host.c_str(), req.port, m_logger, 1);
|
||||
|
||||
if (!m_active || !stream) return;
|
||||
|
||||
auto connPtr = std::make_unique<wpi::HttpConnection>(std::move(stream), 1);
|
||||
wpi::HttpConnection* conn = connPtr.get();
|
||||
|
||||
// update m_settingsConn
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_settingsConn = std::move(connPtr);
|
||||
}
|
||||
|
||||
// Just need a handshake as settings are sent via GET parameters
|
||||
std::string warn;
|
||||
if (!conn->Handshake(req, &warn)) SWARNING(GetName() << ": " << warn);
|
||||
|
||||
conn->stream->close();
|
||||
}
|
||||
|
||||
CS_HttpCameraKind HttpCameraImpl::GetKind() const {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
return m_kind;
|
||||
}
|
||||
|
||||
bool HttpCameraImpl::SetUrls(wpi::ArrayRef<std::string> urls,
|
||||
CS_Status* status) {
|
||||
std::vector<wpi::HttpLocation> locations;
|
||||
for (const auto& url : urls) {
|
||||
bool error = false;
|
||||
std::string errorMsg;
|
||||
locations.emplace_back(url, &error, &errorMsg);
|
||||
if (error) {
|
||||
SERROR(GetName() << ": " << errorMsg);
|
||||
*status = CS_BAD_URL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_locations.swap(locations);
|
||||
m_nextLocation = 0;
|
||||
m_streamSettingsUpdated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
return urls;
|
||||
}
|
||||
|
||||
void HttpCameraImpl::CreateProperty(const wpi::Twine& name,
|
||||
const wpi::Twine& httpParam,
|
||||
bool viaSettings, CS_PropertyKind kind,
|
||||
int minimum, int maximum, int step,
|
||||
int defaultValue, int value) const {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_propertyData.emplace_back(std::make_unique<PropertyData>(
|
||||
name, httpParam, viaSettings, kind, minimum, maximum, step, defaultValue,
|
||||
value));
|
||||
|
||||
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CREATED, name,
|
||||
m_propertyData.size() + 1, kind, value,
|
||||
wpi::Twine{});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void HttpCameraImpl::CreateEnumProperty(
|
||||
const wpi::Twine& name, const wpi::Twine& 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>(
|
||||
name, httpParam, viaSettings, CS_PROP_ENUM, 0, choices.size() - 1, 1,
|
||||
defaultValue, value));
|
||||
|
||||
auto& enumChoices = m_propertyData.back()->enumChoices;
|
||||
enumChoices.clear();
|
||||
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{});
|
||||
m_notifier.NotifySourceProperty(*this, CS_SOURCE_PROPERTY_CHOICES_UPDATED,
|
||||
name, m_propertyData.size() + 1, CS_PROP_ENUM,
|
||||
value, wpi::Twine{});
|
||||
}
|
||||
|
||||
std::unique_ptr<PropertyImpl> HttpCameraImpl::CreateEmptyProperty(
|
||||
const wpi::Twine& name) const {
|
||||
return std::make_unique<PropertyData>(name);
|
||||
}
|
||||
|
||||
bool HttpCameraImpl::CacheProperties(CS_Status* status) const {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
// Pretty typical set of video modes
|
||||
m_videoModes.clear();
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 640, 480, 30);
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 320, 240, 30);
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 160, 120, 30);
|
||||
|
||||
m_properties_cached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SetProperty(int property, int value, CS_Status* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SetStringProperty(int property, const wpi::Twine& value,
|
||||
CS_Status* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SetBrightness(int brightness, CS_Status* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
int HttpCameraImpl::GetBrightness(CS_Status* status) const {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SetWhiteBalanceAuto(CS_Status* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SetWhiteBalanceHoldCurrent(CS_Status* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SetWhiteBalanceManual(int value, CS_Status* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SetExposureAuto(CS_Status* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SetExposureHoldCurrent(CS_Status* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void HttpCameraImpl::SetExposureManual(int value, CS_Status* status) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
bool HttpCameraImpl::SetVideoMode(const VideoMode& mode, CS_Status* status) {
|
||||
if (mode.pixelFormat != VideoMode::kMJPEG) return false;
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_mode = mode;
|
||||
m_streamSettingsUpdated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpCameraImpl::NumSinksChanged() {
|
||||
// ignore
|
||||
}
|
||||
|
||||
void HttpCameraImpl::NumSinksEnabledChanged() {
|
||||
m_sinkEnabledCond.notify_one();
|
||||
}
|
||||
|
||||
bool AxisCameraImpl::CacheProperties(CS_Status* status) const {
|
||||
CreateProperty("brightness", "ImageSource.I0.Sensor.Brightness", true,
|
||||
CS_PROP_INTEGER, 0, 100, 1, 50, 50);
|
||||
CreateEnumProperty("white_balance", "ImageSource.I0.Sensor.WhiteBalance",
|
||||
true, 0, 0,
|
||||
{"auto", "hold", "fixed_outdoor1", "fixed_outdoor2",
|
||||
"fixed_indoor", "fixed_fluor1", "fixed_fluor2"});
|
||||
CreateProperty("color_level", "ImageSource.I0.Sensor.ColorLevel", true,
|
||||
CS_PROP_INTEGER, 0, 100, 1, 50, 50);
|
||||
CreateEnumProperty("exposure", "ImageSource.I0.Sensor.Exposure", true, 0, 0,
|
||||
{"auto", "hold", "flickerfree50", "flickerfree60"});
|
||||
CreateProperty("exposure_priority", "ImageSource.I0.Sensor.ExposurePriority",
|
||||
true, CS_PROP_INTEGER, 0, 100, 1, 50, 50);
|
||||
|
||||
// TODO: get video modes from device
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_videoModes.clear();
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 640, 480, 30);
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 480, 360, 30);
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 320, 240, 30);
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 240, 180, 30);
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 176, 144, 30);
|
||||
m_videoModes.emplace_back(VideoMode::kMJPEG, 160, 120, 30);
|
||||
|
||||
m_properties_cached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
|
||||
CS_Source CreateHttpCamera(const wpi::Twine& name, const wpi::Twine& url,
|
||||
CS_HttpCameraKind kind, CS_Status* status) {
|
||||
auto& inst = Instance::GetInstance();
|
||||
std::shared_ptr<HttpCameraImpl> source;
|
||||
switch (kind) {
|
||||
case CS_HTTP_AXIS:
|
||||
source = std::make_shared<AxisCameraImpl>(name, inst.logger,
|
||||
inst.notifier, inst.telemetry);
|
||||
break;
|
||||
default:
|
||||
source = std::make_shared<HttpCameraImpl>(name, kind, inst.logger,
|
||||
inst.notifier, inst.telemetry);
|
||||
break;
|
||||
}
|
||||
if (!source->SetUrls(url.str(), status)) return 0;
|
||||
return inst.CreateSource(CS_SOURCE_HTTP, source);
|
||||
}
|
||||
|
||||
CS_Source CreateHttpCamera(const wpi::Twine& name,
|
||||
wpi::ArrayRef<std::string> urls,
|
||||
CS_HttpCameraKind kind, CS_Status* status) {
|
||||
auto& inst = Instance::GetInstance();
|
||||
if (urls.empty()) {
|
||||
*status = CS_EMPTY_VALUE;
|
||||
return 0;
|
||||
}
|
||||
auto source = std::make_shared<HttpCameraImpl>(name, kind, inst.logger,
|
||||
inst.notifier, inst.telemetry);
|
||||
if (!source->SetUrls(urls, status)) return 0;
|
||||
return inst.CreateSource(CS_SOURCE_HTTP, source);
|
||||
}
|
||||
|
||||
CS_HttpCameraKind GetHttpCameraKind(CS_Source source, CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || data->kind != CS_SOURCE_HTTP) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return CS_HTTP_UNKNOWN;
|
||||
}
|
||||
return static_cast<HttpCameraImpl&>(*data->source).GetKind();
|
||||
}
|
||||
|
||||
void SetHttpCameraUrls(CS_Source source, wpi::ArrayRef<std::string> urls,
|
||||
CS_Status* status) {
|
||||
if (urls.empty()) {
|
||||
*status = CS_EMPTY_VALUE;
|
||||
return;
|
||||
}
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || data->kind != CS_SOURCE_HTTP) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<HttpCameraImpl&>(*data->source).SetUrls(urls, status);
|
||||
}
|
||||
|
||||
std::vector<std::string> GetHttpCameraUrls(CS_Source source,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || data->kind != CS_SOURCE_HTTP) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return std::vector<std::string>{};
|
||||
}
|
||||
return static_cast<HttpCameraImpl&>(*data->source).GetUrls();
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
extern "C" {
|
||||
|
||||
CS_Source CS_CreateHttpCamera(const char* name, const char* url,
|
||||
CS_HttpCameraKind kind, CS_Status* status) {
|
||||
return cs::CreateHttpCamera(name, url, kind, status);
|
||||
}
|
||||
|
||||
CS_Source CS_CreateHttpCameraMulti(const char* name, const char** urls,
|
||||
int count, CS_HttpCameraKind kind,
|
||||
CS_Status* status) {
|
||||
wpi::SmallVector<std::string, 4> vec;
|
||||
vec.reserve(count);
|
||||
for (int i = 0; i < count; ++i) vec.push_back(urls[i]);
|
||||
return cs::CreateHttpCamera(name, vec, kind, status);
|
||||
}
|
||||
|
||||
CS_HttpCameraKind CS_GetHttpCameraKind(CS_Source source, CS_Status* status) {
|
||||
return cs::GetHttpCameraKind(source, status);
|
||||
}
|
||||
|
||||
void CS_SetHttpCameraUrls(CS_Source source, const char** urls, int count,
|
||||
CS_Status* status) {
|
||||
wpi::SmallVector<std::string, 4> vec;
|
||||
vec.reserve(count);
|
||||
for (int i = 0; i < count; ++i) vec.push_back(urls[i]);
|
||||
cs::SetHttpCameraUrls(source, vec, status);
|
||||
}
|
||||
|
||||
char** CS_GetHttpCameraUrls(CS_Source source, int* count, CS_Status* status) {
|
||||
auto urls = cs::GetHttpCameraUrls(source, status);
|
||||
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]);
|
||||
return out;
|
||||
}
|
||||
|
||||
void CS_FreeHttpCameraUrls(char** urls, int count) {
|
||||
if (!urls) return;
|
||||
for (int i = 0; i < count; ++i) std::free(urls[i]);
|
||||
std::free(urls);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user