blob: 6b4a94e215f89fbdeec78750690760966fc8dd04 [file] [log] [blame]
agsantosd09cc6d2020-11-06 17:34:46 -05001/**
2 * Copyright (C) 2020 Savoir-faire Linux Inc.
3 *
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 USA.
19 */
20
21#include "CoinCircleVideoSubscriber.h"
22
23extern "C" {
24#include <libavutil/display.h>
25}
26#include <accel.h>
27
28// LOGGING
29#include <pluglog.h>
30
31#include <stdio.h>
32#include <opencv2/imgproc.hpp>
33
34const std::string TAG = "CoinCircle";
35const char sep = separator();
36
37namespace jami {
38
39CoinCircleVideoSubscriber::CoinCircleVideoSubscriber(const std::string& dataPath)
40 : path_ {dataPath}
41{}
42
43CoinCircleVideoSubscriber::~CoinCircleVideoSubscriber()
44{
45 std::ostringstream oss;
46 oss << "~CoinCircleMediaProcessor" << std::endl;
47 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
48}
49
50void
51CoinCircleVideoSubscriber::update(jami::Observable<AVFrame*>*, AVFrame* const& iFrame)
52{
53 if (!iFrame)
54 return;
55 AVFrame* pluginFrame = const_cast<AVFrame*>(iFrame);
56
57 //======================================================================================
58 // GET FRAME ROTATION
59 AVFrameSideData* side_data = av_frame_get_side_data(iFrame, AV_FRAME_DATA_DISPLAYMATRIX);
60
61 int angle {0};
62 if (side_data) {
63 auto matrix_rotation = reinterpret_cast<int32_t*>(side_data->data);
64 angle = static_cast<int>(av_display_rotation_get(matrix_rotation));
65 }
66
67 //======================================================================================
68 // GET RAW FRAME
69 // Use a non-const Frame
70 // Convert input frame to RGB
71 int inputHeight = pluginFrame->height;
72 int inputWidth = pluginFrame->width;
73 FrameUniquePtr bgrFrame = scaler.convertFormat(transferToMainMemory(pluginFrame,
74 AV_PIX_FMT_NV12),
75 AV_PIX_FMT_RGB24);
76
77 resultFrame = cv::Mat {bgrFrame->height,
78 bgrFrame->width,
79 CV_8UC3,
80 bgrFrame->data[0],
81 static_cast<size_t>(bgrFrame->linesize[0])};
82
83 // First clone the frame as the original one is unusable because of
84 // linespace
85 processingFrame = resultFrame.clone();
86
87 if (firstRun) {
88 // we set were the circle will be draw.
89 int w = resultFrame.size().width;
90 int h = resultFrame.size().height;
91 radius = std::min(w, h) / 8;
92 circlePos.y = static_cast<int>(radius);
93 circlePos.x = static_cast<int>(radius);
94 firstRun = false;
95 }
96
97 drawCoinCircle(angle);
98 copyByLine(bgrFrame->linesize[0]);
99
100 //======================================================================================
101 // REPLACE AVFRAME DATA WITH FRAME DATA
102 if (bgrFrame && bgrFrame->data[0]) {
103 uint8_t* frameData = bgrFrame->data[0];
104 if (angle == 90 || angle == -90) {
105 std::memmove(frameData,
106 resultFrame.data,
107 static_cast<size_t>(pluginFrame->width * pluginFrame->height * 3)
108 * sizeof(uint8_t));
109 }
110 }
111 // Copy Frame meta data
112 if (bgrFrame && pluginFrame) {
113 av_frame_copy_props(bgrFrame.get(), pluginFrame);
114 scaler.moveFrom(pluginFrame, bgrFrame.get());
115 }
116
117 // Remove the pointer
118 pluginFrame = nullptr;
119}
120
121void
122CoinCircleVideoSubscriber::drawCoinCircle(const int angle)
123{
124 if (!processingFrame.empty()) {
125 rotateFrame(angle, processingFrame);
126 cv::circle(processingFrame, circlePos, radius, baseColor, cv::FILLED);
127 rotateFrame(-angle, processingFrame);
128 }
129}
130
131void
132CoinCircleVideoSubscriber::rotateFrame(const int angle, cv::Mat& frame)
133{
134 if (angle == -90)
135 cv::rotate(frame, frame, cv::ROTATE_90_COUNTERCLOCKWISE);
136 else if (std::abs(angle) == 180)
137 cv::rotate(frame, frame, cv::ROTATE_180);
138 else if (angle == 90)
139 cv::rotate(frame, frame, cv::ROTATE_90_CLOCKWISE);
140}
141
142void
143CoinCircleVideoSubscriber::setColor(const std::string& color)
144{
145 int r, g, b = 0;
146 std::sscanf(color.c_str(), "#%02x%02x%02x", &r, &g, &b);
147 baseColor = cv::Scalar(r, g, b);
148 Plog::log(Plog::LogPriority::INFO, TAG, "Color set to: " + color);
149}
150
151void
152CoinCircleVideoSubscriber::copyByLine(const int lineSize)
153{
154 if (3 * processingFrame.cols == lineSize) {
155 std::memcpy(resultFrame.data,
156 processingFrame.data,
157 processingFrame.rows * processingFrame.cols * 3);
158 } else {
159 int rows = processingFrame.rows;
160 int offset = 0;
161 int frameOffset = 0;
162 for (int i = 0; i < rows; i++) {
163 std::memcpy(resultFrame.data + offset, processingFrame.data + frameOffset, lineSize);
164 offset += lineSize;
165 frameOffset += 3 * processingFrame.cols;
166 }
167 }
168}
169
170void
171CoinCircleVideoSubscriber::attached(jami::Observable<AVFrame*>* observable)
172{
173 std::ostringstream oss;
174 oss << "::Attached ! " << std::endl;
175 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
176 observable_ = observable;
177}
178
179void
180CoinCircleVideoSubscriber::detached(jami::Observable<AVFrame*>*)
181{
182 firstRun = true;
183 observable_ = nullptr;
184 std::ostringstream oss;
185 oss << "::Detached()" << std::endl;
186 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
187}
188
189void
190CoinCircleVideoSubscriber::detach()
191{
192 if (observable_) {
193 firstRun = true;
194 std::ostringstream oss;
195 oss << "::Calling detach()" << std::endl;
196 Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
197 observable_->detach(this);
198 }
199}
200} // namespace jami