blob: 928380c5446ccb58f43d9bbf3aeb794c051c449f [file] [log] [blame]
agsantos796b5af2020-12-22 19:38:27 -05001/**
Sébastien Blincb783e32021-02-12 11:34:10 -05002 * Copyright (C) 2020-2021 Savoir-faire Linux Inc.
agsantos796b5af2020-12-22 19:38:27 -05003 *
4 * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA.
20 */
21
22#include "videoSubscriber.h"
23// Use for display rotation matrix
24extern "C" {
25#include <libavutil/display.h>
26}
27#include <accel.h>
28#include <frameUtils.h>
29
30// Opencv processing
31#include <opencv2/imgcodecs.hpp>
32#include <opencv2/imgproc.hpp>
33
34// LOGGING
35#include <pluglog.h>
36
37const std::string TAG = "FORESEG";
38const char sep = separator();
39
40namespace jami {
41
42VideoSubscriber::VideoSubscriber(const std::string& dataPath)
43 : path_ {dataPath}
44 , pluginProcessor {dataPath}
45{
46 /**
47 * Waits for new frames and then process them
48 * Writes the predictions in computedPredictions
49 **/
50 processFrameThread = std::thread([this] {
51 while (running) {
52 std::unique_lock<std::mutex> l(inputLock);
53 inputCv.wait(l, [this] { return not running or newFrame; });
54 if (not running) {
55 break;
56 }
57
58 pluginProcessor.feedInput(fcopy.resizedFrameRGB);
59 newFrame = false;
60 /** Unclock the mutex, this way we let the other thread
61 * copy new data while we are processing the old one
62 **/
63 l.unlock();
64 pluginProcessor.computePredictions();
65 }
66 });
67}
68
69VideoSubscriber::~VideoSubscriber()
70{
71 std::ostringstream oss;
72 oss << "~MediaProcessor" << std::endl;
73 stop();
74 processFrameThread.join();
75 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
76}
77
78void
79VideoSubscriber::update(jami::Observable<AVFrame*>*, AVFrame* const& pluginFrame)
80{
81 if (pluginProcessor.pluginInference.isAllocated() && pluginProcessor.hasBackground()) {
82 if (!pluginFrame)
83 return;
84
85 //======================================================================================
86 // GET FRAME ROTATION
87 AVFrameSideData* side_data = av_frame_get_side_data(pluginFrame,
88 AV_FRAME_DATA_DISPLAYMATRIX);
89
90 int angle {0};
91 if (side_data) {
92 auto matrix_rotation = reinterpret_cast<int32_t*>(side_data->data);
93 angle = static_cast<int>(av_display_rotation_get(matrix_rotation));
94 }
95
96 //======================================================================================
97 // GET RAW FRAME
98 // Use a non-const Frame
99 // Convert input frame to RGB
100 int inputHeight = pluginFrame->height;
101 int inputWidth = pluginFrame->width;
102
103 fcopy.originalSize = cv::Size {inputWidth, inputHeight};
104
105 AVFrame* temp = transferToMainMemory(pluginFrame, AV_PIX_FMT_NV12);
106 AVFrame* bgrFrame = scaler.convertFormat(temp, AV_PIX_FMT_RGB24);
107 av_frame_unref(temp);
108 av_frame_free(&temp);
109 if (!bgrFrame)
110 return;
111 cv::Mat frame = cv::Mat {bgrFrame->height,
112 bgrFrame->width,
113 CV_8UC3,
114 bgrFrame->data[0],
115 static_cast<size_t>(bgrFrame->linesize[0])};
116 // First clone the frame as the original one is unusable because of
117 // linespace
118
119 cv::Mat clone = frame.clone();
120 //======================================================================================
121
122 pluginProcessor.setBackgroundRotation(angle);
123
124 if (firstRun) {
125 pluginProcessor.pluginInference.setExpectedImageDimensions();
126 fcopy.resizedSize = cv::Size {pluginProcessor.pluginInference.getImageWidth(),
127 pluginProcessor.pluginInference.getImageHeight()};
128 pluginProcessor.resetInitValues(fcopy.resizedSize);
129
130 cv::resize(clone, fcopy.resizedFrameRGB, fcopy.resizedSize);
131 pluginProcessor.rotateFrame(angle, fcopy.resizedFrameRGB);
132
133 cv::resize(pluginProcessor.backgroundImage,
134 pluginProcessor.backgroundImage,
135 fcopy.resizedSize);
136
137 firstRun = false;
138 }
139
140 if (!newFrame) {
141 std::lock_guard<std::mutex> l(inputLock);
142 cv::resize(clone, fcopy.resizedFrameRGB, fcopy.resizedSize);
143 pluginProcessor.rotateFrame(angle, fcopy.resizedFrameRGB);
144 newFrame = true;
145 inputCv.notify_all();
146 }
147
148 fcopy.predictionsFrameRGB = frame;
149 fcopy.predictionsResizedFrameRGB = fcopy.resizedFrameRGB.clone();
150 pluginProcessor.rotateFrame(-angle, fcopy.predictionsResizedFrameRGB);
151 pluginProcessor.drawMaskOnFrame(fcopy.predictionsFrameRGB,
152 fcopy.predictionsResizedFrameRGB,
153 pluginProcessor.computedMask,
154 bgrFrame->linesize[0],
155 angle);
156
157 //======================================================================================
158 // REPLACE AVFRAME DATA WITH FRAME DATA
159 if (bgrFrame->data[0]) {
160 uint8_t* frameData = bgrFrame->data[0];
161 if (angle == 90 || angle == -90) {
162 std::memmove(frameData,
163 fcopy.predictionsFrameRGB.data,
164 static_cast<size_t>(pluginFrame->width * pluginFrame->height * 3)
165 * sizeof(uint8_t));
166 }
167
agsantos796b5af2020-12-22 19:38:27 -0500168 moveFrom(pluginFrame, bgrFrame);
169 }
170 av_frame_unref(bgrFrame);
171 av_frame_free(&bgrFrame);
172 }
173}
174
175void
176VideoSubscriber::attached(jami::Observable<AVFrame*>* observable)
177{
178 std::ostringstream oss;
179 oss << "::Attached ! " << std::endl;
180 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
181 observable_ = observable;
182}
183
184void
185VideoSubscriber::detached(jami::Observable<AVFrame*>*)
186{
187 firstRun = true;
188 observable_ = nullptr;
189 std::ostringstream oss;
190 oss << "::Detached()" << std::endl;
191 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
192}
193
194void
195VideoSubscriber::detach()
196{
197 if (observable_) {
198 firstRun = true;
199 std::ostringstream oss;
200 oss << "::Calling detach()" << std::endl;
201 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
202 observable_->detach(this);
203 }
204}
205
206void
207VideoSubscriber::stop()
208{
209 running = false;
210 inputCv.notify_all();
211}
212
213void
214VideoSubscriber::setBackground(const std::string& backgroundPath)
215{
216 pluginProcessor.setBackgroundImage(backgroundPath);
217}
218} // namespace jami