~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.2.1/pjmedia/src/pjmedia-audiodev/android_jni_dev.c

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: android_jni_dev.c 4435 2013-03-11 06:32:58Z nanang $ */
 
2
/* 
 
3
 * Copyright (C) 2012-2012 Teluu Inc. (http://www.teluu.com)
 
4
 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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 
 
19
 */
 
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
 
23
 * by Regis Montoya.
 
24
 */
 
25
 
 
26
#include <pjmedia-audiodev/audiodev_imp.h>
 
27
#include <pj/assert.h>
 
28
#include <pj/log.h>
 
29
#include <pj/os.h>
 
30
#include <pj/string.h>
 
31
#include <pjmedia/errno.h>
 
32
 
 
33
#if defined(PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI) && \
 
34
    PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI != 0
 
35
 
 
36
#include <jni.h>
 
37
#include <sys/resource.h>
 
38
#include <sys/system_properties.h>
 
39
 
 
40
#define THIS_FILE       "android_jni_dev.c"
 
41
#define DRIVER_NAME     "Android JNI"
 
42
 
 
43
struct android_aud_factory
 
44
{
 
45
    pjmedia_aud_dev_factory base;
 
46
    pj_pool_factory        *pf;
 
47
    pj_pool_t              *pool;
 
48
};
 
49
 
 
50
/* 
 
51
 * Sound stream descriptor.
 
52
 * This struct may be used for both unidirectional or bidirectional sound
 
53
 * streams.
 
54
 */
 
55
struct android_aud_stream
 
56
{
 
57
    pjmedia_aud_stream  base;
 
58
    pj_pool_t          *pool;
 
59
    pj_str_t            name;
 
60
    pjmedia_dir         dir;
 
61
    pjmedia_aud_param   param;
 
62
    
 
63
    int                 bytes_per_sample;
 
64
    pj_uint32_t         samples_per_sec;
 
65
    unsigned            samples_per_frame;
 
66
    int                 channel_count;
 
67
    void               *user_data;
 
68
    pj_bool_t           quit_flag;
 
69
    pj_bool_t           running;
 
70
 
 
71
    /* Record */
 
72
    jobject             record;
 
73
    jclass              record_class;
 
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;
 
78
    pj_sem_t           *rec_sem;
 
79
    pj_timestamp        rec_timestamp;
 
80
    
 
81
    /* Track */
 
82
    jobject             track;
 
83
    jclass              track_class;
 
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;
 
88
    pj_sem_t           *play_sem;
 
89
    pj_timestamp        play_timestamp;
 
90
};
 
91
 
 
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,
 
98
                                        unsigned index,
 
99
                                        pjmedia_aud_dev_info *info);
 
100
static pj_status_t android_default_param(pjmedia_aud_dev_factory *f,
 
101
                                         unsigned index,
 
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,
 
107
                                         void *user_data,
 
108
                                         pjmedia_aud_stream **p_aud_strm);
 
109
 
 
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,
 
115
                                void *value);
 
116
static pj_status_t strm_set_cap(pjmedia_aud_stream *strm,
 
117
                                pjmedia_aud_dev_cap cap,
 
118
                                const void *value);
 
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);
 
122
 
 
123
static pjmedia_aud_dev_factory_op android_op =
 
124
{
 
125
    &android_init,
 
126
    &android_destroy,
 
127
    &android_get_dev_count,
 
128
    &android_get_dev_info,
 
129
    &android_default_param,
 
130
    &android_create_stream,
 
131
    &android_refresh
 
132
};
 
133
 
 
134
static pjmedia_aud_stream_op android_strm_op =
 
135
{
 
136
    &strm_get_param,
 
137
    &strm_get_cap,
 
138
    &strm_set_cap,
 
139
    &strm_start,
 
140
    &strm_stop,
 
141
    &strm_destroy
 
142
};
 
143
 
 
144
JavaVM *android_jvm;
 
145
 
 
146
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
 
147
{
 
148
    android_jvm = vm;
 
149
    
 
150
    return JNI_VERSION_1_4;
 
151
}
 
152
 
 
153
static pj_bool_t attach_jvm(JNIEnv **jni_env)
 
154
{
 
155
    if ((*android_jvm)->GetEnv(android_jvm, (void **)jni_env,
 
156
                               JNI_VERSION_1_4) < 0)
 
157
    {
 
158
        if ((*android_jvm)->AttachCurrentThread(android_jvm, jni_env, NULL) < 0)
 
159
        {
 
160
            jni_env = NULL;
 
161
            return PJ_FALSE;
 
162
        }
 
163
        return PJ_TRUE;
 
164
    }
 
165
    
 
166
    return PJ_FALSE;
 
167
}
 
168
 
 
169
#define detach_jvm(attached) \
 
170
    if (attached) \
 
171
        (*android_jvm)->DetachCurrentThread(android_jvm);
 
172
 
 
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
 
177
 
 
178
pj_status_t set_android_thread_priority(int priority)
 
179
{
 
180
    jclass process_class;
 
181
    jmethodID set_prio_method;
 
182
    jthrowable exc;
 
183
    pj_status_t result = PJ_SUCCESS;
 
184
    JNIEnv *jni_env = 0;
 
185
    pj_bool_t attached = attach_jvm(&jni_env);
 
186
    
 
187
    PJ_ASSERT_RETURN(jni_env, PJ_FALSE);
 
188
 
 
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;
 
195
        goto on_return;
 
196
    }
 
197
    
 
198
    /* Get the id of set thread priority function */
 
199
    set_prio_method = (*jni_env)->GetStaticMethodID(jni_env, process_class,
 
200
                                                    "setThreadPriority",
 
201
                                                    "(I)V");
 
202
    if (set_prio_method == 0) {
 
203
        PJ_LOG(4, (THIS_FILE, "Unable to find setThreadPriority() method"));
 
204
        result = PJ_EIGNORED;
 
205
        goto on_return;
 
206
    }
 
207
    
 
208
    /* Set the thread priority */
 
209
    (*jni_env)->CallStaticVoidMethod(jni_env, process_class, set_prio_method,
 
210
                                     priority);    
 
211
    exc = (*jni_env)->ExceptionOccurred(jni_env);
 
212
    if (exc) {
 
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);
 
218
    } else {
 
219
        PJ_LOG(4, (THIS_FILE, "Setting thread priority successful"));        
 
220
    }
 
221
 
 
222
on_return:
 
223
    detach_jvm(attached);
 
224
    return result;
 
225
}
 
226
 
 
227
 
 
228
static int AndroidRecorderCallback(void *userData)
 
229
{
 
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;
 
234
    jbyte *buf;
 
235
    JNIEnv *jni_env = 0;
 
236
    pj_bool_t attached = attach_jvm(&jni_env);
 
237
    
 
238
    PJ_ASSERT_RETURN(jni_env, 0);
 
239
    
 
240
    if (!stream->record) {
 
241
        goto on_return;
 
242
    }
 
243
 
 
244
    PJ_LOG(5, (THIS_FILE, "Recorder thread started"));
 
245
 
 
246
    /* Get methods ids */
 
247
    read_method = (*jni_env)->GetMethodID(jni_env, stream->record_class, 
 
248
                                          "read", "([BII)I");
 
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,
 
252
                                          "stop", "()V");
 
253
    if (read_method==0 || record_method==0 || stop_method==0) {
 
254
        PJ_LOG(3, (THIS_FILE, "Unable to get recording methods"));
 
255
        goto on_return;
 
256
    }
 
257
    
 
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"));
 
262
        goto on_return;
 
263
    }
 
264
    buf = (*jni_env)->GetByteArrayElements(jni_env, inputBuffer, 0);
 
265
    
 
266
    /* Start recording
 
267
     * setpriority(PRIO_PROCESS, 0, -19); //ANDROID_PRIORITY_AUDIO
 
268
     * set priority is probably not enough because it does not change the thread
 
269
     * group in scheduler
 
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
 
273
     */
 
274
    set_android_thread_priority(THREAD_PRIORITY_URGENT_AUDIO);
 
275
    (*jni_env)->CallVoidMethod(jni_env, stream->record, record_method);
 
276
    
 
277
    while (!stream->quit_flag) {
 
278
        pjmedia_frame frame;
 
279
        pj_status_t status;
 
280
        int bytesRead;
 
281
        
 
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)
 
286
                break;
 
287
            (*jni_env)->CallVoidMethod(jni_env, stream->record, record_method);
 
288
        }
 
289
        
 
290
        bytesRead = (*jni_env)->CallIntMethod(jni_env, stream->record,
 
291
                                              read_method, inputBuffer,
 
292
                                              0, size);
 
293
        if (bytesRead <= 0 || bytesRead != size) {
 
294
            PJ_LOG (4, (THIS_FILE, "Record thread : error %d reading data",
 
295
                                   bytesRead));
 
296
            continue;
 
297
        }
 
298
 
 
299
        frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
300
        frame.size =  size;
 
301
        frame.bit_info = 0;
 
302
        frame.buf = (void *)buf;
 
303
        frame.timestamp.u64 = stream->rec_timestamp.u64;
 
304
 
 
305
        status = (*stream->rec_cb)(stream->user_data, &frame);
 
306
 
 
307
        stream->rec_timestamp.u64 += stream->param.samples_per_frame /
 
308
                                     stream->param.channel_count;
 
309
    }
 
310
 
 
311
    (*jni_env)->ReleaseByteArrayElements(jni_env, inputBuffer, buf, 0);
 
312
    (*jni_env)->DeleteLocalRef(jni_env, inputBuffer);
 
313
    
 
314
on_return:
 
315
    detach_jvm(attached);
 
316
    PJ_LOG(5, (THIS_FILE, "Recorder thread stopped"));
 
317
    stream->rec_thread_exited = 1;
 
318
 
 
319
    return 0;
 
320
}
 
321
 
 
322
 
 
323
static int AndroidTrackCallback(void *userData)
 
324
{
 
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;
 
329
    jbyte *buf;
 
330
    JNIEnv *jni_env = 0;
 
331
    pj_bool_t attached = attach_jvm(&jni_env);
 
332
    
 
333
    if (!stream->track) {
 
334
        goto on_return;
 
335
    }
 
336
 
 
337
    PJ_LOG(5, (THIS_FILE, "Playback thread started"));
 
338
 
 
339
    /* Get methods ids */
 
340
    write_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
 
341
                                           "write", "([BII)I");
 
342
    play_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
 
343
                                          "play", "()V");
 
344
    stop_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
 
345
                                          "stop", "()V");
 
346
    flush_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
 
347
                                           "flush", "()V");
 
348
    if (write_method==0 || play_method==0 || stop_method==0 ||
 
349
        flush_method==0)
 
350
    {
 
351
        PJ_LOG(3, (THIS_FILE, "Unable to get audio track methods"));
 
352
        goto on_return;
 
353
    }
 
354
 
 
355
    outputBuffer = (*jni_env)->NewByteArray(jni_env, size);
 
356
    if (outputBuffer == 0) {
 
357
        PJ_LOG(3, (THIS_FILE, "Unable to allocate output buffer"));
 
358
        goto on_return;
 
359
    }
 
360
    buf = (*jni_env)->GetByteArrayElements(jni_env, outputBuffer, 0);
 
361
 
 
362
    /* Start playing */
 
363
    set_android_thread_priority(THREAD_PRIORITY_URGENT_AUDIO);
 
364
    (*jni_env)->CallVoidMethod(jni_env, stream->track, play_method);
 
365
 
 
366
    while (!stream->quit_flag) {
 
367
        pjmedia_frame frame;
 
368
        pj_status_t status;
 
369
        int bytesWritten;
 
370
 
 
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)
 
376
                break;
 
377
            (*jni_env)->CallVoidMethod(jni_env, stream->track, play_method);
 
378
        }
 
379
        
 
380
        frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
381
        frame.size = size;
 
382
        frame.buf = (void *)buf;
 
383
        frame.timestamp.u64 = stream->play_timestamp.u64;
 
384
        frame.bit_info = 0;
 
385
        
 
386
        status = (*stream->play_cb)(stream->user_data, &frame);
 
387
        if (status != PJ_SUCCESS)
 
388
            continue;
 
389
        
 
390
        if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
 
391
            pj_bzero(frame.buf, frame.size);
 
392
        
 
393
        /* Write to the device output. */
 
394
        bytesWritten = (*jni_env)->CallIntMethod(jni_env, stream->track,
 
395
                                                 write_method, outputBuffer,
 
396
                                                 0, size);
 
397
        if (bytesWritten <= 0 || bytesWritten != size) {
 
398
            PJ_LOG(4, (THIS_FILE, "Player thread: Error %d writing data",
 
399
                                  bytesWritten));
 
400
            continue;
 
401
        }
 
402
 
 
403
        stream->play_timestamp.u64 += stream->param.samples_per_frame /
 
404
                                      stream->param.channel_count;
 
405
    };
 
406
    
 
407
    (*jni_env)->ReleaseByteArrayElements(jni_env, outputBuffer, buf, 0);
 
408
    (*jni_env)->DeleteLocalRef(jni_env, outputBuffer);
 
409
    
 
410
on_return:
 
411
    detach_jvm(attached);
 
412
    PJ_LOG(5, (THIS_FILE, "Player thread stopped"));
 
413
    stream->play_thread_exited = 1;
 
414
    
 
415
    return 0;
 
416
}
 
417
 
 
418
/*
 
419
 * Init Android audio driver.
 
420
 */
 
421
pjmedia_aud_dev_factory* pjmedia_android_factory(pj_pool_factory *pf)
 
422
{
 
423
    struct android_aud_factory *f;
 
424
    pj_pool_t *pool;
 
425
    
 
426
    pool = pj_pool_create(pf, "androidjni", 256, 256, NULL);
 
427
    f = PJ_POOL_ZALLOC_T(pool, struct android_aud_factory);
 
428
    f->pf = pf;
 
429
    f->pool = pool;
 
430
    f->base.op = &android_op;
 
431
    
 
432
    return &f->base;
 
433
}
 
434
 
 
435
/* API: Init factory */
 
436
static pj_status_t android_init(pjmedia_aud_dev_factory *f)
 
437
{
 
438
    PJ_UNUSED_ARG(f);
 
439
    
 
440
    PJ_LOG(4, (THIS_FILE, "Android JNI sound library initialized"));
 
441
    
 
442
    return PJ_SUCCESS;
 
443
}
 
444
 
 
445
 
 
446
/* API: refresh the list of devices */
 
447
static pj_status_t android_refresh(pjmedia_aud_dev_factory *f)
 
448
{
 
449
    PJ_UNUSED_ARG(f);
 
450
    return PJ_SUCCESS;
 
451
}
 
452
 
 
453
 
 
454
/* API: Destroy factory */
 
455
static pj_status_t android_destroy(pjmedia_aud_dev_factory *f)
 
456
{
 
457
    struct android_aud_factory *pa = (struct android_aud_factory*)f;
 
458
    pj_pool_t *pool;
 
459
    
 
460
    PJ_LOG(4, (THIS_FILE, "Android JNI sound library shutting down.."));
 
461
    
 
462
    pool = pa->pool;
 
463
    pa->pool = NULL;
 
464
    pj_pool_release(pool);
 
465
    
 
466
    return PJ_SUCCESS;
 
467
}
 
468
 
 
469
/* API: Get device count. */
 
470
static unsigned android_get_dev_count(pjmedia_aud_dev_factory *f)
 
471
{
 
472
    PJ_UNUSED_ARG(f);
 
473
    return 1;
 
474
}
 
475
 
 
476
/* API: Get device info. */
 
477
static pj_status_t android_get_dev_info(pjmedia_aud_dev_factory *f,
 
478
                                        unsigned index,
 
479
                                        pjmedia_aud_dev_info *info)
 
480
{
 
481
    PJ_UNUSED_ARG(f);
 
482
    
 
483
    pj_bzero(info, sizeof(*info));
 
484
    
 
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;
 
490
    
 
491
    return PJ_SUCCESS;
 
492
}
 
493
 
 
494
/* API: fill in with default parameter. */
 
495
static pj_status_t android_default_param(pjmedia_aud_dev_factory *f,
 
496
                                         unsigned index,
 
497
                                         pjmedia_aud_param *param)
 
498
{
 
499
    pjmedia_aud_dev_info adi;
 
500
    pj_status_t status;
 
501
    
 
502
    status = android_get_dev_info(f, index, &adi);
 
503
    if (status != PJ_SUCCESS)
 
504
        return status;
 
505
    
 
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;
 
519
    } else {
 
520
        return PJMEDIA_EAUD_INVDEV;
 
521
    }
 
522
    
 
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;
 
529
    
 
530
    return PJ_SUCCESS;
 
531
}
 
532
 
 
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,
 
538
                                         void *user_data,
 
539
                                         pjmedia_aud_stream **p_aud_strm)
 
