22 #include <freerdp/config.h>
24 #include <winpr/wtypes.h>
26 #include <freerdp/types.h>
27 #include <freerdp/codec/dsp.h>
29 #import <AudioToolbox/AudioToolbox.h>
31 #include "rdpsnd_main.h"
32 #include "TPCircularBuffer.h"
34 #define INPUT_BUFFER_SIZE 32768
35 #define CIRCULAR_BUFFER_SIZE (INPUT_BUFFER_SIZE * 4)
39 rdpsndDevicePlugin device;
40 AudioComponentInstance audio_unit;
46 #define THIS(__ptr) ((rdpsndIOSPlugin*)__ptr)
48 static OSStatus rdpsnd_ios_render_cb(
void* inRefCon,
49 AudioUnitRenderActionFlags __unused* ioActionFlags,
50 const AudioTimeStamp __unused* inTimeStamp, UInt32 inBusNumber,
51 UInt32 __unused inNumberFrames, AudioBufferList* ioData)
58 rdpsndIOSPlugin* p = THIS(inRefCon);
60 for (
unsigned int i = 0; i < ioData->mNumberBuffers; i++)
62 AudioBuffer* target_buffer = &ioData->mBuffers[i];
63 int32_t available_bytes = 0;
64 const void* buffer = TPCircularBufferTail(&p->buffer, &available_bytes);
66 if (buffer != NULL && available_bytes > 0)
68 const int bytes_to_copy = MIN((int32_t)target_buffer->mDataByteSize, available_bytes);
69 memcpy(target_buffer->mData, buffer, bytes_to_copy);
70 target_buffer->mDataByteSize = bytes_to_copy;
71 TPCircularBufferConsume(&p->buffer, bytes_to_copy);
75 target_buffer->mDataByteSize = 0;
76 AudioOutputUnitStop(p->audio_unit);
84 static BOOL rdpsnd_ios_format_supported(rdpsndDevicePlugin* __unused device,
87 if (format->wFormatTag == WAVE_FORMAT_PCM)
95 static BOOL rdpsnd_ios_set_volume(rdpsndDevicePlugin* __unused device, UINT32 __unused value)
100 static void rdpsnd_ios_start(rdpsndDevicePlugin* device)
102 rdpsndIOSPlugin* p = THIS(device);
108 int32_t available_bytes = 0;
109 TPCircularBufferTail(&p->buffer, &available_bytes);
111 if (available_bytes > 0)
114 AudioOutputUnitStart(p->audio_unit);
119 static void rdpsnd_ios_stop(rdpsndDevicePlugin* __unused device)
121 rdpsndIOSPlugin* p = THIS(device);
127 AudioOutputUnitStop(p->audio_unit);
130 TPCircularBufferClear(&p->buffer);
134 static UINT rdpsnd_ios_play(rdpsndDevicePlugin* device,
const BYTE* data,
size_t size)
136 rdpsndIOSPlugin* p = THIS(device);
137 const BOOL ok = TPCircularBufferProduceBytes(&p->buffer, data, size);
142 rdpsnd_ios_start(device);
146 static BOOL rdpsnd_ios_open(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format,
147 UINT32 __unused latency)
149 rdpsndIOSPlugin* p = THIS(device);
155 AudioComponentDescription desc;
156 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
157 desc.componentType = kAudioUnitType_Output;
158 desc.componentSubType = kAudioUnitSubType_RemoteIO;
159 desc.componentFlags = 0;
160 desc.componentFlagsMask = 0;
161 AudioComponent audioComponent = AudioComponentFindNext(NULL, &desc);
163 if (audioComponent == NULL)
167 OSStatus status = AudioComponentInstanceNew(audioComponent, &p->audio_unit);
173 AudioStreamBasicDescription audioFormat = { 0 };
174 audioFormat.mSampleRate = format->nSamplesPerSec;
175 audioFormat.mFormatID = kAudioFormatLinearPCM;
176 audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
177 audioFormat.mFramesPerPacket = 1;
178 audioFormat.mChannelsPerFrame = format->nChannels;
179 audioFormat.mBitsPerChannel = format->wBitsPerSample;
180 audioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8;
181 audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
182 status = AudioUnitSetProperty(p->audio_unit, kAudioUnitProperty_StreamFormat,
183 kAudioUnitScope_Input, 0, &audioFormat,
sizeof(audioFormat));
187 AudioComponentInstanceDispose(p->audio_unit);
188 p->audio_unit = NULL;
193 AURenderCallbackStruct callbackStruct = { 0 };
194 callbackStruct.inputProc = rdpsnd_ios_render_cb;
195 callbackStruct.inputProcRefCon = p;
197 AudioUnitSetProperty(p->audio_unit, kAudioUnitProperty_SetRenderCallback,
198 kAudioUnitScope_Input, 0, &callbackStruct,
sizeof(callbackStruct));
202 AudioComponentInstanceDispose(p->audio_unit);
203 p->audio_unit = NULL;
208 status = AudioUnitInitialize(p->audio_unit);
212 AudioComponentInstanceDispose(p->audio_unit);
213 p->audio_unit = NULL;
218 const BOOL ok = TPCircularBufferInit(&p->buffer, CIRCULAR_BUFFER_SIZE);
222 AudioUnitUninitialize(p->audio_unit);
223 AudioComponentInstanceDispose(p->audio_unit);
224 p->audio_unit = NULL;
232 static void rdpsnd_ios_close(rdpsndDevicePlugin* device)
234 rdpsndIOSPlugin* p = THIS(device);
236 rdpsnd_ios_stop(device);
242 AudioUnitUninitialize(p->audio_unit);
243 AudioComponentInstanceDispose(p->audio_unit);
244 p->audio_unit = NULL;
247 TPCircularBufferCleanup(&p->buffer);
251 static void rdpsnd_ios_free(rdpsndDevicePlugin* device)
253 rdpsndIOSPlugin* p = THIS(device);
255 rdpsnd_ios_close(device);
265 FREERDP_ENTRY_POINT(UINT VCAPITYPE ios_freerdp_rdpsnd_client_subsystem_entry(
268 rdpsndIOSPlugin* p = (rdpsndIOSPlugin*)calloc(1,
sizeof(rdpsndIOSPlugin));
271 return CHANNEL_RC_NO_MEMORY;
273 p->device.Open = rdpsnd_ios_open;
274 p->device.FormatSupported = rdpsnd_ios_format_supported;
275 p->device.SetVolume = rdpsnd_ios_set_volume;
276 p->device.Play = rdpsnd_ios_play;
277 p->device.Start = rdpsnd_ios_start;
278 p->device.Close = rdpsnd_ios_close;
279 p->device.Free = rdpsnd_ios_free;
280 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)p);
281 return CHANNEL_RC_OK;