WebRTC源码分析之模块的执行-Module


WebRTC中将某些功能划分了模块,现在介绍一下模块是如何执行的。WebRTC中会创建一个线程用于执行模块和异步的任务,模块生成以后注册到线程中,模块执行时会计算下次需要执行的时间,线程根据模块提供的时间会按照指定的时间再次调用模块。线程在处理模块的同时也可以处理其他线程投递的任务。

Module使用示例
工程

先创建一个工程用于运行示例。如何创建工程,参考《WebRTC源码分析之工程-project》,在src\examples\BUILD.gn中添加如下内容:

rtc_executable("webrtc_learn"){
    testonly = true
  sources = [
    "webrtclearn/main.cc"         
  ]
  deps = [
    "../rtc_base:rtc_base",                     
    "../modules/utility:utility"
  ]
}
示例
#include <iostream>
#include "api/task_queue/queued_task.h"
#include "modules/utility/source/process_thread_impl.h"
#include "modules/include/module.h"
#include "rtc_base/location.h"

using namespace std;
using namespace webrtc;

/*模块一*/
class my_module_one :public Module
{
public:
    /*每秒执行一次*/
    int64_t TimeUntilNextProcess() { return 1000; }
    
    void Process()
    {
        static int count = 0;
        cout << "process module_one  " << count++ << endl;
    }
};

/*模块二*/
class my_module_two : public Module 
{
public:
    /*每两秒执行一次*/
    int64_t TimeUntilNextProcess() { return 2000; }

    void Process() 
    {
        static int count = 0;
        cout << "process module_two  " << count++ << endl;
    }
};

/*任务*/
class asyn_task : public QueuedTask 
{
 public:
  asyn_task(string task_name)
      : task_name_(task_name) {}

  bool Run() override 
  {
    cout << "post a " << task_name_ <<endl;

    return true;
  }

 private:
  string task_name_;
};

int main()
{
    /*创建模块处理对象*/
    unique_ptr<ProcessThread> pt = ProcessThread::Create("process thread");

    /*定义任务*/
    unique_ptr<QueuedTask> task1(new asyn_task("task_one"));
    unique_ptr<QueuedTask> task2(new asyn_task("task_two"));
    
    /*定义模块*/
    my_module_one mmo;
    my_module_two mmt;

    /*开始处理任务*/
    pt->Start();
    
    /*模块注册*/
    pt->RegisterModule(&mmo, RTC_FROM_HERE);
    pt->RegisterModule(&mmt, RTC_FROM_HERE);

    /*3秒后投递一个即时任务*/
    Sleep(3000);
    pt->PostTask(std::move(task1));

    /*3秒后再投递一个即时任务*/
    Sleep(3000);
    pt->PostTask(std::move(task2));

    /*1秒后注销模块*/
    Sleep(1000);
    pt->DeRegisterModule(&mmo);
    pt->DeRegisterModule(&mmt);

    /*关闭模块处理线程,回收资源。*/
    pt->Stop();

    return 0;
}

在这里插入图片描述

向模块处理线程注册了两个模块,其中一个模块一秒执行一次,另外一个模块两秒执行一次。在模块的执行期间向模块处理线程投递了两个异步的任务。

Module源码分析
类关系图

在这里插入图片描述

ProcessThread类

ProcessThread类所在文件的位置:src\modules\utility\include\process_thread.h

class ProcessThread 
{
 public:
  virtual ~ProcessThread();

  /*用于创建ProcessThread类*/
  static std::unique_ptr<ProcessThread> Create(const char* thread_name);

  /*创建模块处理线程*/
  virtual void Start() = 0;

  /*终止模块处理线程,并回收资源。*/
  virtual void Stop() = 0;

  /*激活模块处理线程,运行指定模块。*/
  virtual void WakeUp(Module* module) = 0;

  /*向模块处理线程投递任务*/
  virtual void PostTask(std::unique_ptr<QueuedTask> task) = 0;

  /*模块注册*/
  virtual void RegisterModule(Module* module, const rtc::Location& from) = 0;

  /*注销模块*/
  virtual void DeRegisterModule(Module* module) = 0;
};
Module类

ProcessThread类所在文件的位置:src\modules\include\module.h

class Module 
{
 public:
  /*返回下一次执行Process函数的时间,单位是毫秒。*/
  virtual int64_t TimeUntilNextProcess() = 0;

  /*模块处理函数*/
  virtual void Process() = 0;

  /*绑定或者解绑ProcessThread到当前模块*/
  virtual void ProcessThreadAttached(ProcessThread* process_thread) {}

 protected:
  virtual ~Module() {}
};

模块需要继承本接口,覆写指定的函数。

QueuedTask类

QueuedTask类所在文件的位置:src\api\task_queue\queued_task.h

class QueuedTask 
{
 public:
  virtual ~QueuedTask() = default;
  virtual bool Run() = 0;
};

所有需要执行的任务都需要继承此类,并覆写Run()函数。

ProcessThreadImpl类

ProcessThreadImpl类所在文件的位置:src\modules\utility\source\process_thread_impl.h process_thread_impl.cc

ModuleCallback类
struct ModuleCallback
{
    ModuleCallback() = delete;
    ModuleCallback(ModuleCallback&& cb) = default;
    ModuleCallback(const ModuleCallback& cb) = default;

    /*构造器*/
    ModuleCallback(Module* module, const rtc::Location& location)
        : module(module), location(location) {}

    bool operator==(const ModuleCallback& cb) const
    {
        return cb.module == module;
    }

    /*保存的模块*/
    Module* const module;
    
    /*模块下次执行时间,绝对时间。*/
    int64_t next_callback = 0;     
    
    /*模块注册时的位置*/
    const rtc::Location location;   
    
private:
    ModuleCallback& operator=(ModuleCallback&);
};

对模块的简单封装

数据成员
typedef std::list<ModuleCallback> ModuleList;

/*模块处理线程中注册的模块*/
ModuleList modules_;

/*互斥锁*/
rtc::CriticalSection lock_;

/*记录ProcessThreadImpl对象创建时的线程*/
rtc::ThreadChecker thread_checker_;

/*用于阻塞、唤醒模块处理线程*/
rtc::Event wake_up_;

/*模块处理线程对象,线程句柄。*/
std::unique_ptr<rtc::PlatformThread> thread_;

/*任务队列,保存投递的任务。*/
std::queue<QueuedTask*> queue_;

/*是否结束模块处理线程*/
bool stop_;

/*模块处理线程的名字*/
const char* thread_name_;  
构造器与析构器
ProcessThreadImpl::ProcessThreadImpl(const char* thread_name)
    : stop_(false), thread_name_(thread_name) {}

std::unique_ptr<ProcessThread> ProcessThread::Create(const char* thread_name) 
{
  return std::unique_ptr<ProcessThread>(new ProcessThreadImpl(thread_name));
}

ProcessThreadImpl对象的创建可以通过静态成员函数Create()创建。

ProcessThreadImpl::~ProcessThreadImpl() 
{
  /*需要在主线程中销毁ProcessThreadImpl对象*/
  RTC_DCHECK(thread_checker_.IsCurrent());
  RTC_DCHECK(!thread_.get());
  RTC_DCHECK(!stop_);

  /*清空任务队列*/
  while (!queue_.empty()) 
  {
    delete queue_.front();
    queue_.pop();
  }
}
模块处理线程的创建与销毁
void ProcessThreadImpl::Start() 
{
  /*需要在主线程中执行本函数*/
  RTC_DCHECK(thread_checker_.IsCurrent());
  RTC_DCHECK(!thread_.get());

  /*判断是否创建了模块处理线程*/
  if (thread_.get())
    return;

  /*若模块处理线程已经运行则断言失败*/
  RTC_DCHECK(!stop_);

  /*遍历module列表,将ProcessThreadImpl绑定到模块上。*/
  for (ModuleCallback& m : modules_)
    m.module->ProcessThreadAttached(this);

  /*创建模块处理线程对象*/
  thread_.reset(new rtc::PlatformThread(&ProcessThreadImpl::Run, this, thread_name_));

  /*创建并运行模块处理线程*/
  thread_->Start();
}

创建模块处理线程并运行

void ProcessThreadImpl::Stop() 
{
  /*只能在主线程销毁模块处理线程*/
  RTC_DCHECK(thread_checker_.IsCurrent());
    
  if (!thread_.get())
    return;

  {
    rtc::CritScope lock(&lock_);
    /*终止模块处理线程*/
    stop_ = true;  
  }

  /*主线程进入阻塞,等待模块处理线程退出。*/
  wake_up_.Set();  

  /*模块处理线程主动终止后,会唤醒主线程回收资源。*/
  thread_->Stop();  
  
  /*模块处理线程结束后,更改子线程状态。*/
  stop_ = false;    

  /*释放线程对象*/
  thread_.reset();   

  /*遍历module列表,解除绑定到模块上的ProcessThreadImpl。*/
  for (ModuleCallback& m : modules_)
    m.module->ProcessThreadAttached(nullptr);
}

主线程中主动终止模块处理线程,不能强行终止,需要等待模块处理线程主动退出,在等待的过程中主线程会进入睡眠状态,模块处理线程结束后会唤醒主线程。

模块的注册与注销
void ProcessThreadImpl::RegisterModule(Module* module,const rtc::Location& from) 
{
  /*模块注册的工作只能在主线程中进行*/
  RTC_DCHECK(thread_checker_.IsCurrent());
  RTC_DCHECK(module) << from.ToString();

#if RTC_DCHECK_IS_ON
  {
    rtc::CritScope lock(&lock_);

	/*遍历modules_,查看注册模块是否已经存在,不能重复注册。*/
    for (const ModuleCallback& mc : modules_) 
	{
      RTC_DCHECK(mc.module != module)
          << "Already registered here: " << mc.location.ToString() 
          << "\n"
          << "Now attempting from here: " << from.ToString();
    }
  }
#endif

  /*绑定ProcessThreadImpl到模块中*/
  if (thread_.get())
    module->ProcessThreadAttached(this);

  /*将模块推到modules_中*/
  {
    rtc::CritScope lock(&lock_);
    modules_.push_back(ModuleCallback(module, from));    
  }

    /*唤醒模块处理线程,处理新注册的模块。*/
    wake_up_.Set(); 
}

注册模块时,有可能模块处理线程处于阻塞状态,需要将阻塞的线程唤醒。

void ProcessThreadImpl::DeRegisterModule(Module* module) 
{
  /*注销模块只能在主线程中进行*/
  RTC_DCHECK(thread_checker_.IsCurrent());
  RTC_DCHECK(module);

  {
    rtc::CritScope lock(&lock_);

	/*移除指定的模块*/
    modules_.remove_if(
    [&module](const ModuleCallback& m) { return m.module == module; });
  }
  
  /*注销模块中的ProcessThreadImpl*/
  module->ProcessThreadAttached(nullptr);   
}

注销模块,需要把模块处理线程中的模块移除,同时把模块内的ProcessThreadImpl也移除。

投递任务
void ProcessThreadImpl::PostTask(std::unique_ptr<QueuedTask> task) 
{
  {
    rtc::CritScope lock(&lock_);

	/*往任务队列中投递一个任务。*/
    queue_.push(task.release());  
  }

  /*唤醒模块处理线程处理任务。*/
  wake_up_.Set();     
}

模块处理线程不仅仅能处理模块,也可以处理其他线程投递的任务。

执行指定模块
const int64_t kCallProcessImmediately = -1;

用于标识模块需要立即执行

void ProcessThreadImpl::WakeUp(Module* module) 
{
  {
    rtc::CritScope lock(&lock_);
 
	/*遍历modules_列表,找到指定的模块。*/
    for (ModuleCallback& m : modules_)
	{
      if (m.module == module)
		/*将模块标识为立即处理*/
        m.next_callback = kCallProcessImmediately; 
    }
  }

  /*唤醒模块处理线程,有模块需要处理。*/
  wake_up_.Set();       
}
模块的处理
bool ProcessThreadImpl::Run(void* obj) 
{
  return static_cast<ProcessThreadImpl*>(obj)->Process();
}

模块处理线程的入口函数

int64_t GetNextCallbackTime(Module* module, int64_t time_now) 
{
  /*模块下次被调用的时间间隔*/
  int64_t interval = module->TimeUntilNextProcess();
    
  /*时间间隔为负数,表示模块需要立即被调用。*/
  if (interval < 0)   
  {
    return time_now;
  }

  return time_now + interval;
}

将模块调用的时间间隔转换为绝对的时间

bool ProcessThreadImpl::Process() 
{
  TRACE_EVENT1("webrtc", "ProcessThreadImpl", "name", thread_name_);

  /*获取当前时间*/
  int64_t now = rtc::TimeMillis();
  int64_t next_checkpoint = now + (1000 * 60);      /*60秒后*/

  {
    rtc::CritScope lock(&lock_);
    
    /*主线程标识模块处理线程退出时,结束执行,退出线程。*/
    if (stop_)    
      return false;

	/*
	 * 遍历modules_,处理每个module,检查是否到了module的调用时间。
	 * 到了调用时间的模块,调用模块内的处理函数Process()。
	 */
    for (ModuleCallback& m : modules_) 
	{
	  /*模块首次被调用,计算模块下次调用的绝对时间。*/
      if (m.next_callback == 0)
        m.next_callback = GetNextCallbackTime(m.module, now);

	  /* 如果模块的调用时间到了,或者模块被标识为立即调用,
	   * 则调用模块内的处理函数Process()。*/
      if (m.next_callback <= now || m.next_callback == kCallProcessImmediately) 
	  {
        {
          TRACE_EVENT2("webrtc", "ModuleProcess", "function",
                       m.location.function_name(), "file",
                       m.location.file_and_line());

          /*调用模块内的处理函数*/
          m.module->Process();  
        }

        int64_t new_now = rtc::TimeMillis();

	    /*模块处理结束后,更新模块下次调用的时间。*/
        m.next_callback = GetNextCallbackTime(m.module, new_now);
      }

	  /*找到所模块中最小的下次调用时间*/
      if (m.next_callback < next_checkpoint)
        next_checkpoint = m.next_callback;
    }

	/*处理完模块,处理线程的异步任务。*/
    while (!queue_.empty()) 
	{
      /*任务的处理方式按照FIFO*/
      QueuedTask* task = queue_.front();   
      queue_.pop();     
      lock_.Leave();    
      
      /*处理任务*/
      task->Run();      
      delete task;
        
      lock_.Enter();
    }
  }

  /*计算模块处理线程的阻塞时间*/
  int64_t time_to_wait = next_checkpoint - rtc::TimeMillis();

  /*若等待时间大于零,则将模块处理线程阻塞指定时间。*/
  if (time_to_wait > 0)  
    wake_up_.Wait(static_cast<int>(time_to_wait));

  return true;
}

模块处理线程不仅会处理模块,而且会处理其他线程投递的任务。

小结

本文介绍了模块是如何执行的,关于模块的更多知识需要继续阅读源码。

参考:WebRTC的模块处理机制

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WebRTC(Web Real-Time Communication)是一个开源项目,它提供了在浏览器中实现实时音视频通信的技术。下面是对WebRTC源码的简要分析WebRTC源码主要分为以下几个模块: 1. 信令(Signaling)模块:负责建立和维护通信的连接。它使用WebSocket或者其他协议进行通信,包括传输SDP(Session Description Protocol)和ICE(Interactive Connectivity Establishment)信息。 2. 媒体(Media)模块:处理音视频数据的采集、编码、解码和传输。媒体模块使用WebRTC提供的API,通过WebRTC的PeerConnection建立点对点的媒体通信。 3. 网络(Networking)模块:处理网络传输相关的功能,例如NAT穿越、ICE候选地址的收集和选择、STUN和TURN服务器的使用等。 4. 安全(Security)模块:处理加密和身份验证相关的功能,确保通信过程的安全性和隐私性。 5. SDP解析(SDP Parsing)模块:解析和生成SDP信息,SDP包含了关于媒体会话的描述和参数。 6. ICE代理(ICE Agent)模块:负责管理ICE协议的运行,处理候选地址的收集和选择,以及NAT穿越等功能。 7. RTP/RTCP模块:处理音视频的实时传输协议(RTP)和实时传输控制协议(RTCP),包括数据包的发送和接收、丢包恢复、拥塞控制等。 8. 编解码器(Codec)模块:负责音视频数据的编码和解码,WebRTC支持一系列开源编解码器,如VP8、VP9、H.264等。 这些模块之间相互协作,实现了基于浏览器的实时音视频通信。WebRTC源码使用C++语言编写,涉及到了底层的网络和媒体处理,同时也提供了一系列的API供开发者使用。 请注意,由于WebRTC源码较为庞大,这里只是简要地介绍了主要模块,实际的源码分析需要深入研究和阅读源码
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值