mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-26 01:51:41 +00:00
Compare commits
196 Commits
v2024.3.2
...
v2025.0.0-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7173dbd3c | ||
|
|
2ff7033edf | ||
|
|
ca92ef89d3 | ||
|
|
b8c2571638 | ||
|
|
d2b1aa1869 | ||
|
|
76a3a60712 | ||
|
|
f5df6f88c8 | ||
|
|
7f5970b27a | ||
|
|
25865759f4 | ||
|
|
e2893fc1a3 | ||
|
|
b6bd798f9e | ||
|
|
66c0abb732 | ||
|
|
e884221a8d | ||
|
|
3a0ee5c9a7 | ||
|
|
bb8480c690 | ||
|
|
d3aa7f85dd | ||
|
|
3d6b710293 | ||
|
|
ae6954c78f | ||
|
|
a087544933 | ||
|
|
b9935c9885 | ||
|
|
3617a95260 | ||
|
|
b0cc84a9c7 | ||
|
|
e2dcbd016d | ||
|
|
72ae751b9a | ||
|
|
27cbbfe2ab | ||
|
|
65c6306047 | ||
|
|
a0efc9ca31 | ||
|
|
917b5dde66 | ||
|
|
300595da9e | ||
|
|
0606da64c9 | ||
|
|
1c884b2260 | ||
|
|
5b94421b5a | ||
|
|
ad18fa62ee | ||
|
|
5221069bcc | ||
|
|
df4694c9df | ||
|
|
b99d9c1710 | ||
|
|
d6b66bfa55 | ||
|
|
8def7b2222 | ||
|
|
afaf7e2c3f | ||
|
|
5f8c842223 | ||
|
|
bdc42532ed | ||
|
|
21e970f2cd | ||
|
|
38ee6476f2 | ||
|
|
a97904ed1f | ||
|
|
1828fdaaa4 | ||
|
|
7751f6d1d2 | ||
|
|
7c8a36f3eb | ||
|
|
ae8e4289b9 | ||
|
|
c1fc86033a | ||
|
|
9782abbcb1 | ||
|
|
0ad4cd69d0 | ||
|
|
76685fe7e8 | ||
|
|
3b8d8a367b | ||
|
|
a6ac4228c3 | ||
|
|
98fcbdb44e | ||
|
|
958387ddd3 | ||
|
|
a2f2502f70 | ||
|
|
fbfef85f45 | ||
|
|
cdbd64c3df | ||
|
|
eb3635e622 | ||
|
|
9ef6d13b27 | ||
|
|
e2545231b8 | ||
|
|
4252a36668 | ||
|
|
f1e072fc98 | ||
|
|
221d568bd9 | ||
|
|
c62396ce4e | ||
|
|
f42bc45ee8 | ||
|
|
8c107e4b75 | ||
|
|
652c721895 | ||
|
|
7e00f2d3eb | ||
|
|
5c5e5af0c6 | ||
|
|
237ebfd0f2 | ||
|
|
dc0e9712e6 | ||
|
|
d05c7c125b | ||
|
|
294c9946ae | ||
|
|
6220c6be4d | ||
|
|
72a6d22d9a | ||
|
|
8834cb1de4 | ||
|
|
ae655a3a71 | ||
|
|
65f4505e3c | ||
|
|
badd090538 | ||
|
|
2fd8dae503 | ||
|
|
27efd37c52 | ||
|
|
0c822b45ab | ||
|
|
58751338ec | ||
|
|
0b5aec82ff | ||
|
|
820f68dc08 | ||
|
|
8c420fa4c1 | ||
|
|
ab315e24c8 | ||
|
|
0f45fe9486 | ||
|
|
7fc17811fa | ||
|
|
7fbbecb5b7 | ||
|
|
b0d3bf4ddf | ||
|
|
40b35f0d51 | ||
|
|
637647b941 | ||
|
|
0a967e0e62 | ||
|
|
4ce8f3f935 | ||
|
|
178fe99f12 | ||
|
|
305a0657e2 | ||
|
|
fb3e0e1ecb | ||
|
|
2e828ae053 | ||
|
|
d3af27be94 | ||
|
|
4cb2edbb98 | ||
|
|
d88c71ffdc | ||
|
|
6c9dcc157e | ||
|
|
dc00a13d83 | ||
|
|
bdc7344df1 | ||
|
|
a6dd95eb9e | ||
|
|
2563ff9f18 | ||
|
|
f77d01c085 | ||
|
|
408980462f | ||
|
|
2e71e85b8d | ||
|
|
6a73ca8c08 | ||
|
|
9ed2f66914 | ||
|
|
d3060d8eba | ||
|
|
27babe5584 | ||
|
|
7596aeda10 | ||
|
|
c76b358290 | ||
|
|
bad56bcbe8 | ||
|
|
e172aa66f7 | ||
|
|
9c7120e6bf | ||
|
|
0afc35f336 | ||
|
|
ae4bcefefc | ||
|
|
513d1a0a15 | ||
|
|
a85e7693de | ||
|
|
5359112b15 | ||
|
|
7601b7250a | ||
|
|
a9cfd0d0f9 | ||
|
|
c71db8ea9c | ||
|
|
70417f64da | ||
|
|
f5e08652f8 | ||
|
|
eec99eb653 | ||
|
|
9cae707065 | ||
|
|
0f8aa8aedf | ||
|
|
ac32f921f6 | ||
|
|
67fe11f9cd | ||
|
|
1ec089c7f9 | ||
|
|
1727c74b80 | ||
|
|
890185acee | ||
|
|
fd363fdf5a | ||
|
|
1e4a647918 | ||
|
|
39d33bfca6 | ||
|
|
5edc652c05 | ||
|
|
d9eb3691d8 | ||
|
|
a42ffb8fa4 | ||
|
|
d4e6a068ac | ||
|
|
cdfa2ece6f | ||
|
|
962bf7ff10 | ||
|
|
2cd3935aa8 | ||
|
|
427b7dcc11 | ||
|
|
e73050a8fa | ||
|
|
3e6c0d0b71 | ||
|
|
dc4c63568a | ||
|
|
b620b6a4dd | ||
|
|
d7dfa63ae9 | ||
|
|
e89c8c1008 | ||
|
|
abfe2488ff | ||
|
|
3e5187ff32 | ||
|
|
7bc0380694 | ||
|
|
98d2f45fa9 | ||
|
|
d14dfed828 | ||
|
|
f26adc556d | ||
|
|
3c14d87006 | ||
|
|
77536e68f0 | ||
|
|
c88be31ec2 | ||
|
|
74f648689e | ||
|
|
2def62a1ef | ||
|
|
3a5d24ab1d | ||
|
|
02c78bc9b6 | ||
|
|
998340296d | ||
|
|
d26e6d9ecc | ||
|
|
fbb3669546 | ||
|
|
c46847b32a | ||
|
|
33f12f0e31 | ||
|
|
25ad6eafd5 | ||
|
|
84a55d13f3 | ||
|
|
4a548935d3 | ||
|
|
85ea5f8497 | ||
|
|
c32e7db8e3 | ||
|
|
2392c9f278 | ||
|
|
0dbdbb2aac | ||
|
|
b38540e1af | ||
|
|
ff929d4a5f | ||
|
|
b2113d3a9a | ||
|
|
5370f249a1 | ||
|
|
74057543aa | ||
|
|
b4674bacb9 | ||
|
|
fd4424eb89 | ||
|
|
c10f7d91b7 | ||
|
|
0b13459469 | ||
|
|
a1af2357e8 | ||
|
|
38c128fe6a | ||
|
|
9ed0631ec9 | ||
|
|
fb947fe998 | ||
|
|
4f9d73783b | ||
|
|
3b2a2381b6 |
@@ -28,6 +28,11 @@ AlignConsecutiveMacros:
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveShortCaseStatements:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCaseColons: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments:
|
||||
@@ -141,6 +146,7 @@ IntegerLiteralSeparator:
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
KeepEmptyLinesAtEOF: false
|
||||
LambdaBodyIndentation: Signature
|
||||
LineEnding: DeriveLF
|
||||
MacroBlockBegin: ''
|
||||
@@ -201,6 +207,7 @@ RawStringFormats:
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
RemoveParentheses: Leave
|
||||
RemoveSemicolon: false
|
||||
RequiresClausePosition: OwnLine
|
||||
RequiresExpressionIndentation: OuterScope
|
||||
@@ -218,6 +225,7 @@ SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeJsonColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
@@ -232,23 +240,28 @@ SpaceBeforeParensOptions:
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInParens: Never
|
||||
SpacesInParensOptions:
|
||||
InCStyleCasts: false
|
||||
InConditionalStatements: false
|
||||
InEmptyParentheses: false
|
||||
Other: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: c++20
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
VerilogBreakBetweenInstancePorts: true
|
||||
WhitespaceSensitiveMacros:
|
||||
- BOOST_PP_STRINGIZE
|
||||
- CF_SWIFT_NAME
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
color: false
|
||||
definitions: []
|
||||
definitions: [cmake/modules]
|
||||
line_length: 100
|
||||
list_expansion: favour-inlining
|
||||
quiet: false
|
||||
|
||||
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -3,11 +3,10 @@
|
||||
*.h text eol=lf
|
||||
*.inc text eol=lf
|
||||
*.java text eol=lf
|
||||
*.jinja text eol=lf
|
||||
*.json text eol=lf
|
||||
*.md text eol=lf
|
||||
*.xml text eol=lf
|
||||
|
||||
# Generated files
|
||||
hal/src/generated/** linguist-generated
|
||||
ntcore/src/generated/** linguist-generated
|
||||
wpimath/src/generated/** linguist-generated
|
||||
*/src/generated/** linguist-generated
|
||||
|
||||
36
.github/workflows/cmake.yml
vendored
36
.github/workflows/cmake.yml
vendored
@@ -16,11 +16,11 @@ jobs:
|
||||
name: Linux
|
||||
container: wpilib/roborio-cross-ubuntu:2024-22.04
|
||||
flags: "-DCMAKE_BUILD_TYPE=Release -DWITH_EXAMPLES=ON"
|
||||
- os: macOS-12
|
||||
- os: macOS-14
|
||||
name: macOS
|
||||
container: ""
|
||||
env: "PATH=\"/usr/local/opt/protobuf@3/bin:$PATH\""
|
||||
flags: "-DCMAKE_BUILD_TYPE=Release -DWITH_JAVA=OFF -DWITH_EXAMPLES=ON -DCMAKE_LIBRARY_PATH=/usr/local/opt/protobuf@3/lib -DProtobuf_INCLUDE_DIR=/usr/local/opt/protobuf@3/include -DProtobuf_PROTOC_EXECUTABLE=/usr/local/opt/protobuf@3/bin/protoc"
|
||||
env: "PATH=\"/opt/homebrew/opt/protobuf@3/bin:$PATH\""
|
||||
flags: "-DCMAKE_BUILD_TYPE=Release -DWITH_JAVA=OFF -DWITH_EXAMPLES=ON -DCMAKE_LIBRARY_PATH=/opt/homebrew/opt/protobuf@3/lib -DProtobuf_INCLUDE_DIR=/opt/homebrew/opt/protobuf@3/include -DProtobuf_PROTOC_EXECUTABLE=/opt/homebrew/opt/protobuf@3/bin/protoc"
|
||||
|
||||
name: "Build - ${{ matrix.name }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -28,27 +28,14 @@ jobs:
|
||||
steps:
|
||||
- name: Install dependencies (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3 libprotobuf-dev protobuf-compiler ninja-build
|
||||
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java libprotobuf-dev protobuf-compiler ninja-build
|
||||
|
||||
- name: Install QuickBuffers (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf_1.3.3_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.3_amd64.deb
|
||||
|
||||
- name: Install opencv (macOS)
|
||||
- name: Install dependencies (macOS)
|
||||
run: brew install opencv protobuf@3 ninja
|
||||
if: runner.os == 'macOS'
|
||||
|
||||
- name: Set up Python 3.8 (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.3
|
||||
|
||||
- name: Install jinja
|
||||
run: python -m pip install jinja2
|
||||
uses: mozilla-actions/sccache-action@v0.0.5
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -71,21 +58,18 @@ jobs:
|
||||
name: "Build - Windows"
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
- uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
|
||||
- name: Install CMake
|
||||
uses: lukka/get-cmake@v3.27.6
|
||||
uses: lukka/get-cmake@v3.29.3
|
||||
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.3
|
||||
|
||||
- name: Install jinja
|
||||
run: python -m pip install jinja2
|
||||
uses: mozilla-actions/sccache-action@v0.0.5
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run vcpkg
|
||||
uses: lukka/run-vcpkg@v11.1
|
||||
uses: lukka/run-vcpkg@v11.5
|
||||
with:
|
||||
vcpkgDirectory: ${{ runner.workspace }}/vcpkg
|
||||
vcpkgGitCommitId: 37c3e63a1306562f7f59c4c3c8892ddd50fdf992 # HEAD on 2024-02-24
|
||||
|
||||
26
.github/workflows/command-robotpy-pr.yml
vendored
Normal file
26
.github/workflows/command-robotpy-pr.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Comment on PR for robotpy
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
paths:
|
||||
- 'wpilibNewCommands/**'
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Comment on PR
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: 'This PR modifies commands. Please open a corresponding PR in [Python Commands](https://github.com/robotpy/robotpy-commands-v2/) and include a link to this PR.'
|
||||
})
|
||||
8
.github/workflows/comment-command.yml
vendored
8
.github/workflows/comment-command.yml
vendored
@@ -33,17 +33,17 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.COMMENT_COMMAND_PAT_TOKEN }}"
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: '3.10'
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Install wpiformat
|
||||
run: pip3 install wpiformat
|
||||
run: pip3 install wpiformat==2024.34
|
||||
- name: Run wpiformat
|
||||
run: wpiformat
|
||||
- name: Run spotlessApply
|
||||
|
||||
10
.github/workflows/documentation.yml
vendored
10
.github/workflows/documentation.yml
vendored
@@ -22,8 +22,8 @@ jobs:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 13
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Set environment variables (Development)
|
||||
run: |
|
||||
echo "BRANCH=development" >> $GITHUB_ENV
|
||||
@@ -41,11 +41,11 @@ jobs:
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew docs:generateJavaDocs docs:doxygen -PbuildServer ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
- name: Install SSH Client 🔑
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.GH_DEPLOY_KEY }}
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@v4.4.1
|
||||
uses: JamesIves/github-pages-deploy-action@v4.6.1
|
||||
with:
|
||||
ssh-key: true
|
||||
repository-name: wpilibsuite/wpilibsuite.github.io
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
single-commit: true
|
||||
folder: docs/build/docs
|
||||
- name: Trigger Workflow
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.DISPATCH_PAT_TOKEN }}
|
||||
script: |
|
||||
|
||||
@@ -11,4 +11,4 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: gradle/actions/wrapper-validation@v3
|
||||
|
||||
42
.github/workflows/gradle.yml
vendored
42
.github/workflows/gradle.yml
vendored
@@ -61,7 +61,7 @@ jobs:
|
||||
|
||||
build-host:
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: 12
|
||||
MACOSX_DEPLOYMENT_TARGET: 13
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -72,40 +72,34 @@ jobs:
|
||||
task: "build"
|
||||
build-options: "-PciDebugOnly"
|
||||
outputs: "build/allOutputs"
|
||||
build-dir: "c:\\work"
|
||||
- os: windows-2022
|
||||
artifact-name: Win64Release
|
||||
architecture: x64
|
||||
build-options: "-PciReleaseOnly"
|
||||
task: "copyAllOutputs"
|
||||
outputs: "build/allOutputs"
|
||||
build-dir: "c:\\work"
|
||||
- os: windows-2022
|
||||
artifact-name: WinArm64Debug
|
||||
architecture: x64
|
||||
task: "build"
|
||||
build-options: "-PciDebugOnly -Pbuildwinarm64 -Ponlywindowsarm64"
|
||||
outputs: "build/allOutputs"
|
||||
build-dir: "c:\\work"
|
||||
- os: windows-2022
|
||||
artifact-name: WinArm64Release
|
||||
architecture: x64
|
||||
build-options: "-PciReleaseOnly -Pbuildwinarm64 -Ponlywindowsarm64"
|
||||
task: "copyAllOutputs"
|
||||
outputs: "build/allOutputs"
|
||||
build-dir: "c:\\work"
|
||||
- os: macOS-12
|
||||
- os: macOS-14
|
||||
artifact-name: macOS
|
||||
architecture: x64
|
||||
architecture: aarch64
|
||||
task: "build"
|
||||
outputs: "build/allOutputs"
|
||||
build-dir: "."
|
||||
- os: windows-2022
|
||||
artifact-name: Win32
|
||||
architecture: x86
|
||||
task: ":ntcoreffi:build"
|
||||
outputs: "ntcoreffi/build/outputs"
|
||||
build-dir: "c:\\work"
|
||||
name: "Build - ${{ matrix.artifact-name }}"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@@ -114,11 +108,11 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
architecture: ${{ matrix.architecture }}
|
||||
- name: Import Developer ID Certificate
|
||||
uses: wpilibsuite/import-signing-certificate@v1
|
||||
uses: wpilibsuite/import-signing-certificate@v2
|
||||
with:
|
||||
certificate-data: ${{ secrets.APPLE_CERTIFICATE_DATA }}
|
||||
certificate-passphrase: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
@@ -138,21 +132,16 @@ jobs:
|
||||
- name: Set Java Heap Size
|
||||
run: sed -i 's/-Xmx2g/-Xmx1g/g' gradle.properties
|
||||
if: matrix.artifact-name == 'Win32'
|
||||
- name: Configure build directory (Windows)
|
||||
run: xcopy . ${{ matrix.build-dir }} /i /s /e /h /q
|
||||
if: matrix.os == 'windows-2022'
|
||||
- name: Check disk free space (Windows)
|
||||
run: wmic logicaldisk get caption, freespace
|
||||
if: matrix.os == 'windows-2022'
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew ${{ matrix.task }} --build-cache -PbuildServer -PskipJavaFormat ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
working-directory: ${{ matrix.build-dir }}
|
||||
env:
|
||||
ARTIFACTORY_PUBLISH_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PUBLISH_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
- name: Sign Libraries with Developer ID
|
||||
run: ./gradlew copyAllOutputs --build-cache -PbuildServer -PskipJavaFormat -PdeveloperID=${{ secrets.APPLE_DEVELOPER_ID }} ${{ matrix.build-options }} ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
working-directory: ${{ matrix.build-dir }}
|
||||
if: |
|
||||
matrix.artifact-name == 'macOS' && (github.repository_owner == 'wpilibsuite' &&
|
||||
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')))
|
||||
@@ -162,7 +151,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.artifact-name }}
|
||||
path: ${{ matrix.build-dir }}/${{ matrix.outputs }}
|
||||
path: ${{ matrix.outputs }}
|
||||
|
||||
build-documentation:
|
||||
name: "Build - Documentation"
|
||||
@@ -173,7 +162,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Set release environment variable
|
||||
run: echo "EXTRA_GRADLE_ARGS=-PreleaseMode" >> $GITHUB_ENV
|
||||
@@ -235,7 +224,7 @@ jobs:
|
||||
github.repository_owner == 'wpilibsuite' &&
|
||||
(github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Combine (Main)
|
||||
if: |
|
||||
@@ -262,3 +251,18 @@ jobs:
|
||||
with:
|
||||
name: Maven
|
||||
path: ~/releases
|
||||
|
||||
dispatch:
|
||||
name: dispatch
|
||||
needs: [combine]
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: peter-evans/repository-dispatch@v3
|
||||
if: |
|
||||
github.repository_owner == 'wpilibsuite' &&
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
token: ${{ secrets.TOOL_REPO_ACCESS_TOKEN }}
|
||||
repository: wpilibsuite/smartdashboard
|
||||
event-type: tag
|
||||
client-payload: '{"package_name": "allwpilib", "package_version": "${{ github.ref_name }}"}'
|
||||
|
||||
18
.github/workflows/lint-format.yml
vendored
18
.github/workflows/lint-format.yml
vendored
@@ -22,12 +22,12 @@ jobs:
|
||||
run: |
|
||||
git checkout -b pr
|
||||
git branch -f main origin/main
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: '3.10'
|
||||
- name: Install wpiformat
|
||||
run: pip3 install wpiformat==2024.32
|
||||
run: pip3 install wpiformat==2024.34
|
||||
- name: Run
|
||||
run: wpiformat
|
||||
- name: Check output
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
tidy:
|
||||
name: "clang-tidy"
|
||||
runs-on: ubuntu-22.04
|
||||
container: wpilib/roborio-cross-ubuntu:2023-22.04
|
||||
container: wpilib/roborio-cross-ubuntu:2024-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -61,12 +61,12 @@ jobs:
|
||||
git config --global --add safe.directory /__w/allwpilib/allwpilib
|
||||
git checkout -b pr
|
||||
git branch -f main origin/main
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: '3.10'
|
||||
- name: Install wpiformat
|
||||
run: pip3 install wpiformat
|
||||
run: pip3 install wpiformat==2024.34
|
||||
- name: Create compile_commands.json
|
||||
run: |
|
||||
./gradlew generateCompileCommands -Ptoolchain-optional-roboRio
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
java-version: 21
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew docs:zipDocs -PbuildServer -PdocWarningsAsErrors ${{ env.EXTRA_GRADLE_ARGS }}
|
||||
|
||||
96
.github/workflows/pregenerate.yml
vendored
96
.github/workflows/pregenerate.yml
vendored
@@ -1,46 +1,50 @@
|
||||
name: Check Pregenerated Files
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches-ignore:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update:
|
||||
name: "Update"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install jinja
|
||||
run: python -m pip install jinja2
|
||||
- name: Install protobuf dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler && wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf-1.3.3-linux-x86_64.exe && chmod +x protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
|
||||
- name: Run hal
|
||||
run: ./hal/generate_usage_reporting.py
|
||||
- name: Run ntcore
|
||||
run: ./ntcore/generate_topics.py
|
||||
- name: Run wpimath
|
||||
run: ./wpimath/generate_numbers.py && ./wpimath/generate_quickbuf.py protoc protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
|
||||
- name: Add untracked files to index so they count as changes
|
||||
run: git add -A
|
||||
- name: Check output
|
||||
run: git --no-pager diff --exit-code HEAD
|
||||
- name: Generate diff
|
||||
run: git diff HEAD > pregenerated-files-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pregenerated-files-fixes
|
||||
path: pregenerated-files-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
name: Check Pregenerated Files
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches-ignore:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update:
|
||||
name: "Update"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install jinja
|
||||
run: python -m pip install jinja2
|
||||
- name: Install protobuf dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler && wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf-1.3.3-linux-x86_64.exe && chmod +x protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
|
||||
- name: Run hal
|
||||
run: ./hal/generate_usage_reporting.py
|
||||
- name: Run ntcore
|
||||
run: ./ntcore/generate_topics.py
|
||||
- name: Run wpimath
|
||||
run: ./wpimath/generate_numbers.py && ./wpimath/generate_quickbuf.py --quickbuf_plugin=protoc-gen-quickbuf-1.3.3-linux-x86_64.exe
|
||||
- name: Run HIDs
|
||||
run: ./wpilibj/generate_hids.py && ./wpilibc/generate_hids.py && ./wpilibNewCommands/generate_hids.py
|
||||
- name: Run PWM Controllers
|
||||
run: ./wpilibj/generate_pwm_motor_controllers.py && ./wpilibc/generate_pwm_motor_controllers.py
|
||||
- name: Add untracked files to index so they count as changes
|
||||
run: git add -A
|
||||
- name: Check output
|
||||
run: git --no-pager diff --exit-code HEAD
|
||||
- name: Generate diff
|
||||
run: git diff HEAD > pregenerated-files-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pregenerated-files-fixes
|
||||
path: pregenerated-files-fixes.patch
|
||||
if: ${{ failure() }}
|
||||
|
||||
11
.github/workflows/sanitizers.yml
vendored
11
.github/workflows/sanitizers.yml
vendored
@@ -29,17 +29,10 @@ jobs:
|
||||
container: wpilib/roborio-cross-ubuntu:2024-22.04
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java python-is-python3 clang-14 libprotobuf-dev protobuf-compiler ninja-build
|
||||
|
||||
- name: Install QuickBuffers
|
||||
if: runner.os == 'Linux'
|
||||
run: wget https://github.com/HebiRobotics/QuickBuffers/releases/download/1.3.3/protoc-gen-quickbuf_1.3.3_amd64.deb && sudo apt install ./protoc-gen-quickbuf_1.3.3_amd64.deb
|
||||
run: sudo apt-get update && sudo apt-get install -y libopencv-dev libopencv4.5-java clang-14 libprotobuf-dev protobuf-compiler ninja-build
|
||||
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.3
|
||||
|
||||
- name: Install jinja
|
||||
run: python -m pip install jinja2
|
||||
uses: mozilla-actions/sccache-action@v0.0.5
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
144
.github/workflows/tools.yml
vendored
Normal file
144
.github/workflows/tools.yml
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
name: Tools
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
YEAR: 2024
|
||||
|
||||
jobs:
|
||||
build-artifacts:
|
||||
name: "Build - WPILib"
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DISPLAY: ':10'
|
||||
steps:
|
||||
- name: Free Disk Space
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
tool-cache: false
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: false
|
||||
docker-images: false
|
||||
swap-storage: false
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build WPILib with Gradle
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: wpilib/roborio-cross-ubuntu:2024-22.04
|
||||
options: -v ${{ github.workspace }}:/work -w /work -e GITHUB_REF -e CI -e DISPLAY
|
||||
run: df . && rm -f semicolon_delimited_script && ./gradlew :wpilibc:publish :wpilibj:publish :wpilibNewCommands:publish :hal:publish :cameraserver:publish :ntcore:publish :cscore:publish :wpimath:publish :wpinet:publish :wpiutil:publish :apriltag:publish :wpiunits:publish :simulation:halsim_gui:publish :simulation:halsim_ds_socket:publish -x test -x Javadoc -x doxygen --build-cache && cp -r /root/releases/maven/development /work
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MavenArtifacts
|
||||
path: |
|
||||
development
|
||||
retention-days: 1
|
||||
|
||||
Robotbuilder:
|
||||
name: "Build - RobotBuilder"
|
||||
needs: [build-artifacts]
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DISPLAY: ':10'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: wpilibsuite/robotbuilder
|
||||
fetch-depth: 0
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: MavenArtifacts
|
||||
- name: Patch RobotBuilder to use local development
|
||||
run: cd src/main/resources/export && echo "wpi.maven.useLocal = false" >> java/build.gradle && echo "wpi.maven.useFrcMavenLocalDevelopment = true" >> java/build.gradle && echo "wpi.versions.wpilibVersion = '$YEAR.424242.+'" >> java/build.gradle && echo "wpi.versions.wpimathVersion = '$YEAR.424242.+'" >> java/build.gradle && echo "wpi.maven.useLocal = false" >> cpp/build.gradle && echo "wpi.maven.useFrcMavenLocalDevelopment = true" >> cpp/build.gradle && echo "wpi.versions.wpilibVersion = '$YEAR.424242.+'" >> cpp/build.gradle && echo "wpi.versions.wpimathVersion = '$YEAR.424242.+'" >> cpp/build.gradle
|
||||
- name: Install and run xvfb
|
||||
run: sudo apt-get update && sudo apt-get install -y xvfb && Xvfb $DISPLAY &
|
||||
- name: Move artifacts
|
||||
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
- name: Build RobotBuilder with Gradle
|
||||
run: ./gradlew build test --tests 'robotbuilder.exporters.*' -x htmlSanityCheck -PbuildServer -PreleaseMode ; cat build/test-results/test/TEST-robotbuilder.exporters.*.xml ;
|
||||
- name: Summarize RobotBuilder Test Results
|
||||
uses: EnricoMi/publish-unit-test-result-action@v2
|
||||
if: always()
|
||||
with:
|
||||
files: |
|
||||
build/test-results/test/TEST*.xml
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: TestResults
|
||||
path: |
|
||||
build/reports/
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RobotBuilder Build
|
||||
path: |
|
||||
build/libs/
|
||||
retention-days: 7
|
||||
|
||||
Shuffleboard:
|
||||
name: "Build - Shuffleboard"
|
||||
needs: [build-artifacts]
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: wpilibsuite/shuffleboard
|
||||
fetch-depth: 0
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: MavenArtifacts
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
- name: Move artifacts
|
||||
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get install -y libgtk2.0-0
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build -x Javadoc
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Shuffleboard Build
|
||||
path: |
|
||||
build/allOutputs/
|
||||
retention-days: 7
|
||||
|
||||
PathWeaver:
|
||||
name: "Build - PathWeaver"
|
||||
needs: [build-artifacts]
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: wpilibsuite/PathWeaver
|
||||
fetch-depth: 0
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: MavenArtifacts
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
- name: Move artifacts
|
||||
run: mkdir -p ~/releases/maven/development && cp -r edu ~/releases/maven/development
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: PathWeaver Build
|
||||
path: |
|
||||
build/allOutputs/
|
||||
retention-days: 7
|
||||
4
.github/workflows/upstream-utils.yml
vendored
4
.github/workflows/upstream-utils.yml
vendored
@@ -70,6 +70,10 @@ jobs:
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./update_protobuf.py
|
||||
- name: Run update_sleipnir.py
|
||||
run: |
|
||||
cd upstream_utils
|
||||
./update_sleipnir.py
|
||||
- name: Add untracked files to index so they count as changes
|
||||
run: git add -A
|
||||
- name: Check output
|
||||
|
||||
@@ -20,6 +20,7 @@ generatedFileExclude {
|
||||
simulation/gz_msgs/src/include/simulation/gz_msgs/msgs\.h$
|
||||
fieldImages/src/main/native/resources/
|
||||
apriltag/src/test/resources/
|
||||
wpilibc/src/generated/
|
||||
}
|
||||
|
||||
repoRootNameOverride {
|
||||
|
||||
@@ -64,7 +64,7 @@ option(WITH_CSCORE "Build cscore (needs OpenCV)" ON)
|
||||
option(WITH_NTCORE "Build ntcore" ON)
|
||||
option(WITH_WPIMATH "Build wpimath" ON)
|
||||
option(WITH_WPIUNITS "Build wpiunits" ON)
|
||||
option(WITH_WPILIB "Build hal, wpilibc/j, and myRobot (needs OpenCV)" ON)
|
||||
option(WITH_WPILIB "Build hal, wpilibc/j, and developerRobot (needs OpenCV)" ON)
|
||||
option(WITH_EXAMPLES "Build examples" OFF)
|
||||
option(WITH_TESTS "Build unit tests (requires internet connection)" ON)
|
||||
option(WITH_GUI "Build GUI items" ON)
|
||||
@@ -198,6 +198,9 @@ endif()
|
||||
set(include_dest include)
|
||||
set(java_lib_dest java)
|
||||
set(jni_lib_dest jni)
|
||||
if(WITH_JAVA)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
|
||||
endif()
|
||||
|
||||
if(USE_SYSTEM_LIBUV)
|
||||
set(LIBUV_SYSTEM_REPLACE
|
||||
@@ -284,7 +287,7 @@ set(CMAKE_EXE_LINKER_FLAGS_ASAN
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_ASAN
|
||||
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=address"
|
||||
CACHE STRING
|
||||
"Linker lags to be used to create shared libraries for Asan build type."
|
||||
"Linker flags to be used to create shared libraries for Asan build type."
|
||||
FORCE
|
||||
)
|
||||
|
||||
@@ -312,7 +315,7 @@ set(CMAKE_EXE_LINKER_FLAGS_TSAN
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_TSAN
|
||||
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=thread"
|
||||
CACHE STRING
|
||||
"Linker lags to be used to create shared libraries for Tsan build type."
|
||||
"Linker flags to be used to create shared libraries for Tsan build type."
|
||||
FORCE
|
||||
)
|
||||
|
||||
@@ -340,7 +343,7 @@ set(CMAKE_EXE_LINKER_FLAGS_UBSAN
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_UBSAN
|
||||
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=undefined"
|
||||
CACHE STRING
|
||||
"Linker lags to be used to create shared libraries for Ubsan build type."
|
||||
"Linker flags to be used to create shared libraries for Ubsan build type."
|
||||
FORCE
|
||||
)
|
||||
|
||||
@@ -405,7 +408,7 @@ if(WITH_WPILIB)
|
||||
if(WITH_EXAMPLES)
|
||||
add_subdirectory(wpilibcExamples)
|
||||
endif()
|
||||
add_subdirectory(myRobot)
|
||||
add_subdirectory(developerRobot)
|
||||
endif()
|
||||
|
||||
if(WITH_SIMULATION_MODULES AND NOT WITH_EXTERNAL_HAL)
|
||||
|
||||
@@ -80,6 +80,8 @@ xₖ₊₁ = Axₖ + Buₖ
|
||||
|
||||
Changes should be submitted as a Pull Request against the main branch of WPILib. For most changes, commits will be squashed upon merge. For particularly large changes, multiple commits are ok, but assume one commit unless asked otherwise. We may ask you to break a PR into multiple standalone PRs or commits for rebase within one PR to separate unrelated changes. No change will be merged unless it is up to date with the current main branch. We do this to make sure that the git history isn't too cluttered.
|
||||
|
||||
During the build season, breaking changes or other changes intended for the next season can be created as a pull request against the development branch of WPILib. After the season is over, the changes in the development branch will be merged into main.
|
||||
|
||||
### Merge Process
|
||||
|
||||
When you first submit changes, GitHub Actions will attempt to run `./gradlew check` on your change. If this fails, you will need to fix any issues that it sees. Once Actions passes, we will begin the review process in more earnest. One or more WPILib team members will review your change. This will be a back-and-forth process with the WPILib team and the greater community. Once we are satisfied that your change is ready, we will allow our hosted instance to test it. This will run the full gamut of checks, including integration tests on actual hardware. Once all tests have passed and the team is satisfied, we will merge your change into the WPILib repository.
|
||||
|
||||
@@ -19,7 +19,7 @@ To build a project using a development build, find the build.gradle file and ope
|
||||
wpi.maven.useLocal = false
|
||||
wpi.maven.useDevelopment = true
|
||||
wpi.versions.wpilibVersion = 'YEAR.+'
|
||||
wpi.versions.wpimathVersion = 'YEAR.+
|
||||
wpi.versions.wpimathVersion = 'YEAR.+'
|
||||
```
|
||||
|
||||
The top of your ``build.gradle`` file should now look similar to the code below. Ignore any differences in versions.
|
||||
@@ -89,15 +89,4 @@ wpi.versions.wpimathVersion = 'YEAR.424242.+'
|
||||
|
||||
# roboRIO Development
|
||||
|
||||
This repo contains a myRobot project built in way to do full project development without needing to do a full publish into GradleRIO. These also only require building the minimum amount of binaries for the roboRIO, so the builds are much faster as well.
|
||||
|
||||
The setup only works if the roboRIO is USB connected. If an alternate IP address is preferred, the `address` block in myRobot\build.gradle can be changed to point to another address.
|
||||
|
||||
The following 3 tasks can be used for deployment:
|
||||
* `:myRobot:deployShared` deploys the C++ project using shared dependencies. Prefer this one for most C++ development.
|
||||
* `:myRobot:deployStatic` deploys the C++ project with all dependencies statically linked.
|
||||
* `:myRobot:deployJava` deploys the Java project and all required dependencies. Also installs the JRE if not currently installed.
|
||||
|
||||
Deploying any of these to the roboRIO will disable the current startup project until it is redeployed.
|
||||
|
||||
From here, ssh into the roboRIO using the `lvuser` account and run `frcRunRobot.sh` (It's in path).
|
||||
See the [developerRobot](developerRobot/README.md) subproject.
|
||||
|
||||
@@ -18,7 +18,7 @@ Desktop tools publish to the development repo on every push to main. To publish
|
||||
|
||||
## Publishing VS Code
|
||||
Before publishing, make sure to update the gradlerio version in `vscode-wpilib/resources/gradle/version.txt` Also make sure the gradle wrapper version matches the wrapper required by gradlerio.
|
||||
Upon pushing a tag, a release will be built, and the files will be uploaded to the releases on GitHub. For publishing to the marketplace, you need a Microsoft account and to be added as a maintainer.
|
||||
Upon pushing a tag, a release will be built, and the files will be uploaded to the releases on GitHub.
|
||||
|
||||
## Publishing GradleRIO
|
||||
Before publishing, make sure to update the version in build.gradle. Publishing must happen locally, using the command `./gradlew publishPlugin`. This does require your API key for publishing to be set.
|
||||
|
||||
151
README-CMAKE.md
151
README-CMAKE.md
@@ -1,31 +1,41 @@
|
||||
# WPILib CMake Support
|
||||
|
||||
WPILib is normally built with Gradle, however for some systems, such as Linux based coprocessors, Gradle doesn't work correctly, especially if cscore is needed, which requires OpenCV. Furthermore, the CMake build can be used for C++ development because it provides better build caching compared to Gradle. We provide the CMake build for these cases. Although it is supported on Windows, these docs will only go over Linux builds.
|
||||
WPILib is normally built with Gradle, however for some systems, such as Linux based coprocessors, Gradle doesn't work correctly, especially if cscore is needed, which requires OpenCV. Furthermore, the CMake build can be used for C++ development because it provides better build caching compared to Gradle. We provide the CMake build for these cases. Although macOS is supported, these docs will only go over Linux and Windows builds, but should mostly work for macOS as well. If you are stuck, you can look at the GitHub workflows for any OS to see how it works.
|
||||
|
||||
## Libraries that get built
|
||||
* wpiutil
|
||||
* ntcore
|
||||
* cscore
|
||||
* apriltag
|
||||
* cameraserver
|
||||
* hal
|
||||
* wpilib
|
||||
* halsim
|
||||
* cscore
|
||||
* hal (simulation HAL only)
|
||||
* ntcore
|
||||
* romiVendordep
|
||||
* simulation extensions
|
||||
* wpigui
|
||||
* wpimath
|
||||
* wpiunits
|
||||
* wpilib (wpilibc, wpilibj, and myRobot)
|
||||
* wpilibNewCommands
|
||||
* wpimath
|
||||
* wpinet
|
||||
* wpiunits
|
||||
* wpiutil
|
||||
* xrpVendordep
|
||||
|
||||
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.
|
||||
## GUI apps that get built
|
||||
* datalogtool
|
||||
* glass
|
||||
* outlineviewer
|
||||
* roborioteamnumbersetter
|
||||
* sysid
|
||||
* halsim_gui (if simulation extensions are enabled)
|
||||
|
||||
By default, all libraries 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. Data Log Tool and the roboRIO Team Number Setter are only built if libssh is available.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The jinja2 pip package is needed to generate classes for NT4's pubsub.
|
||||
|
||||
The protobuf library and compiler are needed for protobuf generation. The QuickBuffers protoc-gen package is also required when Java is being built; this can be obtained from https://github.com/HebiRobotics/QuickBuffers/releases/.
|
||||
The protobuf library and compiler are needed for protobuf generation.
|
||||
|
||||
OpenCV needs to be findable by CMake. On systems like the Jetson, this is installed by default. Otherwise, you will need to build OpenCV from source and install it.
|
||||
|
||||
If you want JNI and Java, you will need a JDK of at least version 11 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the JDK directory.
|
||||
If you want JNI and Java, you will need a JDK of at least version 17 installed. In addition, you need a `JAVA_HOME` environment variable set properly and set to the JDK directory.
|
||||
|
||||
If you are building with unit tests or simulation modules, you will also need an Internet connection for the initial setup process, as CMake will clone google-test and imgui from GitHub.
|
||||
|
||||
@@ -33,34 +43,34 @@ If you are building with unit tests or simulation modules, you will also need an
|
||||
|
||||
The following build options are available:
|
||||
|
||||
* `WITH_JAVA` (ON Default)
|
||||
* This option will enable Java and JNI builds. If this is on, `WITH_SHARED_LIBS` must be on. Otherwise CMake will error.
|
||||
* `WITH_JAVA_SOURCE` (ON Default)
|
||||
* This option will build Java source JARs for each enabled Java library. This does not require `WITH_JAVA` to be on, allowing source JARs to be built without the compiled JARs if desired.
|
||||
* `WITH_SHARED_LIBS` (ON Default)
|
||||
* This option will cause cmake to build static libraries instead of shared libraries. If this is off, `WITH_JAVA` must be off. Otherwise CMake will error.
|
||||
* `BUILD_SHARED_LIBS` (ON Default)
|
||||
* This option will cause CMake to build static libraries instead of shared libraries. If this is off, `WITH_JAVA` must be off. Otherwise CMake will error.
|
||||
* `WITH_CSCORE` (ON Default)
|
||||
* This option will cause cscore to be built. Turning this off will implicitly disable cameraserver, the hal and wpilib as well, irrespective of their specific options. If this is off, the OpenCV build requirement is removed.
|
||||
* This option will cause cscore to be built. Turning this off will implicitly disable cameraserver. If this is off, the OpenCV build requirement is removed.
|
||||
* `WITH_EXAMPLES` (OFF Default)
|
||||
* This option will build C++ examples.
|
||||
* `WITH_GUI` (ON Default)
|
||||
* This option will build GUI items. If this is off, and `WITH_SIMULATION_MODULES` is on, the simulation GUI will not be built.
|
||||
* `WITH_JAVA` (ON Default)
|
||||
* This option will enable Java and JNI builds. If this is on, `BUILD_SHARED_LIBS` must be on. Otherwise CMake will error.
|
||||
* `WITH_JAVA_SOURCE` (`WITH_JAVA` Default)
|
||||
* This option will build Java source JARs for each enabled Java library. This does not require `WITH_JAVA` to be on, allowing source JARs to be built without the compiled JARs if desired.
|
||||
* `WITH_NTCORE` (ON Default)
|
||||
* This option will cause ntcore to be built. Turning this off will implicitly disable wpinet and wpilib as well, irrespective of their specific options.
|
||||
* This option will cause ntcore to be built. Turning this off will implicitly disable wpinet, and will cause an error if `WITH_WPILIB` is enabled.
|
||||
* `WITH_SIMULATION_MODULES` (ON Default)
|
||||
* This option will build simulation modules.
|
||||
* `WITH_TESTS` (ON Default)
|
||||
* This option will build C++ unit tests. These can be run via `ctest -C <config>`, where `<config>` is the build configuration, e.g. `Debug` or `Release`.
|
||||
* `WITH_WPILIB` (ON Default)
|
||||
* This option will build the HAL and wpilibc/j during the build. The HAL is the simulation HAL, unless the external HAL options are used. The CMake build has no capability to build for the roboRIO.
|
||||
* `WITH_WPIMATH` (ON Default)
|
||||
* This option will build the wpimath library. This option must be on to build wpilib.
|
||||
* `WITH_WPIUNITS` (ON Default)
|
||||
* This option will build the wpiunits library. This option must be on to build the Java wpimath library and requires `WITH_JAVA` to also be on.
|
||||
* `WITH_WPILIB` (ON Default)
|
||||
* This option will build the hal and wpilibc/j 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.
|
||||
* `WITH_EXAMPLES` (ON Default)
|
||||
* This option will build C++ examples.
|
||||
* `WITH_TESTS` (ON Default)
|
||||
* This option will build C++ unit tests. These can be run via `make test`.
|
||||
* `WITH_GUI` (ON Default)
|
||||
* This option will build GUI items.
|
||||
* `WITH_SIMULATION_MODULES` (ON Default)
|
||||
* This option will build simulation modules, including wpigui and the HALSim plugins.
|
||||
* `WITH_EXTERNAL_HAL` (OFF Default)
|
||||
* TODO
|
||||
* This option will build wpilib with an externally built HAL.
|
||||
* `EXTERNAL_HAL_FILE`
|
||||
* TODO
|
||||
* Set this option to the CMake File of the externally built HAL. NOTE: set it to the file itself, not the folder the file is located in!
|
||||
* `OPENCV_JAVA_INSTALL_DIR`
|
||||
* Set this option to the location of the archive of the OpenCV Java bindings (it should be called opencv-xxx.jar, with the x'es being version numbers). NOTE: set it to the LOCATION of the file, not the file itself!
|
||||
* `NO_WERROR` (OFF Default)
|
||||
@@ -78,24 +88,28 @@ 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. [Here](https://cmake.org/cmake/help/v3.0/manual/ccmake.1.html) is the link to the documentation for that program.
|
||||
If you want, you can also use `ccmake` in order to visually set these properties as well. [Here](https://cmake.org/cmake/help/v3.0/manual/ccmake.1.html) is the link to the documentation for that program. On Windows, you can use `cmake-gui` instead.
|
||||
|
||||
Note that if you are cross-compiling, you will need to override the protobuf options manually to point to the libraries for the target platform. Leave the protoc binary location as the path to the binary for the host platform, since protoc needs to execute on the host platform.
|
||||
|
||||
## 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.
|
||||
Once you have CMake setup. run `cmake --build .` from the directory you configured CMake in. This will build all libraries possible. We recommend running `cmake --build .` with multiple jobs. For allwpilib, a good rule of thumb is one worker for every 2 GB of available RAM. To run a multiple job build, run the following command with x being the number of jobs you want.
|
||||
|
||||
```
|
||||
make -jx
|
||||
cmake --build . --parallel x
|
||||
```
|
||||
|
||||
The `ninja` generator is also supported, and can be enabled by passing `-GNinja` to the initial `cmake` command.
|
||||
Note: wpimath takes gigabytes of RAM to compile. Because of this, the compilers may crash while building due to a lack of memory and your computer may slow down. If you have less than 16 GB of RAM available, you may want to consider building it separately first by adding `--target wpimath` and running it with ~3 jobs to prevent crashes from running out of memory.
|
||||
|
||||
To build with a certain configuration, like `Debug` or `Release`, add `--config <config>`, where `<config>` is the name of the configuration you want to build with.
|
||||
|
||||
## 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.
|
||||
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
|
||||
sudo cmake --build . --target install
|
||||
```
|
||||
|
||||
## Using the installed libraries for C++.
|
||||
@@ -104,7 +118,7 @@ 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
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
project(vision_app) # Project Name Here
|
||||
|
||||
@@ -114,7 +128,7 @@ add_executable(my_vision_app main.cpp) # executable 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.
|
||||
If you want to use other libraries or are building a robot program, `wpilibc` and `hal` should be added in the `target_link_libraries` function, along with any other libraries you plan on using, e.g. `wpimath`.
|
||||
|
||||
Add a `main.cpp` file to contain your code, and create a build folder. Move into the build folder, and run
|
||||
|
||||
@@ -122,11 +136,43 @@ Add a `main.cpp` file to contain your code, and create a build folder. Move into
|
||||
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.
|
||||
After that, run `cmake --build .`. 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
|
||||
|
||||
Using the built JARs is move involved than using the C++ libraries, but mostly consists of adding the correct directories to PATH.
|
||||
|
||||
Add the directory where the JARs are located (e.g, `/usr/local/java`) to PATH. If you are on Windows, you also need to add the `lib`, `bin`, and `share` directories to PATH. Then, create a new folder to contain your project. Add the following code below to a `CMakeLists.txt` file in that directory.
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
project(robot)
|
||||
|
||||
find_package(Java REQUIRED COMPONENTS Development)
|
||||
include(UseJava)
|
||||
find_package(wpilib REQUIRED)
|
||||
find_jar(opencvJar opencv-xxx PATHS ENV PATH) # Change to OpenCV version
|
||||
|
||||
file(GLOB_RECURSE JAVA_SOURCES *.java)
|
||||
# If you want Gradle compatibility or you are using one of the templates/examples, comment out the above line and uncomment this line instead:
|
||||
# file(GLOB_RECURSE JAVA_SOURCES src/main/java/*.java)
|
||||
add_jar(robot ${JAVA_SOURCES}
|
||||
INCLUDE_JARS apriltag_jar cscore_jar hal_jar ntcore_jar wpilibNewCommands_jar wpimath_jar wpinet_jar wpiutil_jar wpiunits_jar wpilibj_jar ${opencvJar})
|
||||
export_jars(TARGETS robot FILE robot.jar)
|
||||
```
|
||||
This includes all the built JARs except for the vendordeps. If you are not using a JAR/library, you may remove it.
|
||||
Add a `Main.java` 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 `cmake --build .` to create your JAR file. To execute the JAR file, you need to include the wpilib JARs and your JAR in the classpath, and execute your Java program's entry point. If you are using cscore or cameraserver, you also need to include the path to the OpenCV JAR. If you built it from source, it will be in your OpenCV build directory. If it's installed on the system, CMake may find it from PATH, but you will likely need to locate the JAR and manually give CMake the JAR directory. If you are on Linux, you will also need to add the path of the libraries to `LD_LIBRARY_PATH`. This can be done by prepending `LD_LIBRARY_PATH=/path/to/libraries` to the Java command. If you need to add more paths, separate them with colons. The final command should look like `java -cp "robot.jar:/path/to/library_jars/*" main.package.Main`, using a semicolon to separate paths instead of a colon if you are on Windows. If you are on Linux, the final command should look more like `LD_LIBRARY_PATH=/path/to/libraries java -cp robot.jar:/path/to/library_jars/* main.package.Main`.
|
||||
|
||||
## Using vendordeps
|
||||
|
||||
Vendordeps are not included as part of the `wpilib` CMake package. However, if you want to use a vendordep, you need to use `find_package(VENDORDEP)`, where `VENDORDEP` is the name of the vendordep (case-sensitive), like `xrpVendordep` or `romiVenderdep`. Note that wpilibNewCommands, while a vendordep in normal robot projects, is not built as a vendordep in CMake, and is instead included as part of the `wpilib` CMake package. After you used `find_package`, you can reference the vendordep library like normal, either by using `target_link_libraries` for C++ or `add_jar` for Java.
|
||||
|
||||
## Troubleshooting
|
||||
Below are some common issues that are run into when building.
|
||||
@@ -153,7 +199,7 @@ CMake Error at cscore/CMakeLists.txt:3 (find_package):
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -167,3 +213,18 @@ CMake Error at /usr/share/cmake-3.5/Modules/FindPackageHandleStandardArgs.cmake:
|
||||
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 `-DWITH_JAVA=OFF`.
|
||||
|
||||
#### Java: Can't find dependent libraries
|
||||
|
||||
If one of the libraries can't be found, you will get an error similar to this one:
|
||||
|
||||
```
|
||||
java.io.IOException: wpiHaljni could not be loaded from path or an embedded resource.
|
||||
attempted to load for platform /windows/x86-64/
|
||||
Last Load Error:
|
||||
C:\Program Files (x86)\allwpilib\bin\wpiHaljni.dll: Can't find dependent libraries
|
||||
```
|
||||
|
||||
If you get this error, that's usually an indication that not all your libraries are in your PATH. The two libraries that should be in your PATH are OpenCV and protobuf. If the error is coming from cscore, it's likely you're missing OpenCV. Otherwise, it's likely you're missing protobuf.
|
||||
|
||||
Note that Linux will not have this specific type of error, as it will usually tell you the dependent library you are missing. In that case, you most likely need to add the library to `LD_LIBRARY_PATH`.
|
||||
|
||||
18
README.md
18
README.md
@@ -20,11 +20,11 @@ Welcome to the WPILib project. This repository contains the HAL, WPILibJ, and WP
|
||||
- [Running examples in simulation](#running-examples-in-simulation)
|
||||
- [Publishing](#publishing)
|
||||
- [Structure and Organization](#structure-and-organization)
|
||||
- [Contributing to WPILib](#contributing-to-wpilib)
|
||||
- [Contributing to WPILib](./CONTRIBUTING.md)
|
||||
|
||||
## WPILib Mission
|
||||
|
||||
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.md).
|
||||
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++, Python, 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.md).
|
||||
|
||||
# Quick Start
|
||||
|
||||
@@ -41,11 +41,11 @@ Using Gradle makes building WPILib very straightforward. It only has a few depen
|
||||
|
||||
## Requirements
|
||||
|
||||
- [JDK 11](https://adoptium.net/temurin/releases/?version=11)
|
||||
- [JDK 17](https://adoptium.net/temurin/releases/?version=17)
|
||||
- Note that the JRE is insufficient; the full JDK is required
|
||||
- On Ubuntu, run `sudo apt install openjdk-11-jdk`
|
||||
- On Windows, install the JDK 11 .msi from the link above
|
||||
- On macOS, install the JDK 11 .pkg from the link above
|
||||
- On Ubuntu, run `sudo apt install openjdk-17-jdk`
|
||||
- On Windows, install the JDK 17 .msi from the link above
|
||||
- On macOS, install the JDK 17 .pkg from the link above
|
||||
- C++ compiler
|
||||
- On Linux, install GCC 11 or greater
|
||||
- On Windows, install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/) and select the C++ programming language during installation (Gradle can't use the build tools for Visual Studio)
|
||||
@@ -60,7 +60,7 @@ On macOS ARM, run `softwareupdate --install-rosetta`. This is necessary to be ab
|
||||
|
||||
## Setup
|
||||
|
||||
Clone the WPILib repository and follow the instructions above for installing any required tooling.
|
||||
Clone the WPILib repository and follow the instructions above for installing any required tooling. The build process uses versioning information from git. Downloading the source is not sufficient to run the build.
|
||||
|
||||
See the [styleguide README](https://github.com/wpilibsuite/styleguide/blob/main/README.md) for wpiformat setup instructions.
|
||||
|
||||
@@ -179,7 +179,3 @@ The hal directory contains more C++ code meant to run on the roboRIO. HAL is an
|
||||
The upstream_utils directory contains scripts for updating copies of thirdparty code in the repository.
|
||||
|
||||
The [styleguide repository](https://github.com/wpilibsuite/styleguide) contains our style guides for C++ and Java code. Anything submitted to the WPILib project needs to follow the code style guides outlined in there. For details about the style, please see the contributors document [here](CONTRIBUTING.md#coding-guidelines).
|
||||
|
||||
# Contributing to WPILib
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
@@ -43,6 +43,7 @@ Team 254 Library wpilibj/src/main/java/edu/wpi/first/wpilibj/spline/SplineP
|
||||
Portable File Dialogs wpigui/src/main/native/include/portable-file-dialogs.h
|
||||
V8 export-template wpiutil/src/main/native/include/wpi/SymbolExports.h
|
||||
GCEM wpimath/src/main/native/thirdparty/gcem/include/
|
||||
Sleipnir wpimath/src/main/native/thirdparty/sleipnir
|
||||
|
||||
==============================================================================
|
||||
Google Test License
|
||||
@@ -1209,3 +1210,18 @@ limitations under the License.
|
||||
2024 Field Image
|
||||
================
|
||||
2024 Field Image from MikLast: https://www.chiefdelphi.com/t/2024-crescendo-top-down-field-renders/447764
|
||||
|
||||
================
|
||||
Sleipnir License
|
||||
================
|
||||
Copyright (c) Sleipnir contributors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Neither the name of the copyright holder 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 HOLDER 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.
|
||||
|
||||
@@ -7,7 +7,7 @@ include(FetchContent)
|
||||
fetchcontent_declare(
|
||||
apriltaglib
|
||||
GIT_REPOSITORY https://github.com/wpilibsuite/apriltag.git
|
||||
GIT_TAG 64be6ab26abf5e995321997fd0752c609a7e30f4
|
||||
GIT_TAG da208cc38c1b78fe89861616d44c0692e76b6b8b
|
||||
)
|
||||
|
||||
# Don't use apriltag's CMakeLists.txt due to conflicting naming and JNI
|
||||
@@ -30,7 +30,6 @@ if(WITH_JAVA)
|
||||
find_package(Java REQUIRED)
|
||||
find_package(JNI REQUIRED)
|
||||
include(UseJava)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
|
||||
|
||||
set(CMAKE_JNI_TARGET true)
|
||||
|
||||
@@ -43,6 +42,8 @@ if(WITH_JAVA)
|
||||
${OPENCV_JAVA_INSTALL_DIR}
|
||||
${OpenCV_INSTALL_PATH}/bin
|
||||
${OpenCV_INSTALL_PATH}/share/java
|
||||
${OpenCV_INSTALL_PATH}/share/java/opencv4
|
||||
${OpenCV_INSTALL_PATH}/share/OpenCV/java
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
@@ -63,6 +64,7 @@ if(WITH_JAVA)
|
||||
OUTPUT_NAME apriltag
|
||||
GENERATE_NATIVE_HEADERS apriltag_jni_headers
|
||||
)
|
||||
set_property(TARGET apriltag_jar PROPERTY FOLDER "java")
|
||||
|
||||
install_jar(apriltag_jar DESTINATION ${java_lib_dest})
|
||||
install_jar_exports(TARGETS apriltag_jar FILE apriltag_jar.cmake DESTINATION share/apriltag)
|
||||
@@ -77,6 +79,7 @@ if(WITH_JAVA)
|
||||
add_dependencies(apriltagjni apriltag_jar)
|
||||
|
||||
install(TARGETS apriltagjni EXPORT apriltagjni)
|
||||
export(TARGETS apriltagjni FILE apriltagjni.cmake NAMESPACE apriltagjni::)
|
||||
endif()
|
||||
|
||||
if(WITH_JAVA_SOURCE)
|
||||
@@ -99,7 +102,13 @@ if(WITH_JAVA_SOURCE)
|
||||
set_property(TARGET apriltag_src_jar PROPERTY FOLDER "java")
|
||||
endif()
|
||||
|
||||
generate_resources(src/main/native/resources/edu/wpi/first/apriltag generated/main/cpp APRILTAG frc apriltag_resources_src)
|
||||
generate_resources(
|
||||
src/main/native/resources/edu/wpi/first/apriltag
|
||||
generated/main/cpp
|
||||
APRILTAG
|
||||
frc
|
||||
apriltag_resources_src
|
||||
)
|
||||
|
||||
file(GLOB apriltag_native_src src/main/native/cpp/*.cpp)
|
||||
|
||||
@@ -137,6 +146,7 @@ target_include_directories(
|
||||
)
|
||||
|
||||
install(TARGETS apriltag EXPORT apriltag)
|
||||
export(TARGETS apriltag FILE apriltag.cmake NAMESPACE apriltag::)
|
||||
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/apriltag")
|
||||
|
||||
configure_file(apriltag-config.cmake.in ${WPILIB_BINARY_DIR}/apriltag-config.cmake)
|
||||
|
||||
@@ -39,11 +39,7 @@ public class AprilTag {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof AprilTag) {
|
||||
var other = (AprilTag) obj;
|
||||
return ID == other.ID && pose.equals(other.pose);
|
||||
}
|
||||
return false;
|
||||
return obj instanceof AprilTag tag && ID == tag.ID && pose.equals(tag.pose);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -99,12 +99,8 @@ public class AprilTagDetector implements AutoCloseable {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Config other = (Config) obj;
|
||||
return numThreads == other.numThreads
|
||||
return obj instanceof Config other
|
||||
&& numThreads == other.numThreads
|
||||
&& quadDecimate == other.quadDecimate
|
||||
&& quadSigma == other.quadSigma
|
||||
&& refineEdges == other.refineEdges
|
||||
@@ -194,12 +190,8 @@ public class AprilTagDetector implements AutoCloseable {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof QuadThresholdParameters)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QuadThresholdParameters other = (QuadThresholdParameters) obj;
|
||||
return minClusterPixels == other.minClusterPixels
|
||||
return obj instanceof QuadThresholdParameters other
|
||||
&& minClusterPixels == other.minClusterPixels
|
||||
&& maxNumMaxima == other.maxNumMaxima
|
||||
&& criticalAngle == other.criticalAngle
|
||||
&& maxLineFitMSE == other.maxLineFitMSE
|
||||
|
||||
@@ -148,19 +148,14 @@ public class AprilTagFieldLayout {
|
||||
*/
|
||||
@JsonIgnore
|
||||
public final void setOrigin(OriginPosition origin) {
|
||||
switch (origin) {
|
||||
case kBlueAllianceWallRightSide:
|
||||
setOrigin(new Pose3d());
|
||||
break;
|
||||
case kRedAllianceWallRightSide:
|
||||
setOrigin(
|
||||
new Pose3d(
|
||||
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
|
||||
new Rotation3d(0, 0, Math.PI)));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported enum value");
|
||||
}
|
||||
var pose =
|
||||
switch (origin) {
|
||||
case kBlueAllianceWallRightSide -> Pose3d.kZero;
|
||||
case kRedAllianceWallRightSide -> new Pose3d(
|
||||
new Translation3d(m_fieldDimensions.fieldLength, m_fieldDimensions.fieldWidth, 0),
|
||||
new Rotation3d(0, 0, Math.PI));
|
||||
};
|
||||
setOrigin(pose);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,12 +225,19 @@ public class AprilTagFieldLayout {
|
||||
* @throws UncheckedIOException If the layout does not exist.
|
||||
*/
|
||||
public static AprilTagFieldLayout loadField(AprilTagFields field) {
|
||||
try {
|
||||
return loadFromResource(field.m_resourceFile);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(
|
||||
"Could not load AprilTagFieldLayout from " + field.m_resourceFile, e);
|
||||
if (field.m_fieldLayout == null) {
|
||||
try {
|
||||
field.m_fieldLayout = loadFromResource(field.m_resourceFile);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(
|
||||
"Could not load AprilTagFieldLayout from " + field.m_resourceFile, e);
|
||||
}
|
||||
}
|
||||
// Copy layout because the layout's origin is mutable
|
||||
return new AprilTagFieldLayout(
|
||||
field.m_fieldLayout.getTags(),
|
||||
field.m_fieldLayout.getFieldLength(),
|
||||
field.m_fieldLayout.getFieldWidth());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,11 +266,9 @@ public class AprilTagFieldLayout {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof AprilTagFieldLayout) {
|
||||
var other = (AprilTagFieldLayout) obj;
|
||||
return m_apriltags.equals(other.m_apriltags) && m_origin.equals(other.m_origin);
|
||||
}
|
||||
return false;
|
||||
return obj instanceof AprilTagFieldLayout layout
|
||||
&& m_apriltags.equals(layout.m_apriltags)
|
||||
&& m_origin.equals(layout.m_origin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -281,11 +281,11 @@ public class AprilTagFieldLayout {
|
||||
private static class FieldDimensions {
|
||||
@SuppressWarnings("MemberName")
|
||||
@JsonProperty(value = "length")
|
||||
public double fieldLength;
|
||||
public final double fieldLength;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
@JsonProperty(value = "width")
|
||||
public double fieldWidth;
|
||||
public final double fieldWidth;
|
||||
|
||||
@JsonCreator()
|
||||
FieldDimensions(
|
||||
|
||||
@@ -24,6 +24,8 @@ public enum AprilTagFields {
|
||||
/** Resource filename. */
|
||||
public final String m_resourceFile;
|
||||
|
||||
AprilTagFieldLayout m_fieldLayout;
|
||||
|
||||
AprilTagFields(String resourceFile) {
|
||||
m_resourceFile = kBaseResourceDir + resourceFile;
|
||||
}
|
||||
@@ -33,7 +35,9 @@ public enum AprilTagFields {
|
||||
*
|
||||
* @return AprilTagFieldLayout of the field
|
||||
* @throws UncheckedIOException If the layout does not exist
|
||||
* @deprecated Use {@link AprilTagFieldLayout#loadField(AprilTagFields)} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "2025")
|
||||
public AprilTagFieldLayout loadAprilTagLayoutField() {
|
||||
return AprilTagFieldLayout.loadField(this);
|
||||
}
|
||||
|
||||
@@ -55,12 +55,8 @@ public class AprilTagPoseEstimator {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Config other = (Config) obj;
|
||||
return tagSize == other.tagSize
|
||||
return obj instanceof Config other
|
||||
&& tagSize == other.tagSize
|
||||
&& fx == other.fx
|
||||
&& fy == other.fy
|
||||
&& cx == other.cx
|
||||
|
||||
@@ -17,8 +17,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
public class AprilTagJNI {
|
||||
static boolean libraryLoaded = false;
|
||||
|
||||
static RuntimeLoader<AprilTagJNI> loader = null;
|
||||
|
||||
/** Sets whether JNI should be loaded in the static block. */
|
||||
public static class Helper {
|
||||
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||
@@ -48,10 +46,7 @@ public class AprilTagJNI {
|
||||
static {
|
||||
if (Helper.getExtractOnStaticLoad()) {
|
||||
try {
|
||||
loader =
|
||||
new RuntimeLoader<>(
|
||||
"apriltagjni", RuntimeLoader.getDefaultExtractionRoot(), AprilTagJNI.class);
|
||||
loader.loadLibrary();
|
||||
RuntimeLoader.loadLibrary("apriltagjni");
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
|
||||
@@ -166,7 +166,9 @@ void from_json(const wpi::json& json, AprilTagFieldLayout& layout);
|
||||
*
|
||||
* @param field The predefined field
|
||||
* @return AprilTagFieldLayout of the field
|
||||
* @deprecated Use AprilTagFieldLayout::LoadField() instead
|
||||
*/
|
||||
[[deprecated("Use AprilTagFieldLayout::LoadField() instead")]]
|
||||
WPILIB_DLLEXPORT AprilTagFieldLayout
|
||||
LoadAprilTagLayoutField(AprilTagField field);
|
||||
|
||||
|
||||
@@ -29,15 +29,10 @@ class AprilTagDetectorTest {
|
||||
@SuppressWarnings("MemberName")
|
||||
AprilTagDetector detector;
|
||||
|
||||
static RuntimeLoader<Core> loader;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
try {
|
||||
loader =
|
||||
new RuntimeLoader<>(
|
||||
Core.NATIVE_LIBRARY_NAME, RuntimeLoader.getDefaultExtractionRoot(), Core.class);
|
||||
loader.loadLibrary();
|
||||
RuntimeLoader.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||
} catch (IOException ex) {
|
||||
fail(ex);
|
||||
}
|
||||
@@ -158,7 +153,7 @@ class AprilTagDetectorTest {
|
||||
var estimator =
|
||||
new AprilTagPoseEstimator(new AprilTagPoseEstimator.Config(0.2, 500, 500, 320, 240));
|
||||
AprilTagPoseEstimate est = estimator.estimateOrthogonalIteration(results[0], 200);
|
||||
assertEquals(new Transform3d(), est.pose2);
|
||||
assertEquals(Transform3d.kZero, est.pose2);
|
||||
Transform3d pose = estimator.estimate(results[0]);
|
||||
assertEquals(est.pose1, pose);
|
||||
} finally {
|
||||
|
||||
@@ -19,7 +19,7 @@ class AprilTagPoseSetOriginTest {
|
||||
var layout =
|
||||
new AprilTagFieldLayout(
|
||||
List.of(
|
||||
new AprilTag(1, new Pose3d(new Translation3d(0, 0, 0), new Rotation3d(0, 0, 0))),
|
||||
new AprilTag(1, Pose3d.kZero),
|
||||
new AprilTag(
|
||||
2,
|
||||
new Pose3d(
|
||||
@@ -40,7 +40,7 @@ class AprilTagPoseSetOriginTest {
|
||||
new Pose3d(
|
||||
new Translation3d(
|
||||
Units.feetToMeters(50.0), Units.feetToMeters(23.0), Units.feetToMeters(4)),
|
||||
new Rotation3d(0.0, 0.0, 0)),
|
||||
Rotation3d.kZero),
|
||||
layout.getTagPose(2).orElse(null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ class AprilTagSerializationTest {
|
||||
var layout =
|
||||
new AprilTagFieldLayout(
|
||||
List.of(
|
||||
new AprilTag(1, new Pose3d(0, 0, 0, new Rotation3d(0, 0, 0))),
|
||||
new AprilTag(3, new Pose3d(0, 1, 0, new Rotation3d(0, 0, 0)))),
|
||||
new AprilTag(1, Pose3d.kZero),
|
||||
new AprilTag(3, new Pose3d(0, 1, 0, Rotation3d.kZero))),
|
||||
Units.feetToMeters(54.0),
|
||||
Units.feetToMeters(27.0));
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class LoadConfigTest {
|
||||
Units.inchesToMeters(127.272),
|
||||
Units.inchesToMeters(216.01),
|
||||
Units.inchesToMeters(67.932),
|
||||
new Rotation3d(0, 0, 0));
|
||||
Rotation3d.kZero);
|
||||
Optional<Pose3d> maybePose = layout.getTagPose(1);
|
||||
assertTrue(maybePose.isPresent());
|
||||
assertEquals(expectedPose, maybePose.get());
|
||||
|
||||
@@ -114,8 +114,8 @@ subprojects {
|
||||
|
||||
plugins.withType(JavaPlugin) {
|
||||
java {
|
||||
sourceCompatibility = 11
|
||||
targetCompatibility = 11
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 17
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,5 +9,5 @@ repositories {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation "edu.wpi.first:native-utils:2024.5.2"
|
||||
implementation "edu.wpi.first:native-utils:2025.1.0"
|
||||
}
|
||||
|
||||
@@ -9,16 +9,19 @@ find_package(OpenCV REQUIRED)
|
||||
if(WITH_JAVA)
|
||||
find_package(Java REQUIRED)
|
||||
include(UseJava)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
|
||||
|
||||
#find java files, copy them locally
|
||||
|
||||
set(OPENCV_JAVA_INSTALL_DIR ${OpenCV_INSTALL_PATH}/share/OpenCV/java/)
|
||||
set(OPENCV_JAVA_INSTALL_DIR ${OpenCV_INSTALL_PATH}/share/java/opencv4)
|
||||
|
||||
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
|
||||
PATHS
|
||||
${OPENCV_JAVA_INSTALL_DIR}
|
||||
${OpenCV_INSTALL_PATH}/bin
|
||||
${OpenCV_INSTALL_PATH}/share/java
|
||||
${OpenCV_INSTALL_PATH}/share/OpenCV/java
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
@@ -30,6 +33,7 @@ if(WITH_JAVA)
|
||||
INCLUDE_JARS wpiutil_jar cscore_jar ntcore_jar ${OPENCV_JAR_FILE}
|
||||
OUTPUT_NAME cameraserver
|
||||
)
|
||||
set_property(TARGET cameraserver_jar PROPERTY FOLDER "java")
|
||||
|
||||
install_jar(cameraserver_jar DESTINATION ${java_lib_dest})
|
||||
install_jar_exports(
|
||||
@@ -73,6 +77,7 @@ target_link_libraries(cameraserver PUBLIC ntcore cscore wpiutil ${OpenCV_LIBS})
|
||||
set_property(TARGET cameraserver PROPERTY FOLDER "libraries")
|
||||
|
||||
install(TARGETS cameraserver EXPORT cameraserver)
|
||||
export(TARGETS cameraserver FILE cameraserver.cmake NAMESPACE cameraserver::)
|
||||
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/cameraserver")
|
||||
|
||||
configure_file(cameraserver-config.cmake.in ${WPILIB_BINARY_DIR}/cameraserver-config.cmake)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/fmt/raw_ostream.h>
|
||||
#include <wpi/json.h>
|
||||
#include <wpi/print.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#include "cameraserver/CameraServer.h"
|
||||
@@ -71,7 +72,7 @@ bool ReadCameraConfig(const wpi::json& config) {
|
||||
try {
|
||||
c.name = config.at("name").get<std::string>();
|
||||
} catch (const wpi::json::exception& e) {
|
||||
fmt::print(stderr, "config error in '{}': could not read camera name: {}\n",
|
||||
wpi::print(stderr, "config error in '{}': could not read camera name: {}\n",
|
||||
configFile, e.what());
|
||||
return false;
|
||||
}
|
||||
@@ -80,7 +81,7 @@ bool ReadCameraConfig(const wpi::json& config) {
|
||||
try {
|
||||
c.path = config.at("path").get<std::string>();
|
||||
} catch (const wpi::json::exception& e) {
|
||||
fmt::print(stderr,
|
||||
wpi::print(stderr,
|
||||
"config error in '{}': camera '{}': could not read path: {}\n",
|
||||
configFile, c.name, e.what());
|
||||
return false;
|
||||
@@ -98,7 +99,7 @@ bool ReadConfig() {
|
||||
std::unique_ptr<wpi::MemoryBuffer> fileBuffer =
|
||||
wpi::MemoryBuffer::GetFile(configFile, ec);
|
||||
if (fileBuffer == nullptr || ec) {
|
||||
fmt::print(stderr, "could not open '{}': {}\n", configFile, ec.message());
|
||||
wpi::print(stderr, "could not open '{}': {}\n", configFile, ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -107,14 +108,14 @@ bool ReadConfig() {
|
||||
try {
|
||||
j = wpi::json::parse(fileBuffer->GetCharBuffer());
|
||||
} catch (const wpi::json::parse_error& e) {
|
||||
fmt::print(stderr, "config error in '{}': byte {}: {}\n", configFile,
|
||||
wpi::print(stderr, "config error in '{}': byte {}: {}\n", configFile,
|
||||
e.byte, e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
// top level must be an object
|
||||
if (!j.is_object()) {
|
||||
fmt::print(stderr, "config error in '{}': must be JSON object\n",
|
||||
wpi::print(stderr, "config error in '{}': must be JSON object\n",
|
||||
configFile);
|
||||
return false;
|
||||
}
|
||||
@@ -123,7 +124,7 @@ bool ReadConfig() {
|
||||
try {
|
||||
team = j.at("team").get<unsigned int>();
|
||||
} catch (const wpi::json::exception& e) {
|
||||
fmt::print(stderr, "config error in '{}': could not read team number: {}\n",
|
||||
wpi::print(stderr, "config error in '{}': could not read team number: {}\n",
|
||||
configFile, e.what());
|
||||
return false;
|
||||
}
|
||||
@@ -137,13 +138,13 @@ bool ReadConfig() {
|
||||
} else if (wpi::equals_lower(str, "server")) {
|
||||
server = true;
|
||||
} else {
|
||||
fmt::print(
|
||||
wpi::print(
|
||||
stderr,
|
||||
"config error in '{}': could not understand ntmode value '{}'\n",
|
||||
configFile, str);
|
||||
}
|
||||
} catch (const wpi::json::exception& e) {
|
||||
fmt::print(stderr, "config error in '{}': could not read ntmode: {}\n",
|
||||
wpi::print(stderr, "config error in '{}': could not read ntmode: {}\n",
|
||||
configFile, e.what());
|
||||
}
|
||||
}
|
||||
@@ -156,7 +157,7 @@ bool ReadConfig() {
|
||||
}
|
||||
}
|
||||
} catch (const wpi::json::exception& e) {
|
||||
fmt::print(stderr, "config error in '{}': could not read cameras: {}\n",
|
||||
wpi::print(stderr, "config error in '{}': could not read cameras: {}\n",
|
||||
configFile, e.what());
|
||||
return false;
|
||||
}
|
||||
@@ -165,7 +166,7 @@ bool ReadConfig() {
|
||||
}
|
||||
|
||||
void StartCamera(const CameraConfig& config) {
|
||||
fmt::print("Starting camera '{}' on {}\n", config.name, config.path);
|
||||
wpi::print("Starting camera '{}' on {}\n", config.name, config.path);
|
||||
auto camera =
|
||||
frc::CameraServer::StartAutomaticCapture(config.name, config.path);
|
||||
|
||||
@@ -189,7 +190,7 @@ int main(int argc, char* argv[]) {
|
||||
std::puts("Setting up NetworkTables server");
|
||||
ntinst.StartServer();
|
||||
} else {
|
||||
fmt::print("Setting up NetworkTables client for team {}\n", team);
|
||||
wpi::print("Setting up NetworkTables client for team {}\n", team);
|
||||
ntinst.StartClient4("multicameraserver");
|
||||
ntinst.SetServerTeam(team);
|
||||
}
|
||||
|
||||
@@ -94,24 +94,24 @@ public final class CameraServer {
|
||||
|
||||
void update(VideoEvent event) {
|
||||
switch (event.propertyKind) {
|
||||
case kBoolean:
|
||||
case kBoolean -> {
|
||||
if (m_booleanValueEntry != null) {
|
||||
m_booleanValueEntry.set(event.value != 0);
|
||||
}
|
||||
break;
|
||||
case kInteger:
|
||||
case kEnum:
|
||||
}
|
||||
case kInteger, kEnum -> {
|
||||
if (m_integerValueEntry != null) {
|
||||
m_integerValueEntry.set(event.value);
|
||||
}
|
||||
break;
|
||||
case kString:
|
||||
}
|
||||
case kString -> {
|
||||
if (m_stringValueEntry != null) {
|
||||
m_stringValueEntry.set(event.valueStr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default -> {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,112 +231,93 @@ public final class CameraServer {
|
||||
event -> {
|
||||
synchronized (CameraServer.class) {
|
||||
switch (event.kind) {
|
||||
case kSourceCreated:
|
||||
{
|
||||
// Create subtable for the camera
|
||||
NetworkTable table = m_publishTable.getSubTable(event.name);
|
||||
m_publishers.put(
|
||||
event.sourceHandle, new SourcePublisher(table, event.sourceHandle));
|
||||
break;
|
||||
case kSourceCreated -> {
|
||||
// Create subtable for the camera
|
||||
NetworkTable table = m_publishTable.getSubTable(event.name);
|
||||
m_publishers.put(
|
||||
event.sourceHandle, new SourcePublisher(table, event.sourceHandle));
|
||||
}
|
||||
case kSourceDestroyed -> {
|
||||
SourcePublisher publisher = m_publishers.remove(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
try {
|
||||
publisher.close();
|
||||
} catch (Exception e) {
|
||||
// ignore (nothing we can do about it)
|
||||
}
|
||||
}
|
||||
case kSourceDestroyed:
|
||||
{
|
||||
SourcePublisher publisher = m_publishers.remove(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
}
|
||||
case kSourceConnected -> {
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
// update the description too (as it may have changed)
|
||||
publisher.m_descriptionPublisher.set(
|
||||
CameraServerJNI.getSourceDescription(event.sourceHandle));
|
||||
publisher.m_connectedPublisher.set(true);
|
||||
}
|
||||
}
|
||||
case kSourceDisconnected -> {
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
publisher.m_connectedPublisher.set(false);
|
||||
}
|
||||
}
|
||||
case kSourceVideoModesUpdated -> {
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
publisher.m_modesPublisher.set(getSourceModeValues(event.sourceHandle));
|
||||
}
|
||||
}
|
||||
case kSourceVideoModeChanged -> {
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
publisher.m_modeEntry.set(videoModeToString(event.mode));
|
||||
}
|
||||
}
|
||||
case kSourcePropertyCreated -> {
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
publisher.m_properties.put(
|
||||
event.propertyHandle, new PropertyPublisher(publisher.m_table, event));
|
||||
}
|
||||
}
|
||||
case kSourcePropertyValueUpdated -> {
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
PropertyPublisher pp = publisher.m_properties.get(event.propertyHandle);
|
||||
if (pp != null) {
|
||||
pp.update(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
case kSourcePropertyChoicesUpdated -> {
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
PropertyPublisher pp = publisher.m_properties.get(event.propertyHandle);
|
||||
if (pp != null && pp.m_choicesTopic != null) {
|
||||
try {
|
||||
publisher.close();
|
||||
} catch (Exception e) {
|
||||
// ignore (nothing we can do about it)
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceConnected:
|
||||
{
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
// update the description too (as it may have changed)
|
||||
publisher.m_descriptionPublisher.set(
|
||||
CameraServerJNI.getSourceDescription(event.sourceHandle));
|
||||
publisher.m_connectedPublisher.set(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceDisconnected:
|
||||
{
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
publisher.m_connectedPublisher.set(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceVideoModesUpdated:
|
||||
{
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
publisher.m_modesPublisher.set(getSourceModeValues(event.sourceHandle));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourceVideoModeChanged:
|
||||
{
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
publisher.m_modeEntry.set(videoModeToString(event.mode));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourcePropertyCreated:
|
||||
{
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
publisher.m_properties.put(
|
||||
event.propertyHandle, new PropertyPublisher(publisher.m_table, event));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourcePropertyValueUpdated:
|
||||
{
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
PropertyPublisher pp = publisher.m_properties.get(event.propertyHandle);
|
||||
if (pp != null) {
|
||||
pp.update(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSourcePropertyChoicesUpdated:
|
||||
{
|
||||
SourcePublisher publisher = m_publishers.get(event.sourceHandle);
|
||||
if (publisher != null) {
|
||||
PropertyPublisher pp = publisher.m_properties.get(event.propertyHandle);
|
||||
if (pp != null && pp.m_choicesTopic != null) {
|
||||
try {
|
||||
String[] choices =
|
||||
CameraServerJNI.getEnumPropertyChoices(event.propertyHandle);
|
||||
if (pp.m_choicesPublisher == null) {
|
||||
pp.m_choicesPublisher = pp.m_choicesTopic.publish();
|
||||
}
|
||||
pp.m_choicesPublisher.set(choices);
|
||||
} catch (VideoException ignored) {
|
||||
// ignore (just don't publish choices if we can't get them)
|
||||
String[] choices =
|
||||
CameraServerJNI.getEnumPropertyChoices(event.propertyHandle);
|
||||
if (pp.m_choicesPublisher == null) {
|
||||
pp.m_choicesPublisher = pp.m_choicesTopic.publish();
|
||||
}
|
||||
pp.m_choicesPublisher.set(choices);
|
||||
} catch (VideoException ignored) {
|
||||
// ignore (just don't publish choices if we can't get them)
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kSinkSourceChanged:
|
||||
case kSinkCreated:
|
||||
case kSinkDestroyed:
|
||||
case kNetworkInterfacesChanged:
|
||||
{
|
||||
m_addresses = CameraServerJNI.getNetworkInterfaces();
|
||||
updateStreamValues();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case kSinkSourceChanged,
|
||||
kSinkCreated,
|
||||
kSinkDestroyed,
|
||||
kNetworkInterfacesChanged -> {
|
||||
m_addresses = CameraServerJNI.getNetworkInterfaces();
|
||||
updateStreamValues();
|
||||
}
|
||||
default -> {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -352,23 +333,20 @@ public final class CameraServer {
|
||||
* @param source Source index.
|
||||
*/
|
||||
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:";
|
||||
}
|
||||
return switch (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))) {
|
||||
case kUsb -> "usb:" + CameraServerJNI.getUsbCameraPath(source);
|
||||
case kHttp -> {
|
||||
String[] urls = CameraServerJNI.getHttpCameraUrls(source);
|
||||
if (urls.length > 0) {
|
||||
yield "ip:" + urls[0];
|
||||
} else {
|
||||
yield "ip:";
|
||||
}
|
||||
case kCv:
|
||||
return "cv:";
|
||||
default:
|
||||
return "unknown:";
|
||||
}
|
||||
}
|
||||
case kCv -> "cv:";
|
||||
case kRaw -> "raw:";
|
||||
case kUnknown -> "unknown:";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,20 +480,17 @@ public final class CameraServer {
|
||||
|
||||
/** Provide string description of pixel format. */
|
||||
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";
|
||||
}
|
||||
return switch (pixelFormat) {
|
||||
case kMJPEG -> "MJPEG";
|
||||
case kYUYV -> "YUYV";
|
||||
case kRGB565 -> "RGB565";
|
||||
case kBGR -> "BGR";
|
||||
case kBGRA -> "BGRA";
|
||||
case kGray -> "Gray";
|
||||
case kY16 -> "Y16";
|
||||
case kUYVY -> "UYVY";
|
||||
case kUnknown -> "Unknown";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -632,7 +607,10 @@ public final class CameraServer {
|
||||
*
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
* @return The Axis camera capturing images.
|
||||
* @deprecated Call startAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "2025")
|
||||
@SuppressWarnings("removal")
|
||||
public static AxisCamera addAxisCamera(String host) {
|
||||
return addAxisCamera("Axis Camera", host);
|
||||
}
|
||||
@@ -644,7 +622,10 @@ public final class CameraServer {
|
||||
*
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
* @return The Axis camera capturing images.
|
||||
* @deprecated Call startAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "2025")
|
||||
@SuppressWarnings("removal")
|
||||
public static AxisCamera addAxisCamera(String[] hosts) {
|
||||
return addAxisCamera("Axis Camera", hosts);
|
||||
}
|
||||
@@ -655,7 +636,10 @@ public final class CameraServer {
|
||||
* @param name The name to give the camera
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
* @return The Axis camera capturing images.
|
||||
* @deprecated Call startAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "2025")
|
||||
@SuppressWarnings("removal")
|
||||
public static AxisCamera addAxisCamera(String name, String host) {
|
||||
AxisCamera camera = new AxisCamera(name, host);
|
||||
// Create a passthrough MJPEG server for USB access
|
||||
@@ -670,7 +654,10 @@ public final class CameraServer {
|
||||
* @param name The name to give the camera
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
* @return The Axis camera capturing images.
|
||||
* @deprecated Call startAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "2025")
|
||||
@SuppressWarnings("removal")
|
||||
public static AxisCamera addAxisCamera(String name, String[] hosts) {
|
||||
AxisCamera camera = new AxisCamera(name, hosts);
|
||||
// Create a passthrough MJPEG server for USB access
|
||||
|
||||
@@ -113,6 +113,8 @@ static std::string_view MakeSourceValue(CS_Source source,
|
||||
}
|
||||
case CS_SOURCE_CV:
|
||||
return "cv:";
|
||||
case CS_SOURCE_RAW:
|
||||
return "raw:";
|
||||
default:
|
||||
return "unknown:";
|
||||
}
|
||||
@@ -502,6 +504,7 @@ cs::UsbCamera CameraServer::StartAutomaticCapture(std::string_view name,
|
||||
return camera;
|
||||
}
|
||||
|
||||
WPI_IGNORE_DEPRECATED
|
||||
cs::AxisCamera CameraServer::AddAxisCamera(std::string_view host) {
|
||||
return AddAxisCamera("Axis Camera", host);
|
||||
}
|
||||
@@ -557,7 +560,7 @@ cs::AxisCamera CameraServer::AddAxisCamera(std::string_view name,
|
||||
csShared->ReportAxisCamera(camera.GetHandle());
|
||||
return camera;
|
||||
}
|
||||
|
||||
WPI_UNIGNORE_DEPRECATED
|
||||
cs::MjpegServer CameraServer::AddSwitchedCamera(std::string_view name) {
|
||||
auto& inst = ::GetInstance();
|
||||
// create a dummy CvSource
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <wpi/deprecated.h>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
|
||||
@@ -73,13 +75,16 @@ class CameraServer {
|
||||
*/
|
||||
static cs::MjpegServer StartAutomaticCapture(const cs::VideoSource& camera);
|
||||
|
||||
WPI_IGNORE_DEPRECATED
|
||||
/**
|
||||
* 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")
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::string_view host);
|
||||
|
||||
/**
|
||||
@@ -88,7 +93,9 @@ class CameraServer {
|
||||
* This overload calls AddAxisCamera() with name "Axis Camera".
|
||||
*
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(const char* host);
|
||||
|
||||
/**
|
||||
@@ -97,7 +104,9 @@ class CameraServer {
|
||||
* This overload calls AddAxisCamera() with name "Axis Camera".
|
||||
*
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(const std::string& host);
|
||||
|
||||
/**
|
||||
@@ -106,7 +115,9 @@ class CameraServer {
|
||||
* This overload calls AddAxisCamera() with name "Axis Camera".
|
||||
*
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::span<const std::string> hosts);
|
||||
|
||||
/**
|
||||
@@ -115,8 +126,10 @@ class CameraServer {
|
||||
* This overload calls AddAxisCamera() with name "Axis Camera".
|
||||
*
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
template <typename T>
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::initializer_list<T> hosts);
|
||||
|
||||
/**
|
||||
@@ -124,7 +137,9 @@ class CameraServer {
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::string_view name,
|
||||
std::string_view host);
|
||||
|
||||
@@ -133,7 +148,9 @@ class CameraServer {
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::string_view name, const char* host);
|
||||
|
||||
/**
|
||||
@@ -141,7 +158,9 @@ class CameraServer {
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::string_view name,
|
||||
const std::string& host);
|
||||
|
||||
@@ -150,7 +169,9 @@ class CameraServer {
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::string_view name,
|
||||
std::span<const std::string> hosts);
|
||||
|
||||
@@ -159,10 +180,13 @@ class CameraServer {
|
||||
*
|
||||
* @param name The name to give the camera
|
||||
* @param hosts Array of Camera host IPs/DNS names
|
||||
* @deprecated Call StartAutomaticCapture with a HttpCamera instead.
|
||||
*/
|
||||
template <typename T>
|
||||
[[deprecated("Call StartAutomaticCapture with a HttpCamera instead.")]]
|
||||
static cs::AxisCamera AddAxisCamera(std::string_view name,
|
||||
std::initializer_list<T> hosts);
|
||||
WPI_UNIGNORE_DEPRECATED
|
||||
|
||||
/**
|
||||
* Adds a virtual camera for switching between two streams. Unlike the
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace frc {
|
||||
|
||||
WPI_IGNORE_DEPRECATED
|
||||
template <typename T>
|
||||
inline cs::AxisCamera CameraServer::AddAxisCamera(
|
||||
std::initializer_list<T> hosts) {
|
||||
@@ -27,5 +28,6 @@ inline cs::AxisCamera CameraServer::AddAxisCamera(
|
||||
}
|
||||
return AddAxisCamera(name, vec);
|
||||
}
|
||||
WPI_UNIGNORE_DEPRECATED
|
||||
|
||||
} // namespace frc
|
||||
|
||||
@@ -3,6 +3,7 @@ include(CompileWarnings)
|
||||
macro(wpilib_add_test name srcdir)
|
||||
file(GLOB_RECURSE test_src ${srcdir}/*.cpp)
|
||||
add_executable(${name}_test ${test_src})
|
||||
set_property(TARGET ${name}_test PROPERTY FOLDER "tests")
|
||||
wpilib_target_warnings(${name}_test)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(${name}_test PRIVATE -DGTEST_LINKED_AS_SHARED_LIBRARY)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <fmt/core.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <hal/HAL.h>
|
||||
#include <wpi/print.h>
|
||||
|
||||
#include "mockds/MockDS.h"
|
||||
|
||||
@@ -27,7 +28,7 @@ class TestEnvironment : public testing::Environment {
|
||||
m_alreadySetUp = true;
|
||||
|
||||
if (!HAL_Initialize(500, 0)) {
|
||||
fmt::print(stderr, "FATAL ERROR: HAL could not be initialized\n");
|
||||
wpi::print(stderr, "FATAL ERROR: HAL could not be initialized\n");
|
||||
std::exit(-1);
|
||||
}
|
||||
|
||||
@@ -39,7 +40,7 @@ class TestEnvironment : public testing::Environment {
|
||||
// able to run on the hardware.
|
||||
HAL_ObserveUserProgramStarting();
|
||||
|
||||
fmt::print("Started coms\n");
|
||||
wpi::print("Started coms\n");
|
||||
|
||||
int enableCounter = 0;
|
||||
|
||||
@@ -54,13 +55,13 @@ class TestEnvironment : public testing::Environment {
|
||||
if (enableCounter > 50) {
|
||||
// Robot did not enable properly after 5 seconds.
|
||||
// Force exit
|
||||
fmt::print(stderr, " Failed to enable. Aborting\n");
|
||||
wpi::print(stderr, " Failed to enable. Aborting\n");
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
fmt::print("Waiting for enable: {}\n", enableCounter++);
|
||||
wpi::print("Waiting for enable: {}\n", enableCounter++);
|
||||
HAL_RefreshDSData();
|
||||
}
|
||||
std::this_thread::sleep_for(500ms);
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
#include <hal/cpp/fpga_clock.h>
|
||||
#include <wpi/Logger.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/print.h>
|
||||
#include <wpinet/UDPClient.h>
|
||||
|
||||
static void LoggerFunc(unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg) {
|
||||
if (level == 20) {
|
||||
fmt::print(stderr, "DS: {}\n", msg);
|
||||
wpi::print(stderr, "DS: {}\n", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -31,7 +32,7 @@ static void LoggerFunc(unsigned int level, const char* file, unsigned int line,
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
fmt::print(stderr, "DS: {}: {} ({}:{})\n", levelmsg, msg, file, line);
|
||||
wpi::print(stderr, "DS: {}: {} ({}:{})\n", levelmsg, msg, file, line);
|
||||
}
|
||||
|
||||
static void generateEnabledDsPacket(wpi::SmallVectorImpl<uint8_t>& data,
|
||||
|
||||
@@ -190,12 +190,12 @@ struct RelayHandle {
|
||||
HAL_RelayHandle handle = 0;
|
||||
};
|
||||
|
||||
#define ASSERT_LAST_ERROR_STATUS(status, x) \
|
||||
do { \
|
||||
ASSERT_EQ(status, HAL_USE_LAST_ERROR); \
|
||||
[[maybe_unused]] const char* lastErrorMessageInMacro = \
|
||||
HAL_GetLastError(&status); \
|
||||
ASSERT_EQ(status, x); \
|
||||
#define ASSERT_LAST_ERROR_STATUS(status, x) \
|
||||
do { \
|
||||
ASSERT_EQ(status, HAL_USE_LAST_ERROR); \
|
||||
[[maybe_unused]] \
|
||||
const char* lastErrorMessageInMacro = HAL_GetLastError(&status); \
|
||||
ASSERT_EQ(status, x); \
|
||||
} while (0)
|
||||
|
||||
} // namespace hlt
|
||||
|
||||
@@ -48,6 +48,7 @@ target_link_libraries(cscore PUBLIC wpinet wpiutil ${OpenCV_LIBS})
|
||||
set_property(TARGET cscore PROPERTY FOLDER "libraries")
|
||||
|
||||
install(TARGETS cscore EXPORT cscore)
|
||||
export(TARGETS cscore FILE cscore.cmake NAMESPACE cscore::)
|
||||
install(DIRECTORY src/main/native/include/ DESTINATION "${include_dest}/cscore")
|
||||
|
||||
configure_file(cscore-config.cmake.in ${WPILIB_BINARY_DIR}/cscore-config.cmake)
|
||||
@@ -83,12 +84,11 @@ if(WITH_JAVA)
|
||||
find_package(Java REQUIRED)
|
||||
find_package(JNI REQUIRED)
|
||||
include(UseJava)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding" "UTF8" "-Xlint:unchecked")
|
||||
|
||||
#find java files, copy them locally
|
||||
|
||||
if("${OPENCV_JAVA_INSTALL_DIR}" STREQUAL "")
|
||||
set(OPENCV_JAVA_INSTALL_DIR ${OpenCV_INSTALL_PATH}/share/OpenCV/java/)
|
||||
set(OPENCV_JAVA_INSTALL_DIR ${OpenCV_INSTALL_PATH}/share/java/opencv4)
|
||||
endif()
|
||||
|
||||
find_file(
|
||||
@@ -98,6 +98,7 @@ if(WITH_JAVA)
|
||||
${OPENCV_JAVA_INSTALL_DIR}
|
||||
${OpenCV_INSTALL_PATH}/bin
|
||||
${OpenCV_INSTALL_PATH}/share/java
|
||||
${OpenCV_INSTALL_PATH}/share/OpenCV/java
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
find_file(
|
||||
@@ -112,7 +113,10 @@ if(WITH_JAVA)
|
||||
${OpenCV_INSTALL_PATH}/bin/Release
|
||||
${OpenCV_INSTALL_PATH}/bin/Debug
|
||||
${OpenCV_INSTALL_PATH}/lib
|
||||
${OpenCV_INSTALL_PATH}/lib/Release
|
||||
${OpenCV_INSTALL_PATH}/lib/Debug
|
||||
${OpenCV_INSTALL_PATH}/lib/jni
|
||||
${OpenCV_INSTALL_PATH}/share/java/opencv4
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
@@ -128,6 +132,7 @@ if(WITH_JAVA)
|
||||
OUTPUT_NAME cscore
|
||||
GENERATE_NATIVE_HEADERS cscore_jni_headers
|
||||
)
|
||||
set_property(TARGET cscore_jar PROPERTY FOLDER "java")
|
||||
|
||||
install_jar(cscore_jar DESTINATION ${java_lib_dest})
|
||||
install_jar_exports(TARGETS cscore_jar FILE cscore_jar.cmake DESTINATION share/cscore)
|
||||
@@ -141,6 +146,7 @@ if(WITH_JAVA)
|
||||
${cvFile}Loc
|
||||
NAMES
|
||||
${cvFile}${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}.dll
|
||||
${cvFile}${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}d.dll
|
||||
PATHS
|
||||
${OPENCV_JAVA_INSTALL_DIR}
|
||||
${OpenCV_INSTALL_PATH}/bin
|
||||
@@ -163,6 +169,7 @@ if(WITH_JAVA)
|
||||
add_dependencies(cscorejni cscore_jar)
|
||||
|
||||
install(TARGETS cscorejni EXPORT cscorejni)
|
||||
export(TARGETS cscorejni FILE cscorejni.cmake NAMESPACE cscorejni::)
|
||||
endif()
|
||||
|
||||
if(WITH_JAVA_SOURCE)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/print.h>
|
||||
|
||||
#include "cscore.h"
|
||||
|
||||
@@ -12,11 +12,11 @@ int main() {
|
||||
CS_Status status = 0;
|
||||
|
||||
for (const auto& caminfo : cs::EnumerateUsbCameras(&status)) {
|
||||
fmt::print("{}: {} ({})\n", caminfo.dev, caminfo.path, caminfo.name);
|
||||
wpi::print("{}: {} ({})\n", caminfo.dev, caminfo.path, caminfo.name);
|
||||
if (!caminfo.otherPaths.empty()) {
|
||||
std::puts("Other device paths:");
|
||||
for (auto&& path : caminfo.otherPaths) {
|
||||
fmt::print(" {}\n", path);
|
||||
wpi::print(" {}\n", path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,26 +24,26 @@ int main() {
|
||||
|
||||
std::puts("Properties:");
|
||||
for (const auto& prop : camera.EnumerateProperties()) {
|
||||
fmt::print(" {}", prop.GetName());
|
||||
wpi::print(" {}", prop.GetName());
|
||||
switch (prop.GetKind()) {
|
||||
case cs::VideoProperty::kBoolean:
|
||||
fmt::print(" (bool): value={} default={}", prop.Get(),
|
||||
wpi::print(" (bool): value={} default={}", prop.Get(),
|
||||
prop.GetDefault());
|
||||
break;
|
||||
case cs::VideoProperty::kInteger:
|
||||
fmt::print(" (int): value={} min={} max={} step={} default={}",
|
||||
wpi::print(" (int): value={} min={} max={} step={} default={}",
|
||||
prop.Get(), prop.GetMin(), prop.GetMax(), prop.GetStep(),
|
||||
prop.GetDefault());
|
||||
break;
|
||||
case cs::VideoProperty::kString:
|
||||
fmt::print(" (string): {}", prop.GetString());
|
||||
wpi::print(" (string): {}", prop.GetString());
|
||||
break;
|
||||
case cs::VideoProperty::kEnum: {
|
||||
fmt::print(" (enum): value={}", prop.Get());
|
||||
wpi::print(" (enum): value={}", prop.Get());
|
||||
auto choices = prop.GetChoices();
|
||||
for (size_t i = 0; i < choices.size(); ++i) {
|
||||
if (!choices[i].empty()) {
|
||||
fmt::print("\n {}: {}", i, choices[i]);
|
||||
wpi::print("\n {}: {}", i, choices[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -71,7 +71,7 @@ int main() {
|
||||
pixelFormat = "Unknown";
|
||||
break;
|
||||
}
|
||||
fmt::print(" PixelFormat:{} Width:{} Height:{} FPS:{}\n", pixelFormat,
|
||||
wpi::print(" PixelFormat:{} Width:{} Height:{} FPS:{}\n", pixelFormat,
|
||||
mode.width, mode.height, mode.fps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <wpi/print.h>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
@@ -24,10 +24,10 @@ int main() {
|
||||
for (;;) {
|
||||
uint64_t time = cvsink.GrabFrame(test);
|
||||
if (time == 0) {
|
||||
fmt::print("error: {}\n", cvsink.GetError());
|
||||
wpi::print("error: {}\n", cvsink.GetError());
|
||||
continue;
|
||||
}
|
||||
fmt::print("got frame at time {} size ({}, {})\n", time, test.size().width,
|
||||
wpi::print("got frame at time {} size ({}, {})\n", time, test.size().width,
|
||||
test.size().height);
|
||||
cv::flip(test, flip, 0);
|
||||
cvsource.PutFrame(flip);
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/print.h>
|
||||
|
||||
#include "cscore.h"
|
||||
|
||||
@@ -73,26 +73,26 @@ int main(int argc, char** argv) {
|
||||
// Print settings
|
||||
std::puts("Properties:");
|
||||
for (const auto& prop : camera.EnumerateProperties()) {
|
||||
fmt::print(" {}", prop.GetName());
|
||||
wpi::print(" {}", prop.GetName());
|
||||
switch (prop.GetKind()) {
|
||||
case cs::VideoProperty::kBoolean:
|
||||
fmt::print(" (bool): value={} default={}", prop.Get(),
|
||||
wpi::print(" (bool): value={} default={}", prop.Get(),
|
||||
prop.GetDefault());
|
||||
break;
|
||||
case cs::VideoProperty::kInteger:
|
||||
fmt::print(" (int): value={} min={} max={} step={} default={}",
|
||||
wpi::print(" (int): value={} min={} max={} step={} default={}",
|
||||
prop.Get(), prop.GetMin(), prop.GetMax(), prop.GetStep(),
|
||||
prop.GetDefault());
|
||||
break;
|
||||
case cs::VideoProperty::kString:
|
||||
fmt::print(" (string): {}", prop.GetString());
|
||||
wpi::print(" (string): {}", prop.GetString());
|
||||
break;
|
||||
case cs::VideoProperty::kEnum: {
|
||||
fmt::print(" (enum): value={}", prop.Get());
|
||||
wpi::print(" (enum): value={}", prop.Get());
|
||||
auto choices = prop.GetChoices();
|
||||
for (size_t i = 0; i < choices.size(); ++i) {
|
||||
if (!choices[i].empty()) {
|
||||
fmt::print("\n {}: {}", i, choices[i]);
|
||||
wpi::print("\n {}: {}", i, choices[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <wpi/print.h>
|
||||
|
||||
#include "cscore.h"
|
||||
#include "cscore_cv.h"
|
||||
@@ -24,10 +24,10 @@ int main() {
|
||||
for (;;) {
|
||||
uint64_t time = cvsink.GrabFrame(test);
|
||||
if (time == 0) {
|
||||
fmt::print("error: {}\n", cvsink.GetError());
|
||||
wpi::print("error: {}\n", cvsink.GetError());
|
||||
continue;
|
||||
}
|
||||
fmt::print("got frame at time {} size ({}, {})\n", time, test.size().width,
|
||||
wpi::print("got frame at time {} size ({}, {})\n", time, test.size().width,
|
||||
test.size().height);
|
||||
cv::flip(test, flip, 0);
|
||||
cvsource.PutFrame(flip);
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/print.h>
|
||||
|
||||
#include "cscore.h"
|
||||
|
||||
int main() {
|
||||
fmt::print("hostname: {}\n", cs::GetHostname());
|
||||
wpi::print("hostname: {}\n", cs::GetHostname());
|
||||
std::puts("IPv4 network addresses:");
|
||||
for (const auto& addr : cs::GetNetworkInterfaces()) {
|
||||
fmt::print(" {}\n", addr);
|
||||
wpi::print(" {}\n", addr);
|
||||
}
|
||||
cs::UsbCamera camera{"usbcam", 0};
|
||||
camera.SetVideoMode(cs::VideoMode::kMJPEG, 320, 240, 30);
|
||||
@@ -22,7 +22,7 @@ int main() {
|
||||
CS_Status status = 0;
|
||||
cs::AddListener(
|
||||
[&](const cs::RawEvent& event) {
|
||||
fmt::print("FPS={} MBPS={}\n", camera.GetActualFPS(),
|
||||
wpi::print("FPS={} MBPS={}\n", camera.GetActualFPS(),
|
||||
(camera.GetActualDataRate() / 1000000.0));
|
||||
},
|
||||
cs::RawEvent::kTelemetryUpdated, false, &status);
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <wpi/print.h>
|
||||
#include <wpi/spinlock.h>
|
||||
#include <wpigui.h>
|
||||
|
||||
@@ -39,7 +38,7 @@ int main() {
|
||||
// get frame from camera
|
||||
uint64_t time = cvsink.GrabFrame(frame);
|
||||
if (time == 0) {
|
||||
fmt::print("error: {}\n", cvsink.GetError());
|
||||
wpi::print("error: {}\n", cvsink.GetError());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,22 +20,12 @@ public class RawCVMatSink extends ImageSink {
|
||||
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;
|
||||
return switch (pixelFormat) {
|
||||
case kYUYV, kRGB565, kY16, kUYVY -> CvType.CV_8UC2;
|
||||
case kBGR -> CvType.CV_8UC3;
|
||||
case kBGRA -> CvType.CV_8UC4;
|
||||
case kGray, kMJPEG, kUnknown -> CvType.CV_8UC1;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
package edu.wpi.first.cscore;
|
||||
|
||||
import edu.wpi.first.util.RuntimeDetector;
|
||||
import edu.wpi.first.util.CombinedRuntimeLoader;
|
||||
|
||||
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(CombinedRuntimeLoader.getPlatformPath());
|
||||
System.out.println(CameraServerJNI.getHostname());
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <wpi/print.h>
|
||||
|
||||
#include "cscore.h"
|
||||
|
||||
int main() {
|
||||
fmt::print("{}\n", cs::GetHostname());
|
||||
wpi::print("{}\n", cs::GetHostname());
|
||||
}
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
|
||||
package edu.wpi.first.cscore;
|
||||
|
||||
/** A source that represents an Axis IP camera. */
|
||||
/**
|
||||
* A source that represents an Axis IP camera.
|
||||
*
|
||||
* @deprecated Use HttpCamera instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "2025")
|
||||
public class AxisCamera extends HttpCamera {
|
||||
private static String hostToUrl(String host) {
|
||||
return "http://" + host + "/mjpg/video.mjpg";
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.cscore;
|
||||
|
||||
import edu.wpi.first.util.RuntimeLoader;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.opencv.core.Core;
|
||||
|
||||
/** CameraServer CV JNI. */
|
||||
public class CameraServerCvJNI {
|
||||
static boolean libraryLoaded = false;
|
||||
|
||||
static RuntimeLoader<Core> loader = null;
|
||||
|
||||
/** Sets whether JNI should be loaded in the static block. */
|
||||
public static class Helper {
|
||||
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||
|
||||
/**
|
||||
* Returns true if the JNI should be loaded in the static block.
|
||||
*
|
||||
* @return True if the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static boolean getExtractOnStaticLoad() {
|
||||
return extractOnStaticLoad.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the JNI should be loaded in the static block.
|
||||
*
|
||||
* @param load Whether the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static void setExtractOnStaticLoad(boolean load) {
|
||||
extractOnStaticLoad.set(load);
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
private Helper() {}
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @throws IOException if library load failed
|
||||
*/
|
||||
public static synchronized void forceLoad() throws IOException {
|
||||
if (libraryLoaded) {
|
||||
return;
|
||||
}
|
||||
CameraServerJNI.forceLoad();
|
||||
loader =
|
||||
new RuntimeLoader<>(
|
||||
Core.NATIVE_LIBRARY_NAME, RuntimeLoader.getDefaultExtractionRoot(), Core.class);
|
||||
loader.loadLibrary();
|
||||
libraryLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CV source.
|
||||
*
|
||||
* @param name Name.
|
||||
* @param pixelFormat OpenCV pixel format.
|
||||
* @param width Image width.
|
||||
* @param height Image height.
|
||||
* @param fps Frames per second.
|
||||
* @return CV source.
|
||||
*/
|
||||
public static native int createCvSource(
|
||||
String name, int pixelFormat, int width, int height, int fps);
|
||||
|
||||
/**
|
||||
* Put source frame.
|
||||
*
|
||||
* @param source Source handle.
|
||||
* @param imageNativeObj Image native object handle.
|
||||
*/
|
||||
public static native void putSourceFrame(int source, long imageNativeObj);
|
||||
|
||||
/**
|
||||
* Creates a CV sink.
|
||||
*
|
||||
* @param name Name.
|
||||
* @param pixelFormat OpenCV pixel format.
|
||||
* @return CV sink handle.
|
||||
*/
|
||||
public static native int createCvSink(String name, int pixelFormat);
|
||||
|
||||
// /**
|
||||
// * Creates a CV sink callback.
|
||||
// *
|
||||
// * @param name Name.
|
||||
// * @param processFrame Process frame callback.
|
||||
// */
|
||||
// public static native int createCvSinkCallback(String name,
|
||||
// void (*processFrame)(long time));
|
||||
|
||||
/**
|
||||
* Returns sink frame handle.
|
||||
*
|
||||
* @param sink Sink handle.
|
||||
* @param imageNativeObj Image native object handle.
|
||||
* @return Sink frame handle.
|
||||
*/
|
||||
public static native long grabSinkFrame(int sink, long imageNativeObj);
|
||||
|
||||
/**
|
||||
* Returns sink frame timeout in microseconds.
|
||||
*
|
||||
* @param sink Sink handle.
|
||||
* @param imageNativeObj Image native object handle.
|
||||
* @param timeout Timeout in seconds.
|
||||
* @return Sink frame timeout in microseconds.
|
||||
*/
|
||||
public static native long grabSinkFrameTimeout(int sink, long imageNativeObj, double timeout);
|
||||
|
||||
/** Utility class. */
|
||||
private CameraServerCvJNI() {}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ import java.util.function.Consumer;
|
||||
public class CameraServerJNI {
|
||||
static boolean libraryLoaded = false;
|
||||
|
||||
static RuntimeLoader<CameraServerJNI> loader = null;
|
||||
|
||||
/** Sets whether JNI should be loaded in the static block. */
|
||||
public static class Helper {
|
||||
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||
@@ -46,10 +44,7 @@ public class CameraServerJNI {
|
||||
static {
|
||||
if (Helper.getExtractOnStaticLoad()) {
|
||||
try {
|
||||
loader =
|
||||
new RuntimeLoader<>(
|
||||
"cscorejni", RuntimeLoader.getDefaultExtractionRoot(), CameraServerJNI.class);
|
||||
loader.loadLibrary();
|
||||
RuntimeLoader.loadLibrary("cscorejni");
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
@@ -67,10 +62,7 @@ public class CameraServerJNI {
|
||||
if (libraryLoaded) {
|
||||
return;
|
||||
}
|
||||
loader =
|
||||
new RuntimeLoader<>(
|
||||
"cscorejni", RuntimeLoader.getDefaultExtractionRoot(), CameraServerJNI.class);
|
||||
loader.loadLibrary();
|
||||
RuntimeLoader.loadLibrary("cscorejni");
|
||||
libraryLoaded = true;
|
||||
}
|
||||
|
||||
@@ -212,6 +204,7 @@ public class CameraServerJNI {
|
||||
* Creates a raw source.
|
||||
*
|
||||
* @param name Source name.
|
||||
* @param isCv true for a Cv source.
|
||||
* @param pixelFormat Pixel format.
|
||||
* @param width Image width.
|
||||
* @param height Image height.
|
||||
@@ -219,7 +212,7 @@ public class CameraServerJNI {
|
||||
* @return Raw source handle.
|
||||
*/
|
||||
public static native int createRawSource(
|
||||
String name, int pixelFormat, int width, int height, int fps);
|
||||
String name, boolean isCv, int pixelFormat, int width, int height, int fps);
|
||||
|
||||
//
|
||||
// Source Functions
|
||||
@@ -630,9 +623,10 @@ public class CameraServerJNI {
|
||||
* Creates a raw sink.
|
||||
*
|
||||
* @param name Sink name.
|
||||
* @param isCv true for a Cv source.
|
||||
* @return Raw sink handle.
|
||||
*/
|
||||
public static native int createRawSink(String name);
|
||||
public static native int createRawSink(String name, boolean isCv);
|
||||
|
||||
//
|
||||
// Sink Functions
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
package edu.wpi.first.cscore;
|
||||
|
||||
import edu.wpi.first.util.PixelFormat;
|
||||
import edu.wpi.first.util.RawFrame;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
/**
|
||||
@@ -12,15 +15,42 @@ import org.opencv.core.Mat;
|
||||
* OpenCV builds. For an alternate OpenCV, see the documentation how to build your own with RawSink.
|
||||
*/
|
||||
public class CvSink extends ImageSink {
|
||||
private final RawFrame m_frame = new RawFrame();
|
||||
private Mat m_tmpMat;
|
||||
private ByteBuffer m_origByteBuffer;
|
||||
private int m_width;
|
||||
private int m_height;
|
||||
private PixelFormat m_pixelFormat;
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_tmpMat != null) {
|
||||
m_tmpMat.release();
|
||||
}
|
||||
m_frame.close();
|
||||
super.close();
|
||||
}
|
||||
|
||||
private int getCVFormat(PixelFormat pixelFormat) {
|
||||
return switch (pixelFormat) {
|
||||
case kYUYV, kRGB565, kY16, kUYVY -> CvType.CV_8UC2;
|
||||
case kBGR -> CvType.CV_8UC3;
|
||||
case kBGRA -> CvType.CV_8UC4;
|
||||
case kGray, kMJPEG, kUnknown -> CvType.CV_8UC1;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sink for accepting OpenCV images. WaitForFrame() must be called on the created sink to
|
||||
* Create a sink for accepting OpenCV images. grabFrame() must be called on the created sink to
|
||||
* get each new image.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat Source pixel format
|
||||
*/
|
||||
public CvSink(String name, PixelFormat pixelFormat) {
|
||||
super(CameraServerCvJNI.createCvSink(name, pixelFormat.getValue()));
|
||||
super(CameraServerJNI.createRawSink(name, true));
|
||||
m_pixelFormat = pixelFormat;
|
||||
OpenCvLoader.forceStaticLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,22 +63,9 @@ public class CvSink extends ImageSink {
|
||||
this(name, PixelFormat.kBGR);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
* provided image will have the pixelFormat this class was constructed with.
|
||||
*
|
||||
* @param image Where to store the image.
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error message)
|
||||
@@ -59,7 +76,7 @@ public class CvSink extends ImageSink {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* provided image will have the pixelFormat this class was constructed with.
|
||||
*
|
||||
* @param image Where to store the image.
|
||||
* @param timeout Retrieval timeout in seconds.
|
||||
@@ -67,18 +84,140 @@ public class CvSink extends ImageSink {
|
||||
* is in 1 us increments.
|
||||
*/
|
||||
public long grabFrame(Mat image, double timeout) {
|
||||
return CameraServerCvJNI.grabSinkFrameTimeout(m_handle, image.nativeObj, timeout);
|
||||
long rv = grabFrameDirect(timeout);
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
m_tmpMat.copyTo(image);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Wait for the next frame and get the image. May block forever. The provided image will have the
|
||||
* pixelFormat this class was constructed with.
|
||||
*
|
||||
* @param image Where to store the image.
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
|
||||
* is in 1 us increments.
|
||||
*/
|
||||
public long grabFrameNoTimeout(Mat image) {
|
||||
return CameraServerCvJNI.grabSinkFrame(m_handle, image.nativeObj);
|
||||
long rv = grabFrameNoTimeoutDirect();
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
m_tmpMat.copyTo(image);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direct backing mat for this sink.
|
||||
*
|
||||
* <p>This mat can be invalidated any time any of the grab* methods are called, or when the CvSink
|
||||
* is closed.
|
||||
*
|
||||
* @return The backing mat.
|
||||
*/
|
||||
public Mat getDirectMat() {
|
||||
return m_tmpMat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and store the image. Times out (returning 0) after 0.225 seconds. The
|
||||
* provided image will have the pixelFormat this class was constructed with. Use getDirectMat() to
|
||||
* grab the image.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error message)
|
||||
*/
|
||||
public long grabFrameDirect() {
|
||||
return grabFrameDirect(0.225);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and store the image. Times out (returning 0) after timeout seconds. The
|
||||
* provided image will have the pixelFormat this class was constructed with. Use getDirectMat() to
|
||||
* grab the image.
|
||||
*
|
||||
* @param timeout Retrieval timeout in seconds.
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
|
||||
* is in 1 us increments.
|
||||
*/
|
||||
@SuppressWarnings("PMD.CompareObjectsWithEquals")
|
||||
public long grabFrameDirect(double timeout) {
|
||||
m_frame.setInfo(0, 0, 0, m_pixelFormat);
|
||||
long rv =
|
||||
CameraServerJNI.grabRawSinkFrameTimeout(m_handle, m_frame, m_frame.getNativeObj(), timeout);
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (m_frame.getData() != m_origByteBuffer
|
||||
|| m_width != m_frame.getWidth()
|
||||
|| m_height != m_frame.getHeight()
|
||||
|| m_pixelFormat != m_frame.getPixelFormat()) {
|
||||
m_origByteBuffer = m_frame.getData();
|
||||
m_height = m_frame.getHeight();
|
||||
m_width = m_frame.getWidth();
|
||||
m_pixelFormat = m_frame.getPixelFormat();
|
||||
if (m_frame.getStride() == 0) {
|
||||
m_tmpMat =
|
||||
new Mat(
|
||||
m_frame.getHeight(),
|
||||
m_frame.getWidth(),
|
||||
getCVFormat(m_pixelFormat),
|
||||
m_origByteBuffer);
|
||||
} else {
|
||||
m_tmpMat =
|
||||
new Mat(
|
||||
m_frame.getHeight(),
|
||||
m_frame.getWidth(),
|
||||
getCVFormat(m_pixelFormat),
|
||||
m_origByteBuffer,
|
||||
m_frame.getStride());
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the next frame and store the image. May block forever. The provided image will have
|
||||
* the pixelFormat this class was constructed with. Use getDirectMat() to grab the image.
|
||||
*
|
||||
* @return Frame time, or 0 on error (call GetError() to obtain the error message); the frame time
|
||||
* is in 1 us increments.
|
||||
*/
|
||||
@SuppressWarnings("PMD.CompareObjectsWithEquals")
|
||||
public long grabFrameNoTimeoutDirect() {
|
||||
m_frame.setInfo(0, 0, 0, m_pixelFormat);
|
||||
long rv = CameraServerJNI.grabRawSinkFrame(m_handle, m_frame, m_frame.getNativeObj());
|
||||
if (rv <= 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (m_frame.getData() != m_origByteBuffer
|
||||
|| m_width != m_frame.getWidth()
|
||||
|| m_height != m_frame.getHeight()
|
||||
|| m_pixelFormat != m_frame.getPixelFormat()) {
|
||||
m_origByteBuffer = m_frame.getData();
|
||||
m_height = m_frame.getHeight();
|
||||
m_width = m_frame.getWidth();
|
||||
m_pixelFormat = m_frame.getPixelFormat();
|
||||
if (m_frame.getStride() == 0) {
|
||||
m_tmpMat =
|
||||
new Mat(
|
||||
m_frame.getHeight(),
|
||||
m_frame.getWidth(),
|
||||
getCVFormat(m_pixelFormat),
|
||||
m_origByteBuffer);
|
||||
} else {
|
||||
m_tmpMat =
|
||||
new Mat(
|
||||
m_frame.getHeight(),
|
||||
m_frame.getWidth(),
|
||||
getCVFormat(m_pixelFormat),
|
||||
m_origByteBuffer,
|
||||
m_frame.getStride());
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@ public class CvSource extends ImageSource {
|
||||
*/
|
||||
public CvSource(String name, VideoMode mode) {
|
||||
super(
|
||||
CameraServerCvJNI.createCvSource(
|
||||
name, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps));
|
||||
CameraServerJNI.createRawSource(
|
||||
name, true, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps));
|
||||
OpenCvLoader.forceStaticLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,19 +35,140 @@ public class CvSource extends ImageSource {
|
||||
* @param fps fps
|
||||
*/
|
||||
public CvSource(String name, PixelFormat pixelFormat, int width, int height, int fps) {
|
||||
super(CameraServerCvJNI.createCvSource(name, pixelFormat.getValue(), width, height, fps));
|
||||
super(CameraServerJNI.createRawSource(name, true, pixelFormat.getValue(), width, height, fps));
|
||||
OpenCvLoader.forceStaticLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks
|
||||
*
|
||||
* <p>The image format is guessed from the number of channels. The channel mapping is as follows.
|
||||
* 1: kGray 2: kYUYV 3: BGR 4: BGRA Any other channel numbers will throw an error. If your image
|
||||
* is an in alternate format, use the overload that takes a PixelFormat.
|
||||
*
|
||||
* @param image OpenCV Image
|
||||
*/
|
||||
public void putFrame(Mat image) {
|
||||
// We only support 8 bit channels
|
||||
boolean cleanupRequired = false;
|
||||
Mat finalImage;
|
||||
if (image.depth() == 0) {
|
||||
finalImage = image;
|
||||
} else {
|
||||
finalImage = new Mat();
|
||||
image.convertTo(finalImage, 0);
|
||||
cleanupRequired = true;
|
||||
}
|
||||
|
||||
try {
|
||||
int channels = finalImage.channels();
|
||||
PixelFormat format =
|
||||
switch (channels) {
|
||||
case 1 -> PixelFormat.kGray; // 1 channel is assumed Grayscale
|
||||
case 2 -> PixelFormat.kYUYV; // 2 channels is assumed YUYV
|
||||
case 3 -> PixelFormat.kBGR; // 3 channels is assumed BGR
|
||||
case 4 -> PixelFormat.kBGRA; // 4 channels is assumed BGRA
|
||||
default -> throw new VideoException(
|
||||
"Unable to get pixel format for " + channels + " channels");
|
||||
};
|
||||
|
||||
putFrame(finalImage, format, true);
|
||||
} finally {
|
||||
if (cleanupRequired) {
|
||||
finalImage.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks.
|
||||
*
|
||||
* <p>Only 8-bit single-channel or 3-channel (with BGR channel order) images are supported. If the
|
||||
* format, depth or channel order is different, use Mat.convertTo() and/or cvtColor() to convert
|
||||
* it first.
|
||||
* <p>The format of the Mat must match the PixelFormat. You will corrupt memory if they dont. With
|
||||
* skipVerification false, we will verify the number of channels matches the pixel format. If
|
||||
* skipVerification is true, this step is skipped and is passed straight through.
|
||||
*
|
||||
* @param image OpenCV image
|
||||
* @param format The pixel format of the image
|
||||
* @param skipVerification skip verifying pixel format
|
||||
*/
|
||||
public void putFrame(Mat image) {
|
||||
CameraServerCvJNI.putSourceFrame(m_handle, image.nativeObj);
|
||||
public void putFrame(Mat image, PixelFormat format, boolean skipVerification) {
|
||||
// We only support 8-bit images, convert if necessary
|
||||
boolean cleanupRequired = false;
|
||||
Mat finalImage;
|
||||
if (image.depth() == 0) {
|
||||
finalImage = image;
|
||||
} else {
|
||||
finalImage = new Mat();
|
||||
image.convertTo(finalImage, 0);
|
||||
cleanupRequired = true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!skipVerification) {
|
||||
verifyFormat(finalImage, format);
|
||||
}
|
||||
long step = image.step1() * image.elemSize1();
|
||||
CameraServerJNI.putRawSourceFrameData(
|
||||
m_handle,
|
||||
finalImage.dataAddr(),
|
||||
(int) finalImage.total() * finalImage.channels(),
|
||||
finalImage.width(),
|
||||
finalImage.height(),
|
||||
(int) step,
|
||||
format.getValue());
|
||||
|
||||
} finally {
|
||||
if (cleanupRequired) {
|
||||
finalImage.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyFormat(Mat image, PixelFormat pixelFormat) {
|
||||
int channels = image.channels();
|
||||
switch (pixelFormat) {
|
||||
case kBGR:
|
||||
if (channels == 3) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kBGRA:
|
||||
if (channels == 4) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kGray:
|
||||
if (channels == 1) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kRGB565:
|
||||
if (channels == 2) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kUYVY:
|
||||
if (channels == 2) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kY16:
|
||||
if (channels == 2) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kYUYV:
|
||||
if (channels == 2) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kMJPEG:
|
||||
if (channels == 1) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,16 +40,12 @@ public class HttpCamera extends VideoCamera {
|
||||
* @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;
|
||||
}
|
||||
return switch (kind) {
|
||||
case 1 -> HttpCameraKind.kMJPGStreamer;
|
||||
case 2 -> HttpCameraKind.kCSCore;
|
||||
case 3 -> HttpCameraKind.kAxis;
|
||||
default -> HttpCameraKind.kUnknown;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
79
cscore/src/main/java/edu/wpi/first/cscore/OpenCvLoader.java
Normal file
79
cscore/src/main/java/edu/wpi/first/cscore/OpenCvLoader.java
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.cscore;
|
||||
|
||||
import edu.wpi.first.util.RuntimeLoader;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.opencv.core.Core;
|
||||
|
||||
/** OpenCV Native Loader. */
|
||||
public final class OpenCvLoader {
|
||||
@SuppressWarnings("PMD.MutableStaticState")
|
||||
static boolean libraryLoaded;
|
||||
|
||||
/** Sets whether JNI should be loaded in the static block. */
|
||||
public static final class Helper {
|
||||
private static AtomicBoolean extractOnStaticLoad = new AtomicBoolean(true);
|
||||
|
||||
/**
|
||||
* Returns true if the JNI should be loaded in the static block.
|
||||
*
|
||||
* @return True if the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static boolean getExtractOnStaticLoad() {
|
||||
return extractOnStaticLoad.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the JNI should be loaded in the static block.
|
||||
*
|
||||
* @param load Whether the JNI should be loaded in the static block.
|
||||
*/
|
||||
public static void setExtractOnStaticLoad(boolean load) {
|
||||
extractOnStaticLoad.set(load);
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
private Helper() {}
|
||||
}
|
||||
|
||||
static {
|
||||
if (Helper.getExtractOnStaticLoad()) {
|
||||
try {
|
||||
RuntimeLoader.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
libraryLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a static load.
|
||||
*
|
||||
* @return a garbage value
|
||||
*/
|
||||
public static int forceStaticLoad() {
|
||||
return libraryLoaded ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force load the library.
|
||||
*
|
||||
* @throws IOException if library load failed
|
||||
*/
|
||||
public static synchronized void forceLoad() throws IOException {
|
||||
if (libraryLoaded) {
|
||||
return;
|
||||
}
|
||||
RuntimeLoader.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||
libraryLoaded = true;
|
||||
}
|
||||
|
||||
/** Utility class. */
|
||||
private OpenCvLoader() {}
|
||||
}
|
||||
@@ -75,48 +75,28 @@ public class VideoEvent {
|
||||
* @return The kind
|
||||
*/
|
||||
public static Kind getKindFromInt(int kind) {
|
||||
switch (kind) {
|
||||
case 0x0001:
|
||||
return Kind.kSourceCreated;
|
||||
case 0x0002:
|
||||
return Kind.kSourceDestroyed;
|
||||
case 0x0004:
|
||||
return Kind.kSourceConnected;
|
||||
case 0x0008:
|
||||
return Kind.kSourceDisconnected;
|
||||
case 0x0010:
|
||||
return Kind.kSourceVideoModesUpdated;
|
||||
case 0x0020:
|
||||
return Kind.kSourceVideoModeChanged;
|
||||
case 0x0040:
|
||||
return Kind.kSourcePropertyCreated;
|
||||
case 0x0080:
|
||||
return Kind.kSourcePropertyValueUpdated;
|
||||
case 0x0100:
|
||||
return Kind.kSourcePropertyChoicesUpdated;
|
||||
case 0x0200:
|
||||
return Kind.kSinkSourceChanged;
|
||||
case 0x0400:
|
||||
return Kind.kSinkCreated;
|
||||
case 0x0800:
|
||||
return Kind.kSinkDestroyed;
|
||||
case 0x1000:
|
||||
return Kind.kSinkEnabled;
|
||||
case 0x2000:
|
||||
return Kind.kSinkDisabled;
|
||||
case 0x4000:
|
||||
return Kind.kNetworkInterfacesChanged;
|
||||
case 0x10000:
|
||||
return Kind.kSinkPropertyCreated;
|
||||
case 0x20000:
|
||||
return Kind.kSinkPropertyValueUpdated;
|
||||
case 0x40000:
|
||||
return Kind.kSinkPropertyChoicesUpdated;
|
||||
case 0x80000:
|
||||
return Kind.kUsbCamerasChanged;
|
||||
default:
|
||||
return Kind.kUnknown;
|
||||
}
|
||||
return switch (kind) {
|
||||
case 0x0001 -> Kind.kSourceCreated;
|
||||
case 0x0002 -> Kind.kSourceDestroyed;
|
||||
case 0x0004 -> Kind.kSourceConnected;
|
||||
case 0x0008 -> Kind.kSourceDisconnected;
|
||||
case 0x0010 -> Kind.kSourceVideoModesUpdated;
|
||||
case 0x0020 -> Kind.kSourceVideoModeChanged;
|
||||
case 0x0040 -> Kind.kSourcePropertyCreated;
|
||||
case 0x0080 -> Kind.kSourcePropertyValueUpdated;
|
||||
case 0x0100 -> Kind.kSourcePropertyChoicesUpdated;
|
||||
case 0x0200 -> Kind.kSinkSourceChanged;
|
||||
case 0x0400 -> Kind.kSinkCreated;
|
||||
case 0x0800 -> Kind.kSinkDestroyed;
|
||||
case 0x1000 -> Kind.kSinkEnabled;
|
||||
case 0x2000 -> Kind.kSinkDisabled;
|
||||
case 0x4000 -> Kind.kNetworkInterfacesChanged;
|
||||
case 0x10000 -> Kind.kSinkPropertyCreated;
|
||||
case 0x20000 -> Kind.kSinkPropertyValueUpdated;
|
||||
case 0x40000 -> Kind.kSinkPropertyChoicesUpdated;
|
||||
case 0x80000 -> Kind.kUsbCamerasChanged;
|
||||
default -> Kind.kUnknown;
|
||||
};
|
||||
}
|
||||
|
||||
VideoEvent(
|
||||
|
||||
@@ -64,7 +64,10 @@ public class VideoListener implements AutoCloseable {
|
||||
|
||||
private static final ReentrantLock s_lock = new ReentrantLock();
|
||||
private static final Map<Integer, Consumer<VideoEvent>> s_listeners = new HashMap<>();
|
||||
|
||||
@SuppressWarnings("PMD.SingularField")
|
||||
private static Thread s_thread;
|
||||
|
||||
private static int s_poller;
|
||||
private static boolean s_waitQueue;
|
||||
private static final Condition s_waitQueueCond = s_lock.newCondition();
|
||||
|
||||
@@ -42,18 +42,13 @@ public class VideoProperty {
|
||||
* @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;
|
||||
}
|
||||
return switch (kind) {
|
||||
case 1 -> Kind.kBoolean;
|
||||
case 2 -> Kind.kInteger;
|
||||
case 4 -> Kind.kString;
|
||||
case 8 -> Kind.kEnum;
|
||||
default -> Kind.kNone;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,5 +212,5 @@ public class VideoProperty {
|
||||
}
|
||||
|
||||
int m_handle;
|
||||
private Kind m_kind;
|
||||
private final Kind m_kind;
|
||||
}
|
||||
|
||||
@@ -43,14 +43,12 @@ public class VideoSink implements AutoCloseable {
|
||||
* @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;
|
||||
}
|
||||
return switch (kind) {
|
||||
case 2 -> Kind.kMjpeg;
|
||||
case 4 -> Kind.kCv;
|
||||
case 8 -> Kind.kRaw;
|
||||
default -> Kind.kUnknown;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,16 +79,13 @@ public class VideoSource implements AutoCloseable {
|
||||
* @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;
|
||||
}
|
||||
return switch (kind) {
|
||||
case 1 -> Kind.kUsb;
|
||||
case 2 -> Kind.kHttp;
|
||||
case 4 -> Kind.kCv;
|
||||
case 8 -> Kind.kRaw;
|
||||
default -> Kind.kUnknown;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@ public class RawSink extends ImageSink {
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
*/
|
||||
public RawSink(String name) {
|
||||
super(CameraServerJNI.createRawSink(name));
|
||||
super(CameraServerJNI.createRawSink(name, false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,7 @@ public class RawSource extends ImageSource {
|
||||
public RawSource(String name, VideoMode mode) {
|
||||
super(
|
||||
CameraServerJNI.createRawSource(
|
||||
name, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps));
|
||||
name, false, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,7 +39,7 @@ public class RawSource extends ImageSource {
|
||||
* @param fps fps
|
||||
*/
|
||||
public RawSource(String name, PixelFormat pixelFormat, int width, int height, int fps) {
|
||||
super(CameraServerJNI.createRawSource(name, pixelFormat.getValue(), width, height, fps));
|
||||
super(CameraServerJNI.createRawSource(name, false, pixelFormat.getValue(), width, height, fps));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -104,3 +104,144 @@ void ConfigurableSourceImpl::SetEnumPropertyChoices(
|
||||
prop->name, property, CS_PROP_ENUM,
|
||||
prop->value, {});
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
static constexpr unsigned SourceMask = CS_SOURCE_CV | CS_SOURCE_RAW;
|
||||
|
||||
void NotifySourceError(CS_Source source, std::string_view msg,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<ConfigurableSourceImpl&>(*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<ConfigurableSourceImpl&>(*data->source).SetConnected(connected);
|
||||
}
|
||||
|
||||
void SetSourceDescription(CS_Source source, std::string_view description,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<ConfigurableSourceImpl&>(*data->source)
|
||||
.SetDescription(description);
|
||||
}
|
||||
|
||||
CS_Property CreateSourceProperty(CS_Source source, std::string_view name,
|
||||
CS_PropertyKind kind, int minimum, int maximum,
|
||||
int step, int defaultValue, int value,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return -1;
|
||||
}
|
||||
int property = static_cast<ConfigurableSourceImpl&>(*data->source)
|
||||
.CreateProperty(name, kind, minimum, maximum, step,
|
||||
defaultValue, value);
|
||||
return Handle{source, property, Handle::kProperty};
|
||||
}
|
||||
|
||||
CS_Property CreateSourcePropertyCallback(
|
||||
CS_Source source, std::string_view name, CS_PropertyKind kind, int minimum,
|
||||
int maximum, int step, int defaultValue, int value,
|
||||
std::function<void(CS_Property property)> onChange, CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return -1;
|
||||
}
|
||||
int property = static_cast<ConfigurableSourceImpl&>(*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,
|
||||
std::span<const 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<ConfigurableSourceImpl&>(*data->source)
|
||||
.SetEnumPropertyChoices(propertyIndex, choices, status);
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
extern "C" {
|
||||
void CS_NotifySourceError(CS_Source source, const struct WPI_String* msg,
|
||||
CS_Status* status) {
|
||||
return cs::NotifySourceError(source, wpi::to_string_view(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 struct WPI_String* description,
|
||||
CS_Status* status) {
|
||||
return cs::SetSourceDescription(source, wpi::to_string_view(description),
|
||||
status);
|
||||
}
|
||||
|
||||
CS_Property CS_CreateSourceProperty(CS_Source source,
|
||||
const struct WPI_String* name,
|
||||
enum CS_PropertyKind kind, int minimum,
|
||||
int maximum, int step, int defaultValue,
|
||||
int value, CS_Status* status) {
|
||||
return cs::CreateSourceProperty(source, wpi::to_string_view(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 struct WPI_String* choices,
|
||||
int count, CS_Status* status) {
|
||||
wpi::SmallVector<std::string, 8> vec;
|
||||
vec.reserve(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
vec.emplace_back(wpi::to_string_view(&choices[i]));
|
||||
}
|
||||
return cs::SetSourceEnumPropertyChoices(source, property, vec, status);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -33,7 +33,7 @@ class ConfigurableSourceImpl : public SourceImpl {
|
||||
void NumSinksChanged() override;
|
||||
void NumSinksEnabledChanged() override;
|
||||
|
||||
// OpenCV-specific functions
|
||||
// Frame based specific functions
|
||||
void NotifyError(std::string_view msg);
|
||||
int CreateProperty(std::string_view name, CS_PropertyKind kind, int minimum,
|
||||
int maximum, int step, int defaultValue, int value);
|
||||
|
||||
@@ -1,270 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "CvSinkImpl.h"
|
||||
|
||||
#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(std::string_view name, wpi::Logger& logger,
|
||||
Notifier& notifier, Telemetry& telemetry,
|
||||
VideoMode::PixelFormat pixelFormat)
|
||||
: SinkImpl{name, logger, notifier, telemetry}, m_pixelFormat{pixelFormat} {
|
||||
m_active = true;
|
||||
// m_thread = std::thread(&CvSinkImpl::ThreadMain, this);
|
||||
}
|
||||
|
||||
CvSinkImpl::CvSinkImpl(std::string_view name, wpi::Logger& logger,
|
||||
Notifier& notifier, Telemetry& telemetry,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
std::function<void(uint64_t time)> processFrame)
|
||||
: SinkImpl{name, logger, notifier, telemetry}, m_pixelFormat{pixelFormat} {}
|
||||
|
||||
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, m_pixelFormat)) {
|
||||
// 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, m_pixelFormat)) {
|
||||
// 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(std::string_view name, VideoMode::PixelFormat pixelFormat,
|
||||
CS_Status* status) {
|
||||
auto& inst = Instance::GetInstance();
|
||||
return inst.CreateSink(
|
||||
CS_SINK_CV, std::make_shared<CvSinkImpl>(name, inst.logger, inst.notifier,
|
||||
inst.telemetry, pixelFormat));
|
||||
}
|
||||
|
||||
CS_Sink CreateCvSinkCallback(std::string_view name,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
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, pixelFormat, processFrame));
|
||||
}
|
||||
|
||||
static constexpr unsigned SinkMask = CS_SINK_CV | CS_SINK_RAW;
|
||||
|
||||
void SetSinkDescription(CS_Sink sink, std::string_view 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();
|
||||
}
|
||||
|
||||
std::string_view GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || (data->kind & SinkMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return {};
|
||||
}
|
||||
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, enum WPI_PixelFormat pixelFormat,
|
||||
CS_Status* status) {
|
||||
return cs::CreateCvSink(
|
||||
name, static_cast<VideoMode::PixelFormat>(pixelFormat), status);
|
||||
}
|
||||
|
||||
CS_Sink CS_CreateCvSinkCallback(const char* name,
|
||||
enum WPI_PixelFormat pixelFormat, void* data,
|
||||
void (*processFrame)(void* data, uint64_t time),
|
||||
CS_Status* status) {
|
||||
return cs::CreateCvSinkCallback(
|
||||
name, static_cast<VideoMode::PixelFormat>(pixelFormat),
|
||||
[=](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"
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#ifndef CSCORE_CVSINKIMPL_H_
|
||||
#define CSCORE_CVSINKIMPL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <wpi/condition_variable.h>
|
||||
|
||||
#include "Frame.h"
|
||||
#include "SinkImpl.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class SourceImpl;
|
||||
|
||||
class CvSinkImpl : public SinkImpl {
|
||||
public:
|
||||
CvSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
|
||||
Telemetry& telemetry, VideoMode::PixelFormat pixelFormat);
|
||||
CvSinkImpl(std::string_view name, wpi::Logger& logger, Notifier& notifier,
|
||||
Telemetry& telemetry, VideoMode::PixelFormat pixelFormat,
|
||||
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;
|
||||
VideoMode::PixelFormat m_pixelFormat;
|
||||
};
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_CVSINKIMPL_H_
|
||||
@@ -1,232 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include "CvSourceImpl.h"
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
#include <wpi/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(std::string_view name, wpi::Logger& logger,
|
||||
Notifier& notifier, Telemetry& telemetry,
|
||||
const VideoMode& mode)
|
||||
: ConfigurableSourceImpl{name, logger, notifier, telemetry, mode} {}
|
||||
|
||||
CvSourceImpl::~CvSourceImpl() = default;
|
||||
|
||||
void CvSourceImpl::PutFrame(cv::Mat& image) {
|
||||
// We only support 8-bit images; convert if necessary.
|
||||
cv::Mat finalImage;
|
||||
if (image.depth() == CV_8U) {
|
||||
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: {}-channel images not supported", image.channels());
|
||||
return;
|
||||
}
|
||||
SourceImpl::PutFrame(std::move(dest), wpi::Now());
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
|
||||
CS_Source CreateCvSource(std::string_view name, const VideoMode& mode,
|
||||
CS_Status* status) {
|
||||
auto& inst = Instance::GetInstance();
|
||||
return inst.CreateSource(CS_SOURCE_CV, std::make_shared<CvSourceImpl>(
|
||||
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, std::string_view 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, std::string_view 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, std::string_view 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, std::string_view name, CS_PropertyKind kind, int minimum,
|
||||
int maximum, int step, int defaultValue, int value,
|
||||
std::function<void(CS_Property property)> onChange, CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
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,
|
||||
std::span<const 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"
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#ifndef CSCORE_CVSOURCEIMPL_H_
|
||||
#define CSCORE_CVSOURCEIMPL_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
#include "ConfigurableSourceImpl.h"
|
||||
#include "SourceImpl.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
class CvSourceImpl : public ConfigurableSourceImpl {
|
||||
public:
|
||||
CvSourceImpl(std::string_view 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_
|
||||
@@ -314,6 +314,54 @@ Image* Frame::ConvertImpl(Image* image, VideoMode::PixelFormat pixelFormat,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
// If source is RGB565, YUYV, UYVY, Gray or Y16, need to convert to BGR
|
||||
// first
|
||||
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);
|
||||
}
|
||||
} else 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::kUYVY) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
cur = newImage;
|
||||
} else {
|
||||
cur = ConvertUYVYToBGR(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);
|
||||
}
|
||||
} else if (cur->pixelFormat == VideoMode::kY16) {
|
||||
// Check to see if BGR version already exists...
|
||||
if (Image* newImage =
|
||||
GetExistingImage(cur->width, cur->height, VideoMode::kBGR)) {
|
||||
cur = newImage;
|
||||
} else if (Image* newImage = GetExistingImage(cur->width, cur->height,
|
||||
VideoMode::kGray)) {
|
||||
cur = ConvertGrayToBGR(newImage);
|
||||
} else {
|
||||
cur = ConvertGrayToBGR(ConvertY16ToGray(cur));
|
||||
}
|
||||
}
|
||||
return ConvertBGRToBGRA(cur);
|
||||
case VideoMode::kYUYV:
|
||||
case VideoMode::kUYVY:
|
||||
default:
|
||||
@@ -664,6 +712,28 @@ Image* Frame::ConvertY16ToGray(Image* image) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Image* Frame::ConvertBGRToBGRA(Image* image) {
|
||||
if (!image || image->pixelFormat != VideoMode::kBGR) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate a RGB565 image
|
||||
auto newImage =
|
||||
m_impl->source.AllocImage(VideoMode::kBGRA, image->width, image->height,
|
||||
image->width * image->height * 4);
|
||||
|
||||
// Convert
|
||||
cv::cvtColor(image->AsMat(), newImage->AsMat(), cv::COLOR_BGR2BGRA);
|
||||
|
||||
// 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::GetImageImpl(int width, int height,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
int requiredJpegQuality, int defaultJpegQuality) {
|
||||
@@ -727,3 +797,16 @@ void Frame::ReleaseFrame() {
|
||||
m_impl->source.ReleaseFrameImpl(std::unique_ptr<Impl>(m_impl));
|
||||
m_impl = nullptr;
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
std::unique_ptr<Image> CreateImageFromBGRA(cs::SourceImpl* source, size_t width,
|
||||
size_t height, size_t stride,
|
||||
const uint8_t* data) {
|
||||
cv::Mat finalImage{static_cast<int>(height), static_cast<int>(width), CV_8UC4,
|
||||
const_cast<uint8_t*>(data), stride};
|
||||
std::unique_ptr<Image> dest = source->AllocImage(
|
||||
VideoMode::PixelFormat::kBGR, width, height, width * height * 3);
|
||||
cv::cvtColor(finalImage, dest->AsMat(), cv::COLOR_BGRA2BGR);
|
||||
return dest;
|
||||
}
|
||||
} // namespace cs
|
||||
|
||||
@@ -22,6 +22,10 @@ namespace cs {
|
||||
|
||||
class SourceImpl;
|
||||
|
||||
std::unique_ptr<Image> CreateImageFromBGRA(cs::SourceImpl* source, size_t width,
|
||||
size_t height, size_t stride,
|
||||
const uint8_t* data);
|
||||
|
||||
class Frame {
|
||||
friend class SourceImpl;
|
||||
|
||||
@@ -206,6 +210,7 @@ class Frame {
|
||||
Image* ConvertGrayToMJPEG(Image* image, int quality);
|
||||
Image* ConvertGrayToY16(Image* image);
|
||||
Image* ConvertY16ToGray(Image* image);
|
||||
Image* ConvertBGRToBGRA(Image* image);
|
||||
|
||||
Image* GetImage(int width, int height, VideoMode::PixelFormat pixelFormat) {
|
||||
if (pixelFormat == VideoMode::kMJPEG) {
|
||||
|
||||
@@ -634,55 +634,47 @@ std::vector<std::string> GetHttpCameraUrls(CS_Source source,
|
||||
|
||||
extern "C" {
|
||||
|
||||
CS_Source CS_CreateHttpCamera(const char* name, const char* url,
|
||||
CS_Source CS_CreateHttpCamera(const struct WPI_String* name,
|
||||
const struct WPI_String* url,
|
||||
CS_HttpCameraKind kind, CS_Status* status) {
|
||||
return cs::CreateHttpCamera(name, url, kind, status);
|
||||
return cs::CreateHttpCamera(wpi::to_string_view(name),
|
||||
wpi::to_string_view(url), kind, status);
|
||||
}
|
||||
|
||||
CS_Source CS_CreateHttpCameraMulti(const char* name, const char** urls,
|
||||
int count, CS_HttpCameraKind kind,
|
||||
CS_Status* status) {
|
||||
CS_Source CS_CreateHttpCameraMulti(const struct WPI_String* name,
|
||||
const struct WPI_String* 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]);
|
||||
vec.emplace_back(wpi::to_string_view(&urls[i]));
|
||||
}
|
||||
return cs::CreateHttpCamera(name, vec, kind, status);
|
||||
return cs::CreateHttpCamera(wpi::to_string_view(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) {
|
||||
void CS_SetHttpCameraUrls(CS_Source source, const struct WPI_String* 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]);
|
||||
vec.emplace_back(wpi::to_string_view(&urls[i]));
|
||||
}
|
||||
cs::SetHttpCameraUrls(source, vec, status);
|
||||
}
|
||||
|
||||
char** CS_GetHttpCameraUrls(CS_Source source, int* count, CS_Status* status) {
|
||||
WPI_String* 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*)));
|
||||
WPI_String* out = WPI_AllocateStringArray(urls.size());
|
||||
*count = urls.size();
|
||||
for (size_t i = 0; i < urls.size(); ++i) {
|
||||
out[i] = cs::ConvertToC(urls[i]);
|
||||
cs::ConvertToC(&out[i], 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"
|
||||
|
||||
@@ -63,6 +63,9 @@ class Image {
|
||||
case VideoMode::kBGR:
|
||||
type = CV_8UC3;
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
type = CV_8UC4;
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
case VideoMode::kMJPEG:
|
||||
default:
|
||||
@@ -81,6 +84,8 @@ class Image {
|
||||
return 2 * width;
|
||||
case VideoMode::kBGR:
|
||||
return 3 * width;
|
||||
case VideoMode::kBGRA:
|
||||
return 4 * width;
|
||||
case VideoMode::kGray:
|
||||
return width;
|
||||
case VideoMode::kMJPEG:
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/print.h>
|
||||
|
||||
using namespace cs;
|
||||
|
||||
static void def_log_func(unsigned int level, const char* file,
|
||||
unsigned int line, const char* msg) {
|
||||
if (level == 20) {
|
||||
fmt::print(stderr, "CS: {}\n", msg);
|
||||
wpi::print(stderr, "CS: {}\n", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,7 +29,7 @@ static void def_log_func(unsigned int level, const char* file,
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
fmt::print(stderr, "CS: {}: {} ({}:{})\n", levelmsg, msg,
|
||||
wpi::print(stderr, "CS: {}: {} ({}:{})\n", levelmsg, msg,
|
||||
fs::path{file}.filename().string(), line);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/fmt/raw_ostream.h>
|
||||
#include <wpi/print.h>
|
||||
#include <wpinet/HttpUtil.h>
|
||||
#include <wpinet/TCPAcceptor.h>
|
||||
#include <wpinet/raw_socket_istream.h>
|
||||
@@ -135,7 +135,7 @@ class MjpegServerImpl::ConnThread : public wpi::SafeThread {
|
||||
static void SendHeader(wpi::raw_ostream& os, int code,
|
||||
std::string_view codeText, std::string_view contentType,
|
||||
std::string_view extra = {}) {
|
||||
fmt::print(os, "HTTP/1.0 {} {}\r\n", code, codeText);
|
||||
wpi::print(os, "HTTP/1.0 {} {}\r\n", code, codeText);
|
||||
os << "Connection: close\r\n"
|
||||
"Server: CameraServer/1.0\r\n"
|
||||
"Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, "
|
||||
@@ -302,7 +302,7 @@ bool MjpegServerImpl::ConnThread::ProcessCommand(wpi::raw_ostream& os,
|
||||
case CS_PROP_INTEGER:
|
||||
case CS_PROP_ENUM: {
|
||||
if (auto v = wpi::parse_integer<int>(value, 10)) {
|
||||
fmt::print(response, "{}: {}\r\n", param, v.value());
|
||||
wpi::print(response, "{}: {}\r\n", param, v.value());
|
||||
SDEBUG4("HTTP parameter \"{}\" value {}", param, value);
|
||||
source.SetProperty(prop, v.value(), &status);
|
||||
} else {
|
||||
@@ -356,10 +356,10 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
|
||||
continue;
|
||||
}
|
||||
auto kind = source.GetPropertyKind(prop);
|
||||
fmt::print(os, "<p /><label for=\"{0}\">{0}</label>\n", name);
|
||||
wpi::print(os, "<p /><label for=\"{0}\">{0}</label>\n", name);
|
||||
switch (kind) {
|
||||
case CS_PROP_BOOLEAN:
|
||||
fmt::print(os,
|
||||
wpi::print(os,
|
||||
"<input id=\"{0}\" type=\"checkbox\" "
|
||||
"onclick=\"update('{0}', this.checked ? 1 : 0)\" ",
|
||||
name);
|
||||
@@ -374,12 +374,12 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
|
||||
auto min = source.GetPropertyMin(prop, &status);
|
||||
auto max = source.GetPropertyMax(prop, &status);
|
||||
auto step = source.GetPropertyStep(prop, &status);
|
||||
fmt::print(os,
|
||||
wpi::print(os,
|
||||
"<input type=\"range\" min=\"{1}\" max=\"{2}\" "
|
||||
"value=\"{3}\" id=\"{0}\" step=\"{4}\" "
|
||||
"oninput=\"updateInt('#{0}op', '{0}', value)\" />\n",
|
||||
name, min, max, valI, step);
|
||||
fmt::print(os, "<output for=\"{0}\" id=\"{0}op\">{1}</output>\n", name,
|
||||
wpi::print(os, "<output for=\"{0}\" id=\"{0}op\">{1}</output>\n", name,
|
||||
valI);
|
||||
break;
|
||||
}
|
||||
@@ -397,25 +397,25 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
|
||||
for (char ch : *choice) {
|
||||
ch_name.push_back(wpi::isPrint(ch) ? ch : ' ');
|
||||
}
|
||||
fmt::print(os,
|
||||
wpi::print(os,
|
||||
"<input id=\"{0}{1}\" type=\"radio\" name=\"{0}\" "
|
||||
"value=\"{2}\" onclick=\"update('{0}', {1})\"",
|
||||
name, j, ch_name.str());
|
||||
if (j == valE) {
|
||||
os << " checked";
|
||||
}
|
||||
fmt::print(os, " /><label for=\"{}{}\">{}</label>\n", name, j,
|
||||
wpi::print(os, " /><label for=\"{}{}\">{}</label>\n", name, j,
|
||||
ch_name.str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CS_PROP_STRING: {
|
||||
wpi::SmallString<128> strval_buf;
|
||||
fmt::print(os,
|
||||
wpi::print(os,
|
||||
"<input type=\"text\" id=\"{0}box\" name=\"{0}\" "
|
||||
"value=\"{1}\" />\n",
|
||||
name, source.GetStringProperty(prop, strval_buf, &status));
|
||||
fmt::print(os,
|
||||
wpi::print(os,
|
||||
"<input type=\"button\" value =\"Submit\" "
|
||||
"onclick=\"update('{0}', {0}box.value)\" />\n",
|
||||
name);
|
||||
@@ -438,9 +438,7 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
|
||||
|
||||
os << "<p>Supported Video Modes:</p>\n";
|
||||
os << "<table cols=\"4\" style=\"border: 1px solid black\">\n";
|
||||
os << "<tr><th>Pixel Format</th>"
|
||||
<< "<th>Width</th>"
|
||||
<< "<th>Height</th>"
|
||||
os << "<tr><th>Pixel Format</th>" << "<th>Width</th>" << "<th>Height</th>"
|
||||
<< "<th>FPS</th></tr>";
|
||||
for (auto mode : source.EnumerateVideoModes(&status)) {
|
||||
os << "<tr><td>";
|
||||
@@ -457,6 +455,9 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
|
||||
case VideoMode::kBGR:
|
||||
os << "BGR";
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
os << "BGRA";
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
os << "gray";
|
||||
break;
|
||||
@@ -470,7 +471,7 @@ void MjpegServerImpl::ConnThread::SendHTML(wpi::raw_ostream& os,
|
||||
os << "unknown";
|
||||
break;
|
||||
}
|
||||
fmt::print(os, "</td><td>{}</td><td>{}</td><td>{}</td></tr>", mode.width,
|
||||
wpi::print(os, "</td><td>{}</td><td>{}</td><td>{}</td></tr>", mode.width,
|
||||
mode.height, mode.fps);
|
||||
}
|
||||
os << "</table>\n";
|
||||
@@ -499,21 +500,21 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os,
|
||||
wpi::SmallString<128> name_buf;
|
||||
auto name = source.GetPropertyName(prop, name_buf, &status);
|
||||
auto kind = source.GetPropertyKind(prop);
|
||||
fmt::print(os, "\n\"name\": \"{}\"", name);
|
||||
fmt::print(os, ",\n\"id\": \"{}\"", prop);
|
||||
fmt::print(os, ",\n\"type\": \"{}\"", static_cast<int>(kind));
|
||||
fmt::print(os, ",\n\"min\": \"{}\"", source.GetPropertyMin(prop, &status));
|
||||
fmt::print(os, ",\n\"max\": \"{}\"", source.GetPropertyMax(prop, &status));
|
||||
fmt::print(os, ",\n\"step\": \"{}\"",
|
||||
wpi::print(os, "\n\"name\": \"{}\"", name);
|
||||
wpi::print(os, ",\n\"id\": \"{}\"", prop);
|
||||
wpi::print(os, ",\n\"type\": \"{}\"", static_cast<int>(kind));
|
||||
wpi::print(os, ",\n\"min\": \"{}\"", source.GetPropertyMin(prop, &status));
|
||||
wpi::print(os, ",\n\"max\": \"{}\"", source.GetPropertyMax(prop, &status));
|
||||
wpi::print(os, ",\n\"step\": \"{}\"",
|
||||
source.GetPropertyStep(prop, &status));
|
||||
fmt::print(os, ",\n\"default\": \"{}\"",
|
||||
wpi::print(os, ",\n\"default\": \"{}\"",
|
||||
source.GetPropertyDefault(prop, &status));
|
||||
os << ",\n\"value\": \"";
|
||||
switch (kind) {
|
||||
case CS_PROP_BOOLEAN:
|
||||
case CS_PROP_INTEGER:
|
||||
case CS_PROP_ENUM:
|
||||
fmt::print(os, "{}", source.GetProperty(prop, &status));
|
||||
wpi::print(os, "{}", source.GetProperty(prop, &status));
|
||||
break;
|
||||
case CS_PROP_STRING: {
|
||||
wpi::SmallString<128> strval_buf;
|
||||
@@ -543,7 +544,7 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os,
|
||||
for (char ch : *choice) {
|
||||
ch_name.push_back(std::isprint(ch) ? ch : ' ');
|
||||
}
|
||||
fmt::print(os, "\"{}\": \"{}\"", j, ch_name.str());
|
||||
wpi::print(os, "\"{}\": \"{}\"", j, ch_name.str());
|
||||
}
|
||||
os << "}\n";
|
||||
}
|
||||
@@ -585,9 +586,9 @@ void MjpegServerImpl::ConnThread::SendJSON(wpi::raw_ostream& os,
|
||||
os << "unknown";
|
||||
break;
|
||||
}
|
||||
fmt::print(os, "\",\n\"width\": \"{}\"", mode.width);
|
||||
fmt::print(os, ",\n\"height\": \"{}\"", mode.height);
|
||||
fmt::print(os, ",\n\"fps\": \"{}\"", mode.fps);
|
||||
wpi::print(os, "\",\n\"width\": \"{}\"", mode.width);
|
||||
wpi::print(os, ",\n\"height\": \"{}\"", mode.height);
|
||||
wpi::print(os, ",\n\"fps\": \"{}\"", mode.fps);
|
||||
os << '}';
|
||||
}
|
||||
os << "\n]\n}\n";
|
||||
@@ -772,8 +773,8 @@ void MjpegServerImpl::ConnThread::SendStream(wpi::raw_socket_ostream& os) {
|
||||
header.clear();
|
||||
oss << "\r\n--" BOUNDARY "\r\n"
|
||||
<< "Content-Type: image/jpeg\r\n";
|
||||
fmt::print(oss, "Content-Length: {}\r\n", size);
|
||||
fmt::print(oss, "X-Timestamp: {}\r\n", timestamp);
|
||||
wpi::print(oss, "Content-Length: {}\r\n", size);
|
||||
wpi::print(oss, "X-Timestamp: {}\r\n", timestamp);
|
||||
oss << "\r\n";
|
||||
os << oss.str();
|
||||
if (addDHT) {
|
||||
@@ -878,8 +879,7 @@ void MjpegServerImpl::ConnThread::ProcessRequest() {
|
||||
ProcessCommand(os, *source, parameters, true);
|
||||
} else {
|
||||
SendHeader(os, 200, "OK", "text/plain");
|
||||
os << "Ignored due to no connected source."
|
||||
<< "\r\n";
|
||||
os << "Ignored due to no connected source." << "\r\n";
|
||||
SDEBUG("Ignored due to no connected source.");
|
||||
}
|
||||
break;
|
||||
@@ -1047,13 +1047,17 @@ int GetMjpegServerPort(CS_Sink sink, CS_Status* status) {
|
||||
|
||||
extern "C" {
|
||||
|
||||
CS_Sink CS_CreateMjpegServer(const char* name, const char* listenAddress,
|
||||
int port, CS_Status* status) {
|
||||
return cs::CreateMjpegServer(name, listenAddress, port, status);
|
||||
CS_Sink CS_CreateMjpegServer(const struct WPI_String* name,
|
||||
const struct WPI_String* listenAddress, int port,
|
||||
CS_Status* status) {
|
||||
return cs::CreateMjpegServer(wpi::to_string_view(name),
|
||||
wpi::to_string_view(listenAddress), port,
|
||||
status);
|
||||
}
|
||||
|
||||
char* CS_GetMjpegServerListenAddress(CS_Sink sink, CS_Status* status) {
|
||||
return ConvertToC(cs::GetMjpegServerListenAddress(sink, status));
|
||||
void CS_GetMjpegServerListenAddress(CS_Sink sink, WPI_String* listenAddress,
|
||||
CS_Status* status) {
|
||||
cs::ConvertToC(listenAddress, cs::GetMjpegServerListenAddress(sink, status));
|
||||
}
|
||||
|
||||
int CS_GetMjpegServerPort(CS_Sink sink, CS_Status* status) {
|
||||
|
||||
@@ -143,25 +143,28 @@ void RawSinkImpl::ThreadMain() {
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
CS_Sink CreateRawSink(std::string_view name, CS_Status* status) {
|
||||
static constexpr unsigned SinkMask = CS_SINK_CV | CS_SINK_RAW;
|
||||
|
||||
CS_Sink CreateRawSink(std::string_view name, bool isCv, CS_Status* status) {
|
||||
auto& inst = Instance::GetInstance();
|
||||
return inst.CreateSink(CS_SINK_RAW,
|
||||
return inst.CreateSink(isCv ? CS_SINK_CV : CS_SINK_RAW,
|
||||
std::make_shared<RawSinkImpl>(
|
||||
name, inst.logger, inst.notifier, inst.telemetry));
|
||||
}
|
||||
|
||||
CS_Sink CreateRawSinkCallback(std::string_view name,
|
||||
CS_Sink CreateRawSinkCallback(std::string_view name, bool isCv,
|
||||
std::function<void(uint64_t time)> processFrame,
|
||||
CS_Status* status) {
|
||||
auto& inst = Instance::GetInstance();
|
||||
return inst.CreateSink(CS_SINK_RAW, std::make_shared<RawSinkImpl>(
|
||||
name, inst.logger, inst.notifier,
|
||||
inst.telemetry, processFrame));
|
||||
return inst.CreateSink(
|
||||
isCv ? CS_SINK_CV : CS_SINK_RAW,
|
||||
std::make_shared<RawSinkImpl>(name, inst.logger, inst.notifier,
|
||||
inst.telemetry, processFrame));
|
||||
}
|
||||
|
||||
uint64_t GrabSinkFrame(CS_Sink sink, WPI_RawFrame& image, CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || data->kind != CS_SINK_RAW) {
|
||||
if (!data || (data->kind & SinkMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return 0;
|
||||
}
|
||||
@@ -171,25 +174,27 @@ uint64_t GrabSinkFrame(CS_Sink sink, WPI_RawFrame& image, CS_Status* status) {
|
||||
uint64_t GrabSinkFrameTimeout(CS_Sink sink, WPI_RawFrame& image, double timeout,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || data->kind != CS_SINK_RAW) {
|
||||
if (!data || (data->kind & SinkMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return 0;
|
||||
}
|
||||
return static_cast<RawSinkImpl&>(*data->sink).GrabFrame(image, timeout);
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
extern "C" {
|
||||
CS_Sink CS_CreateRawSink(const char* name, CS_Status* status) {
|
||||
return cs::CreateRawSink(name, status);
|
||||
CS_Sink CS_CreateRawSink(const struct WPI_String* name, CS_Bool isCv,
|
||||
CS_Status* status) {
|
||||
return cs::CreateRawSink(wpi::to_string_view(name), isCv, status);
|
||||
}
|
||||
|
||||
CS_Sink CS_CreateRawSinkCallback(const char* name, void* data,
|
||||
void (*processFrame)(void* data,
|
||||
uint64_t time),
|
||||
CS_Status* status) {
|
||||
CS_Sink CS_CreateRawSinkCallback(
|
||||
const struct WPI_String* name, CS_Bool isCv, void* data,
|
||||
void (*processFrame)(void* data, uint64_t time), CS_Status* status) {
|
||||
return cs::CreateRawSinkCallback(
|
||||
name, [=](uint64_t time) { processFrame(data, time); }, status);
|
||||
wpi::to_string_view(name), isCv,
|
||||
[=](uint64_t time) { processFrame(data, time); }, status);
|
||||
}
|
||||
|
||||
uint64_t CS_GrabRawSinkFrame(CS_Sink sink, struct WPI_RawFrame* image,
|
||||
@@ -201,4 +206,5 @@ uint64_t CS_GrabRawSinkFrameTimeout(CS_Sink sink, struct WPI_RawFrame* image,
|
||||
double timeout, CS_Status* status) {
|
||||
return cs::GrabSinkFrameTimeout(sink, *image, timeout, status);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -22,62 +22,46 @@ RawSourceImpl::RawSourceImpl(std::string_view name, wpi::Logger& logger,
|
||||
RawSourceImpl::~RawSourceImpl() = default;
|
||||
|
||||
void RawSourceImpl::PutFrame(const WPI_RawFrame& image) {
|
||||
int type;
|
||||
switch (image.pixelFormat) {
|
||||
case VideoMode::kYUYV:
|
||||
case VideoMode::kRGB565:
|
||||
case VideoMode::kY16:
|
||||
case VideoMode::kUYVY:
|
||||
type = CV_8UC2;
|
||||
break;
|
||||
case VideoMode::kBGR:
|
||||
type = CV_8UC3;
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
case VideoMode::kMJPEG:
|
||||
default:
|
||||
type = CV_8UC1;
|
||||
break;
|
||||
}
|
||||
cv::Mat finalImage{image.height, image.width, type, image.data,
|
||||
static_cast<size_t>(image.stride)};
|
||||
std::unique_ptr<Image> dest =
|
||||
AllocImage(static_cast<VideoMode::PixelFormat>(image.pixelFormat),
|
||||
image.width, image.height, image.size);
|
||||
finalImage.copyTo(dest->AsMat());
|
||||
|
||||
SourceImpl::PutFrame(std::move(dest), wpi::Now());
|
||||
auto currentTime = wpi::Now();
|
||||
std::string_view data_view{reinterpret_cast<char*>(image.data), image.size};
|
||||
SourceImpl::PutFrame(static_cast<VideoMode::PixelFormat>(image.pixelFormat),
|
||||
image.width, image.height, data_view, currentTime);
|
||||
}
|
||||
|
||||
namespace cs {
|
||||
CS_Source CreateRawSource(std::string_view name, const VideoMode& mode,
|
||||
CS_Status* status) {
|
||||
static constexpr unsigned SourceMask = CS_SOURCE_CV | CS_SOURCE_RAW;
|
||||
|
||||
CS_Source CreateRawSource(std::string_view name, bool isCv,
|
||||
const VideoMode& mode, CS_Status* status) {
|
||||
auto& inst = Instance::GetInstance();
|
||||
return inst.CreateSource(CS_SOURCE_RAW, std::make_shared<RawSourceImpl>(
|
||||
name, inst.logger, inst.notifier,
|
||||
inst.telemetry, mode));
|
||||
return inst.CreateSource(
|
||||
isCv ? CS_SOURCE_CV : CS_SOURCE_RAW,
|
||||
std::make_shared<RawSourceImpl>(name, inst.logger, inst.notifier,
|
||||
inst.telemetry, mode));
|
||||
}
|
||||
|
||||
void PutSourceFrame(CS_Source source, const WPI_RawFrame& image,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSource(source);
|
||||
if (!data || data->kind != CS_SOURCE_RAW) {
|
||||
if (!data || (data->kind & SourceMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
static_cast<RawSourceImpl&>(*data->source).PutFrame(image);
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
extern "C" {
|
||||
CS_Source CS_CreateRawSource(const char* name, const CS_VideoMode* mode,
|
||||
CS_Status* status) {
|
||||
return cs::CreateRawSource(name, static_cast<const cs::VideoMode&>(*mode),
|
||||
status);
|
||||
CS_Source CS_CreateRawSource(const struct WPI_String* name, CS_Bool isCv,
|
||||
const CS_VideoMode* mode, CS_Status* status) {
|
||||
return cs::CreateRawSource(wpi::to_string_view(name), isCv,
|
||||
static_cast<const cs::VideoMode&>(*mode), status);
|
||||
}
|
||||
|
||||
void CS_PutRawSourceFrame(CS_Source source, const struct WPI_RawFrame* image,
|
||||
CS_Status* status) {
|
||||
return cs::PutSourceFrame(source, *image, status);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
#include "SinkImpl.h"
|
||||
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/json.h>
|
||||
|
||||
#include "Instance.h"
|
||||
#include "Notifier.h"
|
||||
#include "SourceImpl.h"
|
||||
#include "c_util.h"
|
||||
|
||||
using namespace cs;
|
||||
|
||||
@@ -195,3 +197,64 @@ void SinkImpl::UpdatePropertyValue(int property, bool setString, int value,
|
||||
}
|
||||
|
||||
void SinkImpl::SetSourceImpl(std::shared_ptr<SourceImpl> source) {}
|
||||
|
||||
namespace cs {
|
||||
static constexpr unsigned SinkMask = CS_SINK_CV | CS_SINK_RAW;
|
||||
|
||||
void SetSinkDescription(CS_Sink sink, std::string_view description,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || (data->kind & SinkMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
data->sink->SetDescription(description);
|
||||
}
|
||||
|
||||
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 data->sink->GetError();
|
||||
}
|
||||
|
||||
std::string_view GetSinkError(CS_Sink sink, wpi::SmallVectorImpl<char>& buf,
|
||||
CS_Status* status) {
|
||||
auto data = Instance::GetInstance().GetSink(sink);
|
||||
if (!data || (data->kind & SinkMask) == 0) {
|
||||
*status = CS_INVALID_HANDLE;
|
||||
return {};
|
||||
}
|
||||
return 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;
|
||||
}
|
||||
data->sink->SetEnabled(enabled);
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
extern "C" {
|
||||
void CS_SetSinkDescription(CS_Sink sink, const struct WPI_String* description,
|
||||
CS_Status* status) {
|
||||
return cs::SetSinkDescription(sink, wpi::to_string_view(description), status);
|
||||
}
|
||||
|
||||
void CS_GetSinkError(CS_Sink sink, struct WPI_String* error,
|
||||
CS_Status* status) {
|
||||
wpi::SmallString<128> buf;
|
||||
cs::ConvertToC(error, cs::GetSinkError(sink, buf, status));
|
||||
}
|
||||
|
||||
void CS_SetSinkEnabled(CS_Sink sink, CS_Bool enabled, CS_Status* status) {
|
||||
return cs::SetSinkEnabled(sink, enabled, status);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -196,6 +196,8 @@ bool SourceImpl::SetConfigJson(const wpi::json& config, CS_Status* status) {
|
||||
mode.pixelFormat = cs::VideoMode::kRGB565;
|
||||
} else if (wpi::equals_lower(str, "bgr")) {
|
||||
mode.pixelFormat = cs::VideoMode::kBGR;
|
||||
} else if (wpi::equals_lower(str, "bgra")) {
|
||||
mode.pixelFormat = cs::VideoMode::kBGRA;
|
||||
} else if (wpi::equals_lower(str, "gray")) {
|
||||
mode.pixelFormat = cs::VideoMode::kGray;
|
||||
} else if (wpi::equals_lower(str, "y16")) {
|
||||
@@ -361,6 +363,9 @@ wpi::json SourceImpl::GetConfigJsonObject(CS_Status* status) {
|
||||
case VideoMode::kBGR:
|
||||
pixelFormat = "bgr";
|
||||
break;
|
||||
case VideoMode::kBGRA:
|
||||
pixelFormat = "bgra";
|
||||
break;
|
||||
case VideoMode::kGray:
|
||||
pixelFormat = "gray";
|
||||
break;
|
||||
@@ -450,6 +455,15 @@ std::unique_ptr<Image> SourceImpl::AllocImage(
|
||||
|
||||
void SourceImpl::PutFrame(VideoMode::PixelFormat pixelFormat, int width,
|
||||
int height, std::string_view data, Frame::Time time) {
|
||||
if (pixelFormat == VideoMode::PixelFormat::kBGRA) {
|
||||
// Write BGRA as BGR to save a copy
|
||||
auto image =
|
||||
CreateImageFromBGRA(this, width, height, width * 4,
|
||||
reinterpret_cast<const uint8_t*>(data.data()));
|
||||
PutFrame(std::move(image), time);
|
||||
return;
|
||||
}
|
||||
|
||||
auto image = AllocImage(pixelFormat, width, height, data.size());
|
||||
|
||||
// Copy in image data
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "cscore_c.h" // NOLINT(build/include_order)
|
||||
|
||||
#include <wpi/MemAlloc.h>
|
||||
|
||||
#include "c_util.h"
|
||||
#include "cscore_cpp.h"
|
||||
|
||||
@@ -11,56 +13,53 @@ using namespace cs;
|
||||
|
||||
static void ConvertToC(CS_UsbCameraInfo* out, const UsbCameraInfo& in) {
|
||||
out->dev = in.dev;
|
||||
out->path = ConvertToC(in.path);
|
||||
out->name = ConvertToC(in.name);
|
||||
out->otherPaths = static_cast<char**>(
|
||||
wpi::safe_malloc(in.otherPaths.size() * sizeof(char*)));
|
||||
cs::ConvertToC(&out->path, in.path);
|
||||
cs::ConvertToC(&out->name, in.name);
|
||||
out->otherPaths = WPI_AllocateStringArray(in.otherPaths.size());
|
||||
out->otherPathsCount = in.otherPaths.size();
|
||||
for (size_t i = 0; i < in.otherPaths.size(); ++i) {
|
||||
out->otherPaths[i] = cs::ConvertToC(in.otherPaths[i]);
|
||||
cs::ConvertToC(&out->otherPaths[i], in.otherPaths[i]);
|
||||
}
|
||||
out->vendorId = in.vendorId;
|
||||
out->productId = in.productId;
|
||||
}
|
||||
|
||||
static void FreeUsbCameraInfo(CS_UsbCameraInfo* info) {
|
||||
std::free(info->path);
|
||||
std::free(info->name);
|
||||
WPI_FreeString(&info->path);
|
||||
WPI_FreeString(&info->name);
|
||||
for (int i = 0; i < info->otherPathsCount; ++i) {
|
||||
std::free(info->otherPaths[i]);
|
||||
WPI_FreeString(&info->otherPaths[i]);
|
||||
}
|
||||
std::free(info->otherPaths);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
CS_Source CS_CreateUsbCameraDev(const char* name, int dev, CS_Status* status) {
|
||||
return cs::CreateUsbCameraDev(name, dev, status);
|
||||
CS_Source CS_CreateUsbCameraDev(const struct WPI_String* name, int dev,
|
||||
CS_Status* status) {
|
||||
return cs::CreateUsbCameraDev(wpi::to_string_view(name), dev, status);
|
||||
}
|
||||
|
||||
CS_Source CS_CreateUsbCameraPath(const char* name, const char* path,
|
||||
CS_Source CS_CreateUsbCameraPath(const struct WPI_String* name,
|
||||
const struct WPI_String* path,
|
||||
CS_Status* status) {
|
||||
return cs::CreateUsbCameraPath(name, path, status);
|
||||
return cs::CreateUsbCameraPath(wpi::to_string_view(name),
|
||||
wpi::to_string_view(path), status);
|
||||
}
|
||||
|
||||
void CS_SetUsbCameraPath(CS_Source source, const char* path,
|
||||
void CS_SetUsbCameraPath(CS_Source source, const struct WPI_String* path,
|
||||
CS_Status* status) {
|
||||
cs::SetUsbCameraPath(source, path, status);
|
||||
cs::SetUsbCameraPath(source, wpi::to_string_view(path), status);
|
||||
}
|
||||
|
||||
char* CS_GetUsbCameraPath(CS_Source source, CS_Status* status) {
|
||||
return ConvertToC(cs::GetUsbCameraPath(source, status));
|
||||
void CS_GetUsbCameraPath(CS_Source source, WPI_String* path,
|
||||
CS_Status* status) {
|
||||
ConvertToC(path, cs::GetUsbCameraPath(source, status));
|
||||
}
|
||||
|
||||
CS_UsbCameraInfo* CS_GetUsbCameraInfo(CS_Source source, CS_Status* status) {
|
||||
auto info = cs::GetUsbCameraInfo(source, status);
|
||||
if (*status != CS_OK) {
|
||||
return nullptr;
|
||||
}
|
||||
CS_UsbCameraInfo* out = static_cast<CS_UsbCameraInfo*>(
|
||||
wpi::safe_malloc(sizeof(CS_UsbCameraInfo)));
|
||||
ConvertToC(out, info);
|
||||
return out;
|
||||
void CS_GetUsbCameraInfo(CS_Source source, CS_UsbCameraInfo* info,
|
||||
CS_Status* status) {
|
||||
auto info_cpp = cs::GetUsbCameraInfo(source, status);
|
||||
ConvertToC(info, info_cpp);
|
||||
}
|
||||
|
||||
CS_UsbCameraInfo* CS_EnumerateUsbCameras(int* count, CS_Status* status) {
|
||||
@@ -89,7 +88,6 @@ void CS_FreeUsbCameraInfo(CS_UsbCameraInfo* info) {
|
||||
return;
|
||||
}
|
||||
FreeUsbCameraInfo(info);
|
||||
std::free(info);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -5,19 +5,16 @@
|
||||
#ifndef CSCORE_C_UTIL_H_
|
||||
#define CSCORE_C_UTIL_H_
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
|
||||
#include <wpi/MemAlloc.h>
|
||||
#include <wpi/string.h>
|
||||
|
||||
namespace cs {
|
||||
|
||||
inline char* ConvertToC(std::string_view in) {
|
||||
char* out = static_cast<char*>(wpi::safe_malloc(in.size() + 1));
|
||||
std::memmove(out, in.data(), in.size());
|
||||
out[in.size()] = '\0';
|
||||
return out;
|
||||
inline void ConvertToC(struct WPI_String* output, std::string_view str) {
|
||||
char* write = WPI_AllocateString(output, str.size());
|
||||
std::memcpy(write, str.data(), str.size());
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <wpi/MemAlloc.h>
|
||||
#include <wpi/SmallString.h>
|
||||
|
||||
@@ -20,12 +19,12 @@ static CS_Event ConvertToC(const cs::RawEvent& rawEvent) {
|
||||
event.kind = static_cast<CS_EventKind>(static_cast<int>(rawEvent.kind));
|
||||
event.source = rawEvent.sourceHandle;
|
||||
event.sink = rawEvent.sinkHandle;
|
||||
event.name = rawEvent.name.c_str();
|
||||
cs::ConvertToC(&event.name, rawEvent.name);
|
||||
event.mode = rawEvent.mode;
|
||||
event.property = rawEvent.propertyHandle;
|
||||
event.propertyKind = rawEvent.propertyKind;
|
||||
event.value = rawEvent.value;
|
||||
event.valueStr = rawEvent.valueStr.c_str();
|
||||
cs::ConvertToC(&event.name, rawEvent.valueStr);
|
||||
event.listener = rawEvent.listener;
|
||||
return event;
|
||||
}
|
||||
@@ -54,13 +53,10 @@ CS_PropertyKind CS_GetPropertyKind(CS_Property property, CS_Status* status) {
|
||||
return cs::GetPropertyKind(property, status);
|
||||
}
|
||||
|
||||
char* CS_GetPropertyName(CS_Property property, CS_Status* status) {
|
||||
void CS_GetPropertyName(CS_Property property, WPI_String* name,
|
||||
CS_Status* status) {
|
||||
wpi::SmallString<128> buf;
|
||||
auto str = cs::GetPropertyName(property, buf, status);
|
||||
if (*status != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return cs::ConvertToC(str);
|
||||
cs::ConvertToC(name, cs::GetPropertyName(property, buf, status));
|
||||
}
|
||||
|
||||
int CS_GetProperty(CS_Property property, CS_Status* status) {
|
||||
@@ -87,28 +83,24 @@ int CS_GetPropertyDefault(CS_Property property, CS_Status* status) {
|
||||
return cs::GetPropertyDefault(property, status);
|
||||
}
|
||||
|
||||
char* CS_GetStringProperty(CS_Property property, CS_Status* status) {
|
||||
wpi::SmallString<128> buf;
|
||||
auto str = cs::GetStringProperty(property, buf, status);
|
||||
if (*status != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return cs::ConvertToC(str);
|
||||
}
|
||||
|
||||
void CS_SetStringProperty(CS_Property property, const char* value,
|
||||
void CS_GetStringProperty(CS_Property property, WPI_String* value,
|
||||
CS_Status* status) {
|
||||
return cs::SetStringProperty(property, value, status);
|
||||
wpi::SmallString<128> buf;
|
||||
cs::ConvertToC(value, cs::GetStringProperty(property, buf, status));
|
||||
}
|
||||
|
||||
char** CS_GetEnumPropertyChoices(CS_Property property, int* count,
|
||||
CS_Status* status) {
|
||||
void CS_SetStringProperty(CS_Property property, const struct WPI_String* value,
|
||||
CS_Status* status) {
|
||||
return cs::SetStringProperty(property, wpi::to_string_view(value), status);
|
||||
}
|
||||
|
||||
WPI_String* CS_GetEnumPropertyChoices(CS_Property property, int* count,
|
||||
CS_Status* status) {
|
||||
auto choices = cs::GetEnumPropertyChoices(property, status);
|
||||
char** out =
|
||||
static_cast<char**>(wpi::safe_malloc(choices.size() * sizeof(char*)));
|
||||
WPI_String* out = WPI_AllocateStringArray(choices.size());
|
||||
*count = choices.size();
|
||||
for (size_t i = 0; i < choices.size(); ++i) {
|
||||
out[i] = cs::ConvertToC(choices[i]);
|
||||
cs::ConvertToC(&out[i], choices[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -117,22 +109,15 @@ CS_SourceKind CS_GetSourceKind(CS_Source source, CS_Status* status) {
|
||||
return cs::GetSourceKind(source, status);
|
||||
}
|
||||
|
||||
char* CS_GetSourceName(CS_Source source, CS_Status* status) {
|
||||
void CS_GetSourceName(CS_Source source, WPI_String* name, CS_Status* status) {
|
||||
wpi::SmallString<128> buf;
|
||||
auto str = cs::GetSourceName(source, buf, status);
|
||||
if (*status != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return cs::ConvertToC(str);
|
||||
cs::ConvertToC(name, cs::GetSourceName(source, buf, status));
|
||||
}
|
||||
|
||||
char* CS_GetSourceDescription(CS_Source source, CS_Status* status) {
|
||||
void CS_GetSourceDescription(CS_Source source, WPI_String* description,
|
||||
CS_Status* status) {
|
||||
wpi::SmallString<128> buf;
|
||||
auto str = cs::GetSourceDescription(source, buf, status);
|
||||
if (*status != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return cs::ConvertToC(str);
|
||||
cs::ConvertToC(description, cs::GetSourceDescription(source, buf, status));
|
||||
}
|
||||
|
||||
uint64_t CS_GetSourceLastFrameTime(CS_Source source, CS_Status* status) {
|
||||
@@ -153,9 +138,10 @@ CS_Bool CS_IsSourceEnabled(CS_Source source, CS_Status* status) {
|
||||
return cs::IsSourceEnabled(source, status);
|
||||
}
|
||||
|
||||
CS_Property CS_GetSourceProperty(CS_Source source, const char* name,
|
||||
CS_Property CS_GetSourceProperty(CS_Source source,
|
||||
const struct WPI_String* name,
|
||||
CS_Status* status) {
|
||||
return cs::GetSourceProperty(source, name, status);
|
||||
return cs::GetSourceProperty(source, wpi::to_string_view(name), status);
|
||||
}
|
||||
|
||||
CS_Property* CS_EnumerateSourceProperties(CS_Source source, int* count,
|
||||
@@ -210,13 +196,15 @@ CS_Bool CS_SetSourceFPS(CS_Source source, int fps, CS_Status* status) {
|
||||
return cs::SetSourceFPS(source, fps, status);
|
||||
}
|
||||
|
||||
CS_Bool CS_SetSourceConfigJson(CS_Source source, const char* config,
|
||||
CS_Bool CS_SetSourceConfigJson(CS_Source source,
|
||||
const struct WPI_String* config,
|
||||
CS_Status* status) {
|
||||
return cs::SetSourceConfigJson(source, config, status);
|
||||
return cs::SetSourceConfigJson(source, wpi::to_string_view(config), status);
|
||||
}
|
||||
|
||||
char* CS_GetSourceConfigJson(CS_Source source, CS_Status* status) {
|
||||
return cs::ConvertToC(cs::GetSourceConfigJson(source, status));
|
||||
void CS_GetSourceConfigJson(CS_Source source, WPI_String* config,
|
||||
CS_Status* status) {
|
||||
cs::ConvertToC(config, cs::GetSourceConfigJson(source, status));
|
||||
}
|
||||
|
||||
CS_VideoMode* CS_EnumerateSourceVideoModes(CS_Source source, int* count,
|
||||
@@ -287,27 +275,20 @@ CS_SinkKind CS_GetSinkKind(CS_Sink sink, CS_Status* status) {
|
||||
return cs::GetSinkKind(sink, status);
|
||||
}
|
||||
|
||||
char* CS_GetSinkName(CS_Sink sink, CS_Status* status) {
|
||||
void CS_GetSinkName(CS_Sink sink, WPI_String* name, CS_Status* status) {
|
||||
wpi::SmallString<128> buf;
|
||||
auto str = cs::GetSinkName(sink, buf, status);
|
||||
if (*status != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return cs::ConvertToC(str);
|
||||
cs::ConvertToC(name, cs::GetSinkName(sink, buf, status));
|
||||
}
|
||||
|
||||
char* CS_GetSinkDescription(CS_Sink sink, CS_Status* status) {
|
||||
void CS_GetSinkDescription(CS_Sink sink, WPI_String* description,
|
||||
CS_Status* status) {
|
||||
wpi::SmallString<128> buf;
|
||||
auto str = cs::GetSinkDescription(sink, buf, status);
|
||||
if (*status != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return cs::ConvertToC(str);
|
||||
cs::ConvertToC(description, cs::GetSinkDescription(sink, buf, status));
|
||||
}
|
||||
|
||||
CS_Property CS_GetSinkProperty(CS_Sink sink, const char* name,
|
||||
CS_Property CS_GetSinkProperty(CS_Sink sink, const struct WPI_String* name,
|
||||
CS_Status* status) {
|
||||
return cs::GetSinkProperty(sink, name, status);
|
||||
return cs::GetSinkProperty(sink, wpi::to_string_view(name), status);
|
||||
}
|
||||
|
||||
CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
|
||||
@@ -321,13 +302,13 @@ CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
|
||||
return out;
|
||||
}
|
||||
|
||||
CS_Bool CS_SetSinkConfigJson(CS_Sink sink, const char* config,
|
||||
CS_Bool CS_SetSinkConfigJson(CS_Sink sink, const struct WPI_String* config,
|
||||
CS_Status* status) {
|
||||
return cs::SetSinkConfigJson(sink, config, status);
|
||||
return cs::SetSinkConfigJson(sink, wpi::to_string_view(config), status);
|
||||
}
|
||||
|
||||
char* CS_GetSinkConfigJson(CS_Sink sink, CS_Status* status) {
|
||||
return cs::ConvertToC(cs::GetSinkConfigJson(sink, status));
|
||||
void CS_GetSinkConfigJson(CS_Sink sink, WPI_String* config, CS_Status* status) {
|
||||
cs::ConvertToC(config, cs::GetSinkConfigJson(sink, status));
|
||||
}
|
||||
|
||||
void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status) {
|
||||
@@ -338,9 +319,10 @@ CS_Source CS_GetSinkSource(CS_Sink sink, CS_Status* status) {
|
||||
return cs::GetSinkSource(sink, status);
|
||||
}
|
||||
|
||||
CS_Property CS_GetSinkSourceProperty(CS_Sink sink, const char* name,
|
||||
CS_Property CS_GetSinkSourceProperty(CS_Sink sink,
|
||||
const struct WPI_String* name,
|
||||
CS_Status* status) {
|
||||
return cs::GetSinkSourceProperty(sink, name, status);
|
||||
return cs::GetSinkSourceProperty(sink, wpi::to_string_view(name), status);
|
||||
}
|
||||
|
||||
CS_Sink CS_CopySink(CS_Sink sink, CS_Status* status) {
|
||||
@@ -361,7 +343,7 @@ void CS_SetListenerOnExit(void (*onExit)(void* data), void* data) {
|
||||
|
||||
CS_Listener CS_AddListener(void* data,
|
||||
void (*callback)(void* data, const CS_Event* event),
|
||||
int eventMask, int immediateNotify,
|
||||
int eventMask, CS_Bool immediateNotify,
|
||||
CS_Status* status) {
|
||||
return cs::AddListener(
|
||||
[=](const cs::RawEvent& rawEvent) {
|
||||
@@ -436,8 +418,15 @@ double CS_GetTelemetryAverageValue(CS_Handle handle, CS_TelemetryKind kind,
|
||||
return cs::GetTelemetryAverageValue(handle, kind, status);
|
||||
}
|
||||
|
||||
void CS_SetLogger(CS_LogFunc func, unsigned int min_level) {
|
||||
cs::SetLogger(func, min_level);
|
||||
void CS_SetLogger(CS_LogFunc func, void* data, unsigned int min_level) {
|
||||
cs::SetLogger(
|
||||
[=](unsigned int level, const char* file, unsigned int line,
|
||||
const char* msg) {
|
||||
auto fileStr = wpi::make_string(file);
|
||||
auto msgStr = wpi::make_string(msg);
|
||||
func(data, level, &fileStr, line, &msgStr);
|
||||
},
|
||||
min_level);
|
||||
}
|
||||
|
||||
void CS_SetDefaultLogger(unsigned int min_level) {
|
||||
@@ -494,20 +483,6 @@ void CS_ReleaseEnumeratedSinks(CS_Sink* sinks, int count) {
|
||||
std::free(sinks);
|
||||
}
|
||||
|
||||
void CS_FreeString(char* str) {
|
||||
std::free(str);
|
||||
}
|
||||
|
||||
void CS_FreeEnumPropertyChoices(char** choices, int count) {
|
||||
if (!choices) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
std::free(choices[i]);
|
||||
}
|
||||
std::free(choices);
|
||||
}
|
||||
|
||||
void CS_FreeEnumeratedProperties(CS_Property* properties, int count) {
|
||||
std::free(properties);
|
||||
}
|
||||
@@ -516,29 +491,18 @@ void CS_FreeEnumeratedVideoModes(CS_VideoMode* modes, int count) {
|
||||
std::free(modes);
|
||||
}
|
||||
|
||||
char* CS_GetHostname(void) {
|
||||
return cs::ConvertToC(cs::GetHostname());
|
||||
void CS_GetHostname(struct WPI_String* hostname) {
|
||||
cs::ConvertToC(hostname, cs::GetHostname());
|
||||
}
|
||||
|
||||
char** CS_GetNetworkInterfaces(int* count) {
|
||||
WPI_String* CS_GetNetworkInterfaces(int* count) {
|
||||
auto interfaces = cs::GetNetworkInterfaces();
|
||||
char** out =
|
||||
static_cast<char**>(wpi::safe_malloc(interfaces.size() * sizeof(char*)));
|
||||
WPI_String* out = WPI_AllocateStringArray(interfaces.size());
|
||||
*count = interfaces.size();
|
||||
for (size_t i = 0; i < interfaces.size(); ++i) {
|
||||
out[i] = cs::ConvertToC(interfaces[i]);
|
||||
cs::ConvertToC(&out[i], interfaces[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void CS_FreeNetworkInterfaces(char** interfaces, int count) {
|
||||
if (!interfaces) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
std::free(interfaces[i]);
|
||||
}
|
||||
std::free(interfaces);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -6,14 +6,12 @@
|
||||
#include <span>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
|
||||
#define WPI_RAWFRAME_JNI
|
||||
#include <wpi/RawFrame.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/jni_util.h>
|
||||
|
||||
#include "cscore_cv.h"
|
||||
#include "cscore_raw.h"
|
||||
#include "cscore_runloop.h"
|
||||
#include "edu_wpi_first_cscore_CameraServerJNI.h"
|
||||
@@ -82,32 +80,6 @@ static void ListenerOnExit() {
|
||||
jvm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
/// throw java exception
|
||||
static void ThrowJavaException(JNIEnv* env, const std::exception* e) {
|
||||
wpi::SmallString<128> what;
|
||||
jclass je = nullptr;
|
||||
|
||||
if (e) {
|
||||
const char* exception_type = "std::exception";
|
||||
|
||||
if (dynamic_cast<const cv::Exception*>(e)) {
|
||||
exception_type = "cv::Exception";
|
||||
je = env->FindClass("org/opencv/core/CvException");
|
||||
}
|
||||
|
||||
what = exception_type;
|
||||
what += ": ";
|
||||
what += e->what();
|
||||
} else {
|
||||
what = "unknown exception";
|
||||
}
|
||||
|
||||
if (!je) {
|
||||
je = exceptionEx;
|
||||
}
|
||||
env->ThrowNew(je, what.c_str());
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
@@ -593,40 +565,15 @@ Java_edu_wpi_first_cscore_CameraServerJNI_createHttpCameraMulti
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerCvJNI
|
||||
* Method: createCvSource
|
||||
* Signature: (Ljava/lang/String;IIII)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerCvJNI_createCvSource
|
||||
(JNIEnv* env, jclass, jstring name, jint pixelFormat, jint width, jint height,
|
||||
jint fps)
|
||||
{
|
||||
if (!name) {
|
||||
nullPointerEx.Throw(env, "name cannot be null");
|
||||
return 0;
|
||||
}
|
||||
CS_Status status = 0;
|
||||
auto val = cs::CreateCvSource(
|
||||
JStringRef{env, name}.str(),
|
||||
cs::VideoMode{static_cast<cs::VideoMode::PixelFormat>(pixelFormat),
|
||||
static_cast<int>(width), static_cast<int>(height),
|
||||
static_cast<int>(fps)},
|
||||
&status);
|
||||
CheckStatus(env, status);
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerJNI
|
||||
* Method: createRawSource
|
||||
* Signature: (Ljava/lang/String;IIII)I
|
||||
* Signature: (Ljava/lang/String;ZIIII)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_createRawSource
|
||||
(JNIEnv* env, jclass, jstring name, jint pixelFormat, jint width, jint height,
|
||||
jint fps)
|
||||
(JNIEnv* env, jclass, jstring name, jboolean isCv, jint pixelFormat,
|
||||
jint width, jint height, jint fps)
|
||||
{
|
||||
if (!name) {
|
||||
nullPointerEx.Throw(env, "name cannot be null");
|
||||
@@ -634,7 +581,7 @@ Java_edu_wpi_first_cscore_CameraServerJNI_createRawSource
|
||||
}
|
||||
CS_Status status = 0;
|
||||
auto val = cs::CreateRawSource(
|
||||
JStringRef{env, name}.str(),
|
||||
JStringRef{env, name}.str(), isCv,
|
||||
cs::VideoMode{static_cast<cs::VideoMode::PixelFormat>(pixelFormat),
|
||||
static_cast<int>(width), static_cast<int>(height),
|
||||
static_cast<int>(fps)},
|
||||
@@ -1202,27 +1149,6 @@ Java_edu_wpi_first_cscore_CameraServerJNI_getHttpCameraUrls
|
||||
return MakeJStringArray(env, arr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerCvJNI
|
||||
* Method: putSourceFrame
|
||||
* Signature: (IJ)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerCvJNI_putSourceFrame
|
||||
(JNIEnv* env, jclass, jint source, jlong imageNativeObj)
|
||||
{
|
||||
try {
|
||||
cv::Mat& image = *((cv::Mat*)imageNativeObj);
|
||||
CS_Status status = 0;
|
||||
cs::PutSourceFrame(source, image, &status);
|
||||
CheckStatus(env, status);
|
||||
} catch (const std::exception& e) {
|
||||
ThrowJavaException(env, &e);
|
||||
} catch (...) {
|
||||
ThrowJavaException(env, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerJNI
|
||||
* Method: putRawSourceFrame
|
||||
@@ -1421,42 +1347,21 @@ Java_edu_wpi_first_cscore_CameraServerJNI_createMjpegServer
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerCvJNI
|
||||
* Method: createCvSink
|
||||
* Signature: (Ljava/lang/String;I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerCvJNI_createCvSink
|
||||
(JNIEnv* env, jclass, jstring name, jint pixelFormat)
|
||||
{
|
||||
if (!name) {
|
||||
nullPointerEx.Throw(env, "name cannot be null");
|
||||
return 0;
|
||||
}
|
||||
CS_Status status = 0;
|
||||
auto val = cs::CreateCvSink(
|
||||
JStringRef{env, name}.str(),
|
||||
static_cast<cs::VideoMode::PixelFormat>(pixelFormat), &status);
|
||||
CheckStatus(env, status);
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerJNI
|
||||
* Method: createRawSink
|
||||
* Signature: (Ljava/lang/String;)I
|
||||
* Signature: (Ljava/lang/String;Z)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerJNI_createRawSink
|
||||
(JNIEnv* env, jclass, jstring name)
|
||||
(JNIEnv* env, jclass, jstring name, jboolean isCv)
|
||||
{
|
||||
if (!name) {
|
||||
nullPointerEx.Throw(env, "name cannot be null");
|
||||
return 0;
|
||||
}
|
||||
CS_Status status = 0;
|
||||
auto val = cs::CreateRawSink(JStringRef{env, name}.str(), &status);
|
||||
auto val = cs::CreateRawSink(JStringRef{env, name}.str(), isCv, &status);
|
||||
CheckStatus(env, status);
|
||||
return val;
|
||||
}
|
||||
@@ -1707,54 +1612,6 @@ Java_edu_wpi_first_cscore_CameraServerJNI_setSinkDescription
|
||||
CheckStatus(env, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerCvJNI
|
||||
* Method: grabSinkFrame
|
||||
* Signature: (IJ)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerCvJNI_grabSinkFrame
|
||||
(JNIEnv* env, jclass, jint sink, jlong imageNativeObj)
|
||||
{
|
||||
try {
|
||||
cv::Mat& image = *((cv::Mat*)imageNativeObj);
|
||||
CS_Status status = 0;
|
||||
auto rv = cs::GrabSinkFrame(sink, image, &status);
|
||||
CheckStatus(env, status);
|
||||
return rv;
|
||||
} catch (const std::exception& e) {
|
||||
ThrowJavaException(env, &e);
|
||||
return 0;
|
||||
} catch (...) {
|
||||
ThrowJavaException(env, nullptr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerCvJNI
|
||||
* Method: grabSinkFrameTimeout
|
||||
* Signature: (IJD)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_edu_wpi_first_cscore_CameraServerCvJNI_grabSinkFrameTimeout
|
||||
(JNIEnv* env, jclass, jint sink, jlong imageNativeObj, jdouble timeout)
|
||||
{
|
||||
try {
|
||||
cv::Mat& image = *((cv::Mat*)imageNativeObj);
|
||||
CS_Status status = 0;
|
||||
auto rv = cs::GrabSinkFrameTimeout(sink, image, timeout, &status);
|
||||
CheckStatus(env, status);
|
||||
return rv;
|
||||
} catch (const std::exception& e) {
|
||||
ThrowJavaException(env, &e);
|
||||
return 0;
|
||||
} catch (...) {
|
||||
ThrowJavaException(env, nullptr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_cscore_CameraServerJNI
|
||||
* Method: grabRawSinkFrame
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <wpi/RawFrame.h>
|
||||
#include <wpi/string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstddef>
|
||||
@@ -204,7 +205,7 @@ struct CS_Event {
|
||||
CS_Sink sink;
|
||||
|
||||
// Source/sink/property name
|
||||
const char* name;
|
||||
struct WPI_String name;
|
||||
|
||||
// Fields for CS_SOURCE_VIDEOMODE_CHANGED event
|
||||
CS_VideoMode mode;
|
||||
@@ -213,7 +214,7 @@ struct CS_Event {
|
||||
CS_Property property;
|
||||
enum CS_PropertyKind propertyKind;
|
||||
int value;
|
||||
const char* valueStr;
|
||||
struct WPI_String valueStr;
|
||||
|
||||
// Listener that was triggered
|
||||
CS_Listener listener;
|
||||
@@ -224,10 +225,10 @@ struct CS_Event {
|
||||
*/
|
||||
typedef struct CS_UsbCameraInfo {
|
||||
int dev;
|
||||
char* path;
|
||||
char* name;
|
||||
struct WPI_String path;
|
||||
struct WPI_String name;
|
||||
int otherPathsCount;
|
||||
char** otherPaths;
|
||||
struct WPI_String* otherPaths;
|
||||
int vendorId;
|
||||
int productId;
|
||||
} CS_UsbCameraInfo;
|
||||
@@ -238,34 +239,40 @@ typedef struct CS_UsbCameraInfo {
|
||||
*/
|
||||
enum CS_PropertyKind CS_GetPropertyKind(CS_Property property,
|
||||
CS_Status* status);
|
||||
char* CS_GetPropertyName(CS_Property property, CS_Status* status);
|
||||
void CS_GetPropertyName(CS_Property property, struct WPI_String* name,
|
||||
CS_Status* status);
|
||||
int CS_GetProperty(CS_Property property, CS_Status* status);
|
||||
void CS_SetProperty(CS_Property property, int value, CS_Status* status);
|
||||
int CS_GetPropertyMin(CS_Property property, CS_Status* status);
|
||||
int CS_GetPropertyMax(CS_Property property, CS_Status* status);
|
||||
int CS_GetPropertyStep(CS_Property property, CS_Status* status);
|
||||
int CS_GetPropertyDefault(CS_Property property, CS_Status* status);
|
||||
char* CS_GetStringProperty(CS_Property property, CS_Status* status);
|
||||
void CS_SetStringProperty(CS_Property property, const char* value,
|
||||
void CS_GetStringProperty(CS_Property property, struct WPI_String* value,
|
||||
CS_Status* status);
|
||||
char** CS_GetEnumPropertyChoices(CS_Property property, int* count,
|
||||
CS_Status* status);
|
||||
void CS_SetStringProperty(CS_Property property, const struct WPI_String* value,
|
||||
CS_Status* status);
|
||||
struct WPI_String* CS_GetEnumPropertyChoices(CS_Property property, int* count,
|
||||
CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup cscore_source_create_cfunc Source Creation Functions
|
||||
* @{
|
||||
*/
|
||||
CS_Source CS_CreateUsbCameraDev(const char* name, int dev, CS_Status* status);
|
||||
CS_Source CS_CreateUsbCameraPath(const char* name, const char* path,
|
||||
CS_Source CS_CreateUsbCameraDev(const struct WPI_String* name, int dev,
|
||||
CS_Status* status);
|
||||
CS_Source CS_CreateUsbCameraPath(const struct WPI_String* name,
|
||||
const struct WPI_String* path,
|
||||
CS_Status* status);
|
||||
CS_Source CS_CreateHttpCamera(const char* name, const char* url,
|
||||
CS_Source CS_CreateHttpCamera(const struct WPI_String* name,
|
||||
const struct WPI_String* url,
|
||||
enum CS_HttpCameraKind kind, CS_Status* status);
|
||||
CS_Source CS_CreateHttpCameraMulti(const char* name, const char** urls,
|
||||
int count, enum CS_HttpCameraKind kind,
|
||||
CS_Source CS_CreateHttpCameraMulti(const struct WPI_String* name,
|
||||
const struct WPI_String* urls, int count,
|
||||
enum CS_HttpCameraKind kind,
|
||||
CS_Status* status);
|
||||
CS_Source CS_CreateCvSource(const char* name, const CS_VideoMode* mode,
|
||||
CS_Status* status);
|
||||
CS_Source CS_CreateCvSource(const struct WPI_String* name,
|
||||
const CS_VideoMode* mode, CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@@ -273,15 +280,18 @@ CS_Source CS_CreateCvSource(const char* name, const CS_VideoMode* mode,
|
||||
* @{
|
||||
*/
|
||||
enum CS_SourceKind CS_GetSourceKind(CS_Source source, CS_Status* status);
|
||||
char* CS_GetSourceName(CS_Source source, CS_Status* status);
|
||||
char* CS_GetSourceDescription(CS_Source source, CS_Status* status);
|
||||
void CS_GetSourceName(CS_Source source, struct WPI_String* name,
|
||||
CS_Status* status);
|
||||
void CS_GetSourceDescription(CS_Source source, struct WPI_String* description,
|
||||
CS_Status* status);
|
||||
uint64_t CS_GetSourceLastFrameTime(CS_Source source, CS_Status* status);
|
||||
void CS_SetSourceConnectionStrategy(CS_Source source,
|
||||
enum CS_ConnectionStrategy strategy,
|
||||
CS_Status* status);
|
||||
CS_Bool CS_IsSourceConnected(CS_Source source, CS_Status* status);
|
||||
CS_Bool CS_IsSourceEnabled(CS_Source source, CS_Status* status);
|
||||
CS_Property CS_GetSourceProperty(CS_Source source, const char* name,
|
||||
CS_Property CS_GetSourceProperty(CS_Source source,
|
||||
const struct WPI_String* name,
|
||||
CS_Status* status);
|
||||
CS_Property* CS_EnumerateSourceProperties(CS_Source source, int* count,
|
||||
CS_Status* status);
|
||||
@@ -299,9 +309,11 @@ CS_Bool CS_SetSourcePixelFormat(CS_Source source,
|
||||
CS_Bool CS_SetSourceResolution(CS_Source source, int width, int height,
|
||||
CS_Status* status);
|
||||
CS_Bool CS_SetSourceFPS(CS_Source source, int fps, CS_Status* status);
|
||||
CS_Bool CS_SetSourceConfigJson(CS_Source source, const char* config,
|
||||
CS_Bool CS_SetSourceConfigJson(CS_Source source,
|
||||
const struct WPI_String* config,
|
||||
CS_Status* status);
|
||||
char* CS_GetSourceConfigJson(CS_Source source, CS_Status* status);
|
||||
void CS_GetSourceConfigJson(CS_Source source, struct WPI_String* config,
|
||||
CS_Status* status);
|
||||
CS_VideoMode* CS_EnumerateSourceVideoModes(CS_Source source, int* count,
|
||||
CS_Status* status);
|
||||
CS_Sink* CS_EnumerateSourceSinks(CS_Source source, int* count,
|
||||
@@ -330,9 +342,12 @@ void CS_SetCameraExposureManual(CS_Source source, int value, CS_Status* status);
|
||||
* @defgroup cscore_usbcamera_cfunc UsbCamera Source Functions
|
||||
* @{
|
||||
*/
|
||||
void CS_SetUsbCameraPath(CS_Source source, const char* path, CS_Status* status);
|
||||
char* CS_GetUsbCameraPath(CS_Source source, CS_Status* status);
|
||||
CS_UsbCameraInfo* CS_GetUsbCameraInfo(CS_Source source, CS_Status* status);
|
||||
void CS_SetUsbCameraPath(CS_Source source, const struct WPI_String* path,
|
||||
CS_Status* status);
|
||||
void CS_GetUsbCameraPath(CS_Source source, struct WPI_String* path,
|
||||
CS_Status* status);
|
||||
void CS_GetUsbCameraInfo(CS_Source source, CS_UsbCameraInfo* info,
|
||||
CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@@ -341,38 +356,43 @@ CS_UsbCameraInfo* CS_GetUsbCameraInfo(CS_Source source, CS_Status* status);
|
||||
*/
|
||||
enum CS_HttpCameraKind CS_GetHttpCameraKind(CS_Source source,
|
||||
CS_Status* status);
|
||||
void CS_SetHttpCameraUrls(CS_Source source, const char** urls, int count,
|
||||
CS_Status* status);
|
||||
char** CS_GetHttpCameraUrls(CS_Source source, int* count, CS_Status* status);
|
||||
void CS_SetHttpCameraUrls(CS_Source source, const struct WPI_String* urls,
|
||||
int count, CS_Status* status);
|
||||
struct WPI_String* CS_GetHttpCameraUrls(CS_Source source, int* count,
|
||||
CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup cscore_opencv_source_cfunc OpenCV Source Functions
|
||||
* @defgroup cscore_frame_source_cfunc Frame Source Functions
|
||||
* @{
|
||||
*/
|
||||
void CS_NotifySourceError(CS_Source source, const char* msg, CS_Status* status);
|
||||
void CS_NotifySourceError(CS_Source source, const struct WPI_String* msg,
|
||||
CS_Status* status);
|
||||
void CS_SetSourceConnected(CS_Source source, CS_Bool connected,
|
||||
CS_Status* status);
|
||||
void CS_SetSourceDescription(CS_Source source, const char* description,
|
||||
void CS_SetSourceDescription(CS_Source source,
|
||||
const struct WPI_String* description,
|
||||
CS_Status* status);
|
||||
CS_Property CS_CreateSourceProperty(CS_Source source, const char* name,
|
||||
CS_Property CS_CreateSourceProperty(CS_Source source,
|
||||
const struct WPI_String* name,
|
||||
enum CS_PropertyKind kind, int minimum,
|
||||
int maximum, int step, int defaultValue,
|
||||
int value, CS_Status* status);
|
||||
void CS_SetSourceEnumPropertyChoices(CS_Source source, CS_Property property,
|
||||
const char** choices, int count,
|
||||
CS_Status* status);
|
||||
const struct WPI_String* choices,
|
||||
int count, CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup cscore_sink_create_cfunc Sink Creation Functions
|
||||
* @{
|
||||
*/
|
||||
CS_Sink CS_CreateMjpegServer(const char* name, const char* listenAddress,
|
||||
int port, CS_Status* status);
|
||||
CS_Sink CS_CreateCvSink(const char* name, enum WPI_PixelFormat pixelFormat,
|
||||
CS_Status* status);
|
||||
CS_Sink CS_CreateCvSinkCallback(const char* name,
|
||||
CS_Sink CS_CreateMjpegServer(const struct WPI_String* name,
|
||||
const struct WPI_String* listenAddress, int port,
|
||||
CS_Status* status);
|
||||
CS_Sink CS_CreateCvSink(const struct WPI_String* name,
|
||||
enum WPI_PixelFormat pixelFormat, CS_Status* status);
|
||||
CS_Sink CS_CreateCvSinkCallback(const struct WPI_String* name,
|
||||
enum WPI_PixelFormat pixelFormat, void* data,
|
||||
void (*processFrame)(void* data, uint64_t time),
|
||||
CS_Status* status);
|
||||
@@ -383,18 +403,21 @@ CS_Sink CS_CreateCvSinkCallback(const char* name,
|
||||
* @{
|
||||
*/
|
||||
enum CS_SinkKind CS_GetSinkKind(CS_Sink sink, CS_Status* status);
|
||||
char* CS_GetSinkName(CS_Sink sink, CS_Status* status);
|
||||
char* CS_GetSinkDescription(CS_Sink sink, CS_Status* status);
|
||||
CS_Property CS_GetSinkProperty(CS_Sink sink, const char* name,
|
||||
void CS_GetSinkName(CS_Sink sink, struct WPI_String* name, CS_Status* status);
|
||||
void CS_GetSinkDescription(CS_Sink sink, struct WPI_String* description,
|
||||
CS_Status* status);
|
||||
CS_Property CS_GetSinkProperty(CS_Sink sink, const struct WPI_String* name,
|
||||
CS_Status* status);
|
||||
CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
|
||||
CS_Status* status);
|
||||
void CS_SetSinkSource(CS_Sink sink, CS_Source source, CS_Status* status);
|
||||
CS_Property CS_GetSinkSourceProperty(CS_Sink sink, const char* name,
|
||||
CS_Property CS_GetSinkSourceProperty(CS_Sink sink,
|
||||
const struct WPI_String* name,
|
||||
CS_Status* status);
|
||||
CS_Bool CS_SetSinkConfigJson(CS_Sink sink, const char* config,
|
||||
CS_Bool CS_SetSinkConfigJson(CS_Sink sink, const struct WPI_String* config,
|
||||
CS_Status* status);
|
||||
char* CS_GetSinkConfigJson(CS_Sink sink, CS_Status* status);
|
||||
void CS_GetSinkConfigJson(CS_Sink sink, struct WPI_String* config,
|
||||
CS_Status* status);
|
||||
CS_Source CS_GetSinkSource(CS_Sink sink, CS_Status* status);
|
||||
CS_Sink CS_CopySink(CS_Sink sink, CS_Status* status);
|
||||
void CS_ReleaseSink(CS_Sink sink, CS_Status* status);
|
||||
@@ -404,17 +427,19 @@ void CS_ReleaseSink(CS_Sink sink, CS_Status* status);
|
||||
* @defgroup cscore_mjpegserver_cfunc MjpegServer Sink Functions
|
||||
* @{
|
||||
*/
|
||||
char* CS_GetMjpegServerListenAddress(CS_Sink sink, CS_Status* status);
|
||||
void CS_GetMjpegServerListenAddress(CS_Sink sink,
|
||||
struct WPI_String* listenAddress,
|
||||
CS_Status* status);
|
||||
int CS_GetMjpegServerPort(CS_Sink sink, CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup cscore_opencv_sink_cfunc OpenCV Sink Functions
|
||||
* @defgroup cscore_frame_sink_cfunc Frame Sink Functions
|
||||
* @{
|
||||
*/
|
||||
void CS_SetSinkDescription(CS_Sink sink, const char* description,
|
||||
void CS_SetSinkDescription(CS_Sink sink, const struct WPI_String* description,
|
||||
CS_Status* status);
|
||||
char* CS_GetSinkError(CS_Sink sink, CS_Status* status);
|
||||
void CS_GetSinkError(CS_Sink sink, struct WPI_String* error, CS_Status* status);
|
||||
void CS_SetSinkEnabled(CS_Sink sink, CS_Bool enabled, CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
@@ -459,9 +484,10 @@ double CS_GetTelemetryAverageValue(CS_Handle handle, enum CS_TelemetryKind kind,
|
||||
* @defgroup cscore_logging_cfunc Logging Functions
|
||||
* @{
|
||||
*/
|
||||
typedef void (*CS_LogFunc)(unsigned int level, const char* file,
|
||||
unsigned int line, const char* msg);
|
||||
void CS_SetLogger(CS_LogFunc func, unsigned int min_level);
|
||||
typedef void (*CS_LogFunc)(void* data, unsigned int level,
|
||||
const struct WPI_String* file, unsigned int line,
|
||||
const struct WPI_String* msg);
|
||||
void CS_SetLogger(CS_LogFunc func, void* data, unsigned int min_level);
|
||||
void CS_SetDefaultLogger(unsigned int min_level);
|
||||
/** @} */
|
||||
|
||||
@@ -486,18 +512,14 @@ void CS_ReleaseEnumeratedSources(CS_Source* sources, int count);
|
||||
CS_Sink* CS_EnumerateSinks(int* count, CS_Status* status);
|
||||
void CS_ReleaseEnumeratedSinks(CS_Sink* sinks, int count);
|
||||
|
||||
void CS_FreeString(char* str);
|
||||
void CS_FreeEnumPropertyChoices(char** choices, int count);
|
||||
void CS_FreeUsbCameraInfo(CS_UsbCameraInfo* info);
|
||||
void CS_FreeHttpCameraUrls(char** urls, int count);
|
||||
|
||||
void CS_FreeEnumeratedProperties(CS_Property* properties, int count);
|
||||
void CS_FreeEnumeratedVideoModes(CS_VideoMode* modes, int count);
|
||||
|
||||
char* CS_GetHostname(void);
|
||||
void CS_GetHostname(struct WPI_String* hostName);
|
||||
|
||||
char** CS_GetNetworkInterfaces(int* count);
|
||||
void CS_FreeNetworkInterfaces(char** interfaces, int count);
|
||||
struct WPI_String* CS_GetNetworkInterfaces(int* count);
|
||||
/** @} */
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -68,7 +68,8 @@ struct VideoMode : public CS_VideoMode {
|
||||
kBGR = WPI_PIXFMT_BGR,
|
||||
kGray = WPI_PIXFMT_GRAY,
|
||||
kY16 = WPI_PIXFMT_Y16,
|
||||
kUYVY = WPI_PIXFMT_UYVY
|
||||
kUYVY = WPI_PIXFMT_UYVY,
|
||||
kBGRA = WPI_PIXFMT_BGRA,
|
||||
};
|
||||
VideoMode() {
|
||||
pixelFormat = 0;
|
||||
@@ -294,7 +295,7 @@ std::vector<std::string> GetHttpCameraUrls(CS_Source source, CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup cscore_opencv_source_func OpenCV Source Functions
|
||||
* @defgroup cscore_frame_source_func Frame Source Functions
|
||||
* @{
|
||||
*/
|
||||
void NotifySourceError(CS_Source source, std::string_view msg,
|
||||
@@ -365,7 +366,7 @@ int GetMjpegServerPort(CS_Sink sink, CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup cscore_opencv_sink_func OpenCV Sink Functions
|
||||
* @defgroup cscore_frame_sink_func Frame Sink Functions
|
||||
* @{
|
||||
*/
|
||||
void SetSinkDescription(CS_Sink sink, std::string_view description,
|
||||
|
||||
@@ -7,72 +7,17 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "cscore_c.h"
|
||||
|
||||
#ifdef CSCORE_CSCORE_RAW_CV_H_
|
||||
#error "Cannot include both cscore_cv.h and cscore_raw_cv.h in the same file"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "cscore_oo.h" // NOLINT(build/include_order)
|
||||
|
||||
#endif
|
||||
|
||||
#if CV_VERSION_MAJOR < 4
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { // NOLINT(build/include_order)
|
||||
#endif
|
||||
|
||||
struct CvMat;
|
||||
|
||||
void CS_PutSourceFrame(CS_Source source, struct CvMat* image,
|
||||
CS_Status* status);
|
||||
|
||||
uint64_t CS_GrabSinkFrame(CS_Sink sink, struct CvMat* image, CS_Status* status);
|
||||
uint64_t CS_GrabSinkFrameTimeout(CS_Sink sink, struct CvMat* image,
|
||||
double timeout, CS_Status* status);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // CV_VERSION_MAJOR < 4
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <opencv2/core/mat.hpp>
|
||||
|
||||
#include "cscore_oo.h"
|
||||
|
||||
namespace cv {
|
||||
class Mat;
|
||||
} // namespace cv
|
||||
#include "cscore_raw.h"
|
||||
|
||||
namespace cs {
|
||||
|
||||
/**
|
||||
* @defgroup cscore_cpp_opencv_special cscore C functions taking a cv::Mat*
|
||||
*
|
||||
* These are needed for specific interop implementations.
|
||||
* @{
|
||||
*/
|
||||
extern "C" {
|
||||
uint64_t CS_GrabSinkFrameCpp(CS_Sink sink, cv::Mat* image, CS_Status* status);
|
||||
uint64_t CS_GrabSinkFrameTimeoutCpp(CS_Sink sink, cv::Mat* image,
|
||||
double timeout, CS_Status* status);
|
||||
void CS_PutSourceFrameCpp(CS_Source source, cv::Mat* image, CS_Status* status);
|
||||
} // extern "C"
|
||||
/** @} */
|
||||
|
||||
void PutSourceFrame(CS_Source source, cv::Mat& image, CS_Status* status);
|
||||
uint64_t GrabSinkFrame(CS_Sink sink, cv::Mat& image, CS_Status* status);
|
||||
uint64_t GrabSinkFrameTimeout(CS_Sink sink, cv::Mat& image, double timeout,
|
||||
CS_Status* status);
|
||||
|
||||
/**
|
||||
* A source for user code to provide OpenCV images as video frames.
|
||||
* These sources require the WPILib OpenCV builds.
|
||||
* For an alternate OpenCV, include "cscore_raw_cv.h" instead, and
|
||||
* include your Mat header before that header.
|
||||
*
|
||||
* This is not dependent on any opencv binary ABI, and can be used
|
||||
* with versions of most versions of OpenCV.
|
||||
*/
|
||||
class CvSource : public ImageSource {
|
||||
public:
|
||||
@@ -87,7 +32,7 @@ class CvSource : public ImageSource {
|
||||
CvSource(std::string_view name, const VideoMode& mode);
|
||||
|
||||
/**
|
||||
* Create an OpenCV source.
|
||||
* Create an OpenCV source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat Pixel format
|
||||
@@ -99,26 +44,48 @@ class CvSource : public ImageSource {
|
||||
int height, int fps);
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks.
|
||||
* 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
|
||||
* cv::Mat::convertTo() and/or cv::cvtColor() to convert it first.
|
||||
* <p>
|
||||
* The image format is guessed from the number of channels. The channel
|
||||
* mapping is as follows. 1: kGray 2: kYUYV 3: BGR 4: BGRA Any other channel
|
||||
* numbers will throw an error. If your image is an in alternate format, use
|
||||
* the overload that takes a PixelFormat.
|
||||
*
|
||||
* @param image OpenCV image
|
||||
* @param image OpenCV Image
|
||||
*/
|
||||
void PutFrame(cv::Mat& image);
|
||||
|
||||
/**
|
||||
* Put an OpenCV image and notify sinks.
|
||||
*
|
||||
* <p>
|
||||
* The format of the Mat must match the PixelFormat. You will corrupt memory
|
||||
* if they dont. With skipVerification false, we will verify the number of
|
||||
* channels matches the pixel format. If skipVerification is true, this step
|
||||
* is skipped and is passed straight through.
|
||||
*
|
||||
* @param image OpenCV image
|
||||
* @param pixelFormat The pixel format of the image
|
||||
* @param skipVerification skip verifying pixel format
|
||||
*/
|
||||
void PutFrame(cv::Mat& image, VideoMode::PixelFormat pixelFormat,
|
||||
bool skipVerification);
|
||||
|
||||
private:
|
||||
static bool VerifyFormat(cv::Mat& image, VideoMode::PixelFormat pixelFormat);
|
||||
};
|
||||
|
||||
/**
|
||||
* A sink for user code to accept video frames as OpenCV images.
|
||||
* These sinks require the WPILib OpenCV builds.
|
||||
* For an alternate OpenCV, include "cscore_raw_cv.h" instead, and
|
||||
* include your Mat header before that header.
|
||||
* A source for user code to accept video frames as OpenCV images.
|
||||
*
|
||||
* This is not dependent on any opencv binary ABI, and can be used
|
||||
* with versions of most versions of OpenCV.
|
||||
*/
|
||||
class CvSink : public ImageSink {
|
||||
public:
|
||||
CvSink() = default;
|
||||
CvSink(const CvSink& sink);
|
||||
|
||||
/**
|
||||
* Create a sink for accepting OpenCV images.
|
||||
@@ -127,89 +94,277 @@ class CvSink : public ImageSink {
|
||||
* image.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat Source pixel format
|
||||
* @param pixelFormat The pixel format to read
|
||||
*/
|
||||
explicit CvSink(std::string_view name, VideoMode::PixelFormat pixelFormat =
|
||||
VideoMode::PixelFormat::kBGR);
|
||||
|
||||
/**
|
||||
* Create a sink for accepting OpenCV images in a separate thread.
|
||||
* Wait for the next frame and get the image.
|
||||
* Times out (returning 0) after timeout seconds.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with.
|
||||
*
|
||||
* <p>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().
|
||||
* @param pixelFormat Source pixel format
|
||||
* @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.
|
||||
*/
|
||||
CvSink(std::string_view name, std::function<void(uint64_t time)> processFrame,
|
||||
VideoMode::PixelFormat pixelFormat = VideoMode::PixelFormat::kBGR);
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225);
|
||||
|
||||
/**
|
||||
* Wait for the next frame and get the image. May block forever.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeout(cv::Mat& image);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with. The data is backed by data in the CvSink. It will be invalidated by
|
||||
* any grabFrame*() call on the sink.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225) const;
|
||||
uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The provided image will have the pixelFormat this class was constructed
|
||||
* with. The data is backed by data in the CvSink. It will be invalidated by
|
||||
* any grabFrame*() call on the sink.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeout(cv::Mat& image) const;
|
||||
uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
|
||||
|
||||
private:
|
||||
constexpr int GetCvFormat(WPI_PixelFormat pixelFormat);
|
||||
|
||||
wpi::RawFrame rawFrame;
|
||||
VideoMode::PixelFormat pixelFormat;
|
||||
};
|
||||
|
||||
inline CvSource::CvSource(std::string_view name, const VideoMode& mode) {
|
||||
m_handle = CreateCvSource(name, mode, &m_status);
|
||||
m_handle = CreateRawSource(name, true, mode, &m_status);
|
||||
}
|
||||
|
||||
inline CvSource::CvSource(std::string_view name, VideoMode::PixelFormat format,
|
||||
int width, int height, int fps) {
|
||||
m_handle =
|
||||
CreateCvSource(name, VideoMode{format, width, height, fps}, &m_status);
|
||||
m_handle = CreateRawSource(name, true, VideoMode{format, width, height, fps},
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline bool CvSource::VerifyFormat(cv::Mat& image,
|
||||
VideoMode::PixelFormat pixelFormat) {
|
||||
int channels = image.channels();
|
||||
switch (pixelFormat) {
|
||||
case VideoMode::PixelFormat::kBGR:
|
||||
if (channels == 3) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kBGRA:
|
||||
if (channels == 4) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kGray:
|
||||
if (channels == 1) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kRGB565:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kUYVY:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kY16:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kYUYV:
|
||||
if (channels == 2) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case VideoMode::PixelFormat::kMJPEG:
|
||||
if (channels == 1) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void CvSource::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);
|
||||
}
|
||||
|
||||
int channels = finalImage.channels();
|
||||
VideoMode::PixelFormat format;
|
||||
if (channels == 1) {
|
||||
// 1 channel is assumed Graysacle
|
||||
format = VideoMode::PixelFormat::kGray;
|
||||
} else if (channels == 2) {
|
||||
// 2 channels is assumed YUYV
|
||||
format = VideoMode::PixelFormat::kYUYV;
|
||||
} else if (channels == 3) {
|
||||
// 3 channels is assumed BGR
|
||||
format = VideoMode::PixelFormat::kBGR;
|
||||
} else if (channels == 4) {
|
||||
// 4 channels is assumed BGRA
|
||||
format = VideoMode::PixelFormat::kBGRA;
|
||||
} else {
|
||||
// TODO Error
|
||||
return;
|
||||
}
|
||||
|
||||
PutFrame(finalImage, format, true);
|
||||
}
|
||||
|
||||
inline void CvSource::PutFrame(cv::Mat& image,
|
||||
VideoMode::PixelFormat pixelFormat,
|
||||
bool skipVerification) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (!skipVerification) {
|
||||
if (!VerifyFormat(finalImage, pixelFormat)) {
|
||||
// TODO Error
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WPI_RawFrame frame; // use WPI_Frame because we don't want the destructor
|
||||
frame.data = finalImage.data;
|
||||
frame.freeFunc = nullptr;
|
||||
frame.freeCbData = nullptr;
|
||||
frame.size = finalImage.total() * finalImage.channels();
|
||||
frame.width = finalImage.cols;
|
||||
frame.height = finalImage.rows;
|
||||
frame.stride = finalImage.step;
|
||||
frame.pixelFormat = pixelFormat;
|
||||
m_status = 0;
|
||||
PutSourceFrame(m_handle, image, &m_status);
|
||||
PutSourceFrame(m_handle, frame, &m_status);
|
||||
}
|
||||
|
||||
inline CvSink::CvSink(std::string_view name,
|
||||
VideoMode::PixelFormat pixelFormat) {
|
||||
m_handle = CreateCvSink(name, pixelFormat, &m_status);
|
||||
m_handle = CreateRawSink(name, true, &m_status);
|
||||
this->pixelFormat = pixelFormat;
|
||||
}
|
||||
|
||||
inline CvSink::CvSink(std::string_view name,
|
||||
std::function<void(uint64_t time)> processFrame,
|
||||
VideoMode::PixelFormat pixelFormat) {
|
||||
m_handle = CreateCvSinkCallback(name, pixelFormat, processFrame, &m_status);
|
||||
inline CvSink::CvSink(const CvSink& sink)
|
||||
: ImageSink{sink}, pixelFormat{sink.pixelFormat} {}
|
||||
|
||||
inline uint64_t CvSink::GrabFrame(cv::Mat& image, double timeout) {
|
||||
cv::Mat tmpnam;
|
||||
auto retVal = GrabFrameDirect(tmpnam);
|
||||
if (retVal <= 0) {
|
||||
return retVal;
|
||||
}
|
||||
tmpnam.copyTo(image);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
inline uint64_t CvSink::GrabFrame(cv::Mat& image, double timeout) const {
|
||||
m_status = 0;
|
||||
return GrabSinkFrameTimeout(m_handle, image, timeout, &m_status);
|
||||
inline uint64_t CvSink::GrabFrameNoTimeout(cv::Mat& image) {
|
||||
cv::Mat tmpnam;
|
||||
auto retVal = GrabFrameNoTimeoutDirect(tmpnam);
|
||||
if (retVal <= 0) {
|
||||
return retVal;
|
||||
}
|
||||
tmpnam.copyTo(image);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
inline uint64_t CvSink::GrabFrameNoTimeout(cv::Mat& image) const {
|
||||
m_status = 0;
|
||||
return GrabSinkFrame(m_handle, image, &m_status);
|
||||
inline constexpr int CvSink::GetCvFormat(WPI_PixelFormat pixelFormat) {
|
||||
int type = 0;
|
||||
switch (pixelFormat) {
|
||||
case WPI_PIXFMT_YUYV:
|
||||
case WPI_PIXFMT_RGB565:
|
||||
case WPI_PIXFMT_Y16:
|
||||
case WPI_PIXFMT_UYVY:
|
||||
type = CV_8UC2;
|
||||
break;
|
||||
case WPI_PIXFMT_BGR:
|
||||
type = CV_8UC3;
|
||||
break;
|
||||
case WPI_PIXFMT_BGRA:
|
||||
type = CV_8UC4;
|
||||
break;
|
||||
case WPI_PIXFMT_GRAY:
|
||||
case WPI_PIXFMT_MJPEG:
|
||||
default:
|
||||
type = CV_8UC1;
|
||||
break;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
inline uint64_t CvSink::GrabFrameDirect(cv::Mat& image, double timeout) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.stride = 0;
|
||||
rawFrame.pixelFormat = pixelFormat;
|
||||
auto timestamp = GrabSinkFrameTimeout(m_handle, rawFrame, timeout, &m_status);
|
||||
if (m_status != CS_OK) {
|
||||
return 0;
|
||||
}
|
||||
image =
|
||||
cv::Mat{rawFrame.height, rawFrame.width,
|
||||
GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
|
||||
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
inline uint64_t CvSink::GrabFrameNoTimeoutDirect(cv::Mat& image) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.stride = 0;
|
||||
rawFrame.pixelFormat = pixelFormat;
|
||||
auto timestamp = GrabSinkFrame(m_handle, rawFrame, &m_status);
|
||||
if (m_status != CS_OK) {
|
||||
return 0;
|
||||
}
|
||||
image =
|
||||
cv::Mat{rawFrame.height, rawFrame.width,
|
||||
GetCvFormat(static_cast<WPI_PixelFormat>(rawFrame.pixelFormat)),
|
||||
rawFrame.data, static_cast<size_t>(rawFrame.stride)};
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif
|
||||
|
||||
#endif // CSCORE_CSCORE_CV_H_
|
||||
|
||||
@@ -221,7 +221,9 @@ class VideoSource {
|
||||
/// HTTP video source.
|
||||
kHttp = CS_SOURCE_HTTP,
|
||||
/// CV video source.
|
||||
kCv = CS_SOURCE_CV
|
||||
kCv = CS_SOURCE_CV,
|
||||
/// Raw video source.
|
||||
kRaw = CS_SOURCE_RAW,
|
||||
};
|
||||
|
||||
/** Connection strategy. Used for SetConnectionStrategy(). */
|
||||
@@ -690,8 +692,10 @@ class HttpCamera : public VideoCamera {
|
||||
|
||||
/**
|
||||
* A source that represents an Axis IP camera.
|
||||
*
|
||||
* @deprecated Use HttpCamera instead.
|
||||
*/
|
||||
class AxisCamera : public HttpCamera {
|
||||
class [[deprecated("Use HttpCamera instead.")]] AxisCamera : public HttpCamera {
|
||||
static std::string HostToUrl(std::string_view host);
|
||||
static std::vector<std::string> HostToUrl(std::span<const std::string> hosts);
|
||||
template <typename T>
|
||||
@@ -856,7 +860,9 @@ class VideoSink {
|
||||
/// MJPEG video sink.
|
||||
kMjpeg = CS_SINK_MJPEG,
|
||||
/// CV video sink.
|
||||
kCv = CS_SINK_CV
|
||||
kCv = CS_SINK_CV,
|
||||
/// Raw video sink.
|
||||
kRaw = CS_SINK_RAW,
|
||||
};
|
||||
|
||||
VideoSink() noexcept = default;
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/deprecated.h>
|
||||
|
||||
#include "cscore_oo.h"
|
||||
|
||||
namespace cs {
|
||||
@@ -350,7 +352,7 @@ inline std::vector<std::string> HttpCamera::GetUrls() const {
|
||||
m_status = 0;
|
||||
return ::cs::GetHttpCameraUrls(m_handle, &m_status);
|
||||
}
|
||||
|
||||
WPI_IGNORE_DEPRECATED
|
||||
inline std::vector<std::string> AxisCamera::HostToUrl(
|
||||
std::span<const std::string> hosts) {
|
||||
std::vector<std::string> rv;
|
||||
@@ -389,6 +391,7 @@ template <typename T>
|
||||
inline AxisCamera::AxisCamera(std::string_view name,
|
||||
std::initializer_list<T> hosts)
|
||||
: HttpCamera(name, HostToUrl(hosts), kAxis) {}
|
||||
WPI_UNIGNORE_DEPRECATED
|
||||
|
||||
inline void ImageSource::NotifyError(std::string_view msg) {
|
||||
m_status = 0;
|
||||
|
||||
@@ -28,18 +28,18 @@ uint64_t CS_GrabRawSinkFrame(CS_Sink sink, struct WPI_RawFrame* rawImage,
|
||||
uint64_t CS_GrabRawSinkFrameTimeout(CS_Sink sink, struct WPI_RawFrame* rawImage,
|
||||
double timeout, CS_Status* status);
|
||||
|
||||
CS_Sink CS_CreateRawSink(const char* name, CS_Status* status);
|
||||
CS_Sink CS_CreateRawSink(const struct WPI_String* name, CS_Bool isCv,
|
||||
CS_Status* status);
|
||||
|
||||
CS_Sink CS_CreateRawSinkCallback(const char* name, void* data,
|
||||
void (*processFrame)(void* data,
|
||||
uint64_t time),
|
||||
CS_Status* status);
|
||||
CS_Sink CS_CreateRawSinkCallback(
|
||||
const struct WPI_String* name, CS_Bool isCv, void* data,
|
||||
void (*processFrame)(void* data, uint64_t time), CS_Status* status);
|
||||
|
||||
void CS_PutRawSourceFrame(CS_Source source, const struct WPI_RawFrame* image,
|
||||
CS_Status* status);
|
||||
|
||||
CS_Source CS_CreateRawSource(const char* name, const CS_VideoMode* mode,
|
||||
CS_Status* status);
|
||||
CS_Source CS_CreateRawSource(const struct WPI_String* name, CS_Bool isCv,
|
||||
const CS_VideoMode* mode, CS_Status* status);
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -54,11 +54,11 @@ namespace cs {
|
||||
* @{
|
||||
*/
|
||||
|
||||
CS_Source CreateRawSource(std::string_view name, const VideoMode& mode,
|
||||
CS_Status* status);
|
||||
CS_Source CreateRawSource(std::string_view name, bool isCv,
|
||||
const VideoMode& mode, CS_Status* status);
|
||||
|
||||
CS_Sink CreateRawSink(std::string_view name, CS_Status* status);
|
||||
CS_Sink CreateRawSinkCallback(std::string_view name,
|
||||
CS_Sink CreateRawSink(std::string_view name, bool isCv, CS_Status* status);
|
||||
CS_Sink CreateRawSinkCallback(std::string_view name, bool isCv,
|
||||
std::function<void(uint64_t time)> processFrame,
|
||||
CS_Status* status);
|
||||
|
||||
@@ -166,14 +166,14 @@ class RawSink : public ImageSink {
|
||||
};
|
||||
|
||||
inline RawSource::RawSource(std::string_view name, const VideoMode& mode) {
|
||||
m_handle = CreateRawSource(name, mode, &m_status);
|
||||
m_handle = CreateRawSource(name, false, mode, &m_status);
|
||||
}
|
||||
|
||||
inline RawSource::RawSource(std::string_view name,
|
||||
VideoMode::PixelFormat format, int width,
|
||||
int height, int fps) {
|
||||
m_handle =
|
||||
CreateRawSource(name, VideoMode{format, width, height, fps}, &m_status);
|
||||
m_handle = CreateRawSource(name, false, VideoMode{format, width, height, fps},
|
||||
&m_status);
|
||||
}
|
||||
|
||||
inline void RawSource::PutFrame(wpi::RawFrame& image) {
|
||||
@@ -182,12 +182,12 @@ inline void RawSource::PutFrame(wpi::RawFrame& image) {
|
||||
}
|
||||
|
||||
inline RawSink::RawSink(std::string_view name) {
|
||||
m_handle = CreateRawSink(name, &m_status);
|
||||
m_handle = CreateRawSink(name, false, &m_status);
|
||||
}
|
||||
|
||||
inline RawSink::RawSink(std::string_view name,
|
||||
std::function<void(uint64_t time)> processFrame) {
|
||||
m_handle = CreateRawSinkCallback(name, processFrame, &m_status);
|
||||
m_handle = CreateRawSinkCallback(name, false, processFrame, &m_status);
|
||||
}
|
||||
|
||||
inline uint64_t RawSink::GrabFrame(wpi::RawFrame& image, double timeout) const {
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#ifndef CSCORE_CSCORE_RAW_CV_H_
|
||||
#define CSCORE_CSCORE_RAW_CV_H_
|
||||
|
||||
#ifdef CSCORE_CSCORE_CV_H_
|
||||
#error "Cannot include both cscore_cv.h and cscore_raw_cv.h in the same file"
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <opencv2/core/mat.hpp>
|
||||
|
||||
#include "cscore_raw.h"
|
||||
|
||||
namespace cs {
|
||||
/**
|
||||
* A source for using the raw frame API to provide opencv images.
|
||||
*
|
||||
* If you are using the WPILib OpenCV builds, do not use this, and
|
||||
* instead include "cscore_cv.h" to get a more performant version.
|
||||
*
|
||||
* This is not dependent on any opencv binary ABI, and can be used
|
||||
* with versions of OpenCV that are not 3. If using OpenCV 3, use
|
||||
* CvSource.
|
||||
*/
|
||||
class RawCvSource : public RawSource {
|
||||
public:
|
||||
RawCvSource() = default;
|
||||
|
||||
/**
|
||||
* Create a Raw OpenCV source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param mode Video mode being generated
|
||||
*/
|
||||
RawCvSource(std::string_view name, const VideoMode& mode);
|
||||
|
||||
/**
|
||||
* Create a Raw OpenCV source.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
* @param pixelFormat Pixel format
|
||||
* @param width width
|
||||
* @param height height
|
||||
* @param fps fps
|
||||
*/
|
||||
RawCvSource(std::string_view name, VideoMode::PixelFormat pixelFormat,
|
||||
int width, int height, int 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
|
||||
* cv::Mat::convertTo() and/or cv::cvtColor() to convert it first.
|
||||
*
|
||||
* @param image OpenCV image
|
||||
*/
|
||||
void PutFrame(cv::Mat& image);
|
||||
|
||||
private:
|
||||
wpi::RawFrame rawFrame;
|
||||
};
|
||||
|
||||
/**
|
||||
* A sink for user code to accept raw video frames as OpenCV images.
|
||||
*
|
||||
* If you are using the WPILib OpenCV builds, do not use this, and
|
||||
* instead include "cscore_cv.h" to get a more performant version.
|
||||
*
|
||||
* This is not dependent on any opencv binary ABI, and can be used
|
||||
* with versions of OpenCV that are not 3. If using OpenCV 3, use
|
||||
* CvSink.
|
||||
*/
|
||||
class RawCvSink : public RawSink {
|
||||
public:
|
||||
RawCvSink() = default;
|
||||
|
||||
/**
|
||||
* Create a sink for accepting OpenCV images.
|
||||
*
|
||||
* <p>WaitForFrame() must be called on the created sink to get each new
|
||||
* image.
|
||||
*
|
||||
* @param name Source name (arbitrary unique identifier)
|
||||
*/
|
||||
explicit RawCvSink(std::string_view name);
|
||||
|
||||
/**
|
||||
* Create a sink for accepting OpenCV images in a separate thread.
|
||||
*
|
||||
* <p>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().
|
||||
*/
|
||||
RawCvSink(std::string_view name,
|
||||
std::function<void(uint64_t time)> processFrame);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrame(cv::Mat& image, double timeout = 0.225);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeout(cv::Mat& image);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameDirect(cv::Mat& image, double timeout = 0.225);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
uint64_t GrabFrameNoTimeoutDirect(cv::Mat& image);
|
||||
|
||||
private:
|
||||
wpi::RawFrame rawFrame;
|
||||
};
|
||||
|
||||
inline RawCvSource::RawCvSource(std::string_view name, const VideoMode& mode)
|
||||
: RawSource{name, mode} {}
|
||||
|
||||
inline RawCvSource::RawCvSource(std::string_view name,
|
||||
VideoMode::PixelFormat format, int width,
|
||||
int height, int fps)
|
||||
: RawSource{name, format, width, height, fps} {}
|
||||
|
||||
inline void RawCvSource::PutFrame(cv::Mat& image) {
|
||||
m_status = 0;
|
||||
rawFrame.data = reinterpret_cast<char*>(image.data);
|
||||
rawFrame.width = image.cols;
|
||||
rawFrame.height = image.rows;
|
||||
rawFrame.totalData = image.total() * image.channels();
|
||||
rawFrame.pixelFormat =
|
||||
image.channels() == 3 ? WPI_PIXFMT_BGR : WPI_PIXFMT_GRAY;
|
||||
PutSourceFrame(m_handle, rawFrame, &m_status);
|
||||
}
|
||||
|
||||
inline RawCvSink::RawCvSink(std::string_view name) : RawSink{name} {}
|
||||
|
||||
inline RawCvSink::RawCvSink(std::string_view name,
|
||||
std::function<void(uint64_t time)> processFrame)
|
||||
: RawSink{name, processFrame} {}
|
||||
|
||||
inline uint64_t RawCvSink::GrabFrame(cv::Mat& image, double timeout) {
|
||||
cv::Mat tmpnam;
|
||||
auto retVal = GrabFrameDirect(tmpnam);
|
||||
if (retVal <= 0) {
|
||||
return retVal;
|
||||
}
|
||||
tmpnam.copyTo(image);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
inline uint64_t RawCvSink::GrabFrameNoTimeout(cv::Mat& image) {
|
||||
cv::Mat tmpnam;
|
||||
auto retVal = GrabFrameNoTimeoutDirect(tmpnam);
|
||||
if (retVal <= 0) {
|
||||
return retVal;
|
||||
}
|
||||
tmpnam.copyTo(image);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
inline uint64_t RawCvSink::GrabFrameDirect(cv::Mat& image, double timeout) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.pixelFormat = WPI_PixelFormat::WPI_PIXFMT_BGR;
|
||||
m_status = RawSink::GrabFrame(rawFrame, timeout);
|
||||
if (m_status <= 0) {
|
||||
return m_status;
|
||||
}
|
||||
image = cv::Mat{rawFrame.height, rawFrame.width, CV_8UC3, rawFrame.data};
|
||||
return m_status;
|
||||
}
|
||||
|
||||
inline uint64_t RawCvSink::GrabFrameNoTimeoutDirect(cv::Mat& image) {
|
||||
rawFrame.height = 0;
|
||||
rawFrame.width = 0;
|
||||
rawFrame.pixelFormat = WPI_PixelFormat::WPI_PIXFMT_BGR;
|
||||
m_status = RawSink::GrabFrameNoTimeout(rawFrame);
|
||||
if (m_status <= 0) {
|
||||
return m_status;
|
||||
}
|
||||
image = cv::Mat{rawFrame.height, rawFrame.width, CV_8UC3, rawFrame.data};
|
||||
return m_status;
|
||||
}
|
||||
|
||||
} // namespace cs
|
||||
|
||||
#endif // CSCORE_CSCORE_RAW_CV_H_
|
||||
@@ -80,6 +80,8 @@ static VideoMode::PixelFormat ToPixelFormat(__u32 pixelFormat) {
|
||||
return VideoMode::kRGB565;
|
||||
case V4L2_PIX_FMT_BGR24:
|
||||
return VideoMode::kBGR;
|
||||
case V4L2_PIX_FMT_ABGR32:
|
||||
return VideoMode::kBGRA;
|
||||
case V4L2_PIX_FMT_GREY:
|
||||
return VideoMode::kGray;
|
||||
case V4L2_PIX_FMT_Y16:
|
||||
@@ -102,6 +104,8 @@ static __u32 FromPixelFormat(VideoMode::PixelFormat pixelFormat) {
|
||||
return V4L2_PIX_FMT_RGB565;
|
||||
case VideoMode::kBGR:
|
||||
return V4L2_PIX_FMT_BGR24;
|
||||
case VideoMode::kBGRA:
|
||||
return V4L2_PIX_FMT_ABGR32;
|
||||
case VideoMode::kGray:
|
||||
return V4L2_PIX_FMT_GREY;
|
||||
case VideoMode::kY16:
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
|
||||
@implementation UsbCameraDelegate
|
||||
|
||||
- (id)init {
|
||||
@@ -24,6 +21,8 @@
|
||||
(void)sampleBuffer;
|
||||
(void)connection;
|
||||
|
||||
auto currentTime = wpi::Now();
|
||||
|
||||
auto sharedThis = self.cppImpl.lock();
|
||||
if (!sharedThis) {
|
||||
return;
|
||||
@@ -52,16 +51,12 @@
|
||||
return;
|
||||
}
|
||||
|
||||
size_t currSize = width * 3 * height;
|
||||
|
||||
auto tmpMat = cv::Mat(height, width, CV_8UC4, baseaddress, rowBytes);
|
||||
auto image = sharedThis->AllocImage(cs::VideoMode::PixelFormat::kBGR, width,
|
||||
height, currSize);
|
||||
cv::cvtColor(tmpMat, image->AsMat(), cv::COLOR_BGRA2BGR);
|
||||
std::unique_ptr<cs::Image> image = cs::CreateImageFromBGRA(
|
||||
sharedThis.get(), width, height, rowBytes, reinterpret_cast<uint8_t*>(baseaddress));
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
|
||||
|
||||
sharedThis->objcPutFrame(std::move(image), wpi::Now());
|
||||
sharedThis->objcPutFrame(std::move(image), currentTime);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "Instance.h"
|
||||
#include "c_util.h"
|
||||
#include "cscore_cpp.h"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "UsbCameraImpl.h"
|
||||
|
||||
namespace cs {
|
||||
@@ -135,7 +134,11 @@ std::vector<UsbCameraInfo> EnumerateUsbCameras(CS_Status* status) {
|
||||
std::vector<UsbCameraInfo> retval;
|
||||
NSArray<AVCaptureDeviceType>* deviceTypes = @[
|
||||
AVCaptureDeviceTypeBuiltInWideAngleCamera,
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 140000
|
||||
AVCaptureDeviceTypeExternal
|
||||
#else
|
||||
AVCaptureDeviceTypeExternalUnknown
|
||||
#endif
|
||||
];
|
||||
AVCaptureDeviceDiscoverySession* session = [AVCaptureDeviceDiscoverySession
|
||||
discoverySessionWithDeviceTypes:deviceTypes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user