Renamed plugins from 'opensl_es' to 'opensles'

This commit is contained in:
Armin Novak 2013-09-23 12:38:05 +02:00
parent 9203473d78
commit 2839f5d755
14 changed files with 1648 additions and 434 deletions

View File

@ -48,6 +48,6 @@ if(WITH_PULSE)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "pulse" "")
endif()
if(WITH_OPENSL_ES)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensl_es" "")
if(WITH_OPENSLES)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensles" "")
endif()

View File

@ -593,7 +593,7 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
if (!audin->device)
{
audin_set_subsystem(audin, "opensl_es");
audin_set_subsystem(audin, "opensles");
audin_set_device_name(audin, "default");
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
}

View File

@ -1,419 +0,0 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - OpenSL ES implementation
*
* Copyright 2013 Armin Novak <armin.novak@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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/cmdline.h>
#include <freerdp/addin.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/rdpsnd.h>
#include <SLES/OpenSLES.h>
#include "opensl_io.h"
#include "audin_main.h"
typedef struct _AudinOpenSL_ESDevice
{
IAudinDevice iface;
char* device_name;
OPENSL_STREAM *stream;
UINT32 frames_per_packet;
UINT32 target_rate;
UINT32 actual_rate;
UINT32 target_channels;
UINT32 actual_channels;
int bytes_per_channel;
int wformat;
int block_size;
FREERDP_DSP_CONTEXT* dsp_context;
HANDLE thread;
HANDLE stopEvent;
void* user_data;
} AudinOpenSL_ESDevice;
static BOOL audin_opensl_es_thread_receive(AudinOpenSL_ESDevice* opensl_es,
void* src, int count)
{
int frames;
int cframes;
int ret = 0;
int encoded_size;
BYTE* encoded_data;
int rbytes_per_frame;
int tbytes_per_frame;
rbytes_per_frame = opensl_es->actual_channels * opensl_es->bytes_per_channel;
tbytes_per_frame = opensl_es->target_channels * opensl_es->bytes_per_channel;
if ((opensl_es->target_rate == opensl_es->actual_rate) &&
(opensl_es->target_channels == opensl_es->actual_channels))
{
frames = size / rbytes_per_frame;
}
else
{
opensl_es->dsp_context->resample(opensl_es->dsp_context, src,
opensl_es->bytes_per_channel, opensl_es->actual_channels,
opensl_es->actual_rate, size / rbytes_per_frame,
opensl_es->target_channels, opensl_es->target_rate);
frames = opensl_es->dsp_context->resampled_frames;
DEBUG_DVC("resampled %d frames at %d to %d frames at %d",
size / rbytes_per_frame, opensl_es->actual_rate,
frames, opensl_es->target_rate);
size = frames * tbytes_per_frame;
src = opensl_es->dsp_context->resampled_buffer;
}
while (frames > 0)
{
if (WaitForSingleObject(opensl_es->stopEvent, 0) == WAIT_OBJECT_0)
break;
cframes = opensl_es->frames_per_packet - opensl_es->buffer_frames;
if (cframes > frames)
cframes = frames;
CopyMemory(opensl_es->buffer + opensl_es->buffer_frames * tbytes_per_frame,
src, cframes * tbytes_per_frame);
opensl_es->buffer_frames += cframes;
if (opensl_es->buffer_frames >= opensl_es->frames_per_packet)
{
if (opensl_es->wformat == WAVE_FORMAT_DVI_ADPCM)
{
opensl_es->dsp_context->encode_ima_adpcm(opensl_es->dsp_context,
opensl_es->buffer, opensl_es->buffer_frames * tbytes_per_frame,
opensl_es->target_channels, opensl_es->block_size);
encoded_data = opensl_es->dsp_context->adpcm_buffer;
encoded_size = opensl_es->dsp_context->adpcm_size;
DEBUG_DVC("encoded %d to %d",
opensl_es->buffer_frames * tbytes_per_frame, encoded_size);
}
else
{
encoded_data = opensl_es->buffer;
encoded_size = opensl_es->buffer_frames * tbytes_per_frame;
}
if (WaitForSingleObject(opensl_es->stopEvent, 0) == WAIT_OBJECT_0)
break;
else
ret = opensl_es->receive(encoded_data, encoded_size,
opensl_es->user_data);
opensl_es->buffer_frames = 0;
if (!ret)
break;
}
src += cframes * tbytes_per_frame;
frames -= cframes;
}
return (ret) ? TRUE : FALSE;
}
static void* audin_opensl_es_thread_func(void* arg)
{
float* buffer;
int rbytes_per_frame;
int tbytes_per_frame;
snd_pcm_t* capture_handle = NULL;
AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) arg;
DEBUG_DVC("opensl_es=%p", opensl_es);
buffer = (BYTE*) calloc(sizeof(float), opensl_es->frames_per_packet);
ZeroMemory(buffer, opensl_es->frames_per_packet);
freerdp_dsp_context_reset_adpcm(opensl_es->dsp_context);
while (!(WaitForSingleObject(opensl_es->stopEvent, 0) == WAIT_OBJECT_0))
{
int rc = android_AudioIn(opensl_es->stream, buffer,
opensl_es->frames_per_packet);
if (rc < 0)
{
DEBUG_WARN("snd_pcm_readi (%s)", snd_strerror(error));
break;
}
if (!audin_opensl_es_thread_receive(opensl_es, buffer, rc * sizeof(float)))
break;
}
free(buffer);
DEBUG_DVC("thread shutdown.");
ExitThread(0);
return NULL;
}
static void audin_opensl_es_free(IAudinDevice* device)
{
AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device;
DEBUG_DVC("device=%p", device);
freerdp_dsp_context_free(opensl_es->dsp_context);
free(opensl_es->device_name);
free(opensl_es);
}
static BOOL audin_opensl_es_format_supported(IAudinDevice* device, audinFormat* format)
{
AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device;
SLResult rc;
DEBUG_DVC("device=%p, format=%p", device, 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))
{
return TRUE;
}
break;
case WAVE_FORMAT_DVI_ADPCM:
if ((format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
}
return FALSE;
}
static void audin_opensl_es_set_format(IAudinDevice* device,
audinFormat* format, UINT32 FramesPerPacket)
{
int bs;
AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device;
DEBUG_DVC("device=%p, format=%p, FramesPerPacket=%d",
device, format, FramesPerPacket);
opensl_es->target_rate = format->nSamplesPerSec;
opensl_es->actual_rate = format->nSamplesPerSec;
opensl_es->target_channels = format->nChannels;
opensl_es->actual_channels = format->nChannels;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 8:
opensl_es->format = SND_PCM_FORMAT_S8;
opensl_es->bytes_per_channel = 1;
break;
case 16:
opensl_es->format = SND_PCM_FORMAT_S16_LE;
opensl_es->bytes_per_channel = 2;
break;
}
break;
case WAVE_FORMAT_DVI_ADPCM:
opensl_es->format = SND_PCM_FORMAT_S16_LE;
opensl_es->bytes_per_channel = 2;
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
opensl_es->frames_per_packet =
(opensl_es->frames_per_packet * format->nChannels * 2 /
bs + 1) * bs / (format->nChannels * 2);
DEBUG_DVC("aligned FramesPerPacket=%d",
opensl_es->frames_per_packet);
break;
}
opensl_es->wformat = format->wFormatTag;
opensl_es->block_size = format->nBlockAlign;
}
static int audin_opensl_es_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
{
int status = 0;
int rbytes_per_frame;
int tbytes_per_frame;
AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device;
DEBUG_DVC("device=%p, receive=%d, user_data=%p", device, receive, user_data);
opensl_es->stream = android_OpenAudioDevice(opensl_es->target_rate,
opensl_es->target_channels, 0, opensl_es->frames_per_packet);
opensl_es->receive = receive;
opensl_es->user_data = user_data;
rbytes_per_frame = opensl_es->actual_channels * opensl_es->bytes_per_channel;
tbytes_per_frame = opensl_es->target_channels * opensl_es->bytes_per_channel;
opensl_es->buffer =
(BYTE*) malloc(tbytes_per_frame * opensl_es->frames_per_packet);
ZeroMemory(opensl_es->buffer,
tbytes_per_frame * opensl_es->frames_per_packet);
opensl_es->buffer_frames = 0;
opensl_es->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
opensl_es->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) audin_opensl_es_thread_func,
opensl_es, 0, NULL);
}
static void audin_opensl_es_close(IAudinDevice* device)
{
AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device;
DEBUG_DVC("device=%p", device);
SetEvent(opensl_es->stopEvent);
WaitForSingleObject(opensl_es->thread, INFINITE);
CloseHandle(opensl_es->stopEvent);
CloseHandle(opensl_es->thread);
android_CloseAudioDevice(opensl_es->stream);
opensl_es->stopEvent = NULL;
opensl_es->thread = NULL;
opensl_es->receive = NULL;
opensl_es->user_data = NULL;
opsnsl_es->stream = NULL;
}
static const COMMAND_LINE_ARGUMENT_A audin_opensl_es_args[] =
{
{ "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
static int audin_opensl_es_parse_addin_args(AudinOpenSL_ESDevice* device,
ADDIN_ARGV* args)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AudinOpenSL_ESDevice* opensl_es = (AudinOpenSL_ESDevice*) device;
DEBUG_DVC("device=%p, args=%p", device, args);
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
audin_opensl_es_args, flags, opensl_es, NULL, NULL);
if (status < 0)
return status;
arg = audin_opensl_es_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "audio-dev")
{
opensl_es->device_name = _strdup(arg->Value);
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return status;
}
#ifdef STATIC_CHANNELS
#define freerdp_audin_client_subsystem_entry \
opensl_es_freerdp_audin_client_subsystem_entry
#endif
int freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
AudinOpenSL_ESDevice* opensl_es;
DEBUG_DVC("pEntryPoints=%p", pEntryPoints);
opensl_es = (AudinOpenSL_ESDevice*) malloc(sizeof(AudinOpenSL_ESDevice));
ZeroMemory(opensl_es, sizeof(AudinOpenSL_ESDevice));
opensl_es->iface.Open = audin_opensl_es_open;
opensl_es->iface.FormatSupported = audin_opensl_es_format_supported;
opensl_es->iface.SetFormat = audin_opensl_es_set_format;
opensl_es->iface.Close = audin_opensl_es_close;
opensl_es->iface.Free = audin_opensl_es_free;
args = pEntryPoints->args;
audin_opensl_es_parse_addin_args(opensl_es, args);
if (!opensl_es->device_name)
opensl_es->device_name = _strdup("default");
opensl_es->frames_per_packet = 128;
opensl_es->target_rate = 22050;
opensl_es->actual_rate = 22050;
opensl_es->format = SND_PCM_FORMAT_S16_LE;
opensl_es->target_channels = 2;
opensl_es->actual_channels = 2;
opensl_es->bytes_per_channel = 2;
opensl_es->dsp_context = freerdp_dsp_context_new();
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
(IAudinDevice*) opensl_es);
return 0;
}

