Rewrite of sound and microphone channels

The sound and microphone redirection channels (and in part TSMF)
did not properly decouple encoding/decoding from the backends used
to play/record sound.
Encapsulating encoding/decoding in rewritten freerdp_dsp_* functions
with variable backends, simplifying alsa/oss/pulse/... audio backends.
This commit is contained in:
Armin Novak 2018-02-20 12:15:30 +01:00
parent a9ecd6a6cc
commit f89c1857b9
43 changed files with 3424 additions and 3551 deletions

View File

@ -704,6 +704,18 @@ set(GSM_FEATURE_TYPE "OPTIONAL")
set(GSM_FEATURE_PURPOSE "codec")
set(GSM_FEATURE_DESCRIPTION "GSM audio codec library")
set(LAME_FEATURE_TYPE "OPTIONAL")
set(LAME_FEATURE_PURPOSE "codec")
set(LAME_FEATURE_DESCRIPTION "lame MP3 audio codec library")
set(FAAD2_FEATURE_TYPE "OPTIONAL")
set(FAAD2_FEATURE_PURPOSE "codec")
set(FAAD2_FEATURE_DESCRIPTION "FAAD2 AAC audio codec library")
set(FAAC_FEATURE_TYPE "OPTIONAL")
set(FAAC_FEATURE_PURPOSE "codec")
set(FAAC_FEATURE_DESCRIPTION "FAAC AAC audio codec library")
set(GSSAPI_FEATURE_TYPE "OPTIONAL")
set(GSSAPI_FEATURE_PURPOSE "auth")
set(GSSAPI_FEATURE_DESCRIPTION "add kerberos support")
@ -804,6 +816,9 @@ find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DE
find_feature(x264 ${X264_FEATURE_TYPE} ${X264_FEATURE_PURPOSE} ${X264_FEATURE_DESCRIPTION})
find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION})
find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION})
find_feature(LAME ${LAME_FEATURE_TYPE} ${LAME_FEATURE_PURPOSE} ${LAME_FEATURE_DESCRIPTION})
find_feature(FAAD2 ${FAAD2_FEATURE_TYPE} ${FAAD2_FEATURE_PURPOSE} ${FAAD2_FEATURE_DESCRIPTION})
find_feature(FAAC ${FAAC_FEATURE_TYPE} ${FAAC_FEATURE_PURPOSE} ${FAAC_FEATURE_DESCRIPTION})
find_feature(GSSAPI ${GSSAPI_FEATURE_TYPE} ${GSSAPI_FEATURE_PURPOSE} ${GSSAPI_FEATURE_DESCRIPTION})

View File

@ -35,7 +35,6 @@
#include <alsa/asoundlib.h>
#include <freerdp/addin.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/rdpsnd.h>
#include "audin_main.h"
@ -46,34 +45,52 @@ typedef struct _AudinALSADevice
char* device_name;
UINT32 frames_per_packet;
UINT32 target_rate;
UINT32 actual_rate;
snd_pcm_format_t format;
UINT32 target_channels;
UINT32 actual_channels;
int bytes_per_channel;
int wformat;
int block_size;
FREERDP_DSP_CONTEXT* dsp_context;
AUDIO_FORMAT aformat;
HANDLE thread;
HANDLE stopEvent;
BYTE* buffer;
int buffer_frames;
AudinReceive receive;
void* user_data;
rdpContext* rdpcontext;
} AudinALSADevice;
static snd_pcm_format_t audin_alsa_format(UINT32 wFormatTag, UINT32 bitPerChannel)
{
switch (wFormatTag)
{
case WAVE_FORMAT_PCM:
switch (bitPerChannel)
{
case 16:
return SND_PCM_FORMAT_S16_LE;
case 8:
return SND_PCM_FORMAT_S8;
default:
return SND_PCM_FORMAT_UNKNOWN;
}
case WAVE_FORMAT_ALAW:
return SND_PCM_FORMAT_A_LAW;
case WAVE_FORMAT_MULAW:
return SND_PCM_FORMAT_MU_LAW;
default:
return SND_PCM_FORMAT_UNKNOWN;
}
}
static BOOL audin_alsa_set_params(AudinALSADevice* alsa,
snd_pcm_t* capture_handle)
{
int error;
UINT32 channels = alsa->aformat.nChannels;
snd_pcm_hw_params_t* hw_params;
snd_pcm_format_t format = audin_alsa_format(alsa->aformat.wFormatTag, alsa->aformat.wBitsPerSample);
if ((error = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
@ -85,150 +102,27 @@ static BOOL audin_alsa_set_params(AudinALSADevice* alsa,
snd_pcm_hw_params_any(capture_handle, hw_params);
snd_pcm_hw_params_set_access(capture_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(capture_handle, hw_params, alsa->format);
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->actual_rate,
NULL);
snd_pcm_hw_params_set_format(capture_handle, hw_params, format);
snd_pcm_hw_params_set_rate_near(capture_handle, hw_params,
&alsa->aformat.nSamplesPerSec, NULL);
snd_pcm_hw_params_set_channels_near(capture_handle, hw_params,
&alsa->actual_channels);
&channels);
snd_pcm_hw_params(capture_handle, hw_params);
snd_pcm_hw_params_free(hw_params);
snd_pcm_prepare(capture_handle);
if ((alsa->actual_rate != alsa->target_rate) ||
(alsa->actual_channels != alsa->target_channels))
{
DEBUG_DVC("actual rate %"PRIu32" / channel %"PRIu32" is "
"different from target rate %"PRIu32" / channel %"PRIu32", resampling required.",
alsa->actual_rate, alsa->actual_channels,
alsa->target_rate, alsa->target_channels);
}
alsa->aformat.nChannels = channels;
return TRUE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src,
int size)
{
int frames;
int cframes;
UINT ret = CHANNEL_RC_OK;
int encoded_size;
BYTE* encoded_data;
int rbytes_per_frame;
int tbytes_per_frame;
int status;
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
if ((alsa->target_rate == alsa->actual_rate) &&
(alsa->target_channels == alsa->actual_channels))
{
frames = size / rbytes_per_frame;
}
else
{
alsa->dsp_context->resample(alsa->dsp_context, src, alsa->bytes_per_channel,
alsa->actual_channels, alsa->actual_rate, size / rbytes_per_frame,
alsa->target_channels, alsa->target_rate);
frames = alsa->dsp_context->resampled_frames;
DEBUG_DVC("resampled %d frames at %"PRIu32" to %d frames at %"PRIu32"",
size / rbytes_per_frame, alsa->actual_rate, frames, alsa->target_rate);
src = alsa->dsp_context->resampled_buffer;
}
while (frames > 0)
{
status = WaitForSingleObject(alsa->stopEvent, 0);
if (status == WAIT_FAILED)
{
ret = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", ret);
break;
}
if (status == WAIT_OBJECT_0)
break;
cframes = alsa->frames_per_packet - alsa->buffer_frames;
if (cframes > frames)
cframes = frames;
CopyMemory(alsa->buffer + alsa->buffer_frames * tbytes_per_frame, src,
cframes * tbytes_per_frame);
alsa->buffer_frames += cframes;
if (alsa->buffer_frames >= alsa->frames_per_packet)
{
if (alsa->wformat == WAVE_FORMAT_DVI_ADPCM)
{
if (!alsa->dsp_context->encode_ima_adpcm(alsa->dsp_context,
alsa->buffer, alsa->buffer_frames * tbytes_per_frame,
alsa->target_channels, alsa->block_size))
{
ret = ERROR_INTERNAL_ERROR;
break;
}
encoded_data = alsa->dsp_context->adpcm_buffer;
encoded_size = alsa->dsp_context->adpcm_size;
DEBUG_DVC("encoded %d to %d",
alsa->buffer_frames * tbytes_per_frame, encoded_size);
}
else
{
encoded_data = alsa->buffer;
encoded_size = alsa->buffer_frames * tbytes_per_frame;
}
status = WaitForSingleObject(alsa->stopEvent, 0);
if (status == WAIT_FAILED)
{
ret = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", ret);
break;
}
if (status == WAIT_OBJECT_0)
break;
else
{
DEBUG_DVC("encoded %d [%d] to %d [%X]", alsa->buffer_frames,
tbytes_per_frame, encoded_size,
alsa->wformat);
ret = alsa->receive(encoded_data, encoded_size, alsa->user_data);
}
alsa->buffer_frames = 0;
if (ret != CHANNEL_RC_OK)
break;
}
src += cframes * tbytes_per_frame;
frames -= cframes;
}
return ret;
}
static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
{
long error;
BYTE* buffer;
int rbytes_per_frame;
snd_pcm_t* capture_handle = NULL;
AudinALSADevice* alsa = (AudinALSADevice*) arg;
const size_t rbytes_per_frame = alsa->aformat.nChannels * 4;
DWORD status;
DEBUG_DVC("in");
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
buffer = (BYTE*) calloc(alsa->frames_per_packet, rbytes_per_frame);
if (!buffer)
@ -238,8 +132,6 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
goto out;
}
freerdp_dsp_context_reset_adpcm(alsa->dsp_context);
if ((error = snd_pcm_open(&capture_handle, alsa->device_name,
SND_PCM_STREAM_CAPTURE, 0)) < 0)
{
@ -281,7 +173,10 @@ static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
break;
}
if ((error = audin_alsa_thread_receive(alsa, buffer, error * rbytes_per_frame)))
error = alsa->receive(&alsa->aformat,
buffer, error, alsa->user_data);
if (error)
{
WLog_ERR(TAG, "audin_alsa_thread_receive failed with error %ld", error);
break;
@ -312,14 +207,13 @@ out:
static UINT audin_alsa_free(IAudinDevice* device)
{
AudinALSADevice* alsa = (AudinALSADevice*) device;
freerdp_dsp_context_free(alsa->dsp_context);
free(alsa->device_name);
free(alsa);
return CHANNEL_RC_OK;
}
static BOOL audin_alsa_format_supported(IAudinDevice* device,
audinFormat* format)
const AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
@ -334,15 +228,12 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device,
break;
case WAVE_FORMAT_DVI_ADPCM:
if ((format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
return TRUE;
break;
default:
return FALSE;
}
return FALSE;
@ -353,47 +244,16 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device,
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_alsa_set_format(IAudinDevice* device, audinFormat* format,
static UINT audin_alsa_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
{
int bs;
AudinALSADevice* alsa = (AudinALSADevice*) device;
alsa->target_rate = format->nSamplesPerSec;
alsa->actual_rate = format->nSamplesPerSec;
alsa->target_channels = format->nChannels;
alsa->actual_channels = format->nChannels;
alsa->aformat = *format;
alsa->frames_per_packet = FramesPerPacket;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 8:
alsa->format = SND_PCM_FORMAT_S8;
alsa->bytes_per_channel = 1;
break;
if (audin_alsa_format(format->wFormatTag, format->wBitsPerSample) == SND_PCM_FORMAT_UNKNOWN)
return ERROR_INTERNAL_ERROR;
case 16:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
break;
}
break;
case WAVE_FORMAT_DVI_ADPCM:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
alsa->frames_per_packet = (alsa->frames_per_packet * format->nChannels * 2 /
bs + 1) * bs / (format->nChannels * 2);
DEBUG_DVC("aligned FramesPerPacket=%"PRIu32"",
alsa->frames_per_packet);
break;
}
alsa->wformat = format->wFormatTag;
alsa->block_size = format->nBlockAlign;
return CHANNEL_RC_OK;
}
@ -405,20 +265,9 @@ static UINT audin_alsa_set_format(IAudinDevice* device, audinFormat* format,
static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
{
int tbytes_per_frame;
AudinALSADevice* alsa = (AudinALSADevice*) device;
alsa->receive = receive;
alsa->user_data = user_data;
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
alsa->buffer = (BYTE*) calloc(alsa->frames_per_packet, tbytes_per_frame);
if (!alsa->buffer)
{
WLog_ERR(TAG, "calloc failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
alsa->buffer_frames = 0;
if (!(alsa->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
@ -427,7 +276,7 @@ static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive,
}
if (!(alsa->thread = CreateThread(NULL, 0,
audin_alsa_thread_func, alsa, 0, NULL)))
audin_alsa_thread_func, alsa, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
goto error_out;
@ -435,8 +284,6 @@ static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive,
return CHANNEL_RC_OK;
error_out:
free(alsa->buffer);
alsa->buffer = NULL;
CloseHandle(alsa->stopEvent);
alsa->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
@ -469,14 +316,12 @@ static UINT audin_alsa_close(IAudinDevice* device)
alsa->thread = NULL;
}
free(alsa->buffer);
alsa->buffer = NULL;
alsa->receive = NULL;
alsa->user_data = NULL;
return error;
}
COMMAND_LINE_ARGUMENT_A audin_alsa_args[] =
static COMMAND_LINE_ARGUMENT_A audin_alsa_args[] =
{
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
@ -579,20 +424,10 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
}
alsa->frames_per_packet = 128;
alsa->target_rate = 22050;
alsa->actual_rate = 22050;
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->target_channels = 2;
alsa->actual_channels = 2;
alsa->bytes_per_channel = 2;
alsa->dsp_context = freerdp_dsp_context_new();
if (!alsa->dsp_context)
{
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
alsa->aformat.nChannels = 2;
alsa->aformat.wBitsPerSample = 16;
alsa->aformat.wFormatTag = WAVE_FORMAT_PCM;
alsa->aformat.nSamplesPerSec = 44100;
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
(IAudinDevice*) alsa)))
@ -603,7 +438,6 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
return CHANNEL_RC_OK;
error_out:
freerdp_dsp_context_free(alsa->dsp_context);
free(alsa->device_name);
free(alsa);
return error;

View File

@ -32,11 +32,13 @@
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <winpr/stream.h>
#include <winpr/wlog.h>
#include <freerdp/addin.h>
#include <winpr/stream.h>
#include <freerdp/freerdp.h>
#include <freerdp/codec/dsp.h>
#include "audin_main.h"
#define MSG_SNDIN_VERSION 0x01
@ -70,7 +72,7 @@ struct _AUDIN_CHANNEL_CALLBACK
* be stored as reference when the server sends the format index in
* Open PDU and Format Change PDU
*/
audinFormat* formats;
AUDIO_FORMAT* formats;
UINT32 formats_count;
};
@ -93,45 +95,65 @@ struct _AUDIN_PLUGIN
rdpContext* rdpcontext;
BOOL attached;
wLog* log;
wStream* data;
AUDIO_FORMAT* format;
UINT32 FramesPerPacket;
FREERDP_DSP_CONTEXT* dsp_context;
};
static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args);
static UINT audin_write_and_free_stream(AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStream* out,
BOOL freeStream)
{
UINT error = ERROR_INTERNAL_ERROR;
const size_t length = Stream_GetPosition(s);
const BYTE* data = Stream_Buffer(s);
UINT error;
if (callback && callback->channel && callback->channel->Write)
error = callback->channel->Write(callback->channel, length, data, NULL);
if (!callback || !out)
return ERROR_INVALID_PARAMETER;
if (!callback->channel || !callback->channel->Write)
return ERROR_INTERNAL_ERROR;
Stream_SealLength(out);
error = callback->channel->Write(callback->channel,
Stream_Length(out),
Stream_Buffer(out), NULL);
if (freeStream)
Stream_Free(out, TRUE);
Stream_Free(s, TRUE);
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
static UINT audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
{
wStream* out;
UINT32 Version;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
if (Stream_GetRemainingLength(s) < 4)
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, Version);
DEBUG_DVC("Version=%"PRIu32"", Version);
out = Stream_New(NULL, 5);
if (!out)
{
WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
WLog_ERR(TAG, "Stream_New failed!");
return ERROR_OUTOFMEMORY;
}
Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
Stream_Write_UINT32(out, Version);
return audin_write_and_free_stream(callback, out);
return audin_channel_write_and_free(callback, out, TRUE);
}
/**
@ -139,13 +161,11 @@ static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_send_incoming_data_pdu(AUDIN_CHANNEL_CALLBACK* callback)
static UINT audin_send_incoming_data_pdu(IWTSVirtualChannelCallback* pChannelCallback)
{
BYTE out_data[1] = { MSG_SNDIN_DATA_INCOMING };
if (!callback || !callback->channel || !callback->channel->Write)
return ERROR_INTERNAL_ERROR;
BYTE out_data[1];
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
out_data[0] = MSG_SNDIN_DATA_INCOMING;
return callback->channel->Write(callback->channel, 1, out_data, NULL);
}
@ -154,34 +174,35 @@ static UINT audin_send_incoming_data_pdu(AUDIN_CHANNEL_CALLBACK* callback)
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
static UINT audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
{
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
UINT32 i;
BYTE* fm;
UINT error;
wStream* out;
UINT32 NumFormats;
audinFormat format;
size_t cbSizeFormatsPacket;
AUDIO_FORMAT format;
UINT32 cbSizeFormatsPacket;
if (Stream_GetRemainingLength(s) < 8)
return ERROR_NO_DATA;
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, NumFormats);
DEBUG_DVC("NumFormats %"PRIu32"", NumFormats);
if ((NumFormats < 1) || (NumFormats > 1000))
{
WLog_Print(audin->log, WLOG_ERROR, "bad NumFormats %"PRIu32"", NumFormats);
WLog_ERR(TAG, "bad NumFormats %"PRIu32"", NumFormats);
return ERROR_INVALID_DATA;
}
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket */
callback->formats = (audinFormat*) calloc(NumFormats, sizeof(audinFormat));
callback->formats = (AUDIO_FORMAT*) calloc(NumFormats, sizeof(AUDIO_FORMAT));
if (!callback->formats)
{
WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
WLog_ERR(TAG, "calloc failed!");
return ERROR_INVALID_DATA;
}
@ -190,7 +211,7 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
if (!out)
{
error = CHANNEL_RC_NO_MEMORY;
WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
WLog_ERR(TAG, "Stream_New failed!");
goto out;
}
@ -199,21 +220,23 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
/* SoundFormats (variable) */
for (i = 0; i < NumFormats; i++)
{
BYTE* fm;
if (Stream_GetRemainingLength(s) < 18)
return ERROR_NO_DATA;
return ERROR_INVALID_DATA;
Stream_GetPointer(s, fm);
Stream_Read_UINT16(s, format.wFormatTag);
Stream_Read_UINT16(s, format.nChannels);
Stream_Read_UINT32(s, format.nSamplesPerSec);
Stream_Seek_UINT32(s); /* nAvgBytesPerSec */
Stream_Read_UINT32(s, format.nAvgBytesPerSec);
Stream_Read_UINT16(s, format.nBlockAlign);
Stream_Read_UINT16(s, format.wBitsPerSample);
Stream_Read_UINT16(s, format.cbSize);
format.data = Stream_Pointer(s);
if (Stream_GetRemainingLength(s) < format.cbSize)
return ERROR_NO_DATA;
return ERROR_INVALID_DATA;
Stream_Seek(s, format.cbSize);
DEBUG_DVC("wFormatTag=%"PRIu16" nChannels=%"PRIu16" nSamplesPerSec=%"PRIu32" "
@ -230,9 +253,9 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
if (audin->fixed_rate > 0 && audin->fixed_rate != format.nSamplesPerSec)
continue;
if (audin->device && audin->device->FormatSupported(audin->device, &format))
if (freerdp_dsp_supports_format(&format, TRUE) ||
audin->device->FormatSupported(audin->device, &format))
{
DEBUG_DVC("format ok");
/* Store the agreed format in the corresponding index */
callback->formats[callback->formats_count++] = format;
@ -240,7 +263,7 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
if (!Stream_EnsureRemainingCapacity(out, 18 + format.cbSize))
{
error = CHANNEL_RC_NO_MEMORY;
WLog_Print(audin->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
goto out;
}
@ -248,19 +271,19 @@ static UINT audin_process_formats(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c
}
}
if ((error = audin_send_incoming_data_pdu(callback)))
if ((error = audin_send_incoming_data_pdu(pChannelCallback)))
{
WLog_Print(audin->log, WLOG_ERROR, "audin_send_incoming_data_pdu failed!");
WLog_ERR(TAG, "audin_send_incoming_data_pdu failed!");
goto out;
}
cbSizeFormatsPacket = Stream_GetPosition(out);
cbSizeFormatsPacket = (UINT32) Stream_GetPosition(out);
Stream_SetPosition(out, 0);
Stream_Write_UINT8(out, MSG_SNDIN_FORMATS); /* Header (1 byte) */
Stream_Write_UINT32(out, callback->formats_count); /* NumFormats (4 bytes) */
Stream_Write_UINT32(out, cbSizeFormatsPacket); /* cbSizeFormatsPacket (4 bytes) */
Stream_SetPosition(out, cbSizeFormatsPacket);
error = audin_write_and_free_stream(callback, out);
error = audin_channel_write_and_free(callback, out, TRUE);
out:
if (error != CHANNEL_RC_OK)
@ -277,20 +300,22 @@ out:
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
static UINT audin_send_format_change_pdu(IWTSVirtualChannelCallback* pChannelCallback,
UINT32 NewFormat)
{
wStream* out = Stream_New(NULL, 5);
wStream* out;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
out = Stream_New(NULL, 5);
if (!out)
{
WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
WLog_ERR(TAG, "Stream_New failed!");
return CHANNEL_RC_OK;
}
Stream_Write_UINT8(out, MSG_SNDIN_FORMATCHANGE);
Stream_Write_UINT32(out, NewFormat);
return audin_write_and_free_stream(callback, out);
return audin_channel_write_and_free(callback, out, TRUE);
}
/**
@ -298,20 +323,21 @@ static UINT audin_send_format_change_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALL
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
UINT32 Result)
static UINT audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallback, UINT32 Result)
{
wStream* out = Stream_New(NULL, 5);
wStream* out;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
out = Stream_New(NULL, 5);
if (!out)
{
WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
WLog_ERR(TAG, "Stream_New failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT8(out, MSG_SNDIN_OPEN_REPLY);
Stream_Write_UINT32(out, Result);
return audin_write_and_free_stream(callback, out);
return audin_channel_write_and_free(callback, out, TRUE);
}
/**
@ -319,10 +345,10 @@ static UINT audin_send_open_reply_pdu(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBAC
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_receive_wave_data(const BYTE* data, int size, void* user_data)
static UINT audin_receive_wave_data(const AUDIO_FORMAT* format,
const BYTE* data, size_t size, void* user_data)
{
UINT error;
wStream* out;
AUDIN_PLUGIN* audin;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data;
@ -337,84 +363,125 @@ static UINT audin_receive_wave_data(const BYTE* data, int size, void* user_data)
if (!audin->attached)
return CHANNEL_RC_OK;
if ((error = audin_send_incoming_data_pdu(callback)))
Stream_SetPosition(audin->data, 0);
if (!Stream_EnsureRemainingCapacity(audin->data, 1))
return CHANNEL_RC_NO_MEMORY;
Stream_Write_UINT8(audin->data, MSG_SNDIN_DATA);
if (audin->device->FormatSupported(audin->device, audin->format))
{
WLog_Print(audin->log, WLOG_ERROR, "audin_send_incoming_data_pdu failed!");
if (!Stream_EnsureRemainingCapacity(audin->data, size))
return CHANNEL_RC_NO_MEMORY;
Stream_Write(audin->data, data, size);
}
else if (!freerdp_dsp_encode(audin->dsp_context, format, data, size, audin->data))
return ERROR_INTERNAL_ERROR;
if (Stream_GetPosition(audin->data) <= 1)
return CHANNEL_RC_OK;
if ((error = audin_send_incoming_data_pdu((IWTSVirtualChannelCallback*) callback)))
{
WLog_ERR(TAG, "audin_send_incoming_data_pdu failed!");
return error;
}
out = Stream_New(NULL, size + 1);
if (!out)
{
WLog_Print(audin->log, WLOG_ERROR, "Stream_New failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
Stream_Write_UINT8(out, MSG_SNDIN_DATA);
Stream_Write(out, data, size);
return audin_write_and_free_stream(callback, out);
return audin_channel_write_and_free(callback, audin->data, FALSE);
}
static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback)
{
UINT error = ERROR_INTERNAL_ERROR;
BOOL supported;
AUDIO_FORMAT format;
if (!audin || !audin->device)
return FALSE;
format = *audin->format;
supported = IFCALLRESULT(FALSE, audin->device->FormatSupported, audin->device, &format);
WLog_DBG(TAG, "microphone uses %s codec",
rdpsnd_get_audio_tag_string(format.wFormatTag));
if (!supported)
{
format.wFormatTag = WAVE_FORMAT_PCM;
format.wBitsPerSample = 16;
}
IFCALLRET(audin->device->SetFormat, error,
audin->device, &format,
audin->FramesPerPacket);
if (error != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "SetFormat failed with errorcode %"PRIu32"", error);
return FALSE;
}
if (!audin->device->FormatSupported(audin->device, audin->format))
{
if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format))
return FALSE;
}
IFCALLRET(audin->device->Open, error, audin->device,
audin_receive_wave_data, callback);
if (error != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "Open failed with errorcode %"PRIu32"", error);
return FALSE;
}
return TRUE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s)
static UINT audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
{
audinFormat* format;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
UINT32 initialFormat;
UINT32 FramesPerPacket;
UINT error = CHANNEL_RC_OK;
if (!audin || !callback || !s)
return ERROR_INTERNAL_ERROR;
if (Stream_GetRemainingLength(s) < 8)
return ERROR_NO_DATA;
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, FramesPerPacket);
Stream_Read_UINT32(s, initialFormat);
DEBUG_DVC("FramesPerPacket=%"PRIu32" initialFormat=%"PRIu32"",
FramesPerPacket, initialFormat);
audin->FramesPerPacket = FramesPerPacket;
if (initialFormat >= (UINT32) callback->formats_count)
if (initialFormat >= callback->formats_count)
{
WLog_Print(audin->log, WLOG_ERROR, "invalid format index %"PRIu32" (total %d)",
initialFormat, callback->formats_count);
WLog_ERR(TAG, "invalid format index %"PRIu32" (total %d)",
initialFormat, callback->formats_count);
return ERROR_INVALID_DATA;
}
format = &callback->formats[initialFormat];
audin->format = &callback->formats[initialFormat];
if (audin->device)
if (!audin_open_device(audin, callback))
return ERROR_INTERNAL_ERROR;
if ((error = audin_send_format_change_pdu(pChannelCallback, initialFormat)))
{
IFCALLRET(audin->device->SetFormat, error, audin->device, format, FramesPerPacket);
if (error != CHANNEL_RC_OK)
{
WLog_Print(audin->log, WLOG_ERROR, "SetFormat failed with errorcode %"PRIu32"", error);
return error;
}
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
if (error != CHANNEL_RC_OK)
{
WLog_Print(audin->log, WLOG_ERROR, "Open failed with errorcode %"PRIu32"", error);
return error;
}
}
if ((error = audin_send_format_change_pdu(audin, callback, initialFormat)))
{
WLog_Print(audin->log, WLOG_ERROR, "audin_send_format_change_pdu failed!");
WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
return error;
}
if ((error = audin_send_open_reply_pdu(audin, callback, 0)))
WLog_Print(audin->log, WLOG_ERROR, "audin_send_open_reply_pdu failed!");
if ((error = audin_send_open_reply_pdu(pChannelCallback, 0)))
WLog_ERR(TAG, "audin_send_open_reply_pdu failed!");
return error;
}
@ -424,30 +491,27 @@ static UINT audin_process_open(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* call
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback,
wStream* s)
static UINT audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
{
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
UINT32 NewFormat;
audinFormat* format;
UINT error = CHANNEL_RC_OK;
if (!audin || !callback || !s)
return ERROR_INTERNAL_ERROR;
if (Stream_GetRemainingLength(s) < 4)
return ERROR_NO_DATA;
return ERROR_INVALID_DATA;
Stream_Read_UINT32(s, NewFormat);
DEBUG_DVC("NewFormat=%"PRIu32"", NewFormat);
if (NewFormat >= callback->formats_count)
if (NewFormat >= (UINT32) callback->formats_count)
{
WLog_Print(audin->log, WLOG_ERROR, "invalid format index %"PRIu32" (total %d)",
NewFormat, callback->formats_count);
WLog_ERR(TAG, "invalid format index %"PRIu32" (total %d)",
NewFormat, callback->formats_count);
return ERROR_INVALID_DATA;
}
format = &callback->formats[NewFormat];
audin->format = &callback->formats[NewFormat];
if (audin->device)
{
@ -455,29 +519,16 @@ static UINT audin_process_format_change(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLB
if (error != CHANNEL_RC_OK)
{
WLog_Print(audin->log, WLOG_ERROR, "Close failed with errorcode %"PRIu32"", error);
return error;
}
IFCALLRET(audin->device->SetFormat, error, audin->device, format, 0);
if (error != CHANNEL_RC_OK)
{
WLog_Print(audin->log, WLOG_ERROR, "SetFormat failed with errorcode %"PRIu32"", error);
return error;
}
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
if (error != CHANNEL_RC_OK)
{
WLog_Print(audin->log, WLOG_ERROR, "Open failed with errorcode %"PRIu32"", error);
WLog_ERR(TAG, "Close failed with errorcode %"PRIu32"", error);
return error;
}
}
if ((error = audin_send_format_change_pdu(audin, callback, NewFormat)))
WLog_Print(audin->log, WLOG_ERROR, "audin_send_format_change_pdu failed!");
if (!audin_open_device(audin, callback))
return ERROR_INTERNAL_ERROR;
if ((error = audin_send_format_change_pdu(pChannelCallback, NewFormat)))
WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
return error;
}
@ -491,19 +542,9 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
{
UINT error;
BYTE MessageId;
AUDIN_PLUGIN* audin;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
if (!callback || !data)
return ERROR_INVALID_PARAMETER;
audin = (AUDIN_PLUGIN*) callback->plugin;
if (!audin)
return ERROR_INTERNAL_ERROR;
if (Stream_GetRemainingCapacity(data) < 1)
return ERROR_NO_DATA;
if (Stream_GetRemainingLength(data) < 1)
return ERROR_INVALID_DATA;
Stream_Read_UINT8(data, MessageId);
DEBUG_DVC("MessageId=0x%02"PRIx8"", MessageId);
@ -511,23 +552,23 @@ static UINT audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
switch (MessageId)
{
case MSG_SNDIN_VERSION:
error = audin_process_version(audin, callback, data);
error = audin_process_version(pChannelCallback, data);
break;
case MSG_SNDIN_FORMATS:
error = audin_process_formats(audin, callback, data);
error = audin_process_formats(pChannelCallback, data);
break;
case MSG_SNDIN_OPEN:
error = audin_process_open(audin, callback, data);
error = audin_process_open(pChannelCallback, data);
break;
case MSG_SNDIN_FORMATCHANGE:
error = audin_process_format_change(audin, callback, data);
error = audin_process_format_change(pChannelCallback, data);
break;
default:
WLog_Print(audin->log, WLOG_ERROR, "unknown MessageId=0x%02"PRIx8"", MessageId);
WLog_ERR(TAG, "unknown MessageId=0x%02"PRIx8"", MessageId);
error = ERROR_INVALID_DATA;
break;
}
@ -552,9 +593,10 @@ static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
IFCALLRET(audin->device->Close, error, audin->device);
if (error != CHANNEL_RC_OK)
WLog_Print(audin->log, WLOG_ERROR, "Close failed with errorcode %"PRIu32"", error);
WLog_ERR(TAG, "Close failed with errorcode %"PRIu32"", error);
}
audin->format = NULL;
free(callback->formats);
free(callback);
return error;
@ -570,19 +612,13 @@ static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallb
IWTSVirtualChannelCallback** ppCallback)
{
AUDIN_CHANNEL_CALLBACK* callback;
AUDIN_PLUGIN* audin;
AUDIN_LISTENER_CALLBACK* listener_callback = (AUDIN_LISTENER_CALLBACK*) pListenerCallback;
if (!listener_callback || !listener_callback->plugin)
return ERROR_INTERNAL_ERROR;
audin = (AUDIN_PLUGIN*) listener_callback->plugin;
DEBUG_DVC("...");
callback = (AUDIN_CHANNEL_CALLBACK*) calloc(1, sizeof(AUDIN_CHANNEL_CALLBACK));
if (!callback)
{
WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
@ -608,7 +644,7 @@ static UINT audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManag
if (!audin->listener_callback)
{
WLog_Print(audin->log, WLOG_ERROR, "calloc failed!");
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
@ -636,17 +672,16 @@ static UINT audin_plugin_terminated(IWTSPlugin* pPlugin)
if (error != CHANNEL_RC_OK)
{
WLog_Print(audin->log, WLOG_ERROR, "Free failed with errorcode %"PRIu32"", error);
WLog_ERR(TAG, "Free failed with errorcode %"PRIu32"", error);
// dont stop on error
}
audin->device = NULL;
}
Stream_Free(audin->data, TRUE);
free(audin->subsystem);
audin->subsystem = NULL;
free(audin->device_name);
audin->device_name = NULL;
free(audin->listener_callback);
free(audin);
return CHANNEL_RC_OK;
@ -687,7 +722,7 @@ static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* devi
if (audin->device)
{
WLog_Print(audin->log, WLOG_ERROR, "existing device, abort.");
WLog_ERR(TAG, "existing device, abort.");
return ERROR_ALREADY_EXISTS;
}
@ -701,34 +736,33 @@ static UINT audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* devi
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_load_device_plugin(AUDIN_PLUGIN* audin, char* name, ADDIN_ARGV* args)
static UINT audin_load_device_plugin(IWTSPlugin* pPlugin, const char* name, ADDIN_ARGV* args)
{
PFREERDP_AUDIN_DEVICE_ENTRY entry;
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
UINT error;
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_channel_addin_entry("audin", name, NULL,
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_channel_addin_entry("audin", (LPSTR) name, NULL,
0);
if (entry == NULL)
{
WLog_Print(audin->log, WLOG_ERROR,
"freerdp_load_channel_addin_entry did not return any function pointers for %s ",
name);
WLog_ERR(TAG, "freerdp_load_channel_addin_entry did not return any function pointers for %s ",
name);
return ERROR_INVALID_FUNCTION;
}
entryPoints.plugin = (IWTSPlugin*) audin;
entryPoints.plugin = pPlugin;
entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
entryPoints.args = args;
entryPoints.rdpcontext = audin->rdpcontext;
entryPoints.rdpcontext = ((AUDIN_PLUGIN*)pPlugin)->rdpcontext;
if ((error = entry(&entryPoints)))
{
WLog_Print(audin->log, WLOG_ERROR, "%s entry returned error %"PRIu32".", name, error);
WLog_ERR(TAG, "%s entry returned error %"PRIu32".", name, error);
return error;
}
WLog_Print(audin->log, WLOG_INFO, "Loaded %s backend for audin", name);
WLog_INFO(TAG, "Loaded %s backend for audin", name);
return CHANNEL_RC_OK;
}
@ -744,7 +778,7 @@ static UINT audin_set_subsystem(AUDIN_PLUGIN* audin, const char* subsystem)
if (!audin->subsystem)
{
WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
WLog_ERR(TAG, "_strdup failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
@ -763,7 +797,7 @@ static UINT audin_set_device_name(AUDIN_PLUGIN* audin, char* device_name)
if (!audin->device_name)
{
WLog_Print(audin->log, WLOG_ERROR, "_strdup failed!");
WLog_ERR(TAG, "_strdup failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
@ -810,7 +844,7 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
{
if ((error = audin_set_subsystem(audin, arg->Value)))
{
WLog_Print(audin->log, WLOG_ERROR, "audin_set_subsystem failed with error %"PRIu32"!", error);
WLog_ERR(TAG, "audin_set_subsystem failed with error %"PRIu32"!", error);
return FALSE;
}
}
@ -818,7 +852,7 @@ BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args)
{
if ((error = audin_set_device_name(audin, arg->Value)))
{
WLog_Print(audin->log, WLOG_ERROR, "audin_set_device_name failed with error %"PRIu32"!", error);
WLog_ERR(TAG, "audin_set_device_name failed with error %"PRIu32"!", error);
return FALSE;
}
}
@ -916,7 +950,16 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
return CHANNEL_RC_NO_MEMORY;
}
audin->log = WLog_Get(TAG);
audin->data = Stream_New(NULL, 4096);
if (!audin->data)
goto out;
audin->dsp_context = freerdp_dsp_context_new(TRUE);
if (!audin->dsp_context)
goto out;
audin->attached = TRUE;
audin->iface.Initialize = audin_plugin_initialize;
audin->iface.Connected = NULL;
@ -936,10 +979,10 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
if (audin->subsystem)
{
if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
{
WLog_Print(audin->log, WLOG_ERROR, "audin_load_device_plugin %s failed with error %"PRIu32"!",
audin->subsystem, error);
WLog_ERR(TAG, "audin_load_device_plugin %s failed with error %"PRIu32"!",
audin->subsystem, error);
goto out;
}
}
@ -949,18 +992,18 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
{
if ((error = audin_set_subsystem(audin, entry->subsystem)))
{
WLog_Print(audin->log, WLOG_ERROR, "audin_set_subsystem for %s failed with error %"PRIu32"!",
entry->subsystem, error);
WLog_ERR(TAG, "audin_set_subsystem for %s failed with error %"PRIu32"!",
entry->subsystem, error);
}
else if ((error = audin_set_device_name(audin, entry->device)))
{
WLog_Print(audin->log, WLOG_ERROR, "audin_set_device_name for %s failed with error %"PRIu32"!",
entry->subsystem, error);
WLog_ERR(TAG, "audin_set_device_name for %s failed with error %"PRIu32"!",
entry->subsystem, error);
}
else if ((error = audin_load_device_plugin(audin, audin->subsystem, args)))
else if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
{
WLog_Print(audin->log, WLOG_ERROR, "audin_load_device_plugin %s failed with error %"PRIu32"!",
entry->subsystem, error);
WLog_ERR(TAG, "audin_load_device_plugin %s failed with error %"PRIu32"!",
entry->subsystem, error);
}
entry++;
@ -968,7 +1011,7 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
}
if (audin->device == NULL)
WLog_Print(audin->log, WLOG_ERROR, "no sound device.");
WLog_ERR(TAG, "no sound device.");
error = pEntryPoints->RegisterPlugin(pEntryPoints, "audin", (IWTSPlugin*) audin);
out:

View File

@ -42,7 +42,6 @@
#include <AudioToolbox/AudioQueue.h>
#include <freerdp/addin.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/rdpsnd.h>
#include "audin_main.h"
@ -52,75 +51,74 @@
typedef struct _AudinMacDevice
{
IAudinDevice iface;
IAudinDevice iface;
FREERDP_DSP_CONTEXT* dsp_context;
AUDIO_FORMAT format;
UINT32 FramesPerPacket;
int dev_unit;
audinFormat format;
UINT32 FramesPerPacket;
int dev_unit;
AudinReceive receive;
void* user_data;
AudinReceive receive;
void* user_data;
rdpContext* rdpcontext;
rdpContext* rdpcontext;
bool isOpen;
AudioQueueRef audioQueue;
AudioStreamBasicDescription audioFormat;
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
bool isOpen;
AudioQueueRef audioQueue;
AudioStreamBasicDescription audioFormat;
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
} AudinMacDevice;
static AudioFormatID audin_mac_get_format(const audinFormat* format)
static AudioFormatID audin_mac_get_format(const AUDIO_FORMAT* 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;
*/
}
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;
return 0;
}
static AudioFormatFlags audin_mac_get_flags_for_format(const audinFormat* format)
static AudioFormatFlags audin_mac_get_flags_for_format(const AUDIO_FORMAT* format)
{
switch(format->wFormatTag)
{
case WAVE_FORMAT_DVI_ADPCM:
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_PCM:
return kAudioFormatFlagIsSignedInteger;
default:
return 0;
}
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)
static BOOL audin_mac_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
{
AudioFormatID req_fmt = 0;
AudioFormatID req_fmt = 0;
if (device == NULL || format == NULL)
return FALSE;
if (device == NULL || format == NULL)
return FALSE;
req_fmt = audin_mac_get_format(format);
req_fmt = audin_mac_get_format(format);
if (req_fmt == 0)
return FALSE;
if (req_fmt == 0)
return FALSE;
return TRUE;
return TRUE;
}
/**
@ -128,279 +126,248 @@ static BOOL audin_mac_format_supported(IAudinDevice* device, audinFormat* format
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_mac_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
static UINT audin_mac_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
{
AudinMacDevice* mac = (AudinMacDevice*)device;
AudinMacDevice* mac = (AudinMacDevice*)device;
if (device == NULL || format == NULL)
return ERROR_INVALID_PARAMETER;
if (device == NULL || format == NULL)
return ERROR_INVALID_PARAMETER;
mac->FramesPerPacket = FramesPerPacket;
CopyMemory(&(mac->format), format, sizeof(audinFormat));
mac->FramesPerPacket = FramesPerPacket;
mac->format = *format;
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);
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;
}
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;
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,
static void mac_audio_queue_input_cb(void* aqData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
const AudioTimeStamp* inStartTime,
UInt32 inNumPackets,
const AudioStreamPacketDescription *inPacketDesc)
const AudioStreamPacketDescription* inPacketDesc)
{
AudinMacDevice* mac = (AudinMacDevice*)aqData;
UINT error;
int encoded_size = 0;
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 %"PRIu32"", error);
SetLastError(ERROR_INTERNAL_ERROR);
return;
}
AudinMacDevice* mac = (AudinMacDevice*)aqData;
UINT error;
const BYTE* buffer = inBuffer->mAudioData;
int buffer_size = inBuffer->mAudioDataByteSize;
(void)inAQ;
(void)inStartTime;
(void)inNumPackets;
(void)inPacketDesc;
if ((error = mac->receive(&mac->format, buffer, buffer_size, mac->user_data)))
{
WLog_ERR(TAG, "mac->receive failed with error %"PRIu32"", error);
SetLastError(ERROR_INTERNAL_ERROR);
return;
}
}
static UINT audin_mac_close(IAudinDevice *device)
static UINT audin_mac_close(IAudinDevice* device)
{
UINT errCode = CHANNEL_RC_OK;
char errString[1024];
OSStatus devStat;
AudinMacDevice *mac = (AudinMacDevice*)device;
UINT errCode = CHANNEL_RC_OK;
char errString[1024];
OSStatus devStat;
AudinMacDevice* mac = (AudinMacDevice*)device;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
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 [%"PRIu32"]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
}
mac->isOpen = false;
}
if (mac->isOpen)
{
devStat = AudioQueueStop(mac->audioQueue, true);
if (mac->audioQueue)
{
devStat = AudioQueueDispose(mac->audioQueue, true);
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%"PRIu32"]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
}
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueStop failed with %s [%"PRIu32"]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
}
mac->audioQueue = NULL;
}
mac->isOpen = false;
}
mac->receive = NULL;
mac->user_data = NULL;
if (mac->audioQueue)
{
devStat = AudioQueueDispose(mac->audioQueue, true);
return errCode;
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%"PRIu32"]",
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;
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));
mac->receive = receive;
mac->user_data = user_data;
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%"PRIu32"]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
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 [%"PRIu32"]",
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]);
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 [%"PRIu32"]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
goto err_out;
}
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(mac->audioQueue,
mac->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 = AudioQueueEnqueueBuffer(mac->audioQueue,
mac->audioBuffers[index],
0,
NULL);
freerdp_dsp_context_reset_adpcm(mac->dsp_context);
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(mac->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;
}
mac->isOpen = true;
devStat = AudioQueueStart(mac->audioQueue, NULL);
return CHANNEL_RC_OK;
if (devStat != 0)
{
errCode = GetLastError();
WLog_ERR(TAG, "AudioQueueStart failed with %s [%"PRIu32"]",
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;
audin_mac_close(device);
return CHANNEL_RC_INITIALIZATION_ERROR;
}
static UINT audin_mac_free(IAudinDevice* device)
{
AudinMacDevice *mac = (AudinMacDevice*)device;
AudinMacDevice* mac = (AudinMacDevice*)device;
int error;
int error;
if (device == NULL)
return ERROR_INVALID_PARAMETER;
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);
}
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;
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 }
{ "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)
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;
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;
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);
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;
if (status < 0)
return ERROR_INVALID_PARAMETER;
arg = audin_mac_args;
arg = audin_mac_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
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);
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "dev")
{
str_num = _strdup(arg->Value);
if (mac->dev_unit < 0 || *eptr != '\0')
mac->dev_unit = -1;
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;
}
free(str_num);
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
mac->dev_unit = strtol(str_num, &eptr, 10);
return CHANNEL_RC_OK;
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 BUILTIN_CHANNELS
@ -411,56 +378,44 @@ static UINT audin_mac_parse_addin_args(AudinMacDevice *device, ADDIN_ARGV *args)
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
DWORD errCode;
char errString[1024];
ADDIN_ARGV *args;
AudinMacDevice *mac;
UINT error;
DWORD errCode;
char errString[1024];
ADDIN_ARGV* args;
AudinMacDevice* mac;
UINT error;
mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
if (!mac)
{
errCode = GetLastError();
WLog_ERR(TAG, "calloc failed with %s [%"PRIu32"]",
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
return CHANNEL_RC_NO_MEMORY;
}
if (!mac)
{
errCode = GetLastError();
WLog_ERR(TAG, "calloc failed with %s [%"PRIu32"]",
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->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;
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 %"PRIu32"!", error);
goto error_out;
}
if ((error = audin_mac_parse_addin_args(mac, args)))
{
WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %"PRIu32"!", 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 %"PRIu32"!", error);
goto error_out;
}
return CHANNEL_RC_OK;
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) mac)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
freerdp_dsp_context_free(mac->dsp_context);
free(mac);
return error;
free(mac);
return error;
}

View File

@ -34,7 +34,6 @@
#include <winpr/cmdline.h>
#include <freerdp/addin.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/rdpsnd.h>
#include <SLES/OpenSLES.h>
@ -48,18 +47,13 @@ typedef struct _AudinOpenSLESDevice
IAudinDevice iface;
char* device_name;
OPENSL_STREAM *stream;
OPENSL_STREAM* stream;
AUDIO_FORMAT format;
UINT32 frames_per_packet;
UINT32 rate;
UINT32 channels;
UINT32 bytes_per_channel;
UINT32 format;
UINT32 block_size;
FREERDP_DSP_CONTEXT* dsp_context;
AudinReceive receive;
HANDLE thread;
@ -74,94 +68,59 @@ static DWORD WINAPI audin_opensles_thread_func(LPVOID arg)
{
union
{
void *v;
void* v;
short* s;
BYTE *b;
BYTE* b;
} buffer;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) arg;
const size_t raw_size = opensles->frames_per_packet * opensles->bytes_per_channel;
int rc = CHANNEL_RC_OK;
UINT error = CHANNEL_RC_OK;
DWORD status;
DWORD status;
DEBUG_DVC("opensles=%p", (void*) opensles);
assert(opensles);
assert(opensles->frames_per_packet > 0);
assert(opensles->dsp_context);
assert(opensles->stopEvent);
assert(opensles->stream);
buffer.v = calloc(1, raw_size);
if (!buffer.v)
{
error = CHANNEL_RC_NO_MEMORY;
error = CHANNEL_RC_NO_MEMORY;
WLog_ERR(TAG, "calloc failed!");
if (opensles->rdpcontext)
setChannelError(opensles->rdpcontext, CHANNEL_RC_NO_MEMORY, "audin_opensles_thread_func reported an error");
goto out;
}
freerdp_dsp_context_reset_adpcm(opensles->dsp_context);
if (opensles->rdpcontext)
setChannelError(opensles->rdpcontext, CHANNEL_RC_NO_MEMORY,
"audin_opensles_thread_func reported an error");
goto out;
}
while (1)
{
status = WaitForSingleObject(opensles->stopEvent, 0);
status = WaitForSingleObject(opensles->stopEvent, 0);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
break;
}
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
break;
}
if (status == WAIT_OBJECT_0)
break;
size_t encoded_size;
void *encoded_data;
if (status == WAIT_OBJECT_0)
break;
rc = android_RecIn(opensles->stream, buffer.s, raw_size);
if (rc < 0)
{
WLog_ERR(TAG, "android_RecIn %d", rc);
continue;
}
assert(rc == raw_size);
if (opensles->format == WAVE_FORMAT_ADPCM)
{
if (!opensles->dsp_context->encode_ms_adpcm(opensles->dsp_context,
buffer.b, rc, opensles->channels, opensles->block_size))
{
error = ERROR_INTERNAL_ERROR;
break;
}
error = opensles->receive(&opensles->format,
buffer.v, raw_size, opensles->user_data);
encoded_data = opensles->dsp_context->adpcm_buffer;
encoded_size = opensles->dsp_context->adpcm_size;
}
else if (opensles->format == WAVE_FORMAT_DVI_ADPCM)
{
if (!opensles->dsp_context->encode_ima_adpcm(opensles->dsp_context,
buffer.b, rc,
opensles->channels, opensles->block_size))
{
error = ERROR_INTERNAL_ERROR;
break;
}
encoded_data = opensles->dsp_context->adpcm_buffer;
encoded_size = opensles->dsp_context->adpcm_size;
}
else
{
encoded_data = buffer.v;
encoded_size = rc;
}
error = opensles->receive(encoded_data, encoded_size, opensles->user_data);
if (error)
break;
}
@ -185,7 +144,6 @@ out:
static UINT audin_opensles_free(IAudinDevice* device)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p", (void*) device);
/* The function may have been called out of order,
@ -194,53 +152,38 @@ static UINT audin_opensles_free(IAudinDevice* device)
return CHANNEL_RC_OK;
assert(opensles);
assert(opensles->dsp_context);
assert(!opensles->stream);
freerdp_dsp_context_free(opensles->dsp_context);
free(opensles->device_name);
free(opensles);
return CHANNEL_RC_OK;
}
static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* format)
static BOOL audin_opensles_format_supported(IAudinDevice* device,
const AUDIO_FORMAT* format)
{
#ifdef WITH_DEBUG_DVC
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
#endif
DEBUG_DVC("device=%p, format=%p", (void*) opensles, (void*) format);
assert(format);
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM: /* PCM */
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels >= 1 && format->nChannels <= 2))
{
return TRUE;
}
break;
/* TODO: Deactivated format, does not work, find out why */
// case WAVE_FORMAT_ADPCM: /* IMA ADPCM */
case WAVE_FORMAT_DVI_ADPCM:
if ((format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
(format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels >= 1 && format->nChannels <= 2))
{
return TRUE;
}
break;
default:
DEBUG_DVC("Encoding '%s' [0x%04X"PRIX16"] not supported",
rdpsnd_get_audio_tag_string(format->wFormatTag),
format->wFormatTag);
rdpsnd_get_audio_tag_string(format->wFormatTag),
format->wFormatTag);
break;
}
@ -253,14 +196,11 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* f
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_opensles_set_format(IAudinDevice* device,
audinFormat* format, UINT32 FramesPerPacket)
const AUDIO_FORMAT* format, UINT32 FramesPerPacket)
{
int bs;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p, format=%p, FramesPerPacket=%"PRIu32"",
(void*) device, (void*) format, FramesPerPacket);
assert(format);
/* The function may have been called out of order, ignore
@ -268,52 +208,42 @@ static UINT audin_opensles_set_format(IAudinDevice* device,
if (!opensles)
return CHANNEL_RC_OK;
opensles->format = *format;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
opensles->frames_per_packet = FramesPerPacket;
switch (format->wBitsPerSample)
{
case 4:
opensles->bytes_per_channel = 1;
break;
case 8:
opensles->bytes_per_channel = 1;
break;
case 16:
opensles->bytes_per_channel = 2;
break;
default:
return ERROR_UNSUPPORTED_TYPE;
}
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
opensles->bytes_per_channel = 2;
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
opensles->frames_per_packet =
(FramesPerPacket * format->nChannels * 2 /
bs + 1) * bs / (format->nChannels * 2);
break;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
opensles->frames_per_packet = FramesPerPacket;
break;
default:
WLog_ERR(TAG, "Encoding '%"PRIu16"' [%04"PRIX16"] not supported",
format->wFormatTag,
format->wFormatTag);
format->wFormatTag,
format->wFormatTag);
return ERROR_UNSUPPORTED_TYPE;
}
opensles->rate = format->nSamplesPerSec;
opensles->channels = format->nChannels;
opensles->format = format->wFormatTag;
opensles->block_size = format->nBlockAlign;
DEBUG_DVC("aligned frames_per_packet=%"PRIu32", block_size=%"PRIu32"",
opensles->frames_per_packet, opensles->block_size);
opensles->frames_per_packet, opensles->block_size);
return CHANNEL_RC_OK;
}
@ -323,25 +253,24 @@ static UINT audin_opensles_set_format(IAudinDevice* device,
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
void* user_data)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p, receive=%p, user_data=%p", (void*) device, (void*) receive, (void*) user_data);
DEBUG_DVC("device=%p, receive=%p, user_data=%p", (void*) device, (void*) receive,
(void*) user_data);
assert(opensles);
/* The function may have been called out of order,
* ignore duplicate open requests. */
if(opensles->stream)
if (opensles->stream)
return CHANNEL_RC_OK;
if(!(opensles->stream = android_OpenRecDevice(
opensles->device_name,
opensles->rate,
opensles->channels,
opensles->frames_per_packet,
opensles->bytes_per_channel * 8)))
if (!(opensles->stream = android_OpenRecDevice(
opensles->device_name,
opensles->format.nSamplesPerSec,
opensles->format.nChannels,
opensles->frames_per_packet,
opensles->format.wBitsPerSample)))
{
WLog_ERR(TAG, "android_OpenRecDevice failed!");
return ERROR_INTERNAL_ERROR;
@ -355,13 +284,14 @@ static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive,
WLog_ERR(TAG, "CreateEvent failed!");
goto error_out;
}
if (!(opensles->thread = CreateThread(NULL, 0,
audin_opensles_thread_func,
opensles, 0, NULL)))
audin_opensles_thread_func, opensles, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
android_CloseRecDevice(opensles->stream);
@ -378,11 +308,9 @@ error_out:
*/
static UINT audin_opensles_close(IAudinDevice* device)
{
UINT error;
UINT error;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p", (void*) device);
assert(opensles);
/* The function may have been called out of order,
@ -396,32 +324,32 @@ static UINT audin_opensles_close(IAudinDevice* device)
assert(opensles->stopEvent);
assert(opensles->thread);
assert(opensles->stream);
SetEvent(opensles->stopEvent);
if (WaitForSingleObject(opensles->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
return error;
}
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error);
return error;
}
CloseHandle(opensles->stopEvent);
CloseHandle(opensles->thread);
android_CloseRecDevice(opensles->stream);
opensles->stopEvent = NULL;
opensles->thread = NULL;
opensles->receive = NULL;
opensles->user_data = NULL;
opensles->stream = NULL;
return CHANNEL_RC_OK;
}
static COMMAND_LINE_ARGUMENT_A audin_opensles_args[] =
{
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "audio device name" },
{
"dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "audio device name"
},
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
@ -431,19 +359,17 @@ static COMMAND_LINE_ARGUMENT_A audin_opensles_args[] =
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
ADDIN_ARGV* args)
ADDIN_ARGV* args)
{
UINT status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p, args=%p", (void*) device, (void*) args);
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
audin_opensles_args, flags, opensles, NULL, NULL);
audin_opensles_args, flags, opensles, NULL, NULL);
if (status < 0)
return status;
@ -455,17 +381,16 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "dev")
{
opensles->device_name = _strdup(arg->Value);
if (!opensles->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
@ -487,15 +412,14 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
* @return 0 on success, otherwise a Win32 error code
*/
UINT freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
AudinOpenSLESDevice* opensles;
UINT error;
DEBUG_DVC("pEntryPoints=%p", (void*) pEntryPoints);
opensles = (AudinOpenSLESDevice*) calloc(1, sizeof(AudinOpenSLESDevice));
if (!opensles)
{
WLog_ERR(TAG, "calloc failed!");
@ -508,7 +432,6 @@ UINT freerdp_audin_client_subsystem_entry(
opensles->iface.Close = audin_opensles_close;
opensles->iface.Free = audin_opensles_free;
opensles->rdpcontext = pEntryPoints->rdpcontext;
args = pEntryPoints->args;
if ((error = audin_opensles_parse_addin_args(opensles, args)))
@ -517,14 +440,6 @@ UINT freerdp_audin_client_subsystem_entry(
goto error_out;
}
opensles->dsp_context = freerdp_dsp_context_new();
if (!opensles->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*) opensles)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
@ -533,7 +448,6 @@ UINT freerdp_audin_client_subsystem_entry(
return CHANNEL_RC_OK;
error_out:
freerdp_dsp_context_free(opensles->dsp_context);
free(opensles);
return error;
}

View File

@ -47,7 +47,6 @@
#include <sys/ioctl.h>
#include <freerdp/addin.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/rdpsnd.h>
#include "audin_main.h"
@ -56,12 +55,10 @@ typedef struct _AudinOSSDevice
{
IAudinDevice iface;
FREERDP_DSP_CONTEXT* dsp_context;
HANDLE thread;
HANDLE stopEvent;
audinFormat format;
AUDIO_FORMAT format;
UINT32 FramesPerPacket;
int dev_unit;
@ -76,7 +73,7 @@ typedef struct _AudinOSSDevice
WLog_ERR(TAG, "%s: %i - %s\n", _text, _error, strerror(_error));
static int audin_oss_get_format(audinFormat* format)
static int audin_oss_get_format(const AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
@ -94,25 +91,17 @@ static int audin_oss_get_format(audinFormat* format)
case WAVE_FORMAT_ALAW:
return AFMT_A_LAW;
#if 0 /* This does not work on my desktop. */
case WAVE_FORMAT_MULAW:
return AFMT_MU_LAW;
#endif
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
return AFMT_S16_LE;
}
return 0;
}
static BOOL audin_oss_format_supported(IAudinDevice* device,
audinFormat* format)
const AUDIO_FORMAT* format)
{
int req_fmt = 0;
if (device == NULL || format == NULL)
return FALSE;
@ -127,21 +116,14 @@ static BOOL audin_oss_format_supported(IAudinDevice* device,
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
if (format->nSamplesPerSec > 48000 ||
format->wBitsPerSample != 4 ||
(format->nChannels != 1 && format->nChannels != 2))
return FALSE;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
return TRUE;
break;
default:
return FALSE;
}
req_fmt = audin_oss_get_format(format);
if (req_fmt == 0)
return FALSE;
return TRUE;
}
@ -150,7 +132,7 @@ static BOOL audin_oss_format_supported(IAudinDevice* device,
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_oss_set_format(IAudinDevice* device, audinFormat* format,
static UINT audin_oss_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
{
AudinOSSDevice* oss = (AudinOSSDevice*)device;
@ -159,17 +141,7 @@ static UINT audin_oss_set_format(IAudinDevice* device, audinFormat* format,
return ERROR_INVALID_PARAMETER;
oss->FramesPerPacket = FramesPerPacket;
CopyMemory(&(oss->format), format, sizeof(audinFormat));
switch (format->wFormatTag)
{
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
oss->FramesPerPacket *= 4; /* Compression ratio. */
oss->format.wBitsPerSample *= 4;
break;
}
oss->format = *format;
return CHANNEL_RC_OK;
}
@ -178,8 +150,9 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
char dev_name[PATH_MAX] = "/dev/dsp";
char mixer_name[PATH_MAX] = "/dev/mixer";
int pcm_handle = -1, mixer_handle;
BYTE* buffer = NULL, *encoded_data = NULL;
int tmp, buffer_size, encoded_size;
BYTE* buffer = NULL;
int tmp;
size_t buffer_size;
AudinOSSDevice* oss = (AudinOSSDevice*)arg;
UINT error = 0;
DWORD status;
@ -271,8 +244,6 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
goto err_out;
}
freerdp_dsp_context_reset_adpcm(oss->dsp_context);
while (1)
{
status = WaitForSingleObject(oss->stopEvent, 0);
@ -299,40 +270,7 @@ static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
if (tmp < buffer_size) /* Not enouth data. */
continue;
/* Process. */
switch (oss->format.wFormatTag)
{
case WAVE_FORMAT_ADPCM:
if (!oss->dsp_context->encode_ms_adpcm(oss->dsp_context,
buffer, buffer_size, oss->format.nChannels, oss->format.nBlockAlign))
{
error = ERROR_INTERNAL_ERROR;
goto err_out;
}
encoded_data = oss->dsp_context->adpcm_buffer;
encoded_size = oss->dsp_context->adpcm_size;
break;
case WAVE_FORMAT_DVI_ADPCM:
if (!oss->dsp_context->encode_ima_adpcm(oss->dsp_context,
buffer, buffer_size, oss->format.nChannels, oss->format.nBlockAlign))
{
error = ERROR_INTERNAL_ERROR;
goto err_out;
}
encoded_data = oss->dsp_context->adpcm_buffer;
encoded_size = oss->dsp_context->adpcm_size;
break;
default:
encoded_data = buffer;
encoded_size = buffer_size;
break;
}
if ((error = oss->receive(encoded_data, encoded_size, oss->user_data)))
if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
{
WLog_ERR(TAG, "oss->receive failed with error %"PRIu32"", error);
break;
@ -438,12 +376,11 @@ static UINT audin_oss_free(IAudinDevice* device)
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
}
freerdp_dsp_context_free(oss->dsp_context);
free(oss);
return CHANNEL_RC_OK;
}
COMMAND_LINE_ARGUMENT_A audin_oss_args[] =
static COMMAND_LINE_ARGUMENT_A audin_oss_args[] =
{
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
@ -552,15 +489,6 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
goto error_out;
}
oss->dsp_context = freerdp_dsp_context_new();
if (!oss->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*) oss)))
{
@ -570,7 +498,6 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS
return CHANNEL_RC_OK;
error_out:
freerdp_dsp_context_free(oss->dsp_context);
free(oss);
return error;
}

