Skip to content

Commit 5cceb76

Browse files
jij-workJonas HolmbergStiv-workjohan-hultberg-work
committed
Pipewire audio
Co-authored-by: Jonas Holmberg <[email protected]> Co-authored-by: Stiv Abdullwahed <[email protected]> Co-authored-by: Johan Olsson <[email protected]>
1 parent 0209f76 commit 5cceb76

File tree

15 files changed

+1693
-0
lines changed

15 files changed

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

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ The examples are organized into logical groups to help you find the most relevan
3232
- [vdostream](./vdostream/)
3333
- An example in C that starts a vdo stream and then illustrates how to continuously capture frames from the vdo service, access the received buffer contents as well as the frame metadata.
3434

35+
### Audio
36+
37+
- [audio-capture](./audio-capture/)
38+
- Example in C that illustrate how to capture audio.
39+
- [audio-playback](./audio-playback/)
40+
- Example in C that illustrate how to play audio.
41+
3542
### Machine learning
3643

3744
#### Object Detection

audio-capture/Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
ARG ARCH=armv7hf
2+
ARG VERSION=12.3.0
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 .

audio-capture/README.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
*Copyright (C) 2025, Axis Communications AB, Lund, Sweden. All Rights Reserved.*
2+
3+
# A pipewire stream based ACAP application on an edge device
4+
5+
This README file explains how to build an ACAP application that uses the [pipewire stream API](https://docs.pipewire.org/page_streams.html#ssec_consume). It is achieved by using the containerized API and toolchain images.
6+
7+
Together with this README file, you should be able to find a directory called app. That directory contains the "audiocapture" application source code which can easily be compiled and run with the help of the tools and step by step below.
8+
9+
This example illustrates how to continuously capture audio samples from the pipewire service, access the received buffer contents as well as the audio metadata. Peak level is calculated from the captured samples and logged in the Application log.
10+
11+
The naming convention of the audio nodes in pipewire is described in the [Native SDK API](https://developer.axis.com/acap/api/native-sdk-api/#pipewire)
12+
13+
## Getting started
14+
15+
These instructions will guide you on how to execute the code. Below is the structure and scripts used in the example:
16+
17+
```sh
18+
audio-capture
19+
├── app
20+
│ ├── LICENSE
21+
│ ├── Makefile
22+
│ ├── manifest.json
23+
│ └── audiocapture.c
24+
├── Dockerfile
25+
└── README.md
26+
```
27+
28+
- **app/LICENSE** - Text file which lists all open source licensed source code distributed with the application.
29+
- **app/Makefile** - Makefile containing the build and link instructions for building the ACAP application.
30+
- **app/manifest.json** - Defines the application and its configuration.
31+
- **app/audiocapture.c** - Application to capture audio from the pipewire service in C.
32+
- **Dockerfile** - Docker file with the specified Axis toolchain and API container to build the example specified.
33+
- **README.md** - Step by step instructions on how to run the example.
34+
35+
### How to run the code
36+
37+
Below is the step by step instructions on how to execute the program. So basically starting with the generation of the .eap file to running it on a device:
38+
39+
#### Build the application
40+
41+
Standing in your working directory run the following commands:
42+
43+
> [!NOTE]
44+
>
45+
> Depending on the network your local build machine is connected to, you may need to add proxy
46+
> settings for Docker. See
47+
> [Proxy in build time](https://developer.axis.com/acap/develop/proxy/#proxy-in-build-time).
48+
49+
```sh
50+
docker build --tag <APP_IMAGE> .
51+
```
52+
53+
<APP_IMAGE> is the name to tag the image with, e.g., audiocapture:1.0
54+
55+
Default architecture is **armv7hf**. To build for **aarch64** it's possible to
56+
update the *ARCH* variable in the Dockerfile or to set it in the docker build
57+
command via build argument:
58+
59+
```sh
60+
docker build --build-arg ARCH=aarch64 --tag <APP_IMAGE> .
61+
```
62+
63+
Copy the result from the container image to a local directory build:
64+
65+
```sh
66+
docker cp $(docker create <APP_IMAGE>):/opt/app ./build
67+
```
68+
69+
The working dir now contains a build folder with the following files:
70+
71+
```sh
72+
audio-capture
73+
├── app
74+
│ ├── LICENSE
75+
│ ├── Makefile
76+
│ ├── manifest.json
77+
│ └── audiocapture.c
78+
├── build
79+
│ ├── LICENSE
80+
│ ├── Makefile
81+
│ ├── manifest.json
82+
│ ├── package.conf
83+
│ ├── package.conf.orig
84+
│ ├── param.conf
85+
│ ├── audiocapture*
86+
│ ├── Audio_capture_1_0_0_armv7hf.eap
87+
│ ├── Audio_capture_1_0_0_LICENSE.txt
88+
│ └── audiocapture.c
89+
├── Dockerfile
90+
└── README.md
91+
```
92+
93+
- **build/manifest.json** - Defines the application and its configuration.
94+
- **build/package.conf** - Defines the application and its configuration.
95+
- **build/package.conf.orig** - Defines the application and its configuration, original file.
96+
- **build/param.conf** - File containing application parameters.
97+
- **build/audiocapture*** - Application executable binary file.
98+
- **build/Audio_capture_1_0_0_armv7hf.eap** - Application package .eap file.
99+
- **build/Audio_capture_1_0_0_LICENSE.txt** - Copy of LICENSE file.
100+
101+
#### Install and start the application
102+
103+
Browse to the application page of the Axis device:
104+
105+
```sh
106+
http://<AXIS_DEVICE_IP>/index.html#apps
107+
```
108+
109+
- Click on the tab `Apps` in the device GUI
110+
- Enable `Allow unsigned apps` toggle
111+
- Click `(+ Add app)` button to upload the application file
112+
- Browse to the newly built ACAP application, depending on architecture:
113+
- `Audio_capture_1_0_0_aarch64.eap`
114+
- `Audio_capture_1_0_0_armv7hf.eap`
115+
- Click `Install`
116+
- Run the application by enabling the `Start` switch
117+
118+
#### The expected output
119+
120+
Application log can be found directly at:
121+
122+
```sh
123+
http://<AXIS_DEVICE_IP>/axis-cgi/admin/systemlog.cgi?appname=audiocapture
124+
```
125+
126+
#### Output
127+
128+
```sh
129+
----- Contents of SYSTEM_LOG for 'audiocapture' -----
130+
131+
audiocapture[1346447]: I audiocapture [audiocapture.c:368:main]: Starting.
132+
audiocapture[1346447]: I audiocapture [audiocapture.c:235:registry_event_global]: Found Audio/Source node AudioDevice0Input0.Unprocessed with id 90.
133+
audiocapture[1346447]: D audiocapture [audiocapture.c:114:on_state_changed]: State for stream from AudioDevice0Input0.Unprocessed changed unconnected -> connecting
134+
audiocapture[1346447]: I audiocapture [audiocapture.c:235:registry_event_global]: Found Audio/Source node AudioDevice0Input0 with id 134.
135+
audiocapture[1346447]: D audiocapture [audiocapture.c:114:on_state_changed]: State for stream from AudioDevice0Input0 changed unconnected -> connecting
136+
audiocapture[1346447]: I audiocapture [audiocapture.c:235:registry_event_global]: Found Audio/Sink node AudioDevice0Output0 with id 146.
137+
audiocapture[1346447]: D audiocapture [audiocapture.c:114:on_state_changed]: State for stream from AudioDevice0Output0 changed unconnected -> connecting
138+
audiocapture[1346447]: D audiocapture [audiocapture.c:114:on_state_changed]: State for stream from AudioDevice0Input0.Unprocessed changed connecting -> paused
139+
audiocapture[1346447]: D audiocapture [audiocapture.c:114:on_state_changed]: State for stream from AudioDevice0Input0 changed connecting -> paused
140+
audiocapture[1346447]: D audiocapture [audiocapture.c:114:on_state_changed]: State for stream from AudioDevice0Output0 changed connecting -> paused
141+
audiocapture[1346447]: I audiocapture [audiocapture.c:98:on_param_changed]: Capturing from node AudioDevice0Input0.Unprocessed, 2 channel(s), rate 48000.
142+
audiocapture[1346447]: D audiocapture [audiocapture.c:114:on_state_changed]: State for stream from AudioDevice0Input0.Unprocessed changed paused -> streaming
143+
audiocapture[1346447]: I audiocapture [audiocapture.c:98:on_param_changed]: Capturing from node AudioDevice0Input0, 1 channel(s), rate 48000.
144+
audiocapture[1346447]: D audiocapture [audiocapture.c:114:on_state_changed]: State for stream from AudioDevice0Input0 changed paused -> streaming
145+
audiocapture[1346447]: I audiocapture [audiocapture.c:98:on_param_changed]: Capturing from node AudioDevice0Output0, 1 channel(s), rate 48000.
146+
audiocapture[1346447]: D audiocapture [audiocapture.c:114:on_state_changed]: State for stream from AudioDevice0Output0 changed paused -> streaming
147+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -42.1 dBFS.
148+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -19.8 dBFS.
149+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
150+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -56.8 dBFS.
151+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -6.8 dBFS.
152+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
153+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -68.4 dBFS.
154+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -18.4 dBFS.
155+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
156+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -66.2 dBFS.
157+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -16.2 dBFS.
158+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
159+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -58.9 dBFS.
160+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -8.9 dBFS.
161+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
162+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -69.3 dBFS.
163+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -19.3 dBFS.
164+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
165+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -68.8 dBFS.
166+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -18.8 dBFS.
167+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
168+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -69.6 dBFS.
169+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -19.6 dBFS.
170+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
171+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -67.0 dBFS.
172+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -17.0 dBFS.
173+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
174+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -39.5 dBFS.
175+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak 10.5 dBFS.
176+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
177+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0.Unprocessed, channel 0, peak -62.7 dBFS.
178+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Input0, channel 0, peak -12.7 dBFS.
179+
audiocapture[1346447]: I audiocapture [audiocapture.c:184:on_timeout]: Node AudioDevice0Output0, channel 0, peak -inf dBFS.
180+
```
181+
182+
## License
183+
184+
**[Apache License 2.0](../LICENSE)**

0 commit comments

Comments
 (0)