|
|
|
|
@@ -21,9 +21,9 @@
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include "CSRWLock.h"
|
|
|
|
|
#include "common/KVMFRAudio.h"
|
|
|
|
|
#include "transport/IAudioSource.h"
|
|
|
|
|
|
|
|
|
|
#include <LGProtocol/KVMFRAudio.h>
|
|
|
|
|
#include <LGProtocol/LGMPConfig.h>
|
|
|
|
|
|
|
|
|
|
#include <Windows.h>
|
|
|
|
|
@@ -33,6 +33,7 @@
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
#include "lgmp/host.h"
|
|
|
|
|
#include "lgmp/spmc.h"
|
|
|
|
|
#include "lgmp/stream.h"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -40,21 +41,40 @@ class CLGMPControl;
|
|
|
|
|
class CLGMPHost;
|
|
|
|
|
class CLGMPTransport;
|
|
|
|
|
|
|
|
|
|
class CLGMPAudioTransport final : public IAudioSource
|
|
|
|
|
class CLGMPAudio final : public IAudioSource
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
static constexpr unsigned RESPONSE_COUNT = LGMP_Q_POINTER_LEN;
|
|
|
|
|
static constexpr unsigned CLAIM_HISTORY_COUNT = 1024;
|
|
|
|
|
static constexpr uint32_t INVALID_SLOT = UINT32_MAX;
|
|
|
|
|
static constexpr unsigned INBOUND_DRAIN_MAX =
|
|
|
|
|
KVMFR_AUDIO_STREAM_SLOT_COUNT * 2U;
|
|
|
|
|
static constexpr uint64_t AUTHORITY_STALL_MS = 20U;
|
|
|
|
|
static constexpr uint64_t CAPTURE_REVOKE_ACK_MS =
|
|
|
|
|
KVMFR_AUDIO_OWNER_LEASE_MS;
|
|
|
|
|
|
|
|
|
|
enum class OwnerState
|
|
|
|
|
enum class SubscriberState
|
|
|
|
|
{
|
|
|
|
|
NONE,
|
|
|
|
|
BOUND,
|
|
|
|
|
RELEASING,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum class PumpStage
|
|
|
|
|
{
|
|
|
|
|
IDLE,
|
|
|
|
|
STATUS,
|
|
|
|
|
PLAYBACK_STOP,
|
|
|
|
|
CAPTURE_STOP,
|
|
|
|
|
PLAYBACK_START,
|
|
|
|
|
PLAYBACK_VOLUME,
|
|
|
|
|
PLAYBACK_MUTE,
|
|
|
|
|
CAPTURE_START,
|
|
|
|
|
CAPTURE_VOLUME,
|
|
|
|
|
CAPTURE_MUTE,
|
|
|
|
|
BARRIER,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct PendingResponse
|
|
|
|
|
{
|
|
|
|
|
bool valid = false;
|
|
|
|
|
@@ -85,6 +105,8 @@ private:
|
|
|
|
|
struct PendingCapture
|
|
|
|
|
{
|
|
|
|
|
bool valid = false;
|
|
|
|
|
uint32_t subscriber = INVALID_SLOT;
|
|
|
|
|
uint64_t subscriberSerial = 0;
|
|
|
|
|
LGMPStreamBuffer buffer = {};
|
|
|
|
|
AudioPacket packet = {};
|
|
|
|
|
const uint8_t * data = nullptr;
|
|
|
|
|
@@ -92,13 +114,65 @@ private:
|
|
|
|
|
uint64_t readySerial = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Subscriber
|
|
|
|
|
{
|
|
|
|
|
PLGMPHostStream hostControl = nullptr;
|
|
|
|
|
PLGMPHostStream clientControl = nullptr;
|
|
|
|
|
KVMFRStreamDescriptor hostControlDescriptor = {};
|
|
|
|
|
KVMFRStreamDescriptor clientControlDescriptor = {};
|
|
|
|
|
|
|
|
|
|
SubscriberState state = SubscriberState::NONE;
|
|
|
|
|
uint32_t clientID = 0;
|
|
|
|
|
uint32_t requestGeneration = 0;
|
|
|
|
|
uint32_t clientCapabilities = 0;
|
|
|
|
|
KVMFRAudioSubscriptionFlags subscriptions = 0;
|
|
|
|
|
KVMFRAudioCapabilities grantedCapabilities = 0;
|
|
|
|
|
uint32_t bindingEpoch = 0;
|
|
|
|
|
uint32_t roleEpoch = 0;
|
|
|
|
|
uint32_t playbackReaderID = 0;
|
|
|
|
|
uint32_t playbackReaderEpoch = 0;
|
|
|
|
|
uint32_t hostControlEpoch = 0;
|
|
|
|
|
uint32_t clientControlEpoch = 0;
|
|
|
|
|
uint32_t captureEpoch = 0;
|
|
|
|
|
uint64_t claimToken = 0;
|
|
|
|
|
uint64_t serial = 0;
|
|
|
|
|
uint64_t deadline = 0;
|
|
|
|
|
bool responseAdmitted = false;
|
|
|
|
|
bool subscriptionReceived = false;
|
|
|
|
|
bool ready = false;
|
|
|
|
|
bool awaitingAck = false;
|
|
|
|
|
bool forceUnbind = false;
|
|
|
|
|
bool feedbackRevoked = false;
|
|
|
|
|
bool captureRevoking = false;
|
|
|
|
|
uint64_t captureRevokeRevision = 0;
|
|
|
|
|
uint64_t captureRevokeDeadline = 0;
|
|
|
|
|
|
|
|
|
|
uint64_t hostSequence = 0;
|
|
|
|
|
uint64_t nextControlSequence = 0;
|
|
|
|
|
bool controlSequenceKnown = false;
|
|
|
|
|
uint64_t nextCaptureSequence = 0;
|
|
|
|
|
bool captureSequenceKnown = false;
|
|
|
|
|
bool captureDiscontinuity = false;
|
|
|
|
|
|
|
|
|
|
uint64_t announcedPlayback = 0;
|
|
|
|
|
uint64_t announcedCapture = 0;
|
|
|
|
|
uint64_t stateRevision = 0;
|
|
|
|
|
uint64_t pumpRevision = 0;
|
|
|
|
|
PumpStage pumpStage = PumpStage::IDLE;
|
|
|
|
|
KVMFRAudioStatus pendingStatus = {};
|
|
|
|
|
KVMFRAudioStateBarrier barrier = {};
|
|
|
|
|
uint64_t barrierGeneration = 0;
|
|
|
|
|
KVMFRAudioStreamResponse response = {};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CLGMPHost& m_host;
|
|
|
|
|
CLGMPControl& m_control;
|
|
|
|
|
|
|
|
|
|
PLGMPHostStream m_hostToClientStream = nullptr;
|
|
|
|
|
PLGMPHostStream m_clientToHostStream = nullptr;
|
|
|
|
|
KVMFRStreamDescriptor m_hostToClientDescriptor = {};
|
|
|
|
|
KVMFRStreamDescriptor m_clientToHostDescriptor = {};
|
|
|
|
|
PLGMPHostSPMC m_playbackStream = nullptr;
|
|
|
|
|
KVMFRSPMCDescriptor m_playbackDescriptor = {};
|
|
|
|
|
PLGMPHostStream m_captureStream = nullptr;
|
|
|
|
|
KVMFRStreamDescriptor m_captureDescriptor = {};
|
|
|
|
|
Subscriber m_subscribers[LGMP_MAX_CLIENTS];
|
|
|
|
|
ResponseMemory m_responseMemory[RESPONSE_COUNT];
|
|
|
|
|
PendingResponse m_pendingResponse[LGMP_MAX_CLIENTS];
|
|
|
|
|
ClaimRecord m_claimRecord[CLAIM_HISTORY_COUNT];
|
|
|
|
|
@@ -113,41 +187,29 @@ private:
|
|
|
|
|
bool m_inboundStopping = false;
|
|
|
|
|
IAudioTarget * m_target = nullptr;
|
|
|
|
|
bool m_initialized = false;
|
|
|
|
|
bool m_claimResponderReady = false;
|
|
|
|
|
bool m_started = false;
|
|
|
|
|
bool m_failed = false;
|
|
|
|
|
bool m_failureNotified = false;
|
|
|
|
|
|
|
|
|
|
OwnerState m_ownerState = OwnerState::NONE;
|
|
|
|
|
uint32_t m_ownerClientID = 0;
|
|
|
|
|
uint32_t m_ownerGeneration = 0;
|
|
|
|
|
uint32_t m_clientCapabilities = 0;
|
|
|
|
|
uint64_t m_ownerToken = 0;
|
|
|
|
|
uint64_t m_ownerSerial = 0;
|
|
|
|
|
uint64_t m_ownerDeadline = 0;
|
|
|
|
|
uint64_t m_releaseGeneration = 0;
|
|
|
|
|
bool m_responseAdmitted = false;
|
|
|
|
|
bool m_clientReady = false;
|
|
|
|
|
bool m_activationIssued = false;
|
|
|
|
|
bool m_deactivationIssued = false;
|
|
|
|
|
KVMFRAudioStreamResponse m_ownerResponse = {};
|
|
|
|
|
uint32_t m_feedbackAuthority = INVALID_SLOT;
|
|
|
|
|
uint32_t m_captureOwner = INVALID_SLOT;
|
|
|
|
|
uint32_t m_captureBinding = INVALID_SLOT;
|
|
|
|
|
uint32_t m_nextBindingEpoch = 0;
|
|
|
|
|
uint32_t m_nextRoleEpoch = 0;
|
|
|
|
|
uint64_t m_nextSubscriberSerial = 0;
|
|
|
|
|
bool m_activationCommanded = false;
|
|
|
|
|
|
|
|
|
|
AudioEndpointStatus m_state;
|
|
|
|
|
bool m_stateKnown = false;
|
|
|
|
|
bool m_statusDirty = false;
|
|
|
|
|
uint64_t m_announcedPlayback = 0;
|
|
|
|
|
uint64_t m_playbackRunGeneration = 0;
|
|
|
|
|
uint64_t m_announcedCapture = 0;
|
|
|
|
|
uint64_t m_hostSequence = 0;
|
|
|
|
|
uint64_t m_nextClientSequence = 0;
|
|
|
|
|
bool m_clientSequenceKnown = false;
|
|
|
|
|
bool m_captureDiscontinuity = false;
|
|
|
|
|
uint64_t m_previousPlaybackGeneration = 0;
|
|
|
|
|
bool m_captureCallbackInFlight = false;
|
|
|
|
|
bool m_playbackBlocked = false;
|
|
|
|
|
bool m_playbackReadyPending = false;
|
|
|
|
|
bool m_playbackCreditValid = false;
|
|
|
|
|
LGMPStreamBuffer m_playbackCredit = {};
|
|
|
|
|
AudioPacket m_blockedPlayback = {};
|
|
|
|
|
size_t m_blockedPlaybackSize = 0;
|
|
|
|
|
uint64_t m_authorityBlockedSince = 0;
|
|
|
|
|
uint64_t m_captureReadySerial = 0;
|
|
|
|
|
uint64_t m_inboundActivitySerial = 0;
|
|
|
|
|
PendingCapture m_pendingCapture;
|
|
|
|
|
@@ -158,9 +220,11 @@ private:
|
|
|
|
|
void StopInboundWorker();
|
|
|
|
|
void InboundThread();
|
|
|
|
|
static DWORD CALLBACK InboundThreadProc(void * context);
|
|
|
|
|
bool BindStreams(uint32_t clientID);
|
|
|
|
|
bool TryUnbindStreams();
|
|
|
|
|
void ForceUnbindStreams();
|
|
|
|
|
bool BindSubscriber(uint32_t index, uint32_t clientID,
|
|
|
|
|
const KVMFRAudioClaim& claim);
|
|
|
|
|
bool TryUnbindSubscriber(uint32_t index);
|
|
|
|
|
void ForceUnbindSubscriber(uint32_t index);
|
|
|
|
|
void ResetSubscriber(uint32_t index);
|
|
|
|
|
|
|
|
|
|
static bool SameFormat(const AudioFormat& left,
|
|
|
|
|
const AudioFormat& right);
|
|
|
|
|
@@ -171,6 +235,8 @@ private:
|
|
|
|
|
static KVMFRAudioStreamState WireState(AudioStreamState state);
|
|
|
|
|
static KVMFRStreamDescriptor WireDescriptor(
|
|
|
|
|
const LGMPStreamDescriptor& descriptor);
|
|
|
|
|
static KVMFRSPMCDescriptor WireDescriptor(
|
|
|
|
|
const LGMPSPMCDescriptor& descriptor);
|
|
|
|
|
|
|
|
|
|
bool ValidateClaim(const KVMFRAudioClaim& claim) const;
|
|
|
|
|
bool ResponsePending(uint32_t clientID,
|
|
|
|
|
@@ -180,58 +246,74 @@ private:
|
|
|
|
|
void CleanupResponseMemory();
|
|
|
|
|
void PumpResponses();
|
|
|
|
|
|
|
|
|
|
bool OwnerSubscribed() const;
|
|
|
|
|
void RenewLease();
|
|
|
|
|
void BeginRelease(const char * reason);
|
|
|
|
|
uint32_t FindSubscriber(uint32_t clientID) const;
|
|
|
|
|
uint32_t FindFreeSubscriber() const;
|
|
|
|
|
bool SubscriberPresent(uint32_t clientID,
|
|
|
|
|
const uint32_t clients[LGMP_MAX_CLIENTS], unsigned count) const;
|
|
|
|
|
void RenewLease(Subscriber& subscriber);
|
|
|
|
|
void BeginRelease(uint32_t index, const char * reason,
|
|
|
|
|
bool force = false);
|
|
|
|
|
void MaintainSubscribers(uint64_t now);
|
|
|
|
|
bool HasBoundSubscribers() const;
|
|
|
|
|
bool HasReadyPlayback() const;
|
|
|
|
|
bool PlaybackEligible(const Subscriber& subscriber) const;
|
|
|
|
|
bool FeedbackEligible(const Subscriber& subscriber) const;
|
|
|
|
|
bool CaptureEligible(const Subscriber& subscriber) const;
|
|
|
|
|
void RefreshGranted(uint32_t index, bool bumpRole);
|
|
|
|
|
void AssignFeedbackAuthority(uint32_t index);
|
|
|
|
|
void RevokeFeedbackAuthority(const char * reason);
|
|
|
|
|
void PromoteFeedbackAuthority(uint64_t playbackGeneration);
|
|
|
|
|
void AssignCaptureOwner();
|
|
|
|
|
void MaintainCaptureBinding();
|
|
|
|
|
void MarkStateDirty(uint32_t index);
|
|
|
|
|
void MarkAllStateDirty();
|
|
|
|
|
void ReleasePendingCapture();
|
|
|
|
|
void CancelPlaybackCredit();
|
|
|
|
|
void ClearBlockedPlayback(bool notify);
|
|
|
|
|
bool RememberBlockedPlayback(const AudioPacket& packet, size_t size);
|
|
|
|
|
void MaintainOwner(uint64_t now);
|
|
|
|
|
void ResetOwner();
|
|
|
|
|
|
|
|
|
|
KVMFRAudioStatus WireStatus() const;
|
|
|
|
|
bool CommitMessage(LGMPStreamBuffer& buffer,
|
|
|
|
|
KVMFRAudioStatus WireStatus(uint32_t index) const;
|
|
|
|
|
bool WriteControlMessage(uint32_t index,
|
|
|
|
|
KVMFRAudioMessageType type, uint64_t streamGeneration,
|
|
|
|
|
KVMFRAudioMessageFlags flags, uint64_t position, int64_t timeNs,
|
|
|
|
|
uint64_t rateQ32, uint32_t frames, const void * payload,
|
|
|
|
|
uint32_t length);
|
|
|
|
|
bool WriteMessage(KVMFRAudioMessageType type,
|
|
|
|
|
uint64_t streamGeneration, KVMFRAudioMessageFlags flags,
|
|
|
|
|
uint64_t position, int64_t timeNs, uint64_t rateQ32,
|
|
|
|
|
uint32_t frames, const void * payload, uint32_t length);
|
|
|
|
|
bool PlaybackCurrent(const KVMFRAudioStatus& status) const;
|
|
|
|
|
bool CaptureCurrent(const KVMFRAudioStatus& status) const;
|
|
|
|
|
void BeginStatePump(uint32_t index);
|
|
|
|
|
void PumpSubscriberState(uint32_t index);
|
|
|
|
|
void PumpState();
|
|
|
|
|
void CheckPlaybackCredit();
|
|
|
|
|
void CheckPlaybackBackpressure(uint64_t now);
|
|
|
|
|
|
|
|
|
|
bool ParseInbound(const LGMPStreamBuffer& buffer,
|
|
|
|
|
KVMFRAudioMessage& message) const;
|
|
|
|
|
bool PendingCaptureCurrent() const;
|
|
|
|
|
bool PendingCaptureCurrent(uint32_t index) const;
|
|
|
|
|
bool PrepareCapture(const KVMFRAudioMessage& message,
|
|
|
|
|
const LGMPStreamBuffer& buffer);
|
|
|
|
|
const LGMPStreamBuffer& buffer, uint32_t index);
|
|
|
|
|
void DrainControl(uint32_t index);
|
|
|
|
|
void DrainCapture();
|
|
|
|
|
void DrainInbound();
|
|
|
|
|
void ProcessActivation();
|
|
|
|
|
void NotifyPlaybackReady();
|
|
|
|
|
void NotifyFailure();
|
|
|
|
|
void Fail(const char * operation, LGMP_STATUS status);
|
|
|
|
|
|
|
|
|
|
void InitializeOwner(uint32_t clientID,
|
|
|
|
|
const KVMFRAudioClaim& claim);
|
|
|
|
|
void HandleClaim(uint32_t clientID,
|
|
|
|
|
const KVMFRAudioClaim& claim);
|
|
|
|
|
bool Initialize();
|
|
|
|
|
bool CanRespondToClaims();
|
|
|
|
|
void Process();
|
|
|
|
|
void TransportStop();
|
|
|
|
|
|
|
|
|
|
friend class CLGMPTransport;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
CLGMPAudioTransport(CLGMPHost& host, CLGMPControl& control) :
|
|
|
|
|
CLGMPAudio(CLGMPHost& host, CLGMPControl& control) :
|
|
|
|
|
m_host(host), m_control(control) {}
|
|
|
|
|
~CLGMPAudioTransport() override;
|
|
|
|
|
~CLGMPAudio() override;
|
|
|
|
|
|
|
|
|
|
CLGMPAudioTransport(const CLGMPAudioTransport&) = delete;
|
|
|
|
|
CLGMPAudioTransport& operator=(
|
|
|
|
|
const CLGMPAudioTransport&) = delete;
|
|
|
|
|
CLGMPAudio(const CLGMPAudio&) = delete;
|
|
|
|
|
CLGMPAudio& operator=(
|
|
|
|
|
const CLGMPAudio&) = delete;
|
|
|
|
|
|
|
|
|
|
bool Start(IAudioTarget& target) override;
|
|
|
|
|
void Stop() override;
|
|
|
|
|
|