View File

@ -34,7 +34,7 @@
#include <freerdp/types.h>
#include <freerdp/addin.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/codec/audio.h>
#include <freerdp/client/audin.h>
#include "audin_main.h"
@ -49,14 +49,10 @@ typedef struct _AudinPulseDevice
pa_context* context;
pa_sample_spec sample_spec;
pa_stream* stream;
int format;
int block_size;
AUDIO_FORMAT format;
FREERDP_DSP_CONTEXT* dsp_context;
int bytes_per_frame;
BYTE* buffer;
int buffer_frames;
size_t bytes_per_frame;
size_t buffer_frames;
AudinReceive receive;
void* user_data;
@ -172,12 +168,11 @@ static UINT audin_pulse_free(IAudinDevice* device)
pulse->mainloop = NULL;
}
freerdp_dsp_context_free(pulse->dsp_context);
free(pulse);
return CHANNEL_RC_OK;
}
static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* format)
static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
{
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
@ -186,7 +181,7 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* form
switch (format->wFormatTag)
{
case 1: /* PCM */
case WAVE_FORMAT_PCM:
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
@ -197,8 +192,8 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* form
break;
case 6: /* A-LAW */
case 7: /* U-LAW */
case WAVE_FORMAT_ALAW: /* A-LAW */
case WAVE_FORMAT_MULAW: /* U-LAW */
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8) &&
@ -209,15 +204,8 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* form
break;
case 0x11: /* IMA ADPCM */
if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
default:
return FALSE;
}
return FALSE;
@ -228,10 +216,9 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* form
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_pulse_set_format(IAudinDevice* device, audinFormat* format,
static UINT audin_pulse_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
{
int bs;
pa_sample_spec sample_spec = { 0 };
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
@ -239,16 +226,14 @@ static UINT audin_pulse_set_format(IAudinDevice* device, audinFormat* format,
return ERROR_INVALID_PARAMETER;
if (FramesPerPacket > 0)
{
pulse->frames_per_packet = FramesPerPacket;
}
sample_spec.rate = format->nSamplesPerSec;
sample_spec.channels = format->nChannels;
switch (format->wFormatTag)
{
case 1: /* PCM */
case WAVE_FORMAT_PCM: /* PCM */
switch (format->wBitsPerSample)
{
case 8:
@ -258,31 +243,27 @@ static UINT audin_pulse_set_format(IAudinDevice* device, audinFormat* format,
case 16:
sample_spec.format = PA_SAMPLE_S16LE;
break;
default:
return ERROR_INTERNAL_ERROR;
}
break;
case 6: /* A-LAW */
case WAVE_FORMAT_ALAW: /* A-LAW */
sample_spec.format = PA_SAMPLE_ALAW;
break;
case 7: /* U-LAW */
case WAVE_FORMAT_MULAW: /* U-LAW */
sample_spec.format = PA_SAMPLE_ULAW;
break;
case 0x11: /* IMA ADPCM */
sample_spec.format = PA_SAMPLE_S16LE;
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
pulse->frames_per_packet = (pulse->frames_per_packet * format->nChannels * 2 /
bs + 1) * bs / (format->nChannels * 2);
DEBUG_DVC("aligned FramesPerPacket=%"PRIu32"",
pulse->frames_per_packet);
break;
default:
return ERROR_INTERNAL_ERROR;
}
pulse->sample_spec = sample_spec;
pulse->format = format->wFormatTag;
pulse->block_size = format->nBlockAlign;
pulse->format = *format;
return CHANNEL_RC_OK;
}
@ -313,81 +294,15 @@ static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
{
int frames;
int cframes;
const void* data;
const BYTE* src;
int encoded_size;
BYTE* encoded_data;
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
UINT error = CHANNEL_RC_OK;
/* There is a race condition here where we may receive this callback
* before the buffer has been set up in the main code. It's probably
* possible to fix with additional locking, but it's easier just to
* ignore input until the buffer is ready.
*/
if (pulse->buffer == NULL)
{
/* WLog_ERR(TAG, "%s: ignoring input, pulse buffer not ready.\n", __func__); */
return;
}
pa_stream_peek(stream, &data, &length);
frames = length / pulse->bytes_per_frame;
DEBUG_DVC("length %"PRIdz" frames %d", length, frames);
src = (const BYTE*) data;
while (frames > 0)
{
cframes = pulse->frames_per_packet - pulse->buffer_frames;
if (cframes > frames)
cframes = frames;
memcpy(pulse->buffer + pulse->buffer_frames * pulse->bytes_per_frame,
src, cframes * pulse->bytes_per_frame);
pulse->buffer_frames += cframes;
if (pulse->buffer_frames >= pulse->frames_per_packet)
{
if (pulse->format == 0x11)
{
if (!pulse->dsp_context->encode_ima_adpcm(pulse->dsp_context,
pulse->buffer, pulse->buffer_frames * pulse->bytes_per_frame,
pulse->sample_spec.channels, pulse->block_size))
{
error = ERROR_INTERNAL_ERROR;
break;
}
encoded_data = pulse->dsp_context->adpcm_buffer;
encoded_size = pulse->dsp_context->adpcm_size;
}
else
{
encoded_data = pulse->buffer;
encoded_size = pulse->buffer_frames * pulse->bytes_per_frame;
}
DEBUG_DVC("encoded %d [%d] to %d [%X]",
pulse->buffer_frames, pulse->bytes_per_frame, encoded_size,
pulse->format);
error = pulse->receive(encoded_data, encoded_size, pulse->user_data);
pulse->buffer_frames = 0;
if (!error)
break;
}
src += cframes * pulse->bytes_per_frame;
frames -= cframes;
}
error = pulse->receive(&pulse->format, data, length, pulse->user_data);
pa_stream_drop(stream);
if (error && pulse->rdpcontext)
setChannelError(pulse->rdpcontext, error, "audin_oss_thread_func reported an error");
setChannelError(pulse->rdpcontext, error, "audin_pulse_thread_func reported an error");
}
@ -411,14 +326,6 @@ static UINT audin_pulse_close(IAudinDevice* device)
pulse->receive = NULL;
pulse->user_data = NULL;
if (pulse->buffer)
{
free(pulse->buffer);
pulse->buffer = NULL;
pulse->buffer_frames = 0;
}
return CHANNEL_RC_OK;
}
@ -439,7 +346,6 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
if (!pulse->sample_spec.rate || pulse->stream)
return ERROR_INVALID_PARAMETER;
pulse->buffer = NULL;
pulse->receive = receive;
pulse->user_data = user_data;
pa_threaded_mainloop_lock(pulse->mainloop);
@ -496,15 +402,6 @@ static UINT audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
}
pa_threaded_mainloop_unlock(pulse->mainloop);
freerdp_dsp_context_reset_adpcm(pulse->dsp_context);
pulse->buffer = calloc(pulse->frames_per_packet, pulse->bytes_per_frame);
if (!pulse->buffer)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
pulse->buffer_frames = 0;
DEBUG_DVC("connected");
return CHANNEL_RC_OK;
@ -597,15 +494,6 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
goto error_out;
}
pulse->dsp_context = freerdp_dsp_context_new();
if (!pulse->dsp_context)
{
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
pulse->mainloop = pa_threaded_mainloop_new();
if (!pulse->mainloop)

View File

@ -1,464 +1,502 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - WinMM implementation
*
* Copyright 2013 Zhang Zhaolong <zhangzl2013@126.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* 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 <Windows.h>
#include <MMSystem.h>
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <freerdp/addin.h>
#include <freerdp/client/audin.h>
#include "audin_main.h"
typedef struct _AudinWinmmDevice
{
IAudinDevice iface;
char* device_name;
AudinReceive receive;
void* user_data;
HANDLE thread;
HANDLE stopEvent;
HWAVEIN hWaveIn;
PWAVEFORMATEX *ppwfx;
PWAVEFORMATEX pwfx_cur;
UINT32 ppwfx_size;
UINT32 cFormats;
UINT32 frames_per_packet;
rdpContext* rdpcontext;
} AudinWinmmDevice;
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) dwInstance;
PWAVEHDR pWaveHdr;
UINT error = CHANNEL_RC_OK;
MMRESULT mmResult;
switch(uMsg)
{
case WIM_CLOSE:
break;
case WIM_DATA:
pWaveHdr = (WAVEHDR *)dwParam1;
if (WHDR_DONE == (WHDR_DONE & pWaveHdr->dwFlags))
{
if (pWaveHdr->dwBytesRecorded
&& !(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
{
if ((error = winmm->receive(pWaveHdr->lpData, pWaveHdr->dwBytesRecorded, winmm->user_data)))
break;
mmResult = waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
if (mmResult != MMSYSERR_NOERROR)
error = ERROR_INTERNAL_ERROR;
}
}
break;
case WIM_OPEN:
break;
default:
break;
}
if (error && winmm->rdpcontext)
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
}
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) arg;
char *buffer;
int size, i;
WAVEHDR waveHdr[4];
DWORD status;
MMRESULT rc;
if (!winmm->hWaveIn)
{
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur,
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm, CALLBACK_FUNCTION))
{
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
return 0;
}
}
size = (winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet + 7) / 8;
for (i = 0; i < 4; i++)
{
buffer = (char *) malloc(size);
if (!buffer)
return CHANNEL_RC_NO_MEMORY;
waveHdr[i].dwBufferLength = size;
waveHdr[i].dwFlags = 0;
waveHdr[i].lpData = buffer;
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInPrepareHeader failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
}
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInAddBuffer failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
}
}
rc = waveInStart(winmm->hWaveIn);
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInStart failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
}
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
if (status == WAIT_FAILED)
{
DEBUG_DVC("WaitForSingleObject failed.");
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
}
rc = waveInReset(winmm->hWaveIn);
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInReset failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
}
for (i = 0; i < 4; i++)
{
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInUnprepareHeader failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
}
free(waveHdr[i].lpData);
}
rc = waveInClose(winmm->hWaveIn);
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInClose failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, "audin_winmm_thread_func reported an error");
}
winmm->hWaveIn = NULL;
return 0;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_free(IAudinDevice* device)
{
UINT32 i;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
for (i = 0; i < winmm->cFormats; i++)
{
free(winmm->ppwfx[i]);
}
free(winmm->ppwfx);
free(winmm->device_name);
free(winmm);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_close(IAudinDevice* device)
{
DWORD status;
UINT error = CHANNEL_RC_OK;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
SetEvent(winmm->stopEvent);
status = WaitForSingleObject(winmm->thread, INFINITE);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
return error;
}
CloseHandle(winmm->thread);
CloseHandle(winmm->stopEvent);
winmm->thread = NULL;
winmm->stopEvent = NULL;
winmm->receive = NULL;
winmm->user_data = NULL;
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
{
UINT32 i;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
winmm->frames_per_packet = FramesPerPacket;
for (i = 0; i < winmm->cFormats; i++)
{
if (winmm->ppwfx[i]->wFormatTag == format->wFormatTag
&& winmm->ppwfx[i]->nChannels == format->nChannels
&& winmm->ppwfx[i]->wBitsPerSample == format->wBitsPerSample)
{
winmm->pwfx_cur = winmm->ppwfx[i];
break;
}
}
return CHANNEL_RC_OK;
}
static BOOL audin_winmm_format_supported(IAudinDevice* device, audinFormat* format)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
PWAVEFORMATEX pwfx;
BYTE *data;
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
if (!pwfx)
return FALSE;
pwfx->cbSize = format->cbSize;
pwfx->wFormatTag = format->wFormatTag;
pwfx->nChannels = format->nChannels;
pwfx->nSamplesPerSec = format->nSamplesPerSec;
pwfx->nBlockAlign = format->nBlockAlign;
pwfx->wBitsPerSample = format->wBitsPerSample;
data = (BYTE *)pwfx + sizeof(WAVEFORMATEX);
memcpy(data, format->data, format->cbSize);
if (pwfx->wFormatTag == WAVE_FORMAT_PCM)
{
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
if (MMSYSERR_NOERROR == waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0, WAVE_FORMAT_QUERY))
{
if (winmm->cFormats >= winmm->ppwfx_size)
{
PWAVEFORMATEX *tmp_ppwfx;
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
if (!tmp_ppwfx)
return FALSE;
winmm->ppwfx_size *= 2;
winmm->ppwfx = tmp_ppwfx;
}
winmm->ppwfx[winmm->cFormats++] = pwfx;
return TRUE;
}
}
free(pwfx);
return FALSE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
winmm->receive = receive;
winmm->user_data = user_data;
if (!(winmm->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
if (!(winmm->thread = CreateThread(NULL, 0, audin_winmm_thread_func, winmm, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(winmm->stopEvent);
winmm->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
return CHANNEL_RC_OK;
}
static COMMAND_LINE_ARGUMENT_A audin_winmm_args[] =
{
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* args)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_winmm_args, flags, winmm, NULL, NULL);
arg = audin_winmm_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "dev")
{
winmm->device_name = _strdup(arg->Value);
if (!winmm->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
#ifdef BUILTIN_CHANNELS
#define freerdp_audin_client_subsystem_entry winmm_freerdp_audin_client_subsystem_entry
#else
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
#endif
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
AudinWinmmDevice* winmm;
UINT error;
winmm = (AudinWinmmDevice*) calloc(1, sizeof(AudinWinmmDevice));
if (!winmm)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
winmm->iface.Open = audin_winmm_open;
winmm->iface.FormatSupported = audin_winmm_format_supported;
winmm->iface.SetFormat = audin_winmm_set_format;
winmm->iface.Close = audin_winmm_close;
winmm->iface.Free = audin_winmm_free;
winmm->rdpcontext = pEntryPoints->rdpcontext;
args = pEntryPoints->args;
if ((error = audin_winmm_parse_addin_args(winmm, args)))
{
WLog_ERR(TAG, "audin_winmm_parse_addin_args failed with error %"PRIu32"!", error);
goto error_out;
}
if (!winmm->device_name)
{
winmm->device_name = _strdup("default");
if (!winmm->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
}
winmm->ppwfx_size = 10;
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
if (!winmm->ppwfx)
{
WLog_ERR(TAG, "malloc failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
free(winmm->ppwfx);
free(winmm->device_name);
free(winmm);
return error;
}
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - WinMM implementation
*
* Copyright 2013 Zhang Zhaolong <zhangzl2013@126.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* 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 <Windows.h>
#include <MMSystem.h>
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <freerdp/addin.h>
#include <freerdp/client/audin.h>
#include "audin_main.h"
typedef struct _AudinWinmmDevice
{
IAudinDevice iface;
char* device_name;
AudinReceive receive;
void* user_data;
HANDLE thread;
HANDLE stopEvent;
HWAVEIN hWaveIn;
PWAVEFORMATEX* ppwfx;
PWAVEFORMATEX pwfx_cur;
UINT32 ppwfx_size;
UINT32 cFormats;
UINT32 frames_per_packet;
rdpContext* rdpcontext;
} AudinWinmmDevice;
static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) dwInstance;
PWAVEHDR pWaveHdr;
UINT error = CHANNEL_RC_OK;
MMRESULT mmResult;
switch (uMsg)
{
case WIM_CLOSE:
break;
case WIM_DATA:
pWaveHdr = (WAVEHDR*)dwParam1;
if (WHDR_DONE == (WHDR_DONE & pWaveHdr->dwFlags))
{
if (pWaveHdr->dwBytesRecorded
&& !(WaitForSingleObject(winmm->stopEvent, 0) == WAIT_OBJECT_0))
{
AUDIO_FORMAT format;
format.cbSize = winmm->pwfx_cur->cbSize;
format.nBlockAlign = winmm->pwfx_cur->nBlockAlign;
format.nAvgBytesPerSec = winmm->pwfx_cur->nAvgBytesPerSec;
format.nChannels = winmm->pwfx_cur->nChannels;
format.nSamplesPerSec = winmm->pwfx_cur->nSamplesPerSec;
format.wBitsPerSample = winmm->pwfx_cur->wBitsPerSample;
format.wFormatTag = winmm->pwfx_cur->wFormatTag;
if ((error = winmm->receive(&format, pWaveHdr->lpData, pWaveHdr->dwBytesRecorded,
winmm->user_data)))
break;
mmResult = waveInAddBuffer(hWaveIn, pWaveHdr, sizeof(WAVEHDR));
if (mmResult != MMSYSERR_NOERROR)
error = ERROR_INTERNAL_ERROR;
}
}
break;
case WIM_OPEN:
break;
default:
break;
}
if (error && winmm->rdpcontext)
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
}
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) arg;
char* buffer;
int size, i;
WAVEHDR waveHdr[4];
DWORD status;
MMRESULT rc;
if (!winmm->hWaveIn)
{
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur,
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm, CALLBACK_FUNCTION))
{
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
return ERROR_INTERNAL_ERROR;
}
}
size = (winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
7) / 8;
for (i = 0; i < 4; i++)
{
buffer = (char*) malloc(size);
if (!buffer)
return CHANNEL_RC_NO_MEMORY;
waveHdr[i].dwBufferLength = size;
waveHdr[i].dwFlags = 0;
waveHdr[i].lpData = buffer;
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInPrepareHeader failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
}
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInAddBuffer failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
}
}
rc = waveInStart(winmm->hWaveIn);
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInStart failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
}
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
if (status == WAIT_FAILED)
{
DEBUG_DVC("WaitForSingleObject failed.");
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
}
rc = waveInReset(winmm->hWaveIn);
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInReset failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
}
for (i = 0; i < 4; i++)
{
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInUnprepareHeader failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
}
free(waveHdr[i].lpData);
}
rc = waveInClose(winmm->hWaveIn);
if (MMSYSERR_NOERROR != rc)
{
DEBUG_DVC("waveInClose failed. %"PRIu32"", rc);
if (winmm->rdpcontext)
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
"audin_winmm_thread_func reported an error");
}
winmm->hWaveIn = NULL;
return 0;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_free(IAudinDevice* device)
{
UINT32 i;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
for (i = 0; i < winmm->cFormats; i++)
{
free(winmm->ppwfx[i]);
}
free(winmm->ppwfx);
free(winmm->device_name);
free(winmm);
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_close(IAudinDevice* device)
{
DWORD status;
UINT error = CHANNEL_RC_OK;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
SetEvent(winmm->stopEvent);
status = WaitForSingleObject(winmm->thread, INFINITE);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
return error;
}
CloseHandle(winmm->thread);
CloseHandle(winmm->stopEvent);
winmm->thread = NULL;
winmm->stopEvent = NULL;
winmm->receive = NULL;
winmm->user_data = NULL;
return error;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
UINT32 FramesPerPacket)
{
UINT32 i;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
winmm->frames_per_packet = FramesPerPacket;
for (i = 0; i < winmm->cFormats; i++)
{
if (winmm->ppwfx[i]->wFormatTag == format->wFormatTag
&& winmm->ppwfx[i]->nChannels == format->nChannels
&& winmm->ppwfx[i]->wBitsPerSample == format->wBitsPerSample)
{
winmm->pwfx_cur = winmm->ppwfx[i];
break;
}
}
return CHANNEL_RC_OK;
}
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
PWAVEFORMATEX pwfx;
BYTE* data;
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
if (!pwfx)
return FALSE;
pwfx->cbSize = format->cbSize;
pwfx->wFormatTag = format->wFormatTag;
pwfx->nChannels = format->nChannels;
pwfx->nSamplesPerSec = format->nSamplesPerSec;
pwfx->nBlockAlign = format->nBlockAlign;
pwfx->wBitsPerSample = format->wBitsPerSample;
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
memcpy(data, format->data, format->cbSize);
if (pwfx->wFormatTag == WAVE_FORMAT_PCM)
{
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
if (MMSYSERR_NOERROR == waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0, WAVE_FORMAT_QUERY))
{
if (winmm->cFormats >= winmm->ppwfx_size)
{
PWAVEFORMATEX* tmp_ppwfx;
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
if (!tmp_ppwfx)
return FALSE;
winmm->ppwfx_size *= 2;
winmm->ppwfx = tmp_ppwfx;
}
winmm->ppwfx[winmm->cFormats++] = pwfx;
return TRUE;
}
}
free(pwfx);
return FALSE;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
winmm->receive = receive;
winmm->user_data = user_data;
if (!(winmm->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
if (!(winmm->thread = CreateThread(NULL, 0,
audin_winmm_thread_func, winmm, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(winmm->stopEvent);
winmm->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
return CHANNEL_RC_OK;
}
static COMMAND_LINE_ARGUMENT_A audin_winmm_args[] =
{
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* args)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, audin_winmm_args, flags,
winmm, NULL, NULL);
arg = audin_winmm_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "dev")
{
winmm->device_name = _strdup(arg->Value);
if (!winmm->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
#ifdef BUILTIN_CHANNELS
#define freerdp_audin_client_subsystem_entry winmm_freerdp_audin_client_subsystem_entry
#else
#define freerdp_audin_client_subsystem_entry FREERDP_API freerdp_audin_client_subsystem_entry
#endif
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
AudinWinmmDevice* winmm;
UINT error;
winmm = (AudinWinmmDevice*) calloc(1, sizeof(AudinWinmmDevice));
if (!winmm)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
winmm->iface.Open = audin_winmm_open;
winmm->iface.FormatSupported = audin_winmm_format_supported;
winmm->iface.SetFormat = audin_winmm_set_format;
winmm->iface.Close = audin_winmm_close;
winmm->iface.Free = audin_winmm_free;
winmm->rdpcontext = pEntryPoints->rdpcontext;
args = pEntryPoints->args;
if ((error = audin_winmm_parse_addin_args(winmm, args)))
{
WLog_ERR(TAG, "audin_winmm_parse_addin_args failed with error %"PRIu32"!", error);
goto error_out;
}
if (!winmm->device_name)
{
winmm->device_name = _strdup("default");
if (!winmm->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
}
winmm->ppwfx_size = 10;
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
if (!winmm->ppwfx)
{
WLog_ERR(TAG, "malloc failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %"PRIu32"!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
free(winmm->ppwfx);
free(winmm->device_name);
free(winmm);
return error;
}

View File

@ -339,10 +339,9 @@ static UINT audin_server_recv_data(audin_server* audin, wStream* s,
AUDIO_FORMAT* format;
int sbytes_per_sample;
int sbytes_per_frame;
BYTE* src;
int size;
int frames;
UINT success = CHANNEL_RC_OK;
wStream* out;
UINT success = ERROR_INTERNAL_ERROR;
if (audin->context.selected_client_format < 0)
{
@ -351,53 +350,26 @@ static UINT audin_server_recv_data(audin_server* audin, wStream* s,
return ERROR_INVALID_DATA;
}
out = Stream_New(NULL, 4096);
if (!out)
return ERROR_OUTOFMEMORY;
format = &audin->context.client_formats[audin->context.selected_client_format];
if (format->wFormatTag == WAVE_FORMAT_ADPCM)
if (freerdp_dsp_decode(audin->dsp_context, format, Stream_Pointer(s), length, out))
{
audin->dsp_context->decode_ms_adpcm(audin->dsp_context,
Stream_Pointer(s), length, format->nChannels, format->nBlockAlign);
size = audin->dsp_context->adpcm_size;
src = audin->dsp_context->adpcm_buffer;
sbytes_per_sample = 2;
sbytes_per_frame = format->nChannels * 2;
}
else if (format->wFormatTag == WAVE_FORMAT_DVI_ADPCM)
{
audin->dsp_context->decode_ima_adpcm(audin->dsp_context,
Stream_Pointer(s), length, format->nChannels, format->nBlockAlign);
size = audin->dsp_context->adpcm_size;
src = audin->dsp_context->adpcm_buffer;
sbytes_per_sample = 2;
sbytes_per_frame = format->nChannels * 2;
}
else
{
size = length;
src = Stream_Pointer(s);
Stream_SealLength(out);
sbytes_per_sample = format->wBitsPerSample / 8;
sbytes_per_frame = format->nChannels * sbytes_per_sample;
frames = Stream_Length(out) / sbytes_per_frame;
IFCALLRET(audin->context.ReceiveSamples, success, &audin->context, Stream_Buffer(out), frames);
if (success)
WLog_ERR(TAG, "context.ReceiveSamples failed with error %"PRIu32"", success);
}
if (format->nSamplesPerSec == audin->context.dst_format.nSamplesPerSec
&& format->nChannels == audin->context.dst_format.nChannels)
{
frames = size / sbytes_per_frame;
}
else
{
audin->dsp_context->resample(audin->dsp_context, src, sbytes_per_sample,
format->nChannels, format->nSamplesPerSec, size / sbytes_per_frame,
audin->context.dst_format.nChannels, audin->context.dst_format.nSamplesPerSec);
frames = audin->dsp_context->resampled_frames;
src = audin->dsp_context->resampled_buffer;
}
IFCALLRET(audin->context.ReceiveSamples, success, &audin->context, src, frames);
if (success)
WLog_ERR(TAG, "context.ReceiveSamples failed with error %"PRIu32"", success);
Stream_Free(out, TRUE);
return success;
}
@ -694,7 +666,7 @@ audin_server_context* audin_server_context_new(HANDLE vcm)
audin->context.SelectFormat = audin_server_select_format;
audin->context.Open = audin_server_open;
audin->context.Close = audin_server_close;
audin->dsp_context = freerdp_dsp_context_new();
audin->dsp_context = freerdp_dsp_context_new(FALSE);
if (!audin->dsp_context)
{

View File

@ -23,11 +23,8 @@ set(${MODULE_PREFIX}_SRCS
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntryEx")
target_link_libraries(${MODULE_NAME} winpr freerdp ${CMAKE_THREAD_LIBS_INIT})
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
if(WITH_OSS)

View File

@ -47,22 +47,18 @@ struct rdpsnd_alsa_plugin
{
rdpsndDevicePlugin device;
int latency;
int wformat;
int block_size;
UINT32 latency;
AUDIO_FORMAT aformat;
char* device_name;
snd_pcm_t* pcm_handle;
snd_mixer_t* mixer_handle;
UINT32 source_rate;
UINT32 actual_rate;
UINT32 wLocalTimeClose;
snd_pcm_format_t format;
UINT32 source_channels;
UINT32 actual_channels;
int bytes_per_channel;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
FREERDP_DSP_CONTEXT* dsp_context;
};
#define SND_PCM_CHECK(_func, _status) \
@ -77,33 +73,25 @@ static int rdpsnd_alsa_set_hw_params(rdpsndAlsaPlugin* alsa)
int status;
snd_pcm_hw_params_t* hw_params;
snd_pcm_uframes_t buffer_size_max;
status = snd_pcm_hw_params_malloc(&hw_params);
SND_PCM_CHECK("snd_pcm_hw_params_malloc", status);
status = snd_pcm_hw_params_any(alsa->pcm_handle, hw_params);
SND_PCM_CHECK("snd_pcm_hw_params_any", status);
/* Set interleaved read/write access */
status = snd_pcm_hw_params_set_access(alsa->pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
SND_PCM_CHECK("snd_pcm_hw_params_set_access", status);
/* Set sample format */
status = snd_pcm_hw_params_set_format(alsa->pcm_handle, hw_params, alsa->format);
SND_PCM_CHECK("snd_pcm_hw_params_set_format", status);
/* Set sample rate */
status = snd_pcm_hw_params_set_rate_near(alsa->pcm_handle, hw_params, &alsa->actual_rate, NULL);
SND_PCM_CHECK("snd_pcm_hw_params_set_rate_near", status);
/* Set number of channels */
status = snd_pcm_hw_params_set_channels(alsa->pcm_handle, hw_params, alsa->actual_channels);
SND_PCM_CHECK("snd_pcm_hw_params_set_channels", status);
/* Get maximum buffer size */
status = snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size_max);
SND_PCM_CHECK("snd_pcm_hw_params_get_buffer_size_max", status);
/**
* ALSA Parameters
*
@ -122,33 +110,28 @@ static int rdpsnd_alsa_set_hw_params(rdpsndAlsaPlugin* alsa)
* Commonly this is (2 * period_size), but some hardware can do 8 periods per buffer.
* It is also possible for the buffer size to not be an integer multiple of the period size.
*/
int interrupts_per_sec_near = 50;
int bytes_per_sec = (alsa->actual_rate * alsa->bytes_per_channel * alsa->actual_channels);
int bytes_per_sec = (alsa->actual_rate * alsa->aformat.wBitsPerSample / 8 * alsa->actual_channels);
alsa->buffer_size = buffer_size_max;
alsa->period_size = (bytes_per_sec / interrupts_per_sec_near);
if (alsa->period_size > buffer_size_max)
{
WLog_ERR(TAG, "Warning: requested sound buffer size %lu, got %lu instead\n",
alsa->buffer_size, buffer_size_max);
alsa->buffer_size, buffer_size_max);
alsa->period_size = (buffer_size_max / 8);
}
/* Set buffer size */
status = snd_pcm_hw_params_set_buffer_size_near(alsa->pcm_handle, hw_params, &alsa->buffer_size);
SND_PCM_CHECK("snd_pcm_hw_params_set_buffer_size_near", status);
/* Set period size */
status = snd_pcm_hw_params_set_period_size_near(alsa->pcm_handle, hw_params, &alsa->period_size, NULL);
status = snd_pcm_hw_params_set_period_size_near(alsa->pcm_handle, hw_params, &alsa->period_size,
NULL);
SND_PCM_CHECK("snd_pcm_hw_params_set_period_size_near", status);
status = snd_pcm_hw_params(alsa->pcm_handle, hw_params);
SND_PCM_CHECK("snd_pcm_hw_params", status);
snd_pcm_hw_params_free(hw_params);
return 0;
}
@ -156,27 +139,20 @@ static int rdpsnd_alsa_set_sw_params(rdpsndAlsaPlugin* alsa)
{
int status;
snd_pcm_sw_params_t* sw_params;
status = snd_pcm_sw_params_malloc(&sw_params);
SND_PCM_CHECK("snd_pcm_sw_params_malloc", status);
status = snd_pcm_sw_params_current(alsa->pcm_handle, sw_params);
SND_PCM_CHECK("snd_pcm_sw_params_current", status);
status = snd_pcm_sw_params_set_avail_min(alsa->pcm_handle, sw_params, (alsa->bytes_per_channel * alsa->actual_channels));
status = snd_pcm_sw_params_set_avail_min(alsa->pcm_handle, sw_params,
(alsa->aformat.nChannels * alsa->actual_channels));
SND_PCM_CHECK("snd_pcm_sw_params_set_avail_min", status);
status = snd_pcm_sw_params_set_start_threshold(alsa->pcm_handle, sw_params, alsa->block_size);
status = snd_pcm_sw_params_set_start_threshold(alsa->pcm_handle, sw_params, alsa->aformat.nBlockAlign);
SND_PCM_CHECK("snd_pcm_sw_params_set_start_threshold", status);
status = snd_pcm_sw_params(alsa->pcm_handle, sw_params);
SND_PCM_CHECK("snd_pcm_sw_params", status);
snd_pcm_sw_params_free(sw_params);
status = snd_pcm_prepare(alsa->pcm_handle);
SND_PCM_CHECK("snd_pcm_prepare", status);
return 0;
}
@ -185,10 +161,8 @@ static int rdpsnd_alsa_validate_params(rdpsndAlsaPlugin* alsa)
int status;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
status = snd_pcm_get_params(alsa->pcm_handle, &buffer_size, &period_size);
SND_PCM_CHECK("snd_pcm_get_params", status);
return 0;
}
@ -205,15 +179,14 @@ static int rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa)
return rdpsnd_alsa_validate_params(alsa);
}
static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
UINT32 latency)
{
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (format)
{
alsa->source_rate = format->nSamplesPerSec;
alsa->actual_rate = format->nSamplesPerSec;
alsa->source_channels = format->nChannels;
alsa->actual_channels = format->nChannels;
switch (format->wFormatTag)
@ -223,29 +196,28 @@ static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* for
{
case 8:
alsa->format = SND_PCM_FORMAT_S8;
alsa->bytes_per_channel = 1;
break;
case 16:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
break;
default:
return FALSE;
}
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->bytes_per_channel = 2;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
break;
default:
return FALSE;
}
alsa->wformat = format->wFormatTag;
alsa->block_size = format->nBlockAlign;
}
alsa->latency = latency;
return (rdpsnd_alsa_set_params(alsa) == 0);
}
@ -257,6 +229,7 @@ static BOOL rdpsnd_alsa_open_mixer(rdpsndAlsaPlugin* alsa)
return TRUE;
status = snd_mixer_open(&alsa->mixer_handle, 0);
if (status < 0)
{
WLog_ERR(TAG, "snd_mixer_open failed");
@ -264,6 +237,7 @@ static BOOL rdpsnd_alsa_open_mixer(rdpsndAlsaPlugin* alsa)
}
status = snd_mixer_attach(alsa->mixer_handle, alsa->device_name);
if (status < 0)
{
WLog_ERR(TAG, "snd_mixer_attach failed");
@ -272,6 +246,7 @@ static BOOL rdpsnd_alsa_open_mixer(rdpsndAlsaPlugin* alsa)
}
status = snd_mixer_selem_register(alsa->mixer_handle, NULL, NULL);
if (status < 0)
{
WLog_ERR(TAG, "snd_mixer_selem_register failed");
@ -280,6 +255,7 @@ static BOOL rdpsnd_alsa_open_mixer(rdpsndAlsaPlugin* alsa)
}
status = snd_mixer_load(alsa->mixer_handle);
if (status < 0)
{
WLog_ERR(TAG, "snd_mixer_load failed");
@ -290,7 +266,7 @@ static BOOL rdpsnd_alsa_open_mixer(rdpsndAlsaPlugin* alsa)
return TRUE;
}
static BOOL rdpsnd_alsa_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
static BOOL rdpsnd_alsa_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency)
{
int mode;
int status;
@ -301,18 +277,16 @@ static BOOL rdpsnd_alsa_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, i
mode = 0;
/*mode |= SND_PCM_NONBLOCK;*/
status = snd_pcm_open(&alsa->pcm_handle, alsa->device_name, SND_PCM_STREAM_PLAYBACK, mode);
if (status < 0)
{
WLog_ERR(TAG, "snd_pcm_open failed");
return FALSE;
}
freerdp_dsp_context_reset_adpcm(alsa->dsp_context);
return rdpsnd_alsa_set_format(device, format, latency) &&
rdpsnd_alsa_open_mixer(alsa);
rdpsnd_alsa_open_mixer(alsa);
}
static void rdpsnd_alsa_close(rdpsndDevicePlugin* device)
@ -329,9 +303,6 @@ static void rdpsnd_alsa_close(rdpsndDevicePlugin* device)
if (status != 0)
frames = 0;
alsa->wLocalTimeClose = GetTickCount();
alsa->wLocalTimeClose += (((frames * 1000) / alsa->actual_rate) / alsa->actual_channels);
}
static void rdpsnd_alsa_free(rdpsndDevicePlugin* device)
@ -352,40 +323,22 @@ static void rdpsnd_alsa_free(rdpsndDevicePlugin* device)
}
free(alsa->device_name);
freerdp_dsp_context_free(alsa->dsp_context);
free(alsa);
}
static BOOL rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
static BOOL rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
if (format->cbSize == 0 &&
format->nSamplesPerSec <= 48000 &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels == 1 || format->nChannels == 2))
format->nSamplesPerSec <= 48000 &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
if (format->nSamplesPerSec <= 48000 &&
format->wBitsPerSample == 4 &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
case WAVE_FORMAT_GSM610:
default:
break;
}
@ -403,7 +356,6 @@ static UINT32 rdpsnd_alsa_get_volume(rdpsndDevicePlugin* device)
UINT16 dwVolumeRight;
snd_mixer_elem_t* elem;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
dwVolumeLeft = ((50 * 0xFFFF) / 100); /* 50% */
dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */
@ -417,16 +369,13 @@ static UINT32 rdpsnd_alsa_get_volume(rdpsndDevicePlugin* device)
snd_mixer_selem_get_playback_volume_range(elem, &volume_min, &volume_max);
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &volume_left);
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &volume_right);
dwVolumeLeft = (UINT16) (((volume_left * 0xFFFF) - volume_min) / (volume_max - volume_min));
dwVolumeRight = (UINT16) (((volume_right * 0xFFFF) - volume_min) / (volume_max - volume_min));
dwVolumeLeft = (UINT16)(((volume_left * 0xFFFF) - volume_min) / (volume_max - volume_min));
dwVolumeRight = (UINT16)(((volume_right * 0xFFFF) - volume_min) / (volume_max - volume_min));
break;
}
}
dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
return dwVolume;
}
@ -442,11 +391,11 @@ static BOOL rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (!alsa->mixer_handle && !rdpsnd_alsa_open_mixer(alsa))
return FALSE;
return FALSE;
left = (value & 0xFFFF);
right = ((value >> 16) & 0xFFFF);
for (elem = snd_mixer_first_elem(alsa->mixer_handle); elem; elem = snd_mixer_elem_next(elem))
{
if (snd_mixer_selem_has_playback_volume(elem))
@ -454,8 +403,9 @@ static BOOL rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
snd_mixer_selem_get_playback_volume_range(elem, &volume_min, &volume_max);
volume_left = volume_min + (left * (volume_max - volume_min)) / 0xFFFF;
volume_right = volume_min + (right * (volume_max - volume_min)) / 0xFFFF;
if ((snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, volume_left) < 0) ||
(snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, volume_right) < 0))
(snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, volume_right) < 0))
{
WLog_ERR(TAG, "error setting the volume\n");
return FALSE;
@ -466,108 +416,18 @@ static BOOL rdpsnd_alsa_set_volume(rdpsndDevicePlugin* device, UINT32 value)
return TRUE;
}
static BYTE* rdpsnd_alsa_process_audio_sample(rdpsndDevicePlugin* device, BYTE* data, int* size)
static UINT rdpsnd_alsa_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
{
int frames;
BYTE* srcData;
int srcFrameSize;
int dstFrameSize;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
if (alsa->wformat == WAVE_FORMAT_ADPCM)
{
alsa->dsp_context->decode_ms_adpcm(alsa->dsp_context,
data, *size, alsa->source_channels, alsa->block_size);
*size = alsa->dsp_context->adpcm_size;
srcData = alsa->dsp_context->adpcm_buffer;
}
else if (alsa->wformat == WAVE_FORMAT_DVI_ADPCM)
{
alsa->dsp_context->decode_ima_adpcm(alsa->dsp_context,
data, *size, alsa->source_channels, alsa->block_size);
*size = alsa->dsp_context->adpcm_size;
srcData = alsa->dsp_context->adpcm_buffer;
}
else
{
srcData = data;
}
srcFrameSize = alsa->source_channels * alsa->bytes_per_channel;
dstFrameSize = alsa->actual_channels * alsa->bytes_per_channel;
if ((*size % srcFrameSize) != 0)
return NULL;
if (!((alsa->source_rate == alsa->actual_rate) && (alsa->source_channels == alsa->actual_channels)))
{
alsa->dsp_context->resample(alsa->dsp_context, srcData, alsa->bytes_per_channel,
alsa->source_channels, alsa->source_rate, *size / srcFrameSize,
alsa->actual_channels, alsa->actual_rate);
frames = alsa->dsp_context->resampled_frames;
*size = frames * dstFrameSize;
srcData = alsa->dsp_context->resampled_buffer;
}
data = srcData;
return data;
}
static BOOL rdpsnd_alsa_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
{
int size;
BYTE* data;
size = wave->length;
data = rdpsnd_alsa_process_audio_sample(device, wave->data, &size);
wave->data = (BYTE*) malloc(size);
if (!wave->data)
return FALSE;
CopyMemory(wave->data, data, size);
wave->length = size;
return TRUE;
}
static void rdpsnd_alsa_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
{
BYTE* data;
int offset;
int length;
int status;
size_t offset;
int frame_size;
UINT32 wCurrentTime;
snd_htimestamp_t tstamp;
snd_pcm_uframes_t frames;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
offset = 0;
data = wave->data;
length = wave->length;
frame_size = alsa->actual_channels * alsa->bytes_per_channel;
frame_size = alsa->actual_channels * alsa->aformat.wBitsPerSample / 8;
if (alsa->wLocalTimeClose)
while (offset < size)
{
wCurrentTime = GetTickCount();
if (snd_pcm_htimestamp(alsa->pcm_handle, &frames, &tstamp) == -EPIPE)
{
if (wCurrentTime > alsa->wLocalTimeClose)
snd_pcm_recover(alsa->pcm_handle, -EPIPE, 1);
}
alsa->wLocalTimeClose = 0;
}
while (offset < length)
{
status = snd_pcm_writei(alsa->pcm_handle, &data[offset], (length - offset) / frame_size);
snd_pcm_sframes_t status = snd_pcm_writei(alsa->pcm_handle, &data[offset],
(size - offset) / frame_size);
if (status == -EPIPE)
{
@ -590,11 +450,7 @@ static void rdpsnd_alsa_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
offset += status * frame_size;
}
free(data);
/* From rdpsnd_main.c */
wave->wTimeStampB = wave->wTimeStampA + wave->wAudioLength + 65;
wave->wLocalTimeB = wave->wLocalTimeA + wave->wAudioLength + 65;
return 10; /* TODO: Get real latency in [ms] */
}
static COMMAND_LINE_ARGUMENT_A rdpsnd_alsa_args[] =
@ -614,10 +470,10 @@ static UINT rdpsnd_alsa_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV*
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, rdpsnd_alsa_args, flags,
alsa, NULL, NULL);
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv, rdpsnd_alsa_args, flags, alsa, NULL, NULL);
if (status < 0)
{
WLog_ERR(TAG, "CommandLineParseArgumentsA failed!");
@ -632,14 +488,13 @@ static UINT rdpsnd_alsa_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV*
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "dev")
{
alsa->device_name = _strdup(arg->Value);
if (!alsa->device_name)
return CHANNEL_RC_NO_MEMORY;
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
@ -663,8 +518,8 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
ADDIN_ARGV* args;
rdpsndAlsaPlugin* alsa;
UINT error;
alsa = (rdpsndAlsaPlugin*) calloc(1, sizeof(rdpsndAlsaPlugin));
if (!alsa)
{
WLog_ERR(TAG, "calloc failed!");
@ -673,28 +528,26 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
alsa->device.Open = rdpsnd_alsa_open;
alsa->device.FormatSupported = rdpsnd_alsa_format_supported;
alsa->device.SetFormat = rdpsnd_alsa_set_format;
alsa->device.GetVolume = rdpsnd_alsa_get_volume;
alsa->device.SetVolume = rdpsnd_alsa_set_volume;
alsa->device.WaveDecode = rdpsnd_alsa_wave_decode;
alsa->device.WavePlay = rdpsnd_alsa_wave_play;
alsa->device.Play = rdpsnd_alsa_play;
alsa->device.Close = rdpsnd_alsa_close;
alsa->device.Free = rdpsnd_alsa_free;
args = pEntryPoints->args;
if (args->argc > 1)
{
if ((error = rdpsnd_alsa_parse_addin_args((rdpsndDevicePlugin *) alsa, args)))
if ((error = rdpsnd_alsa_parse_addin_args((rdpsndDevicePlugin*) alsa, args)))
{
WLog_ERR(TAG, "rdpsnd_alsa_parse_addin_args failed with error %"PRIu32"", error);
goto error_parse_args;
}
}
if (!alsa->device_name)
{
alsa->device_name = _strdup("default");
if (!alsa->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
@ -704,27 +557,11 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
}
alsa->pcm_handle = 0;
alsa->source_rate = 22050;
alsa->actual_rate = 22050;
alsa->format = SND_PCM_FORMAT_S16_LE;
alsa->source_channels = 2;
alsa->actual_channels = 2;
alsa->bytes_per_channel = 2;
alsa->dsp_context = freerdp_dsp_context_new();
if (!alsa->dsp_context)
{
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_dsp_context;
}
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) alsa);
return CHANNEL_RC_OK;
error_dsp_context:
freerdp_dsp_context_free(alsa->dsp_context);
error_strdup:
free(alsa->device_name);
error_parse_args:

View File

@ -144,17 +144,17 @@ static void rdpsnd_ios_stop(rdpsndDevicePlugin* __unused device)
}
}
static void rdpsnd_ios_play(rdpsndDevicePlugin* device, BYTE* data, int size)
static UINT rdpsnd_ios_play(rdpsndDevicePlugin* device, BYTE* data, int size)
{
rdpsndIOSPlugin *p = THIS(device);
const BOOL ok = TPCircularBufferProduceBytes(&p->buffer, data, size);
if (!ok)
{
return;
}
return 0;
rdpsnd_ios_start(device);
return 10; /* TODO: Get real latencry in [ms] */
}
static BOOL rdpsnd_ios_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int __unused latency)