540
{
 
541
    struct android_aud_factory *pa = (struct android_aud_factory*)f;
 
542
    pj_pool_t *pool;
 
543
    struct android_aud_stream *stream;
 
544
    pj_status_t status = PJ_SUCCESS;
 
545
    int state = 0;
 
546
    int buffSize, inputBuffSizePlay, inputBuffSizeRec;
 
547
    int channelInCfg, channelOutCfg, sampleFormat;
 
548
    jmethodID constructor_method=0, bufsize_method = 0;
 
549
    jmethodID method_id = 0;
 
550
    jclass jcl;
 
551
    JNIEnv *jni_env = 0;
 
552
    pj_bool_t attached;
 
553
    
 
554
    PJ_ASSERT_RETURN(param->channel_count >= 1 && param->channel_count <= 2,
 
555
                     PJ_EINVAL);
 
556
    PJ_ASSERT_RETURN(param->bits_per_sample==8 || param->bits_per_sample==16,
 
557
                     PJ_EINVAL);
 
558
    PJ_ASSERT_RETURN(play_cb && rec_cb && p_aud_strm, PJ_EINVAL);
 
559
 
 
560
    pool = pj_pool_create(pa->pf, "jnistrm", 1024, 1024, NULL);
 
561
    if (!pool)
 
562
        return PJ_ENOMEM;
 
563
 
 
564
    PJ_LOG(4, (THIS_FILE, "Creating Android JNI stream"));
 
565
    
 
566
    stream = PJ_POOL_ZALLOC_T(pool, struct android_aud_stream);
 
567
    stream->pool = pool;
 
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*/;
 
582
 
 
583
    attached = attach_jvm(&jni_env);
 
584
 
 
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");
 
588
        if (jcl == NULL) {
 
589
            PJ_LOG(3, (THIS_FILE, "Unable to find audio record class"));
 
590
            status = PJMEDIA_EAUD_SYSERR;
 
591
            goto on_error;
 
592
        }
 
593
        stream->record_class = (jclass)(*jni_env)->NewGlobalRef(jni_env, jcl);
 
594
        (*jni_env)->DeleteLocalRef(jni_env, jcl);
 
595
        if (stream->record_class == 0) {
 
596
            status = PJ_ENOMEM;
 
597
            goto on_error;
 
598
        }
 
599
 
 
600
        /* Get the min buffer size function */
 
601
        bufsize_method = (*jni_env)->GetStaticMethodID(jni_env,
 
602
                                                       stream->record_class,
 
603
                                                       "getMinBufferSize",
 
604
                                                       "(III)I");
 
605
        if (bufsize_method == 0) {
 
606
            PJ_LOG(3, (THIS_FILE, "Unable to find audio record "
 
607
                                  "getMinBufferSize() method"));
 
608
            status = PJMEDIA_EAUD_SYSERR;
 
609
            goto on_error;
 
610
        }
 
611
 
 
612
        inputBuffSizeRec = (*jni_env)->CallStaticIntMethod(jni_env,
 
613
                                                           stream->record_class,
 
614
                                                           bufsize_method,
 
615
                                                           param->clock_rate,
 
616
                                                           channelInCfg,
 
617
                                                           sampleFormat);
 
618
        if (inputBuffSizeRec <= 0) {
 
619
            PJ_LOG(3, (THIS_FILE, "Unsupported audio record params"));
 
620
            status = PJMEDIA_EAUD_INIT;
 
621
            goto on_error;
 
622
        }
 
623
    }
 
624
    
 
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");
 
628
        if (jcl == NULL) {
 
629
            PJ_LOG(3, (THIS_FILE, "Unable to find audio track class"));
 
630
            status = PJMEDIA_EAUD_SYSERR;
 
631
            goto on_error;
 
632
        }
 
633
        stream->track_class = (jclass)(*jni_env)->NewGlobalRef(jni_env, jcl);
 
634
        (*jni_env)->DeleteLocalRef(jni_env, jcl);
 
635
        if (stream->track_class == 0) {
 
636
            status = PJ_ENOMEM;
 
637
            goto on_error;
 
638
        }
 
639
 
 
640
        /* Get the min buffer size function */
 
641
        bufsize_method = (*jni_env)->GetStaticMethodID(jni_env,
 
642
                                                       stream->track_class,
 
643
                                                       "getMinBufferSize",
 
644
                                                       "(III)I");
 
645
        if (bufsize_method == 0) {
 
646
            PJ_LOG(3, (THIS_FILE, "Unable to find audio track "
 
647
                                  "getMinBufferSize() method"));
 
648
            status = PJMEDIA_EAUD_SYSERR;
 
649
            goto on_error;
 
650
        }
 
