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

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