一、相关类介绍
licode中的pipeline 内部是一个保存PipelineContext的vector,有IN(输入)、OUT(输出)、BOTH(输入输出)三种类型。
std::vector<PipelineContext*> inCtxs_;
std::vector<PipelineContext*> outCtxs_;
Both 类型对应ContextImpl
pipeline中的类基本都是模板类,下面看下怎么添加到vector中的:
template <class H>
PipelineBase& PipelineBase::addFront(std::shared_ptr<H> handler) {
typedef typename ContextType<H>::type Context;
return addHelper(
std::make_shared<Context>(shared_from_this(), std::move(handler)),
true);
}
addFront的参数是流处理的一些handler,比如RtcpProcessorHandler、FecReceiverHandler等,这些handler和PipeLineBase作为参数生成Context,vector中保存的其实是Context。分析下Context具体是什么。
//是个结构体模板,这个语法比较难度,下面把它展开
template <class Handler>
struct ContextType {
typedef typename std::conditional<
Handler::dir == HandlerDir::BOTH,
ContextImpl<Handler>,
typename std::conditional<
Handler::dir == HandlerDir::IN,
InboundContextImpl<Handler>,
OutboundContextImpl<Handler>
>::type>::type
type;
};
//展开这个模板,相当于下面的语法
if(Handler::dir == HandlerDir::BOTH)
{
type = ContextImpl<Handler>;
}
else if (Handler::dir == HandlerDir::IN)
{
type = InboundContextImpl<Handler> ;
}
else
type = OutboundContextImpl<Handler>;
所以ContextType根据参数Handler的不同类型,其type分别对应
ContextImpl<Handler>、 InboundContextImpl<Handler> 、OutboundContextImpl<Handler>
这三个模板类都继承自PipelineContext,对应文章开头说的pipeline其实就是保存pipeline的vector
二、pipeline 是怎么read的
pipeline在addFront()添加完Handler后,会调用pipeline_->finalize();
每个PipeContext都有nextIn_和nextOut_两个变量,finalize会遍历vector,每个PipeConext的nextIn_会指向vector中下一个PipeContext,相当于把vector中的所有Handler组成一个“链表”联系起来,其中front_是IN的头,back_是OUT的头。代码如下:
void Pipeline::finalize() {
front_ = nullptr;
if (!inCtxs_.empty()) {
front_ = dynamic_cast<InboundLink*>(inCtxs_.front());
for (size_t i = 0; i < inCtxs_.size() - 1; i++) {
inCtxs_[i]->setNextIn(inCtxs_[i+1]);
}
inCtxs_.back()->setNextIn(nullptr);
}
back_ = nullptr;
if (!outCtxs_.empty()) {
back_ = dynamic_cast<OutboundLink*>(outCtxs_.back());
for (size_t i = outCtxs_.size() - 1; i > 0; i--) {
outCtxs_[i]->setNextOut(outCtxs_[i-1]);
}
outCtxs_.front()->setNextOut(nullptr);
}
if (!front_) {
if (!back_) {
}
for (auto it = ctxs_.rbegin(); it != ctxs_.rend(); it++) {
(*it)->attachPipeline();
}
for (auto it = service_ctxs_.rbegin(); it != service_ctxs_.rend(); it++) {
(*it)->attachPipeline();
}
notifyUpdate();
}
1、真正读取的read函数
拿 InboundContextImpl<Handler>举例。OUT也类似,不再单独举例。
void Pipeline::read(std::shared_ptr<DataPacket> packet) {
if (!front_) {
return;
}
front_->read(std::move(packet));
}
//这里的front对应一个InboundContextImpl
// InboundLink overrides
void read(std::shared_ptr<DataPacket> packet) override {
auto guard = this->pipelineWeak_.lock();
//注意第一个参数this,作为参数传递给Handler了,Handler的read会调用
this->handler_->read(this, std::move(packet));
}
//InboundContextImpl 的read中会调用它的Handler,这个handler就是前面addFront时,作为
//模板参数和PipeLineBase一起传进来的
Handler内部也同样会调用read,下面以RtcpProcessorHandler 举例,代码如下:
void RtcpProcessorHandler::read(Context *ctx, std::shared_ptr<DataPacket> packet) {
RtcpHeader *chead = reinterpret_cast<RtcpHeader*> (packet->data);
if (chead->isRtcp()) {
if (chead->packettype == RTCP_Sender_PT) { // Sender Report
processor_->analyzeSr(chead);
}
} else {
if (stats_->getNode()["total"].hasChild("bitrateCalculated")) {
processor_->setPublisherBW(stats_->getNode()["total"]["bitrateCalculated"].value());
}
}
processor_->checkRtcpFb();
ctx->fireRead(std::move(packet));
}
可以看到RtcpProcessorHandler中会调用ctx->fireRead(),这里的ctx就是前面InboundContextImpl。fireRead中会调用
nextIn_即下个Handler,所以整个链条就串起来了。
// InboundHandlerContext overrides
void fireRead(std::shared_ptr<DataPacket> packet) override {
auto guard = this->pipelineWeak_.lock();
if (this->nextIn_) {
this->nextIn_->read(std::move(packet));
}
}