|
|
@ -45,6 +45,12 @@ |
|
|
|
|
|
|
|
#include "module-sles-sink-symdef.h" |
|
|
|
|
|
|
|
//Only certain interfaces are supported by the fast mixer. These are:
|
|
|
|
//SL_IID_ANDROIDSIMPLEBUFFERQUEUE
|
|
|
|
//SL_IID_VOLUME
|
|
|
|
//SL_IID_MUTESOLO
|
|
|
|
#define USE_ANDROID_SIMPLE_BUFFER_QUEUE |
|
|
|
|
|
|
|
#ifdef USE_ANDROID_SIMPLE_BUFFER_QUEUE |
|
|
|
#include <SLES/OpenSLES_Android.h> |
|
|
|
#define DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE |
|
|
@ -69,10 +75,6 @@ |
|
|
|
else fprintf(stderr, "error %d at %s:%d\n", (int) r, __FILE__, __LINE__); \ |
|
|
|
} \ |
|
|
|
} while (0) |
|
|
|
typedef struct { |
|
|
|
short left; |
|
|
|
short right; |
|
|
|
} frame_t; |
|
|
|
|
|
|
|
PA_MODULE_AUTHOR("Lennart Poettering, Nathan Martynov"); |
|
|
|
PA_MODULE_DESCRIPTION("Android OpenSL ES sink"); |
|
|
@ -86,6 +88,11 @@ PA_MODULE_USAGE( |
|
|
|
#define DEFAULT_SINK_NAME "OpenSL ES sink" |
|
|
|
#define BLOCK_USEC (PA_USEC_PER_SEC * 2) |
|
|
|
|
|
|
|
typedef struct pa_memblock_queue_t { |
|
|
|
pa_memblock *memblock; |
|
|
|
struct pa_memblock_queue_t* next; |
|
|
|
} pa_memblock_queue; |
|
|
|
|
|
|
|
struct userdata { |
|
|
|
pa_core *core; |
|
|
|
pa_module *module; |
|
|
@ -110,6 +117,9 @@ struct userdata { |
|
|
|
SLObjectItf bqPlayerObject; |
|
|
|
SLPlayItf bqPlayerPlay; |
|
|
|
BufferQueueItf bqPlayerBufferQueue; |
|
|
|
|
|
|
|
pa_memblock_queue* current; |
|
|
|
pa_memblock_queue* last; |
|
|
|
}; |
|
|
|
|
|
|
|
static const char* const valid_modargs[] = { |
|
|
@ -168,6 +178,17 @@ static void sink_update_requested_latency_cb(pa_sink *s) { |
|
|
|
pa_sink_set_max_request_within_thread(s, nbytes); |
|
|
|
} |
|
|
|
|
|
|
|
static void pa_sles_callback(BufferQueueItf bq, void *context){ |
|
|
|
struct userdata* s = (struct userdata*) context; |
|
|
|
pa_memblock_queue* next; |
|
|
|
if (s->current != NULL){ |
|
|
|
if (s->current->memblock != NULL) pa_memblock_unref(s->current->memblock); |
|
|
|
next = s->current->next; |
|
|
|
free(s->current); |
|
|
|
s->current = next; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static int pa_init_sles_player(struct userdata *s, SLint32 sl_rate) |
|
|
|
{ |
|
|
|
if (s == NULL) return -1; |
|
|
@ -193,7 +214,7 @@ static int pa_init_sles_player(struct userdata *s, SLint32 sl_rate) |
|
|
|
locator_bufferqueue.locatorType = DATALOCATOR_BUFFERQUEUE; |
|
|
|
locator_bufferqueue.numBuffers = 50; |
|
|
|
|
|
|
|
if (sl_rate < 0) { |
|
|
|
if (sl_rate < SL_SAMPLINGRATE_8 || sl_rate > SL_SAMPLINGRATE_192) { |
|
|
|
pa_log("Incompatible sample rate"); |
|
|
|
return -1; |
|
|
|
} |
|
|
@ -223,7 +244,10 @@ static int pa_init_sles_player(struct userdata *s, SLint32 sl_rate) |
|
|
|
result = (*s->bqPlayerObject)->GetInterface(s->bqPlayerObject, SL_IID_PLAY, &s->bqPlayerPlay); checkResult(result); |
|
|
|
result = (*s->bqPlayerObject)->GetInterface(s->bqPlayerObject, IID_BUFFERQUEUE_USED, &s->bqPlayerBufferQueue); checkResult(result); |
|
|
|
|
|
|
|
result = (*s->bqPlayerBufferQueue)->RegisterCallback(s->bqPlayerBufferQueue, pa_sles_callback, s); checkResult(result); |
|
|
|
|
|
|
|
result = (*s->bqPlayerPlay)->SetPlayState(s->bqPlayerPlay, SL_PLAYSTATE_PLAYING); checkResult(result); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
@ -236,6 +260,7 @@ static void pa_destroy_sles_player(struct userdata *s){ |
|
|
|
} |
|
|
|
|
|
|
|
static void process_render(struct userdata *u, pa_usec_t now) { |
|
|
|
pa_memblock_queue* current_block; |
|
|
|
size_t ate = 0; |
|
|
|
|
|
|
|
pa_assert(u); |
|
|
@ -256,6 +281,20 @@ static void process_render(struct userdata *u, pa_usec_t now) { |
|
|
|
|
|
|
|
u->timestamp += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec); |
|
|
|
ate += u->memchunk.length; |
|
|
|
|
|
|
|
current_block = malloc(sizeof(pa_memblock_queue)); |
|
|
|
memset(current_block, 0, sizeof(pa_memblock_queue)); |
|
|
|
|
|
|
|
current_block->memblock = u->memchunk.memblock; |
|
|
|
if (u->current == NULL) { u->current = current_block; } |
|
|
|
if (u->last == NULL) { u->last = current_block; } |
|
|
|
else { |
|
|
|
u->last->next = current_block; |
|
|
|
u->last = current_block; |
|
|
|
} |
|
|
|
|
|
|
|
//pa_memblock_unref(u->memchunk.memblock);
|
|
|
|
pa_memchunk_reset(&u->memchunk); |
|
|
|
if (ate >= u->sink->thread_info.max_request) break; |
|
|
|
} |
|
|
|
} |
|
|
@ -307,39 +346,12 @@ fail: |
|
|
|
finish: |
|
|
|
pa_log_debug("Thread shutting down"); |
|
|
|
} |
|
|
|
|
|
|
|
static SLint32 PA2SLrate(int32_t rate){ |
|
|
|
if (!(rate >= 8000 && rate <= 192000)) return -1; |
|
|
|
switch(rate){ |
|
|
|
case 8000: |
|
|
|
return SL_SAMPLINGRATE_8; |
|
|
|
case 11025: |
|
|
|
return SL_SAMPLINGRATE_11_025; |
|
|
|
case 12000: |
|
|
|
return SL_SAMPLINGRATE_12; |
|
|
|
case 16000: |
|
|
|
return SL_SAMPLINGRATE_16; |
|
|
|
case 22050: |
|
|
|
return SL_SAMPLINGRATE_22_05; |
|
|
|
case 24000: |
|
|
|
return SL_SAMPLINGRATE_24; |
|
|
|
case 32000: |
|
|
|
return SL_SAMPLINGRATE_32; |
|
|
|
case 44100: |
|
|
|
return SL_SAMPLINGRATE_44_1; |
|
|
|
case 48000: |
|
|
|
return SL_SAMPLINGRATE_48; |
|
|
|
case 64000: |
|
|
|
return SL_SAMPLINGRATE_64; |
|
|
|
case 88200: |
|
|
|
return SL_SAMPLINGRATE_88_2; |
|
|
|
case 96000: |
|
|
|
return SL_SAMPLINGRATE_96; |
|
|
|
case 192000: |
|
|
|
return SL_SAMPLINGRATE_192; |
|
|
|
default: |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
static int getenv_int(const char * env, size_t min_len){ |
|
|
|
char * got_env = getenv(env); |
|
|
|
int ret = 0; |
|
|
|
if (got_env != NULL && strlen(got_env) >= min_len) ret = atoi(got_env); //"8000" is 4 symbols
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
int pa__init(pa_module*m) { |
|
|
@ -373,20 +385,25 @@ int pa__init(pa_module*m) { |
|
|
|
//Needed. Don't touch
|
|
|
|
ss.channels = 2; |
|
|
|
ss.format = PA_SAMPLE_S16LE; |
|
|
|
int forceFormat = atoi(getenv("PROPERTY_OUTPUT_SAMPLE_RATE")); |
|
|
|
if (forceFormat >= 8000 && forceFormat <= 192000) |
|
|
|
ss.rate = forceFormat; |
|
|
|
|
|
|
|
m->userdata = u = pa_xnew0(struct userdata, 1); |
|
|
|
|
|
|
|
int forceFormat = getenv_int("PROPERTY_OUTPUT_SAMPLE_RATE", 4); //"8000" is 4 symbols
|
|
|
|
if (forceFormat >= 8000 && forceFormat <= 192000) { |
|
|
|
ss.rate = forceFormat; |
|
|
|
pa_log_info("Sample rate was forced to be %u\n", ss.rate); |
|
|
|
} |
|
|
|
|
|
|
|
u->core = m->core; |
|
|
|
u->module = m; |
|
|
|
u->rtpoll = pa_rtpoll_new(); |
|
|
|
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); |
|
|
|
|
|
|
|
if (pa_init_sles_player(u, PA2SLrate(ss.rate)) < 0) |
|
|
|
//Pulseaudio uses samples per sec but OpenSL ES uses samples per ms
|
|
|
|
if (pa_init_sles_player(u, ss.rate * 1000) < 0) |
|
|
|
goto fail; |
|
|
|
int buff[2] = {0, 0}; |
|
|
|
(*u->bqPlayerBufferQueue)->Enqueue(u->bqPlayerBufferQueue, buff, 1); |
|
|
|
//int buff[2] = {0, 0};
|
|
|
|
//(*u->bqPlayerBufferQueue)->Enqueue(u->bqPlayerBufferQueue, buff, 1);
|
|
|
|
|
|
|
|
pa_sink_new_data_init(&data); |
|
|
|
data.driver = __FILE__; |
|
|
@ -469,6 +486,10 @@ void pa__done(pa_module*m) { |
|
|
|
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); |
|
|
|
pa_thread_free(u->thread); |
|
|
|
} |
|
|
|
|
|
|
|
if (u->engineObject){ |
|
|
|
pa_destroy_sles_player(u); |
|
|
|
} |
|
|
|
|
|
|
|
pa_thread_mq_done(&u->thread_mq); |
|
|
|
|
|
|
|