651
        
 
652
        inputBuffSizePlay = (*jni_env)->CallStaticIntMethod(jni_env,
 
653
                                                            stream->track_class,
 
654
                                                            bufsize_method,
 
655
                                                            param->clock_rate,
 
656
                                                            channelOutCfg,
 
657
                                                            sampleFormat);
 
658
        if (inputBuffSizePlay <= 0) {
 
659
            PJ_LOG(3, (THIS_FILE, "Unsupported audio track params"));
 
660
            status = PJMEDIA_EAUD_INIT;
 
661
            goto on_error;
 
662
        }
 
663
    }
 
664
    
 
665
    if (stream->dir & PJMEDIA_DIR_CAPTURE) {
 
666
        jthrowable exc;
 
667
        int mic_source = 0; /* DEFAULT: default audio source */
 
668
 
 
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;
 
676
            goto on_error;
 
677
        }
 
678
        
 
679
        if (mic_source == 0) {
 
680
            char sdk_version[PROP_VALUE_MAX];
 
681
            pj_str_t pj_sdk_version;
 
682
            int sdk_v;
 
683
 
 
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);
 
687
            if (sdk_v > 10)
 
688
                mic_source = 7; /* VOICE_COMMUNICATION */
 
689
        }
 
690
        PJ_LOG(4, (THIS_FILE, "Using audio input source : %d", mic_source));
 
691
        
 
692
        do {
 
693
            stream->record =  (*jni_env)->NewObject(jni_env,
 
694
                                                    stream->record_class,
 
695
                                                    constructor_method,
 
696
                                                    mic_source, 
 
697
                                                    param->clock_rate,
 
698
                                                    channelInCfg,
 
699
                                                    sampleFormat,
 
700
                                                    inputBuffSizeRec);
 
701
            if (stream->record == 0) {
 
702
                PJ_LOG(3, (THIS_FILE, "Unable to create audio record object"));
 
703
                status = PJMEDIA_EAUD_INIT;
 
704
                goto on_error;
 
705
            }
 
706
        
 
707
            exc = (*jni_env)->ExceptionOccurred(jni_env);
 
708
            if (exc) {
 
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;
 
714
                    goto on_error;
 
715
                }
 
716
                mic_source = 0;
 
717
                PJ_LOG(4, (THIS_FILE, "Trying the default audio source."));
 
718
                continue;
 
719
            }
 
720
 
 
721
            /* Check state */
 
722
            method_id = (*jni_env)->GetMethodID(jni_env, stream->record_class,
 
723
                                                "getState", "()I");
 
724
            if (method_id == 0) {
 
725
                PJ_LOG(3, (THIS_FILE, "Unable to find audio record getState() "
 
726
                                      "method"));
 
727
                status = PJMEDIA_EAUD_SYSERR;
 
728
                goto on_error;
 
729
            }
 
730
            state = (*jni_env)->CallIntMethod(jni_env, stream->record,
 
731
                                              method_id);
 
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;
 
736
                    goto on_error;
 
737
                }
 
738
                mic_source = 0;
 
739
                PJ_LOG(4, (THIS_FILE, "Trying the default audio source."));
 
740
            }
 
741
        } while (state == 0);
 
742
        
 
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;
 
747
            goto on_error;
 
748
        }
 
749
 
 
750
        status = pj_sem_create(stream->pool, NULL, 0, 1, &stream->rec_sem);
 
751
        if (status != PJ_SUCCESS)
 
752
            goto on_error;
 
753
        
 
754
        status = pj_thread_create(stream->pool, "android_recorder",
 
755
                                  AndroidRecorderCallback, stream, 0, 0,
 
756
                                  &stream->rec_thread);
 
757
        if (status != PJ_SUCCESS)
 
758
            goto on_error;
 
759
 
 
760
        PJ_LOG(4, (THIS_FILE, "Audio record initialized successfully."));
 
761
    }
 
762
    
 
763
    if (stream->dir & PJMEDIA_DIR_PLAYBACK) {
 
764
        jthrowable exc;
 
765
        
 
766
        /* Get pointer to the constructor */
 
767
        constructor_method = (*jni_env)->GetMethodID(jni_env,
 
768
                                                     stream->track_class,
 
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;
 
773
            goto on_error;
 
774
        }
 
775
        
 
776
        stream->track = (*jni_env)->NewObject(jni_env,
 
777
                                              stream->track_class,
 
778
                                              constructor_method,
 
779
                                              0, /* STREAM_VOICE_CALL */
 
780
                                              param->clock_rate,
 
781
                                              channelOutCfg,
 
782
                                              sampleFormat,
 
783
                                              inputBuffSizePlay,
 
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;
 
788
            goto on_error;
 
789
        }
 
790
        
 
791
        exc = (*jni_env)->ExceptionOccurred(jni_env);
 
792
        if (exc) {
 
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;
 
797
            goto on_error;
 
798
        }
 
799
        
 
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;
 
804
            goto on_error;
 
805
        }
 
806
        
 
807
        /* Check state */
 
808
        method_id = (*jni_env)->GetMethodID(jni_env, stream->track_class,
 
809
                                            "getState", "()I");
 
810
        if (method_id == 0) {
 
811
            PJ_LOG(3, (THIS_FILE, "Unable to find audio track getState() "
 
812
                                  "method"));
 
813
            status = PJMEDIA_EAUD_SYSERR;
 
814
            goto on_error;
 
815
        }
 
816
        state = (*jni_env)->CallIntMethod(jni_env, stream->track,
 
817
                                          method_id);
 
818
        if (state == 0) { /* STATE_UNINITIALIZED */
 
819
            PJ_LOG(3, (THIS_FILE, "Failure in initializing audio track."));
 
820
            status = PJMEDIA_EAUD_INIT;
 
821
            goto on_error;
 
822
        }
 
823
 
 
824
        status = pj_sem_create(stream->pool, NULL, 0, 1, &stream->play_sem);
 
825
        if (status != PJ_SUCCESS)
 
826
            goto on_error;
 
827
        
 
828
        status = pj_thread_create(stream->pool, "android_track",
 
829
                                  AndroidTrackCallback, stream, 0, 0,
 
830
                                  &stream->play_thread);
 
831
        if (status != PJ_SUCCESS)
 
832
            goto on_error;
 
833
        
 
834
        PJ_LOG(4, (THIS_FILE, "Audio track initialized successfully."));
 
835
    }
 
836
 
 
837
    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
 
838
        strm_set_cap(&stream->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
 
839
                     &param->output_vol);
 
840
    }
 
841
    
 
842
    /* Done */
 
843
    stream->base.op = &android_strm_op;
 
844
    *p_aud_strm = &stream->base;
 
845
    
 
846
    detach_jvm(attached);
 
847
    
 
848
    return PJ_SUCCESS;
 
849
    
 
850
on_error:
 
851
    detach_jvm(attached);
 
852
    strm_destroy(&stream->base);
 
853
    return status;
 
854
}
 
855
 
 
856
/* API: Get stream parameters */
 
857
static pj_status_t strm_get_param(pjmedia_aud_stream *s,
 
858
                                  pjmedia_aud_param *pi)
 
859
{
 
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));
 
863
 
 
864
    if (strm_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
 
865
                     &pi->output_vol) == PJ_SUCCESS)
 
866
    {
 
867
        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
 
868
    }    
 
869
    
 
870
    return PJ_SUCCESS;
 
871
}
 
872
 
 
873
/* API: get capability */
 
874
static pj_status_t strm_get_cap(pjmedia_aud_stream *s,
 
875
                                pjmedia_aud_dev_cap cap,
 
876
                                void *pval)
 
877
{
 
878
    struct android_aud_stream *strm = (struct android_aud_stream*)s;
 
879
    pj_status_t status = PJMEDIA_EAUD_INVCAP;
 
880
    
 
881
    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
 
882
    
 
883
    if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
 
884
        (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
 
885
    {
 
886
    }
 
887
    
 
888
    return status;
 
889
}
 
890
 
 
891
/* API: set capability */
 
892
static pj_status_t strm_set_cap(pjmedia_aud_stream *s,
 
893
                                pjmedia_aud_dev_cap cap,
 
894
                                const void *value)
 
895
{
 
896
    struct android_aud_stream *stream = (struct android_aud_stream*)s;
 
897
    JNIEnv *jni_env = 0;
 
898
    pj_bool_t attached;
 
899
    
 
900
    PJ_ASSERT_RETURN(s && value, PJ_EINVAL);
 
901
    
 
902
    if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
 
903
        (stream->param.dir & PJMEDIA_DIR_PLAYBACK))
 
904
    {
 
905
        if (stream->track) {
 
906
            jmethodID vol_method = 0;
 
907
            int retval;
 
908
            float vol = *(int *)value;
 
909
            
 
910
            attached = attach_jvm(&jni_env);
 
911
            
 
912
            vol_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
 
913
                                                 "setStereoVolume", "(FF)I");
 
914
            if (vol_method) {
 
915
                retval = (*jni_env)->CallIntMethod(jni_env, stream->track,
 
916
                                                   vol_method,
 
917
                                                   vol/100, vol/100);
 
918
            }
 
919
            
 
920
            detach_jvm(attached);
 
921
            
 
922
            if (vol_method && retval == 0)
 
923
                return PJ_SUCCESS;
 
924
        }
 
925
    }
 
926
    
 
927
    return PJMEDIA_EAUD_INVCAP;
 
928
}
 
929
 
 
930
/* API: start stream. */
 
931
static pj_status_t strm_start(pjmedia_aud_stream *s)
 
932
{
 
933
    struct android_aud_stream *stream = (struct android_aud_stream*)s;    
 
934
    
 
935
    if (!stream->running) {
 
936
        stream->running = PJ_TRUE;
 
937
        if (stream->record)
 
938
            pj_sem_post(stream->rec_sem);
 
939
        if (stream->track)
 
940
            pj_sem_post(stream->play_sem);
 
941
    }
 
942
    
 
943
    PJ_LOG(4, (THIS_FILE, "Android JNI stream started"));
 
944
    
 
945
    return PJ_SUCCESS;
 
946
}
 
947
 
 
948
/* API: stop stream. */
 
949
static pj_status_t strm_stop(pjmedia_aud_stream *s)
 
950
{
 
951
    struct android_aud_stream *stream = (struct android_aud_stream*)s;
 
952
 
 
953
    if (!stream->running)
 
954
        return PJ_SUCCESS;
 
955
    
 
956
    stream->running = PJ_FALSE;
 
957
    PJ_LOG(4,(THIS_FILE, "Android JNI stream stopped"));
 
958
    
 
959
    return PJ_SUCCESS;
 
960
}
 
961
 
 
962
/* API: destroy stream. */
 
963
static pj_status_t strm_destroy(pjmedia_aud_stream *s)
 
964
{
 
965
    struct android_aud_stream *stream = (struct android_aud_stream*)s;
 
966
    JNIEnv *jni_env = 0;
 
967
    jmethodID release_method=0;
 
968
    pj_bool_t attached;
 
969
    
 
970
    PJ_LOG(4,(THIS_FILE, "Destroying Android JNI stream..."));
 
971
    
 
972
    stream->quit_flag = PJ_TRUE;
 
973
    
 
974
    /* Stop the stream */
 
975
    strm_stop(s);
 
976
    
 
977
    attached = attach_jvm(&jni_env);
 
978
    
 
979
    if (stream->record){
 
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;
 
985
        }
 
986
        
 
987
        if (stream->rec_sem) {
 
988
            pj_sem_destroy(stream->rec_sem);
 
989
            stream->rec_sem = NULL;
 
990
        }
 
991
        
 
992
        release_method = (*jni_env)->GetMethodID(jni_env, stream->record_class,
 
993
                                                 "release", "()V");
 
994
        (*jni_env)->CallVoidMethod(jni_env, stream->record, release_method);
 
995
        
 
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"));
 
1001
    }
 
1002
    
 
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;
 
1009
        }
 
1010
        
 
1011
        if (stream->play_sem) {
 
1012
            pj_sem_destroy(stream->play_sem);
 
1013
            stream->play_sem = NULL;
 
1014
        }
 
1015
        
 
1016
        release_method = (*jni_env)->GetMethodID(jni_env, stream->track_class,
 
1017
                                                 "release", "()V");
 
1018
        (*jni_env)->CallVoidMethod(jni_env, stream->track, release_method);
 
1019
        
 
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"));
 
1025
    }
 
1026
 
 
1027
    pj_pool_release(stream->pool);
 
1028
    PJ_LOG(4, (THIS_FILE, "Android JNI stream destroyed"));
 
1029
    
 
1030
    detach_jvm(attached);
 
1031
    return PJ_SUCCESS;
 
1032
}
 
1033
 
 
1034
#endif  /* PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI */