refractori to use one common radio_init for both primary and secondary radio
This commit is contained in:
@@ -40,8 +40,6 @@ class AuracastGlobalConfig(BaseModel):
|
|||||||
# so receivers may render earlier than the presentation delay for lower latency.
|
# so receivers may render earlier than the presentation delay for lower latency.
|
||||||
immediate_rendering: bool = False
|
immediate_rendering: bool = False
|
||||||
assisted_listening_stream: bool = False
|
assisted_listening_stream: bool = False
|
||||||
# Adaptive frame dropping: discard sub-frame samples when buffer exceeds threshold
|
|
||||||
enable_adaptive_frame_dropping: bool = False
|
|
||||||
|
|
||||||
# "Audio input. "
|
# "Audio input. "
|
||||||
# "'device' -> use the host's default sound input device, "
|
# "'device' -> use the host's default sound input device, "
|
||||||
|
|||||||
@@ -885,9 +885,6 @@ if __name__ == "__main__":
|
|||||||
#config.immediate_rendering = True
|
#config.immediate_rendering = True
|
||||||
#config.debug = True
|
#config.debug = True
|
||||||
|
|
||||||
config.enable_adaptive_frame_dropping=False
|
|
||||||
# Enable clock drift compensation to prevent latency accumulation
|
|
||||||
|
|
||||||
run_async(
|
run_async(
|
||||||
broadcast(
|
broadcast(
|
||||||
config,
|
config,
|
||||||
|
|||||||
@@ -191,137 +191,53 @@ async def _stream_lc3(audio_data: dict[str, str], bigs_template: list) -> None:
|
|||||||
multicaster1.big_conf = bigs_template
|
multicaster1.big_conf = bigs_template
|
||||||
await multicaster1.start_streaming()
|
await multicaster1.start_streaming()
|
||||||
|
|
||||||
@app.post("/init")
|
async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup, current_mc: multicast_control.Multicaster | None):
|
||||||
async def initialize(conf: auracast_config.AuracastConfigGroup):
|
|
||||||
"""Initializes the primary broadcaster on the streamer thread."""
|
|
||||||
global global_config_group
|
|
||||||
async with _stream_lock:
|
|
||||||
try:
|
|
||||||
global_config_group = conf
|
|
||||||
log.info('Initializing multicaster1 with config:\n %s', conf.model_dump_json(indent=2))
|
|
||||||
# Inline of _init_primary
|
|
||||||
global multicaster1
|
|
||||||
if multicaster1 is not None:
|
|
||||||
await multicaster1.shutdown()
|
|
||||||
multicaster1 = None
|
|
||||||
|
|
||||||
conf.transport = TRANSPORT1
|
|
||||||
conf.enable_adaptive_frame_dropping = any(
|
|
||||||
isinstance(big.audio_source, str) and big.audio_source.startswith('device:')
|
|
||||||
for big in conf.bigs
|
|
||||||
)
|
|
||||||
|
|
||||||
first_source = conf.bigs[0].audio_source if conf.bigs else ''
|
|
||||||
input_device_name = None
|
|
||||||
audio_mode_persist = 'Demo'
|
|
||||||
if isinstance(first_source, str) and first_source.startswith('device:'):
|
|
||||||
input_device_name = first_source.split(':', 1)[1] if ':' in first_source else None
|
|
||||||
alsa_usb_names = {d.get('name') for _, d in get_alsa_usb_inputs()}
|
|
||||||
net_names = {d.get('name') for _, d in get_network_pw_inputs()}
|
|
||||||
audio_mode_persist = 'Network' if (input_device_name in net_names) else 'USB'
|
|
||||||
|
|
||||||
if input_device_name and input_device_name.isdigit():
|
|
||||||
device_index = int(input_device_name)
|
|
||||||
else:
|
|
||||||
device_index = resolve_input_device_index(input_device_name or '')
|
|
||||||
if device_index is None:
|
|
||||||
raise HTTPException(status_code=400, detail=f"Audio device '{input_device_name}' not found.")
|
|
||||||
for big in conf.bigs:
|
|
||||||
if isinstance(big.audio_source, str) and big.audio_source.startswith('device:'):
|
|
||||||
big.audio_source = f'device:{device_index}'
|
|
||||||
devinfo = sd.query_devices(device_index)
|
|
||||||
max_in = int(devinfo.get('max_input_channels') or 1)
|
|
||||||
channels = max(1, min(2, max_in))
|
|
||||||
for big in conf.bigs:
|
|
||||||
big.input_format = f"int16le,{48000},{channels}"
|
|
||||||
|
|
||||||
conf.qos_config.max_transport_latency_ms = int(conf.qos_config.number_of_retransmissions) * 10 + 3
|
|
||||||
|
|
||||||
for big in conf.bigs:
|
|
||||||
big.random_address = gen_random_add()
|
|
||||||
|
|
||||||
multicaster1 = multicast_control.Multicaster(conf, conf.bigs)
|
|
||||||
await multicaster1.init_broadcast()
|
|
||||||
auto_started = False
|
|
||||||
if any(isinstance(big.audio_source, str) and (big.audio_source.startswith("device:") or big.audio_source.startswith("file:")) for big in conf.bigs):
|
|
||||||
await multicaster1.start_streaming()
|
|
||||||
auto_started = True
|
|
||||||
|
|
||||||
demo_count = sum(1 for big in conf.bigs if isinstance(big.audio_source, str) and big.audio_source.startswith('file:'))
|
|
||||||
demo_rate = int(conf.auracast_sampling_rate_hz or 0)
|
|
||||||
demo_type = None
|
|
||||||
if demo_count > 0 and demo_rate > 0:
|
|
||||||
if demo_rate in (48000, 24000, 16000):
|
|
||||||
demo_type = f"{demo_count} × {demo_rate//1000}kHz"
|
|
||||||
else:
|
|
||||||
demo_type = f"{demo_count} × {demo_rate}Hz"
|
|
||||||
persisted = {
|
|
||||||
'channel_names': [big.name for big in conf.bigs],
|
|
||||||
'languages': [big.language for big in conf.bigs],
|
|
||||||
'audio_mode': audio_mode_persist,
|
|
||||||
'input_device': input_device_name,
|
|
||||||
'program_info': [getattr(big, 'program_info', None) for big in conf.bigs],
|
|
||||||
'gain': [getattr(big, 'input_gain', 1.0) for big in conf.bigs],
|
|
||||||
'auracast_sampling_rate_hz': conf.auracast_sampling_rate_hz,
|
|
||||||
'octets_per_frame': conf.octets_per_frame,
|
|
||||||
'presentation_delay_us': getattr(conf, 'presentation_delay_us', None),
|
|
||||||
'rtn': getattr(getattr(conf, 'qos_config', None), 'number_of_retransmissions', None),
|
|
||||||
'immediate_rendering': getattr(conf, 'immediate_rendering', False),
|
|
||||||
'assisted_listening_stream': getattr(conf, 'assisted_listening_stream', False),
|
|
||||||
'stream_password': (conf.bigs[0].code if conf.bigs and getattr(conf.bigs[0], 'code', None) else None),
|
|
||||||
'demo_total_streams': demo_count,
|
|
||||||
'demo_stream_type': demo_type,
|
|
||||||
'is_streaming': auto_started,
|
|
||||||
}
|
|
||||||
# Persist returned settings (avoid touching from worker thread)
|
|
||||||
save_settings(persisted, secondary=False)
|
|
||||||
except Exception as e:
|
|
||||||
log.error("Exception in /init: %s", traceback.format_exc())
|
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
|
||||||
|
|
||||||
@app.post("/init2")
|
|
||||||
async def initialize2(conf: auracast_config.AuracastConfigGroup):
|
|
||||||
"""Initializes the secondary broadcaster on the streamer thread."""
|
|
||||||
try:
|
try:
|
||||||
log.info('Initializing multicaster2 with config:\n %s', conf.model_dump_json(indent=2))
|
log.info('Initializing multicaster with transport %s and config:\n %s', transport, conf.model_dump_json(indent=2))
|
||||||
# Inline of _init_secondary
|
|
||||||
global multicaster2
|
|
||||||
if multicaster2 is not None:
|
|
||||||
await multicaster2.shutdown()
|
|
||||||
multicaster2 = None
|
|
||||||
|
|
||||||
conf.transport = TRANSPORT2
|
if current_mc is not None:
|
||||||
conf.enable_adaptive_frame_dropping = any(
|
await current_mc.shutdown()
|
||||||
isinstance(big.audio_source, str) and big.audio_source.startswith('device:')
|
current_mc = None
|
||||||
for big in conf.bigs
|
|
||||||
)
|
|
||||||
conf.qos_config.max_transport_latency_ms = int(conf.qos_config.number_of_retransmissions) * 10 + 3
|
|
||||||
|
|
||||||
for big in conf.bigs:
|
conf.transport = transport
|
||||||
if isinstance(big.audio_source, str) and big.audio_source.startswith('device:'):
|
|
||||||
device_name = big.audio_source.split(':', 1)[1]
|
|
||||||
net_names = {d.get('name') for _, d in get_network_pw_inputs()}
|
|
||||||
alsa_usb_names = {d.get('name') for _, d in get_alsa_usb_inputs()}
|
|
||||||
device_index = resolve_input_device_index(device_name)
|
|
||||||
if device_index is None:
|
|
||||||
raise HTTPException(status_code=400, detail=f"Audio device '{device_name}' not found.")
|
|
||||||
big.audio_source = f'device:{device_index}'
|
|
||||||
|
|
||||||
multicaster2 = multicast_control.Multicaster(conf, conf.bigs)
|
|
||||||
await multicaster2.init_broadcast()
|
|
||||||
if any(isinstance(big.audio_source, str) and (big.audio_source.startswith("device:") or big.audio_source.startswith("file:")) for big in conf.bigs):
|
|
||||||
await multicaster2.start_streaming()
|
|
||||||
# Build and persist secondary settings analogous to primary
|
|
||||||
first_source = conf.bigs[0].audio_source if conf.bigs else ''
|
first_source = conf.bigs[0].audio_source if conf.bigs else ''
|
||||||
input_device_name = None
|
input_device_name = None
|
||||||
audio_mode_persist = 'Demo'
|
audio_mode_persist = 'Demo'
|
||||||
if isinstance(first_source, str) and first_source.startswith('device:'):
|
if isinstance(first_source, str) and first_source.startswith('device:'):
|
||||||
input_device_name = first_source.split(':', 1)[1] if ':' in first_source else None
|
input_device_name = first_source.split(':', 1)[1] if ':' in first_source else None
|
||||||
try:
|
alsa_usb_names = {d.get('name') for _, d in get_alsa_usb_inputs()}
|
||||||
net_names = {d.get('name') for _, d in get_network_pw_inputs()}
|
net_names = {d.get('name') for _, d in get_network_pw_inputs()}
|
||||||
except Exception:
|
|
||||||
net_names = set()
|
|
||||||
audio_mode_persist = 'Network' if (input_device_name in net_names) else 'USB'
|
audio_mode_persist = 'Network' if (input_device_name in net_names) else 'USB'
|
||||||
|
|
||||||
|
if input_device_name and input_device_name.isdigit():
|
||||||
|
device_index = int(input_device_name)
|
||||||
|
else:
|
||||||
|
device_index = resolve_input_device_index(input_device_name or '')
|
||||||
|
if device_index is None:
|
||||||
|
raise HTTPException(status_code=400, detail=f"Audio device '{input_device_name}' not found.")
|
||||||
|
for big in conf.bigs:
|
||||||
|
if isinstance(big.audio_source, str) and big.audio_source.startswith('device:'):
|
||||||
|
big.audio_source = f'device:{device_index}'
|
||||||
|
devinfo = sd.query_devices(device_index)
|
||||||
|
max_in = int(devinfo.get('max_input_channels') or 1)
|
||||||
|
channels = max(1, min(2, max_in))
|
||||||
|
for big in conf.bigs:
|
||||||
|
big.input_format = f"int16le,{48000},{channels}"
|
||||||
|
|
||||||
|
conf.qos_config.max_transport_latency_ms = int(conf.qos_config.number_of_retransmissions) * 10 + 3
|
||||||
|
|
||||||
|
for big in conf.bigs:
|
||||||
|
big.random_address = gen_random_add()
|
||||||
|
|
||||||
|
mc = multicast_control.Multicaster(conf, conf.bigs)
|
||||||
|
await mc.init_broadcast()
|
||||||
|
|
||||||
|
auto_started = False
|
||||||
|
if any(isinstance(big.audio_source, str) and (big.audio_source.startswith("device:") or big.audio_source.startswith("file:")) for big in conf.bigs):
|
||||||
|
await mc.start_streaming()
|
||||||
|
auto_started = True
|
||||||
|
|
||||||
demo_count = sum(1 for big in conf.bigs if isinstance(big.audio_source, str) and big.audio_source.startswith('file:'))
|
demo_count = sum(1 for big in conf.bigs if isinstance(big.audio_source, str) and big.audio_source.startswith('file:'))
|
||||||
demo_rate = int(conf.auracast_sampling_rate_hz or 0)
|
demo_rate = int(conf.auracast_sampling_rate_hz or 0)
|
||||||
demo_type = None
|
demo_type = None
|
||||||
@@ -330,7 +246,7 @@ async def initialize2(conf: auracast_config.AuracastConfigGroup):
|
|||||||
demo_type = f"{demo_count} × {demo_rate//1000}kHz"
|
demo_type = f"{demo_count} × {demo_rate//1000}kHz"
|
||||||
else:
|
else:
|
||||||
demo_type = f"{demo_count} × {demo_rate}Hz"
|
demo_type = f"{demo_count} × {demo_rate}Hz"
|
||||||
persisted2 = {
|
persisted = {
|
||||||
'channel_names': [big.name for big in conf.bigs],
|
'channel_names': [big.name for big in conf.bigs],
|
||||||
'languages': [big.language for big in conf.bigs],
|
'languages': [big.language for big in conf.bigs],
|
||||||
'audio_mode': audio_mode_persist,
|
'audio_mode': audio_mode_persist,
|
||||||
@@ -346,28 +262,34 @@ async def initialize2(conf: auracast_config.AuracastConfigGroup):
|
|||||||
'stream_password': (conf.bigs[0].code if conf.bigs and getattr(conf.bigs[0], 'code', None) else None),
|
'stream_password': (conf.bigs[0].code if conf.bigs and getattr(conf.bigs[0], 'code', None) else None),
|
||||||
'demo_total_streams': demo_count,
|
'demo_total_streams': demo_count,
|
||||||
'demo_stream_type': demo_type,
|
'demo_stream_type': demo_type,
|
||||||
'is_streaming': any(isinstance(big.audio_source, str) and (big.audio_source.startswith("device:") or big.audio_source.startswith("file:")) for big in conf.bigs),
|
'is_streaming': auto_started,
|
||||||
}
|
}
|
||||||
save_settings(persisted2, secondary=True)
|
return mc, persisted
|
||||||
is_demo = any(isinstance(big.audio_source, str) and big.audio_source.startswith('file:') for big in conf.bigs)
|
except HTTPException:
|
||||||
if is_demo:
|
raise
|
||||||
settings = load_stream_settings() or {}
|
|
||||||
primary_count = int(settings.get('demo_total_streams') or len(settings.get('channel_names') or []))
|
|
||||||
secondary_count = len(conf.bigs or [])
|
|
||||||
total = primary_count + secondary_count
|
|
||||||
settings['demo_total_streams'] = total
|
|
||||||
demo_rate = int(conf.auracast_sampling_rate_hz or 0)
|
|
||||||
if demo_rate > 0:
|
|
||||||
if demo_rate in (48000, 24000, 16000):
|
|
||||||
settings['demo_stream_type'] = f"{total} × {demo_rate//1000}kHz"
|
|
||||||
else:
|
|
||||||
settings['demo_stream_type'] = f"{total} × {demo_rate}Hz"
|
|
||||||
settings['timestamp'] = datetime.utcnow().isoformat()
|
|
||||||
save_stream_settings(settings)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error("Exception in /init2: %s", traceback.format_exc())
|
log.error("Exception in init_radio: %s", traceback.format_exc())
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
@app.post("/init")
|
||||||
|
async def initialize(conf: auracast_config.AuracastConfigGroup):
|
||||||
|
"""Initializes the primary broadcaster on the streamer thread."""
|
||||||
|
async with _stream_lock:
|
||||||
|
global multicaster1, global_config_group
|
||||||
|
mc, persisted = await init_radio(TRANSPORT1, conf, multicaster1)
|
||||||
|
multicaster1 = mc
|
||||||
|
global_config_group = conf
|
||||||
|
save_settings(persisted, secondary=False)
|
||||||
|
|
||||||
|
@app.post("/init2")
|
||||||
|
async def initialize2(conf: auracast_config.AuracastConfigGroup):
|
||||||
|
"""Initializes the secondary broadcaster on the streamer thread."""
|
||||||
|
async with _stream_lock:
|
||||||
|
global multicaster2
|
||||||
|
mc, persisted = await init_radio(TRANSPORT2, conf, multicaster2)
|
||||||
|
multicaster2 = mc
|
||||||
|
save_settings(persisted, secondary=True)
|
||||||
|
|
||||||
@app.post("/stop_audio")
|
@app.post("/stop_audio")
|
||||||
async def stop_audio():
|
async def stop_audio():
|
||||||
"""Stops streaming on both multicaster1 and multicaster2 (worker thread)."""
|
"""Stops streaming on both multicaster1 and multicaster2 (worker thread)."""
|
||||||
@@ -407,97 +329,162 @@ async def get_status():
|
|||||||
return status
|
return status
|
||||||
|
|
||||||
async def _autostart_from_settings():
|
async def _autostart_from_settings():
|
||||||
"""Background task: auto-start last selected device-based input at server startup.
|
settings1 = load_stream_settings() or {}
|
||||||
|
settings2 = load_stream_settings2() or {}
|
||||||
|
|
||||||
Skips Webapp (webrtc) and Demo (file) modes. Polls every 2 seconds until the
|
async def do_primary():
|
||||||
saved device name appears in either USB or Network lists, then builds a config
|
global multicaster1, global_config_group
|
||||||
and initializes streaming.
|
settings = settings1
|
||||||
"""
|
audio_mode = settings.get('audio_mode')
|
||||||
settings = load_stream_settings() or {}
|
input_device_name = settings.get('input_device')
|
||||||
audio_mode = settings.get('audio_mode')
|
rate = settings.get('auracast_sampling_rate_hz')
|
||||||
input_device_name = settings.get('input_device')
|
octets = settings.get('octets_per_frame')
|
||||||
rate = settings.get('auracast_sampling_rate_hz')
|
pres_delay = settings.get('presentation_delay_us')
|
||||||
octets = settings.get('octets_per_frame')
|
saved_rtn = settings.get('rtn')
|
||||||
pres_delay = settings.get('presentation_delay_us')
|
immediate_rendering = settings.get('immediate_rendering', False)
|
||||||
saved_rtn = settings.get('rtn')
|
assisted_listening_stream = settings.get('assisted_listening_stream', False)
|
||||||
immediate_rendering = settings.get('immediate_rendering', False)
|
channel_names = settings.get('channel_names') or ["Broadcast0"]
|
||||||
assisted_listening_stream = settings.get('assisted_listening_stream', False)
|
program_info = settings.get('program_info') or channel_names
|
||||||
channel_names = settings.get('channel_names') or ["Broadcast0"]
|
languages = settings.get('languages') or ["deu"]
|
||||||
program_info = settings.get('program_info') or channel_names
|
stream_password = settings.get('stream_password')
|
||||||
languages = settings.get('languages') or ["deu"]
|
original_ts = settings.get('timestamp')
|
||||||
stream_password = settings.get('stream_password')
|
previously_streaming = bool(settings.get('is_streaming'))
|
||||||
original_ts = settings.get('timestamp')
|
|
||||||
previously_streaming = bool(settings.get('is_streaming'))
|
|
||||||
|
|
||||||
# Only auto-start if the previous state was streaming and it's a device-based input.
|
if not previously_streaming or not input_device_name or rate is None or octets is None:
|
||||||
if not previously_streaming:
|
return
|
||||||
return
|
|
||||||
if not input_device_name:
|
|
||||||
return
|
|
||||||
if rate is None or octets is None:
|
|
||||||
# Not enough info to reconstruct stream reliably
|
|
||||||
return
|
|
||||||
|
|
||||||
# Avoid duplicate start if already streaming
|
|
||||||
current = await _status_primary()
|
|
||||||
if current.get('is_streaming'):
|
|
||||||
return
|
|
||||||
|
|
||||||
while True:
|
|
||||||
# Do not interfere if user started a stream manually in the meantime
|
|
||||||
current = await _status_primary()
|
current = await _status_primary()
|
||||||
if current.get('is_streaming'):
|
if current.get('is_streaming'):
|
||||||
return
|
return
|
||||||
# Abort if saved settings changed to a different target while we were polling
|
while True:
|
||||||
current_settings = load_stream_settings() or {}
|
current = await _status_primary()
|
||||||
if current_settings.get('timestamp') != original_ts:
|
if current.get('is_streaming'):
|
||||||
# Settings were updated (likely by user via /init)
|
|
||||||
# If the target device or mode changed, stop autostart
|
|
||||||
if (
|
|
||||||
current_settings.get('input_device') != input_device_name or
|
|
||||||
current_settings.get('audio_mode') != audio_mode
|
|
||||||
):
|
|
||||||
return
|
return
|
||||||
# Check against the cached device lists
|
current_settings = load_stream_settings() or {}
|
||||||
usb = [d for _, d in get_alsa_usb_inputs()]
|
if current_settings.get('timestamp') != original_ts:
|
||||||
net = [d for _, d in get_network_pw_inputs()]
|
if (
|
||||||
names = {d.get('name') for d in usb} | {d.get('name') for d in net}
|
current_settings.get('input_device') != input_device_name or
|
||||||
if input_device_name in names:
|
current_settings.get('audio_mode') != audio_mode
|
||||||
# Build a minimal config based on saved fields
|
):
|
||||||
bigs = [
|
return
|
||||||
auracast_config.AuracastBigConfig(
|
usb = [d for _, d in get_alsa_usb_inputs()]
|
||||||
code=stream_password,
|
net = [d for _, d in get_network_pw_inputs()]
|
||||||
name=channel_names[0] if channel_names else "Broadcast0",
|
names = {d.get('name') for d in usb} | {d.get('name') for d in net}
|
||||||
program_info=program_info[0] if isinstance(program_info, list) and program_info else program_info,
|
if input_device_name in names:
|
||||||
language=languages[0] if languages else "deu",
|
bigs = [
|
||||||
audio_source=f"device:{input_device_name}",
|
auracast_config.AuracastBigConfig(
|
||||||
# input_format is intentionally omitted to use the default
|
code=stream_password,
|
||||||
iso_que_len=1,
|
name=channel_names[0] if channel_names else "Broadcast0",
|
||||||
sampling_frequency=rate,
|
program_info=program_info[0] if isinstance(program_info, list) and program_info else program_info,
|
||||||
|
language=languages[0] if languages else "deu",
|
||||||
|
audio_source=f"device:{input_device_name}",
|
||||||
|
iso_que_len=1,
|
||||||
|
sampling_frequency=rate,
|
||||||
|
octets_per_frame=octets,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
conf = auracast_config.AuracastConfigGroup(
|
||||||
|
auracast_sampling_rate_hz=rate,
|
||||||
octets_per_frame=octets,
|
octets_per_frame=octets,
|
||||||
|
transport=TRANSPORT1,
|
||||||
|
immediate_rendering=immediate_rendering,
|
||||||
|
assisted_listening_stream=assisted_listening_stream,
|
||||||
|
presentation_delay_us=pres_delay if pres_delay is not None else 40000,
|
||||||
|
bigs=bigs,
|
||||||
)
|
)
|
||||||
]
|
conf.qos_config = auracast_config.AuracastQoSConfig(
|
||||||
conf = auracast_config.AuracastConfigGroup(
|
iso_int_multiple_10ms=1,
|
||||||
auracast_sampling_rate_hz=rate,
|
number_of_retransmissions=int(saved_rtn),
|
||||||
octets_per_frame=octets,
|
max_transport_latency_ms=int(saved_rtn) * 10 + 3,
|
||||||
transport=TRANSPORT1,
|
)
|
||||||
immediate_rendering=immediate_rendering,
|
await asyncio.sleep(2)
|
||||||
assisted_listening_stream=assisted_listening_stream,
|
async with _stream_lock:
|
||||||
presentation_delay_us=pres_delay if pres_delay is not None else 40000,
|
mc, persisted = await init_radio(TRANSPORT1, conf, multicaster1)
|
||||||
bigs=bigs,
|
multicaster1 = mc
|
||||||
)
|
global_config_group = conf
|
||||||
# Attach QoS if saved_rtn present
|
save_settings(persisted, secondary=False)
|
||||||
conf.qos_config = auracast_config.AuracastQoSConfig(
|
return
|
||||||
iso_int_multiple_10ms=1,
|
|
||||||
number_of_retransmissions=int(saved_rtn),
|
|
||||||
max_transport_latency_ms=int(saved_rtn) * 10 + 3,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initialize and start
|
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
await initialize(conf)
|
|
||||||
|
async def do_secondary():
|
||||||
|
global multicaster2
|
||||||
|
settings = settings2
|
||||||
|
audio_mode = settings.get('audio_mode')
|
||||||
|
input_device_name = settings.get('input_device')
|
||||||
|
rate = settings.get('auracast_sampling_rate_hz')
|
||||||
|
octets = settings.get('octets_per_frame')
|
||||||
|
pres_delay = settings.get('presentation_delay_us')
|
||||||
|
saved_rtn = settings.get('rtn')
|
||||||
|
immediate_rendering = settings.get('immediate_rendering', False)
|
||||||
|
assisted_listening_stream = settings.get('assisted_listening_stream', False)
|
||||||
|
channel_names = settings.get('channel_names') or ["Broadcast0"]
|
||||||
|
program_info = settings.get('program_info') or channel_names
|
||||||
|
languages = settings.get('languages') or ["deu"]
|
||||||
|
stream_password = settings.get('stream_password')
|
||||||
|
original_ts = settings.get('timestamp')
|
||||||
|
previously_streaming = bool(settings.get('is_streaming'))
|
||||||
|
|
||||||
|
if not previously_streaming or not input_device_name or rate is None or octets is None:
|
||||||
return
|
return
|
||||||
await asyncio.sleep(2)
|
if multicaster2 is not None:
|
||||||
|
try:
|
||||||
|
if multicaster2.get_status().get('is_streaming'):
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
while True:
|
||||||
|
if multicaster2 is not None:
|
||||||
|
try:
|
||||||
|
if multicaster2.get_status().get('is_streaming'):
|
||||||
|
return
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
current_settings = load_stream_settings2() or {}
|
||||||
|
if current_settings.get('timestamp') != original_ts:
|
||||||
|
if (
|
||||||
|
current_settings.get('input_device') != input_device_name or
|
||||||
|
current_settings.get('audio_mode') != audio_mode
|
||||||
|
):
|
||||||
|
return
|
||||||
|
usb = [d for _, d in get_alsa_usb_inputs()]
|
||||||
|
net = [d for _, d in get_network_pw_inputs()]
|
||||||
|
names = {d.get('name') for d in usb} | {d.get('name') for d in net}
|
||||||
|
if input_device_name in names:
|
||||||
|
bigs = [
|
||||||
|
auracast_config.AuracastBigConfig(
|
||||||
|
code=stream_password,
|
||||||
|
name=channel_names[0] if channel_names else "Broadcast0",
|
||||||
|
program_info=program_info[0] if isinstance(program_info, list) and program_info else program_info,
|
||||||
|
language=languages[0] if languages else "deu",
|
||||||
|
audio_source=f"device:{input_device_name}",
|
||||||
|
iso_que_len=1,
|
||||||
|
sampling_frequency=rate,
|
||||||
|
octets_per_frame=octets,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
conf = auracast_config.AuracastConfigGroup(
|
||||||
|
auracast_sampling_rate_hz=rate,
|
||||||
|
octets_per_frame=octets,
|
||||||
|
transport=TRANSPORT2,
|
||||||
|
immediate_rendering=immediate_rendering,
|
||||||
|
assisted_listening_stream=assisted_listening_stream,
|
||||||
|
presentation_delay_us=pres_delay if pres_delay is not None else 40000,
|
||||||
|
bigs=bigs,
|
||||||
|
)
|
||||||
|
conf.qos_config = auracast_config.AuracastQoSConfig(
|
||||||
|
iso_int_multiple_10ms=1,
|
||||||
|
number_of_retransmissions=int(saved_rtn),
|
||||||
|
max_transport_latency_ms=int(saved_rtn) * 10 + 3,
|
||||||
|
)
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
async with _stream_lock:
|
||||||
|
mc, persisted = await init_radio(TRANSPORT2, conf, multicaster2)
|
||||||
|
multicaster2 = mc
|
||||||
|
save_settings(persisted, secondary=True)
|
||||||
|
return
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
await do_primary()
|
||||||
|
await do_secondary()
|
||||||
|
|
||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
async def _startup_autostart_event():
|
async def _startup_autostart_event():
|
||||||
|
|||||||
Reference in New Issue
Block a user