Blame channels/audin/client/mac/audin_mac.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit 1fb8d4
 * Audio Input Redirection Virtual Channel - Mac OS X implementation
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
Packit 1fb8d4
 * Copyright 2015 Thincast Technologies GmbH
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <stdio.h>
Packit 1fb8d4
#include <stdlib.h>
Packit 1fb8d4
#include <string.h>
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/crt.h>
Packit 1fb8d4
#include <winpr/synch.h>
Packit 1fb8d4
#include <winpr/string.h>
Packit 1fb8d4
#include <winpr/thread.h>
Packit 1fb8d4
#include <winpr/debug.h>
Packit 1fb8d4
#include <winpr/cmdline.h>
Packit 1fb8d4
Packit 1fb8d4
#define __COREFOUNDATION_CFPLUGINCOM__ 1
Packit Service 5a9772
#define IUNKNOWN_C_GUTS   \
Packit Service 5a9772
	void* _reserved;      \
Packit Service 5a9772
	void* QueryInterface; \
Packit Service 5a9772
	void* AddRef;         \
Packit Service 5a9772
	void* Release
Packit 1fb8d4
Packit 1fb8d4
#include <CoreAudio/CoreAudioTypes.h>
Packit 1fb8d4
#include <CoreAudio/CoreAudio.h>
Packit 1fb8d4
#include <AudioToolbox/AudioToolbox.h>
Packit 1fb8d4
#include <AudioToolbox/AudioQueue.h>
Packit 1fb8d4
Packit 1fb8d4
#include <freerdp/addin.h>
Packit 1fb8d4
#include <freerdp/channels/rdpsnd.h>
Packit 1fb8d4
Packit 1fb8d4
#include "audin_main.h"
Packit 1fb8d4
Packit Service 5a9772
#define MAC_AUDIO_QUEUE_NUM_BUFFERS 100
Packit 1fb8d4
Packit 1fb8d4
/* Fix for #4462: Provide type alias if not declared (Mac OS < 10.10)
Packit 1fb8d4
 * https://developer.apple.com/documentation/coreaudio/audioformatid
Packit 1fb8d4
 */
