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
    std::lock_guard<std::mutex> lk(mutex_);
73
    sources_.push_back(ob);
74
75
}

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

82
void VideoMixer::update(Observable<VideoFrameSP>* ob, VideoFrameSP& frame_p)
83
{
84
    std::lock_guard<std::mutex> lk(mutex_);
85
86
87
88
89
90
91
    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::lock_guard<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