mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
191 lines
5.4 KiB
C++
191 lines
5.4 KiB
C++
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
//// PARTICULAR PURPOSE.
|
|
////
|
|
//// Copyright (c) Microsoft Corporation. All rights reserved
|
|
#include "pch.h"
|
|
#include "JitterBuffer.h"
|
|
#include "StringUtils.h"
|
|
#include "BufferUtils.h"
|
|
|
|
#if TV_API
|
|
|
|
using namespace Microsoft::WRL;
|
|
using namespace Windows::Storage::Streams;
|
|
|
|
namespace Microsoft {
|
|
namespace Xbox {
|
|
namespace GameChat {
|
|
|
|
JitterBuffer::JitterBuffer(
|
|
_In_ UINT maxBytesInPeriod,
|
|
_In_ UINT maxPacketsInRingBuffer,
|
|
_In_ UINT lowestNeededPacketCount,
|
|
_In_ UINT packetsBeforeRelaxingNeeded,
|
|
_In_ std::shared_ptr<FactoryCache> factoryCache
|
|
) :
|
|
m_maxPacketsInRingBuffer(maxPacketsInRingBuffer),
|
|
m_maxBytesInPeriod(maxBytesInPeriod),
|
|
m_readIndex(0),
|
|
m_writeIndex(0),
|
|
m_currentPacketCount(0),
|
|
m_dynamicNeededPacketCount(lowestNeededPacketCount), // Just set to lowest and it'll automatically adjust
|
|
m_lowestNeededPacketCount(lowestNeededPacketCount),
|
|
m_packetsBeforeRelaxingNeeded(packetsBeforeRelaxingNeeded),
|
|
m_numberOfPacketsAboveNeededWhileDraining(0),
|
|
m_state(NeedPackets),
|
|
m_factoryCache(factoryCache)
|
|
{
|
|
CHAT_THROW_INVALIDARGUMENT_IF_NULL(factoryCache);
|
|
|
|
for ( UINT i=0; i < m_maxPacketsInRingBuffer; i++ )
|
|
{
|
|
m_ringBuffer.push_back( BufferUtils::FastBufferCreate( m_maxBytesInPeriod, m_factoryCache->GetBufferFactory() ) );
|
|
}
|
|
}
|
|
|
|
JitterBuffer::~JitterBuffer()
|
|
{
|
|
}
|
|
|
|
HRESULT
|
|
JitterBuffer::Push(
|
|
_In_reads_(sourceBufferLengthInBytes) BYTE* sourceBuffer,
|
|
_In_ UINT sourceBufferLengthInBytes
|
|
)
|
|
{
|
|
CHAT_THROW_E_POINTER_IF_NULL( sourceBuffer );
|
|
CHAT_THROW_INVALIDARGUMENT_IF( sourceBufferLengthInBytes > m_maxBytesInPeriod );
|
|
|
|
// Fail if the caller pushed more than m_maxPacketCount
|
|
if( m_currentPacketCount >= m_maxPacketsInRingBuffer )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_BUFFER_OVERFLOW );
|
|
}
|
|
|
|
IBuffer^ buffer = m_ringBuffer[ m_writeIndex ];
|
|
BYTE* bufferBytes = NULL;
|
|
BufferUtils::GetBufferBytes( buffer, &bufferBytes );
|
|
|
|
errno_t err = memcpy_s( bufferBytes, buffer->Capacity, sourceBuffer, sourceBufferLengthInBytes );
|
|
CHAT_THROW_HR_IF(err != 0, E_FAIL); // This should not happen
|
|
buffer->Length = sourceBufferLengthInBytes;
|
|
|
|
// Advance to the next empty buffer in the ring buffer and update the size.
|
|
m_currentPacketCount++;
|
|
m_writeIndex++;
|
|
m_writeIndex %= m_maxPacketsInRingBuffer;
|
|
|
|
// Heuristics for adjusting the dynamic needed packet count
|
|
if ( m_state == NeedPackets )
|
|
{
|
|
if ( m_currentPacketCount >= m_dynamicNeededPacketCount )
|
|
{
|
|
m_state = Draining;
|
|
}
|
|
else
|
|
{
|
|
// Stay in NeedPackets
|
|
}
|
|
}
|
|
else // if ( m_state == Draining )
|
|
{
|
|
m_numberOfPacketsAboveNeededWhileDraining++;
|
|
|
|
// After we get above m_packetsBeforeRelaxingNeeded, we can relax the needed packet
|
|
if ( m_numberOfPacketsAboveNeededWhileDraining > m_packetsBeforeRelaxingNeeded )
|
|
{
|
|
m_numberOfPacketsAboveNeededWhileDraining = 0;
|
|
if( m_dynamicNeededPacketCount > 0 )
|
|
{
|
|
m_dynamicNeededPacketCount--;
|
|
m_dynamicNeededPacketCount = max(m_dynamicNeededPacketCount, m_lowestNeededPacketCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
JitterBuffer::GetFront(
|
|
_Out_ Windows::Storage::Streams::IBuffer^* pBuffer
|
|
)
|
|
{
|
|
if( m_currentPacketCount == 0 ||
|
|
m_state == NeedPackets )
|
|
{
|
|
// Return failure if we're empty or we still haven't gotten above the needed packet count
|
|
*pBuffer = nullptr;
|
|
return HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS );
|
|
}
|
|
else // if ( m_state == Draining )
|
|
{
|
|
*pBuffer = m_ringBuffer[ m_readIndex ];
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
JitterBuffer::Pop()
|
|
{
|
|
if( m_currentPacketCount == 0 ||
|
|
m_state == NeedPackets )
|
|
{
|
|
// Return failure if we're empty or we still haven't gotten above the needed packet count
|
|
return HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS );
|
|
}
|
|
else // if ( m_state == Draining )
|
|
{
|
|
m_readIndex++;
|
|
m_readIndex %= m_maxPacketsInRingBuffer;
|
|
m_currentPacketCount--;
|
|
|
|
if ( m_currentPacketCount < m_lowestNeededPacketCount )
|
|
{
|
|
// When going below the needed packet count, reset the number of packets we have gotten
|
|
m_numberOfPacketsAboveNeededWhileDraining = 0;
|
|
}
|
|
|
|
if ( m_currentPacketCount == 0 )
|
|
{
|
|
// When we hit 0 packets, then we have fully drained and switch the state to NeedPackets
|
|
m_state = NeedPackets;
|
|
|
|
// We hit 0 packet, so increase the needed packet count for more safety but higher latency
|
|
m_dynamicNeededPacketCount++;
|
|
m_dynamicNeededPacketCount = min(m_dynamicNeededPacketCount, m_maxPacketsInRingBuffer);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
UINT
|
|
JitterBuffer::GetCurrentPacketCount()
|
|
{
|
|
return m_currentPacketCount;
|
|
}
|
|
|
|
UINT
|
|
JitterBuffer::GetLowestNeededPacketCount()
|
|
{
|
|
return m_lowestNeededPacketCount;
|
|
}
|
|
|
|
UINT
|
|
JitterBuffer::GetMaxPacketsInRingBuffer()
|
|
{
|
|
return m_maxPacketsInRingBuffer;
|
|
}
|
|
|
|
UINT JitterBuffer::GetDynamicNeededPacketCount()
|
|
{
|
|
return m_dynamicNeededPacketCount;
|
|
}
|
|
|
|
|
|
}}}
|
|
|
|
#endif |