Fixed audio recording with opensles.

This commit is contained in:
Armin Novak 2018-10-04 12:21:28 +02:00
parent 2e1bf90bd9
commit 47f8804073
3 changed files with 126 additions and 268 deletions

View File

@ -29,8 +29,6 @@
#include <string.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/cmdline.h>
#include <freerdp/addin.h>
@ -56,85 +54,29 @@ typedef struct _AudinOpenSLESDevice
AudinReceive receive;
HANDLE thread;
HANDLE stopEvent;
void* user_data;
rdpContext* rdpcontext;
wLog* log;
} AudinOpenSLESDevice;
static DWORD WINAPI audin_opensles_thread_func(LPVOID arg)
static UINT audin_opensles_close(IAudinDevice* device);
static void audin_receive(void* context, const void* data, size_t size)
{
union
UINT error;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) context;
if (!opensles || !data)
{
void* v;
short* s;
BYTE* b;
} buffer;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) arg;
const size_t raw_size = opensles->frames_per_packet * opensles->bytes_per_channel;
int rc = CHANNEL_RC_OK;
UINT error = CHANNEL_RC_OK;
DWORD status;
WLog_Print(opensles->log, WLOG_DEBUG, "opensles=%p", (void*) opensles);
assert(opensles);
assert(opensles->frames_per_packet > 0);
assert(opensles->stopEvent);
assert(opensles->stream);
buffer.v = calloc(1, raw_size);
if (!buffer.v)
{
error = CHANNEL_RC_NO_MEMORY;
WLog_Print(opensles->log, WLOG_ERROR, "calloc failed!");
if (opensles->rdpcontext)
setChannelError(opensles->rdpcontext, CHANNEL_RC_NO_MEMORY,
"audin_opensles_thread_func reported an error");
goto out;
WLog_ERR(TAG, "[%s] Invalid arguments context=%p, data=%p", __FUNCTION__, opensles, data);
return;
}
while (1)
{
status = WaitForSingleObject(opensles->stopEvent, 0);
if (status == WAIT_FAILED)
{
error = GetLastError();
WLog_Print(opensles->log, WLOG_ERROR, "WaitForSingleObject failed with error %"PRIu32"!", error);
break;
}
if (status == WAIT_OBJECT_0)
break;
rc = android_RecIn(opensles->stream, buffer.s, raw_size);
if (rc < 0)
{
WLog_Print(opensles->log, WLOG_ERROR, "android_RecIn %d", rc);
continue;
}
error = opensles->receive(&opensles->format,
buffer.v, raw_size, opensles->user_data);
if (error)
break;
}
free(buffer.v);
out:
WLog_Print(opensles->log, WLOG_DEBUG, "thread shutdown.");
error = opensles->receive(&opensles->format, data, size, opensles->user_data);
if (error && opensles->rdpcontext)
setChannelError(opensles->rdpcontext, error, "audin_opensles_thread_func reported an error");
ExitThread(error);
return error;
setChannelError(opensles->rdpcontext, error, "audin_receive reported an error");
}
/**
@ -156,8 +98,6 @@ static UINT audin_opensles_free(IAudinDevice* device)
if (!opensles)
return CHANNEL_RC_OK;
assert(opensles);
assert(!opensles->stream);
free(opensles->device_name);
free(opensles);
return CHANNEL_RC_OK;
@ -274,46 +214,26 @@ static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive,
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p, receive=%p, user_data=%p", (void*) device,
(void*) receive,
(void*) user_data);
assert(opensles);
/* The function may have been called out of order,
* ignore duplicate open requests. */
if (opensles->stream)
return CHANNEL_RC_OK;
goto error_out;
if (!(opensles->stream = android_OpenRecDevice(
opensles->device_name,
opensles, audin_receive,
opensles->format.nSamplesPerSec,
opensles->format.nChannels,
opensles->frames_per_packet,
opensles->format.wBitsPerSample)))
{
WLog_Print(opensles->log, WLOG_ERROR, "android_OpenRecDevice failed!");
return ERROR_INTERNAL_ERROR;
goto error_out;
}
opensles->receive = receive;
opensles->user_data = user_data;
if (!(opensles->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_Print(opensles->log, WLOG_ERROR, "CreateEvent failed!");
goto error_out;
}
if (!(opensles->thread = CreateThread(NULL, 0,
audin_opensles_thread_func, opensles, 0, NULL)))
{
WLog_Print(opensles->log, WLOG_ERROR, "CreateThread failed!");
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
android_CloseRecDevice(opensles->stream);
opensles->stream = NULL;
CloseHandle(opensles->stopEvent);
opensles->stopEvent = NULL;
audin_opensles_close(opensles);
return ERROR_INTERNAL_ERROR;
}
@ -322,38 +242,15 @@ error_out:
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT audin_opensles_close(IAudinDevice* device)
UINT audin_opensles_close(IAudinDevice* device)
{
UINT error;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
if (!opensles)
return ERROR_INVALID_PARAMETER;
WLog_Print(opensles->log, WLOG_DEBUG, "device=%p", (void*) device);
assert(opensles);
/* The function may have been called out of order,
* ignore duplicate requests. */
if (!opensles->stopEvent)
{
WLog_Print(opensles->log, WLOG_ERROR, "[ERROR] function called without matching open.");
return ERROR_REQUEST_OUT_OF_SEQUENCE;
}
assert(opensles->stopEvent);
assert(opensles->thread);
assert(opensles->stream);
SetEvent(opensles->stopEvent);
if (WaitForSingleObject(opensles->thread, INFINITE) == WAIT_FAILED)
{
error = GetLastError();
WLog_Print(opensles->log, WLOG_ERROR, "WaitForSingleObject failed with error %"PRIu32"", error);
return error;
}
CloseHandle(opensles->stopEvent);
CloseHandle(opensles->thread);
android_CloseRecDevice(opensles->stream);
opensles->stopEvent = NULL;
opensles->thread = NULL;
opensles->receive = NULL;
opensles->user_data = NULL;
opensles->stream = NULL;

View File

@ -34,6 +34,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define CONV16BIT 32768
#define CONVMYFLT (1./32768.)
typedef struct
{
size_t size;
void* data;
} queue_element;
struct opensl_stream
{
// engine interfaces
SLObjectItf engineObject;
SLEngineItf engineEngine;
// device interfaces
SLDeviceVolumeItf deviceVolume;
// recorder interfaces
SLObjectItf recorderObject;
SLRecordItf recorderRecord;
SLAndroidSimpleBufferQueueItf recorderBufferQueue;
unsigned int inchannels;
unsigned int sr;
unsigned int buffersize;
unsigned int bits_per_sample;
queue_element* prep;
queue_element* next;
void* context;
opensl_receive_t receive;
};
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
// creates the OpenSL ES audio engine
@ -236,42 +269,84 @@ static void openSLDestroyEngine(OPENSL_STREAM* p)
}
}
static queue_element* opensles_queue_element_new(size_t size)
{
queue_element* q = calloc(1, sizeof(queue_element));
if (!q)
goto fail;
q->size = size;
q->data = malloc(size);
if (!q->data)
goto fail;
return q;
fail:
free(q);
return NULL;
}
static void opensles_queue_element_free(void* obj)
{
queue_element* e = (queue_element*)obj;
if (e)
free(e->data);
free(e);
}
// open the android audio device for input
OPENSL_STREAM* android_OpenRecDevice(char* name, int sr, int inchannels,
OPENSL_STREAM* android_OpenRecDevice(void* context, opensl_receive_t receive,
int sr,
int inchannels,
int bufferframes, int bits_per_sample)
{
OPENSL_STREAM* p;
if (!context || !receive)
return NULL;
p = (OPENSL_STREAM*) calloc(1, sizeof(OPENSL_STREAM));
if (!p)
return NULL;
p->context = context;
p->receive = receive;
p->inchannels = inchannels;
p->sr = sr;
p->queue = Queue_New(TRUE, -1, -1);
p->buffersize = bufferframes;
p->bits_per_sample = bits_per_sample;
if ((p->bits_per_sample != 8) && (p->bits_per_sample != 16))
{
android_CloseRecDevice(p);
return NULL;
}
goto fail;
if (openSLCreateEngine(p) != SL_RESULT_SUCCESS)
{
android_CloseRecDevice(p);
return NULL;
}
goto fail;
if (openSLRecOpen(p) != SL_RESULT_SUCCESS)
{
android_CloseRecDevice(p);
return NULL;
}
goto fail;
/* Create receive buffers, prepare them and start recording */
p->prep = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
p->next = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
if (!p->prep || !p->next)
goto fail;
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
p->next->data, p->next->size);
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
p->prep->data, p->prep->size);
(*p->recorderRecord)->SetRecordState(p->recorderRecord,
SL_RECORDSTATE_RECORDING);
return p;
fail:
android_CloseRecDevice(p);
return NULL;
}
// close the android audio device
@ -280,30 +355,8 @@ void android_CloseRecDevice(OPENSL_STREAM* p)
if (p == NULL)
return;
if (p->queue)
{
while (Queue_Count(p->queue) > 0)
{
queue_element* e = Queue_Dequeue(p->queue);
free(e->data);
free(e);
}
Queue_Free(p->queue);
}
if (p->next)
{
free(p->next->data);
free(p->next);
}
if (p->prep)
{
free(p->prep->data);
free(p->prep);
}
opensles_queue_element_free(p->next);
opensles_queue_element_free(p->prep);
openSLDestroyEngine(p);
free(p);
}
@ -311,86 +364,25 @@ void android_CloseRecDevice(OPENSL_STREAM* p)
// this callback handler is called every time a buffer finishes recording
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{
queue_element* e;
OPENSL_STREAM* p = (OPENSL_STREAM*) context;
assert(p);
assert(p->next);
assert(p->prep);
assert(p->queue);
e = calloc(1, sizeof(queue_element));
queue_element* e;
if (!p)
return;
e = p->next;
if (!e)
return;
e->data = calloc(p->buffersize, p->bits_per_sample / 8);
if (!p->context || !p->receive)
WLog_WARN(TAG, "Missing receive callback=%p, context=%p", p->receive, p->context);
else
p->receive(p->context, e->data, e->size);
if (!e->data)
{
free(e);
return;
}
e->size = p->buffersize * p->bits_per_sample / 8;
Queue_Enqueue(p->queue, p->next);
p->next = p->prep;
p->prep = e;
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
e->data, e->size);
}
// gets a buffer of size samples from the device
int android_RecIn(OPENSL_STREAM* p, short* buffer, int size)
{
queue_element* e;
int rc;
DWORD status;
assert(p);
assert(buffer);
assert(size > 0);
/* Initial trigger for the queue. */
if (!p->prep)
{
p->prep = calloc(1, sizeof(queue_element));
p->prep->data = calloc(p->buffersize, p->bits_per_sample / 8);
p->prep->size = p->buffersize * p->bits_per_sample / 8;
p->next = calloc(1, sizeof(queue_element));
p->next->data = calloc(p->buffersize, p->bits_per_sample / 8);
p->next->size = p->buffersize * p->bits_per_sample / 8;
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
p->next->data, p->next->size);
(*p->recorderBufferQueue)->Enqueue(p->recorderBufferQueue,
p->prep->data, p->prep->size);
(*p->recorderRecord)->SetRecordState(p->recorderRecord,
SL_RECORDSTATE_RECORDING);
}
/* Wait for queue to be filled... */
if (!Queue_Count(p->queue))
{
status = WaitForSingleObject(p->queue->event, INFINITE);
if (status == WAIT_FAILED)
{
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", GetLastError());
return -1;
}
}
e = Queue_Dequeue(p->queue);
if (!e)
{
WLog_ERR(TAG, "[ERROR] got e=NULL from queue");
return -1;
}
rc = (e->size < size) ? e->size : size;
assert(size == e->size);
assert(p->buffersize * p->bits_per_sample / 8 == size);
memcpy(buffer, e->data, rc);
free(e->data);
free(e);
return rc;
}

View File

@ -33,9 +33,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <winpr/synch.h>
#include <winpr/collections.h>
#include <freerdp/api.h>
#include <stdlib.h>
@ -44,51 +41,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
extern "C" {
#endif
typedef struct
{
size_t size;
void* data;
} queue_element;
typedef struct opensl_stream OPENSL_STREAM;
typedef struct opensl_stream
{
// engine interfaces
SLObjectItf engineObject;
SLEngineItf engineEngine;
// device interfaces
SLDeviceVolumeItf deviceVolume;
// recorder interfaces
SLObjectItf recorderObject;
SLRecordItf recorderRecord;
SLAndroidSimpleBufferQueueItf recorderBufferQueue;
unsigned int inchannels;
unsigned int sr;
unsigned int buffersize;
unsigned int bits_per_sample;
wQueue* queue;
queue_element* prep;
queue_element* next;
} OPENSL_STREAM;
typedef void (*opensl_receive_t)(void* context, const void* data, size_t size);
/*
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
*/
FREERDP_LOCAL OPENSL_STREAM* android_OpenRecDevice(char* name, int sr,
FREERDP_LOCAL OPENSL_STREAM* android_OpenRecDevice(void* context,
opensl_receive_t receive, int sr,
int inchannels,
int bufferframes, int bits_per_sample);
/*
Close the audio device
*/
FREERDP_LOCAL void android_CloseRecDevice(OPENSL_STREAM* p);
/*
Read a buffer from the OpenSL stream *p, of size samples. Returns the number of samples read.
*/
FREERDP_LOCAL int android_RecIn(OPENSL_STREAM* p, short* buffer, int size);
#ifdef __cplusplus
};
#endif