/* API: create stream */ static pj_status_t android_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, void *user_data, pjmedia_aud_stream **p_aud_strm) { PJ_LOG(4,(THIS_FILE, "Creating stream")); struct android_aud_factory *pa = (struct android_aud_factory*)f; pj_pool_t *pool; struct android_aud_stream *stream; pj_status_t status; int has_set_in_call = 0; int state = 0; PJ_ASSERT_RETURN(play_cb && rec_cb && p_aud_strm, PJ_EINVAL); // Only supports for mono channel for now PJ_ASSERT_RETURN(param->channel_count == 1, PJ_EINVAL); pool = pj_pool_create(pa->pf, "sndstream", 1024, 1024, NULL); if (!pool) { return PJ_ENOMEM; } stream = PJ_POOL_ZALLOC_T(pool, struct android_aud_stream); stream->pool = pool; pj_strdup2_with_null(pool, &stream->name, "Android stream"); stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; stream->param = *param; stream->user_data = user_data; stream->samples_per_sec = param->clock_rate; stream->samples_per_frame = param->samples_per_frame; stream->bytes_per_sample = param->bits_per_sample / 8; stream->channel_count = param->channel_count; stream->rec_cb = rec_cb; stream->play_cb = play_cb; PJ_LOG(3, (THIS_FILE, "Create stream : %d samples/sec, %d samples/frame, %d bytes/sample", stream->samples_per_sec, stream->samples_per_frame, stream->bytes_per_sample)); /* if(pj_sem_create(pool, NULL, 0, 2, &stream->audio_launch_sem) != PJ_SUCCESS){ pj_pool_release(pool); return PJ_ENOMEM; } */ int inputBuffSize=0, inputBuffSizePlay, inputBuffSizeRec; int sampleFormat; //TODO : return codes should be better JNIEnv *jni_env = 0; ATTACH_JVM(jni_env); jmethodID constructor_method=0, get_min_buffer_size_method = 0, method_id = 0; status = on_setup_audio_wrapper(param->clock_rate); if(status != PJ_SUCCESS){ return PJMEDIA_EAUD_INVOP; } has_set_in_call = 1; /* if (attachResult != 0) { PJ_LOG(1, (THIS_FILE, "Not able to attach the jvm")); pj_pool_release(pool); return PJ_ENOMEM; } */ if (param->bits_per_sample == 8) { sampleFormat = 3; //ENCODING_PCM_8BIT } else if (param->bits_per_sample == 16) { sampleFormat = 2; //ENCODING_PCM_16BIT } else { pj_pool_release(pool); return PJMEDIA_EAUD_SAMPFORMAT; } PJ_LOG(3, (THIS_FILE, "Sample format is : %d for %d ", sampleFormat, param->bits_per_sample)); if (stream->dir & PJMEDIA_DIR_CAPTURE) { //Get pointer to the java class stream->record_class = (jclass)jni_env->NewGlobalRef(jni_env->FindClass("android/media/AudioRecord")); if (stream->record_class == 0) { PJ_LOG(2, (THIS_FILE, "Not able to find audio record class")); goto on_error; } PJ_LOG(3, (THIS_FILE, "We have the class")); //Get the min buffer function get_min_buffer_size_method = jni_env->GetStaticMethodID(stream->record_class, "getMinBufferSize", "(III)I"); if (get_min_buffer_size_method == 0) { PJ_LOG(2, (THIS_FILE, "Not able to find audio record getMinBufferSize method")); goto on_error; } PJ_LOG(3, (THIS_FILE, "We have the buffer method")); //Call it inputBuffSizeRec = jni_env->CallStaticIntMethod(stream->record_class, get_min_buffer_size_method, param->clock_rate, 2, sampleFormat); if(inputBuffSizeRec <= 0){ PJ_LOG(2, (THIS_FILE, "Min buffer size is not a valid value")); goto on_error; } if(inputBuffSizeRec <= 4096){ inputBuffSizeRec = 4096 * 3/2; } int frameSizeInBytes = (param->bits_per_sample == 8) ? 1 : 2; if ( inputBuffSizeRec % frameSizeInBytes != 0 ){ inputBuffSizeRec ++; } PJ_LOG(3, (THIS_FILE, "Min record buffer %d", inputBuffSizeRec)); if(inputBuffSizeRec > inputBuffSize){ inputBuffSize = inputBuffSizeRec; } } if (stream->dir & PJMEDIA_DIR_PLAYBACK) { //Get pointer to the java class stream->track_class = (jclass)jni_env->NewGlobalRef(jni_env->FindClass("android/media/AudioTrack")); if (stream->track_class == 0) { PJ_LOG(2, (THIS_FILE, "Not able to find audio track class")); goto on_error; } PJ_LOG(3, (THIS_FILE, "We have the track class")); //Get the min buffer function get_min_buffer_size_method = jni_env->GetStaticMethodID(stream->track_class, "getMinBufferSize", "(III)I"); if (get_min_buffer_size_method == 0) { PJ_LOG(2, (THIS_FILE, "Not able to find audio record getMinBufferSize method")); goto on_error; } PJ_LOG(3, (THIS_FILE, "We have the buffer method")); //Call it inputBuffSizePlay = jni_env->CallStaticIntMethod(stream->track_class, get_min_buffer_size_method, param->clock_rate, 2, sampleFormat); if(inputBuffSizePlay < 0){ PJ_LOG(2, (THIS_FILE, "Min buffer size is not a valid value")); goto on_error; } //Not sure that's a good idea if(inputBuffSizePlay < 2*2*1024*param->clock_rate/8000){ inputBuffSizePlay = 2*2*1024*param->clock_rate/8000; } int frameSizeInBytes = (param->bits_per_sample == 8) ? 1 : 2; if ( inputBuffSizePlay % frameSizeInBytes != 0 ){ inputBuffSizePlay ++; } //inputBuffSizePlay = inputBuffSizePlay << 1; PJ_LOG(3, (THIS_FILE, "Min play buffer %d", inputBuffSizePlay)); if(inputBuffSizePlay > inputBuffSize){ inputBuffSize = inputBuffSizePlay; } } PJ_LOG(3, (THIS_FILE, "Min buffer %d", inputBuffSize)); if (stream->dir & PJMEDIA_DIR_CAPTURE) { //Get pointer to the constructor constructor_method = jni_env->GetMethodID(stream->record_class,"<init>", "(IIIII)V"); if (constructor_method == 0) { PJ_LOG(2, (THIS_FILE, "Not able to find audio record class constructor")); goto on_error; } int mic_source = on_set_micro_source_wrapper(); if(mic_source == 0){ mic_source = 1; char sdk_version[PROP_VALUE_MAX]; __system_property_get("ro.build.version.sdk", sdk_version); pj_str_t pj_sdk_version = pj_str(sdk_version); int sdk_v = pj_strtoul(&pj_sdk_version); if(sdk_v >= 10){ mic_source = 7; } } PJ_LOG(3, (THIS_FILE, "Use micro source : %d", mic_source)); stream->record = jni_env->NewObject(stream->record_class, constructor_method, mic_source, // Mic input source: 1 = MIC / 7 = VOICE_COMMUNICATION param->clock_rate, 2, // CHANNEL_CONFIGURATION_MONO sampleFormat, inputBuffSizeRec); if (stream->record == 0) { PJ_LOG(1, (THIS_FILE, "Not able to instantiate record class")); goto on_error; } jthrowable exc = jni_env->ExceptionOccurred(); if (exc) { jni_env->ExceptionDescribe(); jni_env->ExceptionClear(); PJ_LOG(2, (THIS_FILE, "The micro source was probably not valid")); // Try to fallback on MIC source -- lazy failure if(mic_source != 1){ PJ_LOG(4, (THIS_FILE, "Try default source")); stream->record = jni_env->NewObject(stream->record_class, constructor_method, 1, // Mic input source: 1 = MIC / 7 = VOICE_COMMUNICATION param->clock_rate, 2, // CHANNEL_CONFIGURATION_MONO sampleFormat, inputBuffSizeRec); if (stream->record == 0) { PJ_LOG(1, (THIS_FILE, "Not able to instantiate record class")); goto on_error; } }else{ PJ_LOG(1, (THIS_FILE, "Not able to instantiate record class")); goto on_error; } } // Check state method_id = jni_env->GetMethodID(stream->record_class,"getState", "()I"); state = jni_env->CallIntMethod(stream->record, method_id); if(state == 0){ /* STATE_UNINITIALIZED */ // Try to fallback on MIC source -- lazy failure if(mic_source != 1){ PJ_LOG(4, (THIS_FILE, "Try default source")); stream->record = jni_env->NewObject(stream->record_class, constructor_method, 1, // Mic input source: 1 = MIC / 7 = VOICE_COMMUNICATION param->clock_rate, 2, // CHANNEL_CONFIGURATION_MONO sampleFormat, inputBuffSizeRec); if (stream->record == 0) { PJ_LOG(1, (THIS_FILE, "Not able to instantiate record class")); goto on_error; } }else{ PJ_LOG(1, (THIS_FILE, "Not able to instantiate record class")); goto on_error; } } stream->record = jni_env->NewGlobalRef(stream->record); PJ_LOG(3, (THIS_FILE, "We have capture the instance done")); } if (stream->dir & PJMEDIA_DIR_PLAYBACK) { //Get pointer to the constructor constructor_method = jni_env->GetMethodID(stream->track_class,"<init>", "(IIIIII)V"); if (constructor_method == 0) { PJ_LOG(2, (THIS_FILE, "Not able to find audio track class constructor")); goto on_error; } stream->track = jni_env->NewObject(stream->track_class, constructor_method, 0, // VOICE_CALL // 3, //MUSIC param->clock_rate, 2, // CHANNEL_CONFIGURATION_MONO sampleFormat, inputBuffSizePlay /**2*/, 1); // MODE_STREAM stream->track = jni_env->NewGlobalRef(stream->track); if (stream->track == 0) { PJ_LOG(1, (THIS_FILE, "Not able to instantiate track class")); goto on_error; } //TODO check if initialized properly PJ_LOG(3, (THIS_FILE, "We have the track instance done")); } //OK, done *p_aud_strm = &stream->base; (*p_aud_strm)->op = &android_strm_op; DETACH_JVM(jni_env); return PJ_SUCCESS; on_error: if(has_set_in_call == 1){ on_teardown_audio_wrapper(); } DETACH_JVM(jni_env); pj_pool_release(pool); return PJ_ENOMEM; }
/* API: Init factory */ static pj_status_t android_init(pjmedia_aud_dev_factory *f) { int mic_source; int state = 0; jthrowable exc; PJ_UNUSED_ARG(f); PJ_LOG(4,(THIS_FILE, "Android sound library initialized")); PJ_LOG(4,(THIS_FILE, "Sound device count=%d", android_get_dev_count(f))); JNIEnv *jni_env = 0; ATTACH_JVM(jni_env); jmethodID constructor_method = 0, method_id = 0; g_record_class = (jclass)jni_env->NewGlobalRef(jni_env->FindClass("android/media/AudioRecord")); if (g_record_class == 0) { PJ_LOG(2, (THIS_FILE, "zzc Not able to find audio record class")); goto on_error; } //Get pointer to the constructor constructor_method = jni_env->GetMethodID(g_record_class,"<init>", "(IIIII)V"); if (constructor_method == 0) { PJ_LOG(2, (THIS_FILE, "zzc Not able to find audio record class constructor")); goto on_error; } mic_source = on_set_micro_source_wrapper(); if(mic_source == 0) { mic_source = 1; char sdk_version[PROP_VALUE_MAX]; __system_property_get("ro.build.version.sdk", sdk_version); pj_str_t pj_sdk_version = pj_str(sdk_version); int sdk_v = pj_strtoul(&pj_sdk_version); if(sdk_v >= 10) { mic_source = 7; } } PJ_LOG(3, (THIS_FILE, "zzc Use micro source : %d", mic_source)); g_audio_record = jni_env->NewObject(g_record_class, constructor_method, 1, // Mic input source: 1 = MIC / 7 = VOICE_COMMUNICATION 16000, //2, // CHANNEL_CONFIGURATION_MONO 16, // lxd CHANNEL_IN_MONO 2, //6144 16000//lxd ); if (g_audio_record == 0) { PJ_LOG(1, (THIS_FILE, "zzc Not able to instantiate record class")); goto on_error; } exc = jni_env->ExceptionOccurred(); if (exc) { jni_env->ExceptionDescribe(); jni_env->ExceptionClear(); PJ_LOG(2, (THIS_FILE, "zzc The micro source was probably not valid")); // Try to fallback on MIC source -- lazy failure if(mic_source != 1) { PJ_LOG(4, (THIS_FILE, "zzc Try default source")); g_audio_record = jni_env->NewObject(g_record_class, constructor_method, 1, // Mic input source: 1 = MIC / 7 = VOICE_COMMUNICATION 16000, //2, // CHANNEL_CONFIGURATION_MONO 16, // lxd CHANNEL_IN_MONO 2, //6144 16000//lxd ); if (g_audio_record == 0) { PJ_LOG(1, (THIS_FILE, "zzc Not able to instantiate record class")); goto on_error; } } else { PJ_LOG(1, (THIS_FILE, "zzc Not able to instantiate record class")); goto on_error; } } // Check state method_id = jni_env->GetMethodID(g_record_class,"getState", "()I"); state = jni_env->CallIntMethod(g_audio_record, method_id); if(state == 0){ /* STATE_UNINITIALIZED */ // Try to fallback on MIC source -- lazy failure if(mic_source != 1){ PJ_LOG(4, (THIS_FILE, "Try default source")); g_audio_record = jni_env->NewObject(g_record_class, constructor_method, 1, // Mic input source: 1 = MIC / 7 = VOICE_COMMUNICATION 16000, //2, // CHANNEL_CONFIGURATION_MONO 16, // lxd CHANNEL_IN_MONO 2, //6144 16000//lxd ); if (g_audio_record == 0) { PJ_LOG(1, (THIS_FILE, "Not able to instantiate record class")); goto on_error; } }else{ PJ_LOG(1, (THIS_FILE, "Not able to instantiate record class")); goto on_error; } } g_audio_record = jni_env->NewGlobalRef(g_audio_record); return PJ_SUCCESS; on_error: on_teardown_audio_wrapper(); DETACH_JVM(jni_env); return PJMEDIA_ESNDINDEVID; }