Fixed audio recording with opensles.
This commit is contained in:
parent
2e1bf90bd9
commit
47f8804073
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user