Esempio n. 1
0
/* 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;
}