diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c index 510572cf2..8b11e1c44 100644 --- a/channels/rdpsnd/client/rdpsnd_main.c +++ b/channels/rdpsnd/client/rdpsnd_main.c @@ -86,6 +86,9 @@ struct rdpsnd_plugin BOOL isOpen; AUDIO_FORMAT* fixed_format; + UINT32 startPlayTime; + size_t totalPlaySize; + char* subsystem; char* device_name; @@ -370,6 +373,8 @@ static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo, rdpsnd->isOpen = TRUE; rdpsnd->wCurrentFormatNo = wFormatNo; + rdpsnd->startPlayTime = 0; + rdpsnd->totalPlaySize = 0; } return TRUE; @@ -437,6 +442,54 @@ static UINT rdpsnd_send_wave_confirm_pdu(rdpsndPlugin* rdpsnd, UINT16 wTimeStamp return rdpsnd_virtual_channel_write(rdpsnd, pdu); } +static BOOL rdpsnd_detect_overrun(rdpsndPlugin* rdpsnd, AUDIO_FORMAT* format, size_t size) +{ + UINT32 bpf; + UINT32 now; + UINT32 duration; + UINT32 totalDuration; + UINT32 remainingDuration; + UINT32 maxDuration; + + bpf = format->nChannels * format->wBitsPerSample / 8; + duration = (UINT32)(1000 * size / bpf / format->nSamplesPerSec); + totalDuration = (UINT32)(1000 * rdpsnd->totalPlaySize / bpf / format->nSamplesPerSec); + now = GetTickCountPrecise(); + if (rdpsnd->startPlayTime == 0) + { + rdpsnd->startPlayTime = now; + rdpsnd->totalPlaySize = size; + return FALSE; + } + else if (now - rdpsnd->startPlayTime > totalDuration + 10) + { + /* Buffer underrun */ + WLog_Print(rdpsnd->log, WLOG_DEBUG, "Buffer underrun by %u ms", + (UINT)(now - rdpsnd->startPlayTime - totalDuration)); + rdpsnd->startPlayTime = now; + rdpsnd->totalPlaySize = size; + return FALSE; + } + else + { + /* Calculate remaining duration to be played */ + remainingDuration = totalDuration - (now - rdpsnd->startPlayTime); + + /* Maximum allow duration calculation */ + maxDuration = duration * 2 + rdpsnd->latency; + + if (remainingDuration + duration > maxDuration) + { + WLog_Print(rdpsnd->log, WLOG_DEBUG, "Buffer overrun pending %u ms dropping %u ms", + remainingDuration, duration); + return TRUE; + } + + rdpsnd->totalPlaySize += size; + return FALSE; + } +} + static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size) { BYTE* data; @@ -454,7 +507,7 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size) "Wave: cBlockNo: %" PRIu8 " wTimeStamp: %" PRIu16 ", size: %" PRIdz, rdpsnd->cBlockNo, rdpsnd->wTimeStamp, size); - if (rdpsnd->device && rdpsnd->attached) + if (rdpsnd->device && rdpsnd->attached && !rdpsnd_detect_overrun(rdpsnd, format, size)) { UINT status = CHANNEL_RC_OK; wStream* pcmData = StreamPool_Take(rdpsnd->pool, 4096);