[idd] audio: bridge broker packets over LGMP streams

Add optional playback and capture streams to the LGMP transport.
Negotiate ownership over the existing control path and keep audio
availability isolated from display transport readiness.

Route broker packets through public LGMP stream APIs without changing
the LGMP queue table or shared-memory mapping.
This commit is contained in:
Geoffrey McRae
2026-09-02 01:47:11 +10:00
parent 947f03ede6
commit 348f92d8b6
11 changed files with 2079 additions and 17 deletions

View File

@@ -25,6 +25,8 @@
#include <LGProtocol/KVMFR.h>
#include <LGProtocol/KVMFRRecovery.h>
#include <string.h>
static bool TranslateFrameScheduleFlags(
uint32_t source, uint32_t& destination)
{
@@ -66,6 +68,7 @@ static const FrameProfile FRAME_PROFILES[] =
CLGMPTransport::CLGMPTransport(const TransportInstance& config) :
m_config(config),
m_control(m_host),
m_audio(m_host, m_control),
m_frames(m_host, m_ivshmem),
m_input(m_host),
m_clipboard(m_host)
@@ -91,12 +94,16 @@ bool CLGMPTransport::Initialize()
if (!m_host.Initialize(m_ivshmem))
return false;
// Preserve the existing shared-memory layout by appending input and then
// clipboard after the frame/pointer queues and retained pointer state.
// Preserve the existing shared-memory layout by appending input,
// clipboard, and optional audio after the frame/pointer queues and
// retained pointer state.
if (!m_frames.Initialize() || !m_control.Initialize() ||
!m_input.Initialize() || !m_clipboard.Initialize())
return false;
if (!m_audio.Initialize())
DEBUG_WARN("LGMP audio transport is unavailable");
m_frames.SealMemoryLayout();
return true;
}
@@ -122,12 +129,6 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
recovery.active, admission.state, admission.error);
}
// Before the swap chain establishes the frame-buffer alignment, service
// only the protocol-independent recovery channel. This preserves the old
// transport startup boundary while keeping recovery available immediately.
if (!Atomic::Load(m_ready, std::memory_order_acquire))
return ProcessResult::OK;
const LGMP_STATUS processStatus = m_host.Process();
if (processStatus != LGMP_OK)
{
@@ -143,14 +144,16 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
return ProcessResult::FAILURE;
}
m_input.FlushStatus();
const bool ready = Atomic::Load(m_ready, std::memory_order_acquire);
const uint64_t now = CFrameScheduler::Nanotime();
// Take the frame subscriber snapshot before processing scheduling messages,
// then publish both updates together just as the original timer did.
const CLGMPFrameTransport::SubscriberSnapshot subscribers =
m_frames.SnapshotSubscribers();
CLGMPFrameTransport::SubscriberSnapshot subscribers;
if (ready)
subscribers = m_frames.SnapshotSubscribers();
m_audio.Process();
uint8_t data[LGMP_MSGS_SIZE];
size_t size;
@@ -225,12 +228,35 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
DEBUG_WARN("Ignoring invalid KVMFR frame schedule");
break;
}
case KVMFR_MESSAGE_AUDIO_CLAIM:
{
KVMFRAudioClaim claim = {};
const bool valid = size == sizeof(claim);
if (valid)
memcpy(&claim, data, sizeof(claim));
else
DEBUG_WARN("Ignoring invalid KVMFR audio claim");
// Do not let stream binding or a full response queue hold the
// shared pointer/control receive head.
m_control.AckData();
if (valid)
m_audio.HandleClaim(sourceClientID, claim);
continue;
}
}
m_control.AckData();
}
m_frames.FinalizeSubscribers(subscribers, now);
m_audio.Process();
if (ready)
{
m_input.FlushStatus();
m_frames.FinalizeSubscribers(subscribers, now);
}
if (m_control.HasNewSubscribers())
m_control.RequestReplay();
@@ -245,6 +271,7 @@ void CLGMPTransport::Stop()
m_hasActive = false;
m_clipboard.Stop();
m_input.Stop();
m_audio.TransportStop();
}
void CLGMPTransport::SyncRecovery()