Packit 1fb8d4
#ifndef AudioFormatID
Packit 1fb8d4
typedef UInt32 AudioFormatID;
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#ifndef AudioFormatFlags
Packit 1fb8d4
typedef UInt32 AudioFormatFlags;
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
typedef struct _AudinMacDevice
Packit 1fb8d4
{
Packit 1fb8d4
	IAudinDevice iface;
Packit 1fb8d4
Packit 1fb8d4
	AUDIO_FORMAT format;
Packit 1fb8d4
	UINT32 FramesPerPacket;
Packit 1fb8d4
	int dev_unit;
Packit 1fb8d4
Packit 1fb8d4
	AudinReceive receive;
Packit 1fb8d4
	void* user_data;
Packit 1fb8d4
Packit 1fb8d4
	rdpContext* rdpcontext;
Packit 1fb8d4
Packit 1fb8d4
	bool isOpen;
Packit 1fb8d4
	AudioQueueRef audioQueue;
Packit 1fb8d4
	AudioStreamBasicDescription audioFormat;
Packit 1fb8d4
	AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
Packit 1fb8d4
} AudinMacDevice;
Packit 1fb8d4
Packit 1fb8d4
static AudioFormatID audin_mac_get_format(const AUDIO_FORMAT* format)
Packit 1fb8d4
{
Packit 1fb8d4
	switch (format->wFormatTag)
Packit 1fb8d4
	{
Packit 1fb8d4
		case WAVE_FORMAT_PCM:
Packit 1fb8d4
			return kAudioFormatLinearPCM;
Packit 1fb8d4
Packit 1fb8d4
		default:
Packit 1fb8d4
			return 0;
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static AudioFormatFlags audin_mac_get_flags_for_format(const AUDIO_FORMAT* format)
Packit 1fb8d4
{
Packit 1fb8d4
	switch (format->wFormatTag)
Packit 1fb8d4
	{
Packit 1fb8d4
		case WAVE_FORMAT_PCM:
Packit 1fb8d4
			return kAudioFormatFlagIsSignedInteger;
Packit 1fb8d4
Packit 1fb8d4
		default:
Packit 1fb8d4
			return 0;
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL audin_mac_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
Packit 1fb8d4
{
Packit 1fb8d4
	AudioFormatID req_fmt = 0;
Packit 1fb8d4
Packit 1fb8d4
	if (device == NULL || format == NULL)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	req_fmt = audin_mac_get_format(format);
Packit 1fb8d4
Packit 1fb8d4
	if (req_fmt == 0)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * Function description
Packit 1fb8d4
 *
Packit 1fb8d4
 * @return 0 on success, otherwise a Win32 error code
Packit 1fb8d4
 */
Packit 1fb8d4
static UINT audin_mac_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
Packit 1fb8d4
                                 UINT32 FramesPerPacket)
Packit 1fb8d4
{
Packit 1fb8d4
	AudinMacDevice* mac = (AudinMacDevice*)device;
Packit 1fb8d4
Packit 1fb8d4
	if (device == NULL || format == NULL)
Packit 1fb8d4
		return ERROR_INVALID_PARAMETER;
Packit 1fb8d4
Packit 1fb8d4
	mac->FramesPerPacket = FramesPerPacket;
Packit 1fb8d4
	mac->format = *format;
Packit 1fb8d4
	WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
Packit Service 5a9772
	          audio_format_get_tag_string(format->wFormatTag), format->nChannels,
Packit Service 5a9772
	          format->nSamplesPerSec, format->wBitsPerSample);
Packit 1fb8d4
	mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
Packit 1fb8d4
Packit 1fb8d4
	if (format->wBitsPerSample == 0)
Packit 1fb8d4
		mac->audioFormat.mBitsPerChannel = 16;
Packit 1fb8d4
Packit 1fb8d4
	mac->audioFormat.mBytesPerFrame = 0;
Packit 1fb8d4
	mac->audioFormat.mBytesPerPacket = 0;
Packit 1fb8d4
	mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
Packit 1fb8d4
	mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
Packit 1fb8d4
	mac->audioFormat.mFormatID = audin_mac_get_format(format);
Packit 1fb8d4
	mac->audioFormat.mFramesPerPacket = 1;
Packit 1fb8d4
	mac->audioFormat.mReserved = 0;
Packit 1fb8d4
	mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
Packit 1fb8d4
	return CHANNEL_RC_OK;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
static void mac_audio_queue_input_cb(void* aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
Packit Service 5a9772
                                     const AudioTimeStamp* inStartTime, UInt32 inNumPackets,
Packit 1fb8d4
                                     const AudioStreamPacketDescription* inPacketDesc)
Packit 1fb8d4
{
Packit 1fb8d4
	AudinMacDevice* mac = (AudinMacDevice*)aqData;
Packit 1fb8d4
	UINT error = CHANNEL_RC_OK;
Packit 1fb8d4
	const BYTE* buffer = inBuffer->mAudioData;
Packit 1fb8d4
	int buffer_size = inBuffer->mAudioDataByteSize;
Packit 1fb8d4
	(void)inAQ;
Packit 1fb8d4
	(void)inStartTime;
Packit 1fb8d4
	(void)inNumPackets;
Packit 1fb8d4
	(void)inPacketDesc;
Packit 1fb8d4
Packit 1fb8d4
	if (buffer_size > 0)
Packit 1fb8d4
		error = mac->receive(&mac->format, buffer, buffer_size, mac->user_data);
Packit 1fb8d4
Packit 1fb8d4
	AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
Packit 1fb8d4
Packit 1fb8d4
	if (error)
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "mac->receive failed with error %" PRIu32 "", error);
Packit 1fb8d4
		SetLastError(ERROR_INTERNAL_ERROR);
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static UINT audin_mac_close(IAudinDevice* device)
Packit 1fb8d4
{
Packit 1fb8d4
	UINT errCode = CHANNEL_RC_OK;
Packit 1fb8d4
	char errString[1024];
Packit 1fb8d4
	OSStatus devStat;
Packit 1fb8d4
	AudinMacDevice* mac = (AudinMacDevice*)device;
Packit 1fb8d4
Packit 1fb8d4
	if (device == NULL)
Packit 1fb8d4
		return ERROR_INVALID_PARAMETER;
Packit 1fb8d4
Packit 1fb8d4
	if (mac->isOpen)
Packit 1fb8d4
	{
Packit 1fb8d4
		devStat = AudioQueueStop(mac->audioQueue, true);
Packit 1fb8d4
Packit 1fb8d4
		if (devStat != 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			errCode = GetLastError();
Packit Service 5a9772
			WLog_ERR(TAG, "AudioQueueStop failed with %s [%" PRIu32 "]",
Packit 1fb8d4
			         winpr_strerror(errCode, errString, sizeof(errString)), errCode);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		mac->isOpen = false;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (mac->audioQueue)
Packit 1fb8d4
	{
Packit 1fb8d4
		devStat = AudioQueueDispose(mac->audioQueue, true);
Packit 1fb8d4
Packit 1fb8d4
		if (devStat != 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			errCode = GetLastError();
Packit Service 5a9772
			WLog_ERR(TAG, "AudioQueueDispose failed with %s [%" PRIu32 "]",
Packit 1fb8d4
			         winpr_strerror(errCode, errString, sizeof(errString)), errCode);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		mac->audioQueue = NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	mac->receive = NULL;
Packit 1fb8d4
	mac->user_data = NULL;
Packit 1fb8d4
	return errCode;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static UINT audin_mac_open(IAudinDevice* device, AudinReceive receive, void* user_data)
Packit 1fb8d4
{
Packit 1fb8d4
	AudinMacDevice* mac = (AudinMacDevice*)device;
Packit 1fb8d4
	DWORD errCode;
Packit 1fb8d4
	char errString[1024];
Packit 1fb8d4
	OSStatus devStat;
Packit 1fb8d4
	size_t index;
Packit 1fb8d4
	mac->receive = receive;
Packit 1fb8d4
	mac->user_data = user_data;
Packit Service 5a9772
	devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb, mac, NULL,
Packit Service 5a9772
	                             kCFRunLoopCommonModes, 0, &(mac->audioQueue));
Packit 1fb8d4
Packit 1fb8d4
	if (devStat != 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		errCode = GetLastError();
Packit Service 5a9772
		WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%" PRIu32 "]",
Packit 1fb8d4
		         winpr_strerror(errCode, errString, sizeof(errString)), errCode);
Packit 1fb8d4
		goto err_out;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
Packit 1fb8d4
	{
Packit 1fb8d4
		devStat = AudioQueueAllocateBuffer(mac->audioQueue,
Packit 1fb8d4
		                                   mac->FramesPerPacket * 2 * mac->format.nChannels,
Packit 1fb8d4
		                                   &mac->audioBuffers[index]);
Packit 1fb8d4
Packit 1fb8d4
		if (devStat != 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			errCode = GetLastError();
Packit Service 5a9772
			WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%" PRIu32 "]",
Packit 1fb8d4
			         winpr_strerror(errCode, errString, sizeof(errString)), errCode);
Packit 1fb8d4
			goto err_out;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit Service 5a9772
		devStat = AudioQueueEnqueueBuffer(mac->audioQueue, mac->audioBuffers[index], 0, NULL);
Packit 1fb8d4
Packit 1fb8d4
		if (devStat != 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			errCode = GetLastError();
Packit Service 5a9772
			WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]",
Packit 1fb8d4
			         winpr_strerror(errCode, errString, sizeof(errString)), errCode);
Packit 1fb8d4
			goto err_out;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	devStat = AudioQueueStart(mac->audioQueue, NULL);
Packit 1fb8d4
Packit 1fb8d4
	if (devStat != 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		errCode = GetLastError();
Packit Service 5a9772
		WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]",
Packit 1fb8d4
		         winpr_strerror(errCode, errString, sizeof(errString)), errCode);
Packit 1fb8d4
		goto err_out;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	mac->isOpen = true;
Packit 1fb8d4
	return CHANNEL_RC_OK;
Packit 1fb8d4
err_out:
Packit 1fb8d4
	audin_mac_close(device);
Packit 1fb8d4
	return CHANNEL_RC_INITIALIZATION_ERROR;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static UINT audin_mac_free(IAudinDevice* device)
Packit 1fb8d4
{
Packit 1fb8d4
	AudinMacDevice* mac = (AudinMacDevice*)device;
Packit 1fb8d4
	int error;
Packit 1fb8d4
Packit 1fb8d4
	if (device == NULL)
Packit 1fb8d4
		return ERROR_INVALID_PARAMETER;
Packit 1fb8d4
Packit 1fb8d4
	if ((error = audin_mac_close(device)))
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	free(mac);
Packit 1fb8d4
	return CHANNEL_RC_OK;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static UINT audin_mac_parse_addin_args(AudinMacDevice* device, ADDIN_ARGV* args)
Packit 1fb8d4
{
Packit 1fb8d4
	DWORD errCode;
Packit 1fb8d4
	char errString[1024];
Packit 1fb8d4
	int status;
Packit Service 5a9772
	char *str_num, *eptr;
Packit 1fb8d4
	DWORD flags;
Packit 1fb8d4
	COMMAND_LINE_ARGUMENT_A* arg;
Packit Service 5a9772
	COMMAND_LINE_ARGUMENT_A audin_mac_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
Packit Service 5a9772
		                                           NULL, NULL, -1, NULL, "audio device name" },
Packit Service 5a9772
		                                         { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
Packit Service 5a9772
Packit 1fb8d4
	AudinMacDevice* mac = (AudinMacDevice*)device;
Packit 1fb8d4
Packit 1fb8d4
	if (args->argc == 1)
Packit 1fb8d4
		return CHANNEL_RC_OK;
Packit 1fb8d4
Packit Service 5a9772
	flags =
Packit Service 5a9772
	    COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
Packit Service 5a9772
	status =
Packit Service 5a9772
	    CommandLineParseArgumentsA(args->argc, args->argv, audin_mac_args, flags, mac, NULL, NULL);
Packit 1fb8d4
Packit 1fb8d4
	if (status < 0)
Packit 1fb8d4
		return ERROR_INVALID_PARAMETER;
Packit 1fb8d4
Packit 1fb8d4
	arg = audin_mac_args;
Packit 1fb8d4
Packit 1fb8d4
	do
Packit 1fb8d4
	{
Packit 1fb8d4
		if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
Packit 1fb8d4
			continue;
Packit 1fb8d4
Packit Service 5a9772
		CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
Packit 1fb8d4
		{
Packit 1fb8d4
			str_num = _strdup(arg->Value);
Packit 1fb8d4
Packit 1fb8d4
			if (!str_num)
Packit 1fb8d4
			{
Packit 1fb8d4
				errCode = GetLastError();
Packit 1fb8d4
				WLog_ERR(TAG, "_strdup failed with %s [%d]",
Packit 1fb8d4
				         winpr_strerror(errCode, errString, sizeof(errString)), errCode);
Packit 1fb8d4
				return CHANNEL_RC_NO_MEMORY;
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			mac->dev_unit = strtol(str_num, &eptr, 10);
Packit 1fb8d4
Packit 1fb8d4
			if (mac->dev_unit < 0 || *eptr != '\0')
Packit 1fb8d4
				mac->dev_unit = -1;
Packit 1fb8d4
Packit 1fb8d4
			free(str_num);
Packit 1fb8d4
		}
Packit 1fb8d4
		CommandLineSwitchEnd(arg)
Packit Service 5a9772
	} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
Packit 1fb8d4
Packit 1fb8d4
	return CHANNEL_RC_OK;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#ifdef BUILTIN_CHANNELS
Packit Service 5a9772
#define freerdp_audin_client_subsystem_entry mac_freerdp_audin_client_subsystem_entry
Packit 1fb8d4
#else
Packit Service 5a9772
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
Packit 1fb8d4
{
Packit 1fb8d4
	DWORD errCode;
Packit 1fb8d4
	char errString[1024];
Packit 1fb8d4
	ADDIN_ARGV* args;
Packit 1fb8d4
	AudinMacDevice* mac;
Packit 1fb8d4
	UINT error;
Packit 1fb8d4
	mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
Packit 1fb8d4
Packit 1fb8d4
	if (!mac)
Packit 1fb8d4
	{
Packit 1fb8d4
		errCode = GetLastError();
Packit Service 5a9772
		WLog_ERR(TAG, "calloc failed with %s [%" PRIu32 "]",
Packit 1fb8d4
		         winpr_strerror(errCode, errString, sizeof(errString)), errCode);
Packit 1fb8d4
		return CHANNEL_RC_NO_MEMORY;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	mac->iface.Open = audin_mac_open;
Packit 1fb8d4
	mac->iface.FormatSupported = audin_mac_format_supported;
Packit 1fb8d4
	mac->iface.SetFormat = audin_mac_set_format;
Packit 1fb8d4
	mac->iface.Close = audin_mac_close;
Packit 1fb8d4
	mac->iface.Free = audin_mac_free;
Packit 1fb8d4
	mac->rdpcontext = pEntryPoints->rdpcontext;
Packit 1fb8d4
	mac->dev_unit = -1;
Packit 1fb8d4
	args = pEntryPoints->args;
Packit 1fb8d4
Packit 1fb8d4
	if ((error = audin_mac_parse_addin_args(mac, args)))
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %" PRIu32 "!", error);
Packit 1fb8d4
		goto error_out;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit Service 5a9772
	if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)mac)))
Packit 1fb8d4
	{
Packit Service 5a9772
		WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
Packit 1fb8d4
		goto error_out;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return CHANNEL_RC_OK;
Packit 1fb8d4
error_out:
Packit 1fb8d4
	free(mac);
Packit 1fb8d4
	return error;
Packit 1fb8d4
}