video_mixer.cpp 4.79 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 *  Copyright (C) 2013 Savoir-Faire Linux Inc.
 *
 *  Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
 */

32
#include "libav_deps.h"
33 34
#include "video_mixer.h"
#include "check.h"
35 36 37
#include "client/video_controls.h"
#include "manager.h"
#include "logger.h"
38

39 40
#include <cmath>

41 42
namespace sfl_video {

43
VideoMixer::VideoMixer(const std::string id) :
44
    VideoGenerator::VideoGenerator()
45
    , id_(id)
46 47
    , width_(0)
    , height_(0)
48 49 50 51 52 53 54 55 56 57 58 59 60
    , sources_()
    , mutex_()
    , sink_()
{
    auto videoCtrl = Manager::instance().getVideoControls();
    if (!videoCtrl->hasPreviewStarted()) {
        videoCtrl->startPreview();
        MYSLEEP(1);
    }

    // Local video camera is always attached
    videoCtrl->getVideoPreview()->attach(this);
}
61 62 63

VideoMixer::~VideoMixer()
{
64 65 66 67
    stop_sink();

    auto videoCtrl = Manager::instance().getVideoControls();
    videoCtrl->getVideoPreview()->detach(this);
68 69
}

70
void VideoMixer::attached(Observable<VideoFrameSP>* ob)
71
{
72 73
    std::unique_lock<std::mutex> lk(mutex_);
    sources_.push_back(ob);
74 75
}

76
void VideoMixer::detached(Observable<VideoFrameSP>* ob)
77
{
78 79
    std::unique_lock<std::mutex> lk(mutex_);
    sources_.remove(ob);
80 81
}

82
void VideoMixer::update(Observable<VideoFrameSP>* ob, VideoFrameSP& frame_p)
83 84 85 86 87 88 89 90 91
{
    std::unique_lock<std::mutex> lk(mutex_);
    int i=0;
    for (auto x : sources_) {
        if (x == ob) break;
        i++;
    }
    render_frame(*frame_p, i);
}
92

93
void VideoMixer::render_frame(VideoFrame& input, const int index)
94
{
95 96 97
    VideoScaler scaler;
    VideoFrame scaled_input;

98 99 100
    if (!width_ or !height_)
        return;

101
    VideoFrame &output = getNewFrame();
102

103 104 105 106 107 108 109 110 111 112 113
    if (!output.allocBuffer(width_, height_, VIDEO_PIXFMT_YUV420P)) {
        ERROR("VideoFrame::allocBuffer() failed");
        return;
    }

    VideoFrameSP previous_p=obtainLastFrame();
    if (previous_p)
        previous_p->copy(output);
    previous_p.reset();

    const int n=sources_.size();
114 115 116 117
    const int zoom=ceil(sqrt(n));
    const int cell_width=width_ / zoom;
    const int cell_height=height_ / zoom;

118 119 120 121 122
    if (!scaled_input.allocBuffer(cell_width, cell_height,
                                  VIDEO_PIXFMT_YUV420P)) {
        ERROR("VideoFrame::allocBuffer() failed");
        return;
    }
Guillaume Roguez's avatar
Guillaume Roguez committed
123

124 125
    int xoff = (index % zoom) * cell_width;
    int yoff = (index / zoom) * cell_height;
126

127 128
    scaler.scale(input, scaled_input);
    output.blit(scaled_input, xoff, yoff);
129 130

    publishFrame();
131 132
}

133 134
void VideoMixer::setDimensions(int width, int height)
{
135
    std::unique_lock<std::mutex> lk(mutex_);
136 137
    width_ = width;
    height_ = height;
138

139 140 141 142 143
    // cleanup the previous frame to have a nice copy in rendering method
    VideoFrameSP previous_p=obtainLastFrame();
    if (previous_p)
        previous_p->clear();

144 145
    stop_sink();
    start_sink();
146
}
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
void VideoMixer::start_sink()
{
    if (sink_.start()) {
        if (this->attach(&sink_)) {
            Manager::instance().getVideoControls()->startedDecoding(id_+"_MX", sink_.openedName(), width_, height_);
            DEBUG("MX: shm sink <%s> started: size = %dx%d",
                  sink_.openedName().c_str(), width_, height_);
        }
    } else
        WARN("MX: sink startup failed");
}

void VideoMixer::stop_sink()
{
    if (this->detach(&sink_)) {
        Manager::instance().getVideoControls()->stoppedDecoding(id_+"_MX", sink_.openedName());
        sink_.stop();
    }
}

int VideoMixer::getWidth() const
{ return width_; }

int VideoMixer::getHeight() const
{ return height_; }

int VideoMixer::getPixelFormat() const
{ return VIDEO_PIXFMT_YUV420P; }
176

177
} // end namespace sfl_video