Skip to content

Commit 2dc9e9d

Browse files
Nikita-AjitNikita AjitGustav LindbergdanielmyhGustavLindberg99
committed
Add ACAP example to show PTZ WS API use
Change-Id: I9bbe22273559e337cc4a22a1f87cdd0e39479129 Co-authored-by: Nikita Ajit <nikitaa@axis.com> Co-authored-by: Gustav Lindberg <gustali@axis.com> Co-authored-by: Daniel Myhrman <74043942+danielmyh@users.noreply.github.com> Co-authored-by: Gustav Lindberg <95423695+GustavLindberg99@users.noreply.github.com> Co-authored-by: Joakim Roubert <joakimr@axis.com> Co-authored-by: Johan Hultberg <johanol@axis.com> Co-authored-by: Zimon Kuhs <zimonk@axis.com> Co-authored-by: Dan Skorup <dans@axis.com> Co-authored-by: Stiv Abdullwahed <stiva@axis.com> Co-authored-by: Isak Jacobsson <isakj@axis.com>
1 parent d69ca13 commit 2dc9e9d

16 files changed

Lines changed: 908 additions & 0 deletions
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Build ptz-control-ws-api application
2+
on:
3+
workflow_dispatch:
4+
push:
5+
paths:
6+
- 'ptz-control-ws-api/**'
7+
- '!ptz-control-ws-api/README.md'
8+
- '.github/workflows/ptz-control-ws-api.yml'
9+
jobs:
10+
test-app:
11+
name: Test app
12+
runs-on: ubuntu-latest
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
axis-os: ["12.11.58"]
17+
arch: ["armv7hf"]
18+
env:
19+
EXREPO: acap-native-examples
20+
EXNAME: ptz-control-ws-api
21+
steps:
22+
- uses: actions/checkout@v6
23+
- uses: docker/setup-buildx-action@v3
24+
25+
- name: Build ${{ env.EXNAME }} application
26+
env:
27+
imagetag: ${{ env.EXREPO }}_${{ env.EXNAME }}:${{ matrix.arch }}
28+
run: |
29+
docker image rm -f $imagetag
30+
cd $EXNAME
31+
docker build --no-cache --tag $imagetag --build-arg ARCH=${{ matrix.arch }} .
32+
docker cp $(docker create $imagetag):/opt/app ./build
33+
cd ..
34+
docker image rm -f $imagetag

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ The examples are organized into logical groups to help you find the most relevan
128128
- Examples that showcase how to use the Message Broker API.
129129
- [device-data-hub](./device-data-hub/)
130130
- Examples that showcase how to use the Device Data Hub API (https://developer.axis.com/acap/api/#device-data-hub-api).
131+
- [ptz-control-ws-api](./ptz-control-ws-api/)
132+
- An example demonstrating PTZ (Pan-Tilt-Zoom) control using the [PTZ Control WS API](https://developer.axis.com/acap/reference/supported-apis/ptz-control-ws-api).
131133
- [reproducible-package](./reproducible-package/)
132134
- An example of how to create a reproducible application package.
133135
- [recording-playback](./recording-playback/)

ptz-control-ws-api/Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
ARG ARCH=aarch64
2+
ARG VERSION=12.11.0-rc.1
3+
ARG UBUNTU_VERSION=24.04
4+
ARG REPO=axisecp
5+
ARG SDK=acap-native-sdk
6+
7+
FROM ${REPO}/${SDK}:${VERSION}-${ARCH}-ubuntu${UBUNTU_VERSION}
8+
9+
# Building the ACAP application
10+
COPY ./app /opt/app/
11+
WORKDIR /opt/app
12+
RUN . /opt/axis/acapsdk/environment-setup* && acap-build .

ptz-control-ws-api/README.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
*Copyright (C) 2026, Axis Communications AB, Lund, Sweden. All Rights Reserved.*
2+
3+
# An ACAP application using the PTZ Control Websocket API
4+
5+
## API specific information
6+
7+
The [PTZ Control WS API](https://developer.axis.com/acap/reference/supported-apis/#ptz-control-ws-api) is a VAPIX API that allows a client to perform PTZ continuous move control over a websocket connection. The client can use the API together with PTZ topics on [Device Data Hub API](https://developer.axis.com/acap/reference/supported-apis/#device-data-hub-api) to stream PTZ data.
8+
9+
The PTZ Control WS API is a websocket based API that creates a websocket session between the client/an ACAP application and an Axis PTZ camera. The client can use the API to list the supported versions of the API, to get the capabilities in terms of allowed functionality and ranges of pan, tilt and zoom, perform continuous move of the PTZ. The websocket session persists as long as the user wants it to send PTZ move commands.
10+
11+
The continuous move has a default timeout of 3 minutes, post which the move stops. The API is supported only on mechanical PTZ cameras (with some exceptions). A client can use the API discovery to know if the API is supported on a particular camera.
12+
13+
A client can use the [Device Data Hub API](https://developer.axis.com/acap/reference/supported-apis/#device-data-hub-api) to subscribe to PTZ specific datahub topics and stream PTZ status. The PTZ Control WS API does not support polling of PTZ data and instead provides streaming of PTZ data through the [Device Data Hub API(https://developer.axis.com/acap/reference/supported-apis/#device-data-hub-api). The PTZ datahub topics are:
14+
15+
- `com.axis.ptz.pan-tilt-state.v1`
16+
- `com.axis.ptz.optical-zoom-state.v1`
17+
18+
### When to use the WS API
19+
20+
The PTZ Control WS API complements the legacy [VAPIX PTZ API (ptz.cgi)](https://developer.axis.com/vapix/network-video/pantiltzoom-api/) and is not intended to replace the existing API.
21+
22+
However, the VAPIX PTZ API is retained mainly for backwards compatibility. For continuous move use case, we recommend the client to use the new PTZ Control WS API. This creates an end-to-end websocket between the client and the device, removes the network layer latency and hops involved to establish a TCP and hence an HTTP connection for every couple of requests (this is how it works in VAPIX PTZ API).
23+
24+
If a client is interested in PTZ control for more than continuous move, they can either use the PTZ Control WS API and VAPIX PTZ API in combination or only the VAPIX PTZ API for simplicity.
25+
26+
### When to subscribe to the datahub PTZ topics
27+
28+
The datahub PTZ topics are available on mechanical PTZ cameras on ARTPEC-7 (excluding V5925 and V5938) and ARTPEC-9 architecture. A client can subscribe to these topics independent of the PTZ Control WS API. The usecase of the topics is to stream PTZ data on the datahub stream (for example, after a PTZ control move).
29+
30+
The PTZ Control WS API is one such usecase where a client can subscribe to the datahub PTZ topics. After a continuous move, via the PTZ Control WS API, PTZ data is published on the datahub stream, if subscribed to the PTZ topics. This removes the need for polling of PTZ status after a continuous move command (as required in ptz.cgi).
31+
32+
## Outline of example
33+
34+
The example explains how to build an ACAP application that uses the PTZ Control WS API to perform a continuous PTZ move and consume PTZ data via the [Device Data Hub API](https://developer.axis.com/acap/reference/supported-apis/#device-data-hub-api).
35+
36+
The example ACAP application, in the app directory, contains the application source code to call the PTZ Control WS API.
37+
38+
1. Global initialization of cURL as digest authorization of the client is required.
39+
2. Create a websocket client. The client subscribes to datahub topics of interest.
40+
3. Immediately after subscription, datahub streams an event for each topic subscribed that gives current PTZ data.
41+
4. Send a move command in response to the first event, triggers an event from datahub.
42+
5. Keep the application running.
43+
44+
Below is the structure and files used in the example:
45+
46+
```none
47+
ptz-control-ws-api
48+
├── app
49+
│ ├── listener.c
50+
│ ├── listener.h
51+
│ ├── main.c
52+
│ ├── subscriber.c
53+
│ ├── subscriber.h
54+
│ ├── websocket_client.c
55+
│ ├── websocket_client.h
56+
│ ├── LICENSE
57+
│ ├── Makefile
58+
│ └── manifest.json
59+
├── Dockerfile
60+
└── README.md
61+
```
62+
63+
- **app/listener.h/c** - Callback that receives datahub topic updates and sends events on the datahub stream
64+
- **app/main.c** - The main() program
65+
- **app/subscriber.h/c** - Module that connects to datahub and subscribes to topics
66+
- **app/websocket_client.h/c** - Module to create the websocket connection and perform authentication of the user
67+
- **app/LICENSE** - Text file which lists all open source licensed source code distributed with the application
68+
- **app/Makefile** - Build and link instructions for the application
69+
- **app/manifest.json** - Defines the application and its configuration. This includes additional parameters.
70+
- **Dockerfile** - Assembles an image containing the ACAP Native SDK and builds the application using it
71+
- **README.md** - Step by step instructions on how to run the example
72+
73+
### Versions
74+
75+
- The feature to call PTZ Control WS API was introduced in AXIS OS 12.11
76+
- The feature [Device Data Hub API](https://developer.axis.com/acap/reference/supported-apis/#device-data-hub-api) was introduced in 12.10
77+
- The feature to subscribe to PTZ datahub topics was introduced in 12.11
78+
79+
## Getting started
80+
81+
These instructions are guidelines on how to execute the code.
82+
83+
### How to run the code
84+
85+
Below is the step by step instructions on how to execute the program. It starts with the generation of the .eap file and ends with running it on a device.
86+
87+
#### Build the application
88+
89+
Standing in your working directory run the following commands:
90+
91+
> [!NOTE]
92+
>
93+
> Depending on the network your local build machine is connected to, you may need to add proxy
94+
> settings for Docker. See
95+
> [Proxy in build time](https://developer.axis.com/acap/develop/proxy/#proxy-in-build-time).
96+
97+
```sh
98+
docker build --platform=linux/amd64 --tag <APP_IMAGE> --build-arg ARCH=<ARCH> .
99+
```
100+
101+
- `<APP_IMAGE>` is the name to tag the image with, e.g., `ptz_control_ws_api_example:1.0`
102+
- `<ARCH>` is the SDK architecture, `armv7hf` or `aarch64`.
103+
104+
Copy the result from the container image to a local directory `build`:
105+
106+
```sh
107+
docker cp $(docker create --platform=linux/amd64 <APP_IMAGE>):/opt/app ./build
108+
```
109+
110+
The `build` directory contains the build artifacts, where the application
111+
is found with suffix `.eap`, depending on which SDK architecture that was
112+
chosen, one of these files should be found:
113+
114+
- `ptz_control_ws_api_example_armv7hf.eap`
115+
- `ptz_control_ws_api_example_aarch64.eap`
116+
117+
> [!NOTE]
118+
>
119+
> For detailed information on how to build, install, and run ACAP applications, refer to the official ACAP documentation: [Build, install, and run](https://developer.axis.com/acap/develop/build-install-run/).
120+
121+
#### Install and start the application
122+
123+
Browse to the application page of the Axis device:
124+
125+
```none
126+
http://<AXIS_DEVICE_IP>/index.html#apps
127+
```
128+
129+
1. Click on the tab **Apps** in the device GUI
130+
2. Enable **Allow unsigned apps** toggle
131+
3. Click **(+ Add app)** button to upload the application file
132+
4. Select the newly built application package, depending on architecture:
133+
134+
- `ptz_control_ws_api_example_armv7hf.eap`
135+
- `ptz_control_ws_api_example_aarch64.eap`
136+
137+
5. Click **Install**
138+
6. Run the application by enabling the **Start** switch
139+
140+
#### The expected output
141+
142+
The expected output is that:
143+
144+
1. When the ACAP application starts, it uses the PTZ Control WS API to make a continuous move.
145+
2. The ACAP application then uses the datahub API to listen for that move's PTZ status, via subscribed PTZ topics.
146+
3. When the example ACAP application receives a datahub event it logs the event in the syslog and stops the move.
147+
4. Due to hardware and software latencies, more events will be received and logged before the move stops completely.
148+
149+
Note that step 1 will trigger step 3, but if a move is made manually while the ACAP application is still running, that will trigger step 3 again, producing more logs and causing that move to stop.
150+
151+
The application log from step 3 can be found by either
152+
153+
- Browsing to http://<AXIS_DEVICE_IP>/axis-cgi/admin/systemlog.cgi?appname=ptz_control_ws_api_example.
154+
- Browsing to the Apps page and select App log.
155+
156+
It will first log that it successfully retrieved the session token, subscribed to the datahub topics, and started running the main loop:
157+
158+
```none
159+
[ INFO ] ptz_control_ws_api_example[542446]: Retrieved session token: 5057201671775133927
160+
[ INFO ] ptz_control_ws_api_example[542446]: Subscribed to topic: com.axis.ptz.pan-tilt-state.v1
161+
[ INFO ] ptz_control_ws_api_example[542446]: Subscribed to topic: com.axis.ptz.optical-zoom-state.v1
162+
```
163+
164+
It will then log a lot of datahub events, similar to below logs:
165+
166+
```none
167+
[ INFO ] ptz_control_ws_api_example[542446]: Received com.axis.ptz.pan-tilt-state.v1 topic event on channel 1, pan -169.622650, tilt -78.801727, pan moving: false, tilt moving: false, pan speed: 2.323047, tilt speed: 0.000000
168+
[ INFO ] ptz_control_ws_api_example[542446]: Received com.axis.ptz.optical-zoom-state.v1 topic event on channel 1, zoom mag: 1.000463, hfov: 58.307686, vfov: 34.821022, zoom moving: false
169+
[ INFO ] ptz_control_ws_api_example[542446]: Received com.axis.ptz.pan-tilt-state.v1 topic event on channel 1, pan -156.583588, tilt -77.959900, pan moving: false, tilt moving: false, pan speed: 0.000000, tilt speed: 0.000000
170+
[ INFO ] ptz_control_ws_api_example[542446]: Received com.axis.ptz.optical-zoom-state.v1 topic event on channel 1, zoom mag: 1.000463, hfov: 58.307686, vfov: 34.821022, zoom moving: false
171+
```
172+
173+
## License
174+
175+
**[MIT](../LICENSE)**

ptz-control-ws-api/app/LICENSE

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
MIT License
2+
3+
Copyright (C) 2026, Axis Communications AB, Lund, Sweden. All Rights Reserved.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+
23+
================================================================================
24+
Third party licenses
25+
================================================================================
26+
27+
--------------------------------------------------------------------------------
28+
curl license
29+
--------------------------------------------------------------------------------
30+
31+
COPYRIGHT AND PERMISSION NOTICE
32+
33+
Copyright (C) Daniel Stenberg, <daniel@haxx.se>, and many
34+
contributors, see the THANKS file.
35+
36+
All rights reserved.
37+
38+
Permission to use, copy, modify, and distribute this software for any purpose
39+
with or without fee is hereby granted, provided that the above copyright
40+
notice and this permission notice appear in all copies.
41+
42+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
45+
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
46+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
47+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
48+
OR OTHER DEALINGS IN THE SOFTWARE.
49+
50+
Except as contained in this notice, the name of a copyright holder shall not
51+
be used in advertising or otherwise to promote the sale, use or other dealings
52+
in this Software without prior written authorization of the copyright holder.
53+
54+
--------------------------------------------------------------------------------
55+
jansson license
56+
--------------------------------------------------------------------------------
57+
58+
Copyright (c) 2009-2020 Petri Lehtinen <petri@digip.org>
59+
60+
Permission is hereby granted, free of charge, to any person obtaining a copy
61+
of this software and associated documentation files (the "Software"), to deal
62+
in the Software without restriction, including without limitation the rights
63+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
64+
copies of the Software, and to permit persons to whom the Software is
65+
furnished to do so, subject to the following conditions:
66+
67+
The above copyright notice and this permission notice shall be included in
68+
all copies or substantial portions of the Software.
69+
70+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
71+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
73+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
74+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
75+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
76+
THE SOFTWARE.

ptz-control-ws-api/app/Makefile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
PROG1 = $(shell jq -r '.acapPackageConf.setup.appName' manifest.json)
2+
OBJS1 = main.c \
3+
listener.c \
4+
subscriber.c \
5+
utils.c \
6+
websocket_client.c
7+
PROGS = $(PROG1)
8+
DEBUG_DIR = debug
9+
10+
PKGS = glib-2.0 jansson libcurl device-data-hub-client-c
11+
12+
CFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --cflags $(PKGS))
13+
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --libs $(PKGS))
14+
LDLIBS += -lsystemd
15+
16+
CFLAGS += -Wall \
17+
-Wextra \
18+
-Wstrict-prototypes \
19+
-Wmissing-prototypes \
20+
-Wformat=2 \
21+
-Wpointer-arith \
22+
-Wdisabled-optimization \
23+
-Wfloat-equal \
24+
-Wbad-function-cast \
25+
-Winline \
26+
-W \
27+
-Werror
28+
29+
all: $(PROGS)
30+
31+
$(PROG1): $(OBJS1)
32+
# Create debug directory and build binary with debug symbols
33+
install -d $(DEBUG_DIR)
34+
35+
$(CC) $^ $(CFLAGS) $(LIBS) $(LDFLAGS) $(LDLIBS) -o $(DEBUG_DIR)/$@
36+
cp $(DEBUG_DIR)/$@ .
37+
$(STRIP) $@
38+
39+
clean:
40+
rm -rf $(PROGS) *.o *.eap* *_LICENSE.txt package.conf* param.conf tmp* $(DEBUG_DIR)

0 commit comments

Comments
 (0)