View File

@ -32,7 +32,6 @@
#include <winpr/crt.h>
#include <freerdp/types.h>
#include <freerdp/codec/dsp.h>
#define __COREFOUNDATION_CFPLUGINCOM__ 1
#define IUNKNOWN_C_GUTS void *_reserved; void* QueryInterface; void* AddRef; void* Release
@ -51,41 +50,36 @@ struct rdpsnd_mac_plugin
BOOL isOpen;
BOOL isPlaying;
UINT32 latency;
AUDIO_FORMAT format;
size_t lastAudioBufferIndex;
size_t audioBufferIndex;
AudioQueueRef audioQueue;
AudioStreamBasicDescription audioFormat;
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
Float64 lastStartTime;
int wformat;
int block_size;
FREERDP_DSP_CONTEXT* dsp_context;
};
typedef struct rdpsnd_mac_plugin rdpsndMacPlugin;
static void mac_audio_queue_output_cb(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
static void mac_audio_queue_output_cb(void* inUserData, AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer)
{
rdpsndMacPlugin* mac = (rdpsndMacPlugin*)inUserData;
if (inBuffer == mac->audioBuffers[mac->lastAudioBufferIndex]) {
if (inBuffer == mac->audioBuffers[mac->lastAudioBufferIndex])
{
AudioQueuePause(mac->audioQueue);
mac->isPlaying = FALSE;
}
}
static BOOL rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
static BOOL rdpsnd_mac_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
UINT32 latency)
{
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
mac->latency = (UINT32) latency;
CopyMemory(&(mac->format), format, sizeof(AUDIO_FORMAT));
mac->audioFormat.mSampleRate = format->nSamplesPerSec;
mac->audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
mac->audioFormat.mFramesPerPacket = 1;
@ -94,101 +88,78 @@ static BOOL rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* form
mac->audioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8;
mac->audioFormat.mBytesPerPacket = format->nBlockAlign;
mac->audioFormat.mReserved = 0;
switch (format->wFormatTag)
{
case WAVE_FORMAT_ALAW:
mac->audioFormat.mFormatID = kAudioFormatALaw;
break;
case WAVE_FORMAT_MULAW:
mac->audioFormat.mFormatID = kAudioFormatULaw;
break;
case WAVE_FORMAT_PCM:
mac->audioFormat.mFormatID = kAudioFormatLinearPCM;
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
mac->audioFormat.mFormatID = kAudioFormatLinearPCM;
mac->audioFormat.mBitsPerChannel = 16;
mac->audioFormat.mBytesPerFrame = (16 * format->nChannels) / 8;
mac->audioFormat.mBytesPerPacket = mac->audioFormat.mFramesPerPacket * mac->audioFormat.mBytesPerFrame;
break;
case WAVE_FORMAT_GSM610:
mac->audioFormat.mFormatID = kAudioFormatMicrosoftGSM;
break;
default:
break;
}
mac->wformat = format->wFormatTag;
mac->block_size = format->nBlockAlign;
rdpsnd_print_audio_format(format);
return TRUE;
}
static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency)
{
int index;
OSStatus status;
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
if (mac->isOpen)
return TRUE;
mac->audioBufferIndex = 0;
if (!device->SetFormat(device, format, 0))
{
WLog_ERR(TAG, "SetFormat failure\n");
if (!rdpsnd_mac_set_format(device, format, latency))
return FALSE;
}
freerdp_dsp_context_reset_adpcm(mac->dsp_context);
status = AudioQueueNewOutput(&(mac->audioFormat),
mac_audio_queue_output_cb, mac,
NULL, NULL, 0, &(mac->audioQueue));
mac_audio_queue_output_cb, mac,
NULL, NULL, 0, &(mac->audioQueue));
if (status != 0)
{
WLog_ERR(TAG, "AudioQueueNewOutput failure\n");
return FALSE;
}
UInt32 DecodeBufferSizeFrames;
UInt32 propertySize = sizeof(DecodeBufferSizeFrames);
status = AudioQueueGetProperty(mac->audioQueue,
kAudioQueueProperty_DecodeBufferSizeFrames,
&DecodeBufferSizeFrames,
&propertySize);
kAudioQueueProperty_DecodeBufferSizeFrames,
&DecodeBufferSizeFrames,
&propertySize);
if (status != 0)
{
WLog_DBG(TAG, "AudioQueueGetProperty failure: kAudioQueueProperty_DecodeBufferSizeFrames\n");
return FALSE;
}
for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
{
status = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE, &mac->audioBuffers[index]);
status = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE,
&mac->audioBuffers[index]);
if (status != 0)
{
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed\n");
return FALSE;
}
}
mac->lastStartTime = 0;
mac->isOpen = TRUE;
return TRUE;
}
@ -196,14 +167,13 @@ static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
static void rdpsnd_mac_close(rdpsndDevicePlugin* device)
{
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
if (mac->isOpen)
{
size_t index;
mac->isOpen = FALSE;
AudioQueueStop(mac->audioQueue, true);
for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
{
AudioQueueFreeBuffer(mac->audioQueue, mac->audioBuffers[index]);
@ -211,7 +181,6 @@ static void rdpsnd_mac_close(rdpsndDevicePlugin* device)
AudioQueueDispose(mac->audioQueue, true);
mac->audioQueue = NULL;
mac->isPlaying = FALSE;
}
}
@ -219,28 +188,24 @@ static void rdpsnd_mac_close(rdpsndDevicePlugin* device)
static void rdpsnd_mac_free(rdpsndDevicePlugin* device)
{
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
device->Close(device);
freerdp_dsp_context_free(mac->dsp_context);
free(mac);
}
static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
return TRUE;
case WAVE_FORMAT_GSM610:
return TRUE;
default:
return FALSE;
}
return FALSE;
}
@ -251,17 +216,15 @@ static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin* device, UINT32 value)
UINT16 volumeLeft;
UINT16 volumeRight;
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
if (!mac->audioQueue)
return FALSE;
volumeLeft = (value & 0xFFFF);
volumeRight = ((value >> 16) & 0xFFFF);
fVolume = ((float) volumeLeft) / 65535.0;
status = AudioQueueSetParameter(mac->audioQueue, kAudioQueueParam_Volume, fVolume);
if (status != 0)
{
WLog_ERR(TAG, "AudioQueueSetParameter kAudioQueueParam_Volume failed: %f\n", fVolume);
@ -274,87 +237,51 @@ static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin* device, UINT32 value)
static void rdpsnd_mac_start(rdpsndDevicePlugin* device)
{
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
if (!mac->isPlaying)
{
OSStatus status;
if (!mac->audioQueue)
return;
status = AudioQueueStart(mac->audioQueue, NULL);
if (status != 0)
{
WLog_ERR(TAG, "AudioQueueStart failed\n");
}
mac->isPlaying = TRUE;
}
}
static BOOL rdpsnd_mac_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
static UINT rdpsnd_mac_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
{
int length;
BYTE* data;
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
if (mac->wformat == WAVE_FORMAT_ADPCM)
{
mac->dsp_context->decode_ms_adpcm(mac->dsp_context, wave->data, wave->length, mac->format.nChannels, mac->block_size);
length = mac->dsp_context->adpcm_size;
data = mac->dsp_context->adpcm_buffer;
}
else if (mac->wformat == WAVE_FORMAT_DVI_ADPCM)
{
mac->dsp_context->decode_ima_adpcm(mac->dsp_context, wave->data, wave->length, mac->format.nChannels, mac->block_size);
length = mac->dsp_context->adpcm_size;
data = mac->dsp_context->adpcm_buffer;
}
else
{
length = wave->length;
data = wave->data;
}
wave->data = (BYTE*) malloc(length);
CopyMemory(wave->data, data, length);
wave->length = length;
return TRUE;
}
static void rdpsnd_mac_waveplay(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
{
int length;
size_t length;
AudioQueueBufferRef audioBuffer;
AudioTimeStamp outActualStartTime;
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
if (!mac->isOpen)
return;
return 0;
audioBuffer = mac->audioBuffers[mac->audioBufferIndex];
length = wave->length > audioBuffer->mAudioDataBytesCapacity ? audioBuffer->mAudioDataBytesCapacity : wave->length;
CopyMemory(audioBuffer->mAudioData, wave->data, length);
free(wave->data);
wave->data = NULL;
length = size > audioBuffer->mAudioDataBytesCapacity ? audioBuffer->mAudioDataBytesCapacity : size;
CopyMemory(audioBuffer->mAudioData, data, length);
audioBuffer->mAudioDataByteSize = length;
audioBuffer->mUserData = wave;
audioBuffer->mUserData = mac;
AudioQueueEnqueueBufferWithParameters(mac->audioQueue, audioBuffer, 0, 0, 0, 0, 0, NULL, NULL, &outActualStartTime);
UInt64 startTimeDelta = (outActualStartTime.mSampleTime - mac->lastStartTime) / 100.0;
wave->wLocalTimeB = wave->wLocalTimeA + startTimeDelta + wave->wAudioLength;
wave->wTimeStampB = wave->wTimeStampA + wave->wLocalTimeB - wave->wLocalTimeA;
mac->lastStartTime = outActualStartTime.mSampleTime;
mac->lastAudioBufferIndex = mac->audioBufferIndex;
mac->audioBufferIndex++;
mac->audioBufferIndex %= MAC_AUDIO_QUEUE_NUM_BUFFERS;
device->Start(device);
rdpsnd_mac_start(device);
return 10; /* TODO: Get real latencry in [ms] */
}
#ifdef BUILTIN_CHANNELS
@ -371,25 +298,17 @@ static void rdpsnd_mac_waveplay(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
{
rdpsndMacPlugin* mac;
mac = (rdpsndMacPlugin*) calloc(1, sizeof(rdpsndMacPlugin));
if (!mac)
return CHANNEL_RC_NO_MEMORY;
mac->device.Open = rdpsnd_mac_open;
mac->device.FormatSupported = rdpsnd_mac_format_supported;
mac->device.SetFormat = rdpsnd_mac_set_format;
mac->device.SetVolume = rdpsnd_mac_set_volume;
mac->device.WaveDecode = rdpsnd_mac_wave_decode;
mac->device.WavePlay = rdpsnd_mac_waveplay;
mac->device.Start = rdpsnd_mac_start;
mac->device.Play = rdpsnd_mac_play;
mac->device.Close = rdpsnd_mac_close;
mac->device.Free = rdpsnd_mac_free;
mac->dsp_context = freerdp_dsp_context_new();
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) mac);
return CHANNEL_RC_OK;
}

View File

@ -36,7 +36,6 @@
#include <winpr/collections.h>
#include <freerdp/types.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/log.h>
#include "opensl_io.h"
@ -48,7 +47,7 @@ struct rdpsnd_opensles_plugin
{
rdpsndDevicePlugin device;
int latency;
UINT32 latency;
int wformat;
int block_size;
char* device_name;
@ -60,7 +59,6 @@ struct rdpsnd_opensles_plugin
UINT32 rate;
UINT32 channels;
int format;
FREERDP_DSP_CONTEXT* dsp_context;
};
static int rdpsnd_opensles_volume_to_millibel(unsigned short level, int max)
@ -91,9 +89,6 @@ static bool rdpsnd_opensles_check_handle(const rdpsndopenslesPlugin* hdl)
rc = false;
else
{
if (!hdl->dsp_context)
rc = false;
if (!hdl->stream)
rc = false;
}
@ -120,11 +115,11 @@ static int rdpsnd_opensles_set_params(rdpsndopenslesPlugin* opensles)
}
static BOOL rdpsnd_opensles_set_format(rdpsndDevicePlugin* device,
AUDIO_FORMAT* format, int latency)
const AUDIO_FORMAT* format, UINT32 latency)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
rdpsnd_opensles_check_handle(opensles);
DEBUG_SND("opensles=%p format=%p, latency=%d", (void*) opensles, (void*) format, latency);
DEBUG_SND("opensles=%p format=%p, latency=%"PRIu32, (void*) opensles, (void*) format, latency);
if (format)
{
@ -143,11 +138,11 @@ static BOOL rdpsnd_opensles_set_format(rdpsndDevicePlugin* device,
}
static BOOL rdpsnd_opensles_open(rdpsndDevicePlugin* device,
AUDIO_FORMAT* format, int latency)
const AUDIO_FORMAT* format, UINT32 latency)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p format=%p, latency=%d, rate=%"PRIu32"",
(void*) opensles, (void*) format, latency, opensles->rate);
DEBUG_SND("opensles=%p format=%p, latency=%"PRIu32", rate=%"PRIu32"",
(void*) opensles, (void*) format, latency, opensles->rate);
if (rdpsnd_opensles_check_handle(opensles))
return TRUE;
@ -161,8 +156,7 @@ static BOOL rdpsnd_opensles_open(rdpsndDevicePlugin* device,
else
rdpsnd_opensles_set_volume(device, opensles->volume);
rdpsnd_opensles_set_format(device, format, latency);
return TRUE;
return rdpsnd_opensles_set_format(device, format, latency);
}
static void rdpsnd_opensles_close(rdpsndDevicePlugin* device)
@ -184,13 +178,11 @@ static void rdpsnd_opensles_free(rdpsndDevicePlugin* device)
assert(opensles);
assert(opensles->device_name);
free(opensles->device_name);
assert(opensles->dsp_context);
freerdp_dsp_context_free(opensles->dsp_context);
free(opensles);
}
static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device,
AUDIO_FORMAT* format)
const AUDIO_FORMAT* format)
{
DEBUG_SND("format=%"PRIu16", cbsize=%"PRIu16", samples=%"PRIu32", bits=%"PRIu16", channels=%"PRIu16", align=%"PRIu16"",
format->wFormatTag, format->cbSize, format->nSamplesPerSec,
@ -211,20 +203,6 @@ static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device,
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
if (format->nSamplesPerSec <= 48000 &&
format->wBitsPerSample == 4 &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
case WAVE_FORMAT_GSM610:
default:
break;
}
@ -283,46 +261,22 @@ static BOOL rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device,
return TRUE;
}
static void rdpsnd_opensles_play(rdpsndDevicePlugin* device,
BYTE* data, int size)
static UINT rdpsnd_opensles_play(rdpsndDevicePlugin* device,
const BYTE* data, size_t size)
{
union
{
BYTE* b;
short* s;
const BYTE* b;
const short* s;
} src;
int ret;
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p, data=%p, size=%d", (void*) opensles, (void*) data, size);
if (!rdpsnd_opensles_check_handle(opensles))
return;
if (opensles->format == WAVE_FORMAT_ADPCM)
{
DEBUG_SND("dsp_context=%p, channels=%"PRIu32", block_size=%d",
(void*) opensles->dsp_context, opensles->channels, opensles->block_size);
opensles->dsp_context->decode_ms_adpcm(opensles->dsp_context,
data, size, opensles->channels, opensles->block_size);
size = opensles->dsp_context->adpcm_size;
src.b = opensles->dsp_context->adpcm_buffer;
}
else if (opensles->format == WAVE_FORMAT_DVI_ADPCM)
{
DEBUG_SND("dsp_context=%p, channels=%"PRIu32", block_size=%d",
(void*) opensles->dsp_context, opensles->channels, opensles->block_size);
opensles->dsp_context->decode_ima_adpcm(opensles->dsp_context,
data, size, opensles->channels, opensles->block_size);
size = opensles->dsp_context->adpcm_size;
src.b = opensles->dsp_context->adpcm_buffer;
}
else
{
src.b = data;
}
return 0;
src.b = data;
DEBUG_SND("size=%d, src=%p", size, (void*) src.b);
assert(0 == size % 2);
assert(size > 0);
@ -331,6 +285,8 @@ static void rdpsnd_opensles_play(rdpsndDevicePlugin* device,
if (ret < 0)
WLog_ERR(TAG, "android_AudioOut failed (%d)", ret);
return 10; /* TODO: Get real latencry in [ms] */
}
static void rdpsnd_opensles_start(rdpsndDevicePlugin* device)
@ -416,7 +372,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(
opensles->device.Open = rdpsnd_opensles_open;
opensles->device.FormatSupported = rdpsnd_opensles_format_supported;
opensles->device.SetFormat = rdpsnd_opensles_set_format;
opensles->device.GetVolume = rdpsnd_opensles_get_volume;
opensles->device.SetVolume = rdpsnd_opensles_set_volume;
opensles->device.Start = rdpsnd_opensles_start;
@ -440,20 +395,10 @@ UINT freerdp_rdpsnd_client_subsystem_entry(
opensles->rate = 44100;
opensles->channels = 2;
opensles->format = WAVE_FORMAT_ADPCM;
opensles->dsp_context = freerdp_dsp_context_new();
if (!opensles->dsp_context)
{
error = CHANNEL_RC_NO_MEMORY;
goto out_dsp_new;
}
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd,
(rdpsndDevicePlugin*) opensles);
DEBUG_SND("success");
return CHANNEL_RC_OK;
out_dsp_new:
free(opensles->device_name);
outstrdup:
free(opensles);
return error;

View File

@ -47,7 +47,6 @@
#include <sys/ioctl.h>
#include <freerdp/types.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/log.h>
#include "rdpsnd_main.h"
@ -64,10 +63,8 @@ struct rdpsnd_oss_plugin
int supported_formats;
int latency;
UINT32 latency;
AUDIO_FORMAT format;
FREERDP_DSP_CONTEXT* dsp_context;
};
#define OSS_LOG_ERR(_text, _error) \
@ -77,7 +74,7 @@ struct rdpsnd_oss_plugin
}
static int rdpsnd_oss_get_format(AUDIO_FORMAT* format)
static int rdpsnd_oss_get_format(const AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
@ -95,21 +92,15 @@ static int rdpsnd_oss_get_format(AUDIO_FORMAT* format)
case WAVE_FORMAT_ALAW:
return AFMT_A_LAW;
#if 0 /* This does not work on my desktop. */
case WAVE_FORMAT_MULAW:
return AFMT_MU_LAW;
#endif
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
return AFMT_S16_LE;
}
return 0;
}
static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
{
int req_fmt = 0;
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
@ -128,14 +119,12 @@ static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
if (format->nSamplesPerSec > 48000 ||
format->wBitsPerSample != 4 ||
(format->nChannels != 1 && format->nChannels != 2))
return FALSE;
case WAVE_FORMAT_MULAW:
case WAVE_FORMAT_ALAW:
break;
default:
return FALSE;
}
req_fmt = rdpsnd_oss_get_format(format);
@ -155,7 +144,8 @@ static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT
return TRUE;
}
static BOOL rdpsnd_oss_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
static BOOL rdpsnd_oss_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
UINT32 latency)
{
int tmp;
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
@ -227,7 +217,7 @@ static void rdpsnd_oss_open_mixer(rdpsndOssPlugin* oss)
}
}
static BOOL rdpsnd_oss_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
static BOOL rdpsnd_oss_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency)
{
char dev_name[PATH_MAX] = "/dev/dsp";
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
@ -272,7 +262,6 @@ static BOOL rdpsnd_oss_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
return FALSE;
}
freerdp_dsp_context_reset_adpcm(oss->dsp_context);
rdpsnd_oss_set_format(device, format, latency);
rdpsnd_oss_open_mixer(oss);
return TRUE;
@ -308,7 +297,6 @@ static void rdpsnd_oss_free(rdpsndDevicePlugin* device)
return;
rdpsnd_oss_close(device);
freerdp_dsp_context_free(oss->dsp_context);
free(oss);
}
@ -370,68 +358,36 @@ static BOOL rdpsnd_oss_set_volume(rdpsndDevicePlugin* device, UINT32 value)
return TRUE;
}
static BOOL rdpsnd_oss_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
static UINT rdpsnd_oss_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
{
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
if (device == NULL || wave == NULL)
return FALSE;
if (device == NULL || oss->mixer_handle == -1)
return 0;
switch (oss->format.wFormatTag)
while (size > 0)
{
case WAVE_FORMAT_ADPCM:
oss->dsp_context->decode_ms_adpcm(oss->dsp_context,
wave->data, wave->length, oss->format.nChannels, oss->format.nBlockAlign);
wave->length = oss->dsp_context->adpcm_size;
wave->data = oss->dsp_context->adpcm_buffer;
break;
case WAVE_FORMAT_DVI_ADPCM:
oss->dsp_context->decode_ima_adpcm(oss->dsp_context,
wave->data, wave->length, oss->format.nChannels, oss->format.nBlockAlign);
wave->length = oss->dsp_context->adpcm_size;
wave->data = oss->dsp_context->adpcm_buffer;
break;
}
return TRUE;
}
static void rdpsnd_oss_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
{
BYTE* data;
int offset, size, status, latency;
rdpsndOssPlugin* oss = (rdpsndOssPlugin*)device;
if (device == NULL || wave == NULL)
return;
offset = 0;
data = wave->data;
size = wave->length;
latency = oss->latency;
while (offset < size)
{
status = write(oss->pcm_handle, &data[offset], (size - offset));
ssize_t status = write(oss->pcm_handle, data, size);
if (status < 0)
{
OSS_LOG_ERR("write fail", errno);
rdpsnd_oss_close(device);
rdpsnd_oss_open(device, NULL, latency);
rdpsnd_oss_open(device, NULL, oss->latency);
break;
}
offset += status;
data += status;
if (status <= size)
size -= status;
else
size = 0;
}
/* From rdpsnd_main.c */
wave->wTimeStampB = wave->wTimeStampA + wave->wAudioLength + 65 + latency;
wave->wLocalTimeB = wave->wLocalTimeA + wave->wAudioLength + 65 + latency;
return 10; /* TODO: Get real latency in [ms] */
}
static COMMAND_LINE_ARGUMENT_A rdpsnd_oss_args[] =
{
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
@ -514,11 +470,9 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
oss->device.Open = rdpsnd_oss_open;
oss->device.FormatSupported = rdpsnd_oss_format_supported;
oss->device.SetFormat = rdpsnd_oss_set_format;
oss->device.GetVolume = rdpsnd_oss_get_volume;
oss->device.SetVolume = rdpsnd_oss_set_volume;
oss->device.WaveDecode = rdpsnd_oss_wave_decode;
oss->device.WavePlay = rdpsnd_oss_wave_play;
oss->device.Play = rdpsnd_oss_play;
oss->device.Close = rdpsnd_oss_close;
oss->device.Free = rdpsnd_oss_free;
oss->pcm_handle = -1;
@ -526,14 +480,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
oss->dev_unit = -1;
args = pEntryPoints->args;
rdpsnd_oss_parse_addin_args((rdpsndDevicePlugin*)oss, args);
oss->dsp_context = freerdp_dsp_context_new();
if (!oss->dsp_context)
{
free(oss);
return CHANNEL_RC_NO_MEMORY;
}
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)oss);
return CHANNEL_RC_OK;
}

View File

@ -25,16 +25,10 @@ include_directories(${PULSE_INCLUDE_DIR})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
list(APPEND ${MODULE_PREFIX}_LIBS ${PULSE_LIBRARY})
list(APPEND ${MODULE_PREFIX}_LIBS freerdp)
list(APPEND ${MODULE_PREFIX}_LIBS winpr)
if(GSM_FOUND)
list(APPEND ${MODULE_PREFIX}_LIBS ${GSM_LIBRARIES})
endif()
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/Pulse")

View File

@ -33,10 +33,6 @@
#include <pulse/pulseaudio.h>
#ifdef WITH_GSM
#include <gsm/gsm.h>
#endif
#include <freerdp/types.h>
#include <freerdp/codec/dsp.h>
@ -53,23 +49,15 @@ struct rdpsnd_pulse_plugin
pa_context* context;
pa_sample_spec sample_spec;
pa_stream* stream;
int format;
int block_size;
int latency;
FREERDP_DSP_CONTEXT* dsp_context;
#ifdef WITH_GSM
gsm gsm_context;
wStream* gsmBuffer;
#endif
UINT32 latency;
};
static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format);
static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userdata)
{
pa_context_state_t state;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) userdata;
state = pa_context_get_state(context);
switch (state)
@ -140,7 +128,6 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device)
static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) userdata;
pa_threaded_mainloop_signal(pulse->mainloop, 0);
}
@ -161,7 +148,6 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
{
pa_stream_state_t state;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) userdata;
state = pa_stream_get_state(stream);
switch (state)
@ -183,7 +169,6 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata
static void rdpsnd_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) userdata;
pa_threaded_mainloop_signal(pulse->mainloop, 0);
}
@ -191,30 +176,27 @@ static void rdpsnd_pulse_close(rdpsndDevicePlugin* device)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
#ifdef WITH_GSM
if (pulse->gsm_context)
gsm_destroy(pulse->gsm_context);
#endif
if (!pulse->context || !pulse->stream)
return;
pa_threaded_mainloop_lock(pulse->mainloop);
rdpsnd_pulse_wait_for_operation(pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse));
rdpsnd_pulse_wait_for_operation(pulse, pa_stream_drain(pulse->stream,
rdpsnd_pulse_stream_success_callback, pulse));
pa_stream_disconnect(pulse->stream);
pa_stream_unref(pulse->stream);
pulse->stream = NULL;
pa_threaded_mainloop_unlock(pulse->mainloop);
}
static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT* format)
static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_FORMAT* format)
{
pa_sample_spec sample_spec = { 0 };
if (!pulse->context)
return;
return FALSE;
if (!rdpsnd_pulse_format_supported(&pulse->device, format))
return FALSE;
sample_spec.rate = format->nSamplesPerSec;
sample_spec.channels = format->nChannels;
@ -227,15 +209,15 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT*
case 8:
sample_spec.format = PA_SAMPLE_U8;
break;
case 16:
sample_spec.format = PA_SAMPLE_S16LE;
break;
}
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
sample_spec.format = PA_SAMPLE_S16LE;
default:
return FALSE;
}
break;
case WAVE_FORMAT_ALAW:
@ -246,17 +228,16 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, AUDIO_FORMAT*
sample_spec.format = PA_SAMPLE_ULAW;
break;
case WAVE_FORMAT_GSM610:
sample_spec.format = PA_SAMPLE_S16LE;
break;
default:
return FALSE;
}
pulse->sample_spec = sample_spec;
pulse->format = format->wFormatTag;
pulse->block_size = format->nBlockAlign;
return TRUE;
}
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
UINT32 latency)
{
pa_stream_state_t state;
pa_stream_flags_t flags;
@ -267,7 +248,9 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
if (!pulse->context || pulse->stream)
return TRUE;
rdpsnd_pulse_set_format_spec(pulse, format);
if (!rdpsnd_pulse_set_format_spec(pulse, format))
return FALSE;
pulse->latency = latency;
if (pa_sample_spec_valid(&pulse->sample_spec) == 0)
@ -277,8 +260,8 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
}
pa_threaded_mainloop_lock(pulse->mainloop);
pulse->stream = pa_stream_new(pulse->context, "freerdp", &pulse->sample_spec, NULL);
if (!pulse->stream)
{
pa_threaded_mainloop_unlock(pulse->mainloop);
@ -288,21 +271,20 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
/* register essential callbacks */
pa_stream_set_state_callback(pulse->stream, rdpsnd_pulse_stream_state_callback, pulse);
pa_stream_set_write_callback(pulse->stream, rdpsnd_pulse_stream_request_callback, pulse);
flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
if (pulse->latency > 0)
{
buffer_attr.maxlength = pa_usec_to_bytes(pulse->latency * 2 * 1000, &pulse->sample_spec);
buffer_attr.tlength = pa_usec_to_bytes(pulse->latency * 1000, &pulse->sample_spec);
buffer_attr.prebuf = (UINT32) -1;
buffer_attr.minreq = (UINT32) -1;
buffer_attr.fragsize = (UINT32) -1;
buffer_attr.prebuf = (UINT32) - 1;
buffer_attr.minreq = (UINT32) - 1;
buffer_attr.fragsize = (UINT32) - 1;
flags |= PA_STREAM_ADJUST_LATENCY;
}
if (pa_stream_connect_playback(pulse->stream,
pulse->device_name, pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0)
pulse->device_name, pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0)
{
pa_threaded_mainloop_unlock(pulse->mainloop);
return TRUE;
@ -326,17 +308,7 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
pa_threaded_mainloop_unlock(pulse->mainloop);
if (state == PA_STREAM_READY)
{
freerdp_dsp_context_reset_adpcm(pulse->dsp_context);
#ifdef WITH_GSM
if (pulse->gsm_context)
gsm_destroy(pulse->gsm_context);
pulse->gsm_context = gsm_create();
#endif
return TRUE;
}
rdpsnd_pulse_close(device);
return FALSE;
@ -369,16 +341,11 @@ static void rdpsnd_pulse_free(rdpsndDevicePlugin* device)
pulse->mainloop = NULL;
}
#ifdef WITH_GSM
Stream_Free(pulse->gsmBuffer, TRUE);
#endif
free(pulse->device_name);
freerdp_dsp_context_free(pulse->dsp_context);
free(pulse);
}
static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device;
@ -389,65 +356,31 @@ static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, AUDIO_FORM
{
case WAVE_FORMAT_PCM:
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
(format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
{
return TRUE;
}
break;
case WAVE_FORMAT_ALAW:
case WAVE_FORMAT_MULAW:
if (format->cbSize == 0 &&
(format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8) &&
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
(format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 8) &&
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
{
return TRUE;
}
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
#ifdef WITH_GSM
case WAVE_FORMAT_GSM610:
if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
(format->nBlockAlign == 65) && (format->nChannels == 1))
{
return TRUE;
}
break;
#endif
}
return FALSE;
}
static BOOL rdpsnd_pulse_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
if (pulse->stream)
{
pa_threaded_mainloop_lock(pulse->mainloop);
pa_stream_disconnect(pulse->stream);
pa_stream_unref(pulse->stream);
pulse->stream = NULL;
pa_threaded_mainloop_unlock(pulse->mainloop);
}
return rdpsnd_pulse_open(device, format, latency);
}
static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
{
pa_cvolume cv;
@ -459,17 +392,15 @@ static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
if (!pulse->context || !pulse->stream)
return FALSE;
left = (pa_volume_t) (value & 0xFFFF);
right = (pa_volume_t) ((value >> 16) & 0xFFFF);
left = (pa_volume_t)(value & 0xFFFF);
right = (pa_volume_t)((value >> 16) & 0xFFFF);
pa_cvolume_init(&cv);
cv.channels = 2;
cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF;
cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF;
pa_threaded_mainloop_lock(pulse->mainloop);
operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream), &cv, NULL, NULL);
operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream),
&cv, NULL, NULL);
if (operation)
pa_operation_unref(operation);
@ -478,93 +409,23 @@ static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value)
return TRUE;
}
static BYTE* rdpsnd_pulse_convert_audio(rdpsndDevicePlugin* device, BYTE* data, int* size)
static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
{
BYTE* pcmData = NULL;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
if (pulse->format == WAVE_FORMAT_ADPCM)
{
pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context,
data, *size, pulse->sample_spec.channels, pulse->block_size);
*size = pulse->dsp_context->adpcm_size;
pcmData = pulse->dsp_context->adpcm_buffer;
}
else if (pulse->format == WAVE_FORMAT_DVI_ADPCM)
{
pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context,
data, *size, pulse->sample_spec.channels, pulse->block_size);
*size = pulse->dsp_context->adpcm_size;
pcmData = pulse->dsp_context->adpcm_buffer;
}
#ifdef WITH_GSM
else if (pulse->format == WAVE_FORMAT_GSM610)
{
int inPos = 0;
int inSize = *size;
UINT16 gsmBlockBuffer[160];
Stream_SetPosition(pulse->gsmBuffer, 0);
while (inSize)
{
ZeroMemory(gsmBlockBuffer, sizeof(gsmBlockBuffer));
gsm_decode(pulse->gsm_context, (gsm_byte*) &data[inPos], (gsm_signal*) gsmBlockBuffer);
if ((inPos % 65) == 0)
{
inPos += 33;
inSize -= 33;
}
else
{
inPos += 32;
inSize -= 32;
}
if (!Stream_EnsureRemainingCapacity(pulse->gsmBuffer, 160 * 2))
return NULL;
Stream_Write(pulse->gsmBuffer, (void*) gsmBlockBuffer, 160 * 2);
}
Stream_SealLength(pulse->gsmBuffer);
pcmData = Stream_Buffer(pulse->gsmBuffer);
*size = Stream_Length(pulse->gsmBuffer);
}
#endif
else
{
pcmData = data;
}
return pcmData;
}
static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, BYTE* data, int size)
{
int length;
size_t length;
int status;
BYTE* pcmData;
pa_usec_t latency;
int negative;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
if (!pulse->stream)
return;
pcmData = rdpsnd_pulse_convert_audio(device, data, &size);
if (!pcmData)
return;
if (!pulse->stream || !data)
return 0;
pa_threaded_mainloop_lock(pulse->mainloop);
while (size > 0)
{
while ((length = pa_stream_writable_size(pulse->stream)) == 0)
{
pa_threaded_mainloop_wait(pulse->mainloop);
}
if (length < 0)
break;
@ -572,18 +433,23 @@ static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, BYTE* data, int size)
if (length > size)
length = size;
status = pa_stream_write(pulse->stream, pcmData, length, NULL, 0LL, PA_SEEK_RELATIVE);
status = pa_stream_write(pulse->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
if (status < 0)
{
break;
}
pcmData += length;
data += length;
size -= length;
}
if (pa_stream_get_latency(pulse->stream, &latency, &negative) != 0)
latency = 0;
pa_threaded_mainloop_unlock(pulse->mainloop);
return latency / 1000;
}
static void rdpsnd_pulse_start(rdpsndDevicePlugin* device)
@ -598,7 +464,7 @@ static void rdpsnd_pulse_start(rdpsndDevicePlugin* device)
pa_threaded_mainloop_unlock(pulse->mainloop);
}
COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] =
static COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] =
{
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
@ -615,29 +481,28 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*) device;
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
rdpsnd_pulse_args, flags, pulse, NULL, NULL);
rdpsnd_pulse_args, flags, pulse, NULL, NULL);
if (status < 0)
return ERROR_INVALID_DATA;
arg = rdpsnd_pulse_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "dev")
{
pulse->device_name = _strdup(arg->Value);
if (!pulse->device_name)
return ERROR_OUTOFMEMORY;
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
@ -661,24 +526,24 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
ADDIN_ARGV* args;
rdpsndPulsePlugin* pulse;
UINT ret;
pulse = (rdpsndPulsePlugin*) calloc(1, sizeof(rdpsndPulsePlugin));
if (!pulse)
return CHANNEL_RC_NO_MEMORY;
pulse->device.Open = rdpsnd_pulse_open;
pulse->device.FormatSupported = rdpsnd_pulse_format_supported;
pulse->device.SetFormat = rdpsnd_pulse_set_format;
pulse->device.SetVolume = rdpsnd_pulse_set_volume;
pulse->device.Play = rdpsnd_pulse_play;
pulse->device.Start = rdpsnd_pulse_start;
pulse->device.Close = rdpsnd_pulse_close;
pulse->device.Free = rdpsnd_pulse_free;
args = pEntryPoints->args;
if (args->argc > 1)
{
ret = rdpsnd_pulse_parse_addin_args((rdpsndDevicePlugin *) pulse, args);
ret = rdpsnd_pulse_parse_addin_args((rdpsndDevicePlugin*) pulse, args);
if (ret != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "error parsing arguments");
@ -687,17 +552,6 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
}
ret = CHANNEL_RC_NO_MEMORY;
pulse->dsp_context = freerdp_dsp_context_new();
if (!pulse->dsp_context)
goto error;
#ifdef WITH_GSM
pulse->gsmBuffer = Stream_New(NULL, 4096);
if (!pulse->gsmBuffer)
goto error;
#endif
pulse->mainloop = pa_threaded_mainloop_new();
if (!pulse->mainloop)
@ -709,14 +563,13 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
goto error;
pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse);
ret = ERROR_INVALID_OPERATION;
if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse))
goto error;
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) pulse);
return CHANNEL_RC_OK;
error:
rdpsnd_pulse_free((rdpsndDevicePlugin*)pulse);
return ret;

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,4 @@
#define DEBUG_SND(...) do { } while (0)
#endif
UINT rdpsnd_virtual_channel_write(rdpsndPlugin* rdpsnd, wStream* s);
#endif /* FREERDP_CHANNEL_RDPSND_CLIENT_MAIN_H */

View File

@ -36,7 +36,6 @@
#include <winpr/cmdline.h>
#include <freerdp/types.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/log.h>
#include "rdpsnd_main.h"
@ -49,15 +48,14 @@ struct rdpsnd_winmm_plugin
HWAVEOUT hWaveOut;
WAVEFORMATEX format;
int wformat;
int block_size;
UINT32 volume;
FREERDP_DSP_CONTEXT* dsp_context;
HANDLE next;
};
static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* out)
{
BOOL result = FALSE;
if (!in || !out)
return FALSE;
ZeroMemory(out, sizeof(WAVEFORMATEX));
out->wFormatTag = WAVE_FORMAT_PCM;
@ -68,40 +66,31 @@ static BOOL rdpsnd_winmm_convert_format(const AUDIO_FORMAT* in, WAVEFORMATEX* ou
{
case WAVE_FORMAT_PCM:
out->wBitsPerSample = in->wBitsPerSample;
result = TRUE;
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
out->wBitsPerSample = 16;
result = TRUE;
break;
default:
return FALSE;
}
out->nBlockAlign = out->nChannels * out->wBitsPerSample / 8;
out->nAvgBytesPerSec = out->nSamplesPerSec * out->nBlockAlign;
return result;
}
static BOOL rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
{
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
if (format)
{
if (!rdpsnd_winmm_convert_format(format, &winmm->format))
return FALSE;
winmm->wformat = format->wFormatTag;
winmm->block_size = format->nBlockAlign;
}
return TRUE;
}
static void CALLBACK rdpsnd_winmm_callback_function(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
static BOOL rdpsnd_winmm_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
UINT32 latency)
{
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
if (!rdpsnd_winmm_convert_format(format, &winmm->format))
return FALSE;
return TRUE;
}
static void CALLBACK rdpsnd_winmm_callback_function(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
RDPSND_WAVE* wave;
LPWAVEHDR lpWaveHdr;
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) dwInstance;
@ -110,42 +99,27 @@ static void CALLBACK rdpsnd_winmm_callback_function(HWAVEOUT hwo, UINT uMsg, DWO
case MM_WOM_OPEN:
WLog_DBG(TAG, "MM_WOM_OPEN");
break;
case MM_WOM_CLOSE:
WLog_DBG(TAG, "MM_WOM_CLOSE");
SetEvent(winmm->next);
break;
case MM_WOM_DONE:
{
UINT32 wTimeDelta;
lpWaveHdr = (LPWAVEHDR) dwParam1;
WLog_DBG(TAG, "MM_WOM_DONE");
lpWaveHdr = (LPWAVEHDR) dwParam1;
free(lpWaveHdr);
SetEvent(winmm->next);
break;
if (!lpWaveHdr)
return;
wave = (RDPSND_WAVE*) lpWaveHdr->dwUser;
if (!wave)
return;
WLog_DBG(TAG, "MM_WOM_DONE: dwBufferLength: %"PRIu32" cBlockNo: %"PRIu8"",
lpWaveHdr->dwBufferLength, wave->cBlockNo);
wave->wLocalTimeB = GetTickCount();
wTimeDelta = wave->wLocalTimeB - wave->wLocalTimeA;
wave->wTimeStampB = wave->wTimeStampA + wTimeDelta;
winmm->device.WaveConfirm(&(winmm->device), wave);
free(lpWaveHdr->lpData);
free(lpWaveHdr);
free(wave);
}
default:
WLog_DBG(TAG, "UNKNOWN [0x%08"PRIx32"]", uMsg);
break;
}
}
static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency)
static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
UINT32 latency)
{
MMRESULT mmResult;
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
@ -153,15 +127,15 @@ static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
if (winmm->hWaveOut)
return TRUE;
rdpsnd_winmm_set_format(device, format, latency);
freerdp_dsp_context_reset_adpcm(winmm->dsp_context);
if (!rdpsnd_winmm_set_format(device, format, latency))
return FALSE;
mmResult = waveOutOpen(&winmm->hWaveOut, WAVE_MAPPER, &winmm->format,
(DWORD_PTR) rdpsnd_winmm_callback_function, (DWORD_PTR) winmm, CALLBACK_FUNCTION);
(DWORD_PTR) rdpsnd_winmm_callback_function, (DWORD_PTR) winmm, CALLBACK_FUNCTION);
if (mmResult != MMSYSERR_NOERROR)
{
WLog_ERR(TAG, "waveOutOpen failed: %"PRIu32"", mmResult);
WLog_ERR(TAG, "waveOutOpen failed: %"PRIu32"", mmResult);
return FALSE;
}
@ -169,7 +143,7 @@ static BOOL rdpsnd_winmm_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format,
if (mmResult != MMSYSERR_NOERROR)
{
WLog_ERR(TAG, "waveOutSetVolume failed: %"PRIu32"", mmResult);
WLog_ERR(TAG, "waveOutSetVolume failed: %"PRIu32"", mmResult);
return FALSE;
}
@ -184,14 +158,13 @@ static void rdpsnd_winmm_close(rdpsndDevicePlugin* device)
if (winmm->hWaveOut)
{
mmResult = waveOutReset(winmm->hWaveOut);
mmResult = waveOutClose(winmm->hWaveOut);
if (mmResult != MMSYSERR_NOERROR)
{
WLog_ERR(TAG, "waveOutClose failure: %"PRIu32"", mmResult);
}
winmm->hWaveOut = NULL;
}
}
@ -203,14 +176,12 @@ static void rdpsnd_winmm_free(rdpsndDevicePlugin* device)
if (winmm)
{
rdpsnd_winmm_close(device);
freerdp_dsp_context_free(winmm->dsp_context);
CloseHandle(winmm->next);
free(winmm);
}
}
static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
static BOOL rdpsnd_winmm_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format)
{
MMRESULT result;
WAVEFORMATEX out;
@ -231,9 +202,7 @@ static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device)
DWORD dwVolume;
UINT16 dwVolumeLeft;
UINT16 dwVolumeRight;
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
dwVolumeLeft = ((50 * 0xFFFF) / 100); /* 50% */
dwVolumeRight = ((50 * 0xFFFF) / 100); /* 50% */
dwVolume = (dwVolumeLeft << 16) | dwVolumeRight;
@ -242,14 +211,12 @@ static UINT32 rdpsnd_winmm_get_volume(rdpsndDevicePlugin* device)
return dwVolume;
waveOutGetVolume(winmm->hWaveOut, &dwVolume);
return dwVolume;
}
static BOOL rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
{
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
winmm->volume = value;
if (!winmm->hWaveOut)
@ -258,72 +225,38 @@ static BOOL rdpsnd_winmm_set_volume(rdpsndDevicePlugin* device, UINT32 value)
return (waveOutSetVolume(winmm->hWaveOut, value) == MMSYSERR_NOERROR);
}
static BOOL rdpsnd_winmm_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
static void rdpsnd_winmm_start(rdpsndDevicePlugin* device)
{
int length;
BYTE* data;
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
if (winmm->wformat == WAVE_FORMAT_ADPCM)
{
winmm->dsp_context->decode_ms_adpcm(winmm->dsp_context,
wave->data, wave->length, winmm->format.nChannels, winmm->block_size);
length = winmm->dsp_context->adpcm_size;
data = winmm->dsp_context->adpcm_buffer;
}
else if (winmm->wformat == WAVE_FORMAT_DVI_ADPCM)
{
winmm->dsp_context->decode_ima_adpcm(winmm->dsp_context,
wave->data, wave->length, winmm->format.nChannels, winmm->block_size);
length = winmm->dsp_context->adpcm_size;
data = winmm->dsp_context->adpcm_buffer;
}
else
{
length = wave->length;
data = wave->data;
}
wave->data = (BYTE*) malloc(length);
if (!wave->data)
return FALSE;
CopyMemory(wave->data, data, length);
wave->length = length;
return TRUE;
//rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
}
void rdpsnd_winmm_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
static UINT rdpsnd_winmm_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
{
MMRESULT mmResult;
LPWAVEHDR lpWaveHdr;
rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
if (!winmm->hWaveOut)
return;
wave->AutoConfirm = FALSE;
return 0;
lpWaveHdr = (LPWAVEHDR) malloc(sizeof(WAVEHDR));
if (!lpWaveHdr)
return;
return 0;
ZeroMemory(lpWaveHdr, sizeof(WAVEHDR));
lpWaveHdr->dwFlags = 0;
lpWaveHdr->dwLoops = 0;
lpWaveHdr->lpData = (LPSTR) wave->data;
lpWaveHdr->dwBufferLength = wave->length;
lpWaveHdr->dwUser = (DWORD_PTR) wave;
lpWaveHdr->lpData = (LPSTR) data;
lpWaveHdr->dwBufferLength = size;
lpWaveHdr->dwUser = NULL;
lpWaveHdr->lpNext = NULL;
mmResult = waveOutPrepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
if (mmResult != MMSYSERR_NOERROR)
{
WLog_ERR(TAG, "waveOutPrepareHeader failure: %"PRIu32"", mmResult);
return;
return 0;
}
mmResult = waveOutWrite(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
@ -333,18 +266,16 @@ void rdpsnd_winmm_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
WLog_ERR(TAG, "waveOutWrite failure: %"PRIu32"", mmResult);
waveOutUnprepareHeader(winmm->hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
free(lpWaveHdr);
return;
return 0;
}
}
static void rdpsnd_winmm_start(rdpsndDevicePlugin* device)
{
//rdpsndWinmmPlugin* winmm = (rdpsndWinmmPlugin*) device;
WaitForSingleObject(winmm->next, INFINITE);
return 10; /* TODO: Get real latencry in [ms] */
}
static void rdpsnd_winmm_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args)
{
}
#ifdef BUILTIN_CHANNELS
@ -362,38 +293,30 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
{
ADDIN_ARGV* args;
rdpsndWinmmPlugin* winmm;
winmm = (rdpsndWinmmPlugin*) calloc(1, sizeof(rdpsndWinmmPlugin));
if (!winmm)
return CHANNEL_RC_NO_MEMORY;
winmm->device.DisableConfirmThread = TRUE;
winmm->device.Open = rdpsnd_winmm_open;
winmm->device.FormatSupported = rdpsnd_winmm_format_supported;
winmm->device.SetFormat = rdpsnd_winmm_set_format;
winmm->device.GetVolume = rdpsnd_winmm_get_volume;
winmm->device.SetVolume = rdpsnd_winmm_set_volume;
winmm->device.WaveDecode = rdpsnd_winmm_wave_decode;
winmm->device.WavePlay = rdpsnd_winmm_wave_play;
winmm->device.Start = rdpsnd_winmm_start;
winmm->device.Play = rdpsnd_winmm_play;
winmm->device.Close = rdpsnd_winmm_close;
winmm->device.Free = rdpsnd_winmm_free;
winmm->next = CreateEventA(NULL, FALSE, FALSE, "winmm-play-event");
args = pEntryPoints->args;
rdpsnd_winmm_parse_addin_args((rdpsndDevicePlugin*) winmm, args);
winmm->dsp_context = freerdp_dsp_context_new();
if (!winmm->dsp_context)
if (!winmm->next)
{
free(winmm);
return CHANNEL_RC_NO_MEMORY;
}
args = pEntryPoints->args;
rdpsnd_winmm_parse_addin_args((rdpsndDevicePlugin*) winmm, args);
winmm->volume = 0xFFFFFFFF;
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) winmm);
return CHANNEL_RC_OK;
}

View File

@ -246,7 +246,6 @@ static DWORD WINAPI rdpsnd_server_thread(LPVOID arg)
RdpsndServerContext* context;
UINT error = CHANNEL_RC_OK;
context = (RdpsndServerContext*)arg;
nCount = 0;
events[nCount++] = context->priv->channelEvent;
events[nCount++] = context->priv->StopEvent;
@ -393,7 +392,7 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context,
context->priv->out_buffer_size = out_buffer_size;
}
freerdp_dsp_context_reset_adpcm(context->priv->dsp_context);
freerdp_dsp_context_reset(context->priv->dsp_context, format);
out:
LeaveCriticalSection(&context->priv->lock);
return error;
@ -408,103 +407,55 @@ out:
static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context,
UINT16 wTimestamp)
{
int size;
BYTE* src;
int frames;
int fill_size;
size_t length;
size_t start, end = 0;
const BYTE* src;
BOOL status;
AUDIO_FORMAT* format;
int tbytes_per_frame;
ULONG written;
wStream* s = context->priv->rdpsnd_pdu;
UINT error = CHANNEL_RC_OK;
format = &context->client_formats[context->selected_client_format];
tbytes_per_frame = format->nChannels * context->priv->src_bytes_per_sample;
if ((format->nSamplesPerSec == context->src_format.nSamplesPerSec) &&
(format->nChannels == context->src_format.nChannels))
{
src = context->priv->out_buffer;
frames = context->priv->out_pending_frames;
}
else
{
context->priv->dsp_context->resample(context->priv->dsp_context,
context->priv->out_buffer,
context->priv->src_bytes_per_sample, context->src_format.nChannels,
context->src_format.nSamplesPerSec, context->priv->out_pending_frames,
format->nChannels, format->nSamplesPerSec);
frames = context->priv->dsp_context->resampled_frames;
src = context->priv->dsp_context->resampled_buffer;
}
size = frames * tbytes_per_frame;
if (format->wFormatTag == WAVE_FORMAT_DVI_ADPCM)
{
context->priv->dsp_context->encode_ima_adpcm(context->priv->dsp_context,
src, size, format->nChannels, format->nBlockAlign);
src = context->priv->dsp_context->adpcm_buffer;
size = context->priv->dsp_context->adpcm_size;
}
else if (format->wFormatTag == WAVE_FORMAT_ADPCM)
{
context->priv->dsp_context->encode_ms_adpcm(context->priv->dsp_context,
src, size, format->nChannels, format->nBlockAlign);
src = context->priv->dsp_context->adpcm_buffer;
size = context->priv->dsp_context->adpcm_size;
}
context->block_no = (context->block_no + 1) % 256;
/* Fill to nBlockAlign for the last audio packet */
fill_size = 0;
if ((format->wFormatTag == WAVE_FORMAT_DVI_ADPCM
|| format->wFormatTag == WAVE_FORMAT_ADPCM) &&
(context->priv->out_pending_frames < context->priv->out_frames)
&& ((size % format->nBlockAlign) != 0))
{
fill_size = format->nBlockAlign - (size % format->nBlockAlign);
}
/* WaveInfo PDU */
Stream_SetPosition(s, 0);
Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */
Stream_Write_UINT8(s, 0); /* bPad */
Stream_Write_UINT16(s, size + fill_size + 8); /* BodySize */
Stream_Write_UINT16(s, 0); /* BodySize */
Stream_Write_UINT16(s, wTimestamp); /* wTimeStamp */
Stream_Write_UINT16(s, context->selected_client_format); /* wFormatNo */
Stream_Write_UINT8(s, context->block_no); /* cBlockNo */
Stream_Seek(s, 3); /* bPad */
Stream_Write(s, src, 4);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle,
(PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
start = Stream_GetPosition(s);
src = context->priv->out_buffer;
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
if (!status)
if (!freerdp_dsp_encode(context->priv->dsp_context, format, src, length, s))
status = ERROR_INTERNAL_ERROR;
else
{
/* Set stream size */
end = Stream_GetPosition(s);
Stream_SetPosition(s, 2);
Stream_Write_UINT16(s, end - start + 8);
Stream_SetPosition(s, end);
context->block_no = (context->block_no + 1) % 256;
status = WTSVirtualChannelWrite(context->priv->ChannelHandle,
(PCHAR) Stream_Buffer(s), start + 4, &written);
}
if (status != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
error = ERROR_INTERNAL_ERROR;
goto out;
}
Stream_SetPosition(s, 0);
/* Wave PDU */
if (!Stream_EnsureRemainingCapacity(s, size + fill_size))
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
Stream_SetPosition(s, start);
Stream_Write_UINT32(s, 0); /* bPad */
Stream_Write(s, src + 4, size - 4);
if (fill_size > 0)
Stream_Zero(s, fill_size);
Stream_SetPosition(s, start);
status = WTSVirtualChannelWrite(context->priv->ChannelHandle,
(PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
(PCHAR) Stream_Pointer(s), end - start, &written);
if (!status)
{
@ -578,7 +529,6 @@ static UINT rdpsnd_server_set_volume(RdpsndServerContext* context, int left,
BOOL status;
ULONG written;
wStream* s = context->priv->rdpsnd_pdu;
Stream_Write_UINT8(s, SNDC_SETVOLUME);
Stream_Write_UINT8(s, 0);
Stream_Seek_UINT16(s);
@ -790,7 +740,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
goto out_free;
}
priv->dsp_context = freerdp_dsp_context_new();
priv->dsp_context = freerdp_dsp_context_new(TRUE);
if (!priv->dsp_context)
{

View File

@ -40,33 +40,34 @@ typedef struct _TSMFALSAAudioDevice
ITSMFAudioDevice iface;
char device[32];
snd_pcm_t *out_handle;
snd_pcm_t* out_handle;
UINT32 source_rate;
UINT32 actual_rate;
UINT32 source_channels;
UINT32 actual_channels;
UINT32 bytes_per_sample;
FREERDP_DSP_CONTEXT *dsp_context;
} TSMFAlsaAudioDevice;
static BOOL tsmf_alsa_open_device(TSMFAlsaAudioDevice *alsa)
static BOOL tsmf_alsa_open_device(TSMFAlsaAudioDevice* alsa)
{
int error;
error = snd_pcm_open(&alsa->out_handle, alsa->device, SND_PCM_STREAM_PLAYBACK, 0);
if(error < 0)
if (error < 0)
{
WLog_ERR(TAG, "failed to open device %s", alsa->device);
return FALSE;
}
DEBUG_TSMF("open device %s", alsa->device);
return TRUE;
}
static BOOL tsmf_alsa_open(ITSMFAudioDevice *audio, const char *device)
static BOOL tsmf_alsa_open(ITSMFAudioDevice* audio, const char* device)
{
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
if(!device)
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
if (!device)
{
strncpy(alsa->device, "default", sizeof(alsa->device));
}
@ -74,114 +75,109 @@ static BOOL tsmf_alsa_open(ITSMFAudioDevice *audio, const char *device)
{
strncpy(alsa->device, device, sizeof(alsa->device) - 1);
}
return tsmf_alsa_open_device(alsa);
}
static BOOL tsmf_alsa_set_format(ITSMFAudioDevice *audio,
UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
static BOOL tsmf_alsa_set_format(ITSMFAudioDevice* audio,
UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
{
int error;
snd_pcm_uframes_t frames;
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
if(!alsa->out_handle)
snd_pcm_hw_params_t* hw_params;
snd_pcm_sw_params_t* sw_params;
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
if (!alsa->out_handle)
return FALSE;
snd_pcm_drop(alsa->out_handle);
alsa->actual_rate = alsa->source_rate = sample_rate;
alsa->actual_channels = alsa->source_channels = channels;
alsa->bytes_per_sample = bits_per_sample / 8;
error = snd_pcm_hw_params_malloc(&hw_params);
if(error < 0)
if (error < 0)
{
WLog_ERR(TAG, "snd_pcm_hw_params_malloc failed");
return FALSE;
}
snd_pcm_hw_params_any(alsa->out_handle, hw_params);
snd_pcm_hw_params_set_access(alsa->out_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED);
SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(alsa->out_handle, hw_params,
SND_PCM_FORMAT_S16_LE);
SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate_near(alsa->out_handle, hw_params,
&alsa->actual_rate, NULL);
&alsa->actual_rate, NULL);
snd_pcm_hw_params_set_channels_near(alsa->out_handle, hw_params,
&alsa->actual_channels);
&alsa->actual_channels);
frames = sample_rate;
snd_pcm_hw_params_set_buffer_size_near(alsa->out_handle, hw_params,
&frames);
&frames);
snd_pcm_hw_params(alsa->out_handle, hw_params);
snd_pcm_hw_params_free(hw_params);
error = snd_pcm_sw_params_malloc(&sw_params);
if(error < 0)
if (error < 0)
{
WLog_ERR(TAG, "snd_pcm_sw_params_malloc");
return FALSE;
}
snd_pcm_sw_params_current(alsa->out_handle, sw_params);
snd_pcm_sw_params_set_start_threshold(alsa->out_handle, sw_params,
frames / 2);
frames / 2);
snd_pcm_sw_params(alsa->out_handle, sw_params);
snd_pcm_sw_params_free(sw_params);
snd_pcm_prepare(alsa->out_handle);
DEBUG_TSMF("sample_rate %"PRIu32" channels %"PRIu32" bits_per_sample %"PRIu32"",
sample_rate, channels, bits_per_sample);
sample_rate, channels, bits_per_sample);
DEBUG_TSMF("hardware buffer %lu frames", frames);
if((alsa->actual_rate != alsa->source_rate) ||
(alsa->actual_channels != alsa->source_channels))
if ((alsa->actual_rate != alsa->source_rate) ||
(alsa->actual_channels != alsa->source_channels))
{
DEBUG_TSMF("actual rate %"PRIu32" / channel %"PRIu32" is different "
"from source rate %"PRIu32" / channel %"PRIu32", resampling required.",
alsa->actual_rate, alsa->actual_channels,
alsa->source_rate, alsa->source_channels);
alsa->actual_rate, alsa->actual_channels,
alsa->source_rate, alsa->source_channels);
}
return TRUE;
}
static BOOL tsmf_alsa_play(ITSMFAudioDevice *audio, BYTE *data, UINT32 data_size)
static BOOL tsmf_alsa_play(ITSMFAudioDevice* audio, const BYTE* src, UINT32 data_size)
{
int len;
int error;
int frames;
BYTE *end;
BYTE *src;
BYTE *pindex;
const BYTE* end;
const BYTE* pindex;
int rbytes_per_frame;
int sbytes_per_frame;
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
DEBUG_TSMF("data_size %"PRIu32"", data_size);
if(alsa->out_handle)
if (alsa->out_handle)
{
sbytes_per_frame = alsa->source_channels * alsa->bytes_per_sample;
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_sample;
if((alsa->source_rate == alsa->actual_rate) &&
(alsa->source_channels == alsa->actual_channels))
{
src = data;
}
else
{
alsa->dsp_context->resample(alsa->dsp_context, data, alsa->bytes_per_sample,
alsa->source_channels, alsa->source_rate, data_size / sbytes_per_frame,
alsa->actual_channels, alsa->actual_rate);
frames = alsa->dsp_context->resampled_frames;
DEBUG_TSMF("resampled %"PRIu32" frames at %"PRIu32" to %d frames at %"PRIu32"",
data_size / sbytes_per_frame, alsa->source_rate, frames, alsa->actual_rate);
data_size = frames * rbytes_per_frame;
src = alsa->dsp_context->resampled_buffer;
}
pindex = src;
end = pindex + data_size;
while(pindex < end)
while (pindex < end)
{
len = end - pindex;
frames = len / rbytes_per_frame;
error = snd_pcm_writei(alsa->out_handle, pindex, frames);
if(error == -EPIPE)
if (error == -EPIPE)
{
snd_pcm_recover(alsa->out_handle, error, 0);
error = 0;
}
else if(error < 0)
else if (error < 0)
{
DEBUG_TSMF("error len %d", error);
snd_pcm_close(alsa->out_handle);
@ -189,45 +185,51 @@ static BOOL tsmf_alsa_play(ITSMFAudioDevice *audio, BYTE *data, UINT32 data_size
tsmf_alsa_open_device(alsa);
break;
}
DEBUG_TSMF("%d frames played.", error);
if(error == 0)
if (error == 0)
break;
pindex += error * rbytes_per_frame;
}
}
free(data);
return TRUE;
}
static UINT64 tsmf_alsa_get_latency(ITSMFAudioDevice *audio)
static UINT64 tsmf_alsa_get_latency(ITSMFAudioDevice* audio)
{
UINT64 latency = 0;
snd_pcm_sframes_t frames = 0;
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
if(alsa->out_handle && alsa->actual_rate > 0 &&
snd_pcm_delay(alsa->out_handle, &frames) == 0 &&
frames > 0)
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
if (alsa->out_handle && alsa->actual_rate > 0 &&
snd_pcm_delay(alsa->out_handle, &frames) == 0 &&
frames > 0)
{
latency = ((UINT64)frames) * 10000000LL / (UINT64) alsa->actual_rate;
}
return latency;
}
static BOOL tsmf_alsa_flush(ITSMFAudioDevice *audio)
static BOOL tsmf_alsa_flush(ITSMFAudioDevice* audio)
{
return TRUE;
}
static void tsmf_alsa_free(ITSMFAudioDevice *audio)
static void tsmf_alsa_free(ITSMFAudioDevice* audio)
{
TSMFAlsaAudioDevice *alsa = (TSMFAlsaAudioDevice *) audio;
TSMFAlsaAudioDevice* alsa = (TSMFAlsaAudioDevice*) audio;
DEBUG_TSMF("");
if(alsa->out_handle)
if (alsa->out_handle)
{
snd_pcm_drain(alsa->out_handle);
snd_pcm_close(alsa->out_handle);
}
freerdp_dsp_context_free(alsa->dsp_context);
free(alsa);
}
@ -237,10 +239,10 @@ static void tsmf_alsa_free(ITSMFAudioDevice *audio)
#define freerdp_tsmf_client_audio_subsystem_entry FREERDP_API freerdp_tsmf_client_audio_subsystem_entry
#endif
ITSMFAudioDevice *freerdp_tsmf_client_audio_subsystem_entry(void)
ITSMFAudioDevice* freerdp_tsmf_client_audio_subsystem_entry(void)
{
TSMFAlsaAudioDevice *alsa;
alsa = (TSMFAlsaAudioDevice *) malloc(sizeof(TSMFAlsaAudioDevice));
TSMFAlsaAudioDevice* alsa;
alsa = (TSMFAlsaAudioDevice*) malloc(sizeof(TSMFAlsaAudioDevice));
ZeroMemory(alsa, sizeof(TSMFAlsaAudioDevice));
alsa->iface.Open = tsmf_alsa_open;
alsa->iface.SetFormat = tsmf_alsa_set_format;
@ -248,6 +250,5 @@ ITSMFAudioDevice *freerdp_tsmf_client_audio_subsystem_entry(void)
alsa->iface.GetLatency = tsmf_alsa_get_latency;
alsa->iface.Flush = tsmf_alsa_flush;
alsa->iface.Free = tsmf_alsa_free;
alsa->dsp_context = freerdp_dsp_context_new();
return (ITSMFAudioDevice *) alsa;
return (ITSMFAudioDevice*) alsa;
}

View File

@ -35,9 +35,9 @@
#include <limits.h>
#include <unistd.h>
#if defined(__OpenBSD__)
#include <soundcard.h>
#include <soundcard.h>
#else
#include <sys/soundcard.h>
#include <sys/soundcard.h>
#endif
#include <sys/ioctl.h>
@ -129,7 +129,8 @@ static BOOL tsmf_oss_open(ITSMFAudioDevice* audio, const char* device)
return TRUE;
}
static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels,
UINT32 bits_per_sample)
{
int tmp;
TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
@ -161,11 +162,11 @@ static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UIN
OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
DEBUG_TSMF("sample_rate %"PRIu32" channels %"PRIu32" bits_per_sample %"PRIu32"",
sample_rate, channels, bits_per_sample);
sample_rate, channels, bits_per_sample);
return TRUE;
}
static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, BYTE* data, UINT32 data_size)
static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size)
{
int status;
UINT32 offset;
@ -176,10 +177,7 @@ static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, BYTE* data, UINT32 data_size)
return FALSE;
if (data == NULL || data_size == 0)
{
free(data);
return TRUE;
}
offset = 0;
oss->data_size_last = data_size;
@ -191,14 +189,12 @@ static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, BYTE* data, UINT32 data_size)
if (status < 0)
{
OSS_LOG_ERR("write fail", errno);
free(data);
return FALSE;
}
offset += status;
}
free(data);
return TRUE;
}

