码迷,mamicode.com
首页 > 编程语言 > 详细

libjingle主线程的消息响应

时间:2017-05-09 13:50:46      阅读:244      评论:0      收藏:0      [点我收藏+]

标签:rac   对象   webrtc   tcap   步骤   from   trunk   creat   变量   

 trunk\talk\app\webrtc\peerconnectionproxy.h文件中定义了PeerConnection的代理类,class PeerConnection : public PeerConnectionInterface,而#define BEGIN_PROXY_MAP(c) \
  class c##Proxy : public c##Interface所以BEGIN_PROXY_MAP(PeerConnection)展开即为 

 class PeerConnection : public PeerConnectionInterface。 
所以在peerconnection例子的talk\examples\peerconnection\client\conductor.cc文件Conductor::InitializePeerConnection()函数调用PeerConnectionFactory::CreatePeerConnection函数,该函数通过signaling_thread_对象调用PeerConnectionFactory::CreatePeerConnection_s,在该函数中调用PeerConnectionProxy::Create函数,trunk\talk\app\webrtc\proxy.h文件中对代理类的Create函数进行定义,形式如TestProxy::Create(Thread*, TestInterface*)。
 所以上面PeerConnectionFactory::CreatePeerConnection_s函数中调用PeerConnectionProxy::Create创建的就是PeerConnectionProxy指针对象,而且这个指针对象最终会返回给trunk\talk\examples\peerconnection\client\conductor.h文件中的成员变量talk_base::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;以后通过peer_connection_调用的函数其实都是PeerConnectionProxy::XXX。 
 又因为PeerConnectionProxy封装了一个talk_base::Thread* owner_thread_对象和PeerConnection指针对象,当通过PeerConnectionProxy::XXX调用函数时,会通过其成员变量指针(线程指针)调用PeerConnection::XXX,这里的线程指针就是trunk\talk\app\webrtc\peerconnectionfactory.h文件中PeerConnectionFactory类的成员变量talk_base::Thread* signaling_thread_;。 
在trunk\talk\base\win32socketserver.cc文件中,不太理解Win32SocketServer::Wait函数中的cms不为0时调用GetMessage(&msg, NULL, s_wm_wakeup_id, s_wm_wakeup_id);以及Win32SocketServer::MessageWindow::OnMessage函数中对消息s_wm_wakeup_id的处理。看了半天也没有明白,就只好在Win32SocketServer::Wait和Win32SocketServer::WakeUp中加OutputDebugPrintf来输出相关信息。
 在PeerConnectionFactory的构造函数中创建了两个talk_base::Thread线程signaling_thread_和worker_thread_。当双击呼叫对端时,主线程调用signaling_thread_线程指针对象发送MSG_INIT_FACTORY消息。然后是PeerConnectionFactory::Initialize_s函数对该消息的响应,在该函数中重置其成员变量talk_base::scoped_ptr<cricket::ChannelManager> channel_manager_;,并将worker_thread_指针对象传递给该成员,然后调用ChannelManager::Init初始化函数,继而调用ChannelManager::SetAudioOptions,在这个函数中通过worker_thread_指针以模板函数Thread::Invoke调用。又因为其响应函数ChannelManager::SetAudioOptions_w是在signaling_thread_线程消息响应中执行,所以不会执行到Win32SocketServer::Wait和Win32SocketServer::WakeUp函数中。 
 同样在响应MSG_INIT_FACTORY消息的PeerConnectionFactory::Initialize_s函数中,该函数调用的ChannelManager::Init中,当执行完上面ChannelManager::SetAudioOptions_w函数后,会调用ChannelManager::SetCaptureDevice函数,该函数类似于ChannelManager::SetAudioOptions函数,也通过worker_thread_指针以模板函数Thread::Invoke调用执行ChannelManager::SetCaptureDevice_w。 signaling_thread_线程指针发送的第二个消息是MSG_CREATE_PEERCONNECTION,第三个消息则是MSG_CREATE_AUDIOSOURCE。
 第三个消息则是MSG_CREATE_AUDIOSOURCE,第四个消息则是MSG_CREATE_VIDEOSOURCE,这两个消息的发送是在双击呼叫对端后,调用Conductor::InitializePeerConnection()函数进而调用Conductor::AddStreams()函数,在该函数中依次调用PeerConnectionFactory::CreateAudioSource()和PeerConnectionFactory::CreateVideoSource()函数发送。 
 在Win32SocketServer::Wait和Win32SocketServer::WakeUp函数中添加OutputDebugPrintf来输出相关信息,当在主线程中调用signaling_thread_->Send时,进入到Thread::Send发现,先调用current_thread->socketserver()->Wait(kForever, false);将当前线程即主线程Wait阻塞,然后signaling_thread_线程的消息响应循环回去响应signaling_thread_->Send所发送的消息,即在Thread::ProcessMessages中调用MessageQueue::Get,然后在这个函数中去调用Thread::ReceiveSends,在这个函数中响应了signaling_thread_->Send所发送的消息后,调用smsg.thread->socketserver()->WakeUp();即主线程的Win32SocketServer::WakeUp,这样由于在Thread::Send函数中主线程调用了current_thread->socketserver()->Wait(kForever, false);被阻塞,现在就可以继续执行了,然后主线程中执行Thread::Send的最后一条语句current_thread->socketserver()->WakeUp();即Win32SocketServer::WakeUp。 
 这样当下次在主线程中调用signaling_thread_->Send时,在Thread::Send中调用current_thread->socketserver()->Wait(kForever, false);时并不会阻塞主线程,但是signaling_thread_->Send发送的消息还未被响应即ready变量还为false,直到signaling_thread_线程的消息响应循环在调用Thread::ReceiveSends()响应了这个消息后才将ready赋值为true。所以在Thread::Send函数中的while循环中会第二次调用current_thread->socketserver()->Wait(kForever, false);这次就会阻塞主线程。
  基于以上两段的分析,所以在主线程中调用signaling_thread_->Send时,Win32SocketServer::Wait和Win32SocketServer::WakeUp函数中消息的打印次序是,第一次调用先打印Wait,然后是打印两次WakeUp,当以后调用signaling_thread_->Send时,则是先打印两次Wait,然后再打印两次WakeUp。其中后面响应signaling_thread_->Send时,先打印的Wait事实上是相当于消耗掉在前一次执行signaling_thread_->Send发送消息时,Thread::Send函数中最后一条语句current_thread->socketserver()->WakeUp();。而第二个Wait才是阻塞当前的线程即主线程。当执行signaling_thread_->Send发送消息会打印两次WakeUp,其中第二次即Thread::Send函数中最后一条语句current_thread->socketserver()->WakeUp();而第一次则是signaling_thread_线程的消息响应循环种执行的Thread::ReceiveSends函数中的smsg.thread->socketserver()->WakeUp();。 
 在响应第四个消息MSG_CREATE_VIDEOSOURCE时,在响应该消息时会调用VideoCapturer::SetCaptureState函数,在该函数中通过主线程即main函数中的talk_base::Win32Thread w32_thread;来调用thread_->Post。由于虚函数实际执行的是MessageQueue::Post函数,该函数中会调用ss_->WakeUp();即Win32SocketServer::WakeUp,然后由于Thread::Send中while循环,此消息的响应并未完成,所以会继续Wait。 

在响应第四个消息MSG_CREATE_VIDEOSOURCE时,PeerConnectionFactory::CreateVideoSource_s会调用VideoSource::Create函数进而调用VideoSource::Initialize,该函数又调用ChannelManager::StartVideoCapture,在这个函数中通过worker_thread_->Invoke来执行CaptureManager::StartVideoCapture,当worker_thread_线程消息响应循环中执行CaptureManager::StartVideoCapture中最终会调用VideoCapturer::SetCaptureState,而这个函数中通过主线程即main函数中的talk_base::Win32Thread w32_thread;来调用thread_->Post发送MSG_STATE_CHANGE消息。

 回到Thread::Send函数中,以主线程调用signaling_thread_->Send发送消息为例,检测到发送线程即this不是当前线程(主线程)后,就将消息调用sendlist_.push_back(smsg);保存起来,然后就开始等待消息的响应(Wait for a reply),当调用 ss_->WakeUp();(即PhysicalSocketServer::WakeUp)时,signaling_thread_线程的消息响应循环就会去响应该消息。所以有可能已经signaling_thread_线程的消息响应循环已经对该消息响应完而且调用了smsg.thread->socketserver()->WakeUp();,然后cpu才接着执行Thread::Send函数中ss_->WakeUp();之后的while循环,即current_thread->socketserver()->Wait(kForever, false);。当然这个时候会马上返回,而并不阻塞在Win32SocketServer::Wait函数中的GetMessage(&msg, NULL, s_wm_wakeup_id, s_wm_wakeup_id);,并且会执行Thread::Send函数的最后一条语句current_thread->socketserver()->WakeUp();。这些都是通过打印Win32SocketServer::Wait和Win32SocketServer::WakeUp函数中的相关信息验证得到的。 
 
当双击呼叫对端候,主线程中调用signaling_thread_->Send发送消息依次为MSG_INIT_FACTORY、MSG_CREATE_PEERCONNECTION、MSG_CREATE_AUDIOSOURCE和MSG_CREATE_VIDEOSOURCE。然后在Conductor::AddStreams函数中继续执行,会继续调用signaling_thread_->Send发送消息。如下图所示:
技术分享
 在Conductor::ConnectToPeer函数中调用Conductor::InitializePeerConnection函数,进而调用Conductor::AddStreams(),在Conductor::ConnectToPeer函数中执行完InitializePeerConnection后会调用PeerConnectionProxy::CreateOffer,由于PeerConnectionProxy关联了signaling_thread_所以也会调用signaling_thread_->Send发送消息。在这个步骤执行完之后就开始调用执行Win32SocketServer::MessageWindow::OnMessage函数中的ss_->Pump();(即Win32SocketServer::Pump()),最终会在该函数中调用MessageQueue::Dispatch执行VideoCapturer::OnMessage响应之前调用主线程(即talk_base::Win32Thread w32_thread;)发送的MSG_STATE_CHANGE消息。 
 
 为什么是在PeerConnectionProxy::CreateOffer执行后,才能执行到Win32SocketServer::MessageWindow::OnMessage函数的里面来执行ss_->Pump();,这是因为从调用signaling_thread_->Send发送第一个消息MSG_INIT_FACTORY开始,主线程中一直通过以下形式执行代码,即主线程通过调用Win32SocketServer::Wait函数中的GetMessage(&msg, NULL, s_wm_wakeup_id, s_wm_wakeup_id);来阻塞,然后Win32SocketServer::WakeUp()(signaling_thread_线程和主线程)执行PostMessage(wnd_.handle(), s_wm_wakeup_id, 0, 0);唤醒。主线程一直在响应双击呼叫对端事件,即一直在执行Conductor::ConnectToPeer,当执行完毕该函数后才有机会去在主线程中响应MSG_STATE_CHANGE消息,该消息是在Conductor::ConnectToPeer函数执行过程中通过主线程Post到主线程的消息队列中。
  接上一段当执行完PeerConnectionProxy::CreateOffer后,由于在Thread::Send的最后通过调用current_thread->socketserver()->WakeUp();,该函数即Win32SocketServer::WakeUp(),这个函数PostMessage(wnd_.handle(), s_wm_wakeup_id, 0, 0);,所以主线程中创建的Win32SocketServer对象的成员变量MessageWindow wnd_;会收到消息s_wm_wakeup_id,其实应该是主线程收到消息,因为主线程(即talk_base::Win32Thread w32_thread;)中创建了Win32SocketServer对象及其成员变量MessageWindow wnd_;,根据MSDN对PostMessage函数的解释"Places (posts) a message in the message queue associated with the thread that created the specified window and returns without waiting for the thread to process the message."。所以主线程的消息队列中收到s_wm_wakeup_id消息。这样说明了主线程通过Win32SocketServer::Wait函数的GetMessage(&msg, NULL, s_wm_wakeup_id, s_wm_wakeup_id);,而signaling_thread_线程通过Win32SocketServer::WakeUp函数中的PostMessage(wnd_.handle(), s_wm_wakeup_id, 0, 0);来唤醒,由于"函数GetMessage 是 从调用线程的消息队列里取得一个消息并将其放于指定的结构。GetMessage不接收属于其他线程或应用程序的消息。",但是通过PostMessage的MSDN注释可以明白子线程唤醒主线程。
而且在Conductor::ConnectToPeer函数中会调用主线程发送MSG_STATE_CHANGE消息,会唤醒一个主线程的等待,但是在主线程通过Thread::Send函数中的while循环判断当前的消息并没有被响应会继续阻塞,即主线程消耗掉了一次唤醒,这样当Thread::Send执行到最后,即当前消息发送的流程执行完之后需要再一次唤醒主线程(或者叫当前线程,因为这里是以主线程为例)。看一下Thread::Send函数最后的注释很详细。 
 双击呼叫对端后会执行Conductor::OnMessageFromPeer函数,然后也会调用signaling_thread_->Send发送消息,如下图所示,其中PeerConnectionProxy::AddIceCandidate可能会执行多次。 
 技术分享
 双击呼叫对端后会执行Conductor::OnMessageFromPeer函数,然后也会调用signaling_thread_->Send发送消息,同时打印Win32SocketServer::MessageWindow::OnMessage函数中的ss_->Pump();,发现每调用PeerConnectionProxy::AddIceCandidate执行一次后,都会打印输出ss_->Pump();,而在VideoTrackProxy::AddRenderer之后,有可能打印输出也可能不打印输出。其他就没有再打印输出ss_->Pump();的地方了。 
 当执行Conductor::OnMessageFromPeer函数,第一次调用signaling_thread_->Send发送消息是在PeerConnectionProxy::SetRemoteDescription,此时通过PostThreadMessage将消息NEW_STREAM_ADDED发送给主线程,然后主线程通过Conductor::UIThreadCallback来响应该消息两次调用signaling_thread_->Send发送消息。 
至此,对trunk\talk\base\win32socketserver.cc文件的Win32SocketServer::Wait函数中的GetMessage(&msg, NULL, s_wm_wakeup_id, s_wm_wakeup_id);以及Win32SocketServer::MessageWindow::OnMessage函数中的对s_wm_wakeup_id消息响应的理解告一段落了。
 
 
 

libjingle主线程的消息响应

标签:rac   对象   webrtc   tcap   步骤   from   trunk   creat   变量   

原文地址:http://www.cnblogs.com/wongdu2014/p/6830014.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!