Added mac implementation.
authorArmin Novak <armin.novak@thincast.com>
Fri, 13 Nov 2015 10:47:58 +0000 (11:47 +0100)
committerArmin Novak <armin.novak@thincast.com>
Fri, 13 Nov 2015 10:47:58 +0000 (11:47 +0100)
channels/audin/client/mac/CMakeLists.txt [new file with mode: 0644]
channels/audin/client/mac/audin_mac.c [new file with mode: 0644]

diff --git a/channels/audin/client/mac/CMakeLists.txt b/channels/audin/client/mac/CMakeLists.txt
new file mode 100644 (file)
index 0000000..feca0d6
--- /dev/null
@@ -0,0 +1,34 @@
+# 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" "mac" "")
+
+set(${MODULE_PREFIX}_SRCS
+       audin_mac.c)
+
+include_directories(..)
+include_directories(${MAC_INCLUDE_DIRS})
+
+add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
+
+set(${MODULE_PREFIX}_LIBS freerdp ${MAC_LIBRARIES})
+
+target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
+
+install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
+
diff --git a/channels/audin/client/mac/audin_mac.c b/channels/audin/client/mac/audin_mac.c
new file mode 100644 (file)
index 0000000..07b7894
--- /dev/null
@@ -0,0 +1,461 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Audio Input Redirection Virtual Channel - Mac OS X 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#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>
+
+#include <CoreAudio/CoreAudioTypes.h>
+#include <CoreAudio/CoreAudio.h>
+#include <AudioToolbox/AudioToolbox.h>
+#include <AudioToolbox/AudioQueue.h>
+
+#include <freerdp/addin.h>
+#include <freerdp/codec/dsp.h>
+#include <freerdp/channels/rdpsnd.h>
+
+#include "audin_main.h"
+
+#define MAC_AUDIO_QUEUE_NUM_BUFFERS     100
+#define MAC_AUDIO_QUEUE_BUFFER_SIZE     32768
+
+typedef struct _AudinMacDevice
+{
+    IAudinDevice iface;
+
+    FREERDP_DSP_CONTEXT* dsp_context;
+
+    audinFormat format;
+    UINT32 FramesPerPacket;
+    int dev_unit;
+
+    AudinReceive receive;
+    void* user_data;
+
+    rdpContext* rdpcontext;
+
+    bool isOpen;
+    AudioQueueRef audioQueue;
+    AudioStreamBasicDescription audioFormat;
+    AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
+} AudinMacDevice;
+
+static AudioFormatID audin_mac_get_format(const audinFormat* format)
+{
+    switch (format->wFormatTag)
+    {
+    case WAVE_FORMAT_PCM:
+        return kAudioFormatLinearPCM;
+    /*
+    case WAVE_FORMAT_GSM610:
+        return kAudioFormatMicrosoftGSM;
+    case WAVE_FORMAT_ALAW:
+        return kAudioFormatALaw;
+    case WAVE_FORMAT_MULAW:
+        return kAudioFormatULaw;
+    case WAVE_FORMAT_AAC_MS:
+        return kAudioFormatMPEG4AAC;
+    case WAVE_FORMAT_ADPCM:
+    case WAVE_FORMAT_DVI_ADPCM:
+        return kAudioFormatLinearPCM;
+        */
+    }
+
+    return 0;
+}
+
+static AudioFormatFlags audin_mac_get_flags_for_format(const audinFormat* format)
+{
+    switch(format->wFormatTag)
+    {
+    case WAVE_FORMAT_DVI_ADPCM:
+    case WAVE_FORMAT_ADPCM:
+    case WAVE_FORMAT_PCM:
+        return kAudioFormatFlagIsSignedInteger;
+    default:
+        return 0;
+    }
+}
+
+static BOOL audin_mac_format_supported(IAudinDevice* device, audinFormat* format)
+{
+    AudioFormatID req_fmt = 0;
+
+    if (device == NULL || format == NULL)
+        return FALSE;
+
+    req_fmt = audin_mac_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_mac_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
+{
+    AudinMacDevice* mac = (AudinMacDevice*)device;
+
+    if (device == NULL || format == NULL)
+        return ERROR_INVALID_PARAMETER;
+
+    mac->FramesPerPacket = FramesPerPacket;
+    CopyMemory(&(mac->format), format, sizeof(audinFormat));
+
+    WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
+              rdpsnd_get_audio_tag_string(format->wFormatTag),
+              format->nChannels, format->nSamplesPerSec, format->wBitsPerSample);
+
+    switch (format->wFormatTag)
+    {
+    case WAVE_FORMAT_ADPCM:
+    case WAVE_FORMAT_DVI_ADPCM:
+        mac->FramesPerPacket *= 4; /* Compression ratio. */
+        mac->format.wBitsPerSample *= 4;
+        break;
+    }
+
+    mac->audioFormat.mBitsPerChannel = mac->format.wBitsPerSample;
+    mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
+    mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
+    mac->audioFormat.mFormatID = audin_mac_get_format(format);
+    mac->audioFormat.mFramesPerPacket = 1;
+    mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
+    mac->audioFormat.mBytesPerFrame =
+            mac->audioFormat.mChannelsPerFrame *  mac->audioFormat.mBitsPerChannel / 8;
+    mac->audioFormat.mBytesPerPacket =
+            mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket;
+
+    return CHANNEL_RC_OK;
+}
+
+static void mac_audio_queue_input_cb(void *aqData,
+                                     AudioQueueRef inAQ,
+                                     AudioQueueBufferRef inBuffer,
+                                     const AudioTimeStamp *inStartTime,
+                                     UInt32 inNumPackets,
+                                     const AudioStreamPacketDescription *inPacketDesc)
+{
+    AudinMacDevice* mac = (AudinMacDevice*)aqData;
+    UINT error;
+    int encoded_size;
+    const BYTE *encoded_data;
+    BYTE *buffer = inBuffer->mAudioData;
+    int buffer_size = inBuffer->mAudioDataByteSize;
+
+    (void)inAQ;
+    (void)inStartTime;
+    (void)inNumPackets;
+    (void)inPacketDesc;
+
+
+       /* Process. */
+    switch (mac->format.wFormatTag) {
+    case WAVE_FORMAT_ADPCM:
+        if (!mac->dsp_context->encode_ms_adpcm(mac->dsp_context,
+                                               buffer, buffer_size, mac->format.nChannels, mac->format.nBlockAlign))
+        {
+            SetLastError(ERROR_INTERNAL_ERROR);
+            return;
+        }
+        encoded_data = mac->dsp_context->adpcm_buffer;
+        encoded_size = mac->dsp_context->adpcm_size;
+        break;
+    case WAVE_FORMAT_DVI_ADPCM:
+        if (!mac->dsp_context->encode_ima_adpcm(mac->dsp_context,
+                                                buffer, buffer_size, mac->format.nChannels, mac->format.nBlockAlign))
+        {
+            SetLastError(ERROR_INTERNAL_ERROR);
+            break;
+        }
+        encoded_data = mac->dsp_context->adpcm_buffer;
+        encoded_size = mac->dsp_context->adpcm_size;
+        break;
+    default:
+        encoded_data = buffer;
+        encoded_size = buffer_size;
+        break;
+    }
+
+    if ((error = mac->receive(encoded_data, encoded_size, mac->user_data)))
+    {
+        WLog_ERR(TAG, "mac->receive failed with error %lu", error);
+        SetLastError(ERROR_INTERNAL_ERROR);
+        return;
+    }
+
+}
+
+static UINT audin_mac_close(IAudinDevice *device)
+{
+    UINT errCode = CHANNEL_RC_OK;
+    char errString[1024];
+    OSStatus devStat;
+    AudinMacDevice *mac = (AudinMacDevice*)device;
+
+    if (device == NULL)
+        return ERROR_INVALID_PARAMETER;
+
+    if (mac->isOpen)
+    {
+        devStat = AudioQueueStop(mac->audioQueue, true);
+        if (devStat != 0)
+        {
+            errCode = GetLastError();
+            WLog_ERR(TAG, "AudioQueueStop failed with %s [%d]",
+                     winpr_strerror(errCode, errString, sizeof(errString)), errCode);
+        }
+        mac->isOpen = false;
+    }
+
+    if (mac->audioQueue)
+    {
+        devStat = AudioQueueDispose(mac->audioQueue, true);
+        if (devStat != 0)
+        {
+            errCode = GetLastError();
+            WLog_ERR(TAG, "AudioQueueDispose failed with %s [%d]",
+                     winpr_strerror(errCode, errString, sizeof(errString)), errCode);
+        }
+
+        mac->audioQueue = NULL;
+    }
+
+    mac->receive = NULL;
+    mac->user_data = NULL;
+
+    return errCode;
+}
+
+static UINT audin_mac_open(IAudinDevice *device, AudinReceive receive, void *user_data) {
+    AudinMacDevice *mac = (AudinMacDevice*)device;
+    DWORD errCode;
+    char errString[1024];
+    OSStatus devStat;
+    size_t index;
+
+    mac->receive = receive;
+    mac->user_data = user_data;
+
+    devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb,
+                                 mac, NULL, kCFRunLoopCommonModes, 0, &(mac->audioQueue));
+    if (devStat != 0)
+    {
+        errCode = GetLastError();
+        WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%d]",
+                 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
+        goto err_out;
+    }
+
+    for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
+    {
+        devStat = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE,
+                                           &mac->audioBuffers[index]);
+        if (devStat != 0)
+        {
+            errCode = GetLastError();
+            WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%d]",
+                     winpr_strerror(errCode, errString, sizeof(errString)), errCode);
+            goto err_out;
+        }
+
+        devStat = AudioQueueEnqueueBuffer(mac->audioQueue,
+                                          mac->audioBuffers[index],
+                                          0,
+                                          NULL);
+        if (devStat != 0)
+        {
+            errCode = GetLastError();
+            WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%d]",
+                     winpr_strerror(errCode, errString, sizeof(errString)), errCode);
+            goto err_out;
+        }
+    }
+
+    freerdp_dsp_context_reset_adpcm(mac->dsp_context);
+
+    devStat = AudioQueueStart(mac->audioQueue, NULL);
+    if (devStat != 0)
+    {
+        errCode = GetLastError();
+        WLog_ERR(TAG, "AudioQueueStart failed with %s [%d]",
+                 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
+        goto err_out;
+    }
+    mac->isOpen = true;
+
+    return CHANNEL_RC_OK;
+
+err_out:
+    audin_mac_close(device);
+    return CHANNEL_RC_INITIALIZATION_ERROR;
+}
+
+static UINT audin_mac_free(IAudinDevice* device)
+{
+    AudinMacDevice *mac = (AudinMacDevice*)device;
+
+    int error;
+
+    if (device == NULL)
+        return ERROR_INVALID_PARAMETER;
+
+    if ((error = audin_mac_close(device)))
+    {
+        WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
+    }
+    freerdp_dsp_context_free(mac->dsp_context);
+    free(mac);
+
+    return CHANNEL_RC_OK;
+}
+
+static COMMAND_LINE_ARGUMENT_A audin_mac_args[] =
+{
+    { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
+    { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
+};
+
+static UINT audin_mac_parse_addin_args(AudinMacDevice *device, ADDIN_ARGV *args)
+{
+    DWORD errCode;
+    char errString[1024];
+    int status;
+    char* str_num, *eptr;
+    DWORD flags;
+    COMMAND_LINE_ARGUMENT_A* arg;
+    AudinMacDevice* mac = (AudinMacDevice*)device;
+
+    if (args->argc == 1)
+        return CHANNEL_RC_OK;
+
+    flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
+    status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv, audin_mac_args, flags, mac, NULL, NULL);
+
+    if (status < 0)
+        return ERROR_INVALID_PARAMETER;
+
+    arg = audin_mac_args;
+
+    do
+    {
+        if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
+            continue;
+
+        CommandLineSwitchStart(arg)
+                CommandLineSwitchCase(arg, "dev")
+        {
+            str_num = _strdup(arg->Value);
+            if (!str_num)
+            {
+                errCode = GetLastError();
+                WLog_ERR(TAG, "_strdup failed with %s [%d]",
+                         winpr_strerror(errCode, errString, sizeof(errString)), errCode);
+                return CHANNEL_RC_NO_MEMORY;
+            }
+            mac->dev_unit = strtol(str_num, &eptr, 10);
+
+            if (mac->dev_unit < 0 || *eptr != '\0')
+                mac->dev_unit = -1;
+
+            free(str_num);
+        }
+        CommandLineSwitchEnd(arg)
+    }
+    while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
+
+    return CHANNEL_RC_OK;
+}
+
+#ifdef STATIC_CHANNELS
+#define freerdp_audin_client_subsystem_entry   mac_freerdp_audin_client_subsystem_entry
+#endif
+
+UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
+{
+    DWORD errCode;
+    char errString[1024];
+    ADDIN_ARGV *args;
+    AudinMacDevice *mac;
+    UINT error;
+
+    mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
+    if (!mac)
+    {
+        errCode = GetLastError();
+        WLog_ERR(TAG, "calloc failed with %s [%d]",
+                 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
+        return CHANNEL_RC_NO_MEMORY;
+    }
+
+    mac->iface.Open = audin_mac_open;
+    mac->iface.FormatSupported = audin_mac_format_supported;
+    mac->iface.SetFormat = audin_mac_set_format;
+    mac->iface.Close = audin_mac_close;
+    mac->iface.Free = audin_mac_free;
+    mac->rdpcontext = pEntryPoints->rdpcontext;
+
+    mac->dev_unit = -1;
+    args = pEntryPoints->args;
+
+    if ((error = audin_mac_parse_addin_args(mac, args)))
+    {
+        WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %lu!", error);
+        goto error_out;
+    }
+
+    mac->dsp_context = freerdp_dsp_context_new();
+    if (!mac->dsp_context)
+    {
+        WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
+        error = CHANNEL_RC_NO_MEMORY;
+        goto error_out;
+    }
+
+    if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) mac)))
+    {
+        WLog_ERR(TAG, "RegisterAudinDevice failed with error %lu!", error);
+        goto error_out;
+    }
+
+    return CHANNEL_RC_OK;
+
+error_out:
+    freerdp_dsp_context_free(mac->dsp_context);
+    free(mac);
+    return error;
+
+}