View File

@ -37,68 +37,81 @@ typedef struct _TSMFPulseAudioDevice
ITSMFAudioDevice iface;
char device[32];
pa_threaded_mainloop *mainloop;
pa_context *context;
pa_threaded_mainloop* mainloop;
pa_context* context;
pa_sample_spec sample_spec;
pa_stream *stream;
pa_stream* stream;
} TSMFPulseAudioDevice;
static void tsmf_pulse_context_state_callback(pa_context *context, void *userdata)
static void tsmf_pulse_context_state_callback(pa_context* context, void* userdata)
{
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) userdata;
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
pa_context_state_t state;
state = pa_context_get_state(context);
switch(state)
switch (state)
{
case PA_CONTEXT_READY:
DEBUG_TSMF("PA_CONTEXT_READY");
pa_threaded_mainloop_signal(pulse->mainloop, 0);
break;
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
DEBUG_TSMF("state %d", state);
pa_threaded_mainloop_signal(pulse->mainloop, 0);
break;
default:
DEBUG_TSMF("state %d", state);
break;
}
}
static BOOL tsmf_pulse_connect(TSMFPulseAudioDevice *pulse)
static BOOL tsmf_pulse_connect(TSMFPulseAudioDevice* pulse)
{
pa_context_state_t state;
if(!pulse->context)
if (!pulse->context)
return FALSE;
if(pa_context_connect(pulse->context, NULL, 0, NULL))
if (pa_context_connect(pulse->context, NULL, 0, NULL))
{
WLog_ERR(TAG, "pa_context_connect failed (%d)",
pa_context_errno(pulse->context));
pa_context_errno(pulse->context));
return FALSE;
}
pa_threaded_mainloop_lock(pulse->mainloop);
if(pa_threaded_mainloop_start(pulse->mainloop) < 0)
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
{
pa_threaded_mainloop_unlock(pulse->mainloop);
WLog_ERR(TAG, "pa_threaded_mainloop_start failed (%d)",
pa_context_errno(pulse->context));
pa_context_errno(pulse->context));
return FALSE;
}
for(;;)
for (;;)
{
state = pa_context_get_state(pulse->context);
if(state == PA_CONTEXT_READY)
if (state == PA_CONTEXT_READY)
break;
if(!PA_CONTEXT_IS_GOOD(state))
if (!PA_CONTEXT_IS_GOOD(state))
{
DEBUG_TSMF("bad context state (%d)",
pa_context_errno(pulse->context));
pa_context_errno(pulse->context));
break;
}
pa_threaded_mainloop_wait(pulse->mainloop);
}
pa_threaded_mainloop_unlock(pulse->mainloop);
if(state == PA_CONTEXT_READY)
if (state == PA_CONTEXT_READY)
{
DEBUG_TSMF("connected");
return TRUE;
@ -110,90 +123,104 @@ static BOOL tsmf_pulse_connect(TSMFPulseAudioDevice *pulse)
}
}
static BOOL tsmf_pulse_open(ITSMFAudioDevice *audio, const char *device)
static BOOL tsmf_pulse_open(ITSMFAudioDevice* audio, const char* device)
{
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
if(device)
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
if (device)
{
strncpy(pulse->device, device, sizeof(pulse->device) - 1);
}
pulse->mainloop = pa_threaded_mainloop_new();
if(!pulse->mainloop)
if (!pulse->mainloop)
{
WLog_ERR(TAG, "pa_threaded_mainloop_new failed");
return FALSE;
}
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
if(!pulse->context)
if (!pulse->context)
{
WLog_ERR(TAG, "pa_context_new failed");
return FALSE;
}
pa_context_set_state_callback(pulse->context, tsmf_pulse_context_state_callback, pulse);
if(!tsmf_pulse_connect(pulse))
if (!tsmf_pulse_connect(pulse))
{
WLog_ERR(TAG, "tsmf_pulse_connect failed");
return FALSE;
}
DEBUG_TSMF("open device %s", pulse->device);
return TRUE;
}
static void tsmf_pulse_stream_success_callback(pa_stream *stream, int success, void *userdata)
static void tsmf_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
{
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) userdata;
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
pa_threaded_mainloop_signal(pulse->mainloop, 0);
}
static void tsmf_pulse_wait_for_operation(TSMFPulseAudioDevice *pulse, pa_operation *operation)
static void tsmf_pulse_wait_for_operation(TSMFPulseAudioDevice* pulse, pa_operation* operation)
{
if(operation == NULL)
if (operation == NULL)
return;
while(pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
{
pa_threaded_mainloop_wait(pulse->mainloop);
}
pa_operation_unref(operation);
}
static void tsmf_pulse_stream_state_callback(pa_stream *stream, void *userdata)
static void tsmf_pulse_stream_state_callback(pa_stream* stream, void* userdata)
{
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) userdata;
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
pa_stream_state_t state;
state = pa_stream_get_state(stream);
switch(state)
switch (state)
{
case PA_STREAM_READY:
DEBUG_TSMF("PA_STREAM_READY");
pa_threaded_mainloop_signal(pulse->mainloop, 0);
break;
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
DEBUG_TSMF("state %d", state);
pa_threaded_mainloop_signal(pulse->mainloop, 0);
break;
default:
DEBUG_TSMF("state %d", state);
break;
}
}
static void tsmf_pulse_stream_request_callback(pa_stream *stream, size_t length, void *userdata)
static void tsmf_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
{
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) userdata;
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
DEBUG_TSMF("%"PRIdz"", length);
pa_threaded_mainloop_signal(pulse->mainloop, 0);
}
static BOOL tsmf_pulse_close_stream(TSMFPulseAudioDevice *pulse)
static BOOL tsmf_pulse_close_stream(TSMFPulseAudioDevice* pulse)
{
if(!pulse->context || !pulse->stream)
if (!pulse->context || !pulse->stream)
return FALSE;
DEBUG_TSMF("");
pa_threaded_mainloop_lock(pulse->mainloop);
pa_stream_set_write_callback(pulse->stream, NULL, NULL);
tsmf_pulse_wait_for_operation(pulse,
pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
pa_stream_disconnect(pulse->stream);
pa_stream_unref(pulse->stream);
pulse->stream = NULL;
@ -201,57 +228,68 @@ static BOOL tsmf_pulse_close_stream(TSMFPulseAudioDevice *pulse)
return TRUE;
}
static BOOL tsmf_pulse_open_stream(TSMFPulseAudioDevice *pulse)
static BOOL tsmf_pulse_open_stream(TSMFPulseAudioDevice* pulse)
{
pa_stream_state_t state;
pa_buffer_attr buffer_attr = { 0 };
if(!pulse->context)
if (!pulse->context)
return FALSE;
DEBUG_TSMF("");
pa_threaded_mainloop_lock(pulse->mainloop);
pulse->stream = pa_stream_new(pulse->context, "freerdp",
&pulse->sample_spec, NULL);
if(!pulse->stream)
&pulse->sample_spec, NULL);
if (!pulse->stream)
{
pa_threaded_mainloop_unlock(pulse->mainloop);
WLog_ERR(TAG, "pa_stream_new failed (%d)",
pa_context_errno(pulse->context));
pa_context_errno(pulse->context));
return FALSE;
}
pa_stream_set_state_callback(pulse->stream,
tsmf_pulse_stream_state_callback, pulse);
tsmf_pulse_stream_state_callback, pulse);
pa_stream_set_write_callback(pulse->stream,
tsmf_pulse_stream_request_callback, pulse);
tsmf_pulse_stream_request_callback, pulse);
buffer_attr.maxlength = pa_usec_to_bytes(500000, &pulse->sample_spec);
buffer_attr.tlength = pa_usec_to_bytes(250000, &pulse->sample_spec);
buffer_attr.prebuf = (UINT32) -1;
buffer_attr.minreq = (UINT32) -1;
buffer_attr.fragsize = (UINT32) -1;
if(pa_stream_connect_playback(pulse->stream,
pulse->device[0] ? pulse->device : NULL, &buffer_attr,
PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
NULL, NULL) < 0)
buffer_attr.prebuf = (UINT32) - 1;
buffer_attr.minreq = (UINT32) - 1;
buffer_attr.fragsize = (UINT32) - 1;
if (pa_stream_connect_playback(pulse->stream,
pulse->device[0] ? pulse->device : NULL, &buffer_attr,
PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
NULL, NULL) < 0)
{
pa_threaded_mainloop_unlock(pulse->mainloop);
WLog_ERR(TAG, "pa_stream_connect_playback failed (%d)",
pa_context_errno(pulse->context));
pa_context_errno(pulse->context));
return FALSE;
}
for(;;)
for (;;)
{
state = pa_stream_get_state(pulse->stream);
if(state == PA_STREAM_READY)
if (state == PA_STREAM_READY)
break;
if(!PA_STREAM_IS_GOOD(state))
if (!PA_STREAM_IS_GOOD(state))
{
WLog_ERR(TAG, "bad stream state (%d)",
pa_context_errno(pulse->context));
pa_context_errno(pulse->context));
break;
}
pa_threaded_mainloop_wait(pulse->mainloop);
}
pa_threaded_mainloop_unlock(pulse->mainloop);
if(state == PA_STREAM_READY)
if (state == PA_STREAM_READY)
{
DEBUG_TSMF("connected");
return TRUE;
@ -263,118 +301,133 @@ static BOOL tsmf_pulse_open_stream(TSMFPulseAudioDevice *pulse)
}
}
static BOOL tsmf_pulse_set_format(ITSMFAudioDevice *audio,
UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
static BOOL tsmf_pulse_set_format(ITSMFAudioDevice* audio,
UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample)
{
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
DEBUG_TSMF("sample_rate %"PRIu32" channels %"PRIu32" bits_per_sample %"PRIu32"",
sample_rate, channels, bits_per_sample);
sample_rate, channels, bits_per_sample);
pulse->sample_spec.rate = sample_rate;
pulse->sample_spec.channels = channels;
pulse->sample_spec.format = PA_SAMPLE_S16LE;
return tsmf_pulse_open_stream(pulse);
}
static BOOL tsmf_pulse_play(ITSMFAudioDevice *audio, BYTE *data, UINT32 data_size)
static BOOL tsmf_pulse_play(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size)
{
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
BYTE *src;
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
const BYTE* src;
int len;
int ret;
DEBUG_TSMF("data_size %"PRIu32"", data_size);
if(pulse->stream)
if (pulse->stream)
{
pa_threaded_mainloop_lock(pulse->mainloop);
src = data;
while(data_size > 0)
while (data_size > 0)
{
while((len = pa_stream_writable_size(pulse->stream)) == 0)
while ((len = pa_stream_writable_size(pulse->stream)) == 0)
{
DEBUG_TSMF("waiting");
pa_threaded_mainloop_wait(pulse->mainloop);
}
if(len < 0)
if (len < 0)
break;
if(len > data_size)
if (len > data_size)
len = data_size;
ret = pa_stream_write(pulse->stream, src, len, NULL, 0LL, PA_SEEK_RELATIVE);
if(ret < 0)
if (ret < 0)
{
DEBUG_TSMF("pa_stream_write failed (%d)",
pa_context_errno(pulse->context));
pa_context_errno(pulse->context));
break;
}
src += len;
data_size -= len;
}
pa_threaded_mainloop_unlock(pulse->mainloop);
}
free(data);
return TRUE;
}
static UINT64 tsmf_pulse_get_latency(ITSMFAudioDevice *audio)
static UINT64 tsmf_pulse_get_latency(ITSMFAudioDevice* audio)
{
pa_usec_t usec;
UINT64 latency = 0;
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
if(pulse->stream && pa_stream_get_latency(pulse->stream, &usec, NULL) == 0)
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
if (pulse->stream && pa_stream_get_latency(pulse->stream, &usec, NULL) == 0)
{
latency = ((UINT64)usec) * 10LL;
}
return latency;
}
static BOOL tsmf_pulse_flush(ITSMFAudioDevice *audio)
static BOOL tsmf_pulse_flush(ITSMFAudioDevice* audio)
{
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
pa_threaded_mainloop_lock(pulse->mainloop);
tsmf_pulse_wait_for_operation(pulse,
pa_stream_flush(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
pa_stream_flush(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
pa_threaded_mainloop_unlock(pulse->mainloop);
return TRUE;
}
static void tsmf_pulse_free(ITSMFAudioDevice *audio)
static void tsmf_pulse_free(ITSMFAudioDevice* audio)
{
TSMFPulseAudioDevice *pulse = (TSMFPulseAudioDevice *) audio;
TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
DEBUG_TSMF("");
tsmf_pulse_close_stream(pulse);
if(pulse->mainloop)
if (pulse->mainloop)
{
pa_threaded_mainloop_stop(pulse->mainloop);
}
if(pulse->context)
if (pulse->context)
{
pa_context_disconnect(pulse->context);
pa_context_unref(pulse->context);
pulse->context = NULL;
}
if(pulse->mainloop)
if (pulse->mainloop)
{
pa_threaded_mainloop_free(pulse->mainloop);
pulse->mainloop = NULL;
}
free(pulse);
}
#ifdef BUILTIN_CHANNELS
ITSMFAudioDevice *pulse_freerdp_tsmf_client_audio_subsystem_entry(void)
ITSMFAudioDevice* pulse_freerdp_tsmf_client_audio_subsystem_entry(void)
#else
FREERDP_API ITSMFAudioDevice *freerdp_tsmf_client_audio_subsystem_entry(void)
FREERDP_API ITSMFAudioDevice* freerdp_tsmf_client_audio_subsystem_entry(void)
#endif
{
TSMFPulseAudioDevice *pulse;
TSMFPulseAudioDevice* pulse;
pulse = (TSMFPulseAudioDevice*) calloc(1, sizeof(TSMFPulseAudioDevice));
pulse = (TSMFPulseAudioDevice *) calloc(1, sizeof(TSMFPulseAudioDevice));
if (!pulse)
return NULL;
pulse->iface.Open = tsmf_pulse_open;
pulse->iface.SetFormat = tsmf_pulse_set_format;
pulse->iface.Play = tsmf_pulse_play;
pulse->iface.GetLatency = tsmf_pulse_get_latency;
pulse->iface.Flush = tsmf_pulse_flush;
pulse->iface.Free = tsmf_pulse_free;
return (ITSMFAudioDevice *) pulse;
return (ITSMFAudioDevice*) pulse;
}

View File

@ -27,23 +27,24 @@ typedef struct _ITSMFAudioDevice ITSMFAudioDevice;
struct _ITSMFAudioDevice
{
/* Open the audio device. */
BOOL (*Open) (ITSMFAudioDevice* audio, const char* device);
BOOL (*Open)(ITSMFAudioDevice* audio, const char* device);
/* Set the audio data format. */
BOOL (*SetFormat) (ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels, UINT32 bits_per_sample);
BOOL (*SetFormat)(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels,
UINT32 bits_per_sample);
/* Play audio data. */
BOOL (*Play) (ITSMFAudioDevice* audio, BYTE* data, UINT32 data_size);
BOOL (*Play)(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size);
/* Get the latency of the last written sample, in 100ns */
UINT64 (*GetLatency) (ITSMFAudioDevice* audio);
UINT64(*GetLatency)(ITSMFAudioDevice* audio);
/* Change the playback volume level */
BOOL (*ChangeVolume) (ITSMFAudioDevice* audio, UINT32 newVolume, UINT32 muted);
BOOL (*ChangeVolume)(ITSMFAudioDevice* audio, UINT32 newVolume, UINT32 muted);
/* Flush queued audio data */
BOOL (*Flush) (ITSMFAudioDevice* audio);
BOOL (*Flush)(ITSMFAudioDevice* audio);
/* Free the audio device */
void (*Free) (ITSMFAudioDevice* audio);
void (*Free)(ITSMFAudioDevice* audio);
};
#define TSMF_AUDIO_DEVICE_EXPORT_FUNC_NAME "TSMFAudioDeviceEntry"
typedef ITSMFAudioDevice* (*TSMF_AUDIO_DEVICE_ENTRY) (void);
typedef ITSMFAudioDevice* (*TSMF_AUDIO_DEVICE_ENTRY)(void);
ITSMFAudioDevice* tsmf_load_audio_device(const char* name, const char* device);

View File

@ -159,8 +159,8 @@ struct _TSMF_SAMPLE
static wArrayList* presentation_list = NULL;
static int TERMINATING = 0;
static void _tsmf_presentation_free(TSMF_PRESENTATION* presentation);
static void _tsmf_stream_free(TSMF_STREAM* stream);
static void _tsmf_presentation_free(void* obj);
static void _tsmf_stream_free(void* obj);
static UINT64 get_current_time(void)
{
@ -367,7 +367,7 @@ TSMF_PRESENTATION* tsmf_presentation_new(const BYTE* guid,
goto error_stream_list;
ArrayList_Object(presentation->stream_list)->fnObjectFree =
(OBJECT_FREE_FN) _tsmf_stream_free;
_tsmf_stream_free;
if (ArrayList_Add(presentation_list, presentation) < 0)
goto error_add;
@ -528,6 +528,7 @@ static BOOL tsmf_sample_playback_audio(TSMF_SAMPLE* sample)
{
ret = sample->stream->audio->Play(sample->stream->audio, sample->data,
sample->decoded_size);
free(sample->data);
sample->data = NULL;
sample->decoded_size = 0;
@ -1193,14 +1194,19 @@ BOOL tsmf_stream_flush(TSMF_STREAM* stream)
return TRUE;
}
void _tsmf_presentation_free(TSMF_PRESENTATION* presentation)
void _tsmf_presentation_free(void* obj)
{
tsmf_presentation_stop(presentation);
ArrayList_Clear(presentation->stream_list);
ArrayList_Free(presentation->stream_list);
free(presentation->rects);
ZeroMemory(presentation, sizeof(TSMF_PRESENTATION));
free(presentation);
TSMF_PRESENTATION* presentation = (TSMF_PRESENTATION*)obj;
if (presentation)
{
tsmf_presentation_stop(presentation);
ArrayList_Clear(presentation->stream_list);
ArrayList_Free(presentation->stream_list);
free(presentation->rects);
ZeroMemory(presentation, sizeof(TSMF_PRESENTATION));
free(presentation);
}
}
void tsmf_presentation_free(TSMF_PRESENTATION* presentation)
@ -1414,8 +1420,10 @@ void tsmf_stream_end(TSMF_STREAM* stream, UINT32 message_id,
stream->eos_channel_callback = pChannelCallback;
}
void _tsmf_stream_free(TSMF_STREAM* stream)
void _tsmf_stream_free(void* obj)
{
TSMF_STREAM* stream = (TSMF_STREAM*)obj;
if (!stream)
return;
@ -1552,8 +1560,7 @@ BOOL tsmf_media_init(void)
if (!presentation_list)
return FALSE;
ArrayList_Object(presentation_list)->fnObjectFree = (OBJECT_FREE_FN)
_tsmf_presentation_free;
ArrayList_Object(presentation_list)->fnObjectFree = _tsmf_presentation_free;
}
return TRUE;

View File

@ -132,6 +132,10 @@ option(WITH_DEBUG_SYMBOLS "Pack debug symbols to installer" OFF)
option(WITH_CCACHE "Use ccache support if available" ON)
option(WITH_ICU "Use ICU for unicode conversion" OFF)
if (WITH_FFMPEG)
option(WITH_DSP_FFMPEG "Use FFMPEG for audio encoding/decoding" OFF)
endif(WITH_FFMPEG)
option(USE_VERSION_FROM_GIT_TAG "Extract FreeRDP version from git tag." OFF)
if(ANDROID)

13
cmake/FindFAAC.cmake Normal file
View File

@ -0,0 +1,13 @@
find_path(FAAC_INCLUDE_DIR faac.h)
find_library(FAAC_LIBRARY faac)
find_package_handle_standard_args(FAAC DEFAULT_MSG FAAC_INCLUDE_DIR FAAC_LIBRARY)
if(FAAC_FOUND)
set(FAAC_LIBRARIES ${FAAC_LIBRARY})
set(FAAC_INCLUDE_DIRS ${FAAC_INCLUDE_DIR})
endif()
mark_as_advanced(FAAC_INCLUDE_DIR FAAC_LIBRARY)

13
cmake/FindFAAD2.cmake Normal file
View File

@ -0,0 +1,13 @@
find_path(FAAD2_INCLUDE_DIR faad.h)
find_library(FAAD2_LIBRARY faad)
find_package_handle_standard_args(FAAD2 DEFAULT_MSG FAAD2_INCLUDE_DIR FAAD2_LIBRARY)
if(FAAD2_FOUND)
set(FAAD2_LIBRARIES ${FAAD2_LIBRARY})
set(FAAD2_INCLUDE_DIRS ${FAAD2_INCLUDE_DIR})
endif()
mark_as_advanced(FAAD2_INCLUDE_DIR FAAD2_LIBRARY)

View File

@ -10,42 +10,48 @@ set(REQUIRED_AVCODEC_API_VERSION 53.25.0)
include(FindPkgConfig)
if(PKG_CONFIG_FOUND)
if (PKG_CONFIG_FOUND)
pkg_check_modules(AVCODEC libavcodec)
pkg_check_modules(AVUTIL libavutil)
endif()
pkg_check_modules(AVRESAMPLE libavresample)
endif(PKG_CONFIG_FOUND)
# avcodec
find_path(AVCODEC_INCLUDE_DIR libavcodec/avcodec.h PATHS ${AVCODEC_INCLUDE_DIRS})
find_library(AVCODEC_LIBRARY avcodec PATHS ${AVCODEC_LIBRARY_DIRS})
find_library(AVCODEC_LIBRARY avcodec PATHS $ {AVCODEC_LIBRARY_DIRS})
# avutil
find_path(AVUTIL_INCLUDE_DIR libavutil/avutil.h PATHS ${AVUTIL_INCLUDE_DIRS})
find_library(AVUTIL_LIBRARY avutil PATHS ${AVUTIL_LIBRARY_DIRS})
if(AVCODEC_INCLUDE_DIR AND AVCODEC_LIBRARY)
set(AVCODEC_FOUND TRUE)
endif()
# avresample
find_path(AVRESAMPLE_INCLUDE_DIR libavresample/avresample.h PATHS ${AVRESAMPLE_INCLUDE_DIRS})
find_library(AVRESAMPLE_LIBRARY avresample PATHS ${AVRESAMPLE_LIBRARY_DIRS})
if(AVUTIL_INCLUDE_DIR AND AVUTIL_LIBRARY)
if (AVCODEC_INCLUDE_DIR AND AVCODEC_LIBRARY AND AVRESAMPLE_LIBRARY)
set(AVCODEC_FOUND TRUE)
endif(AVCODEC_INCLUDE_DIR AND AVCODEC_LIBRARY AND AVRESAMPLE_LIBRARY)
if (AVUTIL_INCLUDE_DIR AND AVUTIL_LIBRARY)
set(AVUTIL_FOUND TRUE)
endif()
endif(AVUTIL_INCLUDE_DIR AND AVUTIL_LIBRARY)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFmpeg DEFAULT_MSG AVUTIL_FOUND AVCODEC_FOUND)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFmpeg DEFAULT_MSG AVUTIL_FOUND AVCODEC_FOUND AVRESAMPLE_FOUND)
if (AVCODEC_VERSION)
if (${AVCODEC_VERSION} VERSION_LESS ${REQUIRED_AVCODEC_API_VERSION})
message(FATAL_ERROR "libavcodec version >= ${REQUIRED_AVCODEC_VERSION} (API >= ${REQUIRED_AVCODEC_API_VERSION}) is required")
message(FATAL_ERROR
"libavcodec version >= ${REQUIRED_AVCODEC_VERSION} (API >= ${REQUIRED_AVCODEC_API_VERSION}) is required")
endif()
else()
message("Note: To build libavcodec version >= ${REQUIRED_AVCODEC_VERSION} (API >= ${REQUIRED_AVCODEC_API_VERSION}) is required")
endif()
else(AVCODEC_VERSION)
message("Note: To build libavcodec version >= ${REQUIRED_AVCODEC_VERSION} (API >= ${REQUIRED_AVCODEC_API_VERSION}) is required")
endif(AVCODEC_VERSION)
if(FFMPEG_FOUND)
set(FFMPEG_INCLUDE_DIRS ${AVCODEC_INCLUDE_DIR} ${AVUTIL_INCLUDE_DIR})
set(FFMPEG_LIBRARIES ${AVCODEC_LIBRARY} ${AVUTIL_LIBRARY})
endif()
if (FFMPEG_FOUND)
set(FFMPEG_INCLUDE_DIRS ${AVCODEC_INCLUDE_DIR} ${AVUTIL_INCLUDE_DIR} ${AVRESAMPLE_INCLUDE_DIR})
set(FFMPEG_LIBRARIES ${AVCODEC_LIBRARY} ${AVUTIL_LIBRARY} ${AVRESAMPLE_LIBRARY})
endif(FFMPEG_FOUND)
mark_as_advanced(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARIES)

13
cmake/FindLAME.cmake Normal file
View File

@ -0,0 +1,13 @@
find_path(LAME_INCLUDE_DIR lame/lame.h)
find_library(LAME_LIBRARY NAMES lame mp3lame)
find_package_handle_standard_args(LAME DEFAULT_MSG LAME_INCLUDE_DIR LAME_LIBRARY)
if (LAME_FOUND)
set(LAME_LIBRARIES ${LAME_LIBRARY})
set(LAME_INCLUDE_DIRS ${LAME_INCLUDE_DIR})
endif()
mark_as_advanced(LAME_INCLUDE_DIR LAME_LIBRARY)

View File

@ -52,9 +52,13 @@
#cmakedefine WITH_IOSAUDIO
#cmakedefine WITH_OPENSLES
#cmakedefine WITH_GSM
#cmakedefine WITH_LAME
#cmakedefine WITH_FAAD2
#cmakedefine WITH_FAAC
#cmakedefine WITH_GFX_H264
#cmakedefine WITH_OPENH264
#cmakedefine WITH_FFMPEG
#cmakedefine WITH_DSP_FFMPEG
#cmakedefine WITH_X264
#cmakedefine WITH_MEDIA_FOUNDATION

View File

@ -24,38 +24,28 @@
#include <freerdp/channels/audin.h>
#include <freerdp/freerdp.h>
#include <freerdp/codec/audio.h>
/**
* Subsystem Interface
*/
typedef UINT (*AudinReceive) (const BYTE* data, int size, void* userData);
typedef struct audin_format audinFormat;
struct audin_format
{
UINT16 wFormatTag;
UINT16 nChannels;
UINT32 nSamplesPerSec;
UINT16 nBlockAlign;
UINT16 wBitsPerSample;
UINT16 cbSize;
BYTE* data;
};
typedef UINT(*AudinReceive)(const AUDIO_FORMAT* format,
const BYTE* data, size_t size, void* userData);
typedef struct _IAudinDevice IAudinDevice;
struct _IAudinDevice
{
UINT (*Open) (IAudinDevice* devplugin, AudinReceive receive, void* userData);
BOOL (*FormatSupported) (IAudinDevice* devplugin, audinFormat* format);
UINT (*SetFormat) (IAudinDevice* devplugin, audinFormat* format, UINT32 FramesPerPacket);
UINT (*Close) (IAudinDevice* devplugin);
UINT (*Free) (IAudinDevice* devplugin);
UINT(*Open)(IAudinDevice* devplugin, AudinReceive receive, void* userData);
BOOL (*FormatSupported)(IAudinDevice* devplugin, const AUDIO_FORMAT* format);
UINT(*SetFormat)(IAudinDevice* devplugin, const AUDIO_FORMAT* format, UINT32 FramesPerPacket);
UINT(*Close)(IAudinDevice* devplugin);
UINT(*Free)(IAudinDevice* devplugin);
};
#define AUDIN_DEVICE_EXPORT_FUNC_NAME "freerdp_audin_client_subsystem_entry"
typedef UINT (*PREGISTERAUDINDEVICE)(IWTSPlugin* plugin, IAudinDevice* device);
typedef UINT(*PREGISTERAUDINDEVICE)(IWTSPlugin* plugin, IAudinDevice* device);
struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS
{
@ -67,7 +57,7 @@ struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS
typedef struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS FREERDP_AUDIN_DEVICE_ENTRY_POINTS;
typedef FREERDP_AUDIN_DEVICE_ENTRY_POINTS* PFREERDP_AUDIN_DEVICE_ENTRY_POINTS;
typedef UINT (*PFREERDP_AUDIN_DEVICE_ENTRY)(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints);
typedef UINT(*PFREERDP_AUDIN_DEVICE_ENTRY)(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints);
#endif /* FREERDP_CHANNEL_AUDIN_CLIENT_AUDIN_H */

View File

@ -26,43 +26,18 @@
/**
* Subsystem Interface
*/
struct _RDPSND_WAVE
{
BYTE* data;
int length;
BYTE cBlockNo;
UINT16 wFormatNo;
UINT16 wTimeStampA;
UINT16 wTimeStampB;
UINT16 wAudioLength;
UINT32 wLocalTimeA;
UINT32 wLocalTimeB;
BOOL AutoConfirm;
};
typedef struct _RDPSND_WAVE RDPSND_WAVE;
typedef struct rdpsnd_plugin rdpsndPlugin;
typedef struct rdpsnd_device_plugin rdpsndDevicePlugin;
typedef BOOL (*pcFormatSupported) (rdpsndDevicePlugin* device, AUDIO_FORMAT* format);
typedef BOOL (*pcOpen) (rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency);
typedef BOOL (*pcSetFormat) (rdpsndDevicePlugin* device, AUDIO_FORMAT* format, int latency);
typedef UINT32 (*pcGetVolume) (rdpsndDevicePlugin* device);
typedef BOOL (*pcSetVolume) (rdpsndDevicePlugin* device, UINT32 value);
typedef void (*pcPlay) (rdpsndDevicePlugin* device, BYTE* data, int size);
typedef void (*pcStart) (rdpsndDevicePlugin* device);
typedef void (*pcClose) (rdpsndDevicePlugin* device);
typedef void (*pcFree) (rdpsndDevicePlugin* device);
typedef BOOL (*pcWaveDecode) (rdpsndDevicePlugin* device, RDPSND_WAVE* wave);
typedef void (*pcWavePlay) (rdpsndDevicePlugin* device, RDPSND_WAVE* wave);
typedef UINT (*pcWaveConfirm) (rdpsndDevicePlugin* device, RDPSND_WAVE* wave);
typedef BOOL (*pcFormatSupported)(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format);
typedef BOOL (*pcOpen)(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, UINT32 latency);
typedef UINT32(*pcGetVolume)(rdpsndDevicePlugin* device);
typedef BOOL (*pcSetVolume)(rdpsndDevicePlugin* device, UINT32 value);
typedef UINT (*pcPlay)(rdpsndDevicePlugin* device, const BYTE* data, size_t size);
typedef void (*pcStart)(rdpsndDevicePlugin* device);
typedef void (*pcClose)(rdpsndDevicePlugin* device);
typedef void (*pcFree)(rdpsndDevicePlugin* device);
struct rdpsnd_device_plugin
{
@ -70,19 +45,12 @@ struct rdpsnd_device_plugin
pcFormatSupported FormatSupported;
pcOpen Open;
pcSetFormat SetFormat;
pcGetVolume GetVolume;
pcSetVolume SetVolume;
pcPlay Play;
pcStart Start;
pcClose Close;
pcFree Free;
pcWaveDecode WaveDecode;
pcWavePlay WavePlay;
pcWaveConfirm WaveConfirm;
BOOL DisableConfirmThread;
};
#define RDPSND_DEVICE_EXPORT_FUNC_NAME "freerdp_rdpsnd_client_subsystem_entry"
@ -98,7 +66,7 @@ struct _FREERDP_RDPSND_DEVICE_ENTRY_POINTS
typedef struct _FREERDP_RDPSND_DEVICE_ENTRY_POINTS FREERDP_RDPSND_DEVICE_ENTRY_POINTS;
typedef FREERDP_RDPSND_DEVICE_ENTRY_POINTS* PFREERDP_RDPSND_DEVICE_ENTRY_POINTS;
typedef UINT (*PFREERDP_RDPSND_DEVICE_ENTRY)(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints);
typedef UINT(*PFREERDP_RDPSND_DEVICE_ENTRY)(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints);
#endif /* FREERDP_CHANNEL_RDPSND_CLIENT_RDPSND_H */

View File

@ -188,20 +188,20 @@ typedef struct AUDIO_FORMAT AUDIO_FORMAT;
*/
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
FREERDP_API UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size);
FREERDP_API UINT32 rdpsnd_compute_audio_time_length(const AUDIO_FORMAT* format, size_t size);
FREERDP_API char* rdpsnd_get_audio_tag_string(UINT16 wFormatTag);
FREERDP_API void rdpsnd_print_audio_format(AUDIO_FORMAT* format);
FREERDP_API void rdpsnd_print_audio_formats(AUDIO_FORMAT* formats, UINT16 count);
FREERDP_API void rdpsnd_print_audio_format(const AUDIO_FORMAT* format);
FREERDP_API void rdpsnd_print_audio_formats(const AUDIO_FORMAT* formats, UINT16 count);
FREERDP_API void rdpsnd_free_audio_formats(AUDIO_FORMAT* formats, UINT16 count);
#ifdef __cplusplus
}
}
#endif
#endif /* FREERDP_CODEC_AUDIO_H */

View File

@ -20,63 +20,30 @@
#ifndef FREERDP_CODEC_DSP_H
#define FREERDP_CODEC_DSP_H
#include <freerdp/api.h>
#include <winpr/stream.h>
union _ADPCM
{
struct
{
INT16 last_sample[2];
INT16 last_step[2];
} ima;
struct
{
BYTE predictor[2];
INT32 delta[2];
INT32 sample1[2];
INT32 sample2[2];
} ms;
};
typedef union _ADPCM ADPCM;
#include <freerdp/api.h>
#include <freerdp/codec/audio.h>
typedef struct _FREERDP_DSP_CONTEXT FREERDP_DSP_CONTEXT;
struct _FREERDP_DSP_CONTEXT
{
BYTE* resampled_buffer;
UINT32 resampled_size;
UINT32 resampled_frames;
UINT32 resampled_maxlength;
BYTE* adpcm_buffer;
UINT32 adpcm_size;
UINT32 adpcm_maxlength;
ADPCM adpcm;
BOOL (*resample)(FREERDP_DSP_CONTEXT* context,
const BYTE* src, int bytes_per_sample,
UINT32 schan, UINT32 srate, int sframes,
UINT32 rchan, UINT32 rrate);
BOOL (*decode_ima_adpcm)(FREERDP_DSP_CONTEXT* context,
const BYTE* src, int size, int channels, int block_size);
BOOL (*encode_ima_adpcm)(FREERDP_DSP_CONTEXT* context,
const BYTE* src, int size, int channels, int block_size);
BOOL (*decode_ms_adpcm)(FREERDP_DSP_CONTEXT* context,
const BYTE* src, int size, int channels, int block_size);
BOOL (*encode_ms_adpcm)(FREERDP_DSP_CONTEXT* context,
const BYTE* src, int size, int channels, int block_size);
};
#ifdef __cplusplus
extern "C" {
#endif
FREERDP_API FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(void);
FREERDP_API FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(BOOL encoder);
FREERDP_API BOOL freerdp_dsp_supports_format(const AUDIO_FORMAT* format, BOOL encode);
FREERDP_API BOOL freerdp_dsp_encode(FREERDP_DSP_CONTEXT* context,
const AUDIO_FORMAT* srcFormat,
const BYTE* data, size_t length,
wStream* out);
FREERDP_API BOOL freerdp_dsp_decode(FREERDP_DSP_CONTEXT* context,
const AUDIO_FORMAT* srcFormat,
const BYTE* data, size_t length,
wStream* out);
FREERDP_API void freerdp_dsp_context_free(FREERDP_DSP_CONTEXT* context);
#define freerdp_dsp_context_reset_adpcm(_c) memset(&_c->adpcm, 0, sizeof(ADPCM))
FREERDP_API BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* context,
const AUDIO_FORMAT* targetFormat);
#ifdef __cplusplus
}

View File

@ -155,6 +155,33 @@ if(WITH_SSE2)
endif()
endif()
if (WITH_DSP_FFMPEG)
set(CODEC_SRCS
${CODEC_SRCS}
codec/dsp_ffmpeg.c
codec/dsp_ffmpeg.h)
endif (WITH_DSP_FFMPEG)
if(GSM_FOUND)
freerdp_library_add(${GSM_LIBRARIES})
include_directories(${GSM_INCLUDE_DIRS})
endif()
if(LAME_FOUND)
freerdp_library_add(${LAME_LIBRARIES})
include_directories(${LAME_INCLUDE_DIRS})
endif()
if(FAAD2_FOUND)
freerdp_library_add(${FAAD2_LIBRARIES})
include_directories(${FAAD2_INCLUDE_DIRS})
endif()
if(FAAC_FOUND)
freerdp_library_add(${FAAC_LIBRARIES})
include_directories(${FAAC_INCLUDE_DIRS})
endif()
if(WITH_NEON)
set_source_files_properties(${CODEC_NEON_SRCS} PROPERTIES COMPILE_FLAGS "-mfpu=neon -Wno-unused-variable" )
set(CODEC_SRCS ${CODEC_SRCS} ${CODEC_NEON_SRCS})
@ -324,6 +351,7 @@ endif()
target_link_libraries(${MODULE_NAME} ${PRIVATE_KEYWORD} ${LIBFREERDP_LIBS} winpr)
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries EXPORT FreeRDPTargets)
if (WITH_DEBUG_SYMBOLS AND MSVC AND BUILD_SHARED_LIBS)
get_target_property(OUTPUT_FILENAME ${MODULE_NAME} OUTPUT_NAME)

View File

@ -28,7 +28,7 @@
#define TAG FREERDP_TAG("codec")
UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size)
UINT32 rdpsnd_compute_audio_time_length(const AUDIO_FORMAT* format, size_t size)
{
UINT32 mstime;
UINT32 wSamples;
@ -54,7 +54,6 @@ UINT32 rdpsnd_compute_audio_time_length(AUDIO_FORMAT* format, int size)
if ((format->cbSize == 2) && (format->data))
{
nSamplesPerBlock = *((UINT16*) format->data);
wSamples = (size / format->nBlockAlign) * nSamplesPerBlock;
mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels);
}
@ -113,25 +112,25 @@ char* rdpsnd_get_audio_tag_string(UINT16 wFormatTag)
return "WAVE_FORMAT_UNKNOWN";
}
void rdpsnd_print_audio_format(AUDIO_FORMAT* format)
void rdpsnd_print_audio_format(const AUDIO_FORMAT* format)
{
WLog_INFO(TAG, "%s:\t wFormatTag: 0x%04"PRIX16" nChannels: %"PRIu16" nSamplesPerSec: %"PRIu32" "
"nAvgBytesPerSec: %"PRIu32" nBlockAlign: %"PRIu16" wBitsPerSample: %"PRIu16" cbSize: %"PRIu16"",
rdpsnd_get_audio_tag_string(format->wFormatTag), format->wFormatTag,
format->nChannels, format->nSamplesPerSec, format->nAvgBytesPerSec,
format->nBlockAlign, format->wBitsPerSample, format->cbSize);
"nAvgBytesPerSec: %"PRIu32" nBlockAlign: %"PRIu16" wBitsPerSample: %"PRIu16" cbSize: %"PRIu16"",
rdpsnd_get_audio_tag_string(format->wFormatTag), format->wFormatTag,
format->nChannels, format->nSamplesPerSec, format->nAvgBytesPerSec,
format->nBlockAlign, format->wBitsPerSample, format->cbSize);
}
void rdpsnd_print_audio_formats(AUDIO_FORMAT* formats, UINT16 count)
void rdpsnd_print_audio_formats(const AUDIO_FORMAT* formats, UINT16 count)
{
int index;
AUDIO_FORMAT* format;
UINT16 index;
const AUDIO_FORMAT* format;
if (formats)
{
WLog_INFO(TAG, "AUDIO_FORMATS (%"PRIu16") ={", count);
for (index = 0; index < (int) count; index++)
for (index = 0; index < count; index++)
{
format = &formats[index];
WLog_ERR(TAG, "\t");
@ -144,17 +143,14 @@ void rdpsnd_print_audio_formats(AUDIO_FORMAT* formats, UINT16 count)
void rdpsnd_free_audio_formats(AUDIO_FORMAT* formats, UINT16 count)
{
int index;
AUDIO_FORMAT* format;
UINT16 index;
if (formats)
{
for (index = 0; index < (int) count; index++)
for (index = 0; index < count; index++)
{
format = &formats[index];
if (format->cbSize)
free(format->data);
AUDIO_FORMAT* format = &formats[index];
free(format->data);
}
free(formats);

File diff suppressed because it is too large Load Diff

34
libfreerdp/codec/dsp.h Normal file
View File

@ -0,0 +1,34 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Digital Sound Processing - backend
*
* Copyright 2018 Armin Novak <armin.novak@thincast.com>
* Copyright 2018 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.
*/
#ifndef FREERDP_LIB_CODEC_DSP_H
#define FREERDP_LIB_CODEC_DSP_H
#include <freerdp/api.h>
#include <freerdp/codec/audio.h>
#include <freerdp/codec/dsp.h>
struct _FREERDP_DSP_COMMON_CONTEXT
{
wStream* buffer;
wStream* resample;
};
#endif /* FREERDP_LIB_CODEC_DSP_H */

View File

@ -0,0 +1,566 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Digital Sound Processing - FFMPEG backend
*
* Copyright 2018 Armin Novak <armin.novak@thincast.com>
* Copyright 2018 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 <freerdp/log.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
#include <libavresample/avresample.h>
#include "dsp.h"
#include "dsp_ffmpeg.h"
#define TAG FREERDP_TAG("dsp.ffmpeg")
struct _FREERDP_DSP_CONTEXT
{
AUDIO_FORMAT format;
BOOL isOpen;
BOOL encoder;
UINT32 bufferedSamples;
enum AVCodecID id;
AVCodec* codec;
AVCodecContext* context;
AVFrame* frame;
AVFrame* resampled;
AVFrame* buffered;
AVPacket* packet;
AVAudioResampleContext* rcontext;
};
static enum AVCodecID ffmpeg_get_avcodec(const AUDIO_FORMAT* format)
{
const char* id;
if (!format)
return AV_CODEC_ID_NONE;
id = rdpsnd_get_audio_tag_string(format->wFormatTag);
switch (format->wFormatTag)
{
case WAVE_FORMAT_UNKNOWN:
return AV_CODEC_ID_NONE;
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 16:
return AV_CODEC_ID_PCM_U16LE;
case 8:
return AV_CODEC_ID_PCM_U8;
default:
return AV_CODEC_ID_NONE;
}
case WAVE_FORMAT_DVI_ADPCM:
return AV_CODEC_ID_ADPCM_MS;
case WAVE_FORMAT_ADPCM:
return AV_CODEC_ID_ADPCM_MS;
case WAVE_FORMAT_ALAW:
return AV_CODEC_ID_PCM_ALAW;
case WAVE_FORMAT_MULAW:
return AV_CODEC_ID_PCM_MULAW;
case WAVE_FORMAT_GSM610:
return AV_CODEC_ID_GSM_MS;
case WAVE_FORMAT_AAC_MS:
return AV_CODEC_ID_AAC;
default:
return AV_CODEC_ID_NONE;
}
}
static int ffmpeg_sample_format(const AUDIO_FORMAT* format)
{
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 8:
return AV_SAMPLE_FMT_U8;
case 16:
return AV_SAMPLE_FMT_S16;
default:
return FALSE;
}
case WAVE_FORMAT_DVI_ADPCM:
case WAVE_FORMAT_ADPCM:
return AV_SAMPLE_FMT_S16P;
case WAVE_FORMAT_MPEGLAYER3:
case WAVE_FORMAT_AAC_MS:
return AV_SAMPLE_FMT_FLTP;
case WAVE_FORMAT_GSM610:
return AV_SAMPLE_FMT_S16P;
case WAVE_FORMAT_ALAW:
return AV_SAMPLE_FMT_S16;
default:
return FALSE;
}
}
static void ffmpeg_close_context(FREERDP_DSP_CONTEXT* context)
{
if (context)
{
if (context->context)
avcodec_free_context(&context->context);
if (context->frame)
av_frame_free(&context->frame);
if (context->resampled)
av_frame_free(&context->resampled);
if (context->buffered)
av_frame_free(&context->buffered);
if (context->packet)
av_packet_free(&context->packet);
if (context->rcontext)
avresample_free(&context->rcontext);
context->id = AV_CODEC_ID_NONE;
context->codec = NULL;
context->isOpen = FALSE;
context->context = NULL;
context->frame = NULL;
context->resampled = NULL;
context->packet = NULL;
context->rcontext = NULL;
}
}
static BOOL ffmpeg_open_context(FREERDP_DSP_CONTEXT* context)
{
int ret;
int layout;
const AUDIO_FORMAT* format;
if (!context || context->isOpen)
return FALSE;
format = &context->format;
if (!format)
return FALSE;
layout = av_get_default_channel_layout(format->nChannels);
context->id = ffmpeg_get_avcodec(format);
if (context->id == AV_CODEC_ID_NONE)
goto fail;
if (context->encoder)
context->codec = avcodec_find_encoder(context->id);
else
context->codec = avcodec_find_decoder(context->id);
if (!context->codec)
goto fail;
context->context = avcodec_alloc_context3(context->codec);
if (!context->context)
goto fail;
switch(context->id)
{
/* We need support for multichannel and sample rates != 8000 */
case AV_CODEC_ID_GSM_MS:
context->context->strict_std_compliance = FF_COMPLIANCE_UNOFFICIAL;
break;
default:
break;
}
context->context->channels = format->nChannels;
context->context->channel_layout = layout;
context->context->sample_rate = format->nSamplesPerSec;
context->context->block_align = format->nBlockAlign;
context->context->bit_rate = format->nAvgBytesPerSec * 8;
context->context->sample_fmt = ffmpeg_sample_format(format);
context->context->time_base = av_make_q(1, context->context->sample_rate);
if ((ret = avcodec_open2(context->context, context->codec, NULL)) < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error avcodec_open2 %s [%d]", err, ret);
goto fail;
}
context->packet = av_packet_alloc();
if (!context->packet)
goto fail;
context->frame = av_frame_alloc();
if (!context->frame)
goto fail;
context->resampled = av_frame_alloc();
if (!context->resampled)
goto fail;
context->buffered = av_frame_alloc();
if (!context->buffered)
goto fail;
context->rcontext = avresample_alloc_context();
if (!context->rcontext)
goto fail;
context->frame->channel_layout = layout;
context->frame->channels = format->nChannels;
context->frame->sample_rate = format->nSamplesPerSec;
context->frame->format = AV_SAMPLE_FMT_S16;
if (context->encoder)
{
context->resampled->format = context->context->sample_fmt;
context->resampled->sample_rate = context->context->sample_rate;
}
else
{
context->resampled->format = AV_SAMPLE_FMT_S16;
context->resampled->sample_rate = format->nSamplesPerSec;
}
context->resampled->channel_layout = layout;
context->resampled->channels = format->nChannels;
if (context->context->frame_size > 0)
{
context->buffered->channel_layout = context->resampled->channel_layout;
context->buffered->channels = context->resampled->channels;
context->buffered->format = context->resampled->format;
context->buffered->nb_samples = context->context->frame_size;
if ((ret = av_frame_get_buffer(context->buffered, 1)) < 0)
goto fail;
}
context->isOpen = TRUE;
return TRUE;
fail:
ffmpeg_close_context(context);
return FALSE;
}
static BOOL ffmpeg_resample_frame(AVAudioResampleContext* context,
AVFrame* in, AVFrame* out)
{
int ret;
if (!avresample_is_open(context))
{
if ((ret = avresample_config(context, out, in)) < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret);
return FALSE;
}
if ((ret = (avresample_open(context))) < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret);
return FALSE;
}
}
if ((ret = avresample_convert_frame(context, out, in)) < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret);
return FALSE;
}
return TRUE;
}
static BOOL ffmpeg_encode_frame(AVCodecContext* context, AVFrame* in,
AVPacket* packet, wStream* out)
{
int ret;
/* send the packet with the compressed data to the encoder */
ret = avcodec_send_frame(context, in);
if (ret < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error submitting the packet to the encoder %s [%d]",
err, ret);
return FALSE;
}
/* read all the output frames (in general there may be any number of them */
while (ret >= 0)
{
ret = avcodec_receive_packet(context, packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return TRUE;
else if (ret < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error during encoding %s [%d]", err, ret);
return FALSE;
}
if (!Stream_EnsureRemainingCapacity(out, packet->size))
return FALSE;
Stream_Write(out, packet->data, packet->size);
av_packet_unref(packet);
}
return TRUE;
}
static BOOL ffmpeg_fill_frame(AVFrame* frame, const AUDIO_FORMAT* inputFormat,
const BYTE* data, size_t size)
{
int ret, bpp;
frame->channels = inputFormat->nChannels;
frame->sample_rate = inputFormat->nSamplesPerSec;
frame->format = ffmpeg_sample_format(inputFormat);
frame->channel_layout = av_get_default_channel_layout(frame->channels);
bpp = av_get_bytes_per_sample(frame->format);
frame->nb_samples = size / inputFormat->nChannels / bpp;
if ((ret = avcodec_fill_audio_frame(frame, frame->channels,
frame->format,
data, size, 1)) < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error during audio frame fill %s [%d]", err, ret);
return FALSE;
}
return TRUE;
}
static BOOL ffmpeg_decode(AVCodecContext* dec_ctx, AVPacket* pkt,
AVFrame* frame,
AVAudioResampleContext* resampleContext,
AVFrame* resampled, wStream* out)
{
int ret;
/* send the packet with the compressed data to the decoder */
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error submitting the packet to the decoder %s [%d]",
err, ret);
return FALSE;
}
/* read all the output frames (in general there may be any number of them */
while (ret >= 0)
{
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return TRUE;
else if (ret < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error during decoding %s [%d]", err, ret);
return FALSE;
}
if (!avresample_is_open(resampleContext))
{
if ((ret = avresample_config(resampleContext, resampled, frame)) < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret);
return FALSE;
}
if ((ret = (avresample_open(resampleContext))) < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret);
return FALSE;
}
}
if ((ret = avresample_convert_frame(resampleContext, resampled, frame)) < 0)
{
const char* err = av_err2str(ret);
WLog_ERR(TAG, "Error during resampling %s [%d]", err, ret);
return FALSE;
}
{
const size_t data_size = resampled->channels * resampled->nb_samples * 2;
Stream_EnsureRemainingCapacity(out, data_size);
Stream_Write(out, resampled->data[0], data_size);
}
}
return TRUE;
}
BOOL freerdp_dsp_ffmpeg_supports_format(const AUDIO_FORMAT* format, BOOL encode)
{
enum AVCodecID id = ffmpeg_get_avcodec(format);
if (id == AV_CODEC_ID_NONE)
return FALSE;
if (encode)
return avcodec_find_encoder(id) != NULL;
else
return avcodec_find_decoder(id) != NULL;
}
FREERDP_DSP_CONTEXT* freerdp_dsp_ffmpeg_context_new(BOOL encode)
{
FREERDP_DSP_CONTEXT* context;
avcodec_register_all();
context = calloc(1, sizeof(FREERDP_DSP_CONTEXT));
if (!context)
return NULL;
context->encoder = encode;
return context;
}
void freerdp_dsp_ffmpeg_context_free(FREERDP_DSP_CONTEXT* context)
{
if (context)
{
ffmpeg_close_context(context);
free(context);
}
}
BOOL freerdp_dsp_ffmpeg_context_reset(FREERDP_DSP_CONTEXT* context,
const AUDIO_FORMAT* targetFormat)
{
if (!context || !targetFormat)
return FALSE;
ffmpeg_close_context(context);
context->format = *targetFormat;
return ffmpeg_open_context(context);
}
BOOL freerdp_dsp_ffmpeg_encode(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* format,
const BYTE* data, size_t length, wStream* out)
{
int rc;
int samples, rest;
if (!context || !format || !data || !out || !context->encoder)
return FALSE;
if (!context || !data || !out)
return FALSE;
/* Create input frame */
if (!ffmpeg_fill_frame(context->frame, format, data, length))
return FALSE;
/* Resample to desired format. */
if (!ffmpeg_resample_frame(context->rcontext,
context->frame,
context->resampled))
return FALSE;
if (context->context->frame_size <= 0)
{
return ffmpeg_encode_frame(context->context, context->resampled,
context->packet, out);
}
else
{
rest = samples = context->resampled->nb_samples;
do
{
if (samples + context->bufferedSamples > context->context->frame_size)
samples = context->context->frame_size - context->bufferedSamples;
rc = av_samples_copy(context->buffered->extended_data, context->resampled->extended_data,
context->bufferedSamples, 0, samples,
context->context->channels, context->context->sample_fmt);
rest -= samples;
context->bufferedSamples += samples;
if (context->context->frame_size <= context->bufferedSamples)
{
/* Encode in desired format. */
if (!ffmpeg_encode_frame(context->context, context->buffered,
context->packet, out))
return FALSE;
context->bufferedSamples = 0;
}
}
while (rest > 0);
return TRUE;
}
}
BOOL freerdp_dsp_ffmpeg_decode(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* srcFormat,
const BYTE* data, size_t length, wStream* out)
{
if (!context || !srcFormat || !data || !out || context->encoder)
return FALSE;
av_init_packet(context->packet);
context->packet->data = data;
context->packet->size = length;
return ffmpeg_decode(context->context, context->packet, context->frame,
context->rcontext, context->resampled, out);
}

View File

@ -0,0 +1,42 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Digital Sound Processing - FFMPEG backend
*
* Copyright 2018 Armin Novak <armin.novak@thincast.com>
* Copyright 2018 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.
*/
#ifndef FREERDP_LIB_CODEC_DSP_FFMPEG_H
#define FREERDP_LIB_CODEC_DSP_FFMPEG_H
#include <freerdp/api.h>
#include <freerdp/codec/audio.h>
#include <freerdp/codec/dsp.h>
FREERDP_DSP_CONTEXT* freerdp_dsp_ffmpeg_context_new(BOOL encode);
BOOL freerdp_dsp_ffmpeg_supports_format(const AUDIO_FORMAT* format, BOOL encode);
BOOL freerdp_dsp_ffmpeg_encode(FREERDP_DSP_CONTEXT* context,
const AUDIO_FORMAT* srcFormat,
const BYTE* data, size_t length,
wStream* out);
BOOL freerdp_dsp_ffmpeg_decode(FREERDP_DSP_CONTEXT* context,
const AUDIO_FORMAT* srcFormat,
const BYTE* data, size_t length,
wStream* out);
void freerdp_dsp_ffmpeg_context_free(FREERDP_DSP_CONTEXT* context);
BOOL freerdp_dsp_ffmpeg_context_reset(FREERDP_DSP_CONTEXT* context,
const AUDIO_FORMAT* targetFormat);
#endif /* FREERDP_LIB_CODEC_DSP_FFMPEG_H */

View File

@ -43,6 +43,7 @@ static UINT AudinServerOpening(audin_server_context* context)
{
AUDIO_FORMAT* agreed_format = NULL;
int i = 0, j = 0;
for (i = 0; i < context->num_client_formats; i++)
{
for (j = 0; j < context->num_server_formats; j++)
@ -55,9 +56,9 @@ static UINT AudinServerOpening(audin_server_context* context)
break;
}
}
if (agreed_format != NULL)
break;
}
if (agreed_format == NULL)
@ -94,6 +95,7 @@ static UINT AudinServerReceiveSamples(audin_server_context* context, const void*
if (subsystem->AudinServerReceiveSamples)
subsystem->AudinServerReceiveSamples(subsystem, client, buf, nframes);
return CHANNEL_RC_OK;
}
@ -101,6 +103,7 @@ int shadow_client_audin_init(rdpShadowClient* client)
{
audin_server_context* audin;
audin = client->audin = audin_server_context_new(client->vcm);
if (!audin)
{
return 0;
@ -121,11 +124,9 @@ int shadow_client_audin_init(rdpShadowClient* client)
}
audin->dst_format = audin->server_formats[0];
audin->Opening = AudinServerOpening;
audin->OpenResult = AudinServerOpenResult;
audin->ReceiveSamples = AudinServerReceiveSamples;
return 1;
}