Commit 73f3e258 authored by Simon Morlat's avatar Simon Morlat
Browse files

Add default settings for many android phones, so that calibration is not...

Add default settings for many android phones, so that calibration is not required on most common models.
Improve latency (again) of sound module.
parent 756a5141
......@@ -93,6 +93,7 @@ LOCAL_SRC_FILES = \
audiofilters/msresample.c \
android/androidsound_depr.cpp \
android/loader.cpp \
android/android_echo.cpp \
android/androidsound.cpp \
android/AudioRecord.cpp \
android/AudioTrack.cpp \
......
......@@ -149,6 +149,7 @@ struct _AudioStream
bool_t eq_active;
bool_t use_ng;/*noise gate*/
bool_t is_beginning;
bool_t is_ec_delay_set;
};
/**
......
......@@ -39,6 +39,7 @@ public final class Hacks {
private Hacks() {}
private static BuiltInEchoCancellerModel[] mBuiltInEchoCancellerModels = new BuiltInEchoCancellerModel[] {
//Deprecated: fill table in mediastreamer2/src/android/android_echo.cpp instead.
new BuiltInEchoCancellerModel("samsung", "GT-I9100"), // Samsung Galaxy SII
new BuiltInEchoCancellerModel("samsung", "GT-I9300"), // Samsung Galaxy SIII
};
......
......@@ -16,6 +16,7 @@ ANDROID_SRC_FILES= \
android/android-opengl-display.c \
android/audio.h \
android/loader.cpp android/loader.h \
android/android_echo.cpp android/android_echo.h \
audiofilters/webrtc_aec.c
EXTRA_DIST= audiofilters/winsnd2.c audiofilters/winsnd.c videofilters/winvideo.c \
......
/*
* android_echo.h -Android echo cancellation utilities.
*
* Copyright (C) 2009-2012 Belledonne Communications, Grenoble, France
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <jni.h>
#include "android_echo.h"
#include "sys/system_properties.h"
struct EcDescription{
const char *manufacturer;
const char *model;
const char *platform;
int has_builtin_ec;
int delay;
};
static EcDescription ec_table[]={
{ "HTC", "Nexus One", "qsd8k", FALSE, 300 },
{ "HTC", "HTC One X", "tegra", FALSE, 150 }, //has a very good acoustic isolation, which result in calibration saying no echo.
//But with speaker mode there is a strong echo if software ec is disabled.
{ "HTC", "HTC Desire", "", FALSE, 250 },
{ "HTC", "HTC Sensation Z710e", "", FALSE, 200 },
{ "HTC", "HTC Wildfire", "", FALSE, 270 },
{ "LGE", "Nexus 4", "msm8960", FALSE, 230 },
{ "LGE", "LS670", "", FALSE, 170 },
{ "motorola", "DROID RAZR", "", FALSE, 400 },
{ "motorola", "MB860", "", FALSE, 200 },
{ "motorola", "XT907", "", FALSE, 500 },
{ "samsung", "GT-S5360", "bcm21553", FALSE, 250 }, /*<Galaxy Y*/
{ "samsung", "GT-S5360L", "", FALSE, 250 }, /*<Galaxy Y*/
{ "samsung", "GT-S6102", "", TRUE, 0 }, /*<Galaxy Y duo*/
{ "samsung", "GT-S5570", "", FALSE, 160 }, /*<Galaxy Y duo*/
{ "samsung", "GT-S5300", "", TRUE, 0 }, /*<Galaxy Pocket*/
{ "samsung", "GT-S5830i", "", FALSE, 200 }, /* Galaxy S */
{ "samsung", "GT-S5830", "", FALSE, 170 }, /* Galaxy S */
{ "samsung", "GT-S5660", "", FALSE, 160 }, /* Galaxy Gio */
{ "samsung", "GT-I9000", "", FALSE, 200 }, /* Galaxy S */
{ "samsung", "GT-I9001", "", FALSE, 150 }, /* Galaxy S+ */
{ "samsung", "GT-I9070", "", TRUE, 0 }, /* Galaxy S Advance */
{ "samsung", "SPH-D700", "", FALSE, 200 }, /* Galaxy S Epic 4G*/
{ "samsung", "GT-I9100", "", TRUE, 0 }, /*Galaxy S2*/
{ "samsung", "GT-S7562", "", TRUE, 0 }, /*<Galaxy S Duo*/
{ "samsung", "SCH-I415", "", TRUE, 0 }, /* Galaxy S ??*/
{ "samsung", "SCH-I425", "", TRUE, 0 }, /* Galaxy S ??*/
{ "samsung", "SCH-I535", "", TRUE, 0 }, /* Galaxy S ??*/
{ "samsung", "SPH-D710", "", TRUE, 0 }, /* Galaxy S2 Epic 4G*/
{ "samsung", "GT-I9300", "exynos4", TRUE, 0 }, /*Galaxy S3*/
{ "samsung", "SAMSUNG-SGH-I747","", TRUE, 0 }, /* Galaxy S3*/
{ "samsung", "SPH-L710","", TRUE, 0 }, /* Galaxy S3*/
{ "samsung", "SPH-D710","", TRUE, 0 }, /* Galaxy S3*/
{ "samsung", "SGH-T999", "", TRUE, 0 }, /*Galaxy S3*/
{ "samsung", "GT-I8190", "", TRUE, 0 }, /*Galaxy S3*/
{ "samsung", "SAMSUNG-SGH-I337","", TRUE, 0 }, /* Galaxy S4 ? */
{ "samsung", "GT-N7000", "", TRUE, 0 }, /*Galaxy Note*/
{ "samsung", "GT-N7100", "", TRUE, 0 }, /*Galaxy Note 2*/
{ "samsung", "GT-N7105", "", TRUE, 0 }, /*Galaxy Note 2*/
{ "samsung", "SGH-T889", "", TRUE, 0 }, /*Galaxy Note 2*/
{ "samsung", "Nexus S", "s5pc110", FALSE, 200 },
{ "samsung", "Galaxy Nexus", "", FALSE, 120 },
{ "samsung", "GT-S5570I", "", FALSE, 250},
{ "samsung", "GT-P3100", "", TRUE, 0 }, /* Galaxy Tab*/
{ "samsung", "GT-P7500", "", TRUE, 0 }, /* Galaxy Tab*/
{ "samsung", "GT-P7510", "", TRUE, 0 }, /* Galaxy Tab*/
{ "samsung", "GT-I915", "", TRUE, 0 }, /* Verizon Tab*/
{ "Sony Ericsson","SK17i", "", FALSE, 140 },
{ "Sony Ericsson","ST17i", "", FALSE, 130 },
{ "Sony Ericsson","ST18i", "", FALSE, 140 },
{ "Sony Ericsson","ST25i", "", FALSE, 320 },
{ "Sony Ericsson","ST27i", "", FALSE, 320 },
{ "Sony Ericsson","LT15i", "", FALSE, 150 },
{ "Sony Ericsson","LT18i", "", FALSE, 150 },
{ "Sony Ericsson","LT26i", "", FALSE, 230 },
{ "Sony Ericsson","LT26ii", "", FALSE, 230 },
{ "Sony Ericsson","LT28h", "", FALSE, 210 },
{ "Sony Ericsson","MT11i", "", FALSE, 150 },
{ "Sony Ericsson","MT15i", "", FALSE, 150 },
{ "asus", "Nexus 7", "", FALSE, 170 },
{ NULL, NULL, NULL, FALSE, 0}
};
static EcDescription *lookup_by_model(const char *manufacturer, const char* model){
int i;
for(i=0;ec_table[i].manufacturer!=NULL;i++){
EcDescription *d=&ec_table[i];
if (strcasecmp(d->manufacturer,manufacturer)==0 && strcmp(d->model,model)==0){
return d;
}
}
return NULL;
}
static EcDescription *lookup_by_platform(const char *platform){
int i;
for(i=0;ec_table[i].manufacturer!=NULL;i++){
EcDescription *d=&ec_table[i];
if (strcmp(d->platform,platform)==0){
return d;
}
}
return NULL;
}
int android_sound_get_echo_params(EchoCancellerParams *params){
EcDescription *d;
char manufacturer[PROP_VALUE_MAX]={0};
char model[PROP_VALUE_MAX]={0};
char platform[PROP_VALUE_MAX]={0};
if (__system_property_get("ro.product.manufacturer",manufacturer)<=0){
ms_warning("Could not get product manufacturer.");
return -1;
}
if (__system_property_get("ro.product.model",model)<=0){
ms_warning("Could not get product model.");
return -1;
}
if (__system_property_get("ro.board.platform",platform)<=0){
ms_warning("Could not get board platform.");
}
/* First ask android if the device has an hardware echo canceller (only >=4.2)*/
{
JNIEnv *env=ms_get_jni_env();
jclass aecClass = env->FindClass("android/media/audiofx/AcousticEchoCanceler");
if (aecClass!=NULL){
aecClass= (jclass)env->NewGlobalRef(aecClass);
jmethodID isAvailableID = env->GetStaticMethodID(aecClass,"isAvailable","()Z");
if (isAvailableID!=NULL){
jboolean ret=env->CallStaticBooleanMethod(aecClass,isAvailableID);
if (ret){
ms_message("This device (%s/%s/%s) has a built-in echo canceller.",manufacturer,model,platform);
params->has_builtin_ec=TRUE;
params->delay=0;
env->DeleteGlobalRef(aecClass);
return 0;
}else ms_message("This device (%s/%s/%s) says it has no built-in echo canceller.",manufacturer,model,platform);
}else{
ms_error("isAvailable() not found in class AcousticEchoCanceler !");
env->ExceptionClear(); //very important.
}
env->DeleteGlobalRef(aecClass);
}else{
env->ExceptionClear(); //very important.
}
}
d=lookup_by_model(manufacturer,model);
if (!d){
ms_warning("Lookup by model (%s/%s) failed.",manufacturer,model);
d=lookup_by_platform(platform);
if (!d){
ms_warning("Lookup by platform (%s) also failed.",platform);
return -1;
}
}
ms_message("Found echo cancellation information for %s/%s/%s: builtin=%s, delay=%i ms",
manufacturer,model,platform,d->has_builtin_ec ? "yes" : "no", d->delay);
params->has_builtin_ec=d->has_builtin_ec;
params->delay=d->delay;
return 0;
}
/*
* android_echo.h -Android echo cancellation utilities.
*
* Copyright (C) 2009-2012 Belledonne Communications, Grenoble, France
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef android_echo_h
#define android_echo_h
#include <mediastreamer2/mscommon.h>
struct EchoCancellerParams{
int has_builtin_ec;
int delay;
};
int android_sound_get_echo_params(EchoCancellerParams *params);
#endif
......@@ -26,6 +26,8 @@
#include "AudioRecord.h"
#include "String8.h"
#include "android_echo.h"
#define NATIVE_USE_HARDWARE_RATE 1
//#define TRACE_SND_WRITE_TIMINGS
......@@ -40,8 +42,8 @@ static MSFilter * ms_android_snd_write_new(void);
static Library *libmedia=0;
static Library *libutils=0;
static const int flowControlIntervalMs = 2500;
static const int flowControlThresholdMs = 80;
static const int flowControlIntervalMs = 1000;
static const int flowControlThresholdMs = 40;
static int std_sample_rates[]={
48000,44100,32000,22050,16000,8000,-1
......@@ -254,9 +256,16 @@ MSSndCardDesc android_native_snd_card_desc={
static MSSndCard * android_snd_card_new(void)
{
MSSndCard * obj;
EchoCancellerParams params;
obj=ms_snd_card_new(&android_native_snd_card_desc);
obj->name=ms_strdup("android sound card");
obj->data=new AndroidNativeSndCardData();
if (android_sound_get_echo_params(&params)==0){
if (params.has_builtin_ec) obj->capabilities|=MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER;
else obj->latency=params.delay;
}
return obj;
}
......@@ -314,6 +323,7 @@ static void android_snd_read_preprocess(MSFilter *obj){
ad->mFilter=obj;
ad->read_samples=0;
ad->started=FALSE;
ad->audio_source=AUDIO_SOURCE_VOICE_COMMUNICATION;
for(int i=0;i<2;i++){
ad->rec=new AudioRecord(ad->audio_source,
......@@ -336,7 +346,6 @@ static void android_snd_read_preprocess(MSFilter *obj){
}
if (ad->rec != 0) {
ad->started=true;
ad->rec->start();
}
......@@ -369,10 +378,12 @@ static void android_snd_read_process(MSFilter *obj){
AndroidSndReadData *ad=(AndroidSndReadData*)obj->data;
mblk_t *om;
ms_mutex_lock(&ad->mutex);
if ((ad->rec == 0) || !ad->started) {
if (ad->rec == 0 ) {
ms_mutex_unlock(&ad->mutex);
return;
}
if (!ad->started)
ad->started=TRUE; //so that the callback can now start to queue buffers.
while ((om=getq(&ad->q))!=NULL) {
//ms_message("android_snd_read_process: Outputing %i bytes",msgdsize(om));
......@@ -536,6 +547,9 @@ static void android_snd_write_cb(int event, void *user, void * p_info){
if (info->size > 0){
ms_bufferizer_read(&ad->bf,(uint8_t*)info->raw,info->size);
info->frameCount = info->size / 2;
}else{
/* we have an underrun (no more samples to deliver to the callback). We need to reset minBufferFilling*/
ad->minBufferFilling=-1;
}
ms_mutex_unlock(&ad->mutex);
ad->nbufs++;
......@@ -637,9 +651,11 @@ static void android_snd_write_process(MSFilter *obj){
#endif
if (((uint32_t)(obj->ticker->time - ad->flowControlStart)) >= flowControlIntervalMs) {
int threshold = (flowControlThresholdMs * ad->nchannels * 2 * ad->rate) / 1000;
//ms_message("Time to flow control: minBufferFilling=%i, threshold=%i",ad->minBufferFilling, threshold);
if (ad->minBufferFilling > threshold) {
ms_warning("Too many samples waiting in sound writer, dropping %i bytes", threshold);
ms_bufferizer_skip_bytes(&ad->bf, threshold);
int drop=ad->minBufferFilling - threshold;
ms_warning("Too many samples waiting in sound writer, dropping %i bytes", drop);
ms_bufferizer_skip_bytes(&ad->bf, drop);
}
ad->flowControlStart = obj->ticker->time;
ad->minBufferFilling = -1;
......
......@@ -412,6 +412,8 @@ static void video_capture_detect(MSWebCamManager *obj){
ms_message("Detecting Android VIDEO cards");
JNIEnv *env = ms_get_jni_env();
jclass helperClass = getHelperClassGlobalRef(env);
if (helperClass==NULL) return;
// create 3 int arrays - assuming 2 webcams at most
jintArray indexes = (jintArray)env->NewIntArray(2);
......@@ -552,6 +554,7 @@ static void compute_cropping_offsets(MSVideoSize hwSize, MSVideoSize outputSize,
static jclass getHelperClassGlobalRef(JNIEnv *env) {
ms_message("getHelperClassGlobalRef (env: %p)", env);
// FindClass only returns local references.
if (android_sdk_version >= 9) {
jclass c = env->FindClass(AndroidApi9WrapperPath);
......
......@@ -31,14 +31,22 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define M_PI 3.14159265358979323846
#endif
static const float energy_min=500;
#define MAX_SCANS 10
static const float energy_min_threshold=0.01;
typedef struct _GoertzelState{
uint64_t starttime;
int dur;
float coef;
bool_t event_sent;
bool_t pad[3];
}GoertzelState;
static void goertzel_state_init(GoertzelState *gs, int frequency, int sampling_frequency){
gs->coef=(float)2*(float)cos(2*M_PI*((float)frequency/(float)sampling_frequency));
gs->starttime=0;
gs->dur=0;
}
static float goertzel_state_run(GoertzelState *gs,int16_t *samples, int nsamples, float total_energy){
......@@ -70,15 +78,13 @@ static float compute_energy(int16_t *samples, int nsamples){
}
typedef struct _DetectorState{
MSToneDetectorDef tone_def;
GoertzelState tone_gs;
uint64_t starttime;
MSToneDetectorDef tone_def[MAX_SCANS];
GoertzelState tone_gs[MAX_SCANS];
int nscans;
MSBufferizer *buf;
int dur;
int rate;
int framesize;
int frame_ms;
bool_t event_sent;
}DetectorState;
static void detector_init(MSFilter *f){
......@@ -96,17 +102,32 @@ static void detector_uninit(MSFilter *f){
ms_free(f->data);
}
static int find_free_slot(DetectorState *s){
int i;
for(i=0;i<MAX_SCANS;++i){
if (s->tone_def[i].frequency==0) return i;
}
ms_error("No more free tone detector scans allowed, maximum reached.");
return -1;
}
static int detector_add_scan(MSFilter *f, void *arg){
DetectorState *s=(DetectorState *)f->data;
MSToneDetectorDef *def=(MSToneDetectorDef*)arg;
s->tone_def=*def;
goertzel_state_init(&s->tone_gs,def->frequency,s->rate);
return 0;
int i=find_free_slot(s);
if (i!=-1){
s->tone_def[i]=*def;
s->nscans++;
goertzel_state_init(&s->tone_gs[i],def->frequency,s->rate);
return 0;
}
return -1;
}
static int detector_clear_scans(MSFilter *f, void *arg){
DetectorState *s=(DetectorState *)f->data;
memset(&s->tone_def,0,sizeof(s->tone_def));
s->nscans=0;
return 0;
}
......@@ -116,9 +137,13 @@ static int detector_set_rate(MSFilter *f, void *arg){
return 0;
}
static void end_tone(DetectorState *s){
s->dur=0;
s->event_sent=FALSE;
static void end_all_tones(DetectorState *s){
int i;
for(i=0;i<s->nscans;++i){
GoertzelState *gs=&s->tone_gs[i];
gs->dur=0;
gs->event_sent=FALSE;
}
}
static void detector_process(MSFilter *f){
......@@ -127,30 +152,39 @@ static void detector_process(MSFilter *f){
while ((m=ms_queue_get(f->inputs[0]))!=NULL){
ms_queue_put(f->outputs[0],m);
if (s->tone_def.frequency!=0){
if (s->nscans>0){
ms_bufferizer_put(s->buf,dupmsg(m));
}
}
if (s->tone_def.frequency!=0){
if (s->nscans>0){
uint8_t *buf=alloca(s->framesize);
while(ms_bufferizer_read(s->buf,buf,s->framesize)!=0){
float en=compute_energy((int16_t*)buf,s->framesize/2);
if (en>energy_min){
float freq_en=goertzel_state_run(&s->tone_gs,(int16_t*)buf,s->framesize/2,en);
if (freq_en>=s->tone_def.min_amplitude){
if (s->dur==0) s->starttime=f->ticker->time;
s->dur+=s->frame_ms;
if (s->dur>s->tone_def.min_duration && !s->event_sent){
MSToneDetectorEvent event;
strncpy(event.tone_name,s->tone_def.tone_name,sizeof(event.tone_name));
event.tone_start_time=s->starttime;
ms_filter_notify(f,MS_TONE_DETECTOR_EVENT,&event);
s->event_sent=TRUE;
if (en>energy_min_threshold*(32767.0*32767.0*0.7)){
int i;
for(i=0;i<s->nscans;++i){
GoertzelState *gs=&s->tone_gs[i];
MSToneDetectorDef *tone_def=&s->tone_def[i];
float freq_en=goertzel_state_run(gs,(int16_t*)buf,s->framesize/2,en);
if (freq_en>=tone_def->min_amplitude){
if (gs->dur==0) gs->starttime=f->ticker->time;
gs->dur+=s->frame_ms;
if (gs->dur>=tone_def->min_duration && !gs->event_sent){
MSToneDetectorEvent event;
strncpy(event.tone_name,tone_def->tone_name,sizeof(event.tone_name));
event.tone_start_time=gs->starttime;
ms_filter_notify(f,MS_TONE_DETECTOR_EVENT,&event);
gs->event_sent=TRUE;
}
}else{
gs->event_sent=FALSE;
gs->dur=0;
gs->starttime=0;
}
}else end_tone(s);
}else end_tone(s);
}
}else end_all_tones(s);
}
}
}
......@@ -159,7 +193,7 @@ static MSFilterMethod detector_methods[]={
{ MS_TONE_DETECTOR_ADD_SCAN, detector_add_scan },
{ MS_TONE_DETECTOR_CLEAR_SCANS, detector_clear_scans },
{ MS_FILTER_SET_SAMPLE_RATE, detector_set_rate },
{ 0 , NULL }
{ 0 , NULL}
};
#ifndef _MSC_VER
......
......@@ -362,10 +362,12 @@ int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char
stream->ec=NULL;
}
if (stream->ec){
int delay_ms=ms_snd_card_get_minimal_latency(captcard);
if (delay_ms!=0){
ms_message("Overriding echo canceller delay with value provided by soundcard: %i ms",delay_ms);
ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
if (!stream->is_ec_delay_set){
int delay_ms=ms_snd_card_get_minimal_latency(captcard);
if (delay_ms!=0){
ms_message("Setting echo canceller delay with value provided by soundcard: %i ms",delay_ms);
ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
}
}
ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
}
......@@ -685,12 +687,13 @@ void audio_stream_set_relay_session_id(AudioStream *stream, const char *id){
void audio_stream_set_echo_canceller_params(AudioStream *stream, int tail_len_ms, int delay_ms, int framesize){
if (stream->ec){
if (tail_len_ms!=0)
if (tail_len_ms>0)
ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_TAIL_LENGTH,&tail_len_ms);
if (delay_ms!=0){
if (delay_ms>0){
stream->is_ec_delay_set=TRUE;
ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&delay_ms);
}
if (framesize!=0)
if (framesize>0)
ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_FRAMESIZE,&framesize);
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment