owt
Open WebRTC Toolkit Media Server
The media server for OWT provides an efficient video conference and streaming service that is based on WebRTC
基本概念
owt中的混屏是在類VideoMixer中實現(xiàn)的事示,owt-server/source/agent/video/videoMixer/VideoMixer.h
VideoMixer實現(xiàn)了根據(jù)指定的布局:layoutSolution 生成混屏視頻的能力亚隙。包括幾個基本概念:
input
input表示輸入圖像的流寸五,每個input有一個整數(shù)的inputIndex
通過addInput添加input招刹,其中FrameSource,會提供待解碼的數(shù)據(jù)鱼炒,來源于webrtc的videoReceiveStream渠鸽,由VideoMixer內(nèi)部解碼
bool VideoMixer::addInput(const int inputIndex, const std::string& codec, owt_base::FrameSource* source, const std::string& avatar)
output
output表示需要混屏的輸出流,每一個output有一個字符串outStreamID,在內(nèi)部被映射為一個唯一的int outputIndex
bool VideoMixer::addOutput(
const std::string& outStreamID
, const std::string& codec
, const owt_base::VideoCodecProfile profile
, const std::string& resolution
, const unsigned int framerateFPS
, const unsigned int bitrateKbps
, const unsigned int keyFrameIntervalSeconds
, owt_base::FrameDestination* dest)
FrameDestination 送到VideoSendStream墨缘,進(jìn)行編碼和打包為rtp包
layoutSolution
layoutSolution是一個混屏圖像的布局描述信息星虹,包括每個區(qū)域的坐標(biāo),大小等信息镊讼,最重要的是每一個區(qū)域包括一個inputIndex宽涌,混屏線程根據(jù)inputIndex去查找這個流的最后一幀圖像
所有布局的變化都體現(xiàn)在layoutSolution中。
一個VideoMixer只支持一個布局蝶棋,所有output都是同一個布局卸亮。
void VideoMixer::updateLayoutSolution(LayoutSolution& solution);
VideoMixer內(nèi)部邏輯
一、VideoMixer
VideoMixer內(nèi)部包含一個VideoFrameMixer m_frameMixer玩裙,VideoMixer只是對外部提供簡單的api兼贸,并沒有什么實際的工作。工作全部交給VideoFrameMixer完成吃溅。
VideoMixer的意義:
1溶诞、對外提供簡單api
2、將外部string類型的outputStreamId轉(zhuǎn)換為VideoFrameMixer需要的整數(shù)outputIndex
二决侈、VideoFrameMixer
VideoFrameMixer完成了input數(shù)據(jù)的解碼工作螺垢,解碼器是在VideoFrameMixerImpl::addInput這里創(chuàng)建的,并且將流程串聯(lián)起來赖歌,形成了inputsource->decoder->compositorIn->m_compositor的流程
bool VideoFrameMixerImpl::addInput(int input, owt_base::FrameFormat format, owt_base::FrameSource* source, const std::string& avatar) {
boost::shared_ptr<CompositeIn> compositorIn(new CompositeIn(input, avatar, m_compositor));
source->addVideoDestination(decoder.get());
decoder->addVideoDestination(compositorIn.get());
}
VideoFrameMixer的意義:
1枉圃、完成數(shù)據(jù)解碼
2、串聯(lián)工作流程
3庐冯、給內(nèi)部的SoftVideoCompositor::m_compositor提供輸入圖像
三孽亲、SoftVideoCompositor
SoftVideoCompositor完成了不同幀率,相同布局的outout的混屏工作展父。
對于不同幀率墨林,巧妙的通過內(nèi)部的2個SoftFrameGenerator,來生成不同幀率的混屏數(shù)據(jù)犯祠。
SoftVideoCompositor::SoftVideoCompositor(uint32_t maxInput, VideoSize rootSize, YUVColor bgColor, bool crop)
: m_maxInput(maxInput)
{
m_inputs.resize(m_maxInput);
for (auto& input : m_inputs) {
input.reset(new SoftInput());
}
m_avatarManager.reset(new AvatarManager(maxInput));
m_generators.resize(2);
m_generators[0].reset(new SoftFrameGenerator(this, rootSize, bgColor, crop, 60, 15));
m_generators[1].reset(new SoftFrameGenerator(this, rootSize, bgColor, crop, 48, 6));
}
通過構(gòu)造函數(shù)可以看到旭等,SoftVideoCompositor保存了input,input的數(shù)據(jù)衡载,通過上層工作流中的compositorIn調(diào)用pushInput輸入進(jìn)來搔耕,并進(jìn)行緩存:
void SoftVideoCompositor::pushInput(int input, const Frame& frame)
{
assert(frame.format == owt_base::FRAME_FORMAT_I420);
webrtc::VideoFrame* i420Frame = reinterpret_cast<webrtc::VideoFrame*>(frame.payload);
m_inputs[input]->pushInput(i420Frame);
}
2個SoftFrameGenerator,分別處理了一批相關(guān)的幀率的混屏工作,比如:
FPS為15 30 60在m_generators[0]中混屏
FPS為6 12 24 48在m_generators[1]中混屏
SoftFrameGenerator的意義:
1弃榨、為所有output緩存公用的輸入圖像
2菩收、將不同幀率,交給擅長某個幀率處理的SoftFrameGenerator中處理鲸睛,節(jié)省混屏次數(shù)娜饵,從而節(jié)省性能
四、SoftFrameGenerator
SoftFrameGenerator是真正做混屏工作的類官辈,它處理2倍關(guān)系的一批幀率的output的混屏工作箱舞。
混屏工作,通過timer線程進(jìn)行觸發(fā)拳亿,最終執(zhí)行到SoftFrameGenerator::onTimeout()函數(shù)中晴股,做混屏的具體計算
此函數(shù)內(nèi)部,通過m_counter變量肺魁,巧妙的實現(xiàn)了不同幀率只需混屏一次的性能優(yōu)化电湘。
onTimeout會按照最大幀率來觸發(fā),通過下面的核心代碼鹅经,判斷了是否需要混屏寂呛,混屏后是否需要回調(diào),觸發(fā)哪個幀率的output的回調(diào)
bool hasValidOutput = false;
{
boost::unique_lock<boost::shared_mutex> lock(m_outputMutex);
for (uint32_t i = 0; i < m_outputs.size(); i++) {
if (m_counter % (i + 1))
continue;
if (m_outputs[i].size() > 0) {
hasValidOutput = true;
break;
}
}
}
這個邏輯很巧妙瘾晃,但理解起來有點困難昧谊,可以通過下圖進(jìn)行理解: