From 8160b5cdc8547277f3f31ee1e41524754d5d987d Mon Sep 17 00:00:00 2001 From: Jan-Simon Möller Date: Wed, 26 Jan 2022 12:25:31 +0100 Subject: Update to documentation before Marlin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is mostly a cleanup before the Magic Marlin Release. We do have the Application Framework chapter rewrite pending. Bug-AGL: SPEC-4236 Signed-off-by: Jan-Simon Möller Change-Id: I7e1f39667b4db417497b1d0c84065c173fa18439 Reviewed-on: https://gerrit.automotivelinux.org/gerrit/c/AGL/documentation/+/27097 --- docs/5_Component_Documentation/5_appfw.md | 7 - .../5_application_framework.md | 7 + docs/5_Component_Documentation/6_cynagora.md | 7 - .../6_pipewire_wireplumber.md | 159 +++++++++++++ .../7_ic-sound-manager.md | 120 ++++++++++ docs/5_Component_Documentation/7_pyagl.md | 250 --------------------- .../8_pipewire_wireplumber.md | 159 ------------- .../9_ic-sound-manager.md | 120 ---------- 8 files changed, 286 insertions(+), 543 deletions(-) delete mode 100644 docs/5_Component_Documentation/5_appfw.md create mode 100644 docs/5_Component_Documentation/5_application_framework.md delete mode 100644 docs/5_Component_Documentation/6_cynagora.md create mode 100644 docs/5_Component_Documentation/6_pipewire_wireplumber.md create mode 100644 docs/5_Component_Documentation/7_ic-sound-manager.md delete mode 100644 docs/5_Component_Documentation/7_pyagl.md delete mode 100644 docs/5_Component_Documentation/8_pipewire_wireplumber.md delete mode 100644 docs/5_Component_Documentation/9_ic-sound-manager.md (limited to 'docs/5_Component_Documentation') diff --git a/docs/5_Component_Documentation/5_appfw.md b/docs/5_Component_Documentation/5_appfw.md deleted file mode 100644 index 1af8796..0000000 --- a/docs/5_Component_Documentation/5_appfw.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Application Framework ---- - -# AppFW - -FIXME. diff --git a/docs/5_Component_Documentation/5_application_framework.md b/docs/5_Component_Documentation/5_application_framework.md new file mode 100644 index 0000000..1af8796 --- /dev/null +++ b/docs/5_Component_Documentation/5_application_framework.md @@ -0,0 +1,7 @@ +--- +title: Application Framework +--- + +# AppFW + +FIXME. diff --git a/docs/5_Component_Documentation/6_cynagora.md b/docs/5_Component_Documentation/6_cynagora.md deleted file mode 100644 index d2c74ce..0000000 --- a/docs/5_Component_Documentation/6_cynagora.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Cynagora ---- - -# cynagora - -FIXME. diff --git a/docs/5_Component_Documentation/6_pipewire_wireplumber.md b/docs/5_Component_Documentation/6_pipewire_wireplumber.md new file mode 100644 index 0000000..e58b97d --- /dev/null +++ b/docs/5_Component_Documentation/6_pipewire_wireplumber.md @@ -0,0 +1,159 @@ +--- +title: PipeWire / WirePlumber +--- + +# PipeWire / WirePlumber + +## Overview + +AGL uses the PipeWire daemon service to provide audio playback and capture capabilities. +PipeWire is accompanied by a secondary service, WirePlumber (also referred to as the +*session manager*), which provides policy management, device discovery, configuration and more. + +Applications can connect to the PipeWire service through its UNIX socket, by using either the +*libpipewire* or *libwireplumber* libraries as a front-end to that socket. + +Upstream documentation for these components can be found at the links below: + +- [PipeWire documentation](https://docs.pipewire.org/) + +- [WirePlumber documentation](https://pipewire.pages.freedesktop.org/wireplumber/) + +- [PipeWire Wiki](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/home) + +## APIs + +### libpipewire + +The main entry point for applications to access the audio system is the API offered by +*libpipewire*. The functionality offered by *libpipewire* is vast and it is beyond the +scope of this document to describe it all. + +For playback and capture, applications should use *struct pw_stream* and its associated methods. +See [PipeWire: Tutorial - Part 4: Playing a tone](https://docs.pipewire.org/page_tutorial4.html) +for a starting point. + +### GStreamer (Recommended) + +For convenience, applications that use GStreamer can use the PipeWire GStreamer elements to +plug the functionality offered by *struct pw_stream* directly in the GStreamer pipeline. These +elements are called *pipewiresrc* and *pipewiresink* + +Example: + +```shell +> gst-launch-1.0 audiotestsrc ! pipewiresink +``` + +Through these elements, it is possible to specify the application role by setting it in the +*stream-properties* property of the element, as shown below: + +```shell +> gst-launch-1.0 audiotestsrc ! pipewiresink stream-properties="p,media.role=Multimedia"" +``` + +or, in the C API: + +```c +gst_util_set_object_arg (sink, "stream-properties", "p,media.role=Multimedia"); +``` + +Of course, it is also possible to use *alsasink* and *alsasrc* and route audio through the +virtual ALSA device that is mentioned below. This is also the default behavior of *playbin* +and similar auto-plugging elements, because the PipeWire GStreamer elements are not autoplugged +(this may change in a future version). + +### ALSA + +PipeWire offers a virtual ALSA device (called *pipewire*) that redirects audio to PipeWire +through an ALSA PCM plugin. This device is the default one, so unless you explicitly specify +a device in your ALSA client application, audio will go through PipeWire instead. + +Example: + +```shell +> aplay sound.wav # the default device is 'pipewire' +> aplay -D pipewire sound.wav +``` + +In order to specify the application role while using the ALSA compatibility device, pass the role +as a device parameter like this: + +```shell +> aplay -D pipewire:ROLE=Navigation turnleft.wav +``` + +### Audiomixer service + +See the separate [agl-service-audiomixer](https://git.automotivelinux.org/apps/agl-service-audiomixer/about/) documentation. + +### libwireplumber + +The WirePlumber library provides API that wraps libpipewire and makes it easier to work with +when you are writing control applications, such as a volume mixer. The audiomixer service is in +fact implemented using *libwireplumber*. + +WirePlumber also provides support for lua-based scripting. Standalone scripts, executed with the +*wpexec* tool, may be used as a means to rapidly make use of the API provided by *libwireplumber* + +## Tools + +* **wpctl**: allows inspecting the devices, choosing which source & sink are the default ones + and allows volume/mute adjustments to be made on the command line. Try `wpctl status` and + `wpctl help` to get started with it + +* **wpexec**: allows running wireplumber lua scripts standalone, which is useful to implement + custom scripts to interact with PipeWire + +* **pw-cli**: this is the main tool for interacting with pipewire directly + +* **pw-dump**: dumps all the objects in the pipewire graph to a big JSON. The output of this + tool is very useful to include in bug reports. It is also suitable for implementing scripts + that parse information with jq + +* **pw-dot** is a useful debug tool that dumps the objects in a dot graph for easy visualization + +* **pw-cat / pw-play / pw-record**: This is a set of tools similar to aplay/arecord, for simple + audio operations + +* **pw-top**: This is a performance measurement tool that shows realtime timing information + about the audio pipeline. Before running this tool, you will need to uncomment the loading + of "libpipewire-module-profiler" in /etc/pipewire/pipewire.conf and restart pipewire + +## Systemd Integration + +The PipeWire service, `pipewire.service`, is activated on demand, via systemd socket activation, +by `pipewire.socket`. The WirePlumber service, `wireplumber.service`, is bound to the pipewire +service and therefore started and stopped together with the PipeWire service. + +If you wish to manually stop or restart both services, you can do so by using *systemctl*, +operating on the *.socket* unit: + +```shell +> systemctl restart pipewire.socket +> systemctl stop pipewire.socket +``` + +## Debugging + +The PipeWire daemon can be configured to be more verbose by editing +**/etc/pipewire/pipewire.conf** and setting `log.level=n` (n=0-5) in section +`context.properties`. + +Similarly, the WirePlumber daemon can be configured to be more verbose by editing +**/etc/wireplumber/wireplumber.conf** and setting `log.level=n` (n=0-5) in section +`context.properties`. + +All messages will be available in the systemd journal, inspectable with journalctl. + +For applications, at runtime, `PIPEWIRE_DEBUG` can be set to a value between 0 and 5, +with 5 being the most verbose and 0 the least verbose. + +For applications that use *libwireplumber* the equivalent environment variable is +`WIREPLUMBER_DEBUG`, which also takes values between 0 and 5. + +The default debug level for the daemons is 2, while for applications it's 0 +(with few exceptions). + +More information is also available on +[WirePlumber's documentation on debug logging](https://pipewire.pages.freedesktop.org/wireplumber/daemon-logging.html) diff --git a/docs/5_Component_Documentation/7_ic-sound-manager.md b/docs/5_Component_Documentation/7_ic-sound-manager.md new file mode 100644 index 0000000..75e163e --- /dev/null +++ b/docs/5_Component_Documentation/7_ic-sound-manager.md @@ -0,0 +1,120 @@ +# Instrument Cluster Sound Management + +## Introduction + +This document describes the design of the software setup which enables the integration +of AGL’s sound system with applications running in the Instrument Cluster domain. +This software setup is specific to the case where a single system is used to implement +both the Instrument Cluster and some other domain of the vehicle, typically the +In-Vehicle-Infotainment domain, using container technology to separate them. + +Applications running in the Instrument Cluster need a way to safely play important +sounds to alert the driver of conditions that need the driver’s attention. At the same +time, in a containerized environment that serves multiple vehicle domains, applications +running in other containers may be using the sound hardware to play less important sounds, +such as music, which conflicts with the IC’s need to play sound on the same hardware. + +The solution developed here, for safety reasons, relies on the operating system and the +hardware itself to allow the IC applications to stream sounds to the speakers using a +dedicated device handle, while applications from other domains are all routed through a +sound server that runs on the host container and operates on a different sound device handle. + +However, to achieve good inter-operation, there is need for additional software mechanisms +that will work in combination with this hardware-based solution. First of all, it is necessary +to have a mechanism that allows IC applications to pause all sounds that are being routed via +the sound server while there is an important IC sound playing and resume them afterwards. +This is so that other domain applications can be informed of this temporary pause and offer +the appropriate user experience. Secondly, it is desirable to have separation of duties +between the host and the other domain’s (non-IC) container. It should be the responsibility +of the other domain’s container to implement the sound system policy, so that the host does +not need to be aware of the exact applications that are running on this container. + +## Requirements + +- Single system shared between IC and at least one secondary domain (IVI, other ...) + +- The domains are separated using containers + +- All the containers, including the host, are running a variant of AGL + +- The host OS and the secondary domain container use PipeWire and WirePlumber + to implement the sound system + +- The sound hardware offers, on the Linux kernel driver side, a separate ALSA + device for sounds that belong to the IC and a separate ALSA device for other sounds + +## Architectural design + +![Architecture overview](images/ic-sound-manager/architecture.png) + +The core of the sound system consists of the PipeWire daemon, which is responsible for routing +audio between the kernel and applications running in the “Other Container”. + +The PipeWire session is orchestrated by a secondary daemon, WirePlumber. WirePlumber is +designed in such a way so that it can have multiple instances, for task separation. +One instance shall be running in the host container and it shall be responsible for +managing the devices that PipeWire handles as well as the security isolation between +different applications and different containers. At least one more instance shall be +running in the “Other Container” and be responsible for implementing policy mechanisms +related to the applications that are running in that container. + +Further WirePlumber instances are possible to run as well. For instance, it may be desirable +to have another “policy” instance in a third container that implements another vehicle system +and shares the main PipeWire daemon from the host. Additionally, the “Other Container” may +be running a separate WirePlumber instance to manage bluetooth audio devices, which shall be +the responsibility of that container instead of the host. + +To implement communication between the IC and the host, a third daemon is used: pipewire-ic-ipc. +This daemon listens on a UNIX domain socket for messages from the IC applications and offers +them the ability to pause or resume sounds that are being routed via PipeWire. + +Finally, IC applications are given a library (icipc library) that allows them to send messages +to pipewire-ic-ipc on the host. This library is minimal and has no external dependencies, +for safety reasons. + +For sound playback, IC applications are expected to use the ALSA API directly and communicate +with the dedicated ALSA device that is meant for IC sounds. Arbitration of this device between +different IC applications is out of scope for this document and it is assumed to be a solved +problem. + +### PipeWire-IC-IPC + +This component acts as the server-side component for the UNIX socket that is used for +communication between the IC applications and the host. It is implemented as a pipewire module, +therefore it needs the `/usr/bin/pipewire` process in order to be launched. Launching happens +with a special configuration file (`pipewire-ic-ipc.conf`) which instructs this PipeWire process +to be launched as a client (`core.daemon = false`) and to load only `module-ic-ipc` together +with `module-protocol-native`. The latter enables communication with the daemon instance of +PipeWire (`core.daemon = true`), which implements the sound server. + +![PipeWire-IC-IPC Processes](images/ic-sound-manager/pipewire-ic-ipc-processes.png) + +### icipc library + +The IC Application is given a library (‘libicipc’) that implements the client side of +pipewire-ic-ipc. This library allows sending two commands: + +- SUSPEND + - Asks WirePlumber (via PipeWire) to cork applications and mute the ALSA device used by PipeWire +- RESUME + - Reverts the effects of SUSPEND + +IC Applications are expected to send the SUSPEND command before starting playback of a sound +to their dedicated ALSA device. The RESUME command should be sent after playback of this IC +sound has finished. + +It should be noted that the RESUME command is also issued automatically when the IC application +disconnects from the pipewire-ic-ipc UNIX socket. + +If multiple IC application issue SUSPEND to the pipewire-ic-ipc server, then only the first +SUSPEND generates actions for WirePlumber. The rest are counted and the pipewire-ic-ipc +server expects an equal number of RESUME commands before generating resume actions for +WirePlumber. + +The implementation of the SUSPEND/RESUME mechanism uses PipeWire’s metadata to signal +WirePlumber. PipeWire-IC-IPC will look for the “default” metadata object in PipeWire’s list +of objects and will write the “suspend.playback” key with a value of “true” on id 0. +The metadata change is then notified to all clients. WirePlumber, being a client, gets +notified of this change and takes actions. All actions are defined in Lua scripts. + +![PipeWire-IC-IPC Calls](images/ic-sound-manager/pipewire-ic-ipc-calls.png) diff --git a/docs/5_Component_Documentation/7_pyagl.md b/docs/5_Component_Documentation/7_pyagl.md deleted file mode 100644 index 40f0a53..0000000 --- a/docs/5_Component_Documentation/7_pyagl.md +++ /dev/null @@ -1,250 +0,0 @@ ---- -title: PyAGL ---- - -# 0. Intro -## Main purpose -PyAGL was written to be used as a testing framework replacing the Lua afb-test one, -however the modules are written in a way that could be used as standalone utilities to query -and evaluate apis and verbs from the App Framework Binder services in AGL. - -## High level overview -Python compatibility: **Python 3.8 preferred**, **should** be compatible with **3.6** - -Initial development PyAGL was done with Python **3.6** in mind, heavily using f-strings and a few typings. As of writing this -documentation(June 3rd 2021), current stable AGL version is Koi 11.0.2 which has Python 3.8, and further development is -done using 3.8 and 3.9 runtimes although **no** version-specific features are used from later versions; -features **used** are kept within features offered by Python **3.8**. - -The test suite is written in a relatively standard way of extending **pytest** with a couple tweaks -tailored to Jenkins CI and LAVA for AGL with regards to output and timings/timeouts, and these tweaks -are enabled by running `pytest -L` in order to enable LAVA logging behavior. - -The way PyAGL works could be summarized in several bullets below: - -* `websockets` package is used to communicate to the services, `x-afb-ws-json1` is used as a subprotocol, -* base.py provides AGLBaseService to be extended for each service -* AGLBaseService has a `portfinder()` routine which will use `asyncssh` if used remotely, -to figure out the port of the service's websocket that is listening on. When this was implemented services had a hardcoded listening port, -and was often changed when a new service was introduced. If you specify port, pyagl will connect to it directly. If no port is specified and -portfinder() cannot find the process or listening port should throw an exception and exit. -* main() implementations in most PyAGL services' bindings are intended to be used as a convenient standalone utility to query verbs, although -not necessarily available. -* PyAGL bindings are organized in classes, method names and respective parameters mostly adhere to service verbs/apis described -per service in https://git.automotivelinux.org/apps/agl-service-*/about -For example, in https://git.automotivelinux.org/apps/agl-service-audiomixer/about/ the docs for the service describe 5 verbs - -subscribe, unsubscribe, list_controls, volume, mute - and their respective methods in audiomixer.py. -* as mentioned above `pytest` package is required for unit tests. -* `pytest-async` is needed by pytest to cooperate with asyncio -* `pytest-dependency` is used in cases where specific testing order is needed and used via decorators - -# 1. Using PyAGL -Command line execution is intended for debugging and quick evaluation. -There are few prerequisites to start using it. First, your AGL image **must** be bitbaked with **agl-devel** feature when sourcing __aglsetup.sh__; -if not - the running AGL instance won't have websocket services exposed to listening TCP ports and PyAGL will fail to connect. - -```bash -git clone "https://gerrit.automotivelinux.org/gerrit/src/pyagl" -``` -Preferably create a virtualenv and install the packages in the env -```bash -user@debian:~$ python3 -mvenv ~/.virtualenvs/aglenv -user@debian:~$ source ~/.virtualenvs/aglenv/bin/activate -(aglenv) user@debian:~$ pip install -r requirements.txt -``` -Hard requirements are asyncssh, websockets, pytest, pytest-dependency, pytest-async; the others in the file are dependencies of the mentioned packages. - -If you have installed PyAGL as python package or current working directory is in the project root: -``` -(aglenv) user@debian:~/pyagl$ python3 -m pyagl.services.audiomixer 192.168.234.34 --list_controls -``` -should produce the following or similar result depending on how many controls are exposed and which AGL version you are running: -``` -matching services: ['afm-service-agl-service-audiomixer--0.1--main@1001.service'] -Requesting list_controls with id 359450446 -[RESPONSE][Status: success][359450446][Info: None][Data: [{'control': 'Master Playback', 'volume': 1.0, 'mute': 0}, -{'control': 'Playback: Speech-Low', 'volume': 1.0, 'mute': 0}, {'control': 'Playback: Emergency', 'volume': 1.0, 'mute': 0}, -{'control': 'Playback: Speech-High', 'volume': 1.0, 'mute': 0}, {'control': 'Playback: Navigation', 'volume': 1.0, 'mute': 0}, -{'control': 'Playback: Multimedia', 'volume': 1.0, 'mute': 0}, {'control': 'Playback: Custom-Low', 'volume': 1.0, 'mute': 0}, -{'control': 'Playback: Communication', 'volume': 1.0, 'mute': 0}, {'control': 'Playback: Custom-High', 'volume': 1.0, 'mute': 0}]] -``` - -# 2. Running the tests - -## Markers -Before running the tests it must be noted that __Markers__ are metavariables applied to the tests which play important part -in the behavior of pyagl and add great flexibility when deciding which tests to run. -Each test suite usually have unambiguous marker and description in `pytest.ini` for the reason above. -The default markers are applied in the list variable `pytestmark` in the beginning of each test file. -Each test suite has at least 2 default markers. - -`pytest.mark.asyncio` is a special marker needed because we are running pytest in async mode, -and the other marker is the name of the testsuite - for example `pytest.mark.geoclue` is for `geoclue` tests. - -`pytest.mark.dependency` is another special marker used by the pytest-dependency library helping for -running tests in a particular order when a more complicated setup other than a fixture is needed for -a test or the setup itself is a test or sequence of tests. - -`pytest.mark.hwrequired` is a marker signifying that additional hardware is required for tests to be successful - e.g. bluetooth phonebook tests need a phone with a pbap profile connected - -`pytest.mark.internet` is a marker for tests which require internet connection - -As mentioned above, behavior of pytest is altered by the markers because pytest does expression matching by marker and test names for which suite to be run. -In the example below we select all internet requiring and subscription tests but skip those that require additional hardware. -If we have a match with `internet or subscribe` the test will be selected, but if the test has `hwrequired` will be skipped, otherwise deselected overall. -``` -h3ulcb:~# pyagl -vk "internet or subscribe and not hwrequired" -============================ test session starts ============================ -platform linux -- Python 3.8.2, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -- /usr/bin/python3 -cachedir: .pytest_cache -rootdir: /usr/lib/python3.8/site-packages/pyagl, inifile: pytest.ini -plugins: asyncio-0.10.0, dependency-0.5.1, reverse-1.0.1 -collected 218 items / 163 deselected / 55 selected - -test_bluetooth.py::test_subscribe_device_changes PASSED [ 1%] -... -test_radio.py::test_unsubscribe_all PASSED [ 90%] -test_signal_composer.py::test_subscribe SKIPPED [ 92%] -test_signal_composer.py::test_unsubscribe SKIPPED [ 94%] -test_weather.py::test_current_weather FAILED [ 96%] -test_weather.py::test_subscribe_weather PASSED [ 98%] -test_weather.py::test_unsubscribe_weather PASSED [100%] -==== 1 failed, 37 passed, 17 skipped, 163 deselected, 1 warning in 3.23s ==== - -``` - -## Locally - On the board itself -There is the /usr/bin/pyagl convenience wrapper script which invokes pytest -with the tests residing in `/usr/lib/python3.8/site-packages/pyagl/tests` which -also will pass all commandline arguments down to pytest - -```console -qemux86-64:~# pyagl -=================== test session starts ============================= -platform linux -- Python 3.8.2, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 -rootdir: /usr/lib/python3.8/site-packages/pyagl, inifile: pytest.ini -plugins: dependency-0.5.1, asyncio-0.10.0, reverse-1.0.1 -collected 213 items - -test_audiomixer.py ....... [ 3%] -test_bluetooth.py ............xxxsxx [ 11%] -test_bluetooth_map.py .x.xs. [ 12%] -... -``` - -## Remotely -You must export `AGL_TGT_IP` environment variable first, containing a string with a reachable IP address -configured(either DHCP or static) on one of the interfeces on the AGL instance(board or vm) on your network. -`AGL_TGT_PORT` is not required, however can be exported to skip over connecting to the board via ssh first -in order to figure out the listening port of service. - -```console -user@debian:~$ source ~/.virtualenvs/aglenv/bin/activate -(pyagl) user@debian:~$ export AGL_TGT_IP=192.168.234.34 -(pyagl) user@debian:~$ cd pydev/pyagl/pyagl/tests -(pyagl) user@debian:~/pydev/pyagl/pyagl/tests$ pytest test_geoclue.py -========================= test session starts ========================= -platform linux -- Python 3.9.2, pytest-5.4.1, py-1.8.1, pluggy-0.13.1 -rootdir: /home/user/pydev/pyagl/pyagl, inifile: pytest.ini -plugins: dependency-0.5.1, asyncio-0.11.0 -collected 3 items - -test_geoclue.py ... [100%] -``` -# 3. Writing bindings and/or tests for new services -This assumes you have already went through ["3. Developer Guides > Creating a new service"](../3_Developer_Guides/2_Creating_a_New_Service.md). - -Templates directory contains barebone _cookiecutter_ templates to create your project. -If you do not intend to use cookiecutter, you need a simple service file in which you inherit AGLBaseService -from base.py. -You can take a look at pyagl/services/geoclue.py and pyagl/tests/test_geoclue.py which is probably the -simplest binding in PyAGL for a reference and example. All basic methods like -[send|receive|un/subscribe|portfinder] are implemented in the base class. -You would need to do minimal work to create new service binding from scratch and by example of the geoclue service you need to do the following: - -* do the basic imports -```python3 -from pyagl.services.base import AGLBaseService, AFBResponse -import asyncio -``` - -* inherit AGLBaseService and type in the service class member the service name presuming you are following the AGL naming convention: -(if your new service does not follow the convention, the portfider routine wont work and you'll have to specify service port manually) -```python3 -class GeoClueService(AGLBaseService): - service = 'agl-service-geoclue' -``` - -* if you intend to run the new service binding as a standalone utility, you might want to add your new options to the argparser -```python3 - parser = AGLBaseService.getparser() - parser.add_argument('--location', help='Get current location', action='store_true') -``` - -* override the __init__ method with the respective parameters as api(used in the binding) and systemd service slug -``` - def __init__(self, ip, port=None, api='geoclue'): - super().__init__(ip=ip, port=port, api=api, service='agl-service-geoclue') -``` - -* define your methods and send requests with .request() which prepares the data in a JSON format, request returns message id -``` - async def location(self): - return await self.request('location') -``` - -* get the raw response with data = await .response() or use .afbresponse() to get structured data - -PyAGL is written with asyncio so you'd need an event loop to get your code running. -Most modules have standalone CLI functionality, which is done via ArgumentParser and each module that -is CLI usable inherits static base parser and extends its arguments as shown in the example below: - -```python3 -async def main(loop): - args = GeoClueService.parser.parse_args() - gcs = await GeoClueService(args.ipaddr) - - if args.location: - msgid = await gcs.location() - print(f'Sent location request with messageid {msgid}') - print(await gcs.afbresponse()) - -if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.run_until_complete(main(loop)) -``` -`GeoClueService` will try to return an instance with connection to the service. It will throw an exception if it fails to do so. -`AGLBaseService.afbresponse()` method is a wrapper on top of .response() which will prepare, validate and format data for a convenient usage intended for CLI usage. -`AFBResponse.__str__` is also a convenience method to be able to print all relevant data in regarding state of the response. - -```console -(aglenv) user@debian:~$ python3 -m pyagl.services.geoclue 192.168.234.251 --location -matching services: ['afm-service-agl-service-geoclue--1.0--main.service'] -Sent location request with messageid 29188435 -[RESPONSE][Status: success][29188435][Info: GeoClue location data][Data: {'latitude': 42.6898421, 'longitude': 23.3099069, 'accuracy': 107.4702045}] -``` - - -# 4. Environment variables -The following environment variables are used in the PyAGL project: - -Variable | Description ---- | --- -**AGL_TGT_IP** | **required** -**AGL_TGT_PORT** | **optional**, when this is used portfinder() routine is skipped, attempts direct websocket connection to this port. -**AGL_TEST_TIMEOUT** | **optional**, over-ride the default 5 second timeout value for binding responses. This is useful in many cases where a long-standing request has taken place - e.g. importing few thousand entries from a phonebook -**AGL_AVAILABLE_INTERFACES** | **optional**, specify which of ethernet, wifi, and bluetooth interfaces are available. The value is a comma separated list, with a default value of "ethernet,wifi,bluetooth". -**AGL_BTMAP_RECIPIENT** | **optional**, when running Bluetooth MAP tests, this would be used as phone number to write text messages to. -**AGL_BTMAP_TEXT** | **optional**, when running Bluetooth MAP tests, messages will be composed with this text. -**AGL_CAN_INTERFACE** | **optional**, specify the CAN interface to use for CAN testing, default value is "can0". -**AGL_PBAP_PHONENUM** | **optional**, when running Bluetooth PBAP tests, this phone number will be used to .search(). -**AGL_PBAP_VCF** | **optional**, for the Bluetooh PBAP tests query a contact entry out of the phonebook. -**AGL_BT_TEST_ADDR** | **optional**, for the Bluetooth tests pair/connect/disconnect with an actual Bluetooth device(phone)'s address . The address should have the DBus address style as "dev_00_01_02_03_04_05" instead of a regular colon-separated MAC address. - -# 5. Debugging -PyAGL uses pythons logger library so you can rise the logging level and see the output from most of the modules, -but was disabled by default because of the massive amount of output produced. When the default logger is enabled -all other libraries will show their output with pyagl - e.g. websockets, asyncssh etc. - -README.md in the root directory of the project also contains useful information. - diff --git a/docs/5_Component_Documentation/8_pipewire_wireplumber.md b/docs/5_Component_Documentation/8_pipewire_wireplumber.md deleted file mode 100644 index e58b97d..0000000 --- a/docs/5_Component_Documentation/8_pipewire_wireplumber.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -title: PipeWire / WirePlumber ---- - -# PipeWire / WirePlumber - -## Overview - -AGL uses the PipeWire daemon service to provide audio playback and capture capabilities. -PipeWire is accompanied by a secondary service, WirePlumber (also referred to as the -*session manager*), which provides policy management, device discovery, configuration and more. - -Applications can connect to the PipeWire service through its UNIX socket, by using either the -*libpipewire* or *libwireplumber* libraries as a front-end to that socket. - -Upstream documentation for these components can be found at the links below: - -- [PipeWire documentation](https://docs.pipewire.org/) - -- [WirePlumber documentation](https://pipewire.pages.freedesktop.org/wireplumber/) - -- [PipeWire Wiki](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/home) - -## APIs - -### libpipewire - -The main entry point for applications to access the audio system is the API offered by -*libpipewire*. The functionality offered by *libpipewire* is vast and it is beyond the -scope of this document to describe it all. - -For playback and capture, applications should use *struct pw_stream* and its associated methods. -See [PipeWire: Tutorial - Part 4: Playing a tone](https://docs.pipewire.org/page_tutorial4.html) -for a starting point. - -### GStreamer (Recommended) - -For convenience, applications that use GStreamer can use the PipeWire GStreamer elements to -plug the functionality offered by *struct pw_stream* directly in the GStreamer pipeline. These -elements are called *pipewiresrc* and *pipewiresink* - -Example: - -```shell -> gst-launch-1.0 audiotestsrc ! pipewiresink -``` - -Through these elements, it is possible to specify the application role by setting it in the -*stream-properties* property of the element, as shown below: - -```shell -> gst-launch-1.0 audiotestsrc ! pipewiresink stream-properties="p,media.role=Multimedia"" -``` - -or, in the C API: - -```c -gst_util_set_object_arg (sink, "stream-properties", "p,media.role=Multimedia"); -``` - -Of course, it is also possible to use *alsasink* and *alsasrc* and route audio through the -virtual ALSA device that is mentioned below. This is also the default behavior of *playbin* -and similar auto-plugging elements, because the PipeWire GStreamer elements are not autoplugged -(this may change in a future version). - -### ALSA - -PipeWire offers a virtual ALSA device (called *pipewire*) that redirects audio to PipeWire -through an ALSA PCM plugin. This device is the default one, so unless you explicitly specify -a device in your ALSA client application, audio will go through PipeWire instead. - -Example: - -```shell -> aplay sound.wav # the default device is 'pipewire' -> aplay -D pipewire sound.wav -``` - -In order to specify the application role while using the ALSA compatibility device, pass the role -as a device parameter like this: - -```shell -> aplay -D pipewire:ROLE=Navigation turnleft.wav -``` - -### Audiomixer service - -See the separate [agl-service-audiomixer](https://git.automotivelinux.org/apps/agl-service-audiomixer/about/) documentation. - -### libwireplumber - -The WirePlumber library provides API that wraps libpipewire and makes it easier to work with -when you are writing control applications, such as a volume mixer. The audiomixer service is in -fact implemented using *libwireplumber*. - -WirePlumber also provides support for lua-based scripting. Standalone scripts, executed with the -*wpexec* tool, may be used as a means to rapidly make use of the API provided by *libwireplumber* - -## Tools - -* **wpctl**: allows inspecting the devices, choosing which source & sink are the default ones - and allows volume/mute adjustments to be made on the command line. Try `wpctl status` and - `wpctl help` to get started with it - -* **wpexec**: allows running wireplumber lua scripts standalone, which is useful to implement - custom scripts to interact with PipeWire - -* **pw-cli**: this is the main tool for interacting with pipewire directly - -* **pw-dump**: dumps all the objects in the pipewire graph to a big JSON. The output of this - tool is very useful to include in bug reports. It is also suitable for implementing scripts - that parse information with jq - -* **pw-dot** is a useful debug tool that dumps the objects in a dot graph for easy visualization - -* **pw-cat / pw-play / pw-record**: This is a set of tools similar to aplay/arecord, for simple - audio operations - -* **pw-top**: This is a performance measurement tool that shows realtime timing information - about the audio pipeline. Before running this tool, you will need to uncomment the loading - of "libpipewire-module-profiler" in /etc/pipewire/pipewire.conf and restart pipewire - -## Systemd Integration - -The PipeWire service, `pipewire.service`, is activated on demand, via systemd socket activation, -by `pipewire.socket`. The WirePlumber service, `wireplumber.service`, is bound to the pipewire -service and therefore started and stopped together with the PipeWire service. - -If you wish to manually stop or restart both services, you can do so by using *systemctl*, -operating on the *.socket* unit: - -```shell -> systemctl restart pipewire.socket -> systemctl stop pipewire.socket -``` - -## Debugging - -The PipeWire daemon can be configured to be more verbose by editing -**/etc/pipewire/pipewire.conf** and setting `log.level=n` (n=0-5) in section -`context.properties`. - -Similarly, the WirePlumber daemon can be configured to be more verbose by editing -**/etc/wireplumber/wireplumber.conf** and setting `log.level=n` (n=0-5) in section -`context.properties`. - -All messages will be available in the systemd journal, inspectable with journalctl. - -For applications, at runtime, `PIPEWIRE_DEBUG` can be set to a value between 0 and 5, -with 5 being the most verbose and 0 the least verbose. - -For applications that use *libwireplumber* the equivalent environment variable is -`WIREPLUMBER_DEBUG`, which also takes values between 0 and 5. - -The default debug level for the daemons is 2, while for applications it's 0 -(with few exceptions). - -More information is also available on -[WirePlumber's documentation on debug logging](https://pipewire.pages.freedesktop.org/wireplumber/daemon-logging.html) diff --git a/docs/5_Component_Documentation/9_ic-sound-manager.md b/docs/5_Component_Documentation/9_ic-sound-manager.md deleted file mode 100644 index 75e163e..0000000 --- a/docs/5_Component_Documentation/9_ic-sound-manager.md +++ /dev/null @@ -1,120 +0,0 @@ -# Instrument Cluster Sound Management - -## Introduction - -This document describes the design of the software setup which enables the integration -of AGL’s sound system with applications running in the Instrument Cluster domain. -This software setup is specific to the case where a single system is used to implement -both the Instrument Cluster and some other domain of the vehicle, typically the -In-Vehicle-Infotainment domain, using container technology to separate them. - -Applications running in the Instrument Cluster need a way to safely play important -sounds to alert the driver of conditions that need the driver’s attention. At the same -time, in a containerized environment that serves multiple vehicle domains, applications -running in other containers may be using the sound hardware to play less important sounds, -such as music, which conflicts with the IC’s need to play sound on the same hardware. - -The solution developed here, for safety reasons, relies on the operating system and the -hardware itself to allow the IC applications to stream sounds to the speakers using a -dedicated device handle, while applications from other domains are all routed through a -sound server that runs on the host container and operates on a different sound device handle. - -However, to achieve good inter-operation, there is need for additional software mechanisms -that will work in combination with this hardware-based solution. First of all, it is necessary -to have a mechanism that allows IC applications to pause all sounds that are being routed via -the sound server while there is an important IC sound playing and resume them afterwards. -This is so that other domain applications can be informed of this temporary pause and offer -the appropriate user experience. Secondly, it is desirable to have separation of duties -between the host and the other domain’s (non-IC) container. It should be the responsibility -of the other domain’s container to implement the sound system policy, so that the host does -not need to be aware of the exact applications that are running on this container. - -## Requirements - -- Single system shared between IC and at least one secondary domain (IVI, other ...) - -- The domains are separated using containers - -- All the containers, including the host, are running a variant of AGL - -- The host OS and the secondary domain container use PipeWire and WirePlumber - to implement the sound system - -- The sound hardware offers, on the Linux kernel driver side, a separate ALSA - device for sounds that belong to the IC and a separate ALSA device for other sounds - -## Architectural design - -![Architecture overview](images/ic-sound-manager/architecture.png) - -The core of the sound system consists of the PipeWire daemon, which is responsible for routing -audio between the kernel and applications running in the “Other Container”. - -The PipeWire session is orchestrated by a secondary daemon, WirePlumber. WirePlumber is -designed in such a way so that it can have multiple instances, for task separation. -One instance shall be running in the host container and it shall be responsible for -managing the devices that PipeWire handles as well as the security isolation between -different applications and different containers. At least one more instance shall be -running in the “Other Container” and be responsible for implementing policy mechanisms -related to the applications that are running in that container. - -Further WirePlumber instances are possible to run as well. For instance, it may be desirable -to have another “policy” instance in a third container that implements another vehicle system -and shares the main PipeWire daemon from the host. Additionally, the “Other Container” may -be running a separate WirePlumber instance to manage bluetooth audio devices, which shall be -the responsibility of that container instead of the host. - -To implement communication between the IC and the host, a third daemon is used: pipewire-ic-ipc. -This daemon listens on a UNIX domain socket for messages from the IC applications and offers -them the ability to pause or resume sounds that are being routed via PipeWire. - -Finally, IC applications are given a library (icipc library) that allows them to send messages -to pipewire-ic-ipc on the host. This library is minimal and has no external dependencies, -for safety reasons. - -For sound playback, IC applications are expected to use the ALSA API directly and communicate -with the dedicated ALSA device that is meant for IC sounds. Arbitration of this device between -different IC applications is out of scope for this document and it is assumed to be a solved -problem. - -### PipeWire-IC-IPC - -This component acts as the server-side component for the UNIX socket that is used for -communication between the IC applications and the host. It is implemented as a pipewire module, -therefore it needs the `/usr/bin/pipewire` process in order to be launched. Launching happens -with a special configuration file (`pipewire-ic-ipc.conf`) which instructs this PipeWire process -to be launched as a client (`core.daemon = false`) and to load only `module-ic-ipc` together -with `module-protocol-native`. The latter enables communication with the daemon instance of -PipeWire (`core.daemon = true`), which implements the sound server. - -![PipeWire-IC-IPC Processes](images/ic-sound-manager/pipewire-ic-ipc-processes.png) - -### icipc library - -The IC Application is given a library (‘libicipc’) that implements the client side of -pipewire-ic-ipc. This library allows sending two commands: - -- SUSPEND - - Asks WirePlumber (via PipeWire) to cork applications and mute the ALSA device used by PipeWire -- RESUME - - Reverts the effects of SUSPEND - -IC Applications are expected to send the SUSPEND command before starting playback of a sound -to their dedicated ALSA device. The RESUME command should be sent after playback of this IC -sound has finished. - -It should be noted that the RESUME command is also issued automatically when the IC application -disconnects from the pipewire-ic-ipc UNIX socket. - -If multiple IC application issue SUSPEND to the pipewire-ic-ipc server, then only the first -SUSPEND generates actions for WirePlumber. The rest are counted and the pipewire-ic-ipc -server expects an equal number of RESUME commands before generating resume actions for -WirePlumber. - -The implementation of the SUSPEND/RESUME mechanism uses PipeWire’s metadata to signal -WirePlumber. PipeWire-IC-IPC will look for the “default” metadata object in PipeWire’s list -of objects and will write the “suspend.playback” key with a value of “true” on id 0. -The metadata change is then notified to all clients. WirePlumber, being a client, gets -notified of this change and takes actions. All actions are defined in Lua scripts. - -![PipeWire-IC-IPC Calls](images/ic-sound-manager/pipewire-ic-ipc-calls.png) -- cgit 1.2.3-korg