View File

@ -21,7 +21,7 @@ set(${MODULE_PREFIX}_SRCS
audin_opensl_es.c)
include_directories(..)
include_directories(${OPENSL_ES_INCLUDE_DIRS})
include_directories(${OPENSLES_INCLUDE_DIRS})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
@ -32,7 +32,7 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MODULE freerdp
MODULES freerdp-codec freerdp-utils)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${OPENSL_ES_LIBRARIES})
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${OPENSLES_LIBRARIES})
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})

View File

@ -0,0 +1,420 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Input Redirection Virtual Channel - OpenSL ES implementation
*
* Copyright 2013 Armin Novak <armin.novak@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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/cmdline.h>
#include <freerdp/addin.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/channels/rdpsnd.h>
#include <SLES/OpenSLES.h>
#include "opensl_io.h"
#include "audin_main.h"
typedef struct _AudinOpenSLESDevice
{
IAudinDevice iface;
char* device_name;
OPENSL_STREAM *stream;
UINT32 frames_per_packet;
UINT32 target_rate;
UINT32 actual_rate;
UINT32 target_channels;
UINT32 actual_channels;
int bytes_per_channel;
int wformat;
int format;
int block_size;
FREERDP_DSP_CONTEXT* dsp_context;
HANDLE thread;
HANDLE stopEvent;
void* user_data;
} AudinOpenSLESDevice;
static BOOL audin_opensles_thread_receive(AudinOpenSLESDevice* opensles,
void* src, int count)
{
int frames;
int cframes;
int ret = 0;
int encoded_size;
BYTE* encoded_data;
int rbytes_per_frame;
int tbytes_per_frame;
rbytes_per_frame = opensles->actual_channels * opensles->bytes_per_channel;
tbytes_per_frame = opensles->target_channels * opensles->bytes_per_channel;
if ((opensles->target_rate == opensles->actual_rate) &&
(opensles->target_channels == opensles->actual_channels))
{
frames = size / rbytes_per_frame;
}
else
{
opensles->dsp_context->resample(opensles->dsp_context, src,
opensles->bytes_per_channel, opensles->actual_channels,
opensles->actual_rate, size / rbytes_per_frame,
opensles->target_channels, opensles->target_rate);
frames = opensles->dsp_context->resampled_frames;
DEBUG_DVC("resampled %d frames at %d to %d frames at %d",
size / rbytes_per_frame, opensles->actual_rate,
frames, opensles->target_rate);
size = frames * tbytes_per_frame;
src = opensles->dsp_context->resampled_buffer;
}
while (frames > 0)
{
if (WaitForSingleObject(opensles->stopEvent, 0) == WAIT_OBJECT_0)
break;
cframes = opensles->frames_per_packet - opensles->buffer_frames;
if (cframes > frames)
cframes = frames;
CopyMemory(opensles->buffer + opensles->buffer_frames * tbytes_per_frame,
src, cframes * tbytes_per_frame);
opensles->buffer_frames += cframes;
if (opensles->buffer_frames >= opensles->frames_per_packet)
{
if (opensles->wformat == WAVE_FORMAT_DVI_ADPCM)
{
opensles->dsp_context->encode_ima_adpcm(opensles->dsp_context,
opensles->buffer, opensles->buffer_frames * tbytes_per_frame,
opensles->target_channels, opensles->block_size);
encoded_data = opensles->dsp_context->adpcm_buffer;
encoded_size = opensles->dsp_context->adpcm_size;
DEBUG_DVC("encoded %d to %d",
opensles->buffer_frames * tbytes_per_frame, encoded_size);
}
else
{
encoded_data = opensles->buffer;
encoded_size = opensles->buffer_frames * tbytes_per_frame;
}
if (WaitForSingleObject(opensles->stopEvent, 0) == WAIT_OBJECT_0)
break;
else
ret = opensles->receive(encoded_data, encoded_size,
opensles->user_data);
opensles->buffer_frames = 0;
if (!ret)
break;
}
src += cframes * tbytes_per_frame;
frames -= cframes;
}
return (ret) ? TRUE : FALSE;
}
static void* audin_opensles_thread_func(void* arg)
{
float* buffer;
int rbytes_per_frame;
int tbytes_per_frame;
snd_pcm_t* capture_handle = NULL;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) arg;
DEBUG_SND("opensles=%p", opensles);
buffer = (BYTE*) calloc(sizeof(float), opensles->frames_per_packet);
ZeroMemory(buffer, opensles->frames_per_packet);
freerdp_dsp_context_reset_adpcm(opensles->dsp_context);
while (!(WaitForSingleObject(opensles->stopEvent, 0) == WAIT_OBJECT_0))
{
int rc = android_AudioIn(opensles->stream, buffer,
opensles->frames_per_packet);
if (rc < 0)
{
DEBUG_WARN("snd_pcm_readi (%s)", snd_strerror(error));
break;
}
if (!audin_opensles_thread_receive(opensles, buffer, rc * sizeof(float)))
break;
}
free(buffer);
DEBUG_DVC("thread shutdown.");
ExitThread(0);
return NULL;
}
static void audin_opensles_free(IAudinDevice* device)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p", device);
freerdp_dsp_context_free(opensles->dsp_context);
free(opensles->device_name);
free(opensles);
}
static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* format)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
SLResult rc;
DEBUG_DVC("device=%p, format=%p", device, 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))
{
return TRUE;
}
break;
case WAVE_FORMAT_DVI_ADPCM:
if ((format->nSamplesPerSec <= 48000) &&
(format->wBitsPerSample == 4) &&
(format->nChannels == 1 || format->nChannels == 2))
{
return TRUE;
}
break;
}
return FALSE;
}
static void audin_opensles_set_format(IAudinDevice* device,
audinFormat* format, UINT32 FramesPerPacket)
{
int bs;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p, format=%p, FramesPerPacket=%d",
device, format, FramesPerPacket);
opensles->target_rate = format->nSamplesPerSec;
opensles->actual_rate = format->nSamplesPerSec;
opensles->target_channels = format->nChannels;
opensles->actual_channels = format->nChannels;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 8:
opensles->format = SND_PCM_FORMAT_S8;
opensles->bytes_per_channel = 1;
break;
case 16:
opensles->format = SND_PCM_FORMAT_S16_LE;
opensles->bytes_per_channel = 2;
break;
}
break;
case WAVE_FORMAT_DVI_ADPCM:
opensles->format = SND_PCM_FORMAT_S16_LE;
opensles->bytes_per_channel = 2;
bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
opensles->frames_per_packet =
(opensles->frames_per_packet * format->nChannels * 2 /
bs + 1) * bs / (format->nChannels * 2);
DEBUG_DVC("aligned FramesPerPacket=%d",
opensles->frames_per_packet);
break;
}
opensles->wformat = format->wFormatTag;
opensles->block_size = format->nBlockAlign;
}
static int audin_opensles_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
{
int status = 0;
int rbytes_per_frame;
int tbytes_per_frame;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p, receive=%d, user_data=%p", device, receive, user_data);
opensles->stream = android_OpenAudioDevice(opensles->target_rate,
opensles->target_channels, 0, opensles->frames_per_packet);
opensles->receive = receive;
opensles->user_data = user_data;
rbytes_per_frame = opensles->actual_channels * opensles->bytes_per_channel;
tbytes_per_frame = opensles->target_channels * opensles->bytes_per_channel;
opensles->buffer =
(BYTE*) malloc(tbytes_per_frame * opensles->frames_per_packet);
ZeroMemory(opensles->buffer,
tbytes_per_frame * opensles->frames_per_packet);
opensles->buffer_frames = 0;
opensles->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
opensles->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) audin_opensles_thread_func,
opensles, 0, NULL);
}
static void audin_opensles_close(IAudinDevice* device)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p", device);
SetEvent(opensles->stopEvent);
WaitForSingleObject(opensles->thread, INFINITE);
CloseHandle(opensles->stopEvent);
CloseHandle(opensles->thread);
android_CloseAudioDevice(opensles->stream);
opensles->stopEvent = NULL;
opensles->thread = NULL;
opensles->receive = NULL;
opensles->user_data = NULL;
opsnsles->stream = NULL;
}
static const COMMAND_LINE_ARGUMENT_A audin_opensles_args[] =
{
{ "audio-dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "audio device name" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
static int audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
ADDIN_ARGV* args)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
DEBUG_DVC("device=%p, args=%p", device, args);
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
audin_opensles_args, flags, opensles, NULL, NULL);
if (status < 0)
return status;
arg = audin_opensles_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "audio-dev")
{
opensles->device_name = _strdup(arg->Value);
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return status;
}
#ifdef STATIC_CHANNELS
#define freerdp_audin_client_subsystem_entry \
opensles_freerdp_audin_client_subsystem_entry
#endif
int freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
AudinOpenSLESDevice* opensles;
DEBUG_DVC("pEntryPoints=%p", pEntryPoints);
opensles = (AudinOpenSLESDevice*) malloc(sizeof(AudinOpenSLESDevice));
ZeroMemory(opensles, sizeof(AudinOpenSLESDevice));
opensles->iface.Open = audin_opensles_open;
opensles->iface.FormatSupported = audin_opensles_format_supported;
opensles->iface.SetFormat = audin_opensles_set_format;
opensles->iface.Close = audin_opensles_close;
opensles->iface.Free = audin_opensles_free;
args = pEntryPoints->args;
audin_opensles_parse_addin_args(opensles, args);
if (!opensles->device_name)
opensles->device_name = _strdup("default");
opensles->frames_per_packet = 128;
opensles->target_rate = 22050;
opensles->actual_rate = 22050;
opensles->format = SND_PCM_FORMAT_S16_LE;
opensles->target_channels = 2;
opensles->actual_channels = 2;
opensles->bytes_per_channel = 2;
opensles->dsp_context = freerdp_dsp_context_new();
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
(IAudinDevice*) opensles);
return 0;
}

View File

@ -59,6 +59,6 @@ if(WITH_WINMM)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "winmm" "")
endif()
if(WITH_AUDIOTRACK)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "audiotrack" "")
if(WITH_OPENSLES)
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "opensles" "")
endif()

View File

@ -0,0 +1,43 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# Copyright 2013 Armin Novak <armin.novak@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.
define_channel_client_subsystem("rdpsnd" "opensles" "")
set(${MODULE_PREFIX}_SRCS
opensl_io.c
rdpsnd_opensles.c)
include_directories(..)
include_directories(${OPENSLES_INCLUDE_DIRS})
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "")
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE freerdp
MODULES freerdp-codec freerdp-utils
${OPENSLES_LIBRARIES})
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${OPENSLES_LIBRARIES})
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
if(NOT STATIC_CHANNELS)
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH})
endif()

View File

@ -0,0 +1,623 @@
/*
opensl_io.c:
Android OpenSL input/output module
Copyright (c) 2012, Victor Lazzarini
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include "rdpsnd_main.h"
#include "opensl_io.h"
#define CONV16BIT 32768
#define CONVMYFLT (1./32768.)
static void* createThreadLock(void);
static int waitThreadLock(void *lock);
static void notifyThreadLock(void *lock);
static void destroyThreadLock(void *lock);
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
// creates the OpenSL ES audio engine
static SLresult openSLCreateEngine(OPENSL_STREAM *p)
{
SLresult result;
// create engine
result = slCreateEngine(&(p->engineObject), 0, NULL, 0, NULL, NULL);
DEBUG_SND("engineObject=%p", p->engineObject);
if(result != SL_RESULT_SUCCESS) goto engine_end;
// realize the engine
result = (*p->engineObject)->Realize(p->engineObject, SL_BOOLEAN_FALSE);
DEBUG_SND("Realize=%d", result);
if(result != SL_RESULT_SUCCESS) goto engine_end;
// get the engine interface, which is needed in order to create other objects
result = (*p->engineObject)->GetInterface(p->engineObject, SL_IID_ENGINE, &(p->engineEngine));
DEBUG_SND("engineEngine=%p", p->engineEngine);
if(result != SL_RESULT_SUCCESS) goto engine_end;
engine_end:
return result;
}
// opens the OpenSL ES device for output
static SLresult openSLPlayOpen(OPENSL_STREAM *p)
{
SLresult result;
SLuint32 sr = p->sr;
SLuint32 channels = p->outchannels;
if(channels){
// configure audio source
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
switch(sr){
case 8000:
sr = SL_SAMPLINGRATE_8;
break;
case 11025:
sr = SL_SAMPLINGRATE_11_025;
break;
case 16000:
sr = SL_SAMPLINGRATE_16;
break;
case 22050:
sr = SL_SAMPLINGRATE_22_05;
break;
case 24000:
sr = SL_SAMPLINGRATE_24;
break;
case 32000:
sr = SL_SAMPLINGRATE_32;
break;
case 44100:
sr = SL_SAMPLINGRATE_44_1;
break;
case 48000:
sr = SL_SAMPLINGRATE_48;
break;
case 64000:
sr = SL_SAMPLINGRATE_64;
break;
case 88200:
sr = SL_SAMPLINGRATE_88_2;
break;
case 96000:
sr = SL_SAMPLINGRATE_96;
break;
case 192000:
sr = SL_SAMPLINGRATE_192;
break;
default:
return -1;
}
const SLInterfaceID ids[] = {SL_IID_VOLUME};
const SLboolean req[] = {SL_BOOLEAN_FALSE};
result = (*p->engineEngine)->CreateOutputMix(p->engineEngine, &(p->outputMixObject), 1, ids, req);
DEBUG_SND("engineEngine=%p", p->engineEngine);
assert(!result);
if(result != SL_RESULT_SUCCESS) goto end_openaudio;
// realize the output mix
result = (*p->outputMixObject)->Realize(p->outputMixObject, SL_BOOLEAN_FALSE);
DEBUG_SND("Realize=%d", result);
assert(!result);
if(result != SL_RESULT_SUCCESS) goto end_openaudio;
int speakers;
if(channels > 1)
speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
else speakers = SL_SPEAKER_FRONT_CENTER;
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,channels, sr,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
speakers, SL_BYTEORDER_LITTLEENDIAN};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, p->outputMixObject};
SLDataSink audioSnk = {&loc_outmix, NULL};
// create audio player
const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME};
const SLboolean req1[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*p->engineEngine)->CreateAudioPlayer(p->engineEngine,
&(p->bqPlayerObject), &audioSrc, &audioSnk, 2, ids1, req1);
DEBUG_SND("bqPlayerObject=%p", p->bqPlayerObject);
assert(!result);
if(result != SL_RESULT_SUCCESS) goto end_openaudio;
// realize the player
result = (*p->bqPlayerObject)->Realize(p->bqPlayerObject, SL_BOOLEAN_FALSE);
DEBUG_SND("Realize=%d", result);
assert(!result);
if(result != SL_RESULT_SUCCESS) goto end_openaudio;
// get the play interface
result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_PLAY, &(p->bqPlayerPlay));
DEBUG_SND("bqPlayerPlay=%p", p->bqPlayerPlay);
assert(!result);
if(result != SL_RESULT_SUCCESS) goto end_openaudio;
// get the volume interface
result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_VOLUME, &(p->bqPlayerVolume));
DEBUG_SND("bqPlayerVolume=%p", p->bqPlayerVolume);
assert(!result);
if(result != SL_RESULT_SUCCESS) goto end_openaudio;
// get the buffer queue interface
result = (*p->bqPlayerObject)->GetInterface(p->bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&(p->bqPlayerBufferQueue));
DEBUG_SND("bqPlayerBufferQueue=%p", p->bqPlayerBufferQueue);
assert(!result);
if(result != SL_RESULT_SUCCESS) goto end_openaudio;
// register callback on the buffer queue
result = (*p->bqPlayerBufferQueue)->RegisterCallback(p->bqPlayerBufferQueue, bqPlayerCallback, p);
DEBUG_SND("bqPlayerCallback=%p", p->bqPlayerCallback);
assert(!result);
if(result != SL_RESULT_SUCCESS) goto end_openaudio;
// set the player's state to playing
result = (*p->bqPlayerPlay)->SetPlayState(p->bqPlayerPlay, SL_PLAYSTATE_PLAYING);
DEBUG_SND("SetPlayState=%d", result);
assert(!result);
end_openaudio:
assert(!result);
return result;
}
return SL_RESULT_SUCCESS;
}
// Open the OpenSL ES device for input
static SLresult openSLRecOpen(OPENSL_STREAM *p){
SLresult result;
SLuint32 sr = p->sr;
SLuint32 channels = p->inchannels;
if(channels){
switch(sr){
case 8000:
sr = SL_SAMPLINGRATE_8;
break;
case 11025:
sr = SL_SAMPLINGRATE_11_025;
break;
case 16000:
sr = SL_SAMPLINGRATE_16;
break;
case 22050:
sr = SL_SAMPLINGRATE_22_05;
break;
case 24000:
sr = SL_SAMPLINGRATE_24;
break;
case 32000:
sr = SL_SAMPLINGRATE_32;
break;
case 44100:
sr = SL_SAMPLINGRATE_44_1;
break;
case 48000:
sr = SL_SAMPLINGRATE_48;
break;
case 64000:
sr = SL_SAMPLINGRATE_64;
break;
case 88200:
sr = SL_SAMPLINGRATE_88_2;
break;
case 96000:
sr = SL_SAMPLINGRATE_96;
break;
case 192000:
sr = SL_SAMPLINGRATE_192;
break;
default:
return -1;
}
// configure audio source
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSrc = {&loc_dev, NULL};
// configure audio sink
int speakers;
if(channels > 1)
speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
else speakers = SL_SPEAKER_FRONT_CENTER;
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channels, sr,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
speakers, SL_BYTEORDER_LITTLEENDIAN};
SLDataSink audioSnk = {&loc_bq, &format_pcm};
// create audio recorder
// (requires the RECORD_AUDIO permission)
const SLInterfaceID id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME};
const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*p->engineEngine)->CreateAudioRecorder(p->engineEngine,
&(p->recorderObject), &audioSrc, &audioSnk, 2, id, req);
DEBUG_SND("p->recorderObject=%p", p->recorderObject);
assert(!result);
if (SL_RESULT_SUCCESS != result) goto end_recopen;
// realize the audio recorder
result = (*p->recorderObject)->Realize(p->recorderObject, SL_BOOLEAN_FALSE);
DEBUG_SND("Realize=%d", result);
assert(!result);
if (SL_RESULT_SUCCESS != result) goto end_recopen;
// get the record interface
result = (*p->recorderObject)->GetInterface(p->recorderObject,
SL_IID_RECORD, &(p->recorderRecord));
DEBUG_SND("p->recorderRecord=%p", p->recorderRecord);
assert(!result);
if (SL_RESULT_SUCCESS != result) goto end_recopen;
// get the buffer queue interface
result = (*p->recorderObject)->GetInterface(p->recorderObject,
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&(p->recorderBufferQueue));
DEBUG_SND("p->recorderBufferQueue=%p", p->recorderBufferQueue);
assert(!result);
if (SL_RESULT_SUCCESS != result) goto end_recopen;
// get the record volume
result = (*p->recorderObject)->GetInterface(p->recorderObject,
SL_IID_VOLUME, &(p->recorderVolume));
DEBUG_SND("p->recorderVolume=%p", p->recorderVolume);
assert(!result);
if (SL_RESULT_SUCCESS != result) goto end_recopen;
// register callback on the buffer queue
result = (*p->recorderBufferQueue)->RegisterCallback(p->recorderBufferQueue,
bqRecorderCallback, p);
DEBUG_SND("p->recorderBufferQueue=%p", p->recorderBufferQueue);
assert(!result);
if (SL_RESULT_SUCCESS != result) goto end_recopen;
result = (*p->recorderRecord)->SetRecordState(p->recorderRecord,
SL_RECORDSTATE_RECORDING);
end_recopen:
return result;
}
else return SL_RESULT_SUCCESS;
}
// close the OpenSL IO and destroy the audio engine
static void openSLDestroyEngine(OPENSL_STREAM *p){
// destroy buffer queue audio player object, and invalidate all associated interfaces
if (p->bqPlayerObject != NULL) {
(*p->bqPlayerObject)->Destroy(p->bqPlayerObject);
p->bqPlayerObject = NULL;
p->bqPlayerVolume = NULL;
p->bqPlayerPlay = NULL;
p->bqPlayerBufferQueue = NULL;
p->bqPlayerEffectSend = NULL;
}
// destroy audio recorder object, and invalidate all associated interfaces
if (p->recorderObject != NULL) {
(*p->recorderObject)->Destroy(p->recorderObject);
p->recorderObject = NULL;
p->recorderRecord = NULL;
p->recorderVolume = NULL;
p->recorderBufferQueue = NULL;
}
// destroy output mix object, and invalidate all associated interfaces
if (p->outputMixObject != NULL) {
(*p->outputMixObject)->Destroy(p->outputMixObject);
p->outputMixObject = NULL;
}
// destroy engine object, and invalidate all associated interfaces
if (p->engineObject != NULL) {
(*p->engineObject)->Destroy(p->engineObject);
p->engineObject = NULL;
p->engineEngine = NULL;
}
}
// open the android audio device for input and/or output
OPENSL_STREAM *android_OpenAudioDevice(int sr, int inchannels, int outchannels, int bufferframes){
OPENSL_STREAM *p;
p = (OPENSL_STREAM *) calloc(sizeof(OPENSL_STREAM),1);
p->inchannels = inchannels;
p->outchannels = outchannels;
p->sr = sr;
p->inlock = createThreadLock();
p->outlock = createThreadLock();
if((p->outBufSamples = bufferframes*outchannels) != 0) {
if((p->outputBuffer[0] = (short *) calloc(p->outBufSamples, sizeof(short))) == NULL ||
(p->outputBuffer[1] = (short *) calloc(p->outBufSamples, sizeof(short))) == NULL) {
android_CloseAudioDevice(p);
return NULL;
}
}
if((p->inBufSamples = bufferframes*inchannels) != 0){
if((p->inputBuffer[0] = (short *) calloc(p->inBufSamples, sizeof(short))) == NULL ||
(p->inputBuffer[1] = (short *) calloc(p->inBufSamples, sizeof(short))) == NULL){
android_CloseAudioDevice(p);
return NULL;
}
}
p->currentInputIndex = 0;
p->currentOutputBuffer = 0;
p->currentInputIndex = p->inBufSamples;
p->currentInputBuffer = 0;
if(openSLCreateEngine(p) != SL_RESULT_SUCCESS) {
android_CloseAudioDevice(p);
return NULL;
}
if(openSLRecOpen(p) != SL_RESULT_SUCCESS) {
android_CloseAudioDevice(p);
return NULL;
}
if(openSLPlayOpen(p) != SL_RESULT_SUCCESS) {
android_CloseAudioDevice(p);
return NULL;
}
notifyThreadLock(p->outlock);
notifyThreadLock(p->inlock);
p->time = 0.;
return p;
}
// close the android audio device
void android_CloseAudioDevice(OPENSL_STREAM *p){
if (p == NULL)
return;
openSLDestroyEngine(p);
if (p->inlock != NULL) {
notifyThreadLock(p->inlock);
destroyThreadLock(p->inlock);
p->inlock = NULL;
}
if (p->outlock != NULL) {
notifyThreadLock(p->outlock);
destroyThreadLock(p->outlock);
p->inlock = NULL;
}
if (p->outputBuffer[0] != NULL) {
free(p->outputBuffer[0]);
p->outputBuffer[0] = NULL;
}
if (p->outputBuffer[1] != NULL) {
free(p->outputBuffer[1]);
p->outputBuffer[1] = NULL;
}
if (p->inputBuffer[0] != NULL) {
free(p->inputBuffer[0]);
p->inputBuffer[0] = NULL;
}
if (p->inputBuffer[1] != NULL) {
free(p->inputBuffer[1]);
p->inputBuffer[1] = NULL;
}
free(p);
}
// returns timestamp of the processed stream
double android_GetTimestamp(OPENSL_STREAM *p){
return p->time;
}
// this callback handler is called every time a buffer finishes recording
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
OPENSL_STREAM *p = (OPENSL_STREAM *) context;
notifyThreadLock(p->inlock);
}
// gets a buffer of size samples from the device
int android_AudioIn(OPENSL_STREAM *p,float *buffer,int size){
short *inBuffer;
int i, bufsamps = p->inBufSamples, index = p->currentInputIndex;
if(p == NULL || bufsamps == 0) return 0;
inBuffer = p->inputBuffer[p->currentInputBuffer];
for(i=0; i < size; i++){
if (index >= bufsamps) {
waitThreadLock(p->inlock);
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
inBuffer,bufsamps*sizeof(short));
p->currentInputBuffer = (p->currentInputBuffer ? 0 : 1);
index = 0;
inBuffer = p->inputBuffer[p->currentInputBuffer];
}
buffer[i] = (float) inBuffer[index++]*CONVMYFLT;
}
p->currentInputIndex = index;
if(p->outchannels == 0) p->time += (double) size/(p->sr*p->inchannels);
return i;
}
// this callback handler is called every time a buffer finishes playing
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
OPENSL_STREAM *p = (OPENSL_STREAM *) context;
notifyThreadLock(p->outlock);
}
// puts a buffer of size samples to the device
int android_AudioOut(OPENSL_STREAM *p, short *buffer,int size){
short *outBuffer;
int i, bufsamps = p->outBufSamples, index = p->currentOutputIndex;
assert(p);
assert(buffer);
assert(size > 0);
if(p == NULL || bufsamps == 0)
return 0;
outBuffer = p->outputBuffer[p->currentOutputBuffer];
for(i=0; i < size; i++){
outBuffer[index++] = buffer[i];
if (index >= p->outBufSamples) {
waitThreadLock(p->outlock);
(*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue,
outBuffer,bufsamps*sizeof(short));
p->currentOutputBuffer = (p->currentOutputBuffer ? 0 : 1);
index = 0;
outBuffer = p->outputBuffer[p->currentOutputBuffer];
}
}
p->currentOutputIndex = index;
p->time += (double) size/(p->sr*p->outchannels);
return i;
}
int android_GetInputVolume(OPENSL_STREAM *p){
SLmillibel level;
assert(p);
assert(p->recorderVolume);
SLresult rc = (*p->recorderVolume)->GetVolumeLevel(p->recorderVolume, &level);
assert(SL_RESULT_SUCCESS == rc);
return level;
}
void android_SetInputVolume(OPENSL_STREAM *p, int level){
SLresult rc = (*p->recorderVolume)->SetVolumeLevel(p->recorderVolume, level);
assert(SL_RESULT_SUCCESS == rc);
}
int android_GetOutputVolume(OPENSL_STREAM *p){
SLmillibel level;
assert(p);
assert(p->bqPlayerVolume);
SLresult rc = (*p->bqPlayerVolume)->GetVolumeLevel(p->bqPlayerVolume, &level);
assert(SL_RESULT_SUCCESS == rc);
return level;
}
void android_SetOutputVolume(OPENSL_STREAM *p, int level){
SLresult rc = (*p->bqPlayerVolume)->SetVolumeLevel(p->bqPlayerVolume, level);
assert(SL_RESULT_SUCCESS == rc);
}
//----------------------------------------------------------------------
// thread Locks
// to ensure synchronisation between callbacks and processing code
void* createThreadLock(void)
{
threadLock *p;
p = (threadLock*) malloc(sizeof(threadLock));
if (p == NULL)
return NULL;
memset(p, 0, sizeof(threadLock));
if (pthread_mutex_init(&(p->m), (pthread_mutexattr_t*) NULL) != 0) {
free((void*) p);
return NULL;
}
if (pthread_cond_init(&(p->c), (pthread_condattr_t*) NULL) != 0) {
pthread_mutex_destroy(&(p->m));
free((void*) p);
return NULL;
}
p->s = (unsigned char) 1;
return p;
}
int waitThreadLock(void *lock)
{
threadLock *p;
int retval = 0;
p = (threadLock*) lock;
pthread_mutex_lock(&(p->m));
while (!p->s) {
pthread_cond_wait(&(p->c), &(p->m));
}
p->s = (unsigned char) 0;
pthread_mutex_unlock(&(p->m));
return retval;
}
void notifyThreadLock(void *lock)
{
threadLock *p;
p = (threadLock*) lock;
pthread_mutex_lock(&(p->m));
p->s = (unsigned char) 1;
pthread_cond_signal(&(p->c));
pthread_mutex_unlock(&(p->m));
}
void destroyThreadLock(void *lock)
{
threadLock *p;
p = (threadLock*) lock;
if (p == NULL)
return;
notifyThreadLock(p);
pthread_cond_destroy(&(p->c));
pthread_mutex_destroy(&(p->m));
free(p);
}

View File

@ -0,0 +1,138 @@
/*
opensl_io.c:
Android OpenSL input/output module header
Copyright (c) 2012, Victor Lazzarini
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef OPENSL_IO
#define OPENSL_IO
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <pthread.h>
#include <stdlib.h>
typedef struct threadLock_{
pthread_mutex_t m;
pthread_cond_t c;
unsigned char s;
} threadLock;
#ifdef __cplusplus
extern "C" {
#endif
typedef struct opensl_stream {
// engine interfaces
SLObjectItf engineObject;
SLEngineItf engineEngine;
// output mix interfaces
SLObjectItf outputMixObject;
// buffer queue player interfaces
SLObjectItf bqPlayerObject;
SLPlayItf bqPlayerPlay;
SLVolumeItf bqPlayerVolume;
SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
SLEffectSendItf bqPlayerEffectSend;
// recorder interfaces
SLObjectItf recorderObject;
SLRecordItf recorderRecord;
SLVolumeItf recorderVolume;
SLAndroidSimpleBufferQueueItf recorderBufferQueue;
// buffer indexes
int currentInputIndex;
int currentOutputIndex;
// current buffer half (0, 1)
int currentOutputBuffer;
int currentInputBuffer;
// buffers
short *outputBuffer[2];
short *inputBuffer[2];
// size of buffers
int outBufSamples;
int inBufSamples;
// locks
void* inlock;
void* outlock;
double time;
int inchannels;
int outchannels;
int sr;
} OPENSL_STREAM;
/*
Open the audio device with a given sampling rate (sr), input and output channels and IO buffer size
in frames. Returns a handle to the OpenSL stream
*/
OPENSL_STREAM* android_OpenAudioDevice(int sr, int inchannels, int outchannels, int bufferframes);
/*
Close the audio device
*/
void android_CloseAudioDevice(OPENSL_STREAM *p);
/*
Read a buffer from the OpenSL stream *p, of size samples. Returns the number of samples read.
*/
int android_AudioIn(OPENSL_STREAM *p, float *buffer,int size);
/*
Write a buffer to the OpenSL stream *p, of size samples. Returns the number of samples written.
*/
int android_AudioOut(OPENSL_STREAM *p, short *buffer,int size);
/*
Get the current IO block time in seconds
*/
double android_GetTimestamp(OPENSL_STREAM *p);
/*
* Get the current input volume level.
*/
int android_GetInputVolume(OPENSL_STREAM *p);
/*
* Set the volume input level.
*/
void android_SetInputVolume(OPENSL_STREAM *p, int level);
/*
* Get the current output volume level.
*/
int android_GetOutputVolume(OPENSL_STREAM *p);
/*
* Set the volume output level.
*/
void android_SetOutputVolume(OPENSL_STREAM *p, int level);
#ifdef __cplusplus
};
#endif
#endif // #ifndef OPENSL_IO

View File

@ -0,0 +1,382 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Audio Output Virtual Channel
*
* Copyright 2013 Armin Novak <armin.novak@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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <winpr/sysinfo.h>
#include <winpr/collections.h>
#include <freerdp/types.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/utils/debug.h>
#include "opensl_io.h"
#include "rdpsnd_main.h"
typedef struct rdpsnd_opensles_plugin rdpsndopenslesPlugin;
struct rdpsnd_opensles_plugin
{
rdpsndDevicePlugin device;
int latency;
int wformat;
int block_size;
char* device_name;
OPENSL_STREAM *stream;
UINT32 volume;
UINT32 rate;
UINT32 channels;
int format;
FREERDP_DSP_CONTEXT* dsp_context;
};
static void rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device,
UINT32 volume);
static int rdpsnd_opensles_set_params(rdpsndopenslesPlugin* opensles)
{
DEBUG_SND("opensles=%p", opensles);
return 0;
}
static void rdpsnd_opensles_set_format(rdpsndDevicePlugin* device,
AUDIO_FORMAT* format, int latency)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p format=%p, latency=%d", opensles, format, latency);
if (format)
{
opensles->rate = format->nSamplesPerSec;
opensles->channels = format->nChannels;
switch (format->wFormatTag)
{
case WAVE_FORMAT_PCM:
switch (format->wBitsPerSample)
{
case 4:
opensles->format = WAVE_FORMAT_ADPCM;
break;
case 8:
opensles->format = WAVE_FORMAT_PCM;
break;
case 16:
opensles->format = WAVE_FORMAT_ADPCM;
break;
}
break;
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_DVI_ADPCM:
opensles->format = WAVE_FORMAT_ADPCM;
break;
}
opensles->wformat = format->wFormatTag;
opensles->block_size = format->nBlockAlign;
}
opensles->latency = latency;
rdpsnd_opensles_set_params(opensles);
}
static void rdpsnd_opensles_open(rdpsndDevicePlugin* device,
AUDIO_FORMAT* format, int latency)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p format=%p, latency=%d", opensles, format, latency);
if (opensles->stream)
return;
opensles->stream = android_OpenAudioDevice(
opensles->rate, 0, opensles->channels, opensles->rate * 100);
if (!opensles->stream)
DEBUG_WARN("android_OpenAudioDevice failed");
else
rdpsnd_opensles_set_volume(device, opensles->volume);
}
static void rdpsnd_opensles_close(rdpsndDevicePlugin* device)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p", opensles);
if (!opensles->stream)
return;
android_CloseAudioDevice(opensles->stream);
}
static void rdpsnd_opensles_free(rdpsndDevicePlugin* device)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p", opensles);
free(opensles->device_name);
freerdp_dsp_context_free(opensles->dsp_context);
free(opensles);
}
static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device,
AUDIO_FORMAT* format)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p, format=%p", opensles, 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))
{
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:
break;
case WAVE_FORMAT_MULAW:
break;
case WAVE_FORMAT_GSM610:
break;
}
return FALSE;
}
static UINT32 rdpsnd_opensles_get_volume(rdpsndDevicePlugin* device)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p", opensles);
if (opensles->stream)
return android_GetOutputVolume(opensles->stream);
else
return opensles->volume;
}
static void rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device,
UINT32 value)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p, value=%d", opensles, value);
opensles->volume = value;
if (opensles->stream)
android_SetOutputVolume(opensles->stream, value);
}
static void rdpsnd_opensles_play(rdpsndDevicePlugin* device,
BYTE *data, int size)
{
BYTE* src;
int len;
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p, data=%p, size=%d", opensles, data, size);
assert(opensles);
if (!opensles->stream)
return;
if (opensles->format == WAVE_FORMAT_ADPCM)
{
opensles->dsp_context->decode_ms_adpcm(opensles->dsp_context,
data, size, opensles->channels, opensles->block_size);
size = opensles->dsp_context->adpcm_size;
src = opensles->dsp_context->adpcm_buffer;
}
else if (opensles->format == WAVE_FORMAT_DVI_ADPCM)
{
opensles->dsp_context->decode_ima_adpcm(opensles->dsp_context,
data, size, opensles->channels, opensles->block_size);
size = opensles->dsp_context->adpcm_size;
src = opensles->dsp_context->adpcm_buffer;
}
else
{
src = data;
}
len = size;
while (size > 0)
{
int ret;
if (len < 0)
break;
if (len > size)
len = size;
DEBUG_SND("len=%d, src=%p", len, src);
ret = android_AudioOut(opensles->stream, (short*)src, len / 2);
if (ret < 0)
{
DEBUG_WARN("android_AudioOut failed (%d)", ret);
break;
}
DEBUG_SND("foobar XXXXXXXXXXXX opensles=%p, data=%p, size=%d", opensles, data, size);
src += len;
size -= len;
}
}
static void rdpsnd_opensles_start(rdpsndDevicePlugin* device)
{
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p", opensles);
}
static COMMAND_LINE_ARGUMENT_A rdpsnd_opensles_args[] =
{
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
NULL, NULL, -1, NULL, "device" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
static int rdpsnd_opensles_parse_addin_args(rdpsndDevicePlugin* device,
ADDIN_ARGV* args)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
DEBUG_SND("opensles=%p, args=%p", opensles, args);
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
rdpsnd_opensles_args, flags, opensles, NULL, NULL);
if (status < 0)
return status;
arg = rdpsnd_opensles_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "dev")
{
opensles->device_name = _strdup(arg->Value);
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return status;
}
#ifdef STATIC_CHANNELS
#define freerdp_rdpsnd_client_subsystem_entry \
opensles_freerdp_rdpsnd_client_subsystem_entry
#endif
int freerdp_rdpsnd_client_subsystem_entry(
PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
rdpsndopenslesPlugin* opensles;
DEBUG_SND("pEntryPoints=%p", pEntryPoints);
opensles = (rdpsndopenslesPlugin*) malloc(sizeof(rdpsndopenslesPlugin));
ZeroMemory(opensles, sizeof(rdpsndopenslesPlugin));
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;
opensles->device.Play = rdpsnd_opensles_play;
opensles->device.Close = rdpsnd_opensles_close;
opensles->device.Free = rdpsnd_opensles_free;
args = pEntryPoints->args;
rdpsnd_opensles_parse_addin_args((rdpsndDevicePlugin*) opensles, args);
if (!opensles->device_name)
opensles->device_name = _strdup("default");
opensles->rate = 22050;
opensles->channels = 2;
opensles->format = WAVE_FORMAT_ADPCM;
opensles->dsp_context = freerdp_dsp_context_new();
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd,
(rdpsndDevicePlugin*) opensles);
DEBUG_SND("success");
return 0;
}

