#include "SystemOutputFramePool.h" #include namespace { SystemOutputFramePoolConfig NormalizeConfig(SystemOutputFramePoolConfig config) { if (config.rowBytes == 0) config.rowBytes = VideoIORowBytes(config.pixelFormat, config.width); return config; } } SystemOutputFramePool::SystemOutputFramePool(const SystemOutputFramePoolConfig& config) { Configure(config); } void SystemOutputFramePool::Configure(const SystemOutputFramePoolConfig& config) { std::lock_guard lock(mMutex); mConfig = NormalizeConfig(config); mReadySlots.clear(); mSlots.clear(); mSlots.resize(mConfig.capacity); const std::size_t byteCount = FrameByteCount(); for (StoredSlot& slot : mSlots) { slot.bytes.resize(byteCount); slot.state = OutputFrameSlotState::Free; ++slot.generation; } mAcquireMissCount = 0; mReadyUnderrunCount = 0; } SystemOutputFramePoolConfig SystemOutputFramePool::Config() const { std::lock_guard lock(mMutex); return mConfig; } bool SystemOutputFramePool::AcquireFreeSlot(OutputFrameSlot& slot) { std::lock_guard lock(mMutex); for (std::size_t index = 0; index < mSlots.size(); ++index) { if (mSlots[index].state != OutputFrameSlotState::Free) continue; mSlots[index].state = OutputFrameSlotState::Rendering; ++mSlots[index].generation; FillOutputSlotLocked(index, slot); return true; } slot = OutputFrameSlot(); ++mAcquireMissCount; return false; } bool SystemOutputFramePool::AcquireRenderingSlot(OutputFrameSlot& slot) { return AcquireFreeSlot(slot); } bool SystemOutputFramePool::PublishReadySlot(const OutputFrameSlot& slot) { std::lock_guard lock(mMutex); if (!TransitionSlotLocked(slot, OutputFrameSlotState::Rendering, OutputFrameSlotState::Completed)) return false; mReadySlots.push_back(slot.index); return true; } bool SystemOutputFramePool::PublishCompletedSlot(const OutputFrameSlot& slot) { return PublishReadySlot(slot); } bool SystemOutputFramePool::ConsumeReadySlot(OutputFrameSlot& slot) { std::lock_guard lock(mMutex); while (!mReadySlots.empty()) { const std::size_t index = mReadySlots.front(); mReadySlots.pop_front(); if (index >= mSlots.size() || mSlots[index].state != OutputFrameSlotState::Completed) continue; FillOutputSlotLocked(index, slot); return true; } slot = OutputFrameSlot(); ++mReadyUnderrunCount; return false; } bool SystemOutputFramePool::ConsumeCompletedSlot(OutputFrameSlot& slot) { return ConsumeReadySlot(slot); } bool SystemOutputFramePool::MarkScheduled(const OutputFrameSlot& slot) { std::lock_guard lock(mMutex); if (!IsValidSlotLocked(slot)) return false; if (mSlots[slot.index].state != OutputFrameSlotState::Completed) return false; RemoveReadyIndexLocked(slot.index); mSlots[slot.index].state = OutputFrameSlotState::Scheduled; return true; } bool SystemOutputFramePool::MarkScheduledByBuffer(void* bytes) { if (bytes == nullptr) return false; std::lock_guard lock(mMutex); for (std::size_t index = 0; index < mSlots.size(); ++index) { if (mSlots[index].bytes.empty() || mSlots[index].bytes.data() != bytes) continue; if (mSlots[index].state != OutputFrameSlotState::Completed) return false; RemoveReadyIndexLocked(index); mSlots[index].state = OutputFrameSlotState::Scheduled; return true; } return false; } bool SystemOutputFramePool::ReleaseSlot(const OutputFrameSlot& slot) { std::lock_guard lock(mMutex); if (!IsValidSlotLocked(slot) || mSlots[slot.index].state == OutputFrameSlotState::Free) return false; return ReleaseSlotByIndexLocked(slot.index); } bool SystemOutputFramePool::ReleaseScheduledSlot(const OutputFrameSlot& slot) { std::lock_guard lock(mMutex); return TransitionSlotLocked(slot, OutputFrameSlotState::Scheduled, OutputFrameSlotState::Free); } bool SystemOutputFramePool::ReleaseSlotByBuffer(void* bytes) { if (bytes == nullptr) return false; std::lock_guard lock(mMutex); for (std::size_t index = 0; index < mSlots.size(); ++index) { if (!mSlots[index].bytes.empty() && mSlots[index].bytes.data() == bytes) return ReleaseSlotByIndexLocked(index); } return false; } void SystemOutputFramePool::Clear() { std::lock_guard lock(mMutex); mReadySlots.clear(); for (StoredSlot& slot : mSlots) { slot.state = OutputFrameSlotState::Free; ++slot.generation; } } SystemOutputFramePoolMetrics SystemOutputFramePool::GetMetrics() const { std::lock_guard lock(mMutex); SystemOutputFramePoolMetrics metrics; metrics.capacity = mSlots.size(); metrics.readyCount = mReadySlots.size(); metrics.acquireMissCount = mAcquireMissCount; metrics.readyUnderrunCount = mReadyUnderrunCount; for (const StoredSlot& slot : mSlots) { switch (slot.state) { case OutputFrameSlotState::Free: ++metrics.freeCount; break; case OutputFrameSlotState::Rendering: ++metrics.renderingCount; ++metrics.acquiredCount; break; case OutputFrameSlotState::Completed: ++metrics.completedCount; break; case OutputFrameSlotState::Scheduled: ++metrics.scheduledCount; break; } } return metrics; } bool SystemOutputFramePool::IsValidSlotLocked(const OutputFrameSlot& slot) const { return slot.index < mSlots.size() && mSlots[slot.index].generation == slot.generation; } bool SystemOutputFramePool::TransitionSlotLocked(const OutputFrameSlot& slot, OutputFrameSlotState expectedState, OutputFrameSlotState nextState) { if (!IsValidSlotLocked(slot) || mSlots[slot.index].state != expectedState) return false; mSlots[slot.index].state = nextState; return true; } void SystemOutputFramePool::FillOutputSlotLocked(std::size_t index, OutputFrameSlot& slot) { StoredSlot& storedSlot = mSlots[index]; slot.index = index; slot.generation = storedSlot.generation; slot.frame.bytes = storedSlot.bytes.empty() ? nullptr : storedSlot.bytes.data(); slot.frame.rowBytes = static_cast(mConfig.rowBytes); slot.frame.width = mConfig.width; slot.frame.height = mConfig.height; slot.frame.pixelFormat = mConfig.pixelFormat; slot.frame.nativeFrame = nullptr; slot.frame.nativeBuffer = slot.frame.bytes; } void SystemOutputFramePool::RemoveReadyIndexLocked(std::size_t index) { mReadySlots.erase(std::remove(mReadySlots.begin(), mReadySlots.end(), index), mReadySlots.end()); } bool SystemOutputFramePool::ReleaseSlotByIndexLocked(std::size_t index) { if (index >= mSlots.size() || mSlots[index].state == OutputFrameSlotState::Free) return false; RemoveReadyIndexLocked(index); mSlots[index].state = OutputFrameSlotState::Free; return true; } std::size_t SystemOutputFramePool::FrameByteCount() const { return static_cast(mConfig.rowBytes) * static_cast(mConfig.height); }