Linphone錄音器的初始化流程分析

初始化入口:

linphone_core_init() ——linphonecore.c 1793

static void linphone_core_init(LinphoneCore * lc, const LinphoneCoreVTable *vtable, LpConfig *config, void * userdata){
   const char *remote_provisioning_uri = NULL;
   linphone_task_list_init(&lc->hooks);
   _linphone_core_add_listener(lc, internal_vtable, TRUE, TRUE);
   memcpy(local_vtable,vtable,sizeof(LinphoneCoreVTable));
   _linphone_core_add_listener(lc, local_vtable, TRUE, FALSE);
   lc->factory = ms_factory_new_with_voip();
   linphone_core_register_default_codecs(lc);
   linphone_core_register_offer_answer_providers(lc);
   /* Get the mediastreamer2 event queue */
   /* This allows to run event's callback in linphone_core_iterate() */
   lc->msevq=ms_factory_create_event_queue(lc->factory);
   lc->sal=sal_init(lc->factory);
}

媒體庫初始化在ms_factory_new_with_voip() —— msvoip.c 354

MSFactory *ms_factory_new_with_voip(void){
    ms_message("ms_factory_new_with_voip");
   MSFactory *f = ms_factory_new();
   ms_factory_init_voip(f);
   ms_factory_init_plugins(f);
   return f;
}

第一步、ms_factory_new()新建一個MSFactory對象
第二步、ms_factory_init_voip(),初始化voip;
第三步、ms_factory_init_plugins();初始化插件;

先分析第一步,ms_factory_new()只是實例化一個MSFactory結構體 位於msfactory.c line223


MSFactory *ms_factory_new(void){
   MSFactory *obj=ms_new0(MSFactory,1);
   ms_factory_init(obj);
   return obj;
}

ms_factory_init(); ——msfactory.c 144

void ms_factory_init(MSFactory *obj){
    int i;
    long num_cpu=1;
    char *debug_log_enabled = NULL;
    char *tags;
    ...
    ms_message("Mediastreamer2 factory " MEDIASTREAMER_VERSION " (git: " GIT_VERSION ") initialized.");
    /* register builtin MSFilter's */
     for (i=0;ms_base_filter_descs[i]!=NULL;i++){
             ms_factory_register_filter(obj,ms_base_filter_descs[i]);
    }
    ms_factory_set_cpu_count(obj,num_cpu);
    ms_factory_set_mtu(obj,MS_MTU_DEFAULT);
    #ifdef ANDROID
        ms_factory_add_platform_tag(obj, "android");
    #endif
    #ifdef TARGET_OS_IPHONE
         ms_factory_add_platform_tag(obj, "ios");
    #endif
    ...
    tags = ms_factory_get_platform_tags_as_string(obj);
    ms_message("ms_factory_init() done: platform_tags=%s", tags);
    ms_free(tags);
}

主要是設置MSFactory*的一些屬性,重點在中間的for循環。
其中的ms_base_filter_descs[]定義在basedesc.h中,

extern MSFilterDesc ms_tee_desc;
extern MSFilterDesc ms_join_desc;
extern MSFilterDesc ms_void_source_desc;
extern MSFilterDesc ms_void_sink_desc;
MSFilterDesc * ms_base_filter_descs[]={
&ms_tee_desc,
&ms_join_desc,
&ms_void_source_desc,
&ms_void_sink_desc,
NULL
};

而ms_factory_register_filter()在msfactory.c line 230

void ms_factory_register_filter(MSFactory* factory, MSFilterDesc* desc ) {
    if (desc->id==MS_FILTER_NOT_SET_ID){
             ms_fatal("MSFilterId for %s not set !",desc->name);
     }
    desc->flags|=MS_FILTER_IS_ENABLED; /*by default a registered filter is enabled*/

    /*lastly registered encoder/decoders may replace older ones*/
    factory->desc_list=bctbx_list_prepend(factory->desc_list,desc);
}

將整個數組中定義的desc設置到factory->desc_list;

再看第二步ms_factory_init_voip(MSFactory *) 在msvoip.c line260

void ms_factory_init_voip(MSFactory *obj){
    MSSndCardManager *cm;
    int i;

    if (obj->voip_initd) return;

    ms_srtp_init();
    obj->devices_info = ms_devices_info_new();
        ......
    #if defined(ANDROID) && defined(VIDEO_ENABLED)
        if (AMediaImage_isAvailable()) {
                ms_factory_register_filter(obj, &ms_mediacodec_h264_dec_desc);
                ms_factory_register_filter(obj, &ms_mediacodec_h264_enc_desc);
         }
    #endif

    /* register builtin VoIP MSFilter's */
    for (i=0;ms_voip_filter_descs[i]!=NULL;i++){
             ms_factory_register_filter(obj,ms_voip_filter_descs[i]);
    }

    cm=ms_snd_card_manager_new();
    ms_message("Registering all soundcard handlers");
    cm->factory=obj;
    obj->sndcardmanager = cm;
    for (i=0;ms_snd_card_descs[i]!=NULL;i++){
             ms_snd_card_manager_register_desc(cm,ms_snd_card_descs[i]);
    }
    ...
    #if defined(ANDROID) && defined (VIDEO_ENABLED)
     {
            MSDevicesInfo *devices = ms_factory_get_devices_info(obj);
            SoundDeviceDescription *description = ms_devices_info_get_sound_device_description(devices);
            if (description && description->flags & DEVICE_HAS_CRAPPY_OPENGL) {
                if (!libmsandroiddisplay_init(obj)) {
                     libmsandroiddisplaybad_init(obj);
                }
            } else {
                libmsandroidopengldisplay_init(obj);        
            }
    }
    #endif
     obj->voip_initd=TRUE;
    obj->voip_uninit_func = ms_factory_uninit_voip;
     ms_message("ms_factory_init_voip() done");
}

初始化有個重要的操作,構建一個MSSndCardManager對象,然後將系統允許的集中聲卡註冊進去;
其中ms_snd_card_manager_new() ,在mssndcard.c line27

MSSndCardManager * ms_snd_card_manager_new(void){
        MSSndCardManager *obj=(MSSndCardManager *)ms_new0(MSSndCardManager,1);
        obj->factory = NULL;
        obj->cards=NULL;
        obj->descs=NULL;
        return obj;
}

只是初始化一個MSSndCardManager結構體,並與外部傳入的MSFactory互相關聯屬性;
ms_snd_card_descs[]是預先定義好的

static MSSndCardDesc * ms_snd_card_descs[]={
    #ifdef MS2_FILTERS
    ...
    #ifdef __ALSA_ENABLED__
        &alsa_card_desc,
    #endif
    ...
    #ifdef ANDROID
        &android_native_snd_card_desc,
        &android_native_snd_opensles_card_desc,
        &msandroid_sound_card_desc,
    #endif
    #endif /* MS2_FILTERS */
    NULL
};

對於Android 平臺 一共定義了3中聲卡模型,
其中的android_native_snd_card_desc定義在 androidsound.cpp中

MSSndCardDesc android_native_snd_card_desc={
        "libmedia",                         //char* drive_type
         android_snd_card_detect,       //detect
        android_native_snd_card_init,   //init
        NULL,                           //set_level
        NULL,                           //get_level
        NULL,                           //set_capture
         NULL,                          //set_control
        NULL,                           //get_control
        android_snd_card_create_reader,//create_reader  
        android_snd_card_create_writer,//create_writer
        android_native_snd_card_uninit  //duplicate
};

android_native_snd_opensles_card_desc 定義在androidsound_opensles.cpp中

MSSndCardDesc android_native_snd_opensles_card_desc = {
    "openSLES",
    android_snd_card_detect,
    android_native_snd_card_init,
    NULL,   
    NULL,
     NULL,
    NULL,
     NULL,
    android_snd_card_create_reader,
    android_snd_card_create_writer,
    android_native_snd_card_uninit
};

msandroid_sound_card_desc 定義在 androidsound_depr.cpp中

MSSndCardDesc msandroid_sound_card_desc = {
/*.driver_type=*/"ANDROID SND (deprecated)",
/*.detect=*/ msandroid_sound_detect,
/*.init=*/msandroid_sound_init,
/*.set_level=*/msandroid_sound_set_level,
/*.get_level=*/msandroid_sound_get_level,
/*.set_capture=*/msandroid_sound_set_source,    
/*.set_control=*/NULL,  
/*.get_control=*/NULL,
/*.create_reader=*/msandroid_sound_read_new,
/*.create_writer=*/msandroid_sound_write_new,
/*.uninit=*/msandroid_sound_uninit,
/*.duplicate=*/msandroid_sound_duplicate
};

for循環內,ms_snd_card_manager_register_desc(cm,ms_snd_card_descs[i]);在mssndcard.c line 142;

void ms_snd_card_manager_register_desc(MSSndCardManager *m, MSSndCardDesc *desc){
         if (bctbx_list_find(m->descs, desc) == NULL){
                m->descs=bctbx_list_append(m->descs,desc);
                card_detect(m,desc);
        }
}

遍歷每個ms_snd_card_descs中的desc,並執行card_detect(m,desc);

static void card_detect(MSSndCardManager *m, MSSndCardDesc *desc){
        if (desc->detect!=NULL)
            desc->detect(m);
}

只是執行desc->detect(m);
已知Android平臺有三種聲卡,所以這裏會執行每個聲卡描述的detect函數;

對於android_native_snd_card_desc,在執行desc->detect(m)的時候,實質上就是執行了android_native_snd_card_desc中定義的的detect函數
android_snd_card_detect(m),在androidsound.cpp中.

static void android_snd_card_detect(MSSndCardManager *m) {
   ms_message("android_snd_card_detect");
   bool audio_record_loaded = false;
   bool audio_track_loaded = false;
   bool audio_system_loaded = false;
   bool string8_loaded = false;
   bool refbase_loaded = false;

   SoundDeviceDescription *d = NULL;
   MSDevicesInfo *devices = NULL;

   if (get_sdk_version() > 19) {
      /*it is actually working well on android 5 on Nexus 4 but crashes on Samsung S5, due to, maybe
       * calling convention of C++ method being different. Arguments received by AudioTrack constructor do not match the arguments
       * sent by the caller (TransferType maps to uid argument!).
       * Until we find a rational explanation to this, the native module is disabled on Android 5.
      **/
      ms_message("Native android sound support not tested on SDK [%i], disabled.",get_sdk_version());
      return;
   }

   devices = ms_factory_get_devices_info(m->factory);
   d = ms_devices_info_get_sound_device_description(devices);
   if (d->flags & DEVICE_HAS_UNSTANDARD_LIBMEDIA){
      ms_message("Native android sound support is blacklisted for this device.");
      return;
   }

   /* libmedia and libutils static variable may survive to Linphone restarts
    It is then necessary to perform the *::init() calls even if the libmedia and libutils are there.*/
   if (!libmedia) libmedia=Library::load("/system/lib/libmedia.so");
   if (!libutils) libutils=Library::load("/system/lib/libutils.so");

   if (libmedia && libutils) {
      /*perform initializations in order rather than in a if statement so that all missing symbols are shown in logs*/
      string8_loaded = String8Impl::init(libutils);
      refbase_loaded = RefBaseImpl::init(libutils);
      audio_record_loaded = AudioRecordImpl::init(libmedia);
      audio_track_loaded = AudioTrackImpl::init(libmedia);
      audio_system_loaded = AudioSystemImpl::init(libmedia);
   }
   if (audio_record_loaded && audio_track_loaded && audio_system_loaded && string8_loaded && refbase_loaded) {
      ms_message("Native android sound support available.");
      MSSndCard* card = android_snd_card_new(d);
      ms_snd_card_set_manager(m, card);
      ms_snd_card_manager_add_card(m, card);
      return;
   }
   ms_message("Native android sound support is NOT available.");
}
  • 首先會判斷系統sdk版本,如果19以上的android系統,直接跳過,不使用這個聲卡模型,給出的解釋是,Android5上面的native
    module is disabled;
  • 19以下的版本纔會使用這個聲卡模型:加載需要使用的媒體庫,使用的是system/lib下的libmedia.so,這個很關鍵;
  • 然後使用這個libmedia.so初始化三個媒體
    AudioRecordImpl、AudioTrackImpl、AudioSystemImpl的init;
  • 然後構建真實的聲卡模型對象,android_snd_card_new(d),每個聲卡模型都有自己的android_snd_card_new函數;
