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中实现的,owt-server/source/agent/video/videoMixer/VideoMixer.h
VideoMixer实现了根据指定的布局:layoutSolution 生成混屏视频的能力。包括几个基本概念:
input
input表示输入图像的流,每个input有一个整数的inputIndex
通过addInput添加input,其中FrameSource,会提供待解码的数据,来源于webrtc的videoReceiveStream,由VideoMixer内部解码
bool VideoMixer::addInput(const int inputIndex, const std::string& codec, owt_base::FrameSource* source, const std::string& avatar)
output
output表示需要混屏的输出流,每一个output有一个字符串outStreamID,在内部被映射为一个唯一的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,进行编码和打包为rtp包
layoutSolution
layoutSolution是一个混屏图像的布局描述信息,包括每个区域的坐标,大小等信息,最重要的是每一个区域包括一个inputIndex,混屏线程根据inputIndex去查找这个流的最后一帧图像
所有布局的变化都体现在layoutSolution中。
一个VideoMixer只支持一个布局,所有output都是同一个布局。
void VideoMixer::updateLayoutSolution(LayoutSolution& solution);
VideoMixer内部逻辑
一、VideoMixer
VideoMixer内部包含一个VideoFrameMixer m_frameMixer,VideoMixer只是对外部提供简单的api,并没有什么实际的工作。工作全部交给VideoFrameMixer完成。
VideoMixer的意义:
1、对外提供简单api
2、将外部string类型的outputStreamId转换为VideoFrameMixer需要的整数outputIndex
二、VideoFrameMixer
VideoFrameMixer完成了input数据的解码工作,解码器是在VideoFrameMixerImpl::addInput这里创建的,并且将流程串联起来,形成了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 compositorIn(new CompositeIn(input, avatar, m_compositor));
source->addVideoDestination(decoder.get());
decoder->addVideoDestination(compositorIn.get());
}
VideoFrameMixer的意义:
1、完成数据解码
2、串联工作流程
3、给内部的SoftVideoCompositor::m_compositor提供输入图像
三、SoftVideoCompositor
SoftVideoCompositor完成了不同帧率,相同布局的outout的混屏工作。
对于不同帧率,巧妙的通过内部的2个SoftFrameGenerator,来生成不同帧率的混屏数据。
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));
}
通过构造函数可以看到,SoftVideoCompositor保存了input,input的数据,通过上层工作流中的compositorIn调用pushInput输入进来,并进行缓存:
void SoftVideoCompositor::pushInput(int input, const Frame& frame)
{
assert(frame.format == owt_base::FRAME_FORMAT_I420);
webrtc::VideoFrame* i420Frame = reinterpret_cast(frame.payload);
m_inputs[input]->pushInput(i420Frame);
}
2个SoftFrameGenerator,分别处理了一批相关的帧率的混屏工作,比如:
FPS为15 30 60在m_generators[0]中混屏
FPS为6 12 24 48在m_generators[1]中混屏
SoftFrameGenerator的意义:
1、为所有output缓存公用的输入图像
2、将不同帧率,交给擅长某个帧率处理的SoftFrameGenerator中处理,节省混屏次数,从而节省性能
四、SoftFrameGenerator
SoftFrameGenerator是真正做混屏工作的类,它处理2倍关系的一批帧率的output的混屏工作。
混屏工作,通过timer线程进行触发,最终执行到SoftFrameGenerator::onTimeout()函数中,做混屏的具体计算
此函数内部,通过m_counter变量,巧妙的实现了不同帧率只需混屏一次的性能优化。
onTimeout会按照最大帧率来触发,通过下面的核心代码,判断了是否需要混屏,混屏后是否需要回调,触发哪个帧率的output的回调
bool hasValidOutput = false;
{
boost::unique_lock 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;
}
}
}
这个逻辑很巧妙,但理解起来有点困难,可以通过下图进行理解: