From 52af56d956e4e725f62030b6c990683158de5733 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Thu, 22 Aug 2019 09:07:33 +0200 Subject: [PATCH] Added rdpsnd latency calculation. --- channels/rdpsnd/client/mac/rdpsnd_mac.m | 84 +++++++++++++++---------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/channels/rdpsnd/client/mac/rdpsnd_mac.m b/channels/rdpsnd/client/mac/rdpsnd_mac.m index 76f6517a0..bb54d734e 100644 --- a/channels/rdpsnd/client/mac/rdpsnd_mac.m +++ b/channels/rdpsnd/client/mac/rdpsnd_mac.m @@ -30,6 +30,7 @@ #include #include +#include #include @@ -50,6 +51,7 @@ struct rdpsnd_mac_plugin AVAudioEngine* engine; AVAudioPlayerNode* player; + UINT64 diff; }; typedef struct rdpsnd_mac_plugin rdpsndMacPlugin; @@ -124,12 +126,21 @@ static char* FormatError(OSStatus st) } } +static void rdpsnd_mac_release(rdpsndMacPlugin* mac) +{ + if (mac->player) + [mac->player release]; + mac->player = NULL; + + if (mac->engine) + [mac->engine release]; + mac->engine = NULL; +} + static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency) { OSStatus err; NSError* error; - AudioDeviceID outputDeviceID; - UInt32 propertySize; AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDefaultSystemOutputDevice, kAudioObjectPropertyScopeGlobal, @@ -142,37 +153,25 @@ static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* form if (!rdpsnd_mac_set_format(device, format, latency)) return FALSE; - propertySize = sizeof(outputDeviceID); - err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propertySize, &outputDeviceID); - if (err) - { - WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err)); - return FALSE; - } - mac->engine = [[AVAudioEngine alloc] init]; if (!mac->engine) return FALSE; - err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit, kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, 0, &outputDeviceID, sizeof(outputDeviceID)); - if (err) + mac->player = [[AVAudioPlayerNode alloc] init]; + if (!mac->player) { - [mac->engine release]; - WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err)); + rdpsnd_mac_release(mac); + WLog_ERR(TAG, "AVAudioPlayerNode::init() failed"); return FALSE; } - mac->player = [[AVAudioPlayerNode alloc] init]; [mac->engine attachNode: mac->player]; [mac->engine connect: mac->player to: mac->engine.mainMixerNode format: nil]; if (![mac->engine startAndReturnError: &error]) { - [mac->player release]; - [mac->engine release]; - + device->Close(device); WLog_ERR(TAG, "Failed to start audio player %s", [error UTF8String]); return FALSE; } @@ -185,21 +184,19 @@ static void rdpsnd_mac_close(rdpsndDevicePlugin* device) { rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device; + if (mac->isPlaying) + { + [mac->player stop]; + mac->isPlaying = FALSE; + } + if (mac->isOpen) { - mac->isOpen = FALSE; - - // TODO: Close - if (mac->isPlaying) - [mac->player stop]; [mac->engine stop]; - [mac->engine disconnectNodeInput: mac->player]; - [mac->engine detachNode: mac->player]; - - [mac->player release]; - [mac->engine release]; - mac->isPlaying = FALSE; + mac->isOpen = FALSE; } + + rdpsnd_mac_release(mac); } static void rdpsnd_mac_free(rdpsndDevicePlugin* device) @@ -252,6 +249,7 @@ static void rdpsnd_mac_start(rdpsndDevicePlugin* device) [mac->player play]; mac->isPlaying = TRUE; + mac->diff = 100; /* Initial latency, corrected after first sample is played. */ } } @@ -263,16 +261,28 @@ static UINT rdpsnd_mac_play(rdpsndDevicePlugin* device, const BYTE* data, size_t float * const * db; size_t pos, step, x; AVAudioFrameCount count; + UINT64 start = GetTickCount64(); if (!mac->isOpen) return 0; step = 2 * mac->format.nChannels; - rdpsnd_mac_start(device); count = size / step; format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:mac->format.nSamplesPerSec channels:mac->format.nChannels interleaved:NO]; + if (!format) + { + WLog_WARN(TAG, "AVAudioFormat::init() failed"); + return 0; + } buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity: count]; + if (!buffer) + { + [format release]; + WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed"); + return 0; + } + buffer.frameLength = buffer.frameCapacity; db = buffer.floatChannelData; @@ -287,9 +297,17 @@ static UINT rdpsnd_mac_play(rdpsndDevicePlugin* device, const BYTE* data, size_t } } - [mac->player scheduleBuffer: buffer completionHandler:nil]; + rdpsnd_mac_start(device); - return 10; /* TODO: Get real latencry in [ms] */ + [mac->player scheduleBuffer: buffer completionHandler:^{ + UINT64 stop = GetTickCount64(); + if (start > stop) + mac->diff = 0; + else + mac->diff = stop - start; + }]; + + return mac->diff > UINT_MAX ? UINT_MAX : mac->diff; } #ifdef BUILTIN_CHANNELS