|
| 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