add immediate rendering flag

This commit is contained in:
pstruebi
2025-08-12 16:12:02 +02:00
parent 40eca11641
commit b266c38de3
5 changed files with 41 additions and 37 deletions

View File

@@ -139,12 +139,11 @@ sudo systemctl status auracast-frontend
If you want to run the services as a specific user, edit the `User=` line in the service files accordingly. If you want to run the services as a specific user, edit the `User=` line in the service files accordingly.
# Setup the audio system # Setup the audio system
sudo apt update
sudo apt remove -y libportaudio2 portaudio19-dev libportaudiocpp0 sudo apt remove -y libportaudio2 portaudio19-dev libportaudiocpp0
echo "y" | rpi-update stable echo "y" | rpi-update stable
sudo apt update
# TODO: needed ? # TODO: needed ?
sudo apt install pipewire wireplumber pipewire-audio-client-libraries rtkit sudo apt install pipewire wireplumber pipewire-audio-client-libraries rtkit
mkdir -p ~/.config/pipewire/pipewire.conf.d mkdir -p ~/.config/pipewire/pipewire.conf.d
@@ -158,8 +157,7 @@ sudo cpufreq-set -g performance
sudo apt install -y --no-install-recommends \ sudo apt install -y --no-install-recommends \
git build-essential cmake pkg-config \ git build-essential cmake pkg-config \
libasound2-dev libpulse-dev libjack-jackd2-dev jackd \ libasound2-dev libpulse-devpipewire ethtool linuxptp
pipewire ethtool linuxptp
git clone https://github.com/PortAudio/portaudio.git git clone https://github.com/PortAudio/portaudio.git
cd portaudio cd portaudio
@@ -169,7 +167,7 @@ cmake -S . -B build -G"Unix Makefiles" \
-DBUILD_SHARED_LIBS=ON \ -DBUILD_SHARED_LIBS=ON \
-DPA_USE_ALSA=OFF \ -DPA_USE_ALSA=OFF \
-DPA_USE_PULSEAUDIO=ON \ -DPA_USE_PULSEAUDIO=ON \
-DPA_USE_JACK=ON -DPA_USE_JACK=OFF
cmake --build build -j$(nproc) cmake --build build -j$(nproc)
sudo cmake --install build # installs to /usr/local/lib sudo cmake --install build # installs to /usr/local/lib
sudo ldconfig # refresh linker cache sudo ldconfig # refresh linker cache

View File

@@ -35,6 +35,10 @@ class AuracastGlobalConfig(BaseModel):
presentation_delay_us: int = 40000 presentation_delay_us: int = 40000
# TODO:pydantic does not support bytes serialization - use .hex and np.fromhex() # TODO:pydantic does not support bytes serialization - use .hex and np.fromhex()
manufacturer_data: tuple[int, bytes] | tuple[None, None] = (None, None) manufacturer_data: tuple[int, bytes] | tuple[None, None] = (None, None)
# LE Audio: Broadcast Audio Immediate Rendering (metadata type 0x09)
# When true, include a zero-length LTV with type 0x09 in the subgroup metadata
# so receivers may render earlier than the presentation delay for lower latency.
immediate_rendering: bool = False
# "Audio input. " # "Audio input. "
# "'device' -> use the host's default sound input device, " # "'device' -> use the host's default sound input device, "

View File

@@ -235,6 +235,30 @@ async def init_broadcast(
bap_sampling_freq = getattr(bap.SamplingFrequency, f"FREQ_{global_config.auracast_sampling_rate_hz}") bap_sampling_freq = getattr(bap.SamplingFrequency, f"FREQ_{global_config.auracast_sampling_rate_hz}")
bigs = {} bigs = {}
for i, conf in enumerate(big_config): for i, conf in enumerate(big_config):
metadata=le_audio.Metadata(
[
le_audio.Metadata.Entry(
tag=le_audio.Metadata.Tag.LANGUAGE, data=conf.language.encode()
),
le_audio.Metadata.Entry(
tag=le_audio.Metadata.Tag.PROGRAM_INFO, data=conf.program_info.encode()
),
le_audio.Metadata.Entry(
tag=le_audio.Metadata.Tag.BROADCAST_NAME, data=conf.name.encode()
),
]
+ (
[
# Broadcast Audio Immediate Rendering flag (type 0x09), zero-length value
le_audio.Metadata.Entry(tag = le_audio.Metadata.Tag.BROADCAST_AUDIO_IMMEDIATE_RENDERING_FLAG, data=b"")
]
if global_config.immediate_rendering
else []
)
)
logging.info(
metadata.pretty_print("\n")
)
bigs[f'big{i}'] = {} bigs[f'big{i}'] = {}
# Config advertising set # Config advertising set
bigs[f'big{i}']['basic_audio_announcement'] = bap.BasicAudioAnnouncement( bigs[f'big{i}']['basic_audio_announcement'] = bap.BasicAudioAnnouncement(
@@ -247,19 +271,7 @@ async def init_broadcast(
frame_duration=bap.FrameDuration.DURATION_10000_US, frame_duration=bap.FrameDuration.DURATION_10000_US,
octets_per_codec_frame=global_config.octets_per_frame, octets_per_codec_frame=global_config.octets_per_frame,
), ),
metadata=le_audio.Metadata( metadata=metadata,
[
le_audio.Metadata.Entry(
tag=le_audio.Metadata.Tag.LANGUAGE, data=conf.language.encode()
),
le_audio.Metadata.Entry(
tag=le_audio.Metadata.Tag.PROGRAM_INFO, data=conf.program_info.encode()
),
le_audio.Metadata.Entry(
tag=le_audio.Metadata.Tag.BROADCAST_NAME, data=conf.name.encode()
),
]
),
bis=[ bis=[
bap.BasicAudioAnnouncement.BIS( bap.BasicAudioAnnouncement.BIS(
index=1, index=1,

View File

@@ -1,13 +1,10 @@
import logging import logging
import os import os
import asyncio
import aioconsole
from auracast import multicast from auracast import multicast
from auracast import auracast_config from auracast import auracast_config
from auracast.utils.sounddevice_utils import list_usb_pw_inputs, list_network_pw_inputs from auracast.utils.sounddevice_utils import list_usb_pw_inputs, list_network_pw_inputs
if __name__ == "__main__": if __name__ == "__main__":
logging.basicConfig( #export LOG_LEVEL=DEBUG logging.basicConfig( #export LOG_LEVEL=DEBUG
@@ -25,8 +22,9 @@ if __name__ == "__main__":
os.environ.setdefault("AURACAST_SD_BLOCKSIZE", "32") os.environ.setdefault("AURACAST_SD_BLOCKSIZE", "32")
# Accepts 'low'/'high'/'default' or seconds (float). Our shim parses number strings to float. # Accepts 'low'/'high'/'default' or seconds (float). Our shim parses number strings to float.
os.environ.setdefault("AURACAST_SD_LATENCY", "0.0015") os.environ.setdefault("AURACAST_SD_LATENCY", "0.0015")
print("USB pw inputs:") logging.info("USB pw inputs:")
usb_inputs = list_usb_pw_inputs() usb_inputs = list_usb_pw_inputs()
logging.info("AEs67 pw inputs:")
aes67_inputs = list_network_pw_inputs() aes67_inputs = list_network_pw_inputs()
for i, d in usb_inputs: for i, d in usb_inputs:
logging.info(f"{i}: {d['name']} in={d['max_input_channels']}") logging.info(f"{i}: {d['name']} in={d['max_input_channels']}")
@@ -62,17 +60,13 @@ if __name__ == "__main__":
octets_per_frame=OCTETS_PER_FRAME, octets_per_frame=OCTETS_PER_FRAME,
), ),
#auracast_config.AuracastBigConfigEng(), #auracast_config.AuracastBigConfigEng(),
],
] immediate_rendering=True,
qos_config=auracast_config.AuracastQosHigh(),
auracast_sampling_rate_hz = LC3_SRATE,
octets_per_frame = OCTETS_PER_FRAME, # 32kbps@16kHz
transport=TRANSPORT1
) )
config.qos_config=auracast_config.AuracastQosHigh()
config.transport=TRANSPORT1
# TODO: encrypted streams are not working
config.auracast_sampling_rate_hz = LC3_SRATE
config.octets_per_frame = OCTETS_PER_FRAME # 32kbps@16kHz
#config.debug = True #config.debug = True
multicast.run_async( multicast.run_async(

View File

@@ -14,10 +14,6 @@ print("\nOnly PulseAudio devices:")
for i, d in devices_by_backend("PulseAudio"): for i, d in devices_by_backend("PulseAudio"):
print(f"{i}: {d['name']} in={d['max_input_channels']} out={d['max_output_channels']}") print(f"{i}: {d['name']} in={d['max_input_channels']} out={d['max_output_channels']}")
# Example: only PulseAudio devices on Linux
# print("\nOnly JACK devices:")
# for i, d in devices_by_backend("JACK"):
# print(f"{i}: {d['name']} in={d['max_input_channels']} out={d['max_output_channels']}")
print("Network pw inputs:") print("Network pw inputs:")
for i, d in list_network_pw_inputs(): for i, d in list_network_pw_inputs():