tutorial: HelloWorld

Change-Id: Ie19a7749ba028ceda16a2479398645daeb08f277
diff --git a/HelloWorld/CenterCircleVideoSubscriber.cpp b/HelloWorld/CenterCircleVideoSubscriber.cpp
new file mode 100644
index 0000000..b1e1377
--- /dev/null
+++ b/HelloWorld/CenterCircleVideoSubscriber.cpp
@@ -0,0 +1,187 @@
+/**
+ *  Copyright (C) 2020 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@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.
+ */
+
+#include "CenterCircleVideoSubscriber.h"
+
+extern "C" {
+#include <libavutil/display.h>
+}
+#include <accel.h>
+
+// LOGGING
+#include <pluglog.h>
+
+#include <stdio.h>
+#include <opencv2/imgproc.hpp>
+
+const std::string TAG = "CenterCircle";
+const char sep = separator();
+
+namespace jami {
+
+CenterCircleVideoSubscriber::CenterCircleVideoSubscriber(const std::string& dataPath)
+    : path_ {dataPath}
+{}
+
+CenterCircleVideoSubscriber::~CenterCircleVideoSubscriber()
+{
+    std::ostringstream oss;
+    oss << "~CenterCircleMediaProcessor" << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+}
+
+void
+CenterCircleVideoSubscriber::update(jami::Observable<AVFrame*>*, AVFrame* const& iFrame)
+{
+    if (!iFrame)
+        return;
+    AVFrame* pluginFrame = const_cast<AVFrame*>(iFrame);
+
+    //======================================================================================
+    // GET FRAME ROTATION
+    AVFrameSideData* side_data = av_frame_get_side_data(iFrame, AV_FRAME_DATA_DISPLAYMATRIX);
+
+    int angle {0};
+    if (side_data) {
+        auto matrix_rotation = reinterpret_cast<int32_t*>(side_data->data);
+        angle = static_cast<int>(av_display_rotation_get(matrix_rotation));
+    }
+
+    //======================================================================================
+    // GET RAW FRAME
+    // Use a non-const Frame
+    // Convert input frame to RGB
+    int inputHeight = pluginFrame->height;
+    int inputWidth = pluginFrame->width;
+
+    FrameUniquePtr bgrFrame = scaler.convertFormat(transferToMainMemory(pluginFrame,
+                                                                        AV_PIX_FMT_NV12),
+                                                   AV_PIX_FMT_RGB24);
+    resultFrame = cv::Mat {bgrFrame->height,
+                           bgrFrame->width,
+                           CV_8UC3,
+                           bgrFrame->data[0],
+                           static_cast<size_t>(bgrFrame->linesize[0])};
+
+    // First clone the frame as the original one is unusable because of
+    // linespace
+    processingFrame = resultFrame.clone();
+
+    if (firstRun) {
+        // we set were the circle will be draw.
+        circlePos.y = static_cast<int>(inputHeight / 2);
+        circlePos.x = static_cast<int>(inputWidth / 2);
+        int w = resultFrame.size().width;
+        int h = resultFrame.size().height;
+        radius = std::min(w, h) / 8;
+        firstRun = false;
+    }
+
+    drawCenterCircle();
+    copyByLine(bgrFrame->linesize[0]);
+
+    //======================================================================================
+    // REPLACE AVFRAME DATA WITH FRAME DATA
+    if (bgrFrame && bgrFrame->data[0]) {
+        uint8_t* frameData = bgrFrame->data[0];
+        if (angle == 90 || angle == -90) {
+            std::memmove(frameData,
+                         resultFrame.data,
+                         static_cast<size_t>(pluginFrame->width * pluginFrame->height * 3)
+                             * sizeof(uint8_t));
+        }
+    }
+    // Copy Frame meta data
+    if (bgrFrame && pluginFrame) {
+        av_frame_copy_props(bgrFrame.get(), pluginFrame);
+        scaler.moveFrom(pluginFrame, bgrFrame.get());
+    }
+
+    // Remove the pointer
+    pluginFrame = nullptr;
+}
+
+void
+CenterCircleVideoSubscriber::setColor(const std::string& color)
+{
+    int r, g, b = 0;
+    std::sscanf(color.c_str(), "#%02x%02x%02x", &r, &g, &b);
+    baseColor = cv::Scalar(r, g, b);
+    Plog::log(Plog::LogPriority::INFO, TAG, "Color set to: " + color);
+}
+
+void
+CenterCircleVideoSubscriber::copyByLine(const int lineSize)
+{
+    if (3 * processingFrame.cols == lineSize) {
+        std::memcpy(resultFrame.data,
+                    processingFrame.data,
+                    processingFrame.rows * processingFrame.cols * 3);
+    } else {
+        int rows = processingFrame.rows;
+        int offset = 0;
+        int frameOffset = 0;
+        for (int i = 0; i < rows; i++) {
+            std::memcpy(resultFrame.data + offset, processingFrame.data + frameOffset, lineSize);
+            offset += lineSize;
+            frameOffset += 3 * processingFrame.cols;
+        }
+    }
+}
+
+void
+CenterCircleVideoSubscriber::drawCenterCircle()
+{
+    if (!processingFrame.empty()) {
+        cv::circle(processingFrame, circlePos, radius, baseColor, cv::FILLED);
+    }
+}
+
+void
+CenterCircleVideoSubscriber::attached(jami::Observable<AVFrame*>* observable)
+{
+    std::ostringstream oss;
+    oss << "::Attached ! " << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+    observable_ = observable;
+}
+
+void
+CenterCircleVideoSubscriber::detached(jami::Observable<AVFrame*>*)
+{
+    firstRun = true;
+    observable_ = nullptr;
+    std::ostringstream oss;
+    oss << "::Detached()" << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+}
+
+void
+CenterCircleVideoSubscriber::detach()
+{
+    if (observable_) {
+        firstRun = true;
+        std::ostringstream oss;
+        oss << "::Calling detach()" << std::endl;
+        Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+        observable_->detach(this);
+    }
+}
+} // namespace jami