static MSSndCard * android_snd_card_new(SoundDeviceDescription *d)
{
        MSSndCard * obj;
                        obj=ms_snd_card_new(&android_native_snd_card_desc);
        obj->name=ms_strdup("android sound card");  

        if (d->flags & DEVICE_HAS_BUILTIN_AEC) obj->capabilities|=MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER;
        obj->latency=d->delay;
        obj->data = new AndroidNativeSndCardData(  d->recommended_rate, d->flags);
        return obj;
}

對於android_native_snd_opensles_card_desc,在執行desc->detect(m)的時候,實質上就是執行了android_native_snd_opensles_card_desc中定義的的detect函數 android_snd_card_detect, 在androidsound_opensles.cpp中

static void android_snd_card_detect(MSSndCardManager *m) {
        SoundDeviceDescription* d = NULL;
        MSDevicesInfo *devices = NULL;
        if (initOpenSLES() == 0) { // Try to dlopen libOpenSLES
                ms_message("libOpenSLES correctly loaded, creating OpenSLES MS soundcard");
                devices = ms_factory_get_devices_info(m->factory);  
                d = ms_devices_info_get_sound_device_description(devices);
                 if (d->flags & DEVICE_HAS_CRAPPY_OPENSLES)
                        return;
                 MSSndCard *card = android_snd_card_new(m);
                ms_snd_card_manager_add_card(m, card);
        } else {
                ms_warning("Failed to dlopen libOpenSLES, OpenSLES MS soundcard unavailable");
         }
}   

同樣的,該類下也有android_snd_card_new方法,

static MSSndCard* android_snd_card_new(MSSndCardManager *m) {
            MSSndCard* card = NULL;
            SoundDeviceDescription *d = NULL;
            MSDevicesInfo *devices = NULL;

            card = ms_snd_card_new(&android_native_snd_opensles_card_desc);
            card->name = ms_strdup("android sound card");

            devices = ms_factory_get_devices_info(m->factory);
            d = ms_devices_info_get_sound_device_description(devices);

            OpenSLESContext *context = opensles_context_init();
            if (d->flags & DEVICE_HAS_BUILTIN_OPENSLES_AEC) {
                    card->capabilities |= MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER;
                    context->builtin_aec = true;
            } else if (d->flags & DEVICE_HAS_BUILTIN_AEC) {
                    ms_warning("Removing MS_SND_CARD_CAP_CAPTURE flag from soundcard to use HAEC Java capture soundcard");
                    card->capabilities = MS_SND_CARD_CAP_PLAYBACK;
            }
            card->latency = d->delay;
            card->data = context;   
            if (d->recommended_rate){
                    context->samplerate = d->recommended_rate;
             }
             return card;
}

對於msandroid_sound_card_desc,在執行desc->detect(m)的時候,實質上就是執行了msandroid_sound_card_desc中定義的的detect函數msandroid_sound_detect,

void msandroid_sound_detect(MSSndCardManager *m) {
            ms_message("msandroid_sound_detect");
            MSSndCard *card = msandroid_sound_card_new(m);
             ms_snd_card_manager_add_card(m, card);
}
MSSndCard *msandroid_sound_card_new(MSSndCardManager *m) {
                ms_message("msandroid_sound_card_new");
            SoundDeviceDescription *d = NULL;   
             MSDevicesInfo *devices = NULL;
            MSSndCard *card = ms_snd_card_new(&msandroid_sound_card_desc);
            card->name = ms_strdup("Android Sound card");

            devices = ms_factory_get_devices_info(m->factory);
            d = ms_devices_info_get_sound_device_description(devices);

             if (d->flags & DEVICE_HAS_BUILTIN_AEC) {
                    card->capabilities |= MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER;
            }
            card->data = d;
             return card;
}

三種聲卡都會設置capabilities屬性,這個屬性主要是決定這個聲卡是做爲播放器使用還是錄音器使用,取值不一樣,這裏三種聲卡的capabilities取值爲3,表示既可以作爲錄音器也可以作爲播放器
三種MSSndCardDesc*的detect最終都是調用ms_snd_card_new(MSSndCardDesc*)來實現的;

該函數在mssndcard.c中,line171

MSSndCard * ms_snd_card_new(MSSndCardDesc *desc){
        return ms_snd_card_new_with_name(desc,NULL);
}
MSSndCard * ms_snd_card_new_with_name(MSSndCardDesc *desc,const char* name) {
        ms_message("ms_snd_card_new_with_name %s",desc->driver_type);
        MSSndCard *obj=(MSSndCard *)ms_new0(MSSndCard,1);
        obj->sndcardmanager = NULL;
        obj->desc=desc; 
        obj->name=name?ms_strdup(name):NULL;
        obj->data=NULL;
        obj->id=NULL;
        obj->capabilities=MS_SND_CARD_CAP_CAPTURE|MS_SND_CARD_CAP_PLAYBACK;
        if (desc->init!=NULL)
                desc->init(obj);
        return obj;
}

執行各個desc->init();
對於android_native_snd_card_desc和android_native_snd_opensles_card_desc,init是個空函數,msandroid_sound_card_desc內有init函數;

void msandroid_sound_init(MSSndCard *card){
        /*get running sdk version*/
        JNIEnv *jni_env = ms_get_jni_env();
        jclass version_class = jni_env->FindClass("android/os/Build$VERSION");
        jfieldID fid = jni_env->GetStaticFieldID(version_class, "SDK_INT", "I");
        sdk_version=jni_env->GetStaticIntField(version_class, fid); 
        ms_message("SDK version [%i] detected",sdk_version);
        jni_env->DeleteLocalRef(version_class);
}

對於三種不同的聲卡模型,執行ms_snd_card_new生成MSSndCard模型對象以後,都會通過ms_snd_card_manager_add_card();來完成MSSndCardManager對象和MSSndCard對象的相互關聯

ms_snd_card_manager_add_card() 位於mssndcard.c Line119

void ms_snd_card_manager_add_card(MSSndCardManager *m, MSSndCard *c){
    ms_snd_card_set_manager(m,c);
    ms_message("Card '%s' added with capabilities [%s]",ms_snd_card_get_string_id(c), cap_to_string(c->capabilities));
    m->cards=bctbx_list_append(m->cards,c);
}
void ms_snd_card_set_manager(MSSndCardManager*m, MSSndCard *c){
    if (c->sndcardmanager == NULL) c->sndcardmanager = m;
}

操作比較簡單,首先將給每個MSSndCard對象設置->sndcardmanager屬性爲MSSndCardManager對象;
然後將聲卡模型對象追加到MSSndCardManager對象的cards列表裏;
至此ms_factory_init_voip全部結束。

簡單總結一下:

  • **初始化首先構建一個聲卡模型對象管理者 MSSndCardManager對象,並與傳入的MSFactory互相關聯
    factory->sndcardmanager = sndcardManager;
    sndcardManager->factory =factory;**
  • **然後系統自身配置了一個聲卡模型描述的數組,定義了三種不同的聲卡模型;
    android_native_snd_card_desc;
    android_native_snd_opensles_card_desc;
    msandroid_sound_card_desc;**
  • **通過調用模型中定義的detect函數和init函數指針來構建MSSndCard模型對象,並且與MSSndCardManager對象相互關聯;
    sndcard->sndcardmanager = sndcardmanager;
    sndcardmanager->cards =bctbx_list_append(sndcard);**
  • 最終返回MSFactory模型

需要注意的是,sndcardmanager->cards中存放的聲卡模型列表是按先後順序存放的,這點很重要,因爲後面再讀取音頻配置的手,會根據id和capability來選擇匹配的聲卡,並且是返回第一個匹配的;

第三步,ms_factory_init_plugins(f); 位於msfactory.c line672;

void ms_factory_init_plugins(MSFactory *obj) {
   if (obj->plugins_dir == NULL) {
#ifdef PACKAGE_PLUGINS_DIR
      obj->plugins_dir = ms_strdup(PACKAGE_PLUGINS_DIR);
#else
      obj->plugins_dir = ms_strdup("");
#endif
   }
   if (strlen(obj->plugins_dir) > 0) {
      ms_message("Loading ms plugins from [%s]",obj->plugins_dir);
      ms_factory_load_plugins(obj,obj->plugins_dir);    
   }
}

因爲MSFactory是剛新建並初始化的,這裏他的plugins_dir還沒有值;
這裏預定義了一個PACKAGE_PLUGINS_DIR字符串,賦值給factory->plugins_dir;

ms_factory_load_plugins();在msfactory.c line467中;從log來看, 這裏指定的plugins_dir路徑不存在,結果返回了-1;
至此 ms_factory_new_with_voip()全部結束。

回到linphone_core_init中 ,函數內部最後有一段

    remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
    if(remote_provisioning_uri == NULL){
        linphone_configuring_terminated(lc,LinphoneConfiguringSkipped,NULL)
    }

linphone_core_get_provisioning_uri() 位於remote_provisioning.c line157

const char*linphone_core_get_provisioning_uri(const LinphoneCore *lc){
      return lp_config_get_string(lc->config,"misc","config-uri",NULL);
}

從系統配置文件中讀取misc下的config-uri字段。
系統默認的配置文件中沒有設置這個字段,顧返回的是NULL;
因而繼續執行linphone_configuring_terminated(), 位於linphonecore.c line1670;

void linphone_configuring_terminated(LinphoneCore *lc, LinphoneConfiguringState state, const char *message) {
    ms_message("linphone_configuring_terminated");
    linphone_core_notify_configuring_status(lc, state, message);

    if (state == LinphoneConfiguringSuccessful) {
             if (linphone_core_is_provisioning_transient(lc) == TRUE)
            linphone_core_set_provisioning_uri(lc, NULL);
    }
    if (lc->provisioning_http_listener){
            belle_sip_object_unref(lc->provisioning_http_listener);
             lc->provisioning_http_listener = NULL;
    }
    linphone_core_start(lc);
}

首先更新狀態爲LinphoneConfiguringSkipped;linphone_core_notify_configuring_status位於vtables.c line264

void linphone_core_notify_configuring_status(LinphoneCore *lc, LinphoneConfiguringState status, const char *message) {
    NOTIFY_IF_EXIST(configuring_status, lc,status,message);
    cleanup_dead_vtable_refs(lc);
}

通過NOTIFY_IF_EXIST宏,去在vtables中找configuring_status是否有對應的回調函數,並執行對應的回調通知java層狀態變化;
剩下的兩步操作應該都不存在,直接進入linphone_core_start(lc);位於linphonecore.c line1640;

static void linphone_core_start(LinphoneCore * lc) {
    ms_message("linphone_core_start");
    LinphoneFriendList *list = linphone_core_create_friend_list(lc);
    linphone_friend_list_set_display_name(list, "_default");
    linphone_core_add_friend_list(lc, list);
     linphone_friend_list_unref(list);

     sip_setup_register_all(lc->factory);
     sound_config_read(lc);
     net_config_read(lc);
     rtp_config_read(lc);
    codecs_config_read(lc);
    sip_config_read(lc);
    video_config_read(lc);
    //autoreplier_config_init(&lc->autoreplier_conf);
    lc->presence_model=linphone_presence_model_new_with_activity(LinphonePresenceActivityOnline, NULL);
    misc_config_read(lc);
    ui_config_read(lc);
    #ifdef TUNNEL_ENABLED
        if (lc->tunnel) {
                 linphone_tunnel_configure(lc->tunnel);
        }
    #endif
     linphone_core_notify_display_status(lc,_("Ready"));
     lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon;
    linphone_core_set_state(lc,LinphoneGlobalOn,"Ready");
}

關於聲卡的配置在sound_config_read中,linphonecore.c line825

static void sound_config_read(LinphoneCore *lc)
{
        ms_message("sound_config_read");
    int tmp;
    const char *tmpbuf;
    const char *devid;
    #ifdef __linux
        /*alsadev let the user use custom alsa device within linphone*/ 
        devid=lp_config_get_string(lc->config,"sound","alsadev",NULL);
        if (devid){ 
            ...
                for (l=0,i=strpbrk(d+l,delim);i;i=strpbrk(d+l,delim)){
                    char s=*i;
                    *i='\0';
                    card=ms_alsa_card_new_custom(d+l,d+l);
                    ms_snd_card_manager_add_card(ms_factory_get_snd_card_manager(lc->factory),card);
                    *i=s;
                    l=i-d+1;
                }
            ...
        }
        tmp=lp_config_get_int(lc->config,"sound","alsa_forced_rate",-1);
        if (tmp>0) ms_alsa_card_set_forced_sample_rate(tmp);
    #endif  
    /* retrieve all sound devices */
    build_sound_devices_table(lc);

    devid=lp_config_get_string(lc->config,"sound","playback_dev_id",NULL);
    linphone_core_set_playback_device(lc,devid);

    devid=lp_config_get_string(lc->config,"sound","ringer_dev_id",NULL);
     linphone_core_set_ringer_device(lc,devid);

    devid=lp_config_get_string(lc->config,"sound","capture_dev_id",NULL);
    linphone_core_set_capture_device(lc,devid);
    ... 
}

這個函數主要的操作是通過讀取lc->config中關於sound的配置字段,然後進行相關的設置操作;
按順序進行一下檢查配置:

  1. 檢查是或否存在alsadev字段,如果存在的話,通過ms_alsa_card_new_custom新建一個MSSndCard,並添加到lc->factory->mssndcardmanager;
  2. build_sound_devices_table(lc),從lc中讀取mssndcardmanager中的聲卡模型列表,並重新定義一個字符串數組,保存這些聲卡的driver_type值;
    然後將數組保存到lc->sound_conf.cards中;
  3. 讀取lc->config中sound字段下的playback_dev_id值,並調用linphone_core_set_playback_device設置,這是播放器聲卡
  4. 讀取lc->config中sound字段下的ringer_dev_id值,並調用linphone_core_set_ringer_device設置;
  5. 讀取lc->config中sound字段下的capture_dev_id值,並調用linphone_core_set_capture_device設置,這是錄音器聲卡
  6. 後面依次設置sound下的local_ring、remot_ring、hold_music、echocancelllation、echolimiter、agc等其他相關配置
  7. _linphone_core_set_tone,設置遇忙情況下的響鈴,保存在lc->tones中;

重點分析一下第2~5步的操作

第二步:build_sound_devices_table(lc),位於linphonecore.c line794;

static void build_sound_devices_table(LinphoneCore *lc){
    const char **devices;
    const char **old;
    size_t ndev;
    int i;  
    const bctbx_list_t *elem=ms_snd_card_manager_get_list(ms_factory_get_snd_card_manager(lc->factory));
    ndev=bctbx_list_size(elem);
     devices=ms_malloc((ndev+1)*sizeof(const char *));
    for (i=0;elem!=NULL;elem=elem->next,i++){
            devices[i]=ms_snd_card_get_string_id((MSSndCard *)elem->data);
            ms_message("build sound devices table %i = %s",i,devices[i]);
    }
    devices[ndev]=NULL;
    old=lc->sound_conf.cards;
    lc->sound_conf.cards=devices;
    if (old!=NULL) ms_free((void *)old);
 }

第三步:播放器的設置
系統默認的配置文件中是沒有定義playback_dev_id的,linphone_core_set_playback_device中的第二個入參實際是null;函數位於
linphonecore.c line4812;

int linphone_core_set_playback_device(LinphoneCore *lc, const char * devid){
    ms_message("linphone_core_set_playback_device  %s",devid);
    MSSndCard *card=get_card_from_string_id(devid,MS_SND_CARD_CAP_PLAYBACK, lc->factory);
    lc->sound_conf.play_sndcard=card;
    if (card &&  linphone_core_ready(lc))
             lp_config_set_string(lc->config,"sound","playback_dev_id",ms_snd_card_get_string_id(card));
    return 0;
}

通過傳入的id和MS_SND_CARD_CAP_PLAYBACK類型,從lc->factory中讀取對應的聲卡模型;
然後將這個聲卡模型設置給lc->sound_conf.play_sndcard;
並且將這個聲卡的id設置到系統配置lc->config中;

簡單看一下get_card_from_string_id()這個函數,位於linphonecore.c line4738中;

static MSSndCard *get_card_from_string_id(const char *devid, unsigned int cap, MSFactory *f){
    MSSndCard *sndcard=NULL;
    if (devid!=NULL){
            sndcard=ms_snd_card_manager_get_card(ms_factory_get_snd_card_manager(f),devid);
            if (sndcard!=NULL &&(ms_snd_card_get_capabilities(sndcard) & cap)==0 ){
                 sndcard=NULL;
            }
    }
     if (sndcard==NULL) {
             if ((cap & MS_SND_CARD_CAP_CAPTURE) && (cap & MS_SND_CARD_CAP_PLAYBACK)){
                sndcard=ms_snd_card_manager_get_default_card(ms_factory_get_snd_card_manager(f));
            }else if (cap & MS_SND_CARD_CAP_CAPTURE){
                 sndcard=ms_snd_card_manager_get_default_capture_card(ms_factory_get_snd_card_manager(f));
            }
            else if (cap & MS_SND_CARD_CAP_PLAYBACK){
                sndcard=ms_snd_card_manager_get_default_playback_card(ms_factory_get_snd_card_manager(f));  
             }
             if (sndcard==NULL){/*looks like a bug! take the first one !*/
                const bctbx_list_t *elem=ms_snd_card_manager_get_list(ms_factory_get_snd_card_manager(f));
                 if (elem) sndcard=(MSSndCard*)elem->data;
             }
    }
    if (sndcard==NULL) ms_error("Could not find a suitable soundcard !");
    return sndcard;
}
  • 如果入參devid有值,就通過ms_snd_card_manager_get_card來查找id相同的MSSndCard,同時檢查一下這個聲卡的capability是否與入參中的cap一致;
  • 如果id相同、capability不一致,也重新查找;
  • 根據入參的Cap來選擇不同的查找方式,這裏我們用的是MS_SND_CARD_CAP_PLAYBACK的類別;

所以選擇ms_snd_card_manager_get_default_playback_card(manager); 函數位於 mssndcard.c line100;

MSSndCard * ms_snd_card_manager_get_default_playback_card(MSSndCardManager *m){
    return get_card_with_cap(m, NULL, MS_SND_CARD_CAP_PLAYBACK);
}
static MSSndCard *get_card_with_cap(MSSndCardManager *m, const char *id, unsigned int caps){
    bctbx_list_t *elem; 
    for (elem=m->cards;elem!=NULL;elem=elem->next){
             MSSndCard *card=(MSSndCard*)elem->data;
             if ((id== NULL || strcmp(ms_snd_card_get_string_id(card),id)==0) && (card->capabilities & caps) == caps)   return card;
     }
    return NULL;
}

其實這裏的獲取方式也是根據cap和id來的,如果有id值,則返回同時匹配的聲卡, 如果沒有id值,則返回cap匹配的聲卡;

需要注意的是,這裏只要匹配到第一個滿足條件的聲卡模型,就返回了,所以如果存放的順序也決定了linphone最終選擇使用何種聲卡

第五步: 錄音器的設置
同樣的,系統默認的配置中沒有指定capture_dev_id,整個流程與播音器的設置基本一致;
linphone_core_set_capture_device() 位於linphonecore.c line4831

int linphone_core_set_capture_device(LinphoneCore *lc, const char * devid){
    MSSndCard *card=get_card_from_string_id(devid,MS_SND_CARD_CAP_CAPTURE, lc->factory);
    lc->sound_conf.capt_sndcard=card;
    if (card &&  linphone_core_ready(lc))
            lp_config_set_string(lc->config,"sound","capture_dev_id",ms_snd_card_get_string_id(card));
     return 0;
}

將讀取到的聲卡設置到lc->sound_conf.capt_sndcard中;

後面的操作不細看了,到這裏linphone使用的錄音器和播放器全部設置好了;

**後續再音頻、視屏通話的時候,啓動錄音器就是通過lc->sound_conf.capt_sndcard來獲取對應的聲卡模型;
然後調用模型中定義的函數接口來獲取錄音數據;**

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章