• licode学习之erizo篇--Pipeline_service


    Pipeline是媒体处理的核心流程逻辑。

    Pipeline里面定义了两个主要的概念:Service和Handler。

    Service负责处理那些不仅要看当前数据包,还要分析之前的数据包的那些业务,比如丢包重传;Handler处理当前的数据包的情形,比如生成填充字节。

    在Pipeline里面,Handler和Service是配合起来一起工作的,他们通过一套框架将之关联起来。

    先看看使用上:

    void MediaStream::initializePipeline() {
      handler_manager_ = std::make_shared<HandlerManager>(shared_from_this());
      pipeline_->addService(shared_from_this());
      pipeline_->addService(handler_manager_);
      pipeline_->addService(rtcp_processor_);
      pipeline_->addService(stats_);
      pipeline_->addService(quality_manager_);
      pipeline_->addService(packet_buffer_);
    
      pipeline_->addFront(std::make_shared<PacketReader>(this));
    
      pipeline_->addFront(std::make_shared<RtcpProcessorHandler>());
      pipeline_->addFront(std::make_shared<FecReceiverHandler>());
      pipeline_->addFront(std::make_shared<LayerBitrateCalculationHandler>());
      pipeline_->addFront(std::make_shared<QualityFilterHandler>());
      pipeline_->addFront(std::make_shared<IncomingStatsHandler>());
      pipeline_->addFront(std::make_shared<RtpTrackMuteHandler>());
      pipeline_->addFront(std::make_shared<RtpSlideShowHandler>());
      pipeline_->addFront(std::make_shared<RtpPaddingGeneratorHandler>());
      pipeline_->addFront(std::make_shared<PliPacerHandler>());
      pipeline_->addFront(std::make_shared<BandwidthEstimationHandler>());
      pipeline_->addFront(std::make_shared<RtpPaddingRemovalHandler>());
      pipeline_->addFront(std::make_shared<RtcpFeedbackGenerationHandler>());
      pipeline_->addFront(std::make_shared<RtpRetransmissionHandler>());
      pipeline_->addFront(std::make_shared<SRPacketHandler>());
      pipeline_->addFront(std::make_shared<SenderBandwidthEstimationHandler>());
      pipeline_->addFront(std::make_shared<LayerDetectorHandler>());
      pipeline_->addFront(std::make_shared<OutgoingStatsHandler>());
      pipeline_->addFront(std::make_shared<PacketCodecParser>());
    
      pipeline_->addFront(std::make_shared<PacketWriter>(this));
      pipeline_->finalize();
      pipeline_initialized_ = true;
    }

    在初始化时,pipeline调用了addService和addFront接口,将Service和Handler添加到pipeline中去。在初始化里面,我们可以看到其支持了哪些处理。

    在实际使用中,接收到的数据,调用pipeline的read接口,就完成了解析为裸数据的事儿;调用write接口,就完成了fec等处理数据的事儿。

    pipeline的数据,read的源需要是srtp解密后的数据,处理后为rtp裸数据;write的源为rtp裸数据,处理后的数据经过srtp加密输出到网络。(网络使用的是DtlsTransport接口对接的)

    这些功能先不去管它,这里先弄清楚他们的架构和工作方式。

    阅读这块儿的代码真是不容易,使用了很多模板类,为了方便理解,菜鸟哥根据代码,把所有的模板类替换为了实际的基类,来进行理解。

    先看看pipeline的Service部分的继承体系以及数据结构:

    再结合PipelineBase的addService实现,看一下Service是干啥用的

    template <class S>
    void PipelineBase::addService(std::shared_ptr<S> service) {
      typedef typename ServiceContextType<S>::type Context;
      service_ctxs_.push_back(std::make_shared<Context>(shared_from_this(), std::move(service)));
    }
    template <class Service>
    struct ServiceContextType {
      typedef ServiceContextImpl<Service> type;
    };

    addService其实就是传递一个Service的子类对象,这个子类对象是用来给Context的构造函数传递参数的;Context就是ServiceContextImpl,也就是说addService里面的参数,就是为了创建一个ServiceContextImpl对象,这个对象创建出来以后,被存储在pipelinebase的service_ctxs_成员中。在addService接口中,还将pipeline自身,作为参数,传递给了ServiceContextImpl。通过代码看看这些参数怎么用

      explicit ServiceContextImpl(
          std::weak_ptr<PipelineBase> pipeline,
          std::weak_ptr<S> service) {
        this->impl_ = this;
        this->initialize(pipeline, std::move(service));
      }
    
      void initialize(
          std::weak_ptr<PipelineBase> pipeline,
          std::weak_ptr<S> service) {
        pipeline_weak_ = pipeline;
        pipeline_raw_ = pipeline.lock().get();
        service_ = std::move(service);
      }

        std::weak_ptr<S> getService() {
           return service_;
        }

    
    

    ServiceContextImpl在构造时存储了PipelineBase和Service,这样外面再使用时,可以通过getService来获取到Service的实例。

    这个获取操作很重要,看一下pipeline的notifyUpdate方法,看实际的处理handler(RtcpProcessorHandler)

    void RtcpProcessorHandler::notifyUpdate() {
      auto pipeline = getContext()->getPipelineShared();
      if (pipeline && !stream_) {
        stream_ = pipeline->getService<MediaStream>().get();
        processor_ = pipeline->getService<RtcpProcessor>();
        stats_ = pipeline->getService<Stats>();
      }
    }

    这个地方好神奇,通过调用getService方法,模板传递不同的类型,则能够获取到不同的对象实例。看一下getService方法

    template <class S>
    std::shared_ptr<S> PipelineBase::getService() {
      auto ctx = getServiceContext<S>();
      return ctx ? ctx->getService().lock() : std::shared_ptr<S>();
    }
    
    template <class S>
    typename ServiceContextType<S>::type* PipelineBase::getServiceContext() {
      for (auto pipeline_service_ctx : service_ctxs_) {
        auto ctx = dynamic_cast<typename ServiceContextType<S>::type*>(pipeline_service_ctx.get());
        if (ctx) {
          return ctx;
        }
      }
      return nullptr;
    }

    在getServiceContext方法里面,遍历了pipeline的service_ctxs_,并对每一个ctx进行dynamic_cast转换,能够成功,就返回,不能成功就继续。这个地方真是灵活使用,奇思妙想。

    到这里,就形成了一个共享的方式,所有的handler,都可以获得到所有的service的子类实例,在实现过程中就极大的提升了灵活性,每个service独立做自己的事儿,并且由handler直接进行数据驱动,简直太符合这个媒体处理的需要了。

    总结:Service的核心意义是共享,即每个handler都可以通过类型来获取到所有的Service子类实例,进行使用,而不必要为每个Handler定义不同的接口来传递Service对象。Service也为了多个Handler公用数据而提供服务。

  • 相关阅读:
    【机器学习】:Xgboost和GBDT的不同与比较
    golang pprof
    终于解决了AMD RYZEN 3970X的散热问题
    2022,你好
    二叉树的遍历 → 不用递归,还能遍历吗
    异或运算的巧用 → 不用额外的变量,如何交换两个变量的值?
    关于 RocketMQ 事务消息的正确打开方式 → 你学废了吗
    单向链表的花式玩法 → 还在玩反转?
    二叉树的简单实战 → 一起温故下二叉树的遍历
    序列化和反序列化
  • 原文地址:https://www.cnblogs.com/limedia/p/licode_erizo_pipeline_service.html
Copyright © 2020-2023  润新知