1
/* $Id: android_jni_dev.c 4435 2013-03-11 06:32:58Z nanang $ */
3
* Copyright (C) 2012-2012 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
/* This file is the implementation of Android JNI audio device.
21
* The original code was originally part of CSipSimple
22
* (http://code.google.com/p/csipsimple/) and was kindly donated
26
#include <pjmedia-audiodev/audiodev_imp.h>
27
#include <pj/assert.h>
30
#include <pj/string.h>
31
#include <pjmedia/errno.h>
33
#if defined(PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI) && \
34
PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI != 0
37
#include <sys/resource.h>
38
#include <sys/system_properties.h>
40
#define THIS_FILE "android_jni_dev.c"
41
#define DRIVER_NAME "Android JNI"
43
struct android_aud_factory
45
pjmedia_aud_dev_factory base;
51
* Sound stream descriptor.
52
* This struct may be used for both unidirectional or bidirectional sound
55
struct android_aud_stream
57
pjmedia_aud_stream base;
61
pjmedia_aud_param param;
64
pj_uint32_t samples_per_sec;
65
unsigned samples_per_frame;
74
unsigned rec_buf_size;
75
pjmedia_aud_rec_cb rec_cb;
76
pj_bool_t rec_thread_exited;
77
pj_thread_t *rec_thread;
79
pj_timestamp rec_timestamp;
84
unsigned play_buf_size;
85
pjmedia_aud_play_cb play_cb;
86
pj_bool_t play_thread_exited;
87
pj_thread_t *play_thread;
89
pj_timestamp play_timestamp;
92
/* Factory prototypes */
93
static pj_status_t android_init(pjmedia_aud_dev_factory *f);
94
static pj_status_t android_destroy(pjmedia_aud_dev_factory *f);
95
static pj_status_t android_refresh(pjmedia_aud_dev_factory *f);
96
static unsigned android_get_dev_count(pjmedia_aud_dev_factory *f);
97
static pj_status_t android_get_dev_info(pjmedia_aud_dev_factory *f,
99
pjmedia_aud_dev_info *info);
100
static pj_status_t android_default_param(pjmedia_aud_dev_factory *f,
102
pjmedia_aud_param *param);
103
static pj_status_t android_create_stream(pjmedia_aud_dev_factory *f,
104
const pjmedia_aud_param *param,
105
pjmedia_aud_rec_cb rec_cb,
106
pjmedia_aud_play_cb play_cb,
108
pjmedia_aud_stream **p_aud_strm);
110
/* Stream prototypes */
111
static pj_status_t strm_get_param(pjmedia_aud_stream *strm,
112
pjmedia_aud_param *param);
113
static pj_status_t strm_get_cap(pjmedia_aud_stream *strm,
114
pjmedia_aud_dev_cap cap,
116
static pj_status_t strm_set_cap(pjmedia_aud_stream *strm,
117
pjmedia_aud_dev_cap cap,
119
static pj_status_t strm_start(pjmedia_aud_stream *strm);
120
static pj_status_t strm_stop(pjmedia_aud_stream *strm);
121
static pj_status_t strm_destroy(pjmedia_aud_stream *strm);
123
static pjmedia_aud_dev_factory_op android_op =
127
&android_get_dev_count,
128
&android_get_dev_info,
129
&android_default_param,
130
&android_create_stream,
134
static pjmedia_aud_stream_op android_strm_op =
146
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
150
return JNI_VERSION_1_4;
153
static pj_bool_t attach_jvm(JNIEnv **jni_env)
155
if ((*android_jvm)->GetEnv(android_jvm, (void **)jni_env,
156
JNI_VERSION_1_4) < 0)
158
if ((*android_jvm)->AttachCurrentThread(android_jvm, jni_env, NULL) < 0)
169
#define detach_jvm(attached) \
171
(*android_jvm)->DetachCurrentThread(android_jvm);
173
/* Thread priority utils */
174
/* TODO : port it to pj_thread functions */
175
#define THREAD_PRIORITY_AUDIO -16
176
#define THREAD_PRIORITY_URGENT_AUDIO -19
178
pj_status_t set_android_thread_priority(int priority)
180
jclass process_class;
181
jmethodID set_prio_method;
183
pj_status_t result = PJ_SUCCESS;
185
pj_bool_t attached = attach_jvm(&jni_env);
187
PJ_ASSERT_RETURN(jni_env, PJ_FALSE);
189
/* Get pointer to the java class */
190
process_class = (jclass)(*jni_env)->NewGlobalRef(jni_env,
191
(*jni_env)->FindClass(jni_env, "android/os/Process"));
192
if (process_class == 0) {
193
PJ_LOG(4, (THIS_FILE, "Unable to find os process class"));
194
result = PJ_EIGNORED;
198
/* Get the id of set thread priority function */
199
set_prio_method = (*jni_env)->GetStaticMethodID(jni_env, process_class,
202
if (set_prio_method == 0) {
203
PJ_LOG(4, (THIS_FILE, "Unable to find setThreadPriority() method"));
204
result = PJ_EIGNORED;
208
/* Set the thread priority */
209
(*jni_env)->CallStaticVoidMethod(jni_env, process_class, set_prio_method,
211
exc = (*jni_env)->ExceptionOccurred(jni_env);
213
(*jni_env)->ExceptionDescribe(jni_env);
214
(*jni_env)->ExceptionClear(jni_env);
215
PJ_LOG(4, (THIS_FILE, "Failure in setting thread priority using "
216
"Java API, fallback to setpriority()"));
217
setpriority(PRIO_PROCESS, 0, priority);
219
PJ_LOG(4, (THIS_FILE, "Setting thread priority successful"));
223
detach_jvm(attached);
228
static int AndroidRecorderCallback(void *userData)
230
struct android_aud_stream *stream = (struct android_aud_stream *)userData;
231
jmethodID read_method=0, record_method=0, stop_method=0;
232
int size = stream->rec_buf_size;
233
jbyteArray inputBuffer;
236
pj_bool_t attached = attach_jvm(&jni_env);
238
PJ_ASSERT_RETURN(jni_env, 0);
240
if (!stream->record) {
244
PJ_LOG(5, (THIS_FILE, "Recorder thread started"));
246
/* Get methods ids */
247
read_method = (*jni_env)->GetMethodID(jni_env, stream->record_class,
249
record_method = (*jni_env)->GetMethodID(jni_env, stream->record_class,
250
"startRecording", "()V");
251
stop_method = (*jni_env)->GetMethodID(jni_env, stream->record_class,
253
if (read_method==0 || record_method==0 || stop_method==0) {
254
PJ_LOG(3, (THIS_FILE, "Unable to get recording methods"));
258
/* Create a buffer for frames read */
259
inputBuffer = (*jni_env)->NewByteArray(jni_env, size);
260
if (inputBuffer == 0) {
261
PJ_LOG(3, (THIS_FILE, "Unable to allocate input buffer"));
264
buf = (*jni_env)->GetByteArrayElements(jni_env, inputBuffer, 0);
267
* setpriority(PRIO_PROCESS, 0, -19); //ANDROID_PRIORITY_AUDIO
268
* set priority is probably not enough because it does not change the thread
270
* Temporary solution is to call the java api to set the thread priority.
271
* A cool solution would be to port (if possible) the code from the
272
* android os regarding set_sched groups
274
set_android_thread_priority(THREAD_PRIORITY_URGENT_AUDIO);
275
(*jni_env)->CallVoidMethod(jni_env, stream->record, record_method);
277
while (!stream->quit_flag) {
282
if (!stream->running) {
283
(*jni_env)->CallVoidMethod(jni_env, stream->record, stop_method);
284
pj_sem_wait(stream->rec_sem);
285
if (stream->quit_flag)
287
(*jni_env)->CallVoidMethod(jni_env, stream->record, record_method);
290
bytesRead = (*jni_env)->CallIntMethod(jni_env, stream->record,
291
read_method, inputBuffer,
293
if (bytesRead <= 0 || bytesRead != size) {
294
PJ_LOG (4, (THIS_FILE, "Record thread : error %d reading data",
299
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
302
frame.buf = (void *)buf;
303
frame.timestamp.u64 = stream->rec_timestamp.u64;
305
status = (*stream->rec_cb)(stream->user_data, &frame);
307
stream->rec_timestamp.u64 += stream->param.samples_per_frame /
308
stream->param.channel_count;
311
(*jni_env)->ReleaseByteArrayElements(jni_env, inputBuffer, buf, 0);
312
(*jni_env)->DeleteLocalRef(jni_env, inputBuffer);
315
detach_jvm(attached);
316
PJ_LOG(5, (THIS_FILE, "Recorder thread stopped"));
317
stream->rec_thread_exited = 1;
323
static int AndroidTrackCallback(void *userData)
325
struct android_aud_stream *stream = (struct android_aud_stream*) userData;
326
jmethodID write_method=0, play_method=0, stop_method=0, flush_method=0;
327
int size = stream->play_buf_size;
328
jbyteArray outputBuffer;
331
pj_bool_t attached = attach_jvm(&jni_env);
333
if (!stream->track) {
337
PJ_LOG(5, (THIS_FILE, "Playback thread started"));
339
/* Get methods ids */
340
write_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
342
play_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
344
stop_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
346
flush_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
348
if (write_method==0 || play_method==0 || stop_method==0 ||
351
PJ_LOG(3, (THIS_FILE, "Unable to get audio track methods"));
355
outputBuffer = (*jni_env)->NewByteArray(jni_env, size);
356
if (outputBuffer == 0) {
357
PJ_LOG(3, (THIS_FILE, "Unable to allocate output buffer"));
360
buf = (*jni_env)->GetByteArrayElements(jni_env, outputBuffer, 0);
363
set_android_thread_priority(THREAD_PRIORITY_URGENT_AUDIO);
364
(*jni_env)->CallVoidMethod(jni_env, stream->track, play_method);
366
while (!stream->quit_flag) {
371
if (!stream->running) {
372
(*jni_env)->CallVoidMethod(jni_env, stream->track, stop_method);
373
(*jni_env)->CallVoidMethod(jni_env, stream->track, flush_method);
374
pj_sem_wait(stream->play_sem);
375
if (stream->quit_flag)
377
(*jni_env)->CallVoidMethod(jni_env, stream->track, play_method);
380
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
382
frame.buf = (void *)buf;
383
frame.timestamp.u64 = stream->play_timestamp.u64;
386
status = (*stream->play_cb)(stream->user_data, &frame);
387
if (status != PJ_SUCCESS)
390
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
391
pj_bzero(frame.buf, frame.size);
393
/* Write to the device output. */
394
bytesWritten = (*jni_env)->CallIntMethod(jni_env, stream->track,
395
write_method, outputBuffer,
397
if (bytesWritten <= 0 || bytesWritten != size) {
398
PJ_LOG(4, (THIS_FILE, "Player thread: Error %d writing data",
403
stream->play_timestamp.u64 += stream->param.samples_per_frame /
404
stream->param.channel_count;
407
(*jni_env)->ReleaseByteArrayElements(jni_env, outputBuffer, buf, 0);
408
(*jni_env)->DeleteLocalRef(jni_env, outputBuffer);
411
detach_jvm(attached);
412
PJ_LOG(5, (THIS_FILE, "Player thread stopped"));
413
stream->play_thread_exited = 1;
419
* Init Android audio driver.
421
pjmedia_aud_dev_factory* pjmedia_android_factory(pj_pool_factory *pf)
423
struct android_aud_factory *f;
426
pool = pj_pool_create(pf, "androidjni", 256, 256, NULL);
427
f = PJ_POOL_ZALLOC_T(pool, struct android_aud_factory);
430
f->base.op = &android_op;
435
/* API: Init factory */
436
static pj_status_t android_init(pjmedia_aud_dev_factory *f)
440
PJ_LOG(4, (THIS_FILE, "Android JNI sound library initialized"));
446
/* API: refresh the list of devices */
447
static pj_status_t android_refresh(pjmedia_aud_dev_factory *f)
454
/* API: Destroy factory */
455
static pj_status_t android_destroy(pjmedia_aud_dev_factory *f)
457
struct android_aud_factory *pa = (struct android_aud_factory*)f;
460
PJ_LOG(4, (THIS_FILE, "Android JNI sound library shutting down.."));
464
pj_pool_release(pool);
469
/* API: Get device count. */
470
static unsigned android_get_dev_count(pjmedia_aud_dev_factory *f)
476
/* API: Get device info. */
477
static pj_status_t android_get_dev_info(pjmedia_aud_dev_factory *f,
479
pjmedia_aud_dev_info *info)
483
pj_bzero(info, sizeof(*info));
485
pj_ansi_strcpy(info->name, "Android JNI");
486
info->default_samples_per_sec = 8000;
487
info->caps = PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
488
info->input_count = 1;
489
info->output_count = 1;
494
/* API: fill in with default parameter. */
495
static pj_status_t android_default_param(pjmedia_aud_dev_factory *f,
497
pjmedia_aud_param *param)
499
pjmedia_aud_dev_info adi;
502
status = android_get_dev_info(f, index, &adi);
503
if (status != PJ_SUCCESS)
506
pj_bzero(param, sizeof(*param));
507
if (adi.input_count && adi.output_count) {
508
param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
509
param->rec_id = index;
510
param->play_id = index;
511
} else if (adi.input_count) {
512
param->dir = PJMEDIA_DIR_CAPTURE;
513
param->rec_id = index;
514
param->play_id = PJMEDIA_AUD_INVALID_DEV;
515
} else if (adi.output_count) {
516
param->dir = PJMEDIA_DIR_PLAYBACK;
517
param->play_id = index;
518
param->rec_id = PJMEDIA_AUD_INVALID_DEV;
520
return PJMEDIA_EAUD_INVDEV;
523
param->clock_rate = adi.default_samples_per_sec;
524
param->channel_count = 1;
525
param->samples_per_frame = adi.default_samples_per_sec * 20 / 1000;
526
param->bits_per_sample = 16;
527
param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
528
param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
533
/* API: create stream */
534
static pj_status_t android_create_stream(pjmedia_aud_dev_factory *f,
535
const pjmedia_aud_param *param,
536
pjmedia_aud_rec_cb rec_cb,
537
pjmedia_aud_play_cb play_cb,
539
pjmedia_aud_stream **p_aud_strm)
541
struct android_aud_factory *pa = (struct android_aud_factory*)f;
543
struct android_aud_stream *stream;
544
pj_status_t status = PJ_SUCCESS;
546
int buffSize, inputBuffSizePlay, inputBuffSizeRec;
547
int channelInCfg, channelOutCfg, sampleFormat;
548
jmethodID constructor_method=0, bufsize_method = 0;
549
jmethodID method_id = 0;
554
PJ_ASSERT_RETURN(param->channel_count >= 1 && param->channel_count <= 2,
556
PJ_ASSERT_RETURN(param->bits_per_sample==8 || param->bits_per_sample==16,
558
PJ_ASSERT_RETURN(play_cb && rec_cb && p_aud_strm, PJ_EINVAL);
560
pool = pj_pool_create(pa->pf, "jnistrm", 1024, 1024, NULL);
564
PJ_LOG(4, (THIS_FILE, "Creating Android JNI stream"));
566
stream = PJ_POOL_ZALLOC_T(pool, struct android_aud_stream);
568
pj_strdup2_with_null(pool, &stream->name, "JNI stream");
569
stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
570
pj_memcpy(&stream->param, param, sizeof(*param));
571
stream->user_data = user_data;
572
stream->rec_cb = rec_cb;
573
stream->play_cb = play_cb;
574
buffSize = stream->param.samples_per_frame*stream->param.bits_per_sample/8;
575
stream->rec_buf_size = stream->play_buf_size = buffSize;
576
channelInCfg = (param->channel_count == 1)? 16 /*CHANNEL_IN_MONO*/:
577
12 /*CHANNEL_IN_STEREO*/;
578
channelOutCfg = (param->channel_count == 1)? 4 /*CHANNEL_OUT_MONO*/:
579
12 /*CHANNEL_OUT_STEREO*/;
580
sampleFormat = (param->bits_per_sample == 8)? 3 /*ENCODING_PCM_8BIT*/:
581
2 /*ENCODING_PCM_16BIT*/;
583
attached = attach_jvm(&jni_env);
585
if (stream->dir & PJMEDIA_DIR_CAPTURE) {
586
/* Find audio record class and create global ref */
587
jcl = (*jni_env)->FindClass(jni_env, "android/media/AudioRecord");
589
PJ_LOG(3, (THIS_FILE, "Unable to find audio record class"));
590
status = PJMEDIA_EAUD_SYSERR;
593
stream->record_class = (jclass)(*jni_env)->NewGlobalRef(jni_env, jcl);
594
(*jni_env)->DeleteLocalRef(jni_env, jcl);
595
if (stream->record_class == 0) {
600
/* Get the min buffer size function */
601
bufsize_method = (*jni_env)->GetStaticMethodID(jni_env,
602
stream->record_class,
605
if (bufsize_method == 0) {
606
PJ_LOG(3, (THIS_FILE, "Unable to find audio record "
607
"getMinBufferSize() method"));
608
status = PJMEDIA_EAUD_SYSERR;
612
inputBuffSizeRec = (*jni_env)->CallStaticIntMethod(jni_env,
613
stream->record_class,
618
if (inputBuffSizeRec <= 0) {
619
PJ_LOG(3, (THIS_FILE, "Unsupported audio record params"));
620
status = PJMEDIA_EAUD_INIT;
625
if (stream->dir & PJMEDIA_DIR_PLAYBACK) {
626
/* Find audio track class and create global ref */
627
jcl = (*jni_env)->FindClass(jni_env, "android/media/AudioTrack");
629
PJ_LOG(3, (THIS_FILE, "Unable to find audio track class"));
630
status = PJMEDIA_EAUD_SYSERR;
633
stream->track_class = (jclass)(*jni_env)->NewGlobalRef(jni_env, jcl);
634
(*jni_env)->DeleteLocalRef(jni_env, jcl);
635
if (stream->track_class == 0) {
640
/* Get the min buffer size function */
641
bufsize_method = (*jni_env)->GetStaticMethodID(jni_env,
645
if (bufsize_method == 0) {
646
PJ_LOG(3, (THIS_FILE, "Unable to find audio track "
647
"getMinBufferSize() method"));
648
status = PJMEDIA_EAUD_SYSERR;
652
inputBuffSizePlay = (*jni_env)->CallStaticIntMethod(jni_env,
658
if (inputBuffSizePlay <= 0) {
659
PJ_LOG(3, (THIS_FILE, "Unsupported audio track params"));
660
status = PJMEDIA_EAUD_INIT;
665
if (stream->dir & PJMEDIA_DIR_CAPTURE) {
667
int mic_source = 0; /* DEFAULT: default audio source */
669
/* Get pointer to the constructor */
670
constructor_method = (*jni_env)->GetMethodID(jni_env,
671
stream->record_class,
672
"<init>", "(IIIII)V");
673
if (constructor_method == 0) {
674
PJ_LOG(3, (THIS_FILE, "Unable to find audio record's constructor"));
675
status = PJMEDIA_EAUD_SYSERR;
679
if (mic_source == 0) {
680
char sdk_version[PROP_VALUE_MAX];
681
pj_str_t pj_sdk_version;
684
__system_property_get("ro.build.version.sdk", sdk_version);
685
pj_sdk_version = pj_str(sdk_version);
686
sdk_v = pj_strtoul(&pj_sdk_version);
688
mic_source = 7; /* VOICE_COMMUNICATION */
690
PJ_LOG(4, (THIS_FILE, "Using audio input source : %d", mic_source));
693
stream->record = (*jni_env)->NewObject(jni_env,
694
stream->record_class,
701
if (stream->record == 0) {
702
PJ_LOG(3, (THIS_FILE, "Unable to create audio record object"));
703
status = PJMEDIA_EAUD_INIT;
707
exc = (*jni_env)->ExceptionOccurred(jni_env);
709
(*jni_env)->ExceptionDescribe(jni_env);
710
(*jni_env)->ExceptionClear(jni_env);
711
PJ_LOG(3, (THIS_FILE, "Failure in audio record's constructor"));
712
if (mic_source == 0) {
713
status = PJMEDIA_EAUD_INIT;
717
PJ_LOG(4, (THIS_FILE, "Trying the default audio source."));
722
method_id = (*jni_env)->GetMethodID(jni_env, stream->record_class,
724
if (method_id == 0) {
725
PJ_LOG(3, (THIS_FILE, "Unable to find audio record getState() "
727
status = PJMEDIA_EAUD_SYSERR;
730
state = (*jni_env)->CallIntMethod(jni_env, stream->record,
732
if (state == 0) { /* STATE_UNINITIALIZED */
733
PJ_LOG(3, (THIS_FILE, "Failure in initializing audio record."));
734
if (mic_source == 0) {
735
status = PJMEDIA_EAUD_INIT;
739
PJ_LOG(4, (THIS_FILE, "Trying the default audio source."));
741
} while (state == 0);
743
stream->record = (*jni_env)->NewGlobalRef(jni_env, stream->record);
744
if (stream->record == 0) {
745
PJ_LOG(3, (THIS_FILE, "Unable to create audio record global ref."));
746
status = PJMEDIA_EAUD_INIT;
750
status = pj_sem_create(stream->pool, NULL, 0, 1, &stream->rec_sem);
751
if (status != PJ_SUCCESS)
754
status = pj_thread_create(stream->pool, "android_recorder",
755
AndroidRecorderCallback, stream, 0, 0,
756
&stream->rec_thread);
757
if (status != PJ_SUCCESS)
760
PJ_LOG(4, (THIS_FILE, "Audio record initialized successfully."));
763
if (stream->dir & PJMEDIA_DIR_PLAYBACK) {
766
/* Get pointer to the constructor */
767
constructor_method = (*jni_env)->GetMethodID(jni_env,
769
"<init>", "(IIIIII)V");
770
if (constructor_method == 0) {
771
PJ_LOG(3, (THIS_FILE, "Unable to find audio track's constructor."));
772
status = PJMEDIA_EAUD_SYSERR;
776
stream->track = (*jni_env)->NewObject(jni_env,
779
0, /* STREAM_VOICE_CALL */
784
1 /* MODE_STREAM */);
785
if (stream->track == 0) {
786
PJ_LOG(3, (THIS_FILE, "Unable to create audio track object."));
787
status = PJMEDIA_EAUD_INIT;
791
exc = (*jni_env)->ExceptionOccurred(jni_env);
793
(*jni_env)->ExceptionDescribe(jni_env);
794
(*jni_env)->ExceptionClear(jni_env);
795
PJ_LOG(3, (THIS_FILE, "Failure in audio track's constructor"));
796
status = PJMEDIA_EAUD_INIT;
800
stream->track = (*jni_env)->NewGlobalRef(jni_env, stream->track);
801
if (stream->track == 0) {
802
PJ_LOG(3, (THIS_FILE, "Unable to create audio track's global ref"));
803
status = PJMEDIA_EAUD_INIT;
808
method_id = (*jni_env)->GetMethodID(jni_env, stream->track_class,
810
if (method_id == 0) {
811
PJ_LOG(3, (THIS_FILE, "Unable to find audio track getState() "
813
status = PJMEDIA_EAUD_SYSERR;
816
state = (*jni_env)->CallIntMethod(jni_env, stream->track,
818
if (state == 0) { /* STATE_UNINITIALIZED */
819
PJ_LOG(3, (THIS_FILE, "Failure in initializing audio track."));
820
status = PJMEDIA_EAUD_INIT;
824
status = pj_sem_create(stream->pool, NULL, 0, 1, &stream->play_sem);
825
if (status != PJ_SUCCESS)
828
status = pj_thread_create(stream->pool, "android_track",
829
AndroidTrackCallback, stream, 0, 0,
830
&stream->play_thread);
831
if (status != PJ_SUCCESS)
834
PJ_LOG(4, (THIS_FILE, "Audio track initialized successfully."));
837
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
838
strm_set_cap(&stream->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
843
stream->base.op = &android_strm_op;
844
*p_aud_strm = &stream->base;
846
detach_jvm(attached);
851
detach_jvm(attached);
852
strm_destroy(&stream->base);
856
/* API: Get stream parameters */
857
static pj_status_t strm_get_param(pjmedia_aud_stream *s,
858
pjmedia_aud_param *pi)
860
struct android_aud_stream *strm = (struct android_aud_stream*)s;
861
PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
862
pj_memcpy(pi, &strm->param, sizeof(*pi));
864
if (strm_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
865
&pi->output_vol) == PJ_SUCCESS)
867
pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
873
/* API: get capability */
874
static pj_status_t strm_get_cap(pjmedia_aud_stream *s,
875
pjmedia_aud_dev_cap cap,
878
struct android_aud_stream *strm = (struct android_aud_stream*)s;
879
pj_status_t status = PJMEDIA_EAUD_INVCAP;
881
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
883
if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
884
(strm->param.dir & PJMEDIA_DIR_PLAYBACK))
891
/* API: set capability */
892
static pj_status_t strm_set_cap(pjmedia_aud_stream *s,
893
pjmedia_aud_dev_cap cap,
896
struct android_aud_stream *stream = (struct android_aud_stream*)s;
900
PJ_ASSERT_RETURN(s && value, PJ_EINVAL);
902
if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
903
(stream->param.dir & PJMEDIA_DIR_PLAYBACK))
906
jmethodID vol_method = 0;
908
float vol = *(int *)value;
910
attached = attach_jvm(&jni_env);
912
vol_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
913
"setStereoVolume", "(FF)I");
915
retval = (*jni_env)->CallIntMethod(jni_env, stream->track,
920
detach_jvm(attached);
922
if (vol_method && retval == 0)
927
return PJMEDIA_EAUD_INVCAP;
930
/* API: start stream. */
931
static pj_status_t strm_start(pjmedia_aud_stream *s)
933
struct android_aud_stream *stream = (struct android_aud_stream*)s;
935
if (!stream->running) {
936
stream->running = PJ_TRUE;
938
pj_sem_post(stream->rec_sem);
940
pj_sem_post(stream->play_sem);
943
PJ_LOG(4, (THIS_FILE, "Android JNI stream started"));
948
/* API: stop stream. */
949
static pj_status_t strm_stop(pjmedia_aud_stream *s)
951
struct android_aud_stream *stream = (struct android_aud_stream*)s;
953
if (!stream->running)
956
stream->running = PJ_FALSE;
957
PJ_LOG(4,(THIS_FILE, "Android JNI stream stopped"));
962
/* API: destroy stream. */
963
static pj_status_t strm_destroy(pjmedia_aud_stream *s)
965
struct android_aud_stream *stream = (struct android_aud_stream*)s;
967
jmethodID release_method=0;
970
PJ_LOG(4,(THIS_FILE, "Destroying Android JNI stream..."));
972
stream->quit_flag = PJ_TRUE;
974
/* Stop the stream */
977
attached = attach_jvm(&jni_env);
980
if (stream->rec_thread) {
981
pj_sem_post(stream->rec_sem);
982
pj_thread_join(stream->rec_thread);
983
pj_thread_destroy(stream->rec_thread);
984
stream->rec_thread = NULL;
987
if (stream->rec_sem) {
988
pj_sem_destroy(stream->rec_sem);
989
stream->rec_sem = NULL;
992
release_method = (*jni_env)->GetMethodID(jni_env, stream->record_class,
994
(*jni_env)->CallVoidMethod(jni_env, stream->record, release_method);
996
(*jni_env)->DeleteGlobalRef(jni_env, stream->record);
997
(*jni_env)->DeleteGlobalRef(jni_env, stream->record_class);
998
stream->record = NULL;
999
stream->record_class = NULL;
1000
PJ_LOG(4, (THIS_FILE, "Audio record released"));
1003
if (stream->track) {
1004
if (stream->play_thread) {
1005
pj_sem_post(stream->play_sem);
1006
pj_thread_join(stream->play_thread);
1007
pj_thread_destroy(stream->play_thread);
1008
stream->play_thread = NULL;
1011
if (stream->play_sem) {
1012
pj_sem_destroy(stream->play_sem);
1013
stream->play_sem = NULL;
1016
release_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
1018
(*jni_env)->CallVoidMethod(jni_env, stream->track, release_method);
1020
(*jni_env)->DeleteGlobalRef(jni_env, stream->track);
1021
(*jni_env)->DeleteGlobalRef(jni_env, stream->track_class);
1022
stream->track = NULL;
1023
stream->track_class = NULL;
1024
PJ_LOG(3, (THIS_FILE, "Audio track released"));
1027
pj_pool_release(stream->pool);
1028
PJ_LOG(4, (THIS_FILE, "Android JNI stream destroyed"));
1030
detach_jvm(attached);
1034
#endif /* PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI */