audin: migrate audin plugin and ALSA/PulseAudio devices.
This commit is contained in:
parent
997ecbd112
commit
f5ccf34605
@ -33,4 +33,5 @@ target_link_libraries(drdynvc freerdp-utils)
|
|||||||
install(TARGETS drdynvc DESTINATION ${FREERDP_PLUGIN_PATH})
|
install(TARGETS drdynvc DESTINATION ${FREERDP_PLUGIN_PATH})
|
||||||
|
|
||||||
add_subdirectory(tsmf)
|
add_subdirectory(tsmf)
|
||||||
|
add_subdirectory(audin)
|
||||||
|
|
||||||
|
41
channels/drdynvc/audin/CMakeLists.txt
Normal file
41
channels/drdynvc/audin/CMakeLists.txt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# FreeRDP: A Remote Desktop Protocol Client
|
||||||
|
# FreeRDP cmake build script
|
||||||
|
#
|
||||||
|
# Copyright 2011 O.S. Systems Software Ltda.
|
||||||
|
# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
|
||||||
|
# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||||
|
|
||||||
|
set(AUDIN_SRCS
|
||||||
|
audin_main.c
|
||||||
|
audin_main.h
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(..)
|
||||||
|
|
||||||
|
add_library(audin ${AUDIN_SRCS})
|
||||||
|
set_target_properties(audin PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
target_link_libraries(audin freerdp-utils)
|
||||||
|
|
||||||
|
install(TARGETS audin DESTINATION ${FREERDP_PLUGIN_PATH})
|
||||||
|
|
||||||
|
if(ALSA_FOUND)
|
||||||
|
add_subdirectory(alsa)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(PULSE_FOUND)
|
||||||
|
add_subdirectory(pulse)
|
||||||
|
endif()
|
||||||
|
|
33
channels/drdynvc/audin/alsa/CMakeLists.txt
Normal file
33
channels/drdynvc/audin/alsa/CMakeLists.txt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# FreeRDP: A Remote Desktop Protocol Client
|
||||||
|
# FreeRDP cmake build script
|
||||||
|
#
|
||||||
|
# Copyright 2011 O.S. Systems Software Ltda.
|
||||||
|
# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
|
||||||
|
# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||||
|
|
||||||
|
set(AUDIN_ALSA_SRCS
|
||||||
|
audin_alsa.c
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(..)
|
||||||
|
include_directories(${ALSA_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
add_library(audin_alsa ${AUDIN_ALSA_SRCS})
|
||||||
|
set_target_properties(audin_alsa PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
target_link_libraries(audin_alsa freerdp-utils)
|
||||||
|
target_link_libraries(audin_alsa ${ALSA_LIBRARIES})
|
||||||
|
|
||||||
|
install(TARGETS audin_alsa DESTINATION ${FREERDP_PLUGIN_PATH})
|
356
channels/drdynvc/audin/alsa/audin_alsa.c
Normal file
356
channels/drdynvc/audin/alsa/audin_alsa.c
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
/**
|
||||||
|
* FreeRDP: A Remote Desktop Protocol client.
|
||||||
|
* Audio Input Redirection Virtual Channel - ALSA implementation
|
||||||
|
*
|
||||||
|
* Copyright 2010-2011 Vic Lee
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <freerdp/utils/memory.h>
|
||||||
|
#include <freerdp/utils/thread.h>
|
||||||
|
#include <freerdp/utils/dsp.h>
|
||||||
|
|
||||||
|
#include "audin_main.h"
|
||||||
|
|
||||||
|
typedef struct _AudinALSADevice
|
||||||
|
{
|
||||||
|
IAudinDevice iface;
|
||||||
|
|
||||||
|
char device_name[32];
|
||||||
|
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;
|
||||||
|
ADPCM adpcm;
|
||||||
|
|
||||||
|
freerdp_thread* thread;
|
||||||
|
|
||||||
|
uint8* buffer;
|
||||||
|
int buffer_frames;
|
||||||
|
|
||||||
|
AudinReceive receive;
|
||||||
|
void* user_data;
|
||||||
|
} AudinALSADevice;
|
||||||
|
|
||||||
|
static boolean audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_handle)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
snd_pcm_hw_params_t* hw_params;
|
||||||
|
|
||||||
|
if ((error = snd_pcm_hw_params_malloc(&hw_params)) < 0)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("snd_pcm_hw_params_malloc (%s)",
|
||||||
|
snd_strerror(error));
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
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_channels_near(capture_handle, hw_params,
|
||||||
|
&alsa->actual_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 %d / channel %d is "
|
||||||
|
"different from target rate %d / channel %d, resampling required.",
|
||||||
|
alsa->actual_rate, alsa->actual_channels,
|
||||||
|
alsa->target_rate, alsa->target_channels);
|
||||||
|
}
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean audin_alsa_thread_receive(AudinALSADevice* alsa, uint8* src, int size)
|
||||||
|
{
|
||||||
|
int frames;
|
||||||
|
int cframes;
|
||||||
|
int ret = 0;
|
||||||
|
int encoded_size;
|
||||||
|
uint8* encoded_data;
|
||||||
|
int rbytes_per_frame;
|
||||||
|
int tbytes_per_frame;
|
||||||
|
uint8* resampled_data;
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
resampled_data = NULL;
|
||||||
|
frames = size / rbytes_per_frame;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resampled_data = dsp_resample(src, alsa->bytes_per_channel,
|
||||||
|
alsa->actual_channels, alsa->actual_rate, size / rbytes_per_frame,
|
||||||
|
alsa->target_channels, alsa->target_rate, &frames);
|
||||||
|
DEBUG_DVC("resampled %d frames at %d to %d frames at %d",
|
||||||
|
size / rbytes_per_frame, alsa->actual_rate, frames, alsa->target_rate);
|
||||||
|
size = frames * tbytes_per_frame;
|
||||||
|
src = resampled_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (frames > 0)
|
||||||
|
{
|
||||||
|
cframes = alsa->frames_per_packet - alsa->buffer_frames;
|
||||||
|
if (cframes > frames)
|
||||||
|
cframes = frames;
|
||||||
|
memcpy(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 == 0x11)
|
||||||
|
{
|
||||||
|
encoded_data = dsp_encode_ima_adpcm(&alsa->adpcm,
|
||||||
|
alsa->buffer, alsa->buffer_frames * tbytes_per_frame,
|
||||||
|
alsa->target_channels, alsa->block_size, &encoded_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = alsa->receive(encoded_data, encoded_size, alsa->user_data);
|
||||||
|
alsa->buffer_frames = 0;
|
||||||
|
if (encoded_data != alsa->buffer)
|
||||||
|
xfree(encoded_data);
|
||||||
|
if (!ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
src += cframes * tbytes_per_frame;
|
||||||
|
frames -= cframes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resampled_data)
|
||||||
|
xfree(resampled_data);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* audin_alsa_thread_func(void* arg)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
uint8* buffer;
|
||||||
|
int rbytes_per_frame;
|
||||||
|
int tbytes_per_frame;
|
||||||
|
snd_pcm_t* capture_handle = NULL;
|
||||||
|
AudinALSADevice* alsa = (AudinALSADevice*) arg;
|
||||||
|
|
||||||
|
DEBUG_DVC("in");
|
||||||
|
|
||||||
|
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
|
||||||
|
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
|
||||||
|
alsa->buffer = (uint8*) xzalloc(tbytes_per_frame * alsa->frames_per_packet);
|
||||||
|
alsa->buffer_frames = 0;
|
||||||
|
buffer = (uint8*) xzalloc(rbytes_per_frame * alsa->frames_per_packet);
|
||||||
|
memset(&alsa->adpcm, 0, sizeof(ADPCM));
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ((error = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("snd_pcm_open (%s)", snd_strerror(error));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!audin_alsa_set_params(alsa, capture_handle))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!freerdp_thread_is_stopped(alsa->thread))
|
||||||
|
{
|
||||||
|
error = snd_pcm_readi(capture_handle, buffer, alsa->frames_per_packet);
|
||||||
|
if (error == -EPIPE)
|
||||||
|
{
|
||||||
|
snd_pcm_recover(capture_handle, error, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (error < 0)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("snd_pcm_readi (%s)", snd_strerror(error));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!audin_alsa_thread_receive(alsa, buffer, error * rbytes_per_frame))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
xfree(buffer);
|
||||||
|
xfree(alsa->buffer);
|
||||||
|
alsa->buffer = NULL;
|
||||||
|
if (capture_handle)
|
||||||
|
snd_pcm_close(capture_handle);
|
||||||
|
|
||||||
|
freerdp_thread_quit(alsa->thread);
|
||||||
|
|
||||||
|
DEBUG_DVC("out");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_alsa_free(IAudinDevice* device)
|
||||||
|
{
|
||||||
|
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||||
|
|
||||||
|
freerdp_thread_free(alsa->thread);
|
||||||
|
xfree(alsa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean audin_alsa_format_supported(IAudinDevice* device, audinFormat* format)
|
||||||
|
{
|
||||||
|
switch (format->wFormatTag)
|
||||||
|
{
|
||||||
|
case 1: /* PCM */
|
||||||
|
if (format->cbSize == 0 &&
|
||||||
|
(format->nSamplesPerSec <= 48000) &&
|
||||||
|
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||||
|
(format->nChannels == 1 || format->nChannels == 2))
|
||||||
|
{
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x11: /* IMA ADPCM */
|
||||||
|
if ((format->nSamplesPerSec <= 48000) &&
|
||||||
|
(format->wBitsPerSample == 4) &&
|
||||||
|
(format->nChannels == 1 || format->nChannels == 2))
|
||||||
|
{
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_alsa_set_format(IAudinDevice* device, audinFormat* 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;
|
||||||
|
switch (format->wFormatTag)
|
||||||
|
{
|
||||||
|
case 1: /* PCM */
|
||||||
|
switch (format->wBitsPerSample)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x11: /* IMA 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=%d",
|
||||||
|
alsa->frames_per_packet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
alsa->wformat = format->wFormatTag;
|
||||||
|
alsa->block_size = format->nBlockAlign;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||||
|
{
|
||||||
|
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||||
|
|
||||||
|
DEBUG_DVC("");
|
||||||
|
|
||||||
|
alsa->receive = receive;
|
||||||
|
alsa->user_data = user_data;
|
||||||
|
|
||||||
|
freerdp_thread_start(alsa->thread, audin_alsa_thread_func, alsa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_alsa_close(IAudinDevice* device)
|
||||||
|
{
|
||||||
|
AudinALSADevice* alsa = (AudinALSADevice*) device;
|
||||||
|
|
||||||
|
DEBUG_DVC("");
|
||||||
|
|
||||||
|
freerdp_thread_stop(alsa->thread);
|
||||||
|
|
||||||
|
alsa->receive = NULL;
|
||||||
|
alsa->user_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FreeRDPAudinDeviceEntry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||||
|
{
|
||||||
|
AudinALSADevice* alsa;
|
||||||
|
RDP_PLUGIN_DATA* data;
|
||||||
|
|
||||||
|
alsa = xnew(AudinALSADevice);
|
||||||
|
|
||||||
|
alsa->iface.Open = audin_alsa_open;
|
||||||
|
alsa->iface.FormatSupported = audin_alsa_format_supported;
|
||||||
|
alsa->iface.SetFormat = audin_alsa_set_format;
|
||||||
|
alsa->iface.Close = audin_alsa_close;
|
||||||
|
alsa->iface.Free = audin_alsa_free;
|
||||||
|
|
||||||
|
data = pEntryPoints->plugin_data;
|
||||||
|
if (data && data->data[0] && strcmp(data->data[0], "audin") == 0 &&
|
||||||
|
data->data[1] && strcmp(data->data[1], "alsa") == 0)
|
||||||
|
{
|
||||||
|
strncpy(alsa->device_name, (char*)data->data[2], sizeof(alsa->device_name));
|
||||||
|
}
|
||||||
|
if (alsa->device_name[0] == '\0')
|
||||||
|
{
|
||||||
|
strcpy(alsa->device_name, "default");
|
||||||
|
}
|
||||||
|
|
||||||
|
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->thread = freerdp_thread_new();
|
||||||
|
|
||||||
|
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) alsa);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
542
channels/drdynvc/audin/audin_main.c
Normal file
542
channels/drdynvc/audin/audin_main.c
Normal file
@ -0,0 +1,542 @@
|
|||||||
|
/**
|
||||||
|
* FreeRDP: A Remote Desktop Protocol client.
|
||||||
|
* Audio Input Reirection Virtual Channel
|
||||||
|
*
|
||||||
|
* Copyright 2010-2011 Vic Lee
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <freerdp/utils/memory.h>
|
||||||
|
#include <freerdp/utils/stream.h>
|
||||||
|
#include <freerdp/utils/load_plugin.h>
|
||||||
|
|
||||||
|
#include "audin_main.h"
|
||||||
|
|
||||||
|
#define MSG_SNDIN_VERSION 0x01
|
||||||
|
#define MSG_SNDIN_FORMATS 0x02
|
||||||
|
#define MSG_SNDIN_OPEN 0x03
|
||||||
|
#define MSG_SNDIN_OPEN_REPLY 0x04
|
||||||
|
#define MSG_SNDIN_DATA_INCOMING 0x05
|
||||||
|
#define MSG_SNDIN_DATA 0x06
|
||||||
|
#define MSG_SNDIN_FORMATCHANGE 0x07
|
||||||
|
|
||||||
|
typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK;
|
||||||
|
struct _AUDIN_LISTENER_CALLBACK
|
||||||
|
{
|
||||||
|
IWTSListenerCallback iface;
|
||||||
|
|
||||||
|
IWTSPlugin* plugin;
|
||||||
|
IWTSVirtualChannelManager* channel_mgr;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _AUDIN_CHANNEL_CALLBACK AUDIN_CHANNEL_CALLBACK;
|
||||||
|
struct _AUDIN_CHANNEL_CALLBACK
|
||||||
|
{
|
||||||
|
IWTSVirtualChannelCallback iface;
|
||||||
|
|
||||||
|
IWTSPlugin* plugin;
|
||||||
|
IWTSVirtualChannelManager* channel_mgr;
|
||||||
|
IWTSVirtualChannel* channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The supported format list sent back to the server, which needs to
|
||||||
|
* be stored as reference when the server sends the format index in
|
||||||
|
* Open PDU and Format Change PDU
|
||||||
|
*/
|
||||||
|
audinFormat* formats;
|
||||||
|
int formats_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _AUDIN_PLUGIN AUDIN_PLUGIN;
|
||||||
|
struct _AUDIN_PLUGIN
|
||||||
|
{
|
||||||
|
IWTSPlugin iface;
|
||||||
|
|
||||||
|
AUDIN_LISTENER_CALLBACK* listener_callback;
|
||||||
|
|
||||||
|
/* Parsed plugin data */
|
||||||
|
uint16 fixed_format;
|
||||||
|
uint16 fixed_channel;
|
||||||
|
uint32 fixed_rate;
|
||||||
|
|
||||||
|
/* Device interface */
|
||||||
|
IAudinDevice* device;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, STREAM* s)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
STREAM* out;
|
||||||
|
uint32 Version;
|
||||||
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||||
|
|
||||||
|
stream_read_uint32(s, Version);
|
||||||
|
|
||||||
|
DEBUG_DVC("Version=%d", Version);
|
||||||
|
|
||||||
|
out = stream_new(5);
|
||||||
|
stream_write_uint8(out, MSG_SNDIN_VERSION);
|
||||||
|
stream_write_uint32(out, Version);
|
||||||
|
error = callback->channel->Write(callback->channel, stream_get_length(s), stream_get_head(s), NULL);
|
||||||
|
stream_free(out);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_send_incoming_data_pdu(IWTSVirtualChannelCallback* pChannelCallback)
|
||||||
|
{
|
||||||
|
uint8 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, STREAM* s)
|
||||||
|
{
|
||||||
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||||
|
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||||
|
uint32 i;
|
||||||
|
uint8* fm;
|
||||||
|
int error;
|
||||||
|
STREAM* out;
|
||||||
|
uint32 NumFormats;
|
||||||
|
audinFormat format;
|
||||||
|
uint32 cbSizeFormatsPacket;
|
||||||
|
|
||||||
|
stream_read_uint32(s, NumFormats);
|
||||||
|
DEBUG_DVC("NumFormats %d", NumFormats);
|
||||||
|
if ((NumFormats < 1) || (NumFormats > 1000))
|
||||||
|
{
|
||||||
|
DEBUG_WARN("bad NumFormats %d", NumFormats);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
stream_seek_uint32(s); /* cbSizeFormatsPacket */
|
||||||
|
|
||||||
|
callback->formats = (audinFormat*) xzalloc(NumFormats * sizeof(audinFormat));
|
||||||
|
|
||||||
|
out = stream_new(9);
|
||||||
|
stream_seek(out, 9);
|
||||||
|
|
||||||
|
/* SoundFormats (variable) */
|
||||||
|
for (i = 0; i < NumFormats; i++)
|
||||||
|
{
|
||||||
|
stream_get_mark(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_uint16(s, format.nBlockAlign);
|
||||||
|
stream_read_uint16(s, format.wBitsPerSample);
|
||||||
|
stream_read_uint16(s, format.cbSize);
|
||||||
|
format.data = stream_get_tail(s);
|
||||||
|
stream_seek(s, format.cbSize);
|
||||||
|
|
||||||
|
DEBUG_DVC("wFormatTag=%d nChannels=%d nSamplesPerSec=%d "
|
||||||
|
"nBlockAlign=%d wBitsPerSample=%d cbSize=%d",
|
||||||
|
format.wFormatTag, format.nChannels, format.nSamplesPerSec,
|
||||||
|
format.nBlockAlign, format.wBitsPerSample, format.cbSize);
|
||||||
|
|
||||||
|
if (audin->fixed_format > 0 && audin->fixed_format != format.wFormatTag)
|
||||||
|
continue;
|
||||||
|
if (audin->fixed_channel > 0 && audin->fixed_channel != format.nChannels)
|
||||||
|
continue;
|
||||||
|
if (audin->fixed_rate > 0 && audin->fixed_rate != format.nSamplesPerSec)
|
||||||
|
continue;
|
||||||
|
if (audin->device && audin->device->FormatSupported(audin->device, &format))
|
||||||
|
{
|
||||||
|
DEBUG_DVC("format ok");
|
||||||
|
|
||||||
|
/* Store the agreed format in the corresponding index */
|
||||||
|
callback->formats[callback->formats_count++] = format;
|
||||||
|
/* Put the format to output buffer */
|
||||||
|
stream_check_size(out, 18 + format.cbSize);
|
||||||
|
stream_write(out, fm, 18 + format.cbSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audin_send_incoming_data_pdu(pChannelCallback);
|
||||||
|
|
||||||
|
cbSizeFormatsPacket = stream_get_pos(out);
|
||||||
|
stream_set_pos(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) */
|
||||||
|
|
||||||
|
error = callback->channel->Write(callback->channel, cbSizeFormatsPacket, stream_get_head(out), NULL);
|
||||||
|
stream_free(out);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_send_format_change_pdu(IWTSVirtualChannelCallback* pChannelCallback, uint32 NewFormat)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
STREAM* out;
|
||||||
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||||
|
|
||||||
|
out = stream_new(5);
|
||||||
|
stream_write_uint8(out, MSG_SNDIN_FORMATCHANGE);
|
||||||
|
stream_write_uint32(out, NewFormat);
|
||||||
|
error = callback->channel->Write(callback->channel, 5, stream_get_head(out), NULL);
|
||||||
|
stream_free(out);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallback, uint32 Result)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
STREAM* out;
|
||||||
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||||
|
|
||||||
|
out = stream_new(5);
|
||||||
|
stream_write_uint8(out, MSG_SNDIN_OPEN_REPLY);
|
||||||
|
stream_write_uint32(out, Result);
|
||||||
|
error = callback->channel->Write(callback->channel, 5, stream_get_head(out), NULL);
|
||||||
|
stream_free(out);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean audin_receive_wave_data(uint8* data, int size, void* user_data)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
STREAM* out;
|
||||||
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data;
|
||||||
|
|
||||||
|
error = audin_send_incoming_data_pdu((IWTSVirtualChannelCallback*) callback);
|
||||||
|
if (error != 0)
|
||||||
|
return False;
|
||||||
|
|
||||||
|
out = stream_new(size + 1);
|
||||||
|
stream_write_uint8(out, MSG_SNDIN_DATA);
|
||||||
|
stream_write(out, data, size);
|
||||||
|
error = callback->channel->Write(callback->channel, stream_get_length(out), stream_get_head(out), NULL);
|
||||||
|
stream_free(out);
|
||||||
|
|
||||||
|
return (error == 0 ? True : False);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, STREAM* s)
|
||||||
|
{
|
||||||
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||||
|
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||||
|
audinFormat* format;
|
||||||
|
uint32 initialFormat;
|
||||||
|
uint32 FramesPerPacket;
|
||||||
|
|
||||||
|
stream_read_uint32(s, FramesPerPacket);
|
||||||
|
stream_read_uint32(s, initialFormat);
|
||||||
|
|
||||||
|
DEBUG_DVC("FramesPerPacket=%d initialFormat=%d",
|
||||||
|
FramesPerPacket, initialFormat);
|
||||||
|
|
||||||
|
if (initialFormat >= callback->formats_count)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("invalid format index %d (total %d)",
|
||||||
|
initialFormat, callback->formats_count);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
format = &callback->formats[initialFormat];
|
||||||
|
if (audin->device)
|
||||||
|
{
|
||||||
|
IFCALL(audin->device->SetFormat, audin->device, format, FramesPerPacket);
|
||||||
|
IFCALL(audin->device->Open, audin->device, audin_receive_wave_data, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
audin_send_format_change_pdu(pChannelCallback, initialFormat);
|
||||||
|
audin_send_open_reply_pdu(pChannelCallback, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallback, STREAM* s)
|
||||||
|
{
|
||||||
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||||
|
AUDIN_PLUGIN * audin = (AUDIN_PLUGIN *) callback->plugin;
|
||||||
|
uint32 NewFormat;
|
||||||
|
audinFormat* format;
|
||||||
|
|
||||||
|
stream_read_uint32(s, NewFormat);
|
||||||
|
|
||||||
|
DEBUG_DVC("NewFormat=%d", NewFormat);
|
||||||
|
|
||||||
|
if (NewFormat >= callback->formats_count)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("invalid format index %d (total %d)",
|
||||||
|
NewFormat, callback->formats_count);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
format = &callback->formats[NewFormat];
|
||||||
|
|
||||||
|
if (audin->device)
|
||||||
|
{
|
||||||
|
IFCALL(audin->device->Close, audin->device);
|
||||||
|
IFCALL(audin->device->SetFormat, audin->device, format, 0);
|
||||||
|
IFCALL(audin->device->Open, audin->device, audin_receive_wave_data, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
audin_send_format_change_pdu(pChannelCallback, NewFormat);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||||
|
uint32 cbSize,
|
||||||
|
uint8* pBuffer)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
STREAM* s;
|
||||||
|
uint8 MessageId;
|
||||||
|
|
||||||
|
s = stream_new(0);
|
||||||
|
stream_attach(s, pBuffer, cbSize);
|
||||||
|
|
||||||
|
stream_read_uint8(s, MessageId);
|
||||||
|
|
||||||
|
DEBUG_DVC("MessageId=0x%x", MessageId);
|
||||||
|
|
||||||
|
switch (MessageId)
|
||||||
|
{
|
||||||
|
case MSG_SNDIN_VERSION:
|
||||||
|
error = audin_process_version(pChannelCallback, s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_SNDIN_FORMATS:
|
||||||
|
error = audin_process_formats(pChannelCallback, s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_SNDIN_OPEN:
|
||||||
|
error = audin_process_open(pChannelCallback, s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_SNDIN_FORMATCHANGE:
|
||||||
|
error = audin_process_format_change(pChannelCallback, s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_WARN("unknown MessageId=0x%x", MessageId);
|
||||||
|
error = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_detach(s);
|
||||||
|
stream_free(s);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||||
|
{
|
||||||
|
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
|
||||||
|
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
|
||||||
|
|
||||||
|
DEBUG_DVC("");
|
||||||
|
|
||||||
|
if (audin->device)
|
||||||
|
IFCALL(audin->device->Close, audin->device);
|
||||||
|
|
||||||
|
xfree(callback->formats);
|
||||||
|
xfree(callback);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||||
|
IWTSVirtualChannel* pChannel,
|
||||||
|
uint8* Data,
|
||||||
|
int* pbAccept,
|
||||||
|
IWTSVirtualChannelCallback** ppCallback)
|
||||||
|
{
|
||||||
|
AUDIN_CHANNEL_CALLBACK* callback;
|
||||||
|
AUDIN_LISTENER_CALLBACK* listener_callback = (AUDIN_LISTENER_CALLBACK*) pListenerCallback;
|
||||||
|
|
||||||
|
DEBUG_DVC("");
|
||||||
|
|
||||||
|
callback = xnew(AUDIN_CHANNEL_CALLBACK);
|
||||||
|
|
||||||
|
callback->iface.OnDataReceived = audin_on_data_received;
|
||||||
|
callback->iface.OnClose = audin_on_close;
|
||||||
|
callback->plugin = listener_callback->plugin;
|
||||||
|
callback->channel_mgr = listener_callback->channel_mgr;
|
||||||
|
callback->channel = pChannel;
|
||||||
|
|
||||||
|
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
||||||
|
{
|
||||||
|
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||||
|
|
||||||
|
DEBUG_DVC("");
|
||||||
|
|
||||||
|
audin->listener_callback = xnew(AUDIN_LISTENER_CALLBACK);
|
||||||
|
|
||||||
|
audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
|
||||||
|
audin->listener_callback->plugin = pPlugin;
|
||||||
|
audin->listener_callback->channel_mgr = pChannelMgr;
|
||||||
|
return pChannelMgr->CreateListener(pChannelMgr, "AUDIO_INPUT", 0,
|
||||||
|
(IWTSListenerCallback*) audin->listener_callback, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int audin_plugin_terminated(IWTSPlugin* pPlugin)
|
||||||
|
{
|
||||||
|
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||||
|
|
||||||
|
DEBUG_DVC("");
|
||||||
|
|
||||||
|
if (audin->device)
|
||||||
|
{
|
||||||
|
IFCALL(audin->device->Close, audin->device);
|
||||||
|
IFCALL(audin->device->Free, audin->device);
|
||||||
|
audin->device = NULL;
|
||||||
|
}
|
||||||
|
xfree(audin->listener_callback);
|
||||||
|
xfree(audin);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
|
||||||
|
{
|
||||||
|
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||||
|
|
||||||
|
if (audin->device)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("existing device, abort.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_DVC("device registered.");
|
||||||
|
|
||||||
|
audin->device = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean audin_load_device_plugin(IWTSPlugin* pPlugin, const char* name, RDP_PLUGIN_DATA* data)
|
||||||
|
{
|
||||||
|
char* fullname;
|
||||||
|
PFREERDP_AUDIN_DEVICE_ENTRY entry;
|
||||||
|
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
|
||||||
|
|
||||||
|
if (strrchr(name, '.') != NULL)
|
||||||
|
entry = (PFREERDP_AUDIN_DEVICE_ENTRY)freerdp_load_plugin(name, AUDIN_DEVICE_EXPORT_FUNC_NAME);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fullname = xzalloc(strlen(name) + 8);
|
||||||
|
strcpy(fullname, "audin_");
|
||||||
|
strcat(fullname, name);
|
||||||
|
entry = (PFREERDP_AUDIN_DEVICE_ENTRY)freerdp_load_plugin(fullname, AUDIN_DEVICE_EXPORT_FUNC_NAME);
|
||||||
|
xfree(fullname);
|
||||||
|
}
|
||||||
|
if (entry == NULL)
|
||||||
|
{
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPoints.plugin = pPlugin;
|
||||||
|
entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
|
||||||
|
entryPoints.plugin_data = data;
|
||||||
|
if (entry(&entryPoints) != 0)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("%s entry returns error.", name);
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean audin_process_plugin_data(IWTSPlugin* pPlugin, RDP_PLUGIN_DATA* data)
|
||||||
|
{
|
||||||
|
boolean ret;
|
||||||
|
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
|
||||||
|
RDP_PLUGIN_DATA default_data[2] = { { 0 }, { 0 } };
|
||||||
|
|
||||||
|
if (data->data[0] && strcmp((char*)data->data[0], "audin") == 0)
|
||||||
|
{
|
||||||
|
if (data->data[1] && strcmp((char*)data->data[1], "format") == 0)
|
||||||
|
{
|
||||||
|
audin->fixed_format = atoi(data->data[2]);
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
else if (data->data[1] && strcmp((char*)data->data[1], "rate") == 0)
|
||||||
|
{
|
||||||
|
audin->fixed_rate = atoi(data->data[2]);
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
else if (data->data[1] && strcmp((char*)data->data[1], "channel") == 0)
|
||||||
|
{
|
||||||
|
audin->fixed_channel = atoi(data->data[2]);
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
else if (data->data[1] && ((char*)data->data[1])[0])
|
||||||
|
{
|
||||||
|
return audin_load_device_plugin(pPlugin, (char*)data->data[1], data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
default_data[0].size = sizeof(RDP_PLUGIN_DATA);
|
||||||
|
default_data[0].data[0] = "audin";
|
||||||
|
default_data[0].data[1] = "pulse";
|
||||||
|
default_data[0].data[2] = "";
|
||||||
|
ret = audin_load_device_plugin(pPlugin, "pulse", default_data);
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
default_data[0].size = sizeof(RDP_PLUGIN_DATA);
|
||||||
|
default_data[0].data[0] = "audin";
|
||||||
|
default_data[0].data[1] = "alsa";
|
||||||
|
default_data[0].data[2] = "default";
|
||||||
|
ret = audin_load_device_plugin(pPlugin, "alsa", default_data);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
AUDIN_PLUGIN* audin;
|
||||||
|
|
||||||
|
audin = (AUDIN_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "audin");
|
||||||
|
if (audin == NULL)
|
||||||
|
{
|
||||||
|
audin = xnew(AUDIN_PLUGIN);
|
||||||
|
|
||||||
|
audin->iface.Initialize = audin_plugin_initialize;
|
||||||
|
audin->iface.Connected = NULL;
|
||||||
|
audin->iface.Disconnected = NULL;
|
||||||
|
audin->iface.Terminated = audin_plugin_terminated;
|
||||||
|
error = pEntryPoints->RegisterPlugin(pEntryPoints, "audin", (IWTSPlugin*) audin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == 0)
|
||||||
|
{
|
||||||
|
audin_process_plugin_data((IWTSPlugin*) audin,
|
||||||
|
pEntryPoints->GetPluginData(pEntryPoints));
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
65
channels/drdynvc/audin/audin_main.h
Normal file
65
channels/drdynvc/audin/audin_main.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* FreeRDP: A Remote Desktop Protocol client.
|
||||||
|
* Audio Input Reirection Virtual Channel
|
||||||
|
*
|
||||||
|
* Copyright 2010-2011 Vic Lee
|
||||||
|
*
|
||||||
|
* 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 __AUDIN_MAIN_H
|
||||||
|
#define __AUDIN_MAIN_H
|
||||||
|
|
||||||
|
#include "drdynvc_types.h"
|
||||||
|
|
||||||
|
typedef boolean (*AudinReceive) (uint8* data, int size, void* user_data);
|
||||||
|
|
||||||
|
typedef struct audin_format audinFormat;
|
||||||
|
struct audin_format
|
||||||
|
{
|
||||||
|
uint16 wFormatTag;
|
||||||
|
uint16 nChannels;
|
||||||
|
uint32 nSamplesPerSec;
|
||||||
|
uint16 nBlockAlign;
|
||||||
|
uint16 wBitsPerSample;
|
||||||
|
uint16 cbSize;
|
||||||
|
uint8* data;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _IAudinDevice IAudinDevice;
|
||||||
|
struct _IAudinDevice
|
||||||
|
{
|
||||||
|
void (*Open) (IAudinDevice* devplugin, AudinReceive receive, void* user_data);
|
||||||
|
boolean (*FormatSupported) (IAudinDevice* devplugin, audinFormat* format);
|
||||||
|
void (*SetFormat) (IAudinDevice* devplugin, audinFormat* format, uint32 FramesPerPacket);
|
||||||
|
void (*Close) (IAudinDevice* devplugin);
|
||||||
|
void (*Free) (IAudinDevice* devplugin);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define AUDIN_DEVICE_EXPORT_FUNC_NAME "FreeRDPAudinDeviceEntry"
|
||||||
|
|
||||||
|
typedef void (*PREGISTERAUDINDEVICE)(IWTSPlugin* plugin, IAudinDevice* device);
|
||||||
|
|
||||||
|
struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS
|
||||||
|
{
|
||||||
|
IWTSPlugin* plugin;
|
||||||
|
PREGISTERAUDINDEVICE pRegisterAudinDevice;
|
||||||
|
RDP_PLUGIN_DATA* plugin_data;
|
||||||
|
};
|
||||||
|
typedef struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS FREERDP_AUDIN_DEVICE_ENTRY_POINTS;
|
||||||
|
typedef FREERDP_AUDIN_DEVICE_ENTRY_POINTS* PFREERDP_AUDIN_DEVICE_ENTRY_POINTS;
|
||||||
|
|
||||||
|
typedef int (*PFREERDP_AUDIN_DEVICE_ENTRY)(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints);
|
||||||
|
|
||||||
|
#endif /* __AUDIN_MAIN_H */
|
||||||
|
|
34
channels/drdynvc/audin/pulse/CMakeLists.txt
Normal file
34
channels/drdynvc/audin/pulse/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# FreeRDP: A Remote Desktop Protocol Client
|
||||||
|
# FreeRDP cmake build script
|
||||||
|
#
|
||||||
|
# Copyright 2011 O.S. Systems Software Ltda.
|
||||||
|
# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
|
||||||
|
# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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.
|
||||||
|
|
||||||
|
set(AUDIN_PULSE_SRCS
|
||||||
|
audin_pulse.c
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(..)
|
||||||
|
include_directories(${PULSE_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
add_library(audin_pulse ${AUDIN_PULSE_SRCS})
|
||||||
|
set_target_properties(audin_pulse PROPERTIES PREFIX "")
|
||||||
|
|
||||||
|
target_link_libraries(audin_pulse freerdp-utils)
|
||||||
|
target_link_libraries(audin_pulse ${PULSE_LIBRARIES})
|
||||||
|
|
||||||
|
install(TARGETS audin_pulse DESTINATION ${FREERDP_PLUGIN_PATH})
|
||||||
|
|
471
channels/drdynvc/audin/pulse/audin_pulse.c
Normal file
471
channels/drdynvc/audin/pulse/audin_pulse.c
Normal file
@ -0,0 +1,471 @@
|
|||||||
|
/**
|
||||||
|
* FreeRDP: A Remote Desktop Protocol client.
|
||||||
|
* Audio Input Redirection Virtual Channel - PulseAudio implementation
|
||||||
|
*
|
||||||
|
* Copyright 2010-2011 Vic Lee
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pulse/pulseaudio.h>
|
||||||
|
#include <freerdp/types.h>
|
||||||
|
#include <freerdp/utils/memory.h>
|
||||||
|
#include <freerdp/utils/dsp.h>
|
||||||
|
|
||||||
|
#include "audin_main.h"
|
||||||
|
|
||||||
|
typedef struct _AudinPulseDevice
|
||||||
|
{
|
||||||
|
IAudinDevice iface;
|
||||||
|
|
||||||
|
char device_name[32];
|
||||||
|
uint32 frames_per_packet;
|
||||||
|
pa_threaded_mainloop* mainloop;
|
||||||
|
pa_context* context;
|
||||||
|
pa_sample_spec sample_spec;
|
||||||
|
pa_stream* stream;
|
||||||
|
int format;
|
||||||
|
int block_size;
|
||||||
|
ADPCM adpcm;
|
||||||
|
|
||||||
|
int bytes_per_frame;
|
||||||
|
uint8* buffer;
|
||||||
|
int buffer_frames;
|
||||||
|
|
||||||
|
AudinReceive receive;
|
||||||
|
void* user_data;
|
||||||
|
} AudinPulseDevice;
|
||||||
|
|
||||||
|
static void audin_pulse_context_state_callback(pa_context* context, void* userdata)
|
||||||
|
{
|
||||||
|
pa_context_state_t state;
|
||||||
|
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
||||||
|
|
||||||
|
state = pa_context_get_state(context);
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case PA_CONTEXT_READY:
|
||||||
|
DEBUG_DVC("PA_CONTEXT_READY");
|
||||||
|
pa_threaded_mainloop_signal (pulse->mainloop, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PA_CONTEXT_FAILED:
|
||||||
|
case PA_CONTEXT_TERMINATED:
|
||||||
|
DEBUG_DVC("state %d", (int)state);
|
||||||
|
pa_threaded_mainloop_signal (pulse->mainloop, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_DVC("state %d", (int)state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean audin_pulse_connect(IAudinDevice* device)
|
||||||
|
{
|
||||||
|
pa_context_state_t state;
|
||||||
|
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||||
|
|
||||||
|
if (!pulse->context)
|
||||||
|
return False;
|
||||||
|
|
||||||
|
if (pa_context_connect(pulse->context, NULL, 0, NULL))
|
||||||
|
{
|
||||||
|
DEBUG_WARN("pa_context_connect failed (%d)",
|
||||||
|
pa_context_errno(pulse->context));
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
|
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
|
DEBUG_WARN("pa_threaded_mainloop_start failed (%d)",
|
||||||
|
pa_context_errno(pulse->context));
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
state = pa_context_get_state(pulse->context);
|
||||||
|
if (state == PA_CONTEXT_READY)
|
||||||
|
break;
|
||||||
|
if (!PA_CONTEXT_IS_GOOD(state))
|
||||||
|
{
|
||||||
|
DEBUG_WARN("bad context state (%d)",
|
||||||
|
pa_context_errno(pulse->context));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||||
|
}
|
||||||
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
|
if (state == PA_CONTEXT_READY)
|
||||||
|
{
|
||||||
|
DEBUG_DVC("connected");
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pa_context_disconnect(pulse->context);
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_pulse_free(IAudinDevice* device)
|
||||||
|
{
|
||||||
|
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||||
|
|
||||||
|
DEBUG_DVC("");
|
||||||
|
|
||||||
|
if (!pulse)
|
||||||
|
return;
|
||||||
|
if (pulse->mainloop)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_stop(pulse->mainloop);
|
||||||
|
}
|
||||||
|
if (pulse->context)
|
||||||
|
{
|
||||||
|
pa_context_disconnect(pulse->context);
|
||||||
|
pa_context_unref(pulse->context);
|
||||||
|
pulse->context = NULL;
|
||||||
|
}
|
||||||
|
if (pulse->mainloop)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_free(pulse->mainloop);
|
||||||
|
pulse->mainloop = NULL;
|
||||||
|
}
|
||||||
|
xfree(pulse);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean audin_pulse_format_supported(IAudinDevice* device, audinFormat* format)
|
||||||
|
{
|
||||||
|
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||||
|
|
||||||
|
if (!pulse->context)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (format->wFormatTag)
|
||||||
|
{
|
||||||
|
case 1: /* PCM */
|
||||||
|
if (format->cbSize == 0 &&
|
||||||
|
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||||
|
(format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
|
||||||
|
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||||
|
{
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6: /* A-LAW */
|
||||||
|
case 7: /* U-LAW */
|
||||||
|
if (format->cbSize == 0 &&
|
||||||
|
(format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||||
|
(format->wBitsPerSample == 8) &&
|
||||||
|
(format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
|
||||||
|
{
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x11: /* IMA ADPCM */
|
||||||
|
if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
|
||||||
|
(format->wBitsPerSample == 4) &&
|
||||||
|
(format->nChannels == 1 || format->nChannels == 2))
|
||||||
|
{
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_pulse_set_format(IAudinDevice* device, audinFormat* format, uint32 FramesPerPacket)
|
||||||
|
{
|
||||||
|
int bs;
|
||||||
|
pa_sample_spec sample_spec = { 0 };
|
||||||
|
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||||
|
|
||||||
|
if (!pulse->context)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (FramesPerPacket > 0)
|
||||||
|
{
|
||||||
|
pulse->frames_per_packet = FramesPerPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
sample_spec.rate = format->nSamplesPerSec;
|
||||||
|
sample_spec.channels = format->nChannels;
|
||||||
|
switch (format->wFormatTag)
|
||||||
|
{
|
||||||
|
case 1: /* PCM */
|
||||||
|
switch (format->wBitsPerSample)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
sample_spec.format = PA_SAMPLE_U8;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
sample_spec.format = PA_SAMPLE_S16LE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6: /* A-LAW */
|
||||||
|
sample_spec.format = PA_SAMPLE_ALAW;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7: /* 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=%d",
|
||||||
|
pulse->frames_per_packet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pulse->sample_spec = sample_spec;
|
||||||
|
pulse->format = format->wFormatTag;
|
||||||
|
pulse->block_size = format->nBlockAlign;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
|
||||||
|
{
|
||||||
|
pa_stream_state_t state;
|
||||||
|
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
||||||
|
|
||||||
|
state = pa_stream_get_state(stream);
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case PA_STREAM_READY:
|
||||||
|
DEBUG_DVC("PA_STREAM_READY");
|
||||||
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PA_STREAM_FAILED:
|
||||||
|
case PA_STREAM_TERMINATED:
|
||||||
|
DEBUG_DVC("state %d", (int)state);
|
||||||
|
pa_threaded_mainloop_signal(pulse->mainloop, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_DVC("state %d", (int)state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
|
||||||
|
{
|
||||||
|
int frames;
|
||||||
|
int cframes;
|
||||||
|
boolean ret;
|
||||||
|
const void* data;
|
||||||
|
const uint8* src;
|
||||||
|
int encoded_size;
|
||||||
|
uint8* encoded_data;
|
||||||
|
AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
|
||||||
|
|
||||||
|
pa_stream_peek(stream, &data, &length);
|
||||||
|
frames = length / pulse->bytes_per_frame;
|
||||||
|
|
||||||
|
DEBUG_DVC("length %d frames %d", (int) length, frames);
|
||||||
|
|
||||||
|
src = (const uint8*) 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)
|
||||||
|
{
|
||||||
|
encoded_data = dsp_encode_ima_adpcm(&pulse->adpcm,
|
||||||
|
pulse->buffer, pulse->buffer_frames * pulse->bytes_per_frame,
|
||||||
|
pulse->sample_spec.channels, pulse->block_size, &encoded_size);
|
||||||
|
DEBUG_DVC("encoded %d to %d",
|
||||||
|
pulse->buffer_frames * pulse->bytes_per_frame, encoded_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encoded_data = pulse->buffer;
|
||||||
|
encoded_size = pulse->buffer_frames * pulse->bytes_per_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pulse->receive(encoded_data, encoded_size, pulse->user_data);
|
||||||
|
pulse->buffer_frames = 0;
|
||||||
|
if (encoded_data != pulse->buffer)
|
||||||
|
xfree(encoded_data);
|
||||||
|
if (!ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
src += cframes * pulse->bytes_per_frame;
|
||||||
|
frames -= cframes;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_stream_drop(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void audin_pulse_close(IAudinDevice* device)
|
||||||
|
{
|
||||||
|
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||||
|
|
||||||
|
if (!pulse->context || !pulse->stream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DEBUG_DVC("");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
pulse->receive = NULL;
|
||||||
|
pulse->user_data = NULL;
|
||||||
|
if (pulse->buffer)
|
||||||
|
{
|
||||||
|
xfree(pulse->buffer);
|
||||||
|
pulse->buffer = NULL;
|
||||||
|
pulse->buffer_frames = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
|
||||||
|
{
|
||||||
|
pa_stream_state_t state;
|
||||||
|
pa_buffer_attr buffer_attr = { 0 };
|
||||||
|
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
|
||||||
|
|
||||||
|
if (!pulse->context)
|
||||||
|
return;
|
||||||
|
if (!pulse->sample_spec.rate || pulse->stream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DEBUG_DVC("");
|
||||||
|
|
||||||
|
pulse->receive = receive;
|
||||||
|
pulse->user_data = user_data;
|
||||||
|
|
||||||
|
pa_threaded_mainloop_lock(pulse->mainloop);
|
||||||
|
pulse->stream = pa_stream_new(pulse->context, "freerdp_audin",
|
||||||
|
&pulse->sample_spec, NULL);
|
||||||
|
if (!pulse->stream)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
|
DEBUG_DVC("pa_stream_new failed (%d)",
|
||||||
|
pa_context_errno(pulse->context));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pulse->bytes_per_frame = pa_frame_size(&pulse->sample_spec);
|
||||||
|
pa_stream_set_state_callback(pulse->stream,
|
||||||
|
audin_pulse_stream_state_callback, pulse);
|
||||||
|
pa_stream_set_read_callback(pulse->stream,
|
||||||
|
audin_pulse_stream_request_callback, pulse);
|
||||||
|
buffer_attr.maxlength = (uint32_t) -1;
|
||||||
|
buffer_attr.tlength = (uint32_t) -1;
|
||||||
|
buffer_attr.prebuf = (uint32_t) -1;
|
||||||
|
buffer_attr.minreq = (uint32_t) -1;
|
||||||
|
/* 500ms latency */
|
||||||
|
buffer_attr.fragsize = pa_usec_to_bytes(500000, &pulse->sample_spec);
|
||||||
|
if (pa_stream_connect_record(pulse->stream,
|
||||||
|
pulse->device_name[0] ? pulse->device_name : NULL,
|
||||||
|
&buffer_attr, PA_STREAM_ADJUST_LATENCY) < 0)
|
||||||
|
{
|
||||||
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
|
DEBUG_WARN("pa_stream_connect_playback failed (%d)",
|
||||||
|
pa_context_errno(pulse->context));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
state = pa_stream_get_state(pulse->stream);
|
||||||
|
if (state == PA_STREAM_READY)
|
||||||
|
break;
|
||||||
|
if (!PA_STREAM_IS_GOOD(state))
|
||||||
|
{
|
||||||
|
DEBUG_WARN("bad stream state (%d)",
|
||||||
|
pa_context_errno(pulse->context));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pa_threaded_mainloop_wait(pulse->mainloop);
|
||||||
|
}
|
||||||
|
pa_threaded_mainloop_unlock(pulse->mainloop);
|
||||||
|
if (state == PA_STREAM_READY)
|
||||||
|
{
|
||||||
|
memset(&pulse->adpcm, 0, sizeof(ADPCM));
|
||||||
|
pulse->buffer = xzalloc(pulse->bytes_per_frame * pulse->frames_per_packet);
|
||||||
|
pulse->buffer_frames = 0;
|
||||||
|
DEBUG_DVC("connected");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
audin_pulse_close(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FreeRDPAudinDeviceEntry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||||
|
{
|
||||||
|
AudinPulseDevice* pulse;
|
||||||
|
RDP_PLUGIN_DATA * data;
|
||||||
|
|
||||||
|
pulse = xnew(AudinPulseDevice);
|
||||||
|
|
||||||
|
pulse->iface.Open = audin_pulse_open;
|
||||||
|
pulse->iface.FormatSupported = audin_pulse_format_supported;
|
||||||
|
pulse->iface.SetFormat = audin_pulse_set_format;
|
||||||
|
pulse->iface.Close = audin_pulse_close;
|
||||||
|
pulse->iface.Free = audin_pulse_free;
|
||||||
|
|
||||||
|
data = pEntryPoints->plugin_data;
|
||||||
|
if (data && data->data[0] && strcmp(data->data[0], "audin") == 0 &&
|
||||||
|
data->data[1] && strcmp(data->data[1], "pulse") == 0)
|
||||||
|
{
|
||||||
|
strncpy(pulse->device_name, (char*)data->data[2], sizeof(pulse->device_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
pulse->mainloop = pa_threaded_mainloop_new();
|
||||||
|
if (!pulse->mainloop)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("pa_threaded_mainloop_new failed");
|
||||||
|
audin_pulse_free((IAudinDevice*) pulse);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
|
||||||
|
if (!pulse->context)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("pa_context_new failed");
|
||||||
|
audin_pulse_free((IAudinDevice*) pulse);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
pa_context_set_state_callback(pulse->context, audin_pulse_context_state_callback, pulse);
|
||||||
|
if (!audin_pulse_connect((IAudinDevice*) pulse))
|
||||||
|
{
|
||||||
|
audin_pulse_free((IAudinDevice*) pulse);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user