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:
Родитель
a9ecd6a6cc
Коммит
f89c1857b9
|
@ -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})
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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;
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче