美文网首页webrtc
OWT (Open WebRTC Toolkit) 混屏模块代码

OWT (Open WebRTC Toolkit) 混屏模块代码

作者: 我是榜样 | 来源:发表于2020-10-09 20:33 被阅读0次

    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<CompositeIn> 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<webrtc::VideoFrame*>(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<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;
                }
            }
        }
    

    这个逻辑很巧妙,但理解起来有点困难,可以通过下图进行理解:

    image.png

    相关文章

      网友评论

        本文标题:OWT (Open WebRTC Toolkit) 混屏模块代码

        本文链接:https://www.haomeiwen.com/subject/yolrpktx.html