linphone呼出音频电话时,录音设备执行流程分析

本文详细分析了Android版Linphone呼出音频电话时的录音设备执行流程,从发起呼叫、音频设备初始化到录音数据采集,深入探讨了不同Android声卡模型下的录音实现方式,包括msandroid_sound_card_desc、android_native_snd_card_desc和android_native_snd_opensles_card_desc。通过ms_ticker_start、ms_ticker_run以及不同声卡的process函数,揭示了录音数据如何被采集并发送的过程。
摘要由CSDN通过智能技术生成

发起呼叫

发起呼叫请求的入口在inviteAddressWithParams(address,params); jni接口在linphonecore_jni.cc 中 line 5023;

 

按照流程走,最终调用linphone_core_start_invite(), 在linphonecore.c ——line3161

int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, const LinphoneAddress* destination /* = NULL if to be taken from the call log */){
   ms_message("linphone_core_start_invite");
   int err;
   char *real_url,*barmsg;
   char *from;
   /*try to be best-effort in giving real local or routable contact address */
   linphone_call_set_contact_op(call);

   linphone_core_stop_dtmf_stream(lc);
   linphone_call_make_local_media_description(call);

   if (lc->ringstream==NULL) {
      if (lc->sound_conf.play_sndcard && lc->sound_conf.capt_sndcard){
         /*give a chance a set card prefered sampling frequency*/
         if (call->localdesc->streams[0].max_rate>0) {
            ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate);
         }
         if (!lc->use_files){
             ms_message("start invite call audio_stream_prepare_sound");
            audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard);
         }
      }
   }
   real_url=linphone_address_as_string( destination ? destination : call->log->to);
   from=linphone_address_as_string(call->log->from);

   if (!lc->sip_conf.sdp_200_ack){
      /*we are offering, set local media description before sending the call*/
      sal_call_set_local_media_description(call->op,call->localdesc);
   }
   
   barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url);
   linphone_core_notify_display_status(lc,barmsg);
   ms_free(barmsg);
   
   err=sal_call(call->op,from,real_url);
   
   if (err < 0){
      if (call->state != LinphoneCallError &&
         call->state != LinphoneCallReleased){
         /*sal_call() may invoke call_failure() and call_released() SAL callbacks synchronously,
          * in which case there is no need to perform a state change here.*/
         linphone_core_notify_display_status(lc,_("Could not call"));
         linphone_call_stop_media_streams(call);
         linphone_call_set_state(call,LinphoneCallError,"Call failed");
      }
      goto end;
   }
   if (lc->sip_conf.sdp_200_ack){
      /*we are NOT offering, set local media description after sending the call so that we are ready to
       process the remote offer when it will arrive*/
      sal_call_set_local_media_description(call->op,call->localdesc);
   }
   call->log->call_id=ms_strdup(sal_op_get_call_id(call->op)); /*must be known at that time*/
   linphone_call_set_state(call,LinphoneCallOutgoingProgress,"Outgoing call in progress");
   
end:
   ms_free(real_url);
   ms_free(from);
   return err;
}linphone_call_make_local_media_description(call);

   if (lc->ringstream==NULL) {
      if (lc->sound_conf.play_sndcard && lc->sound_conf.capt_sndcard){
         /*give a chance a set card prefered sampling frequency*/
         if (call->localdesc->streams[0].max_rate>0) {
            ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate);
         }
         if (!lc->use_files){
             ms_message("start invite call audio_stream_prepare_sound");
            audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard);
         }
      }
   }
   real_url=linphone_address_as_string( destination ? destination : call->log->to);
   from=linphone_address_as_string(call->log->from);

   if (!lc->sip_conf.sdp_200_ack){
      /*we are offering, set local media description before sending the call*/
      sal_call_set_local_media_description(call->op,call->localdesc);
   }
   
   barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url);
   linphone_core_notify_display_status(lc,barmsg);
   ms_free(barmsg);
   
   err=sal_call(call->op,from,real_url);
   
   if (err < 0){
      if (call->state != LinphoneCallError &&
         call->state != LinphoneCallReleased){
         /*sal_call() may invoke call_failure() and call_released() SAL callbacks synchronously,
          * in which case there is no need to perform a state change here.*/
         linphone_core_notify_display_status(lc,_("Could not call"));
         linphone_call_stop_media_streams(call);
         linphone_call_set_state(call,LinphoneCallError,"Call failed");
      }
      goto end;
   }
   if (lc->sip_conf.sdp_200_ack){
      /*we are NOT offering, set local media description after sending the call so that we are ready to
       process the remote offer when it will arrive*/
      sal_call_set_local_media_description(call->op,call->localdesc);
   }
   call->log->call_id=ms_strdup(sal_op_get_call_id(call->op)); /*must be known at that time*/
   linphone_call_set_state(call,LinphoneCallOutgoingProgress,"Outgoing call in progress");
   
end:
   ms_free(real_url);
   ms_free(from);
   return err;
}

1、设置本地的媒体描述参数 linphone_call_make_local_media_description(call)

2、判断ringstream是否为空,即响铃的音频流是否构建好,如果没有则准备启动音频库

audio_stream_prepare_sound();

3、在对方还没接响应sip消息前,设置好sip的媒体参数sal_call_set_local_media_description();

4、发起呼叫sal_call();

5、最后根据响应结果,来更新本地媒体描述,sal_call_set_local_media_description,

6、更新状态为LinphoneCallOutgoingProgress;

 

本次不介绍呼叫过程中的sip消息的处理,因此只看下1、2、3步的过程;

 

第一步 linphone_call_make_local_media_description(), linphonecall.c ——line731

代码太长了,主要过程是:

构建一个SalMediaDescription* md对象;

然后设置md->stream[]中的audio、video的multicast_role、ttl属性

设置md的session_id、bandwidth等属性

设置md->stream[]中的audio、video的采样率、端口、协议等于音视频通信协议相关的设置参数

 

第二步 audio_stream_prepare_sound; audiostream.c ——line240

void audio_stream_prepare_sound(AudioStream *stream, MSSndCard *playcard, MSSndCard *captcard){
    ms_message("audio_stream_prepare_sound playcard = %s, captcard = %s",playcard->desc->driver_type,captcard->desc->driver_type);
   audio_stream_unprepare_sound(stream);
   stream->dummy=ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
   rtp_session_set_payload_type(stream->ms.sessions.rtp_session,0);
   rtp_session_enable_rtcp(stream->ms.sessions.rtp_session, FALSE);
   ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);

   if (captcard && playcard){
#ifdef __ios
      int muted = 1;
      stream->soundread=ms_snd_card_create_reader(captcard);
      stream->soundwrite=ms_snd_card_create_writer(playcard);
      ms_filter_link(stream->dummy,0,stream->soundwrite,0);
      ms_filter_call_method(stream->soundwrite, MS_AUDIO_PLAYBACK_MUTE, &muted);
#else
      stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory,  MS_VOID_SINK_ID);
      ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
#endif
   } else {
      stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory,  MS_VOID_SINK_ID);
      ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
      
   }
   if (stream->ms.sessions.ticker == NULL) media_stream_start_ticker(&stream->ms);
   ms_ticker_attach(stream->ms.sessions.ticker,stream->dummy);
   stream->ms.state=MSStreamPreparing;
}ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
   rtp_session_set_payload_type(stream->ms.sessions.rtp_session,0);
   rtp_session_enable_rtcp(stream->ms.sessions.rtp_session, FALSE);
   ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);

   if (captcard && playcard){
#ifdef __ios
      int muted = 1;
      stream->soundread=ms_snd_card_create_reader(captcard);
      stream->soundwrite=ms_snd_card_create_writer(playcard);
      ms_filter_link(stream->dummy,0,stream->soundwrite,0);
      ms_filter_call_method(stream->soundwrite, MS_AUDIO_PLAYBACK_MUTE, &muted);
#else
      stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory,  MS_VOID_SINK_ID);
      ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
#endif
   } else {
      stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory,  MS_VOID_SINK_ID);
      ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
      
   }
   if (stream->ms.sessions.ticker == NULL) media_stream_start_ticker(&stream->ms);
   ms_ticker_attach(stream->ms.sessions.ticker,stream->dummy);
   stream->ms.state=MSStreamPreparing;
}

先释放掉call中原有的audiostream: audio_stream_unprepare_sound(stream);

然后获取ms_factory中MS_RTP_RECV_ID的MSFilter对象:ms_factory_create_filter(),设置到stream->dummy;

后面继续获取MS_VOID_SINK_ID的MSFilter对象,设置到stream->ms.voidsink上;

然后判断stream->ms.sessions.ticker == null的时候,调用media_stream_start_ticker()去初始化并启动一个ms_ticker;

最后调用ms_ticker_attach()来将ms_ticker与stream->dummy关联上;

 

音频准备工作重点就是启用MSTicker并关联到本次通话过程中,

首先获取MSFilter* :ms_factory_create_filter()。 msfactory.c ——line362

MSFilter *ms_factory_create_filter(MSFactory* factory, MSFilterId id){
   MSFilterDesc *desc;
   if (id==MS_FILTER_PLUGIN_ID){
      ms_warning("cannot create plugin filters with ms_filter_new_from_id()");
      return NULL;
   }
   desc=ms_factory_lookup_filter_by_id(factory,id);
   if (desc) return ms_factory_create_filter_from_desc(factory,desc);
   ms_error("No such filter with id %i",id);
   return NULL;
}ms_factory_lookup_filter_by_id(factory,id);
   if (desc) return ms_factory_create_filter_from_desc(factory,desc);
   ms_error("No such filter with id %i",id);
   return NULL;
}

使用的id是MS_RTP_RECV_ID,通过ms_factory_lookup_filter_by_id来找到过滤器的描述;——line385

MSFilterDesc* ms_factory_lookup_filter_by_id( MSFactory* factory, MSFilterId id){
   bctbx_list_t *elem;
   for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
      MSFilterDesc *desc=(MSFilterDesc*)elem->data;
      if (desc->id==id){
         return desc;
      }
   }
   return NULL;
}

实际就是遍历factory的desc_list列表,找到跟id匹配的第一个desc描述体

回到上面的函数中,继续通过dessc找到真正的filter

ms_factory_create_filter_from_desc(); ——line337

MSFilter *ms_factory_create_filter_from_desc(MSFactory* factory, MSFilterDesc *desc){
   MSFilter *obj;
   obj=(MSFilter *)ms_new0(MSFilter,1);
   ms_mutex_init(&obj->lock,NULL);
   obj->desc=desc;
   if (desc->ninputs>0)   obj->inputs=(MSQueue**)ms_new0(MSQueue*,desc->ninputs);
   if (desc->noutputs>0)  obj->outputs=(MSQueue**)ms_new0(MSQueue*,desc->noutputs);

   if (factory->statistics_enabled){
      obj->stats=find_or_create_stats(factory,desc);
   }
   obj->factory=factory;
   if (obj->desc->init!=NULL)
      obj->desc->init(obj);
   return obj;
}init(obj);
   return obj;
}

初始化一个MSFilter,然后将传入的desc设置到obj上:obj->desc = desc;

然后初始化obj->inputs和obj->outputs;

关联MSFactory :obj->factory = factory;

最后调用obj->desc->init(obj)进行MSFilter的初始化;

再回到audio_stream_prepare_sound中,看看MSTicker的初始化流程

media_stream_start_ticker(),位于mediastream.c ——line144

void media_stream_start_ticker(MediaStream *stream) {
    ms_message("media_stream_start_ticker");
   MSTickerParams params = {0};
   char name[32] = {0};

   if (stream->sessions.ticker) return;
   snprintf(name, sizeof(name) - 1, "%s MSTicker", media_stream_type_str(stream));
   name[0] = toupper(name[0]);
   params.name = name;
   params.prio = __ms_get_default_prio((stream->type == MSVideo) ? TRUE : FALSE);
   stream->sessions.ticker = ms_ticker_new_with_params(¶ms);
}ms_ticker_new_with_params(¶ms);
}

构建一个MSTickerParams参数,然后通过它构建MS_Ticker,

ms_ticker_new_with_params(), 位于msticker.c ——line87

MSTicker *ms_ticker_new_with_params(const MSTickerParams *params){
   MSTicker *obj=(MSTicker *)ms_new0(MSTicker,1);
   ms_ticker_init(obj,params);
   return obj;
}ms_ticker_init(obj,params);
   return obj;
}
static void ms_ticker_init(MSTicker *ticker, const MSTickerParams *params)
{
    ms_message("ms_ticker_init");
   ms_mutex_init(&ticker->lock,NULL);
   ticker->execution_list=NULL;
   ticker->task_list=NULL;
   ticker->ticks=1;
   ticker->time=0;
   ticker->interval=TICKER_INTERVAL;
   ticker->run=FALSE;
   ticker->exec_id=0;
   ticker->get_cur_time_ptr=&get_cur_time_ms;
   ticker->get_cur_time_data=NULL;
   ticker->name=ms_strdup(params->name);
   ticker->av_load=0;
   ticker->prio=params->prio;
   ticker->wait_next_tick=wait_next_tick;
   ticker->wait_next_tick_data=ticker;
   ticker->late_event.lateMs = 0;
   ticker->late_event.time = 0;
   ticker->late_event.current_late_ms = 0;
   ms_ticker_start(ticker);
}
static void ms_ticker_start(MSTicker *s){
   s->run=TRUE;
   ms_thread_create(&s->thread,NULL,ms_ticker_run,s);
}ms_ticker_start(ticker);
}
static void ms_ticker_start(MSTicker *s){
   s->run=TRUE;
   ms_thread_create(&s->thread,NULL,ms_ticker_run,s);
}

初始化分为三个步骤

1、new一个MSTicker*对象;

2、配置ticker的属性,默认的间隔是10秒;

3、添加到线程中启动ms_ticker;

 

接着看ms_ticker_attach(),位于msticker.c ——line135

int ms_ticker_attach(MSTicker *ticker, MSFilter *f){
   return ms_ticker_attach_multiple(ticker,f,NULL);
}
int ms_ticker_attach_multiple(MSTicker *ticker,MSFilter *f,...)
{
    ms_message("ms_ticker_attach_multiple");
   bctbx_list_t *sources=NULL;
   bctbx_list_t *filters=NULL;
   bctbx_list_t *it;
   bctbx_list_t *total_sources=NULL;
   va_list l;

   va_start(l,f);

   do{
      if (f->ticker==NULL) {
          ms_message("f->ticker == null");
         filters=ms_filter_find_neighbours(f);
         sources=get_sources(filters);
         if (sources==NULL){
            ms_fatal("No sources found around filter %s",f->desc->name);
            bctbx_list_free(filters);
            break;
         }
         /*run preprocess on each filter: */
         for(it=filters;it!=NULL;it=it->next)
            ms_filter_preprocess((MSFilter*)it->data,ticker);
         bctbx_list_free(filters);
         total_sources=bctbx_list_concat(total_sources,sources);
      }else ms_message("Filter %s is already being scheduled; nothing to do.",f->desc->name);
   }while ((f=va_arg(l,MSFilter*))!=NULL);
   va_end(l);
   if (total_sources){
      ms_mutex_lock(&ticker->lock);
      ticker->execution_list=bctbx_list_concat(ticker->execution_list,total_sources);
      ms_mutex_unlock(&ticker->lock);
   }
   return 0;
}for(it=filters;it!=NULL;it=it->next)
            ms_filter_preprocess((MSFilter*)it->data,ticker);
         bctbx_list_free(filters);
         total_sources=bctbx_list_concat(total_sources,sources);
      }else ms_message("Filter %s is already being scheduled; nothing to do.",f->desc->name);
   }while ((f=va_arg(l,MSFilter*))!=NULL);
   va_end(l);
   if (total_sources){
      ms_mutex_lock(&ticker->lock);
      ticker->execution_list=bctbx_list_concat(ticker->execution_list,total_sources);
      ms_mutex_unlock(&ticker->lock);
   }
   return 0;
}

刚启动的时候,f->ticker=null,

ms_filter_find_neighbours(f),获取整个f的filters列表;

然后遍历整个filters,调用每个ms_filter_preprocess();

执行完preprocess以后,会将当前的total_sources添加到ticker->execution列表中、

系统每个生成的MSTicker下的execution_list都会保存当前所有构建的MSTickers对象,这个会在ms_ticker_run中用到;

ms_filter_preprocess() 位于msfilter.c ——line234

void ms_filter_preprocess(MSFilter *f, struct _MSTicker *t){
    ms_message("ms_filter_preprocess %s" ,f->desc->name);
   f->last_tick=0;
   f->ticker=t;
   if (f->desc->preprocess!=NULL)
      f->desc->preprocess(f);
}preprocess(f);
}

调用每个MSFilter中定义的MSFilterDesc中的preprocess()函数;

这里主要有两种类型的MSFilter,一个是MSRtpRecv,一个是MSVoidSink;

 

MSRtpRecv定义在msrtp.c中,应该是作为rtp数据接收使用的;

MSVoidSink定义在void.c中,这个没有preprocess函数接口;

 

看一下MSRtpRecv中的preprocess(), 在msrtp.c ——line648

static void receiver_preprocess(MSFilter * f){
    ms_message("receiver_preprocess msrtp");
   ReceiverData *d = (ReceiverData *) f->data;
   d->starting=TRUE;
}

修改了f->data->starting的状态值,后面在执行process的时候会校验这个状态;

f->data是真实的媒体库的执行类;

 

至此,第二步的ringstream的初始化完成了;

 

第三步,sal_call_set_local_media_description();

位于sal_op_call.c ——line723

int sal_call_set_local_media_description(SalOp *op, SalMediaDescription *desc){
   if (desc)
      sal_media_description_ref(desc);
   if (op->base.local_media)
      sal_media_description_unref(op->base.local_media);
   op->base.local_media=desc;

   if (op->base.remote_media){
      /*case of an incoming call where we modify the local capabilities between the time
       * the call is ringing and it is accepted (for example if you want to accept without video*/
      /*reset the sdp answer so that it is computed again*/
      if (op->sdp_answer){
         belle_sip_object_unref(op->sdp_answer);
         op->sdp_answer=NULL;
      }
   }
   return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值