[idd] network: retain and replay the latest frame

Keep one pending source frame before the first subscriber connects.
Replay completed encodes with fresh delivery timing and frame IDs.
This commit is contained in:
Geoffrey McRae
2026-10-01 20:32:35 +10:00
parent f44570f039
commit 8ca2edbcc6

View File

@@ -49,6 +49,7 @@
#include "CDebug.h" #include "CDebug.h"
#include "CSRWLock.h" #include "CSRWLock.h"
#include "capture/CFrameScheduler.h"
#include "d3d/CD3D12CommandQueue.h" #include "d3d/CD3D12CommandQueue.h"
#include <Windows.h> #include <Windows.h>
@@ -1388,6 +1389,7 @@ void FinalizeMain()
struct EncodedFrame struct EncodedFrame
{ {
uint64_t configEpoch = 0; uint64_t configEpoch = 0;
uint64_t sourceSerial = 0;
uint64_t frameID = 0; uint64_t frameID = 0;
uint64_t captureTimeNs = 0; uint64_t captureTimeNs = 0;
uint32_t deadlineMs = 0; uint32_t deadlineMs = 0;
@@ -2584,10 +2586,12 @@ void FinalizeMain()
if (result) if (result)
{ {
output.configEpoch = configEpoch; output.configEpoch = configEpoch;
output.frameID = frameID; output.sourceSerial = slot->frame.desc.frame.content ?
output.captureTimeNs = slot->captureTime; slot->frame.desc.frame.content->serial : 0;
output.deadlineMs = MAX_FRAME_LATENCY_MS; output.frameID = frameID;
output.captureTimeNs = slot->captureTime;
output.deadlineMs = MAX_FRAME_LATENCY_MS;
} }
ReleaseSlot(*slot); ReleaseSlot(*slot);
return result; return result;
@@ -2725,15 +2729,17 @@ struct CNetworkVideo::Impl
bool m_active = false; bool m_active = false;
bool m_pending = false; bool m_pending = false;
uint64_t m_configEpoch = 0; uint64_t m_configEpoch = 0;
uint64_t m_nextFrameID = 0; uint64_t m_latestSourceSerial = 0;
uint64_t m_nextPeerSerial = 0; uint64_t m_nextFrameID = 0;
uint64_t m_nextSendSerial = 0; uint64_t m_nextPeerSerial = 0;
uint64_t m_nextSendSerial = 0;
std::vector<PeerState> m_peers; std::vector<PeerState> m_peers;
std::deque<PendingFrame> m_frames; std::deque<PendingFrame> m_frames;
std::deque<SendItem> m_sends; std::deque<SendItem> m_sends;
size_t m_sendBytes = 0; std::shared_ptr<const EncodedFrame> m_latestFrame;
size_t m_sendBytes = 0;
PyroWaveD3D12Encoder m_encoder; PyroWaveD3D12Encoder m_encoder;
@@ -3100,8 +3106,10 @@ struct CNetworkVideo::Impl
void FailNL(uint32_t detail) void FailNL(uint32_t detail)
{ {
m_failed = true; m_failed = true;
m_latestSourceSerial = 0;
m_frames.clear(); m_frames.clear();
m_latestFrame.reset();
for (PeerState& peer : m_peers) for (PeerState& peer : m_peers)
if (peer.subscribed) if (peer.subscribed)
QueueStatusNL(peer, LG_NET_VIDEO_STATE_ERROR, detail); QueueStatusNL(peer, LG_NET_VIDEO_STATE_ERROR, detail);
@@ -3146,11 +3154,12 @@ struct CNetworkVideo::Impl
peer.statusSent = false; peer.statusSent = false;
} }
bool QueueScheduleNL(PeerState& peer, const EncodedFrame& frame) bool QueueScheduleNL(PeerState& peer, const EncodedFrame& frame,
uint64_t frameID, uint64_t presentationBaseNs)
{ {
const uint32_t deadlineMs = (std::min)( const uint32_t deadlineMs = (std::min)(
frame.deadlineMs, peer.subscription.maxFrameLatencyMs); frame.deadlineMs, peer.subscription.maxFrameLatencyMs);
const uint64_t presentationNs = frame.captureTimeNs + const uint64_t presentationNs = presentationBaseNs +
static_cast<uint64_t>(deadlineMs) * UINT64_C(1000000); static_cast<uint64_t>(deadlineMs) * UINT64_C(1000000);
LGNetVideoSchedule schedule = {}; LGNetVideoSchedule schedule = {};
@@ -3158,28 +3167,29 @@ struct CNetworkVideo::Impl
schedule.flags = LG_NET_VIDEO_SCHEDULE_PRESENT | schedule.flags = LG_NET_VIDEO_SCHEDULE_PRESENT |
LG_NET_VIDEO_SCHEDULE_DROP_IF_LATE; LG_NET_VIDEO_SCHEDULE_DROP_IF_LATE;
schedule.configEpoch = frame.configEpoch; schedule.configEpoch = frame.configEpoch;
schedule.frameID = frame.frameID; schedule.frameID = frameID;
schedule.captureTimestampNs = frame.captureTimeNs; schedule.captureTimestampNs = frame.captureTimeNs;
schedule.presentationTimestampNs = presentationNs; schedule.presentationTimestampNs = presentationNs;
schedule.deadlineTimestampNs = presentationNs; schedule.deadlineTimestampNs = presentationNs;
return QueueFixedNL<LGNetVideoSchedule, return QueueFixedNL<LGNetVideoSchedule,
lgNetVideoScheduleEncode>(peer.peer, lgNetVideoScheduleEncode>(peer.peer,
NetworkVideoDelivery::RELIABLE, LG_NET_VIDEO_MESSAGE_SCHEDULE, NetworkVideoDelivery::RELIABLE, LG_NET_VIDEO_MESSAGE_SCHEDULE,
LG_NET_ENVELOPE_FINAL, frame.configEpoch, 0, frame.frameID, LG_NET_ENVELOPE_FINAL, frame.configEpoch, 0, frameID,
schedule, LG_NET_VIDEO_SCHEDULE_WIRE_SIZE); schedule, LG_NET_VIDEO_SCHEDULE_WIRE_SIZE);
} }
static std::shared_ptr<const std::vector<uint8_t>> BuildWholeFramePayload( static std::shared_ptr<const std::vector<uint8_t>> BuildWholeFramePayload(
const EncodedFrame& encoded, uint32_t deadlineMs) const EncodedFrame& encoded, uint32_t deadlineMs,
uint64_t frameID, uint64_t presentationBaseNs)
{ {
LGNetVideoFrame frame = {}; LGNetVideoFrame frame = {};
frame.streamID = STREAM_ID; frame.streamID = STREAM_ID;
frame.codec = LG_NET_VIDEO_CODEC_PYROWAVE; frame.codec = LG_NET_VIDEO_CODEC_PYROWAVE;
frame.flags = LG_NET_VIDEO_FRAME_KEYFRAME; frame.flags = LG_NET_VIDEO_FRAME_KEYFRAME;
frame.configEpoch = encoded.configEpoch; frame.configEpoch = encoded.configEpoch;
frame.frameID = encoded.frameID; frame.frameID = frameID;
frame.captureTimestampNs = encoded.captureTimeNs; frame.captureTimestampNs = encoded.captureTimeNs;
frame.presentationTimestampNs = encoded.captureTimeNs + frame.presentationTimestampNs = presentationBaseNs +
static_cast<uint64_t>(deadlineMs) * UINT64_C(1000000); static_cast<uint64_t>(deadlineMs) * UINT64_C(1000000);
frame.encodedLength = static_cast<uint32_t>(encoded.data.size()); frame.encodedLength = static_cast<uint32_t>(encoded.data.size());
frame.blockCount = static_cast<uint32_t>(encoded.blocks.size()); frame.blockCount = static_cast<uint32_t>(encoded.blocks.size());
@@ -3207,18 +3217,22 @@ struct CNetworkVideo::Impl
} }
bool QueueWholeFrameNL(PeerState& peer, const EncodedFrame& encoded, bool QueueWholeFrameNL(PeerState& peer, const EncodedFrame& encoded,
NetworkVideoDelivery delivery) NetworkVideoDelivery delivery, uint64_t frameID,
uint64_t presentationBaseNs)
{ {
const uint32_t deadlineMs = (std::min)( const uint32_t deadlineMs = (std::min)(
encoded.deadlineMs, peer.subscription.maxFrameLatencyMs); encoded.deadlineMs, peer.subscription.maxFrameLatencyMs);
std::shared_ptr<const std::vector<uint8_t>> payload = std::shared_ptr<const std::vector<uint8_t>> payload =
deadlineMs == encoded.deadlineMs ? encoded.wholePayload : nullptr; deadlineMs == encoded.deadlineMs && frameID == encoded.frameID &&
presentationBaseNs == encoded.captureTimeNs ?
encoded.wholePayload : nullptr;
if (!payload) if (!payload)
payload = BuildWholeFramePayload(encoded, deadlineMs); payload = BuildWholeFramePayload(
encoded, deadlineMs, frameID, presentationBaseNs);
if (!payload) if (!payload)
return false; return false;
return QueueNL(peer.peer, delivery, LG_NET_VIDEO_MESSAGE_FRAME, return QueueNL(peer.peer, delivery, LG_NET_VIDEO_MESSAGE_FRAME,
LG_NET_ENVELOPE_FINAL, encoded.configEpoch, 0, encoded.frameID, LG_NET_ENVELOPE_FINAL, encoded.configEpoch, 0, frameID,
std::move(payload)); std::move(payload));
} }
@@ -3277,7 +3291,7 @@ struct CNetworkVideo::Impl
bool QueueFragmentsNL(PeerState& peer, bool QueueFragmentsNL(PeerState& peer,
std::shared_ptr<const EncodedFrame> encoded, std::shared_ptr<const EncodedFrame> encoded,
NetworkVideoDelivery delivery, uint32_t maxPayload, NetworkVideoDelivery delivery, uint64_t frameID, uint32_t maxPayload,
uint32_t firstBlockIndex = 0, uint32_t firstBlockIndex = 0,
uint32_t firstFragmentIndex = 0) uint32_t firstFragmentIndex = 0)
{ {
@@ -3307,7 +3321,7 @@ struct CNetworkVideo::Impl
item.messageType = LG_NET_VIDEO_MESSAGE_FRAME_FRAGMENT; item.messageType = LG_NET_VIDEO_MESSAGE_FRAME_FRAGMENT;
item.flags = LG_NET_ENVELOPE_FINAL; item.flags = LG_NET_ENVELOPE_FINAL;
item.componentEpoch = encoded->configEpoch; item.componentEpoch = encoded->configEpoch;
item.frameID = encoded->frameID; item.frameID = frameID;
item.encoded = std::move(encoded); item.encoded = std::move(encoded);
item.fragmentIndex = firstFragmentIndex; item.fragmentIndex = firstFragmentIndex;
item.fragmentCount = fragmentCount; item.fragmentCount = fragmentCount;
@@ -3327,8 +3341,8 @@ struct CNetworkVideo::Impl
bool QueueReliableFirstBlockNL(PeerState& peer, bool QueueReliableFirstBlockNL(PeerState& peer,
const std::shared_ptr<const EncodedFrame>& encoded, const std::shared_ptr<const EncodedFrame>& encoded,
uint32_t payloadLimit, uint32_t criticalFragmentCount, uint64_t frameID, uint32_t payloadLimit,
uint32_t fragmentCount) uint32_t criticalFragmentCount, uint32_t fragmentCount)
{ {
if (!encoded || encoded->blocks.empty() || !payloadLimit || if (!encoded || encoded->blocks.empty() || !payloadLimit ||
!criticalFragmentCount || fragmentCount < criticalFragmentCount || !criticalFragmentCount || fragmentCount < criticalFragmentCount ||
@@ -3351,7 +3365,7 @@ struct CNetworkVideo::Impl
item.messageType = LG_NET_VIDEO_MESSAGE_FRAME_FRAGMENT; item.messageType = LG_NET_VIDEO_MESSAGE_FRAME_FRAGMENT;
item.flags = LG_NET_ENVELOPE_FINAL; item.flags = LG_NET_ENVELOPE_FINAL;
item.componentEpoch = encoded->configEpoch; item.componentEpoch = encoded->configEpoch;
item.frameID = encoded->frameID; item.frameID = frameID;
item.encoded = encoded; item.encoded = encoded;
item.fragmentIndex = fragmentIndex; item.fragmentIndex = fragmentIndex;
item.fragmentCount = fragmentCount; item.fragmentCount = fragmentCount;
@@ -3371,7 +3385,7 @@ struct CNetworkVideo::Impl
payload->size() > peer.info.maxReliablePayload || payload->size() > peer.info.maxReliablePayload ||
!QueueNL(peer.peer, NetworkVideoDelivery::RELIABLE, !QueueNL(peer.peer, NetworkVideoDelivery::RELIABLE,
LG_NET_VIDEO_MESSAGE_FRAME_FRAGMENT, LG_NET_ENVELOPE_FINAL, LG_NET_VIDEO_MESSAGE_FRAME_FRAGMENT, LG_NET_ENVELOPE_FINAL,
encoded->configEpoch, 0, encoded->frameID, std::move(payload))) encoded->configEpoch, 0, frameID, std::move(payload)))
return false; return false;
blockOffset += (std::min)(payloadLimit, block.length - blockOffset); blockOffset += (std::min)(payloadLimit, block.length - blockOffset);
} }
@@ -3395,7 +3409,7 @@ struct CNetworkVideo::Impl
LGNetVideoFragment fragment = {}; LGNetVideoFragment fragment = {};
fragment.streamID = STREAM_ID; fragment.streamID = STREAM_ID;
fragment.configEpoch = item.encoded->configEpoch; fragment.configEpoch = item.encoded->configEpoch;
fragment.frameID = item.encoded->frameID; fragment.frameID = item.frameID;
fragment.frameOffset = block.offset + item.blockOffset; fragment.frameOffset = block.offset + item.blockOffset;
fragment.frameLength = fragment.frameLength =
static_cast<uint32_t>(item.encoded->data.size()); static_cast<uint32_t>(item.encoded->data.size());
@@ -3450,9 +3464,10 @@ struct CNetworkVideo::Impl
} }
bool QueueFrameNL(PeerState& peer, bool QueueFrameNL(PeerState& peer,
const std::shared_ptr<const EncodedFrame>& frame) const std::shared_ptr<const EncodedFrame>& frame,
uint64_t frameID, uint64_t presentationBaseNs)
{ {
if (!frame || !peer.configured || if (!frame || !frameID || !presentationBaseNs || !peer.configured ||
peer.componentEpoch != frame->configEpoch || peer.componentEpoch != frame->configEpoch ||
frame->data.empty() || frame->data.size() > frame->data.empty() || frame->data.size() >
peer.subscription.maxFrameLength) peer.subscription.maxFrameLength)
@@ -3480,10 +3495,11 @@ struct CNetworkVideo::Impl
criticalPayloadLimit, criticalFragmentCount) && criticalPayloadLimit, criticalFragmentCount) &&
FragmentPlan(*frame, DeliveryPayload(peer.info, delivery), 1U, FragmentPlan(*frame, DeliveryPayload(peer.info, delivery), 1U,
criticalFragmentCount, payloadLimit, fragmentCount); criticalFragmentCount, payloadLimit, fragmentCount);
result = plan && QueueScheduleNL(peer, *frame) && result = plan && QueueScheduleNL(
QueueReliableFirstBlockNL(peer, frame, criticalPayloadLimit, peer, *frame, frameID, presentationBaseNs) &&
criticalFragmentCount, fragmentCount) && QueueReliableFirstBlockNL(peer, frame, frameID,
QueueFragmentsNL(peer, frame, delivery, criticalPayloadLimit, criticalFragmentCount, fragmentCount) &&
QueueFragmentsNL(peer, frame, delivery, frameID,
payloadLimit + LG_NET_VIDEO_FRAGMENT_HEADER_WIRE_SIZE, 1U, payloadLimit + LG_NET_VIDEO_FRAGMENT_HEADER_WIRE_SIZE, 1U,
criticalFragmentCount); criticalFragmentCount);
} }
@@ -3491,20 +3507,23 @@ struct CNetworkVideo::Impl
{ {
delivery = NetworkVideoDelivery::BULK; delivery = NetworkVideoDelivery::BULK;
result = fullSize <= peer.info.maxBulkPayload ? result = fullSize <= peer.info.maxBulkPayload ?
QueueWholeFrameNL(peer, *frame, delivery) : QueueWholeFrameNL(
QueueScheduleNL(peer, *frame) && peer, *frame, delivery, frameID, presentationBaseNs) :
QueueScheduleNL(peer, *frame, frameID, presentationBaseNs) &&
QueueFragmentsNL( QueueFragmentsNL(
peer, frame, delivery, DeliveryPayload(peer.info, delivery)); peer, frame, delivery, frameID,
DeliveryPayload(peer.info, delivery));
} }
else if (!datagrams && fullSize <= peer.info.maxReliablePayload) else if (!datagrams && fullSize <= peer.info.maxReliablePayload)
result = QueueWholeFrameNL( result = QueueWholeFrameNL(
peer, *frame, NetworkVideoDelivery::RELIABLE); peer, *frame, NetworkVideoDelivery::RELIABLE,
frameID, presentationBaseNs);
else else
{ {
const uint32_t maxPayload = DeliveryPayload(peer.info, delivery); const uint32_t maxPayload = DeliveryPayload(peer.info, delivery);
result = DeliverySupported(peer.info, delivery) && result = DeliverySupported(peer.info, delivery) &&
QueueScheduleNL(peer, *frame) && QueueScheduleNL(peer, *frame, frameID, presentationBaseNs) &&
QueueFragmentsNL(peer, frame, delivery, maxPayload); QueueFragmentsNL(peer, frame, delivery, frameID, maxPayload);
} }
if (!result) if (!result)
@@ -3514,12 +3533,40 @@ struct CNetworkVideo::Impl
peer.congested = true; peer.congested = true;
return false; return false;
} }
peer.lastFrameID = frame->frameID; peer.lastFrameID = frameID;
peer.lastCaptureTimeNs = frame->captureTimeNs; peer.lastCaptureTimeNs = frame->captureTimeNs;
peer.keyframePending = false; peer.keyframePending = false;
return true; return true;
} }
bool ReplayLatestFrameNL(PeerState& peer)
{
if (!m_latestFrame || !m_latestSourceSerial ||
m_latestFrame->configEpoch != m_configEpoch ||
m_latestFrame->sourceSerial != m_latestSourceSerial ||
m_latestFrame->data.size() > peer.subscription.maxFrameLength)
return false;
const uint64_t frameID = Next64(m_nextFrameID);
if (!QueueFrameNL(peer, m_latestFrame, frameID,
CFrameScheduler::Nanotime()))
return false;
m_nextFrameID = frameID;
QueueStatusNL(peer, LG_NET_VIDEO_STATE_STREAMING, 0);
return true;
}
bool RetryFrameReplays()
{
bool queued = false;
CSRWExclusiveLock lock(m_lock);
for (PeerState& peer : m_peers)
if (peer.subscribed && peer.configured && peer.keyframePending)
queued |= ReplayLatestFrameNL(peer);
return queued;
}
static bool PeerFrameDueNL( static bool PeerFrameDueNL(
const PeerState& peer, const EncodedFrame& frame) const PeerState& peer, const EncodedFrame& frame)
{ {
@@ -3566,7 +3613,8 @@ struct CNetworkVideo::Impl
} }
if (buildWholePayload) if (buildWholePayload)
{ {
frame.wholePayload = BuildWholeFramePayload(frame, frame.deadlineMs); frame.wholePayload = BuildWholeFramePayload(frame, frame.deadlineMs,
frame.frameID, frame.captureTimeNs);
if (!frame.wholePayload) if (!frame.wholePayload)
return; return;
} }
@@ -3585,11 +3633,22 @@ struct CNetworkVideo::Impl
if (!m_started || m_stopped || m_failed || !m_active || if (!m_started || m_stopped || m_failed || !m_active ||
encoded->configEpoch != m_configEpoch) encoded->configEpoch != m_configEpoch)
return; return;
uint64_t frameID = encoded->frameID;
if (frameID != m_nextFrameID)
{
frameID = Next64(m_nextFrameID);
m_nextFrameID = frameID;
}
// An older in-flight encode must not replace a newer retained source.
if (encoded->sourceSerial &&
encoded->sourceSerial == m_latestSourceSerial)
m_latestFrame = encoded;
for (PeerState& peer : m_peers) for (PeerState& peer : m_peers)
if (peer.subscribed && peer.configured && if (peer.subscribed && peer.configured &&
PeerFrameDueNL(peer, *encoded)) PeerFrameDueNL(peer, *encoded))
{ {
if (!QueueFrameNL(peer, encoded)) if (!QueueFrameNL(peer, encoded,
frameID, encoded->captureTimeNs))
QueueStatusNL(peer, LG_NET_VIDEO_STATE_STALLED, 2); QueueStatusNL(peer, LG_NET_VIDEO_STATE_STALLED, 2);
else else
QueueStatusNL(peer, LG_NET_VIDEO_STATE_STREAMING, 0); QueueStatusNL(peer, LG_NET_VIDEO_STATE_STREAMING, 0);
@@ -3604,13 +3663,8 @@ struct CNetworkVideo::Impl
{ {
CSRWExclusiveLock lock(m_lock); CSRWExclusiveLock lock(m_lock);
if (m_encoder.Busy() || m_frames.empty() || !m_started || if (m_encoder.Busy() || m_frames.empty() || !m_started ||
m_stopped || m_failed || !m_active || !HasSubscribersNL()) m_stopped || m_failed || !m_active)
return false; return false;
pending = std::move(m_frames.front());
m_frames.pop_front();
if (pending.configEpoch != m_configEpoch ||
!SameCfg(pending.cfg, m_activeCfg))
return true;
bool configured = false; bool configured = false;
for (const PeerState& peer : m_peers) for (const PeerState& peer : m_peers)
if (peer.subscribed && peer.configured) if (peer.subscribed && peer.configured)
@@ -3620,6 +3674,11 @@ struct CNetworkVideo::Impl
configured = true; configured = true;
} }
if (!configured) if (!configured)
return false;
pending = std::move(m_frames.front());
m_frames.pop_front();
if (pending.configEpoch != m_configEpoch ||
!SameCfg(pending.cfg, m_activeCfg))
return true; return true;
frameLimit = (std::min)( frameLimit = (std::min)(
frameLimit, FrameLimitNL(pending.lease->time.postStart)); frameLimit, FrameLimitNL(pending.lease->time.postStart));
@@ -3812,6 +3871,8 @@ struct CNetworkVideo::Impl
{ {
} }
Flush(); Flush();
if (RetryFrameReplays())
Flush();
} }
}; };
@@ -3847,10 +3908,13 @@ void CNetworkVideo::DetachSender()
CSRWExclusiveLock processLock(m_impl->m_processLock); CSRWExclusiveLock processLock(m_impl->m_processLock);
CSRWExclusiveLock callbackLock(m_impl->m_senderCallbackLock); CSRWExclusiveLock callbackLock(m_impl->m_senderCallbackLock);
CSRWExclusiveLock lock(m_impl->m_lock); CSRWExclusiveLock lock(m_impl->m_lock);
m_impl->m_sender = nullptr; m_impl->m_sender = nullptr;
m_impl->m_sendBytes = 0; m_impl->m_latestSourceSerial = 0;
m_impl->m_sendBytes = 0;
m_impl->m_frames.clear();
m_impl->m_sends.clear(); m_impl->m_sends.clear();
m_impl->m_peers.clear(); m_impl->m_peers.clear();
m_impl->m_latestFrame.reset();
} }
bool CNetworkVideo::Start(uint32_t videoBitrateMbps, bool CNetworkVideo::Start(uint32_t videoBitrateMbps,
@@ -3890,8 +3954,10 @@ void CNetworkVideo::Stop()
m_impl->m_changing = false; m_impl->m_changing = false;
m_impl->m_sendBytes = 0; m_impl->m_sendBytes = 0;
m_impl->m_lastBudgetCaptureTimeNs = 0; m_impl->m_lastBudgetCaptureTimeNs = 0;
m_impl->m_latestSourceSerial = 0;
m_impl->m_frames.clear(); m_impl->m_frames.clear();
m_impl->m_sends.clear(); m_impl->m_sends.clear();
m_impl->m_latestFrame.reset();
for (Impl::PeerState& peer : m_impl->m_peers) for (Impl::PeerState& peer : m_impl->m_peers)
{ {
peer.configured = false; peer.configured = false;
@@ -3973,10 +4039,7 @@ void CNetworkVideo::PeerDisconnected(const NetworkVideoPeer& key)
if (peer != m_impl->m_peers.end()) if (peer != m_impl->m_peers.end())
m_impl->m_peers.erase(peer); m_impl->m_peers.erase(peer);
if (!m_impl->HasSubscribersNL()) if (!m_impl->HasSubscribersNL())
{
m_impl->m_lastBudgetCaptureTimeNs = 0; m_impl->m_lastBudgetCaptureTimeNs = 0;
m_impl->m_frames.clear();
}
} }
NetworkVideoPacketResult CNetworkVideo::HandlePacket( NetworkVideoPacketResult CNetworkVideo::HandlePacket(
@@ -4035,6 +4098,7 @@ NetworkVideoPacketResult CNetworkVideo::HandlePacket(
if (!m_impl->QueueConfigNL( if (!m_impl->QueueConfigNL(
*peer, packet.envelope.requestID)) *peer, packet.envelope.requestID))
return NetworkVideoPacketResult::INVALID; return NetworkVideoPacketResult::INVALID;
m_impl->ReplayLatestFrameNL(*peer);
} }
else if (!m_impl->QueueStatusNL( else if (!m_impl->QueueStatusNL(
*peer, LG_NET_VIDEO_STATE_SUBSCRIBED, 0, *peer, LG_NET_VIDEO_STATE_SUBSCRIBED, 0,
@@ -4065,10 +4129,7 @@ NetworkVideoPacketResult CNetworkVideo::HandlePacket(
peer->componentEpoch = 0; peer->componentEpoch = 0;
peer->keyframePending = false; peer->keyframePending = false;
if (!m_impl->HasSubscribersNL()) if (!m_impl->HasSubscribersNL())
{
m_impl->m_lastBudgetCaptureTimeNs = 0; m_impl->m_lastBudgetCaptureTimeNs = 0;
m_impl->m_frames.clear();
}
break; break;
} }
@@ -4091,6 +4152,7 @@ NetworkVideoPacketResult CNetworkVideo::HandlePacket(
if (!peer->subscribed) if (!peer->subscribed)
return NetworkVideoPacketResult::INVALID; return NetworkVideoPacketResult::INVALID;
peer->keyframePending = true; peer->keyframePending = true;
m_impl->ReplayLatestFrameNL(*peer);
break; break;
} }
@@ -4117,7 +4179,10 @@ NetworkVideoPacketResult CNetworkVideo::HandlePacket(
feedback.flags & LG_NET_VIDEO_FEEDBACK_CONGESTED; feedback.flags & LG_NET_VIDEO_FEEDBACK_CONGESTED;
if (feedback.flags & (LG_NET_VIDEO_FEEDBACK_REQUEST_KEYFRAME | if (feedback.flags & (LG_NET_VIDEO_FEEDBACK_REQUEST_KEYFRAME |
LG_NET_VIDEO_FEEDBACK_DECODER_STALLED)) LG_NET_VIDEO_FEEDBACK_DECODER_STALLED))
{
peer->keyframePending = true; peer->keyframePending = true;
m_impl->ReplayLatestFrameNL(*peer);
}
break; break;
} }
@@ -4226,7 +4291,9 @@ void CNetworkVideo::Commit()
m_impl->m_changing = false; m_impl->m_changing = false;
m_impl->m_configEpoch = Next64(m_impl->m_configEpoch); m_impl->m_configEpoch = Next64(m_impl->m_configEpoch);
m_impl->m_lastBudgetCaptureTimeNs = 0; m_impl->m_lastBudgetCaptureTimeNs = 0;
m_impl->m_latestSourceSerial = 0;
m_impl->m_frames.clear(); m_impl->m_frames.clear();
m_impl->m_latestFrame.reset();
for (Impl::PeerState& peer : m_impl->m_peers) for (Impl::PeerState& peer : m_impl->m_peers)
if (peer.subscribed) if (peer.subscribed)
{ {
@@ -4261,13 +4328,13 @@ PushResult CNetworkVideo::Push(FrameIn frame, TexLease lease) noexcept
if (!lock) if (!lock)
return PushResult::BUSY; return PushResult::BUSY;
if (!m_impl->m_started || m_impl->m_stopped || m_impl->m_failed || if (!m_impl->m_started || m_impl->m_stopped || m_impl->m_failed ||
!m_impl->m_active || m_impl->m_changing || !m_impl->m_active || m_impl->m_changing)
!m_impl->HasSubscribersNL())
return PushResult::BUSY; return PushResult::BUSY;
const CFrameTex * texture = lease.Get(); const CFrameTex * texture = lease.Get();
const D3D12_RESOURCE_DESC desc = texture->Get()->GetDesc(); const D3D12_RESOURCE_DESC desc = texture->Get()->GetDesc();
if (frame.graph != texture->graph || frame.desc.node != texture->node || if (!frame.desc.frame.content || !frame.desc.frame.content->serial ||
frame.graph != texture->graph || frame.desc.node != texture->node ||
!Frame::Same(frame.desc.profile, m_impl->m_activeCfg.profile) || !Frame::Same(frame.desc.profile, m_impl->m_activeCfg.profile) ||
!Frame::Same(texture->profile, m_impl->m_activeCfg.profile) || !Frame::Same(texture->profile, m_impl->m_activeCfg.profile) ||
desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D || desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
@@ -4276,9 +4343,11 @@ PushResult CNetworkVideo::Push(FrameIn frame, TexLease lease) noexcept
desc.Format != ProfileFormat(m_impl->m_activeCfg.profile.pixel) || desc.Format != ProfileFormat(m_impl->m_activeCfg.profile.pixel) ||
texture->State() != D3D12_RESOURCE_STATE_COMMON) texture->State() != D3D12_RESOURCE_STATE_COMMON)
return PushResult::STALE; return PushResult::STALE;
const uint64_t sourceSerial = frame.desc.frame.content->serial;
try try
{ {
// Keep the newest source available until a subscriber can encode it.
if (m_impl->m_frames.size() >= MAX_PENDING_FRAMES) if (m_impl->m_frames.size() >= MAX_PENDING_FRAMES)
m_impl->m_frames.pop_front(); m_impl->m_frames.pop_front();
Impl::PendingFrame pending; Impl::PendingFrame pending;
@@ -4287,6 +4356,7 @@ PushResult CNetworkVideo::Push(FrameIn frame, TexLease lease) noexcept
pending.cfg = m_impl->m_activeCfg; pending.cfg = m_impl->m_activeCfg;
pending.configEpoch = m_impl->m_configEpoch; pending.configEpoch = m_impl->m_configEpoch;
m_impl->m_frames.push_back(std::move(pending)); m_impl->m_frames.push_back(std::move(pending));
m_impl->m_latestSourceSerial = sourceSerial;
} }
catch (...) catch (...)
{ {