当前位置: 首页 > 工具软件 > QuteCom > 使用案例 >

qutecom笔记-模型-PhoneCall对象

祁鸿晖
2023-12-01

PhoneCall是封装了一个电话的状态和行为的模型类。是一个电话的核心

PhoneCall的核心功能有两个,一个是记录电话状态,并提供一个观察者提供状态变化时的信号stateChangedEvent(用此实现观察者模式)。
另一个是通过调用IPhoneLine的接口实现accept(),resume(),hold(),close()四个主动的动作。

一、主动的动作
先看四个动作的实现,为方便起见,用P代表视图,C代表控制,M表示模型。前后表示执行前后实例在哪个部分内。注意上下文的切换。
这4个函数的调用链如下:
resume:
   (P) QtPhoneCall::_actionHold QT的QAction connect 到 QtPhoneCall::holdOrResume (P)
   (P) QtPhoneCall::holdOrResume 调用 CPhoneCall::resume或者CPhoneCall::hold (C)
   (C) CPhoneCall::resume PostEvent到线程 QuteCom实例,实例线程将执行CPhoneCall::resumeThreadSafe() 原线程(QtApplication)返回(C)
   (C) 切换上下文至qutecom线程执行,CPhoneCall::resumeThreadSafe()调用PhoneCall::resume (M)
   (M) PhoneCall::resume调用PhoneLine::resumeCall(_callId) (M) 注意这里程序逻辑跳到PhoneLine内
   (M) PhoneCall ----> PhoneLine (M)
   (M) PhoneLine::resumeCall 调用 SipWrapper::resumeCall
   至此跳出owPhone模块到SipWrapper(PhApiWrapper)内。

hold:
   与resume相同,在QtPhoneCall::holdOrResume处分支。QtPhoneCall::holdOrResume视情况调用CPhoneCall::resume或者CPhoneCall::hold
   
accept:
   (P) QtPhoneCall按钮accept或者QtoolBar按钮accept或者QtCallToaster::pickUpButtonClicked或者callButtonClicked(条件ActiveCall)
   (C) CPhoneCall::accept PostEvent至QuteCom实例线程执行acceptThreadSafe (C)
   (C) 切换上下文,acceptThreadSafe调用PhoneCall::accept (M)
   (M) PhoneCall::accept 调用 PhoneLine::acceptCall(_callId); (M) 注意这里程序逻辑跳到PhoneLine内
   (M) PhoneCall ----> PhoneLine (M)
   (M) PhoneLine::acceptCall调用SipWrapper::acceptCall
   至此跳出owPhone模块到SipWrapper(PhApiWrapper)内。
   
close:
   (P) QtPhoneCall::   _ui::rejectButton clicked() | _actionHangupCall::trigled() | _callToaster::hangUpButtonClicked()
   (P) QtPhoneCall::rejectCall() --- CPhoneCall.hangUp (C)
   (C) CPhoneCall.hangUp --- postEvent(hangUpThreadSafe)
   (C) switch context -- hangUpThreadSafe
   (M) PhoneCall.close()
   (M) _phoneLine.rejectCall(_callId) | _phoneLine.closeCall(_callId)
   (SIPWrapper) sipWrapper::rejectCall | sipWrapper::closeCall
   End owphone
   
另外,在状态变化后的applyState中视条件调用hold和resume

注意PhoneCall没有makeCall这个函数,因为从逻辑上说PhoneCall代表一个已经存在的电话。没有makeCall或者是newCallCome之前PhoneCall是不存在的。
顺便看看这个函数的实现,有几个地方有用到makeCall这里选取最典型的情况,按下拨出电话的按钮
makeCall:
   (P)QtQuteCom::callButtonClicked()
   (C)CUserProfile::makeCall(phoneNumber)
   (C)switch context to Qutecom call CUserProfile::makeCallThreadSafe()
   (M)UserProfile.makeCall
   (M)PhoneLine::makeCall === 这里new PhoneCall
   (SIPWrapper)sipWrapper->makeCall
   End owphone
   
总结上面的几个函数:
   ①几个动作的流程都是 Qt界面(P) --> Control(CPhoneCall) --> PostEvent(to Qutecom)后返回
              --> (QuteCom执行)Model(PhoneCall) --> Model(PhoneLine) --> SIPWrapper
   ②由此可见QuteCom作为中介者执行Control派发的各种指令并在QuteCom的上下文内执行Model的代码,隔绝了视图(P)不和模型(M)的联系,故而P和M不知道对方的存在。
   但是P&C,C&M是互相认识的。这里通过巧妙的PostEvent将处理该Event的函数对象传给中介者(Qutecom),因而QuteCom执行的代码却是在其他模块内避免了QuteCom变的臃肿。
   
   ③这里也可以看出Qutecom对象(线程对象)的重要性,它作为M被所有的C认识,当有指令从C传递下到M时,如果需要异步处理
   则可以通过PostEvent交给QuteCom线程处理,自己则立即返回。C亦可直接调用C(一个功能模块的C&M互相认识,如CPhoneCall和PhoneCall);
   
二、记录状态变化
  PhoneCall记录电话状态,并且作为观察者模式下的被观察者(Subject)。必须记录各种状态,并且提供一个注册观察者的接口,当自身的状态发生变化时,通过接口将
  自身的状态信息通知所有观察者。这里的注册接口即为:
  Event<void (PhoneCall & sender, EnumPhoneCallState::PhoneCallState status)> stateChangedEvent;
  Event<void (PhoneCall & sender, piximage * remoteVideoFrame, piximage * localVideoFrame)> videoFrameReceivedEvent;
  stateChangedEvent是一个从boost::signal派生的信号。所有观察者可以通过connect和disconnect这个信号(Event重载了+=和-=)
  得到状态变化的信息(EnumPhoneCallState::PhoneCallState status))。
  videoFrameReceivedEvent记录有新的视频帧来临时的信号,这里略过。
  
  stateChangedEvent唯一被PhoneCall::setState调用。
  
  当被外界调用setState时,即CallState发生变化时,将做如下处理
  (一)调用PhoneCallState::execute执行对象状态变化,调用applyState应用状态变化
  (二)调用stateChangedEvent将状态变化通知观察者。
  
  关于PhoneCallState对象:
  PhoneCallState封装了电话状态对象,多个电话状态由此派生:
  EnumPhoneCallState::PhoneCallState getCode() 返回表示状态的枚举
  void execute(PhoneCall & phoneCall,bool doublecall = false) 执行状态变化时的动作(如停止播放声音,开始播放声音)
  还有几个与播放声音相关的静态函数
  static AudioDevice getRingerAudioDevice();
  static void playSoundIncomingCall();
  static void playSoundOutgoingCall();
  static void playSoundDoubleCall();
  static void playSoundCallClosed();
  static void stopSound();
  以及声音对象
  static Sound * _sound;
  各个PhoneCallState的派生类对象构成一个静态列表_phoneCallStateList,该列表包括以下对象:
   static PhoneCallStateUnknown stateUnknown;
   static PhoneCallStateClosed stateClosed;
   static PhoneCallStateDialing stateDialing;
   static PhoneCallStateError stateError;
   static PhoneCallStateHold stateHold;
   static PhoneCallStateIncoming stateIncoming;
   static PhoneCallStateTalking stateTalking;
   static PhoneCallStateResumed stateResumed;
   static PhoneCallStateRinging stateRinging;
   static PhoneCallStateRingingStart stateRingingStart;
   static PhoneCallStateRingingStop stateRingingStop;
   static PhoneCallStateBusy stateBusy;
   static PhoneCallStateRejected stateRejected;
   static PhoneCallStateUserNotFound stateUserNotFound;
   static PhoneCallStateUserNotAvailable stateUserNotAvailable;
   每个派生类都实现了getCode()和execute()以对应不同的电话状态以及状态改变时所采取的动作。
   
  例如:
  当SIP栈收到电话状态改变的通知时将以此调用
  _sipWrapper.phoneCallStateChangedEvent --(connect)--SipCallbacks::phoneCallStateChangedEventHandler
  -->PhoneLine::setPhoneCallState
   -->phoneCall::setState()
    -->stateChangedEvent
    -->phoneCall::getCode -- check PhoneCall state
     -->phoneCall::execute()
      -->phoneCall::applyState();
  
  stateChangedEvent被以下几个观察者监视
  ①ConferenceCall::phoneCallStateChangedEventHandler 
  ②ConferenceCallParticipant::phoneCallStateChangedEventHandler
  ③CPhoneCall::stateChangedEventHandler
  目前没有用到会议,因此略过①和②看CPhoneCall::stateChangedEventHandler
  
  (M)PhoneCall::stateChangedEvent
  (C)CPhoneCall::stateChangedEventHandler
  (P)PFactory::postEvent(event) -- switch context to QApplication thread
  (P)CPhoneCall::stateChangedEventHandlerThreadSafe
  (P)QtPhoneCall::stateChangedEvent
  
  如上所示,stateChangedEvent逐步调用Control和Present的函数,将状态变化传递给视图。需要注意PFactory::postEvent最后调用的是QApplication::PostEvent,
  故而postEvent后的代码是在Qt GUI线程中执行的。
  
  
  
  
  
  
  
  
  
   
  
  
  
  

 

   

 类似资料: