From d09cc6d9abcaa9f032164dfe0e768edf20bb91c9 Mon Sep 17 00:00:00 2001
From: agsantos <aline.gondimsantos@savoirfairelinux.com>
Date: Fri, 6 Nov 2020 17:34:46 -0500
Subject: [PATCH] tutorial: HelloWorld

Change-Id: Ie19a7749ba028ceda16a2479398645daeb08f277
---
 .gitignore                                 |   1 -
 HelloWorld/CMakeLists.txt                  |  91 +++++++++
 HelloWorld/CenterCircleMediaHandler.cpp    | 116 +++++++++++
 HelloWorld/CenterCircleMediaHandler.h      |  55 ++++++
 HelloWorld/CenterCircleVideoSubscriber.cpp | 187 ++++++++++++++++++
 HelloWorld/CenterCircleVideoSubscriber.h   |  71 +++++++
 HelloWorld/CoinCircleMediaHandler.cpp      | 107 ++++++++++
 HelloWorld/CoinCircleMediaHandler.h        |  55 ++++++
 HelloWorld/CoinCircleVideoSubscriber.cpp   | 200 +++++++++++++++++++
 HelloWorld/CoinCircleVideoSubscriber.h     |  72 +++++++
 HelloWorld/build.sh                        | 220 +++++++++++++++++++++
 HelloWorld/data/icon.png                   | Bin 0 -> 21340 bytes
 HelloWorld/data/preferences.json           |  38 ++++
 HelloWorld/main.cpp                        |  77 ++++++++
 HelloWorld/manifest.json                   |   5 +
 HelloWorld/package.json                    |  19 ++
 16 files changed, 1313 insertions(+), 1 deletion(-)
 create mode 100644 HelloWorld/CMakeLists.txt
 create mode 100644 HelloWorld/CenterCircleMediaHandler.cpp
 create mode 100644 HelloWorld/CenterCircleMediaHandler.h
 create mode 100644 HelloWorld/CenterCircleVideoSubscriber.cpp
 create mode 100644 HelloWorld/CenterCircleVideoSubscriber.h
 create mode 100644 HelloWorld/CoinCircleMediaHandler.cpp
 create mode 100644 HelloWorld/CoinCircleMediaHandler.h
 create mode 100644 HelloWorld/CoinCircleVideoSubscriber.cpp
 create mode 100644 HelloWorld/CoinCircleVideoSubscriber.h
 create mode 100755 HelloWorld/build.sh
 create mode 100644 HelloWorld/data/icon.png
 create mode 100644 HelloWorld/data/preferences.json
 create mode 100644 HelloWorld/main.cpp
 create mode 100644 HelloWorld/manifest.json
 create mode 100644 HelloWorld/package.json

diff --git a/.gitignore b/.gitignore
index 548a5c9..451c560 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,3 @@ config.mak
 *__pycache__*
 /foo/
 /.vscode/
-/HelloWorld/
diff --git a/HelloWorld/CMakeLists.txt b/HelloWorld/CMakeLists.txt
new file mode 100644
index 0000000..bbecc0f
--- /dev/null
+++ b/HelloWorld/CMakeLists.txt
@@ -0,0 +1,91 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name
+set (ProjectName HelloWorld)
+set (Version 1.0.0)
+
+project(${ProjectName} VERSION ${Version})
+
+set (DAEMON ${PROJECT_SOURCE_DIR}/../../daemon)
+set (JPL_FILE_NAME ${ProjectName}.jpl)
+set (DAEMON_SRC ${DAEMON}/src)
+set (CONTRIB_PATH ${DAEMON}/contrib)
+set (PLUGINS_LIB ${PROJECT_SOURCE_DIR}/../lib)
+set (JPL_DIRECTORY ${PROJECT_BINARY_DIR}/jpl)
+set (LIBS_DIR ${PROJECT_SOURCE_DIR}/../contrib/Libs)
+
+if(WIN32)
+    message(OS:\  WINDOWS\ ${CMAKE_SYSTEM_PROCESSOR})
+    if (NOT ${CMAKE_CL_64})
+        message( FATAL_ERROR "\nUse CMake only for x64 Windows" )
+    endif()
+    set (CONTRIB_PLATFORM_CURT x64)
+    set (CONTRIB_PLATFORM ${CONTRIB_PLATFORM_CURT}-windows)
+    set (LIBRARY_FILE_NAME ${ProjectName}.dll)
+    set (FFMPEG ${CONTRIB_PATH}/build/ffmpeg/Build/win32/x64)
+else()
+    message( FATAL_ERROR "\nUse CMake only for Windows! For linux or Android (linux host), use our bash scripts." )
+endif()
+
+message(Building:\   ${ProjectName}\   ${Version})
+message(Build\ path:\ ${PROJECT_BINARY_DIR})
+message(JPL\ assembling\ path:\ ${JPL_DIRECTORY})
+message(JPL\ path:\ ${JPL_DIRECTORY}/../../../build/${ProjectName}/${JPL_FILE_NAME})
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
+
+set(plugin_SRC CoinCircleMediaHandler.cpp
+               CenterCircleVideoSubscriber.cpp
+               CenterCircleMediaHandler.cpp
+               CoinCircleVideoSubscriber.cpp
+               main.cpp
+               ../lib/accel.cpp
+               )
+
+set(plugin_HDR CoinCircleVideoSubscriber.h
+               CenterCircleVideoSubscriber.h
+               CoinCircleMediaHandler.h
+               CenterCircleMediaHandler.h
+               ../lib/accel.h
+               ../lib/framescaler.h
+               ../lib/pluglog.h
+               )
+
+add_library(${ProjectName} SHARED ${plugin_SRC}
+                                  ${plugin_HDR}
+                                  )
+
+target_include_directories(${ProjectName} PUBLIC ${PROJECT_BINARY_DIR}
+                                                 ${PROJECT_SOURCE_DIR}
+                                                 ${PLUGINS_LIB}
+                                                 ${DAEMON_SRC}
+                                                 ${CONTRIB_PATH}
+                                                 ${FFMPEG}/include
+                                                 ${CONTRIB_PATH}/build/opencv/build/install/include
+                                                 )
+target_link_directories(${ProjectName} PUBLIC ${CONTRIB_PATH}
+                                        ${FFMPEG}/bin
+                                        ${CONTRIB_PATH}/build/opencv/build/lib/Release
+                                        ${CONTRIB_PATH}/build/opencv/build/3rdparty/lib/Release
+                                        )
+
+target_link_libraries(${ProjectName} PUBLIC swscale avutil opencv_imgproc411 opencv_core411 zlib)
+
+add_custom_command(
+    TARGET ${ProjectName}
+    PRE_BUILD
+    COMMAND python3 ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --preassemble --plugin=${ProjectName}
+    COMMENT "Assembling Plugin files"
+)
+
+add_custom_command(
+    TARGET ${ProjectName}
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${ProjectName}.lib ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
+    COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${LIBRARY_FILE_NAME} ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
+    COMMAND python3 ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --assemble --plugin=${ProjectName}
+    COMMENT "Generating JPL archive"
+)
diff --git a/HelloWorld/CenterCircleMediaHandler.cpp b/HelloWorld/CenterCircleMediaHandler.cpp
new file mode 100644
index 0000000..903f96f
--- /dev/null
+++ b/HelloWorld/CenterCircleMediaHandler.cpp
@@ -0,0 +1,116 @@
+/**
+ *  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 "CenterCircleMediaHandler.h"
+
+#include "pluglog.h"
+const char sep = separator();
+const std::string TAG = "CenterCircle";
+
+#define NAME "CenterCircle"
+
+namespace jami {
+
+CenterCircleMediaHandler::CenterCircleMediaHandler(std::map<std::string, std::string>&& ppm,
+                                                   std::string&& datapath)
+    : datapath_ {datapath}
+    , ppm_ {ppm}
+{
+    setId(datapath_);
+    mVS = std::make_shared<CenterCircleVideoSubscriber>(datapath_);
+    auto it = ppm_.find("color");
+    if (it != ppm_.end()) {
+        mVS->setColor(it->second);
+    } else {
+        mVS->setColor("#0000FF");
+    }
+}
+
+void
+CenterCircleMediaHandler::notifyAVFrameSubject(const StreamData& data, jami::avSubjectPtr subject)
+{
+    Plog::log(Plog::LogPriority::INFO, TAG, "IN AVFRAMESUBJECT");
+    std::ostringstream oss;
+    std::string direction = data.direction ? "Receive" : "Preview";
+    oss << "NEW SUBJECT: [" << data.id << "," << direction << "]" << std::endl;
+
+    bool preferredStreamDirection = false; // false for output; true for input
+    auto it = ppm_.find("videostream");
+    if (it != ppm_.end()) {
+        preferredStreamDirection = it->second == "1";
+    }
+    oss << "preferredStreamDirection " << preferredStreamDirection << std::endl;
+    if (data.type == StreamType::video && !data.direction
+        && data.direction == preferredStreamDirection) {
+        subject->attach(mVS.get()); // your image
+        oss << "got my sent image attached" << std::endl;
+    } else if (data.type == StreamType::video && data.direction
+               && data.direction == preferredStreamDirection) {
+        subject->attach(mVS.get()); // the image you receive from others on the call
+        oss << "got received image attached" << std::endl;
+    }
+
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+}
+
+std::map<std::string, std::string>
+CenterCircleMediaHandler::getCallMediaHandlerDetails()
+{
+    return {{"name", NAME}, {"iconPath", datapath_ + sep + "icon.png"}, {"pluginId", id()}};
+}
+
+void
+CenterCircleMediaHandler::setPreferenceAttribute(const std::string& key, const std::string& value)
+{
+    auto it = ppm_.find(key);
+    if (it != ppm_.end() && it->second != value) {
+        it->second = value;
+
+        if (key == "color") {
+            mVS->setColor(value);
+            return;
+        }
+    }
+}
+
+bool
+CenterCircleMediaHandler::preferenceMapHasKey(const std::string& key)
+{
+    if (key == "color") {
+        return true;
+    }
+
+    return false;
+}
+
+void
+CenterCircleMediaHandler::detach()
+{
+    mVS->detach();
+}
+
+CenterCircleMediaHandler::~CenterCircleMediaHandler()
+{
+    std::ostringstream oss;
+    oss << " ~CenterCircleMediaHandler from HelloWorld Plugin" << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+    detach();
+}
+} // namespace jami
diff --git a/HelloWorld/CenterCircleMediaHandler.h b/HelloWorld/CenterCircleMediaHandler.h
new file mode 100644
index 0000000..91a4b6f
--- /dev/null
+++ b/HelloWorld/CenterCircleMediaHandler.h
@@ -0,0 +1,55 @@
+/**
+ *  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.
+ */
+
+#pragma once
+
+// Project
+#include "CenterCircleVideoSubscriber.h"
+
+// Plugin
+#include "plugin/jamiplugin.h"
+#include "plugin/mediahandler.h"
+
+using avSubjectPtr = std::weak_ptr<jami::Observable<AVFrame*>>;
+
+namespace jami {
+
+class CenterCircleMediaHandler : public jami::CallMediaHandler
+{
+public:
+    CenterCircleMediaHandler(std::map<std::string, std::string>&& ppm, std::string&& dataPath);
+    ~CenterCircleMediaHandler();
+
+    virtual void notifyAVFrameSubject(const StreamData& data, avSubjectPtr subject) override;
+    virtual std::map<std::string, std::string> getCallMediaHandlerDetails() override;
+
+    virtual void detach() override;
+    virtual void setPreferenceAttribute(const std::string& key, const std::string& value) override;
+    virtual bool preferenceMapHasKey(const std::string& key) override;
+
+    std::shared_ptr<CenterCircleVideoSubscriber> mVS;
+
+    const std::string& dataPath() const { return datapath_; }
+
+private:
+    const std::string datapath_;
+    std::map<std::string, std::string> ppm_;
+};
+} // namespace jami
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
diff --git a/HelloWorld/CenterCircleVideoSubscriber.h b/HelloWorld/CenterCircleVideoSubscriber.h
new file mode 100644
index 0000000..8fb9c02
--- /dev/null
+++ b/HelloWorld/CenterCircleVideoSubscriber.h
@@ -0,0 +1,71 @@
+/**
+ *  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.
+ */
+
+#pragma once
+
+// AvFrame
+extern "C" {
+#include <libavutil/frame.h>
+}
+#include <observer.h>
+
+// Frame Scaler
+#include <framescaler.h>
+
+#include <opencv2/core.hpp>
+
+namespace jami {
+
+class CenterCircleVideoSubscriber : public jami::Observer<AVFrame*>
+{
+public:
+    CenterCircleVideoSubscriber(const std::string& dataPath);
+    ~CenterCircleVideoSubscriber();
+
+    virtual void update(jami::Observable<AVFrame*>*, AVFrame* const&) override;
+    virtual void attached(jami::Observable<AVFrame*>*) override;
+    virtual void detached(jami::Observable<AVFrame*>*) override;
+
+    void detach();
+
+    void setColor(const std::string& color);
+
+private:
+    // Observer pattern
+    Observable<AVFrame*>* observable_ = nullptr;
+
+    // Data
+    std::string path_;
+    FrameScaler scaler;
+
+    // Status variables of the processing
+    bool firstRun {true};
+
+    // define custom variables
+    void copyByLine(const int lineSize);
+    void drawCenterCircle();
+
+    cv::Point circlePos;
+    cv::Mat processingFrame;
+    cv::Mat resultFrame;
+    int radius;
+    cv::Scalar baseColor;
+};
+} // namespace jami
diff --git a/HelloWorld/CoinCircleMediaHandler.cpp b/HelloWorld/CoinCircleMediaHandler.cpp
new file mode 100644
index 0000000..213e836
--- /dev/null
+++ b/HelloWorld/CoinCircleMediaHandler.cpp
@@ -0,0 +1,107 @@
+/**
+ *  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 "CoinCircleMediaHandler.h"
+// Logger
+#include "pluglog.h"
+const char sep = separator();
+const std::string TAG = "CoinCircle";
+
+#define NAME "CoinCircle"
+
+namespace jami {
+
+CoinCircleMediaHandler::CoinCircleMediaHandler(std::map<std::string, std::string>&& ppm,
+                                               std::string&& datapath)
+    : datapath_ {datapath}
+    , ppm_ {ppm}
+{
+    setId(datapath_);
+    mVS = std::make_shared<CoinCircleVideoSubscriber>(datapath_);
+    auto it = ppm_.find("color");
+    if (it != ppm_.end()) {
+        mVS->setColor(it->second);
+    } else {
+        mVS->setColor("#0000FF");
+    }
+}
+
+void
+CoinCircleMediaHandler::notifyAVFrameSubject(const StreamData& data, jami::avSubjectPtr subject)
+{
+    Plog::log(Plog::LogPriority::INFO, TAG, "IN AVFRAMESUBJECT");
+    std::ostringstream oss;
+    std::string direction = data.direction ? "Receive" : "Preview";
+    oss << "NEW SUBJECT: [" << data.id << "," << direction << "]" << std::endl;
+
+    bool preferredStreamDirection = false; // false for output; true for input
+    auto it = ppm_.find("videostream");
+    if (it != ppm_.end()) {
+        preferredStreamDirection = it->second == "1";
+    }
+    oss << "preferredStreamDirection " << preferredStreamDirection << std::endl;
+    if (data.type == StreamType::video && !data.direction
+        && data.direction == preferredStreamDirection) {
+        subject->attach(mVS.get()); // your image
+        oss << "got my sent image attached" << std::endl;
+    } else if (data.type == StreamType::video && data.direction
+               && data.direction == preferredStreamDirection) {
+        subject->attach(mVS.get()); // the image you receive from others on the call
+        oss << "got received image attached" << std::endl;
+    }
+
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+}
+
+std::map<std::string, std::string>
+CoinCircleMediaHandler::getCallMediaHandlerDetails()
+{
+    return {{"name", NAME}, {"iconPath", datapath_ + sep + "icon.png"}, {"pluginId", id()}};
+}
+
+void
+CoinCircleMediaHandler::setPreferenceAttribute(const std::string& key, const std::string& value)
+{
+    auto it = ppm_.find(key);
+    if (it != ppm_.end() && it->second != value) {
+        it->second = value;
+    }
+}
+
+bool
+CoinCircleMediaHandler::preferenceMapHasKey(const std::string& key)
+{
+    return false;
+}
+
+void
+CoinCircleMediaHandler::detach()
+{
+    mVS->detach();
+}
+
+CoinCircleMediaHandler::~CoinCircleMediaHandler()
+{
+    std::ostringstream oss;
+    oss << " ~CoinCircleMediaHandler from HelloWorld Plugin" << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+    detach();
+}
+} // namespace jami
diff --git a/HelloWorld/CoinCircleMediaHandler.h b/HelloWorld/CoinCircleMediaHandler.h
new file mode 100644
index 0000000..c902f58
--- /dev/null
+++ b/HelloWorld/CoinCircleMediaHandler.h
@@ -0,0 +1,55 @@
+/**
+ *  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.
+ */
+
+#pragma once
+
+// Project
+#include "CoinCircleVideoSubscriber.h"
+
+// Plugin
+#include "plugin/jamiplugin.h"
+#include "plugin/mediahandler.h"
+
+using avSubjectPtr = std::weak_ptr<jami::Observable<AVFrame*>>;
+
+namespace jami {
+
+class CoinCircleMediaHandler : public jami::CallMediaHandler
+{
+public:
+    CoinCircleMediaHandler(std::map<std::string, std::string>&& ppm, std::string&& dataPath);
+    ~CoinCircleMediaHandler();
+
+    virtual void notifyAVFrameSubject(const StreamData& data, avSubjectPtr subject) override;
+    virtual std::map<std::string, std::string> getCallMediaHandlerDetails() override;
+
+    virtual void detach() override;
+    virtual void setPreferenceAttribute(const std::string& key, const std::string& value) override;
+    virtual bool preferenceMapHasKey(const std::string& key) override;
+
+    std::shared_ptr<CoinCircleVideoSubscriber> mVS;
+
+    const std::string& dataPath() const { return datapath_; }
+
+private:
+    const std::string datapath_;
+    std::map<std::string, std::string> ppm_;
+};
+} // namespace jami
diff --git a/HelloWorld/CoinCircleVideoSubscriber.cpp b/HelloWorld/CoinCircleVideoSubscriber.cpp
new file mode 100644
index 0000000..6b4a94e
--- /dev/null
+++ b/HelloWorld/CoinCircleVideoSubscriber.cpp
@@ -0,0 +1,200 @@
+/**
+ *  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 "CoinCircleVideoSubscriber.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 = "CoinCircle";
+const char sep = separator();
+
+namespace jami {
+
+CoinCircleVideoSubscriber::CoinCircleVideoSubscriber(const std::string& dataPath)
+    : path_ {dataPath}
+{}
+
+CoinCircleVideoSubscriber::~CoinCircleVideoSubscriber()
+{
+    std::ostringstream oss;
+    oss << "~CoinCircleMediaProcessor" << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+}
+
+void
+CoinCircleVideoSubscriber::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.
+        int w = resultFrame.size().width;
+        int h = resultFrame.size().height;
+        radius = std::min(w, h) / 8;
+        circlePos.y = static_cast<int>(radius);
+        circlePos.x = static_cast<int>(radius);
+        firstRun = false;
+    }
+
+    drawCoinCircle(angle);
+    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
+CoinCircleVideoSubscriber::drawCoinCircle(const int angle)
+{
+    if (!processingFrame.empty()) {
+        rotateFrame(angle, processingFrame);
+        cv::circle(processingFrame, circlePos, radius, baseColor, cv::FILLED);
+        rotateFrame(-angle, processingFrame);
+    }
+}
+
+void
+CoinCircleVideoSubscriber::rotateFrame(const int angle, cv::Mat& frame)
+{
+    if (angle == -90)
+        cv::rotate(frame, frame, cv::ROTATE_90_COUNTERCLOCKWISE);
+    else if (std::abs(angle) == 180)
+        cv::rotate(frame, frame, cv::ROTATE_180);
+    else if (angle == 90)
+        cv::rotate(frame, frame, cv::ROTATE_90_CLOCKWISE);
+}
+
+void
+CoinCircleVideoSubscriber::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
+CoinCircleVideoSubscriber::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
+CoinCircleVideoSubscriber::attached(jami::Observable<AVFrame*>* observable)
+{
+    std::ostringstream oss;
+    oss << "::Attached ! " << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+    observable_ = observable;
+}
+
+void
+CoinCircleVideoSubscriber::detached(jami::Observable<AVFrame*>*)
+{
+    firstRun = true;
+    observable_ = nullptr;
+    std::ostringstream oss;
+    oss << "::Detached()" << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+}
+
+void
+CoinCircleVideoSubscriber::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
diff --git a/HelloWorld/CoinCircleVideoSubscriber.h b/HelloWorld/CoinCircleVideoSubscriber.h
new file mode 100644
index 0000000..bc94ee3
--- /dev/null
+++ b/HelloWorld/CoinCircleVideoSubscriber.h
@@ -0,0 +1,72 @@
+/**
+ *  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.
+ */
+
+#pragma once
+
+// AvFrame
+extern "C" {
+#include <libavutil/frame.h>
+}
+#include <observer.h>
+
+// Frame Scaler
+#include <framescaler.h>
+
+#include <opencv2/core.hpp>
+
+namespace jami {
+
+class CoinCircleVideoSubscriber : public jami::Observer<AVFrame*>
+{
+public:
+    CoinCircleVideoSubscriber(const std::string& dataPath);
+    ~CoinCircleVideoSubscriber();
+
+    virtual void update(jami::Observable<AVFrame*>*, AVFrame* const&) override;
+    virtual void attached(jami::Observable<AVFrame*>*) override;
+    virtual void detached(jami::Observable<AVFrame*>*) override;
+
+    void detach();
+
+    void setColor(const std::string& color);
+
+private:
+    // Observer pattern
+    Observable<AVFrame*>* observable_ = nullptr;
+
+    // Data
+    std::string path_;
+    FrameScaler scaler;
+
+    // Status variables of the processing
+    bool firstRun {true};
+
+    // define custom variables
+    void copyByLine(const int lineSize);
+    void drawCoinCircle(const int angle);
+    void rotateFrame(const int angle, cv::Mat& frame);
+
+    cv::Point circlePos;
+    cv::Mat processingFrame;
+    cv::Mat resultFrame;
+    int radius;
+    cv::Scalar baseColor;
+};
+} // namespace jami
diff --git a/HelloWorld/build.sh b/HelloWorld/build.sh
new file mode 100755
index 0000000..6c7b833
--- /dev/null
+++ b/HelloWorld/build.sh
@@ -0,0 +1,220 @@
+#! /bin/bash
+# Build the plugin for the project
+set -e
+export OSTYPE
+ARCH=$(arch)
+EXTRAPATH=''
+# Flags:
+
+# -p: number of processors to use
+# -c: Runtime plugin cpu/gpu setting.
+# -t: target platform.
+
+
+if [ -z "${DAEMON}" ]; then
+    DAEMON="./../../daemon"
+    echo "DAEMON not provided, building with ${DAEMON}"
+fi
+
+PLUGIN_NAME="HelloWorld"
+JPL_FILE_NAME="${PLUGIN_NAME}.jpl"
+SO_FILE_NAME="lib${PLUGIN_NAME}.so"
+DAEMON_SRC="${DAEMON}/src"
+CONTRIB_PATH="${DAEMON}/contrib"
+PLUGINS_LIB="../lib"
+LIBS_DIR="./../contrib/Libs"
+
+if [ -z "${PLATFORM}" ]; then
+    PLATFORM="linux-gnu"
+    echo "PLATFORM not provided, building with ${PLATFORM}"
+    echo "Other options: redhat-linux"
+fi
+
+while getopts t:c:p OPT; do
+  case "$OPT" in
+    t)
+      PLATFORM="${OPTARG}"
+    ;;
+    c)
+      PROCESSOR="${OPTARG}"
+    ;;
+    p)
+    ;;
+    \?)
+      exit 1
+    ;;
+  esac
+done
+
+if [ "${PLATFORM}" = "linux-gnu" ] || [ "${PLATFORM}" = "redhat-linux" ]
+then
+    python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME}
+
+    CONTRIB_PLATFORM_CURT=${ARCH}
+    CONTRIB_PLATFORM=${CONTRIB_PLATFORM_CURT}-${PLATFORM}
+
+    # Compile
+    clang++ -std=c++17 -shared -fPIC \
+    -Wl,-Bsymbolic,-rpath,"\${ORIGIN}" \
+    -Wall -Wextra \
+    -Wno-unused-variable \
+    -Wno-unused-function \
+    -Wno-unused-parameter \
+    -I"." \
+    -I"${DAEMON_SRC}" \
+    -I"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/include" \
+    -I"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/include/opencv4" \
+    -I"${PLUGINS_LIB}" \
+    ./../lib/accel.cpp \
+    CoinCircleMediaHandler.cpp \
+    CenterCircleVideoSubscriber.cpp \
+    CenterCircleMediaHandler.cpp \
+    CoinCircleVideoSubscriber.cpp \
+    main.cpp \
+    -L"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/lib/" \
+    -l:libswscale.a \
+    -l:libavutil.a \
+    -lopencv_imgproc \
+    -lopencv_core \
+    -lva \
+    -o "build-local/jpl/lib/${CONTRIB_PLATFORM_CURT}-linux-gnu/${SO_FILE_NAME}"
+
+elif [ "${PLATFORM}" = "android" ]
+then
+    python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME} --distribution=${PLATFORM}
+
+    if [ -z "$ANDROID_NDK" ]; then
+            ANDROID_NDK="/home/${USER}/Android/Sdk/ndk/21.1.6352462"
+        echo "ANDROID_NDK not provided, building with ${ANDROID_NDK}"
+    fi
+
+    #=========================================================
+    #    Check if the ANDROID_ABI was provided
+    #    if not, set default
+    #=========================================================
+    if [ -z "$ANDROID_ABI" ]; then
+        ANDROID_ABI="armeabi-v7a arm64-v8a x86_64"
+        echo "ANDROID_ABI not provided, building for ${ANDROID_ABI}"
+    fi
+
+    buildlib() {
+        echo "$CURRENT_ABI"
+
+        #=========================================================
+        #    ANDROID TOOLS
+        #=========================================================
+        export HOST_TAG=linux-x86_64
+        export TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG
+
+        if [ "$CURRENT_ABI" = armeabi-v7a ]
+        then
+        export AR=$TOOLCHAIN/bin/arm-linux-android-ar
+        export AS=$TOOLCHAIN/bin/arm-linux-android-as
+        export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi21-clang
+        export CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi21-clang++
+        export LD=$TOOLCHAIN/bin/arm-linux-android-ld
+        export RANLIB=$TOOLCHAIN/bin/arm-linux-android-ranlib
+        export STRIP=$TOOLCHAIN/bin/arm-linux-androideabi-strip
+        export ANDROID_SYSROOT=${DAEMON}/../client-android/android-toolchain-21-arm/sysroot
+
+        elif [ "$CURRENT_ABI" = arm64-v8a ]
+        then
+        export AR=$TOOLCHAIN/bin/aarch64-linux-android-ar
+        export AS=$TOOLCHAIN/bin/aarch64-linux-android-as
+        export CC=$TOOLCHAIN/bin/aarch64-linux-android21-clang
+        export CXX=$TOOLCHAIN/bin/aarch64-linux-android21-clang++
+        export LD=$TOOLCHAIN/bin/aarch64-linux-android-ld
+        export RANLIB=$TOOLCHAIN/bin/aarch64-linux-android-ranlib
+        export STRIP=$TOOLCHAIN/bin/aarch64-linux-android-strip
+        export ANDROID_SYSROOT=${DAEMON}/../client-android/android-toolchain-21-arm64/sysroot
+
+        elif [ "$CURRENT_ABI" = x86_64 ]
+        then
+        export AR=$TOOLCHAIN/bin/x86_64-linux-android-ar
+        export AS=$TOOLCHAIN/bin/x86_64-linux-android-as
+        export CC=$TOOLCHAIN/bin/x86_64-linux-android21-clang
+        export CXX=$TOOLCHAIN/bin/x86_64-linux-android21-clang++
+        export LD=$TOOLCHAIN/bin/x86_64-linux-android-ld
+        export RANLIB=$TOOLCHAIN/bin/x86_64-linux-android-ranlib
+        export STRIP=$TOOLCHAIN/bin/x86_64-linux-android-strip
+        export ANDROID_SYSROOT=${DAEMON}/../client-android/android-toolchain-21-x86_64/sysroot
+
+        else
+        echo "ABI NOT OK" >&2
+        exit 1
+        fi
+
+        #=========================================================
+        #    CONTRIBS
+        #=========================================================
+        if [ "$CURRENT_ABI" = armeabi-v7a ]
+        then
+        CONTRIB_PLATFORM=arm-linux-androideabi
+
+        elif [ "$CURRENT_ABI" = arm64-v8a ]
+        then
+        CONTRIB_PLATFORM=aarch64-linux-android
+
+        elif [ "$CURRENT_ABI" = x86_64 ]
+        then
+        CONTRIB_PLATFORM=x86_64-linux-android
+        fi
+
+        #NDK SOURCES FOR cpufeatures
+        NDK_SOURCES=${ANDROID_NDK}/sources/android
+
+        #=========================================================
+        #    LD_FLAGS
+        #=========================================================
+        if [ "$CURRENT_ABI" = armeabi-v7a ]
+        then
+        export EXTRA_LDFLAGS="${EXTRA_LDFLAGS} -L${ANDROID_SYSROOT}/usr/lib/arm-linux-androideabi -L${ANDROID_SYSROOT}/usr/lib/arm-linux-androideabi/21"
+        elif [ "$CURRENT_ABI" = arm64-v8a ]
+        then
+        export EXTRA_LDFLAGS="${EXTRA_LDFLAGS} -L${ANDROID_SYSROOT}/usr/lib/aarch64-linux-android -L${ANDROID_SYSROOT}/usr/lib/aarch64-linux-android/21"
+        elif [ "$CURRENT_ABI" = x86_64 ]
+        then
+        export EXTRA_LDFLAGS="${EXTRA_LDFLAGS} -L${ANDROID_SYSROOT}/usr/lib/x86_64-linux-android -L${ANDROID_SYSROOT}/usr/lib/x86_64-linux-android/21"
+        fi
+
+        #=========================================================
+        #    Compile the plugin
+        #=========================================================
+
+        # Create so destination folder
+        $CXX --std=c++14 -O3 -g -fPIC \
+        -Wl,-Bsymbolic,-rpath,"\${ORIGIN}" \
+        -shared \
+        -Wall -Wextra \
+        -Wno-unused-variable \
+        -Wno-unused-function \
+        -Wno-unused-parameter \
+        -I"." \
+        -I"${DAEMON_SRC}" \
+        -I"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/include" \
+        -I"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/include/opencv4" \
+        -I"${PLUGINS_LIB}" \
+        ./../lib/accel.cpp \
+        CoinCircleMediaHandler.cpp \
+        CenterCircleVideoSubscriber.cpp \
+        CenterCircleMediaHandler.cpp \
+        CoinCircleVideoSubscriber.cpp \
+        main.cpp \
+        -L"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/lib/" \
+        -lswscale \
+        -lavutil \
+        -lopencv_imgproc \
+        -lopencv_core \
+        -llog -lz \
+        --sysroot=$ANDROID_SYSROOT \
+        -o "build-local/jpl/lib/$CURRENT_ABI/${SO_FILE_NAME}"
+    }
+
+    # Build the so
+    for i in ${ANDROID_ABI}; do
+        CURRENT_ABI=$i
+        buildlib
+    done
+fi
+
+python3 ./../SDK/jplManipulation.py --assemble --plugin=${PLUGIN_NAME} --distribution=${PLATFORM} --extraPath=${EXTRAPATH}
diff --git a/HelloWorld/data/icon.png b/HelloWorld/data/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..2ce0aab9f71679f71f7b3d53d8448f7e9852b322
GIT binary patch
literal 21340
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

literal 0
HcmV?d00001

diff --git a/HelloWorld/data/preferences.json b/HelloWorld/data/preferences.json
new file mode 100644
index 0000000..a8ba793
--- /dev/null
+++ b/HelloWorld/data/preferences.json
@@ -0,0 +1,38 @@
+[
+    {
+        "category": "stream",
+        "type": "List",
+        "key": "videostream",
+        "title": "Video stream",
+        "summary": "select a video direction",
+        "defaultValue": "0",
+        "scope": "plugin",
+        "entryValues": [
+            "0",
+            "1"
+        ],
+        "entries": [
+            "sent",
+            "received"
+        ]
+    },
+    {
+        "category": "color",
+        "type": "List",
+        "key": "color",
+        "title": "Circle color",
+        "summary": "select a color",
+        "defaultValue": "#00FF00",
+        "scope": "plugin,CenterCircle",
+        "entryValues": [
+            "#0000FF",
+            "#00FF00",
+            "#FF0000"
+        ],
+        "entries": [
+            "blue",
+            "green",
+            "red"
+        ]
+    }
+]
\ No newline at end of file
diff --git a/HelloWorld/main.cpp b/HelloWorld/main.cpp
new file mode 100644
index 0000000..11591b9
--- /dev/null
+++ b/HelloWorld/main.cpp
@@ -0,0 +1,77 @@
+/**
+ *  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 <iostream>
+#include <string.h>
+#include <thread>
+#include <memory>
+#include <plugin/jamiplugin.h>
+
+#include "CenterCircleMediaHandler.h"
+#include "CoinCircleMediaHandler.h"
+
+#ifdef WIN32
+#define EXPORT_PLUGIN __declspec(dllexport)
+#else
+#define EXPORT_PLUGIN
+#endif
+#define HelloWorld_VERSION_MAJOR 1
+#define HelloWorld_VERSION_MINOR 0
+#define HelloWorld_VERSION_PATCH 0
+extern "C" {
+void
+pluginExit(void)
+{}
+
+EXPORT_PLUGIN JAMI_PluginExitFunc
+JAMI_dynPluginInit(const JAMI_PluginAPI* api)
+{
+    std::cout << "**************************" << std::endl << std::endl;
+    std::cout << "**  HelloWorld  **" << std::endl;
+    std::cout << "**************************" << std::endl << std::endl;
+    std::cout << " Version " << HelloWorld_VERSION_MAJOR << "." << HelloWorld_VERSION_MINOR << "."
+              << HelloWorld_VERSION_PATCH << std::endl;
+
+    // If invokeService doesn't return an error
+    if (api) {
+        std::map<std::string, std::string> ppm;
+        api->invokeService(api, "getPluginPreferences", &ppm);
+        std::string dataPath;
+        api->invokeService(api, "getPluginDataPath", &dataPath);
+
+        auto fmpCenterCircleMediaHandler
+            = std::make_unique<jami::CenterCircleMediaHandler>(std::move(ppm), std::move(dataPath));
+        if (api->manageComponent(api,
+                                 "CallMediaHandlerManager",
+                                 fmpCenterCircleMediaHandler.release())) {
+            return nullptr;
+        }
+
+        auto fmpCoinCircleMediaHandler
+            = std::make_unique<jami::CoinCircleMediaHandler>(std::move(ppm), std::move(dataPath));
+        if (api->manageComponent(api,
+                                 "CallMediaHandlerManager",
+                                 fmpCoinCircleMediaHandler.release())) {
+            return nullptr;
+        }
+    }
+    return pluginExit;
+}
+}
diff --git a/HelloWorld/manifest.json b/HelloWorld/manifest.json
new file mode 100644
index 0000000..b524bb8
--- /dev/null
+++ b/HelloWorld/manifest.json
@@ -0,0 +1,5 @@
+{
+    "name": "HelloWorld",
+    "description": "HelloWorld draws a circle in the center of a call's video",
+    "version": "1.0.0"
+}
\ No newline at end of file
diff --git a/HelloWorld/package.json b/HelloWorld/package.json
new file mode 100644
index 0000000..91b5ea8
--- /dev/null
+++ b/HelloWorld/package.json
@@ -0,0 +1,19 @@
+{
+    "name": "HelloWorld",
+    "version": "1.0.0",
+    "extractLibs": false,
+    "deps": [
+        "ffmpeg",
+        "opencv"
+    ],
+    "defines": [],
+    "custom_scripts": {
+        "pre_build": [
+            "mkdir msvc"
+        ],
+        "build": [
+            "cmake --build ./msvc --config Release"
+        ],
+        "post_build": []
+    }
+}
\ No newline at end of file
-- 
GitLab