View File

@ -647,7 +647,9 @@ static void rdpsnd_process_connect(rdpSvcPlugin* plugin)
rdpsnd->latency = -1;
rdpsnd->queue = MessageQueue_New();
rdpsnd->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread, (void*) plugin, 0, NULL);
rdpsnd->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) rdpsnd_schedule_thread,
(void*) plugin, 0, NULL);
args = (ADDIN_ARGV*) plugin->channel_entry_points.pExtendedData;
@ -662,40 +664,59 @@ static void rdpsnd_process_connect(rdpSvcPlugin* plugin)
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
}
#if defined(WITH_IOSAUDIO)
if (!rdpsnd->device)
{
rdpsnd_set_subsystem(rdpsnd, "ios");
rdpsnd_set_device_name(rdpsnd, "");
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
}
#endif
#if defined(WITH_OPENSLES)
if (!rdpsnd->device)
{
rdpsnd_set_subsystem(rdpsnd, "opensles");
rdpsnd_set_device_name(rdpsnd, "");
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
}
#endif
#if defined(WITH_PULSE)
if (!rdpsnd->device)
{
rdpsnd_set_subsystem(rdpsnd, "pulse");
rdpsnd_set_device_name(rdpsnd, "");
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
}
#endif
#if defined(WITH_ALSA)
if (!rdpsnd->device)
{
rdpsnd_set_subsystem(rdpsnd, "alsa");
rdpsnd_set_device_name(rdpsnd, "default");
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
}
#endif
#if defined(WITH_MACAUDIO)
if (!rdpsnd->device)
{
rdpsnd_set_subsystem(rdpsnd, "macaudio");
rdpsnd_set_device_name(rdpsnd, "default");
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
}
#endif
#if defined(WITH_WINMM)
if (!rdpsnd->device)
{
rdpsnd_set_subsystem(rdpsnd, "winmm");
rdpsnd_set_device_name(rdpsnd, "");
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
}
if (!rdpsnd->device)
{
rdpsnd_set_subsystem(rdpsnd, "audiotrack");
rdpsnd_set_device_name(rdpsnd, "");
rdpsnd_load_device_plugin(rdpsnd, rdpsnd->subsystem, args);
}
#endif
if (!rdpsnd->device)
{

View File

@ -22,6 +22,12 @@
#include <freerdp/channels/rdpsnd.h>
#if defined(WITH_DEBUG_SND)
#define DEBUG_SND(fmt, ...) DEBUG_CLASS("rdpsnd", fmt, ## __VA_ARGS__)
#else
#define DEBUG_SND(fmt, ...) do { } while (0)
#endif
struct _RDPSND_WAVE
{
BYTE* data;