diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /channels/audin/client/ios | |
parent | Initial commit. (diff) | |
download | freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip |
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'channels/audin/client/ios')
-rw-r--r-- | channels/audin/client/ios/CMakeLists.txt | 39 | ||||
-rw-r--r-- | channels/audin/client/ios/audin_ios.m | 335 |
2 files changed, 374 insertions, 0 deletions
diff --git a/channels/audin/client/ios/CMakeLists.txt b/channels/audin/client/ios/CMakeLists.txt new file mode 100644 index 0000000..e6ec3ad --- /dev/null +++ b/channels/audin/client/ios/CMakeLists.txt @@ -0,0 +1,39 @@ +# FreeRDP: A Remote Desktop Protocol Implementation + # FreeRDP cmake build script + # + # Copyright (c) 2015 Armin Novak <armin.novak@thincast.com> + # Copyright (c) 2015 Thincast Technologies GmbH + # + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + + define_channel_client_subsystem("audin" "ios" "") + FIND_LIBRARY(CORE_AUDIO CoreAudio) + FIND_LIBRARY(AVFOUNDATION AVFoundation) + FIND_LIBRARY(AUDIO_TOOL AudioToolbox) + + set(${MODULE_PREFIX}_SRCS + audin_ios.m +) + +set(${MODULE_PREFIX}_LIBS + winpr + freerdp + ${AVFOUNDATION} + ${CORE_AUDIO} + ${AUDIO_TOOL} +) + +include_directories(..) +include_directories(${MAC_INCLUDE_DIRS}) + +add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "") diff --git a/channels/audin/client/ios/audin_ios.m b/channels/audin/client/ios/audin_ios.m new file mode 100644 index 0000000..ae30aee --- /dev/null +++ b/channels/audin/client/ios/audin_ios.m @@ -0,0 +1,335 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Audio Input Redirection Virtual Channel - iOS implementation + * + * Copyright (c) 2015 Armin Novak <armin.novak@thincast.com> + * Copyright 2015 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <winpr/crt.h> +#include <winpr/synch.h> +#include <winpr/string.h> +#include <winpr/thread.h> +#include <winpr/debug.h> +#include <winpr/cmdline.h> + +#import <AVFoundation/AVFoundation.h> + +#define __COREFOUNDATION_CFPLUGINCOM__ 1 +#define IUNKNOWN_C_GUTS \ + void *_reserved; \ + void *QueryInterface; \ + void *AddRef; \ + void *Release + +#include <CoreAudio/CoreAudioTypes.h> +#include <AudioToolbox/AudioToolbox.h> +#include <AudioToolbox/AudioQueue.h> + +#include <freerdp/addin.h> +#include <freerdp/channels/rdpsnd.h> + +#include "audin_main.h" + +#define IOS_AUDIO_QUEUE_NUM_BUFFERS 100 + +typedef struct +{ + IAudinDevice iface; + + AUDIO_FORMAT format; + UINT32 FramesPerPacket; + int dev_unit; + + AudinReceive receive; + void *user_data; + + rdpContext *rdpcontext; + + bool isOpen; + AudioQueueRef audioQueue; + AudioStreamBasicDescription audioFormat; + AudioQueueBufferRef audioBuffers[IOS_AUDIO_QUEUE_NUM_BUFFERS]; +} AudinIosDevice; + +static AudioFormatID audin_ios_get_format(const AUDIO_FORMAT *format) +{ + switch (format->wFormatTag) + { + case WAVE_FORMAT_PCM: + return kAudioFormatLinearPCM; + + default: + return 0; + } +} + +static AudioFormatFlags audin_ios_get_flags_for_format(const AUDIO_FORMAT *format) +{ + switch (format->wFormatTag) + { + case WAVE_FORMAT_PCM: + return kAudioFormatFlagIsSignedInteger; + + default: + return 0; + } +} + +static BOOL audin_ios_format_supported(IAudinDevice *device, const AUDIO_FORMAT *format) +{ + AudinIosDevice *ios = (AudinIosDevice *)device; + AudioFormatID req_fmt = 0; + + if (device == NULL || format == NULL) + return FALSE; + + req_fmt = audin_ios_get_format(format); + + if (req_fmt == 0) + return FALSE; + + return TRUE; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT audin_ios_set_format(IAudinDevice *device, const AUDIO_FORMAT *format, + UINT32 FramesPerPacket) +{ + AudinIosDevice *ios = (AudinIosDevice *)device; + + if (device == NULL || format == NULL) + return ERROR_INVALID_PARAMETER; + + ios->FramesPerPacket = FramesPerPacket; + ios->format = *format; + WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]", + audio_format_get_tag_string(format->wFormatTag), format->nChannels, + format->nSamplesPerSec, format->wBitsPerSample); + ios->audioFormat.mBitsPerChannel = format->wBitsPerSample; + + if (format->wBitsPerSample == 0) + ios->audioFormat.mBitsPerChannel = 16; + + ios->audioFormat.mChannelsPerFrame = ios->format.nChannels; + ios->audioFormat.mFramesPerPacket = 1; + + ios->audioFormat.mBytesPerFrame = + ios->audioFormat.mChannelsPerFrame * (ios->audioFormat.mBitsPerChannel / 8); + ios->audioFormat.mBytesPerPacket = + ios->audioFormat.mBytesPerFrame * ios->audioFormat.mFramesPerPacket; + + ios->audioFormat.mFormatFlags = audin_ios_get_flags_for_format(format); + ios->audioFormat.mFormatID = audin_ios_get_format(format); + ios->audioFormat.mReserved = 0; + ios->audioFormat.mSampleRate = ios->format.nSamplesPerSec; + return CHANNEL_RC_OK; +} + +static void ios_audio_queue_input_cb(void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, + const AudioTimeStamp *inStartTime, UInt32 inNumPackets, + const AudioStreamPacketDescription *inPacketDesc) +{ + AudinIosDevice *ios = (AudinIosDevice *)aqData; + UINT error = CHANNEL_RC_OK; + const BYTE *buffer = inBuffer->mAudioData; + int buffer_size = inBuffer->mAudioDataByteSize; + (void)inAQ; + (void)inStartTime; + (void)inNumPackets; + (void)inPacketDesc; + + if (buffer_size > 0) + error = ios->receive(&ios->format, buffer, buffer_size, ios->user_data); + + AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL); + + if (error) + { + WLog_ERR(TAG, "ios->receive failed with error %" PRIu32 "", error); + SetLastError(ERROR_INTERNAL_ERROR); + } +} + +static UINT audin_ios_close(IAudinDevice *device) +{ + UINT errCode = CHANNEL_RC_OK; + char errString[1024]; + OSStatus devStat; + AudinIosDevice *ios = (AudinIosDevice *)device; + + if (device == NULL) + return ERROR_INVALID_PARAMETER; + + if (ios->isOpen) + { + devStat = AudioQueueStop(ios->audioQueue, true); + + if (devStat != 0) + { + errCode = GetLastError(); + WLog_ERR(TAG, "AudioQueueStop failed with %s [%" PRIu32 "]", + winpr_strerror(errCode, errString, sizeof(errString)), errCode); + } + + ios->isOpen = false; + } + + if (ios->audioQueue) + { + devStat = AudioQueueDispose(ios->audioQueue, true); + + if (devStat != 0) + { + errCode = GetLastError(); + WLog_ERR(TAG, "AudioQueueDispose failed with %s [%" PRIu32 "]", + winpr_strerror(errCode, errString, sizeof(errString)), errCode); + } + + ios->audioQueue = NULL; + } + + ios->receive = NULL; + ios->user_data = NULL; + return errCode; +} + +static UINT audin_ios_open(IAudinDevice *device, AudinReceive receive, void *user_data) +{ + AudinIosDevice *ios = (AudinIosDevice *)device; + DWORD errCode; + char errString[1024]; + OSStatus devStat; + + ios->receive = receive; + ios->user_data = user_data; + devStat = AudioQueueNewInput(&(ios->audioFormat), ios_audio_queue_input_cb, ios, NULL, + kCFRunLoopCommonModes, 0, &(ios->audioQueue)); + + if (devStat != 0) + { + errCode = GetLastError(); + WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%" PRIu32 "]", + winpr_strerror(errCode, errString, sizeof(errString)), errCode); + goto err_out; + } + + for (size_t index = 0; index < IOS_AUDIO_QUEUE_NUM_BUFFERS; index++) + { + devStat = AudioQueueAllocateBuffer(ios->audioQueue, + ios->FramesPerPacket * 2 * ios->format.nChannels, + &ios->audioBuffers[index]); + + if (devStat != 0) + { + errCode = GetLastError(); + WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%" PRIu32 "]", + winpr_strerror(errCode, errString, sizeof(errString)), errCode); + goto err_out; + } + + devStat = AudioQueueEnqueueBuffer(ios->audioQueue, ios->audioBuffers[index], 0, NULL); + + if (devStat != 0) + { + errCode = GetLastError(); + WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]", + winpr_strerror(errCode, errString, sizeof(errString)), errCode); + goto err_out; + } + } + + devStat = AudioQueueStart(ios->audioQueue, NULL); + + if (devStat != 0) + { + errCode = GetLastError(); + WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]", + winpr_strerror(errCode, errString, sizeof(errString)), errCode); + goto err_out; + } + + ios->isOpen = true; + return CHANNEL_RC_OK; +err_out: + audin_ios_close(device); + return CHANNEL_RC_INITIALIZATION_ERROR; +} + +static UINT audin_ios_free(IAudinDevice *device) +{ + AudinIosDevice *ios = (AudinIosDevice *)device; + int error; + + if (device == NULL) + return ERROR_INVALID_PARAMETER; + + if ((error = audin_ios_close(device))) + { + WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error); + } + + free(ios); + return CHANNEL_RC_OK; +} + +FREERDP_ENTRY_POINT( + UINT ios_freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)) +{ + DWORD errCode; + char errString[1024]; + const ADDIN_ARGV *args; + AudinIosDevice *ios; + UINT error; + ios = (AudinIosDevice *)calloc(1, sizeof(AudinIosDevice)); + + if (!ios) + { + errCode = GetLastError(); + WLog_ERR(TAG, "calloc failed with %s [%" PRIu32 "]", + winpr_strerror(errCode, errString, sizeof(errString)), errCode); + return CHANNEL_RC_NO_MEMORY; + } + + ios->iface.Open = audin_ios_open; + ios->iface.FormatSupported = audin_ios_format_supported; + ios->iface.SetFormat = audin_ios_set_format; + ios->iface.Close = audin_ios_close; + ios->iface.Free = audin_ios_free; + ios->rdpcontext = pEntryPoints->rdpcontext; + ios->dev_unit = -1; + args = pEntryPoints->args; + + if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice *)ios))) + { + WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error); + goto error_out; + } + + return CHANNEL_RC_OK; +error_out: + free(ios); + return error; +} |