From 1e7736ccadedebbeeba3095543c1cb77cbb94d4b Mon Sep 17 00:00:00 2001
From: agsantos <aline.gondimsantos@savoirfairelinux.com>
Date: Wed, 28 Oct 2020 14:39:13 -0400
Subject: [PATCH] SDK: Scripts to generate plugin's skeleton code

done:
- create plugin folder structure
- manifest.json + icon.png
- copyright header
- main.cpp
- choose number of functionalities and the api to each one of them
- create src files for each functionality (APIs skeleton .h and .cpp)
- create preferences.json
- put colors into prints and clear outputs when needed
- modify src files to set preferences code
- create pakage.json
- add helper files
- use library Cmd
- reorganize functions into classes define inherits stack
- add GNU GPL to python files
- jpl merge function
- pre and post assembles
- default options plugin build
- windows build with build-plugin.py
- add build option for windows build
- generate base CMakeLists.txt and build.sh

Change-Id: Id8eb5a97fa7a51e99a0f9215835c3d5ffea630ad
GitLab: #2
---
 .gitignore                                    |   6 +
 GreenScreen/CMakeLists.txt                    |  14 +-
 GreenScreen/build.sh                          |  79 ++--
 GreenScreen/main.cpp                          |   3 +-
 GreenScreen/manifest.json                     |   2 +-
 GreenScreen/package.json                      |  12 +-
 GreenScreen/pluginMediaHandler.cpp            |   7 +-
 GreenScreen/pluginMediaHandler.h              |   2 +-
 GreenScreen/pluginProcessor.cpp               |   1 -
 GreenScreen/videoSubscriber.cpp               |   2 +-
 README_ASSEMBLE.md                            |   2 +-
 SDK/Docs/buildHelper.txt                      |  30 ++
 SDK/Docs/functionalityHelper.txt              |  20 +
 SDK/Docs/jplAssembleHelper.txt                |  26 ++
 SDK/Docs/jplMergeHelper.txt                   |  15 +
 SDK/Docs/mainHelper.txt                       |  16 +
 SDK/Docs/manifestHelper.txt                   |  14 +
 SDK/Docs/packageHelper.txt                    |  29 ++
 SDK/Docs/pipelineHelper.txt                   |  31 ++
 SDK/Docs/preferenceHelper.txt                 |  44 ++
 SDK/Docs/preferenceList.txt                   |  26 ++
 SDK/Docs/preferencePath.txt                   |  20 +
 SDK/Templates/CMakeLists.txt                  |  81 ++++
 SDK/Templates/build.sh                        | 205 ++++++++
 SDK/Templates/copyright.txt                   |  19 +
 SDK/Templates/defaultDependenciesStrings.json |  10 +
 SDK/Templates/genericConversationHandler.h    |  23 +
 SDK/Templates/genericMediaHandler.cpp         |  84 ++++
 SDK/Templates/genericMediaHandler.h           |  37 ++
 SDK/Templates/genericVideoSubscriber.cpp      | 119 +++++
 SDK/Templates/genericVideoSubscriber.h        |  39 ++
 SDK/Templates/icon.png                        | Bin 0 -> 21340 bytes
 SDK/Templates/main.cpp                        |  48 ++
 SDK/Templates/manifest.json                   |   5 +
 SDK/Templates/package.json                    |  16 +
 SDK/Templates/preferences.json                |  23 +
 SDK/generateProject.py                        |  76 +++
 SDK/jplManipulation.py                        | 239 ++++++++++
 SDK/manifestProfile.py                        | 104 +++++
 SDK/pluginMainSDK.py                          | 200 ++++++++
 SDK/pluginStructureProfile.py                 | 124 +++++
 SDK/preferencesProfile.py                     | 251 ++++++++++
 SDK/requirements.txt                          |   2 +
 SDK/sdkConstants.py                           |  62 +++
 SDK/skeletonSrcProfile.py                     | 438 ++++++++++++++++++
 SDK/userProfile.py                            |  65 +++
 SDK/utils.py                                  |  46 ++
 assemble-plugin.py                            |  49 --
 build-plugin.py                               |  65 ++-
 docker/Dockerfile_android                     |  84 ++--
 lib/accel.cpp                                 |  67 +++
 lib/accel.h                                   |  39 +-
 lib/framescaler.h                             |   6 +-
 53 files changed, 2824 insertions(+), 203 deletions(-)
 create mode 100644 SDK/Docs/buildHelper.txt
 create mode 100644 SDK/Docs/functionalityHelper.txt
 create mode 100644 SDK/Docs/jplAssembleHelper.txt
 create mode 100644 SDK/Docs/jplMergeHelper.txt
 create mode 100644 SDK/Docs/mainHelper.txt
 create mode 100644 SDK/Docs/manifestHelper.txt
 create mode 100644 SDK/Docs/packageHelper.txt
 create mode 100644 SDK/Docs/pipelineHelper.txt
 create mode 100644 SDK/Docs/preferenceHelper.txt
 create mode 100644 SDK/Docs/preferenceList.txt
 create mode 100644 SDK/Docs/preferencePath.txt
 create mode 100644 SDK/Templates/CMakeLists.txt
 create mode 100644 SDK/Templates/build.sh
 create mode 100644 SDK/Templates/copyright.txt
 create mode 100644 SDK/Templates/defaultDependenciesStrings.json
 create mode 100644 SDK/Templates/genericConversationHandler.h
 create mode 100644 SDK/Templates/genericMediaHandler.cpp
 create mode 100644 SDK/Templates/genericMediaHandler.h
 create mode 100644 SDK/Templates/genericVideoSubscriber.cpp
 create mode 100644 SDK/Templates/genericVideoSubscriber.h
 create mode 100644 SDK/Templates/icon.png
 create mode 100644 SDK/Templates/main.cpp
 create mode 100644 SDK/Templates/manifest.json
 create mode 100644 SDK/Templates/package.json
 create mode 100644 SDK/Templates/preferences.json
 create mode 100644 SDK/generateProject.py
 create mode 100644 SDK/jplManipulation.py
 create mode 100644 SDK/manifestProfile.py
 create mode 100644 SDK/pluginMainSDK.py
 create mode 100644 SDK/pluginStructureProfile.py
 create mode 100644 SDK/preferencesProfile.py
 create mode 100644 SDK/requirements.txt
 create mode 100644 SDK/sdkConstants.py
 create mode 100644 SDK/skeletonSrcProfile.py
 create mode 100644 SDK/userProfile.py
 create mode 100644 SDK/utils.py
 delete mode 100644 assemble-plugin.py
 create mode 100644 lib/accel.cpp

diff --git a/.gitignore b/.gitignore
index 022c659..548a5c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,11 @@
 /build/
+*.jpl
 *msvc*
+*build-local*
 *android-toolchain-*
 config.mak
 *Libs*
+*__pycache__*
+/foo/
+/.vscode/
+/HelloWorld/
diff --git a/GreenScreen/CMakeLists.txt b/GreenScreen/CMakeLists.txt
index 61f9d40..9254bd9 100644
--- a/GreenScreen/CMakeLists.txt
+++ b/GreenScreen/CMakeLists.txt
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
 
 # set the project name
 set (ProjectName GreenScreen)
-set (Version 1.0)
+set (Version 1.0.1)
 
 project(${ProjectName} VERSION ${Version})
 
@@ -77,6 +77,7 @@ set(plugin_SRC main.cpp
                pluginProcessor.cpp
                TFInference.cpp
                videoSubscriber.cpp
+               ./../lib/accel.cpp
                )
 
 set(plugin_HDR pluginInference.h
@@ -142,14 +143,11 @@ endif()
 add_custom_command(
     TARGET ${ProjectName}
     PRE_BUILD
-    COMMAND ${CMAKE_COMMAND} -E remove_directory -r ${JPL_DIRECTORY}
-    COMMAND ${CMAKE_COMMAND} -E remove_directory -r ${JPL_DIRECTORY}/../../../build/${ProjectName}
-    COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data ${JPL_DIRECTORY}/data
+    COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --preassemble --plugin=GreenScreen
     COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBS_BIN_DIR}/${TENSORFLOW}/lib/ ${JPL_DIRECTORY}/lib
     COMMAND ${CMAKE_COMMAND} -E make_directory ${JPL_DIRECTORY}/data/models
     COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/modelsSRC/${model} ${JPL_DIRECTORY}/data/models
     COMMAND ${CMAKE_COMMAND} -E rename ${JPL_DIRECTORY}/data/models/${model} ${JPL_DIRECTORY}/data/models/mModel${modelType}
-    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/manifest.json ${JPL_DIRECTORY}
     COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/${preferencesFile} ${JPL_DIRECTORY}/data
     COMMAND ${CMAKE_COMMAND} -E rename ${JPL_DIRECTORY}/data/${preferencesFile} ${JPL_DIRECTORY}/data/preferences.json
     COMMENT "Assembling Plugin files"
@@ -162,7 +160,7 @@ if (WIN32)
         COMMAND ${CMAKE_COMMAND} -E make_directory ${JPL_DIRECTORY}/../../../build/x64-windows/${TENSORFLOW}
         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 python ${PROJECT_SOURCE_DIR}/../assemble-plugin.py --plugins=GreenScreen --extraPath=${TENSORFLOW}
+        COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --assemble --plugin=GreenScreen --extraPath=${TENSORFLOW}
         COMMENT "Generating JPL archive"
     )
 else()
@@ -170,8 +168,8 @@ else()
         TARGET ${ProjectName}
         POST_BUILD
         COMMAND ${CMAKE_COMMAND} -E make_directory ${JPL_DIRECTORY}/../../../build/x86_64-linux-gnu/${TENSORFLOW}
-        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${LIBRARY_FILE_NAME} ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
-        COMMAND python ${PROJECT_SOURCE_DIR}/../assemble-plugin.py --plugins=GreenScreen --extraPath=${TENSORFLOW}
+        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/${LIBRARY_FILE_NAME} ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
+        COMMAND python3 ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --assemble --plugin=GreenScreen --extraPath=${TENSORFLOW}
         COMMENT "Generating JPL archive"
     )
 
diff --git a/GreenScreen/build.sh b/GreenScreen/build.sh
index ad60269..b046871 100755
--- a/GreenScreen/build.sh
+++ b/GreenScreen/build.sh
@@ -2,7 +2,7 @@
 # Build the plugin for the project
 export OSTYPE
 ARCH=$(arch)
-
+EXTRAPATH=''
 # Flags:
 
   # -p: number of processors to use
@@ -59,30 +59,25 @@ if [ -z "${TF}" ]; then
     TF="_tensorflow_cc"
 fi
 echo "Building with ${TF}"
-mkdir ./data/models
-
 
 if [[ "${TF}" = "_tensorflow_cc" ]] && [[ "${PLATFORM}" = "linux-gnu" ]]
 then
     if [ -z "$CUDALIBS" ]; then
-        rm -r ./data/models
         echo "CUDALIBS must point to CUDA 10.1!"
         exit
     fi
     if [ -z "$CUDNN" ]; then
-        rm -r ./data/models
         echo "CUDNN must point to libcudnn.so 7!"
         exit
     fi
 
     echo "Building for ${PROCESSOR}"
 
+    python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME} 
+
     CONTRIB_PLATFORM_CURT=${ARCH}
     CONTRIB_PLATFORM=${CONTRIB_PLATFORM_CURT}-${PLATFORM}
-    DESTINATION_PATH="./../build/${CONTRIB_PLATFORM}/${TF}"
-    mkdir -p "lib/${CONTRIB_PLATFORM}"
-    mkdir -p "${DESTINATION_PATH}"
-
+    EXTRAPATH=${TF}
 
     # Compile
     clang++ -std=c++17 -shared -fPIC \
@@ -99,6 +94,7 @@ then
     -I"${LIBS_DIR}/${TF}/include" \
     -I"${LIBS_DIR}/${TF}/include/third_party/eigen3" \
     -I"${PLUGINS_LIB}" \
+    ./../lib/accel.cpp \
     main.cpp \
     videoSubscriber.cpp \
     pluginProcessor.cpp \
@@ -117,28 +113,30 @@ then
     -llibpng \
     -lva \
     -ltensorflow_cc \
-    -o "lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
-
-    cp "${TF_LIBS_DIR}/${TF}/lib/${CONTRIB_PLATFORM}-gpu61/libtensorflow_cc.so" "lib/$CONTRIB_PLATFORM/libtensorflow_cc.so.2"
-    cp "${CUDALIBS}/libcudart.so" "lib/$CONTRIB_PLATFORM/libcudart.so.10.0"
-    cp "${CUDNN}/libcublas.so.10" "lib/$CONTRIB_PLATFORM/libcublas.so.10.0"
-    cp "${CUDALIBS}/libcufft.so.10" "lib/$CONTRIB_PLATFORM/libcufft.so.10.0"
-    cp "${CUDALIBS}/libcurand.so.10" "lib/$CONTRIB_PLATFORM/libcurand.so.10.0"
-    cp "${CUDALIBS}/libcusolver.so.10" "lib/$CONTRIB_PLATFORM/libcusolver.so.10.0"
-    cp "${CUDALIBS}/libcusparse.so.10" "lib/$CONTRIB_PLATFORM/libcusparse.so.10.0"
-    cp "${CUDNN}/libcudnn.so.7" "lib/$CONTRIB_PLATFORM"
-
-    cp ./modelsSRC/mModel-resnet50float.pb ./data/models/mModel.pb
-    cp ./preferences-tfcc.json ./data/preferences.json
+    -o "build-local/jpl/lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
+
+    cp "${TF_LIBS_DIR}/${TF}/lib/${CONTRIB_PLATFORM}-gpu61/libtensorflow_cc.so" "build-local/jpl/lib/$CONTRIB_PLATFORM/libtensorflow_cc.so.2"
+    cp "${CUDALIBS}/libcudart.so" "build-local/jpl/lib/$CONTRIB_PLATFORM/libcudart.so.10.0"
+    cp "${CUDNN}/libcublas.so.10" "build-local/jpl/lib/$CONTRIB_PLATFORM/libcublas.so.10.0"
+    cp "${CUDALIBS}/libcufft.so.10" "build-local/jpl/lib/$CONTRIB_PLATFORM/libcufft.so.10.0"
+    cp "${CUDALIBS}/libcurand.so.10" "build-local/jpl/lib/$CONTRIB_PLATFORM/libcurand.so.10.0"
+    cp "${CUDALIBS}/libcusolver.so.10" "build-local/jpl/lib/$CONTRIB_PLATFORM/libcusolver.so.10.0"
+    cp "${CUDALIBS}/libcusparse.so.10" "build-local/jpl/lib/$CONTRIB_PLATFORM/libcusparse.so.10.0"
+    cp "${CUDNN}/libcudnn.so.7" "build-local/jpl/lib/$CONTRIB_PLATFORM"
+
+    pwd
+    mkdir ./build-local/jpl/data/models
+    cp ./modelsSRC/mModel-resnet50float.pb ./build-local/jpl/data/models/mModel.pb
+    cp ./preferences-tfcc.json ./build-local/jpl/data/preferences.json
 elif [ "${TF}" = "_tensorflowLite" ]
 then
     if [ "${PLATFORM}" = "linux-gnu" ]
     then
+        python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME}
+
         CONTRIB_PLATFORM_CURT=${ARCH}
         CONTRIB_PLATFORM=${CONTRIB_PLATFORM_CURT}-${PLATFORM}
-        DESTINATION_PATH="./../build/${CONTRIB_PLATFORM}/${TF}"
-        mkdir -p "lib/${CONTRIB_PLATFORM}"
-        mkdir -p "${DESTINATION_PATH}"
+        EXTRAPATH="${TF}"
 
         # Compile
         clang++ -std=c++17 -shared -fPIC \
@@ -155,6 +153,7 @@ then
         -I"${LIBS_DIR}/${TF}/include/flatbuffers" \
         -I"${LIBS_DIR}/${TF}/include" \
         -I"${PLUGINS_LIB}" \
+        ./../lib/accel.cpp \
         main.cpp \
         videoSubscriber.cpp \
         pluginProcessor.cpp \
@@ -173,14 +172,13 @@ then
         -ltensorflowlite \
         -llibpng \
         -lva \
-        -o "lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
+        -o "build-local/jpl/lib/${CONTRIB_PLATFORM}/${SO_FILE_NAME}"
 
-        cp "${TF_LIBS_DIR}/${TF}/lib/${CONTRIB_PLATFORM}/libtensorflowlite.so" "lib/$CONTRIB_PLATFORM"
+        cp "${TF_LIBS_DIR}/${TF}/lib/${CONTRIB_PLATFORM}/libtensorflowlite.so" "build-local/jpl/lib/$CONTRIB_PLATFORM"
 
     elif [ "${PLATFORM}" = "android" ]
     then
-        DESTINATION_PATH="./../build/android"
-        mkdir -p "${DESTINATION_PATH}"
+        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"
@@ -198,7 +196,6 @@ then
 
         buildlib() {
             echo "$CURRENT_ABI"
-            mkdir -p "lib/$CURRENT_ABI"
 
             #=========================================================
             #    ANDROID TOOLS
@@ -241,8 +238,6 @@ then
 
             else
             echo "ABI NOT OK" >&2
-            rm -r lib/
-            rm -r ./data/models
             exit 1
             fi
 
@@ -304,6 +299,7 @@ then
             -I"${LIBS_DIR}/${TF}/include/flatbuffers" \
             -I"${LIBS_DIR}/${TF}/include" \
             -I"${PLUGINS_LIB}" \
+            ./../lib/accel.cpp \
             main.cpp \
             videoSubscriber.cpp \
             pluginProcessor.cpp \
@@ -323,9 +319,9 @@ then
             -ltensorflowlite \
             -llog -lz \
             --sysroot=$ANDROID_SYSROOT \
-            -o "lib/$CURRENT_ABI/${SO_FILE_NAME}"
+            -o "build-local/jpl/lib/$CURRENT_ABI/${SO_FILE_NAME}"
 
-            cp "${TF_LIBS_DIR}/${TF}/lib/${CURRENT_ABI}/libtensorflowlite.so" "lib/$CURRENT_ABI"
+            cp "${TF_LIBS_DIR}/${TF}/lib/${CURRENT_ABI}/libtensorflowlite.so" "build-local/jpl/lib/$CURRENT_ABI"
             rm cpu-features.o
         }
 
@@ -336,16 +332,9 @@ then
         done
     fi
 
-    cp ./modelsSRC/mobilenet_v2_deeplab_v3_256_myquant.tflite ./data/models/mModel.tflite
-    cp ./preferences-tflite.json ./data/preferences.json
+    mkdir ./build-local/jpl/data/models
+    cp ./modelsSRC/mobilenet_v2_deeplab_v3_256_myquant.tflite ./build-local/jpl/data/models/mModel.tflite
+    cp ./preferences-tflite.json ./build-local/jpl/data/preferences.json
 fi
 
-zip -r ${JPL_FILE_NAME} data manifest.json lib
-mv ${JPL_FILE_NAME} ${DESTINATION_PATH}/
-
-# Cleanup
-# Remove lib after compilation
-rm -rf lib
-rm -r ./data/models
-rm ./data/preferences.json
-
+python3 ./../SDK/jplManipulation.py --assemble --plugin=${PLUGIN_NAME} --distribution=${PLATFORM} --extraPath=${EXTRAPATH}
diff --git a/GreenScreen/main.cpp b/GreenScreen/main.cpp
index b14e289..cdf3602 100644
--- a/GreenScreen/main.cpp
+++ b/GreenScreen/main.cpp
@@ -33,6 +33,7 @@
 
 #define GreenScreen_VERSION_MAJOR 1
 #define GreenScreen_VERSION_MINOR 0
+#define GreenScreen_VERSION_PATCH 1
 
 extern "C" {
 void
@@ -46,7 +47,7 @@ JAMI_dynPluginInit(const JAMI_PluginAPI* api)
     std::cout << "**  GREENSCREEN PLUGIN  **" << std::endl;
     std::cout << "**************************" << std::endl << std::endl;
     std::cout << " Version " << GreenScreen_VERSION_MAJOR << "." << GreenScreen_VERSION_MINOR
-              << std::endl;
+              << "." << GreenScreen_VERSION_PATCH << std::endl;
 
     // If invokeService doesn't return an error
     if (api) {
diff --git a/GreenScreen/manifest.json b/GreenScreen/manifest.json
index 49d8a79..0b7d078 100644
--- a/GreenScreen/manifest.json
+++ b/GreenScreen/manifest.json
@@ -1,5 +1,5 @@
 {
     "name": "GreenScreen",
     "description" : "GreenScreen Plugin with Tensorflow 2.1.1",
-    "version" : "1.0"
+    "version" : "1.0.1"
 }
diff --git a/GreenScreen/package.json b/GreenScreen/package.json
index b5f0c31..295f2eb 100644
--- a/GreenScreen/package.json
+++ b/GreenScreen/package.json
@@ -1,20 +1,22 @@
 {
     "name": "GreenScreen",
-    "version": "1.0",
+    "version": "1.0.1",
     "extractLibs": true,
     "deps": [
         "ffmpeg",
-        "opencv"],
+        "opencv"
+    ],
     "defines": [
         "TFLITE=False",
-        "CPU=False"],
+        "CPU=False"
+    ],
     "custom_scripts": {
         "pre_build": [
             "mkdir msvc"
         ],
         "build": [
             "cmake --build ./msvc --config Release"
-            ],
+        ],
         "post_build": []
     }
-}
+}
\ No newline at end of file
diff --git a/GreenScreen/pluginMediaHandler.cpp b/GreenScreen/pluginMediaHandler.cpp
index a9e4fe0..9aae38b 100644
--- a/GreenScreen/pluginMediaHandler.cpp
+++ b/GreenScreen/pluginMediaHandler.cpp
@@ -67,12 +67,7 @@ PluginMediaHandler::notifyAVFrameSubject(const StreamData& data, jami::avSubject
 std::map<std::string, std::string>
 PluginMediaHandler::getCallMediaHandlerDetails()
 {
-    std::map<std::string, std::string> mediaHandlerDetails = {};
-    mediaHandlerDetails["name"] = NAME;
-    mediaHandlerDetails["iconPath"] = datapath_ + sep + "icon.png";
-    mediaHandlerDetails["pluginId"] = id();
-
-    return mediaHandlerDetails;
+    return {{"name", NAME}, {"iconPath", datapath_ + sep + "icon.png"}, {"pluginId", id()}};
 }
 
 void
diff --git a/GreenScreen/pluginMediaHandler.h b/GreenScreen/pluginMediaHandler.h
index 4748609..0e1d011 100644
--- a/GreenScreen/pluginMediaHandler.h
+++ b/GreenScreen/pluginMediaHandler.h
@@ -47,7 +47,7 @@ public:
 
     std::shared_ptr<VideoSubscriber> mVS;
 
-    std::string dataPath() const { return datapath_; }
+    const std::string& dataPath() const { return datapath_; }
 
 private:
     const std::string datapath_;
diff --git a/GreenScreen/pluginProcessor.cpp b/GreenScreen/pluginProcessor.cpp
index 6815f08..3b38830 100644
--- a/GreenScreen/pluginProcessor.cpp
+++ b/GreenScreen/pluginProcessor.cpp
@@ -168,7 +168,6 @@ copyByLine(uchar* frameData, uchar* applyMaskData, const int lineSize, cv::Size
 {
     if (3 * size.width == lineSize) {
         std::memcpy(frameData, applyMaskData, size.height * size.width * 3);
-        ;
     } else {
         int rows = size.height;
         int offset = 0;
diff --git a/GreenScreen/videoSubscriber.cpp b/GreenScreen/videoSubscriber.cpp
index dc14a40..b7ec336 100644
--- a/GreenScreen/videoSubscriber.cpp
+++ b/GreenScreen/videoSubscriber.cpp
@@ -22,9 +22,9 @@
 #include "videoSubscriber.h"
 // Use for display rotation matrix
 extern "C" {
-#include <accel.h>
 #include <libavutil/display.h>
 }
+#include <accel.h>
 
 // Opencv processing
 #include <opencv2/imgcodecs.hpp>
diff --git a/README_ASSEMBLE.md b/README_ASSEMBLE.md
index 04bf28e..39b590f 100644
--- a/README_ASSEMBLE.md
+++ b/README_ASSEMBLE.md
@@ -43,7 +43,7 @@ Dependencies:
 
 $ git clone https://github.com/tensorflow/tensorflow.git
 $ cd tensorflow
-$ git checkout -b v2.1.0
+$ git checkout v2.1.0
 
 
 For Android: (Tensorflow Lite)
diff --git a/SDK/Docs/buildHelper.txt b/SDK/Docs/buildHelper.txt
new file mode 100644
index 0000000..4121eb6
--- /dev/null
+++ b/SDK/Docs/buildHelper.txt
@@ -0,0 +1,30 @@
+
+To compile (or create build files) with default configurations: BUILD (-def)
+
+This option allows the default plugin compilation or the creation of the
+CMakeLists.txt and build.sh files.
+
+As compilation tool:
+    This option does not support cross-compilation neither optional build.
+    If you want to compile for another platform, or if you have build variables
+    that may vary, please directly use the <jami-plugins>/build-plugin.py script.
+
+    -> For GNU/Linux host, remember to set all environment variables
+    before calling the build script.
+    -> For Windows host, remember to properly set cmake configuration
+    options in plugin's package.json.
+
+As build files creation tool:
+    This option creates base files used for plugins build. For GNU/Linux and Android
+    compilations, the current plugin environment expects a build.sh file capable of
+    pre-assembling, building and assembling the final jpl. For Windows, the environment
+    expects a CMakeLists.txt and package.json with custom build rules for cmake.
+
+    For pratical examples and further developpment of those files, you may peek at our
+    available plugins at https://git.jami.net/savoirfairelinux/jami-plugins .
+
+Alternatively, you can ignore this SDK and build your plugin using the build-plugin.py
+script.
+
+For more technical information, please refer to:
+https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/7.-Jami-plugins .
diff --git a/SDK/Docs/functionalityHelper.txt b/SDK/Docs/functionalityHelper.txt
new file mode 100644
index 0000000..b238fdc
--- /dev/null
+++ b/SDK/Docs/functionalityHelper.txt
@@ -0,0 +1,20 @@
+
+Create a functionality's skeleton files: FUNCTIONALITY
+
+A functionality is a capability and a single Jami Plugin can implement
+multiple functionalities.
+
+Each functionality may be applied to different data types that requires
+diferent APIs. Currently, our plugin system supports the following data:
+(1) video during a call (Media Handler API).
+
+
+Be aware that when creating functionalities outside a full plugin pipeline
+creation, some modifications to match the preferences usablities may have
+to be done by hand. If you don't know how to do it, consider creating a
+functionality from inside a full plugin creation pipeline and then you can
+use it as an example. Alternatively, you can peek at our available plugins
+at https://git.jami.net/savoirfairelinux/jami-plugins .
+
+For more technical information, please refer to:
+https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/7.-Jami-plugins .
diff --git a/SDK/Docs/jplAssembleHelper.txt b/SDK/Docs/jplAssembleHelper.txt
new file mode 100644
index 0000000..0dd95c6
--- /dev/null
+++ b/SDK/Docs/jplAssembleHelper.txt
@@ -0,0 +1,26 @@
+
+To assemble a jpl: ASSEMBLE (-pre)
+
+The `assemble -pre` option gives the user the oportunity to create a folder
+as bellow:
+ - linux host: <jami-plugins>/<PLUGINNAME>/build-local/jpl/
+ - windows host: <jami-plugins>/<PLUGINNAME>/msvc/jpl/
+containing all data that will be inside `PLUGINNAME.jpl` archive.
+The exception is the plugin library itself that only will be added after
+compilation.
+
+The default usage will take all files inside the folder created by the `-pre`
+variation and compress them to your PLUGINNAME.jpl archive. This file can be
+find under <jami-plugins>/build/.
+
+Both process must be called from inside the CMakeLists.txt as POST_BUILD and
+PRE_BUILD commands, respectively. Also, the build.sh script must call them.
+Our default CMakeLists.txt and build.sh script already applies them. Also, you
+can check other usage examples from our available plugins at
+https://git.jami.net/savoirfairelinux/jami-plugins .
+
+For more information about our default CMakeLists.txt and build.sh, please refer to:
+    -> help build
+
+For more technical information, please refer to:
+https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/7.-Jami-plugins .
diff --git a/SDK/Docs/jplMergeHelper.txt b/SDK/Docs/jplMergeHelper.txt
new file mode 100644
index 0000000..d315a3f
--- /dev/null
+++ b/SDK/Docs/jplMergeHelper.txt
@@ -0,0 +1,15 @@
+
+To merge two different jpl: MERGE foo.jpl bar.jpl
+
+Be aware tha merging two or more jpls may incur orverwriting some of the files
+inside your plugins archives if they are not equal for all plugins. The only files
+that may not present conflicting contents are the ones that do not repeate themselves.
+If conflicts occur, files from the first jpl, in our case the foo.jpl, will prevail
+over the others.
+
+This tool is useful when you build a plugin for different platforms and want to compile
+a single PLUGINNAME.jpl archive to be publish and used along cross platforms, ie: same jpl
+can be used for Windows, GNU/Linux, and Android.
+
+For more technical information, please refer to:
+https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/7.-Jami-plugins .
diff --git a/SDK/Docs/mainHelper.txt b/SDK/Docs/mainHelper.txt
new file mode 100644
index 0000000..836001a
--- /dev/null
+++ b/SDK/Docs/mainHelper.txt
@@ -0,0 +1,16 @@
+
+Create a main.cpp skeleton files: MAIN
+
+This option creates plugin's 'main.cpp' file. If this file already exists,
+it will be overwritten.
+The main file implement plugin's external loading function that Jami plugin
+system will call to initialize and register all functionalities for latter
+use.
+
+Since every functionality must be covered by the initialization function,
+every time a new functionality is added to the plugin, it also must be added
+to the initialization function. For that reason, this SDK is set to rewrite
+the main.cpp file every time you create a new functionality.
+
+For more technical information, please refer to:
+https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/7.-Jami-plugins .
diff --git a/SDK/Docs/manifestHelper.txt b/SDK/Docs/manifestHelper.txt
new file mode 100644
index 0000000..0c36a2f
--- /dev/null
+++ b/SDK/Docs/manifestHelper.txt
@@ -0,0 +1,14 @@
+
+Create/Modify (or Delete) a manifest file: MANIFEST (-del)
+
+A manifest.json is a json file containing the plugin name, desciption and version.
+
+manifest.json skeleton:
+{
+    "name": "foo",                                       -> plugin name
+    "description: "This plugins does this and that",     -> plugin functionalities description
+    "version": "0.0.0"                                   -> plugin version, must be of the form X.Y.Z
+}
+
+For more technical information, please refer to:
+https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/7.-Jami-plugins .
diff --git a/SDK/Docs/packageHelper.txt b/SDK/Docs/packageHelper.txt
new file mode 100644
index 0000000..60422f7
--- /dev/null
+++ b/SDK/Docs/packageHelper.txt
@@ -0,0 +1,29 @@
+
+Create a package.json: PACKAGE
+
+A package.json is a json file containing important build information needed for the
+windows build pipeline used by the daemon project.
+
+package.json skeleton:
+{
+    "name": "PLUGINNAME",                                -> plugin name;
+    "version": "X.Y.Z",                                  -> plugin version, must be of the form X.Y.Z;
+    "extractLibs": false,                                -> use tensorflow header files inside
+                                                            contrib/libs.tar.gz;
+    "deps": [],                                          -> dependencies that can be build by ring-daemon
+                                                            project. ie: ffmpeg and opencv;
+    "defines": [],                                       -> cmake definitions to configure plugin project;
+    "custom_scripts": {
+        "pre_build": [
+            "mkdir msvc"                                 -> creates directory to generate and build plugin
+                                                            project;
+        ],
+        "build": [
+            "cmake --build ./msvc --config Release"      -> cmake build command.
+            ],
+        "post_build": []
+    }
+}
+
+For more technical information, please refer to:
+https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/7.-Jami-plugins .
diff --git a/SDK/Docs/pipelineHelper.txt b/SDK/Docs/pipelineHelper.txt
new file mode 100644
index 0000000..bbe3f26
--- /dev/null
+++ b/SDK/Docs/pipelineHelper.txt
@@ -0,0 +1,31 @@
+
+Create a plugin skeleton: PLUGIN
+
+A plugin creation will pass through several steps:
+    1 - authorship information;
+    2 - name;
+    3 - manifest;
+    4 - main file;
+    5 - functionalities;
+    6 - preferences;
+    7 - package;
+    8 - build related files,
+
+And will output a basic plugin capable of being build, installed, loaded and used!
+This basic plugin, although will perform no modification to the data to which it
+is linked to. The data process implementation is on your hands!
+
+Feel free to create awesome plugins! But respect these constraints:
+- use ffmpeg and opencv from ring-daemon project.
+- if using tensorflow, choose version 2.1.0. Why?
+  -> We have all needed header files of tensorflow in <jami-plugins>/contrib/libs.tar.gz;
+  -> We provide docker images with libraries for Tensorflow C++ API and Tensorflow
+     Lite, for both Android and Linux development.
+- if you need other libraries, check if we support it's build with ring-daemon project,
+  otherwise, you will have to build it and ensure correctly link to the plugin libraries.
+
+For detailed information about manifest, main file, functionalities, preferences, package,
+and build related files please refer to the specific help command.
+
+For DockerHub links and more technical information, please refer to:
+https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/7.-Jami-plugins .
diff --git a/SDK/Docs/preferenceHelper.txt b/SDK/Docs/preferenceHelper.txt
new file mode 100644
index 0000000..078c1a5
--- /dev/null
+++ b/SDK/Docs/preferenceHelper.txt
@@ -0,0 +1,44 @@
+
+Create (or Delete) a preference: PREFERENCE (-del)
+
+A preference is a internal variable that can be used upon loading or while
+running the plugin. It should be correctly formed and listed in a preferences.json
+file. Also, a preference value only can be modified during runtime if the code that
+applies the new value is within your functionality implementation. For more
+technical information, please refer to:
+https://git.jami.net/savoirfairelinux/ring-project/wikis/technical/7.-Jami-plugins .
+
+Prior continuing, and to easy the process of constructing your preferences, take your
+time to think about the list of preferences you will be needing.
+But do not worry! If you forget something you will have the oportunity to change it
+later.
+
+Example of preferences:
+    -> LIST:
+        {
+            "category" : "Streams",
+            "type": "List",
+            "key": "videostreams",
+            "title": "Streams to transform",
+            "summary": "Select video to transform",
+            "defaultValue": "0",
+            "entries": ["sent", "received"],
+            "entryValues": ["0", "1"],
+            "scope": "plugin"
+        }
+
+    -> PATH:
+        {
+            "category" : "Backgrounds",
+            "type": "Path",
+            "mimeType": "image/png",
+            "key": "background",
+            "title": "Background image",
+            "summary": "Select the image background to use",
+            "defaultValue": "data/backgrounds/background2.png",
+            "scope": "plugin, foo"
+        }
+
+For more detailed information for each of the preferences types, please specify the preference type:
+    -> preference -h list
+    -> preference -h path
diff --git a/SDK/Docs/preferenceList.txt b/SDK/Docs/preferenceList.txt
new file mode 100644
index 0000000..253e64e
--- /dev/null
+++ b/SDK/Docs/preferenceList.txt
@@ -0,0 +1,26 @@
+
+Create (or Delete) a preference: PREFERENCE (-del)
+
+List preference formation example:
+{
+    "category" : "Streams",                       -> user choice;
+    "type": "List",                               -> fixed as List;
+    "key": "videostreams",                        -> user choice, must not repeate other key inside 
+                                                     same preferences.json file;
+    "title": "Streams to transform",              -> user choice;
+    "summary": "Select video to transform",       -> user choice;
+    "defaultValue": "0",                          -> must be a value listed in 'entryValues';
+    "entries": ["sent", "received"],              -> names to the possible values listed in 'entryValues',
+                                                     must have the same size as 'entryValues';
+    "entryValues": ["0", "1"],                    -> list of possible values this preference can take;
+    "scope": "plugin"                             -> if a preference value may be changed while the
+                                                     functionality is being used, this functionality name
+                                                     must be listed in the scope.
+}
+
+A List preference must have a list of possible values to be used.
+and a list of 'names' for these values. For example:
+    If you have two values: '0' and '1', these values may not be understandable
+    by the user.
+    The names 'sent' and 'received' set at 'entries' will be shown in the UI and
+    are more intuitive!
diff --git a/SDK/Docs/preferencePath.txt b/SDK/Docs/preferencePath.txt
new file mode 100644
index 0000000..f9f78c4
--- /dev/null
+++ b/SDK/Docs/preferencePath.txt
@@ -0,0 +1,20 @@
+
+Create (or Delete) a preference: PREFERENCE (-del)
+
+Path preference formation example:
+{
+    "category" : "Backgrounds",                          -> user choice;
+    "type": "Path",                                      -> fixed as Path;
+    "mimeType": "image/png",                             -> user choice;
+    "key": "background",                                 -> user choice, cannot repeate other keys inside
+                                                            same preferences.json file;
+    "title": "Background image",                         -> user choice;
+    "summary": "Select the image background to use",     -> user choice;
+    "defaultValue": "data/backgrounds/background2.png",  -> must be a relative path within the plugin
+                                                            developpment directory. More specifically any
+                                                            file introduced by the developper must be inside
+                                                            <jami-plugins>/<PLUGINNAME>/data directory;
+    "scope": "plugin, foo"                               -> if a preference value may be changed while the
+                                                            functionality is being used, this functionality
+                                                            name must be listed in the scope;
+}
diff --git a/SDK/Templates/CMakeLists.txt b/SDK/Templates/CMakeLists.txt
new file mode 100644
index 0000000..882dafc
--- /dev/null
+++ b/SDK/Templates/CMakeLists.txt
@@ -0,0 +1,81 @@
+cmake_minimum_required(VERSION 3.10)
+
+# set the project name
+set (ProjectName PLUGINNAME)
+set (Version MANIFESTVERSION)
+
+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 ---CPPFILENAME
+               ---../lib/accel.cppFFMPEGCPP
+               ---)
+
+set(plugin_HDR ---HFILENAME
+               ---../lib/accel.hFFMPEGH
+               ../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---
+                                                 )
+target_link_directories(${ProjectName} PUBLIC ${CONTRIB_PATH}
+                                        ---${FFMPEG}/bin---
+                                        )
+
+target_link_libraries(${ProjectName} PUBLIC ---swscale avutil---)
+
+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/SDK/Templates/build.sh b/SDK/Templates/build.sh
new file mode 100644
index 0000000..2452de4
--- /dev/null
+++ b/SDK/Templates/build.sh
@@ -0,0 +1,205 @@
+#! /bin/bash
+# Build the plugin for the project
+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="PLUGINNAME"
+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"${PLUGINS_LIB}" \
+    ---FFMPEGCPP./../lib/accel.cpp \
+    ---CPPFILENAME \
+    ----L"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/lib/" \
+    ----l:libswscale.a \
+    -l:libavutil.a \
+    -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"${PLUGINS_LIB}" \
+        ---FFMPEGCPP./../lib/accel.cpp \
+        ---CPPFILENAME \
+        ----L"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/lib/" \
+        ----lswscale \
+        -lavutil \
+        ----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/SDK/Templates/copyright.txt b/SDK/Templates/copyright.txt
new file mode 100644
index 0000000..cf00a19
--- /dev/null
+++ b/SDK/Templates/copyright.txt
@@ -0,0 +1,19 @@
+/**
+ *  YEAR
+ *
+ *  Author: AUTHORNAME <AUTHORMAIL>
+ *
+ *  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.
+ */
\ No newline at end of file
diff --git a/SDK/Templates/defaultDependenciesStrings.json b/SDK/Templates/defaultDependenciesStrings.json
new file mode 100644
index 0000000..3488cb8
--- /dev/null
+++ b/SDK/Templates/defaultDependenciesStrings.json
@@ -0,0 +1,10 @@
+{
+    "package.json":
+    {
+        "MediaHandler": {
+            "deps": [
+                "ffmpeg"
+            ]
+        }
+    }
+}
\ No newline at end of file
diff --git a/SDK/Templates/genericConversationHandler.h b/SDK/Templates/genericConversationHandler.h
new file mode 100644
index 0000000..1906ce6
--- /dev/null
+++ b/SDK/Templates/genericConversationHandler.h
@@ -0,0 +1,23 @@
+HEADER
+
+#pragma once
+//Project
+#include "chatsubscriber.h"
+//Jami plugin
+#include "plugin/jamiplugin.h"
+#include "plugin/conversationhandler.h"
+
+class GENERICConversationHandler : public jami::ConversationHandler
+{
+public:
+    GENERICConversationHandler(const JAMI_PluginAPI * api, std::string &&dataPath);
+    ~GENERICConversationHandler();
+    void detach();
+    virtual void notifyStrMapSubject(const bool direction,
+                                     jami::strMapSubjectPtr subject) override;
+    const std::string& dataPath() const { return dataPath_; }
+
+private:
+    std::string dataPath_;
+    std::shared_ptr<ChatSubscriber> css;
+};
diff --git a/SDK/Templates/genericMediaHandler.cpp b/SDK/Templates/genericMediaHandler.cpp
new file mode 100644
index 0000000..f3bbb39
--- /dev/null
+++ b/SDK/Templates/genericMediaHandler.cpp
@@ -0,0 +1,84 @@
+HEADER
+
+#include "GENERICMediaHandler.h"
+// Logger
+#include "pluglog.h"
+const char sep = separator();
+const std::string TAG = "GENERIC";
+
+#define NAME "GENERIC"
+
+namespace jami {
+
+GENERICMediaHandler::GENERICMediaHandler(std::map<std::string, std::string>&& ppm,
+                                         std::string&& datapath)
+    : datapath_ {datapath}
+    , ppm_ {ppm}
+{
+    setId(datapath_);
+    mVS = std::make_shared<GENERICVideoSubscriber>(datapath_);
+}
+
+void
+GENERICMediaHandler::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
+    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>
+GENERICMediaHandler::getCallMediaHandlerDetails()
+{
+    return {{"name", NAME}, {"iconPath", datapath_ + sep + "icon.png"}, {"pluginId", id()}};
+}
+
+void
+GENERICMediaHandler::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 == "PREFERENCE1") {
+            // use preference
+            return;
+        }----------------
+    }
+}
+
+bool
+GENERICMediaHandler::preferenceMapHasKey(const std::string& key)
+{----------------
+    if (key == "PREFERENCE2") { return true; }----------------
+    return false;
+}
+
+void
+GENERICMediaHandler::detach()
+{
+    mVS->detach();
+}
+
+GENERICMediaHandler::~GENERICMediaHandler()
+{
+    std::ostringstream oss;
+    oss << " ~GENERICMediaHandler from PLUGINNAME Plugin" << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+    detach();
+}
+} // namespace jami
diff --git a/SDK/Templates/genericMediaHandler.h b/SDK/Templates/genericMediaHandler.h
new file mode 100644
index 0000000..6b3a70d
--- /dev/null
+++ b/SDK/Templates/genericMediaHandler.h
@@ -0,0 +1,37 @@
+HEADER
+
+#pragma once
+
+// Project
+#include "GENERICVideoSubscriber.h"
+
+// Plugin
+#include "plugin/jamiplugin.h"
+#include "plugin/mediahandler.h"
+
+using avSubjectPtr = std::weak_ptr<jami::Observable<AVFrame*>>;
+
+namespace jami {
+
+class GENERICMediaHandler : public jami::CallMediaHandler
+{
+public:
+    GENERICMediaHandler(std::map<std::string, std::string>&& ppm, std::string&& dataPath);
+    ~GENERICMediaHandler();
+
+    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<GENERICVideoSubscriber> 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/SDK/Templates/genericVideoSubscriber.cpp b/SDK/Templates/genericVideoSubscriber.cpp
new file mode 100644
index 0000000..13398cb
--- /dev/null
+++ b/SDK/Templates/genericVideoSubscriber.cpp
@@ -0,0 +1,119 @@
+HEADER
+
+#include "GENERICVideoSubscriber.h"
+
+extern "C" {
+#include <libavutil/display.h>
+}
+#include <accel.h>
+
+// LOGGING
+#include <pluglog.h>
+
+const std::string TAG = "GENERIC";
+const char sep = separator();
+
+namespace jami {
+
+GENERICVideoSubscriber::GENERICVideoSubscriber(const std::string& dataPath)
+    : path_ {dataPath}
+{}
+
+GENERICVideoSubscriber::~GENERICVideoSubscriber()
+{
+    std::ostringstream oss;
+    oss << "~GENERICMediaProcessor" << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+}
+
+void
+GENERICVideoSubscriber::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);
+
+    // transferToMainMemory USED TO COPY FRAME TO MAIN MEMORY IF HW ACCEL IS ENABLED
+    // NOT NEEDED TO BE USED IF ALL YOUR PROCESS WILL BE DONE WITHIN A
+    // HW ACCEL PLATFORM
+
+    if (firstRun) {
+        // IMPLEMENT CODE TO CONFIGURE
+        // VARIABLES UPON FIRST RUN
+        firstRun = false;
+    }
+
+    // IMPLEMENT PROCESS
+
+    //======================================================================================
+    // 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,
+                         frameData, // PUT HERE YOUR PROCESSED FRAME VARIABLE 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
+GENERICVideoSubscriber::attached(jami::Observable<AVFrame*>* observable)
+{
+    std::ostringstream oss;
+    oss << "::Attached ! " << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+    observable_ = observable;
+}
+
+void
+GENERICVideoSubscriber::detached(jami::Observable<AVFrame*>*)
+{
+    firstRun = true;
+    observable_ = nullptr;
+    std::ostringstream oss;
+    oss << "::Detached()" << std::endl;
+    Plog::log(Plog::LogPriority::INFO, TAG, oss.str());
+}
+
+void
+GENERICVideoSubscriber::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/SDK/Templates/genericVideoSubscriber.h b/SDK/Templates/genericVideoSubscriber.h
new file mode 100644
index 0000000..eaa6649
--- /dev/null
+++ b/SDK/Templates/genericVideoSubscriber.h
@@ -0,0 +1,39 @@
+HEADER
+
+#pragma once
+
+// AvFrame
+extern "C" {
+#include <libavutil/frame.h>
+}
+#include <observer.h>
+
+// Frame Scaler
+#include <framescaler.h>
+
+namespace jami {
+
+class GENERICVideoSubscriber : public jami::Observer<AVFrame*>
+{
+public:
+    GENERICVideoSubscriber(const std::string& dataPath);
+    ~GENERICVideoSubscriber();
+
+    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();
+
+private:
+    // Observer pattern
+    Observable<AVFrame*>* observable_ = nullptr;
+
+    // Data
+    std::string path_;
+    FrameScaler scaler;
+
+    // Status variables of the processing
+    bool firstRun {true};
+};
+} // namespace jami
diff --git a/SDK/Templates/icon.png b/SDK/Templates/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/SDK/Templates/main.cpp b/SDK/Templates/main.cpp
new file mode 100644
index 0000000..e97416a
--- /dev/null
+++ b/SDK/Templates/main.cpp
@@ -0,0 +1,48 @@
+HEADER
+#include <iostream>
+#include <string.h>
+#include <thread>
+#include <memory>
+#include <plugin/jamiplugin.h>
+----------------
+#include "INCLUDESAPI.h"----------------
+
+#ifdef WIN32
+#define EXPORT_PLUGIN __declspec(dllexport)
+#else
+#define EXPORT_PLUGIN
+#endif
+#define PLUGINNAME_VERSION_MAJOR PLUGINVERSIONMAJOR
+#define PLUGINNAME_VERSION_MINOR PLUGINVERSIONMINOR
+#define PLUGINNAME_VERSION_PATCH PLUGINVERSIONPATCH
+extern "C" {
+
+void
+pluginExit(void)
+{}
+
+EXPORT_PLUGIN JAMI_PluginExitFunc
+JAMI_dynPluginInit(const JAMI_PluginAPI* api)
+{
+    std::cout << "**************************" << std::endl << std::endl;
+    std::cout << "**  PLUGINNAME  **" << std::endl;
+    std::cout << "**************************" << std::endl << std::endl;
+    std::cout << " Version " << PLUGINNAME_VERSION_MAJOR << "." << PLUGINNAME_VERSION_MINOR << "."
+              << PLUGINNAME_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 fmpPLUGINAPI = std::make_unique<jami::PLUGINAPI>(std::move(ppm), std::move(dataPath));
+        if (api->manageComponent(api, "APIMANAGER", fmpPLUGINAPI.release())) {
+            return nullptr;
+        }
+----------------
+    }
+    return pluginExit;
+}
+}
diff --git a/SDK/Templates/manifest.json b/SDK/Templates/manifest.json
new file mode 100644
index 0000000..d5c8df6
--- /dev/null
+++ b/SDK/Templates/manifest.json
@@ -0,0 +1,5 @@
+{
+    "name": "pluginName",
+    "description": "plugin description",
+    "version": "version"
+}
\ No newline at end of file
diff --git a/SDK/Templates/package.json b/SDK/Templates/package.json
new file mode 100644
index 0000000..0bb6d39
--- /dev/null
+++ b/SDK/Templates/package.json
@@ -0,0 +1,16 @@
+{
+    "name": "",
+    "version": "",
+    "extractLibs": false,
+    "deps": [],
+    "defines": [],
+    "custom_scripts": {
+        "pre_build": [
+            "mkdir msvc"
+        ],
+        "build": [
+            "cmake --build ./msvc --config Release"
+        ],
+        "post_build": []
+    }
+}
diff --git a/SDK/Templates/preferences.json b/SDK/Templates/preferences.json
new file mode 100644
index 0000000..4caacdc
--- /dev/null
+++ b/SDK/Templates/preferences.json
@@ -0,0 +1,23 @@
+[
+    {
+        "category" : "genericList",
+        "type": "List",
+        "key": "keyName",
+        "title": "preference title",
+        "summary": "preference summary",
+        "defaultValue": "default value",
+        "scope": "plugin, Name of API implementation",
+        "entryValues": ["List of", "variables", "true", "values"],
+        "entries": ["List of", "UI", "variables", "names"]
+    },
+    {
+        "category" : "genericPath",
+        "type": "Path",
+        "key": "keyName",
+        "title": "preference title",
+        "summary": "preference summary",
+        "defaultValue": "default values",
+        "scope": "plugin, Name of API implementation",
+        "mimeType": "*/*"
+    }
+]
diff --git a/SDK/generateProject.py b/SDK/generateProject.py
new file mode 100644
index 0000000..588f62d
--- /dev/null
+++ b/SDK/generateProject.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+from utils import *
+
+
+def runoption(action):
+    if(initializeClasses(action)):
+        if (action == ACTIONS["CREATE"]):
+            runFullCreation()
+        elif (action == ACTIONS["CREATE_FUNCTIONALITY"]):
+            modifyFunctionality(action)
+        elif (action == ACTIONS["CREATE_MAIN"]):
+            getSkeletonSourceFiles().createMain(addAllowed='n')
+        elif (action == ACTIONS["CREATE_PREFERENCES"] or action == ACTIONS["DELETE_PREFERENCES"]):
+            modifyPreferences(action)
+        elif (action == ACTIONS["CREATE/MODIFY_MANIFEST"] or action == ACTIONS["DELETE_MANIFEST"]):
+            getSkeletonSourceFiles().modifyManifest(action)
+        elif (action == ACTIONS["CREATE_PACKAGE_JSON"]):
+            getSkeletonSourceFiles().createPackageJson()
+        elif (action == ACTIONS["DELETE"]):
+            delete()
+        elif (action == ACTIONS["PREASSEMBLE"]):
+            from jplManipulation import preAssemble
+            preAssemble(getSkeletonSourceFiles().pluginName)
+        elif (action == ACTIONS["BUILD"]):
+            from jplManipulation import build
+            build(getSkeletonSourceFiles().pluginName)
+        elif (action == ACTIONS["ASSEMBLE"]):
+            from jplManipulation import assemble
+            assemble(getSkeletonSourceFiles().pluginName)
+        elif (action == ACTIONS["CREATE_BUILD_FILES"]):
+            getSkeletonSourceFiles().createBuildFiles()
+
+
+def delete():
+    getSkeletonSourceFiles().delete()
+
+
+def modifyPreferences(action):
+    if (action == ACTIONS["DELETE_PREFERENCES"]):
+        getSkeletonSourceFiles().deletePreference()
+    elif (action == ACTIONS["CREATE_PREFERENCES"]):
+        getSkeletonSourceFiles().createPreferences(getSkeletonSourceFiles().names)
+
+
+def runFullCreation():
+    getSkeletonSourceFiles().createManifest()
+    getSkeletonSourceFiles().createMain()
+    getSkeletonSourceFiles().createPackageJson()
+    getSkeletonSourceFiles().createBuildFiles()
+
+
+def modifyFunctionality(action):
+    if (action == ACTIONS["CREATE_FUNCTIONALITY"]):
+        getSkeletonSourceFiles().createFunctionality()
diff --git a/SDK/jplManipulation.py b/SDK/jplManipulation.py
new file mode 100644
index 0000000..558760a
--- /dev/null
+++ b/SDK/jplManipulation.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+import os
+import sys
+import shutil
+import argparse
+import platform
+import subprocess
+from zipfile import ZipFile
+
+from sdkConstants import OS_IDS
+
+
+def getSystem():
+    system = platform.system().lower()
+    if system == "linux" or system == "linux2":
+        return OS_IDS["Linux"]
+    elif system == "darwin":
+        sys.exit("Plugins not supported on MacOS and IOS")
+    elif system == "windows":
+        return OS_IDS["Windows"]
+    sys.exit("Plugins SDK not supported on this system")
+
+
+def getJpls():
+    filePaths = input("\nInput jpls path you want to merge:\n")
+    filePaths = filePaths.replace(' ', ',').replace(",,", ',')
+    filePaths = filePaths.split(',')
+    if (len(filePaths) > 0):
+        print("\nJpl files to merge:")
+    for filePath in filePaths:
+        print(filePath)
+    return filePaths
+
+
+def checkValidityJpls(filePaths):
+    for filePath in filePaths:
+        if (not os.path.exists(filePath) or not filePath.endswith(".jpl")):
+            return False
+    return True
+
+
+class JPLStructure:
+    def __init__(self, paths):
+        self.paths = paths
+        self.outputName = ''
+        self.pluginNames = []
+        self.ZipObjcts = {}
+        self.getOutName()
+        self.discoverJPLs()
+        self.mergeJPLs()
+
+    def getOutName(self):
+        while (not self.outputName or not self.outputName.endswith(".jpl")):
+            self.outputName = input("\nWhere save the resulting JPL? ")
+        self.OutObj = ZipFile(self.outputName, 'w')
+
+    def discoverJPLs(self):
+        for path in self.paths:
+            name = os.path.split(path)[0].split('.')[0]
+            self.pluginNames.append(name)
+            self.ZipObjcts[path] = []
+            self.ZipObjcts[path].append(ZipFile(path, 'r'))
+        return
+
+    def mergeJPLs(self):
+        self.fileNames = []
+        for path in self.paths:
+            [obj] = self.ZipObjcts[path]
+            for item in obj.filelist:
+                if item.filename not in self.fileNames:
+                    self.OutObj.writestr(item, obj.open(item).read())
+                    self.fileNames.append(item.filename)
+        self.OutObj.close()
+
+
+def onerror(func, path, exc_info):
+    """
+    Error handler for ``shutil.rmtree``.
+
+    If the error is due to an access error (read only file)
+    it attempts to add write permission and then retries.
+
+    If the error is for another reason it re-raises the error.
+
+    Usage : ``shutil.rmtree(path, onerror=onerror)``
+    """
+    import stat
+    if not os.access(path, os.W_OK):
+        # Is the error an access error ?
+        os.chmod(path, stat.S_IWUSR)
+        func(path)
+    else:
+        raise
+
+
+def preAssemble(pluginName, distribution=''):
+    localSystem = getSystem()
+
+    osBuildPath = "build-local"
+    if localSystem == OS_IDS["Linux"]:
+        if (distribution != 'android'):
+            distribution = "x86_64-linux-gnu"
+    elif localSystem == OS_IDS["Darwin"]:
+        sys.exit("Plugins not supported on MacOS and IOS")
+    elif localSystem == OS_IDS["Windows"]:
+        distribution = "x64-windows"
+        osBuildPath = "msvc"
+
+    chDir = False
+    if (osBuildPath in os.getcwd()):
+        chDir = True
+        os.chdir("./../")
+    if (os.path.exists(f"./../{pluginName}/{osBuildPath}/")):
+        if (os.path.exists(f"./../{pluginName}/{osBuildPath}/jpl")):
+            shutil.rmtree(f"./../{pluginName}/{osBuildPath}/jpl", onerror=onerror)
+    else:
+        os.mkdir(f"./../{pluginName}/{osBuildPath}")
+    os.mkdir(f"./../{pluginName}/{osBuildPath}/jpl")
+    os.mkdir(f"./../{pluginName}/{osBuildPath}/jpl/lib")
+    if (distribution != 'android'):
+        os.mkdir(f"./../{pluginName}/{osBuildPath}/jpl/lib/{distribution}")
+    else:
+        if ("ANDROID_ABI" in os.environ.keys()):
+            for abi in os.environ["ANDROID_ABI"].split(' '):
+                os.mkdir(f"./../{pluginName}/{osBuildPath}/jpl/lib/{abi}")
+
+    shutil.copytree(f"./../{pluginName}/data/",
+                    f"./../{pluginName}/{osBuildPath}/jpl/data/")
+    shutil.copyfile(f"./../{pluginName}/manifest.json",
+                    f"./../{pluginName}/{osBuildPath}/jpl/manifest.json")
+    if (os.path.exists(f"./../{pluginName}/data/preferences.json")):
+        shutil.copyfile(
+            f"./../{pluginName}/data/preferences.json",
+            f"./../{pluginName}/{osBuildPath}/jpl/data/preferences.json")
+    if (chDir):
+        os.chdir(f"./{osBuildPath}")
+
+
+def assemble(pluginName, extraPath='', distribution=''):
+    extraPath = '/' + extraPath
+    localSystem = getSystem()
+    root = os.path.dirname(os.path.abspath(__file__)) + "/.."
+    osBuildPath = "build-local"
+
+    if localSystem == OS_IDS["Linux"]:
+        if (distribution != 'android'):
+            distribution = "x86_64-linux-gnu"
+    elif localSystem == OS_IDS["Darwin"]:
+        sys.exit("Plugins not supported on MacOS and IOS")
+    elif localSystem == OS_IDS["Windows"]:
+        distribution = "x64-windows"
+        osBuildPath = 'msvc'
+    if (not os.path.exists(f"{root}/build")):
+        os.mkdir(f"{root}/build")
+    if (not os.path.exists(f"{root}/build/{distribution}")):
+        os.mkdir(f"{root}/build/{distribution}")
+    if (not os.path.exists(f"{root}/build/{distribution}{extraPath}")):
+        os.mkdir(f"{root}/build/{distribution}{extraPath}")
+    if (os.path.exists(f"./../build/{pluginName}.jpl")):
+        os.remove(f"./../build/{pluginName}.jpl")
+
+    outputJPL = f"{root}/build/{distribution}{extraPath}/{pluginName}.jpl"
+    outputBuild = f"{root}/{pluginName}/{osBuildPath}/jpl"
+
+    with ZipFile(outputJPL, 'w') as zipObj:
+        for folderName, subfolders, filenames in os.walk(outputBuild):
+            for filename in filenames:
+                filePath = os.path.join(folderName, filename)
+                zipObj.write(
+                    filePath, f"{folderName.split('/jpl')[-1]}/{filename}")
+        zipObj.close()
+
+
+def build(pluginName):
+    currentDir = os.getcwd()
+    os.chdir('./../')
+    subprocess.run([
+        sys.executable, os.path.join(
+            os.getcwd(), "build-plugin.py"),
+        "--projects", pluginName
+    ], check=True)
+    os.chdir(currentDir)
+
+
+def parser():
+    parser = argparse.ArgumentParser(description='Build some plugins.')
+    parser.add_argument('--plugin', type=str,
+                        help='Name of plugin to be build')
+    parser.add_argument('--extraPath', type=str, default="",
+                        help='output intermediate Path')
+    parser.add_argument('--distribution', type=str, default='',
+                        help="GNU/Linux or Windows, leave empty. Android, type android")
+    # to build or not to build
+    parser.add_argument('--build', action='store_true')
+    parser.add_argument(
+        '--preassemble',
+        action='store_true')  # to preassemble or not
+    # to assemble jpl or not
+    parser.add_argument('--assemble', action='store_true')
+
+    args = parser.parse_args()
+    return args
+
+
+def main():
+    args = parser()
+
+    if args.preassemble:
+        preAssemble(args.plugin, args.distribution)
+    if (args.build):
+        build(args.plugin)
+    if (args.assemble):
+        assemble(args.plugin, args.extraPath, args.distribution)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/SDK/manifestProfile.py b/SDK/manifestProfile.py
new file mode 100644
index 0000000..ba59d7c
--- /dev/null
+++ b/SDK/manifestProfile.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+import os
+import json
+from termcolor import colored, cprint
+
+from sdkConstants import *
+from pluginStructureProfile import PluginStructure
+
+
+class Manifest(PluginStructure):
+    def __init__(self, action):
+        PluginStructure.__init__(self, action)
+        self.description = ''
+        self.version = '0'
+        self.manifest = {}
+        with open("./Templates/manifest.json") as f:
+            self.manifestSkeleton = f.read()
+            f.close()
+        if (self.isManifest()):
+            with open(self.manifestFile) as f:
+                self.manifest = json.load(f)
+                f.close()
+            self.pluginName = self.manifest["name"]
+            self.description = self.manifest["description"]
+            self.version = self.manifest["version"]
+
+    def checkVersionFormat(self, version):
+        return False if (len(version.split('.')) != 3) else True
+
+    def createManifest(self):
+        print(
+            f"\nDefining a manifest for \"{self.pluginName}\" plugin, we gonna need more information..")
+        print(colored("\nPress 'q' to quit this option.", "yellow"))
+
+        pluginDescription = input("\nTell us a description: ")
+        if (pluginDescription == 'q'):
+            return False
+        self.description = pluginDescription
+        pluginVersion = '0'
+        while (not self.checkVersionFormat(
+                pluginVersion) and pluginVersion != ""):
+            print(colored("\nVersion must be of the form X.Y.Z", "yellow"))
+            pluginVersion = input("Set plugin version (default 0.0.0): ")
+            if (pluginVersion == 'q'):
+                return False
+            if (not pluginVersion):
+                pluginVersion = '0.0.0'
+        self.version = pluginVersion
+        self.manifest["name"] = self.pluginName
+        self.manifest["description"] = self.description
+        self.manifest["version"] = self.version
+
+        self.saveManifest(self.manifest)
+        return True
+
+    def modifyManifestInternal(self, action):
+        self.callUpdateVersion = False
+        if (action == ACTIONS["DELETE_MANIFEST"]):
+            self.deleteManifest()
+        elif (self.isManifest()):
+            self.manifest["name"] = self.pluginName
+            pluginDescription = input(
+                "New description for your plugin (ignore to keep previous description): ")
+            pluginVersion = input(
+                "New plugin version (ignore to keep previous version): ")
+            while (not self.checkVersionFormat(
+                    pluginVersion) and pluginVersion != ""):
+                print("Version must be of the form X.Y.Z")
+                pluginVersion = input(
+                    "New plugin version (ignore to keep previous version): ")
+            if (pluginDescription != ""):
+                self.description = pluginDescription
+            self.manifest["description"] = self.description
+            if (pluginVersion !=
+                    "" and self.manifest["version"] != pluginVersion):
+                self.version = pluginVersion
+                self.manifest["version"] = self.version
+                self.callUpdateVersion = True
+            self.saveManifest(self.manifest)
+        else:
+            self.createManifest()
+        return self.callUpdateVersion
diff --git a/SDK/pluginMainSDK.py b/SDK/pluginMainSDK.py
new file mode 100644
index 0000000..1e73c98
--- /dev/null
+++ b/SDK/pluginMainSDK.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+import cmd
+import sys
+from termcolor import colored, cprint
+
+from generateProject import *
+from jplManipulation import *
+
+os.chdir("./SDK")
+
+
+def mainRun(action):
+    runoption(action)
+
+
+class JamiPluginsSDKShell(cmd.Cmd):
+    intro = colored(
+        "\nWelcome to the Jami Plugins SDK shell.    Type help or ? to list commands.\n",
+        "green")
+    prompt = colored('(Jami Plugins SDK) ', "yellow")
+    file = None
+
+    # ----- basic Jami Plugins SDK commands -----
+    # ---- BUILD ----
+    def help_build(self):
+        helperFile = './Docs/buildHelper.txt'
+        with open(helperFile) as f:
+            helperText = f.read()
+            f.close()
+        print(helperText)
+
+    def do_build(self, arg):
+        if (arg == '-def'):
+            runoption(ACTIONS["CREATE_BUILD_FILES"])
+        else:
+            runoption(ACTIONS["BUILD"])
+
+    # ---- JPL MANIPULATION ----
+    def help_merge(self):
+        helperFile = './Docs/jplMergeHelper.txt'
+        with open(helperFile) as f:
+            helperText = f.read()
+            f.close()
+        print(helperText)
+
+    def do_merge(self, arg):
+        filePaths = getJpls()
+        if (len(filePaths) < 2 or not checkValidityJpls(filePaths)):
+            print(
+                colored(
+                    "Files are not valid.\nYou must provide two or more valid jpls to merge\n.",
+                    "red"))
+            return
+        JPLStructure(filePaths)
+        return
+
+    def help_assemble(self):
+        helperFile = './Docs/jplAssembleHelper.txt'
+        with open(helperFile) as f:
+            helperText = f.read()
+            f.close()
+        print(helperText)
+
+    def do_assemble(self, arg):
+        if (arg == '-pre'):
+            runoption(ACTIONS["PREASSEMBLE"])
+        else:
+            runoption(ACTIONS["ASSEMBLE"])
+        return
+
+    # ---- FULL PIPELINE ----
+    def help_plugin(self):
+        helperFile = './Docs/pipelineHelper.txt'
+        with open(helperFile) as f:
+            helperText = f.read()
+            f.close()
+        print(helperText)
+
+    def do_plugin(self, arg):
+        mainRun(ACTIONS["CREATE"])
+
+    # ---- MAIN ----
+    def help_main(self):
+        helperFile = './Docs/mainHelper.txt'
+        with open(helperFile) as f:
+            helperText = f.read()
+            f.close()
+        print(helperText)
+
+    def do_main(self, args):
+        mainRun(ACTIONS["CREATE_MAIN"])
+
+    # ---- FUNCTIONALITY ----
+    def help_functionality(self):
+        helperFile = './Docs/functionalityHelper.txt'
+        with open(helperFile) as f:
+            helperText = f.read()
+            f.close()
+        print(helperText)
+
+    def do_functionality(self, args):
+        mainRun(ACTIONS["CREATE_FUNCTIONALITY"])
+
+    # ---- PACKAGE ----
+    def help_package(self):
+        helperFile = './Docs/packageHelper.txt'
+        with open(helperFile) as f:
+            helperText = f.read()
+            f.close()
+        print(helperText)
+
+    def do_package(self, args):
+        mainRun(ACTIONS["CREATE_PACKAGE_JSON"])
+
+    # ---- PREFERENCES ----
+    def help_preference(self, args=''):
+        helperFile = "./Docs/preferenceHelper.txt"
+        if (PREFERENCES_TYPES[0].lower() in args):  # List - 0
+            helperFile = "./Docs/preferenceList.txt"
+        elif (PREFERENCES_TYPES[1].lower() in args):  # Path - 1
+            helperFile = "./Docs/preferencePath.txt"
+
+        with open(helperFile) as f:
+            helpText = f.read()
+            f.close()
+        print(helpText)
+
+    def do_preference(self, arg):
+        if ("-h" in arg):
+            self.help_preference(arg.replace("-h", ""))
+        elif ("-del" in arg):
+            mainRun(ACTIONS["DELETE_PREFERENCES"])
+        else:
+            mainRun(ACTIONS["CREATE_PREFERENCES"])
+
+    # ---- MANIFEST ----
+    def help_manifest(self):
+        helperFile = './Docs/manifestHelper.txt'
+        with open(helperFile) as f:
+            helperText = f.read()
+            f.close()
+        print(helperText)
+
+    def do_manifest(self, arg):
+        if (arg == "-del"):
+            mainRun(ACTIONS["DELETE_MANIFEST"])
+        else:
+            mainRun(ACTIONS["CREATE/MODIFY_MANIFEST"])
+
+    # ---- PLUGIN DELETION ----
+    def do_delete(self, arg):
+        'Choose a plugin to delete:  DELETE'
+        print("\nYou can not delete these plugins/folders: ")
+        print(colored(FORBIDDEN_NAMES[:-1], "yellow"))
+        mainRun(ACTIONS["DELETE"])
+
+    # ---- EXIT ----
+    def do_exit(self, arg):
+        'Stop Jami Plugins SDK, and exit:  EXIT'
+        print('Thank you for using Jami Plugins SDK')
+        return True
+
+    # ----- clear shell -----
+    def do_clear(self, arg):
+        'Clear your prompt: CLEAR'
+        os.system("clear")
+        print(self.intro)
+
+    def precmd(self, line):
+        line = line.lower()
+        if line == 'eof':
+            return ('exit')
+        return line
+
+
+if __name__ == '__main__':
+    os.system('clear')
+    JamiPluginsSDKShell().cmdloop()
diff --git a/SDK/pluginStructureProfile.py b/SDK/pluginStructureProfile.py
new file mode 100644
index 0000000..d4b5c43
--- /dev/null
+++ b/SDK/pluginStructureProfile.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+from userProfile import UserInfos
+from sdkConstants import *
+import os
+import json
+import shutil
+from termcolor import colored, cprint
+
+
+class PluginStructure(UserInfos):
+    def __init__(self, action):
+        UserInfos.__init__(self, action)
+        self.create(action)
+        self.createFolderStructure()
+
+    def delete(self):
+        if (os.path.exists(self.pluginDirectory)
+                and os.path.isdir(self.pluginDirectory)):
+            shutil.rmtree(self.pluginDirectory)
+
+    def reInitialize(self, pluginName):
+        self.pluginName = pluginName
+        self.pluginDirectory = f"./../{self.pluginName}"
+        self.manifestFile = f"{self.pluginDirectory}/manifest.json"
+        self.pluginDataDirectory = f"{self.pluginDirectory}/data"
+        self.pluginIcon = f"{self.pluginDataDirectory}/icon.png"
+        self.preferencesFile = f"{self.pluginDataDirectory}/preferences.json"
+        self.packageFile = f"{self.pluginDirectory}/package.json"
+        self.cmakelistsFile = f"{self.pluginDirectory}/CMakeLists.txt"
+        self.buildFile = f"{self.pluginDirectory}/build.sh"
+
+    def create(self, action):
+        plugins = os.listdir("../")
+        print(colored("\nLeave Empty or Press 'q' to quit this option.", "yellow"))
+        if (action == ACTIONS["CREATE"]):
+            print("\nNow, you need to tell how you want yout plugin to be called.")
+            pluginName = "SDK"
+            while (pluginName in plugins):
+                pluginName = input("\nChoose a cool name for your plugin: ")
+                if (pluginName == 'q' or not pluginName):
+                    self.reInitialize('')
+                    break
+                if (pluginName in plugins):
+                    print(colored("This name is invalid!", "red"))
+                else:
+                    self.reInitialize(pattern.sub('', pluginName))
+            if (self.pluginName):
+                coloredTitle = colored(self.pluginName, "green")
+                print(f"\nNice! Your {coloredTitle} will be awesome!")
+
+        elif (action == ACTIONS["DELETE"] or action == ACTIONS["DELETE_PREFERENCES"] \
+                or action == ACTIONS["DELETE_MANIFEST"]):
+            pluginName = ""
+            while (pluginName not in plugins or pluginName in FORBIDDEN_NAMES):
+                pluginName = input("\nPlugin to be modified: ")
+                if (pluginName == 'q' or not pluginName):
+                    self.reInitialize('')
+                    break
+                if (pluginName not in plugins or pluginName in FORBIDDEN_NAMES):
+                    print(colored("This name is invalid!", "red"))
+                else:
+                    self.reInitialize(pattern.sub('', pluginName))
+
+        elif (action == ACTIONS["CREATE/MODIFY_MANIFEST"] or action == ACTIONS["CREATE_PREFERENCES"] \
+                or action == ACTIONS["CREATE_FUNCTIONALITY"] or action == ACTIONS["CREATE_PACKAGE_JSON"] \
+                or action == ACTIONS["CREATE_MAIN"] or action == ACTIONS["CREATE_BUILD_FILES"] \
+                or action == ACTIONS["ASSEMBLE"] or action == ACTIONS["PREASSEMBLE"] \
+                or action == ACTIONS["BUILD"]):
+            pluginName = ""
+            while (pluginName in FORBIDDEN_NAMES):
+                pluginName = input("\nPlugin to be modified or created: ")
+                if (pluginName == 'q' or not pluginName):
+                    self.reInitialize('')
+                    break
+                if (pluginName in FORBIDDEN_NAMES):
+                    print(colored("This name is invalid!", "red"))
+                else:
+                    self.reInitialize(pattern.sub('', pluginName))
+
+    def createFolderStructure(self):
+        if (self.pluginName):
+            if (not os.path.exists(self.pluginDirectory)):
+                os.mkdir(self.pluginDirectory)
+            if (not os.path.exists(self.pluginDataDirectory)):
+                os.mkdir(self.pluginDataDirectory)
+            if (not os.path.exists(self.pluginIcon)):
+                shutil.copyfile("./Templates/icon.png", self.pluginIcon)
+
+    def saveManifest(self, manifestTxt):
+        with open(self.manifestFile, 'w') as f:
+            json.dump(manifestTxt, f, indent=4)
+            f.close()
+
+    def deleteManifest(self):
+        if (os.path.exists(self.manifestFile)):
+            os.remove(self.manifestFile)
+
+    def isManifest(self):
+        return os.path.exists(self.manifestFile)
+
+    def isMainDirectory(self):
+        return os.path.exists(self.pluginDirectory)
diff --git a/SDK/preferencesProfile.py b/SDK/preferencesProfile.py
new file mode 100644
index 0000000..43a9a9c
--- /dev/null
+++ b/SDK/preferencesProfile.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+import os
+import json
+from termcolor import colored, cprint
+
+from manifestProfile import Manifest
+
+
+class Preferences(Manifest):
+    def __init__(self, action):
+        Manifest.__init__(self, action)
+        self.preferences = []
+        self.preferencesTitles = []
+        self.preferencesKeys = [""]
+        self.preferences_options = {}
+        self.preferences_options_keys = []
+        self.getPreferencesOptions()
+        self.globPreferencesList()
+
+    def getPreferencesOptions(self):
+        with open("./Templates/preferences.json") as f:
+            preferences = json.load(f)
+            for preference in preferences:
+                self.preferences_options_keys.append(preference["type"])
+                self.preferences_options[preference["type"]] = preference
+            f.close()
+
+    def globPreferencesList(self):
+        if os.path.exists(self.preferencesFile):
+            with open(self.preferencesFile) as f:
+                self.preferences = json.load(f)
+                for preference in self.preferences:
+                    self.preferencesTitles.append(preference["title"])
+                    self.preferencesKeys.append(preference["key"])
+                f.close()
+
+    def completeListValues(self, item, entryValues=[]):
+        if (item == "entryValues"):
+            addNew = ''
+            entryValues = []
+            while (addNew != 'n'):
+                addNew = input(
+                    "\nDo you want to add a new entry Value? [y/n] ")
+                if (addNew == 'y'):
+                    newValue = input("Type one new entry: ")
+                    if (newValue):
+                        entryValues.append(newValue)
+            return entryValues
+        elif (item == 'entries'):
+            entries = []
+            addNew = ''
+            for i in range(len(entryValues)):
+                newValue = input(
+                    f"Type an entry name for '{entryValues[i]}': ")
+                entries.append(newValue)
+            return entries
+
+    def completePathValues(self):
+        allExts = ''
+        while(allExts not in ['y', 'n']):
+            allExts = input("If you want to access all kinds of files? [y/n] ")
+            if (allExts not in ['y', 'n']):
+                print(
+                    colored(
+                        "Invalid answer, please type 'y' or 'n' and then press enter",
+                        "red"))
+        if (allExts == 'y'):
+            return "*/*"
+        else:
+            print("Now, tell us the accepted files extensions, one by one!")
+            newExt = ''
+            ext = []
+            while (newExt != 'n'):
+                temp = input(
+                    "Type a file extension (if empty, all files extensions will be considered valid): ")
+                if (temp):
+                    ext.append("*/." + temp.split('.')[-1])
+                    newExt = ''
+                    while (newExt not in ['y', 'n']):
+                        newExt = input(
+                            "Do you want to add another extension? [y/n] ")
+                        if (newExt not in ['y', 'n']):
+                            print(
+                                colored(
+                                    "Invalid answer, please type 'y' or 'n' and then press enter",
+                                    "red"))
+                else:
+                    return "*/*"
+        return ','.join(ext)
+
+    def createPreferencesInternal(self, names, loop=True):
+        self.newPreferences = []
+        addAllowed = ''
+        while (addAllowed != "n" and addAllowed != "N"):
+            addAllowed = input("\nWould you like to add a preference? [y/n] ")
+            if (addAllowed == "y"):
+                print("\nYour preferences options available are: ")
+                options = []
+                for i, key in enumerate(self.preferences_options_keys):
+                    options.append(str(i))
+                    print(f"({i}) {key};")
+                newType = ''
+                while (newType not in options):
+                    newType = input("\nWhich preference type do you choose: ")
+                preferenceSkeleton = self.preferences_options[self.preferences_options_keys[int(
+                    newType)]].copy()
+                for item in preferenceSkeleton:
+                    if (item != "type"):
+                        if (item == "scope"):
+                            scopeStr = ["plugin"]
+                            forbbidenScope = [""]
+                            scopeOptions = []
+                            addScope = 'n'
+                            if (len(names) > 0):
+                                addScope = ''
+                            for j, name in enumerate(names):
+                                scopeOptions.append(str(j))
+                            while (addScope != 'n' and not set(
+                                    scopeOptions).issubset(set(forbbidenScope))):
+                                if (not set(scopeOptions).issubset(
+                                        set(forbbidenScope))):
+                                    addScope = input(
+                                        "\nWould you like to add a scope? [y/n] ")
+                                    if (addScope == "y"):
+                                        newScope = ''
+                                        print(
+                                            "\nPossible values for scope:")
+                                        for j, name in enumerate(names):
+                                            if (str(j) not in forbbidenScope):
+                                                print(f"({j}) {name};")
+                                        while((newScope not in scopeOptions or newScope in forbbidenScope)):
+                                            newScope = input(
+                                                "\nWhich scope do you choose: ")
+                                        forbbidenScope.append(newScope)
+                                        scopeStr.append(
+                                            f"{names[int(newScope)]}")
+                                else:
+                                    print(
+                                        colored(
+                                            "\nAll available scopes have been added to this preference!\ncontinuing ...",
+                                            "yellow"))
+                                    addScope = "n"
+                            preferenceSkeleton[item] = ", ".join(scopeStr)
+                        elif (preferenceSkeleton["type"] == "List" and item in [
+                                "entries", "entryValues"]):
+                            if (item == 'entryValues'):
+                                preferenceSkeleton[item] = self.completeListValues(
+                                    item)
+                            if (item == 'entries'):
+                                preferenceSkeleton[item] = self.completeListValues(
+                                    item, preferenceSkeleton['entryValues'].copy())
+                        elif (preferenceSkeleton["type"] == "Path" and item in ["mimeType"]):
+                            preferenceSkeleton[item] = self.completePathValues(
+                            )
+                        elif (item == "key"):
+                            itemValue = ""
+                            while (itemValue in self.preferencesKeys):
+                                itemValue = input(
+                                    f"Type a value for {item}: ")
+                                if (itemValue in self.preferencesKeys):
+                                    print(
+                                        colored(
+                                            "\nValue not permited",
+                                            "red"))
+                            self.preferencesKeys.append(itemValue)
+                            preferenceSkeleton[item] = itemValue
+                        elif (item == "defaultValue"):
+                            itemValue = ''
+                            while (not itemValue):
+                                itemValue = input(
+                                    f"Type a value for {item}: ")
+                                if (not itemValue):
+                                    print(
+                                        colored(
+                                            "\nValue not permited",
+                                            "red"))
+                            preferenceSkeleton[item] = itemValue
+                        else:
+                            preferenceSkeleton[item] = input(
+                                f"Type a value for {item}: ")
+                self.newPreferences.append(preferenceSkeleton)
+            if (not loop):
+                return
+
+    def createPreferences(self, names):
+        self.createPreferencesInternal(names)
+
+        for p in self.newPreferences:
+            self.preferences.append(p)
+
+        if (len(self.newPreferences) > 0):
+            with open(self.preferencesFile, 'w') as f:
+                json.dump(self.preferences, f, indent=4)
+                f.close()
+        elif (len(self.preferences) == 0):
+            with open(self.preferencesFile, 'w') as f:
+                json.dump(self.preferences, f, indent=4)
+                f.close()
+
+    def deletePreference(self):
+        options = []
+        print("\nCurrently, these are you preferences: ")
+        for i in range(len(self.preferencesTitles)):
+            print(colored(f"({i}) {self.preferencesTitles[i]}", "yellow"))
+            options.append(str(i))
+        prefDel = ''
+        if (len(options) > 0):
+            while (prefDel not in options):
+                prefDel = input(
+                    "\nWich preference do you want to delete? (choose 'q' to quit) ")
+                if (prefDel not in options and prefDel != 'q'):
+                    print(colored("Invalid options!", "red"))
+                if (prefDel == 'q'):
+                    break
+            if (prefDel != 'q'):
+                for preference in self.preferences:
+                    if self.preferencesTitles[int(
+                            prefDel)] == preference["title"]:
+                        self.preferences.remove(preference)
+                        prefDel = preference
+                        break
+                with open(self.preferencesFile, 'w') as f:
+                    json.dump(self.preferences, f, indent=4)
+                    f.close
+        else:
+            input(
+                colored(
+                    "\nThere is no preference to be deleted.\nPress enter to continue...",
+                    "yellow"))
diff --git a/SDK/requirements.txt b/SDK/requirements.txt
new file mode 100644
index 0000000..ee28905
--- /dev/null
+++ b/SDK/requirements.txt
@@ -0,0 +1,2 @@
+termcolor==1.1.0
+numpy
\ No newline at end of file
diff --git a/SDK/sdkConstants.py b/SDK/sdkConstants.py
new file mode 100644
index 0000000..5c62f2c
--- /dev/null
+++ b/SDK/sdkConstants.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+import re
+import string
+pattern = re.compile(r'[\W_]+')
+
+PLUGINS_APIS = {"MEDIA_HANDLER": '1',
+                "CONVERSATION_HANDLER": '2'}
+APIS_NAMES = {"MEDIA_HANDLER": "MediaHandler",
+              "CONVERSATION_HANDLER": "ConversationHandler"}
+
+ACTIONS = {"CREATE": '1',
+           "CREATE_MAIN": '2',
+           "CREATE/MODIFY_MANIFEST": '3',
+           "DELETE_MANIFEST": '4',
+           "CREATE_PREFERENCES": '5',
+           "DELETE_PREFERENCES": '6',
+           "CREATE_FUNCTIONALITY": '7',
+           "CREATE_PACKAGE_JSON": '8',
+           "DELETE": '9',
+           "ASSEMBLE": '10',
+           "BUILD": '11',
+           "PREASSEMBLE": '12',
+           "CREATE_BUILD_FILES": '13'}
+
+FORBIDDEN_NAMES = [
+    "SDK",
+    "lib",
+    "contrib",
+    "build",
+    "docker",
+    '']
+
+PREFERENCES_TYPES = ["List", "Path"]
+
+OS_IDS = {"Linux": 1,
+          "Windows": 2,
+          "Darwin": 3}
+
+NAME = ''
+EMAIL = ''
\ No newline at end of file
diff --git a/SDK/skeletonSrcProfile.py b/SDK/skeletonSrcProfile.py
new file mode 100644
index 0000000..6e8e7d2
--- /dev/null
+++ b/SDK/skeletonSrcProfile.py
@@ -0,0 +1,438 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+import os
+import sys
+import json
+from zipfile import ZipFile
+from termcolor import colored, cprint
+
+from sdkConstants import *
+from preferencesProfile import Preferences
+
+
+class SkeletonSourceFiles(Preferences):
+    def __init__(self, action):
+        Preferences.__init__(self, action)
+        self.names = []
+        self.apis = []
+        self.mainFile = self.pluginDirectory + "/main.cpp"
+        self.newNames = []
+        self.globNames()
+        self.initPackage()
+
+    def initPackage(self):
+        if os.path.exists(self.packageFile):
+            with open(self.packageFile, 'r') as f:
+                self.package = json.load(f)
+
+    def updateMainFileVersion(self):
+        if os.path.exists(self.mainFile):
+            with open(self.mainFile, 'r') as f:
+                lines = f.readlines()
+                splits = self.version.split('.')
+                if (not self.version):
+                    splits = ['', '', '']
+                splitStr = [
+                    "#define " +
+                    self.pluginName +
+                    "_VERSION_MAJOR ",
+                    "#define " +
+                    self.pluginName +
+                    "_VERSION_MINOR ",
+                    "#define " +
+                    self.pluginName +
+                    "_VERSION_PATCH "]
+                outStr = []
+                idx = 0
+                for line in lines:
+                    if (idx < 3 and splitStr[idx] in line):
+                        parts = line.split(splitStr[idx])
+                        line = line.replace(parts[-1], splits[idx] + "\n")
+                        idx += 1
+                    outStr.append(line)
+                f.close()
+            with open(self.mainFile, 'w') as f:
+                f.write(''.join(outStr))
+
+    def updatePackageFileVersion(self):
+        if os.path.exists(self.packageFile):
+            if(not self.package):
+                with open(self.packageFile, 'w') as f:
+                    self.package = json.load(f)
+                    f.close()
+            self.package["version"] = self.version
+            with open(self.packageFile, 'w') as f:
+                json.dump(self.package, f, indent=4)
+                f.close()
+
+    def updateCMakeListsFileVersion(self):
+        if os.path.exists(self.cmakelistsFile):
+            with open(self.cmakelistsFile, 'r') as f:
+                outStr = f.read()
+                lines = outStr.split("\n")
+                for i, line in enumerate(lines):
+                    if ("set (Version " in line):
+                        lines[i] = f"set (Version {self.version})"
+                        break
+                f.close()
+
+            with open(self.cmakelistsFile, 'w') as f:
+                f.write('\n'.join(lines))
+                f.close()
+
+    def updateVersion(self):
+        self.updateMainFileVersion()
+        self.updatePackageFileVersion()
+        self.updateCMakeListsFileVersion()
+
+    def createMain(self, addAllowed='y'):
+        if (not self.isManifest()):
+            self.createManifest()
+        with open('./Templates/main.cpp') as f:
+            mainStr = f.read()
+            mainStr = mainStr.replace('HEADER', self.header)
+            mainStr = mainStr.replace('PLUGINNAME', self.pluginName)
+            if (not self.checkVersionFormat(self.version)):
+                self.version = '0.0.0'
+            version = self.version.split(".")
+            pluginVersionMajor = version[0]
+            pluginVersionMinor = version[1]
+            pluginVersionPatch = version[2]
+
+            mainStr = mainStr.replace('PLUGINVERSIONMAJOR', pluginVersionMajor)
+            mainStr = mainStr.replace('PLUGINVERSIONMINOR', pluginVersionMinor)
+            mainStr = mainStr.replace('PLUGINVERSIONPATCH', pluginVersionPatch)
+            splits = mainStr.split("----------------")
+            includesAPIStr = None
+            pluginAPIStr = None
+            for i, split in enumerate(splits):
+                if ("PLUGINAPI" in split and "APIMANAGER" in split):
+                    if (not pluginAPIStr):
+                        pluginAPIStr = self.createHandlers(split)
+                    splits[i] = splits[i].replace(split, pluginAPIStr)
+                if ("INCLUDESAPI" in split):  # must always be called first
+                    if (not includesAPIStr):
+                        includesAPIStr = self.getIncludeStr(split, addAllowed)
+                    splits[i] = splits[i].replace(split, includesAPIStr)
+
+            mainStr = ''.join(splits)
+            f.close()
+        with open(self.mainFile, 'w') as f:
+            f.write(mainStr)
+            f.close()
+        return True
+
+    def globNames(self):
+        if (self.isMainDirectory()):
+            files = os.listdir(self.pluginDirectory)
+            for file in files:
+                if (".h" in file):
+                    if (("MediaHandler" in file or "ConversationHandler" in file)):
+                        name = file.replace(".h", "")
+                        name = name.replace("MediaHandler", "")
+                        name = name.replace("ConversationHandler", "")
+                        self.names.append(name)
+                    if ("MediaHandler" in file):
+                        self.apis.append(PLUGINS_APIS["MEDIA_HANDLER"])
+                    if ("ConversationHandler" in file):
+                        self.apis.append(PLUGINS_APIS["CONVERSATION_HANDLER"])
+
+    def createHandlers(self, split):
+        if (len(self.names) == 0):
+            return ""
+        createStr = ""
+        for i, name in enumerate(self.names):
+            if (self.apis[i] == PLUGINS_APIS["MEDIA_HANDLER"]):
+                temp = split.replace("PLUGINAPI", f"{name}MediaHandler")
+                temp = temp.replace("APIMANAGER", "CallMediaHandlerManager")
+            elif (self.apis[i] == PLUGINS_APIS["CONVERSATION_HANDLER"]):
+                temp = split.replace("PLUGINAPI", f"{name}ConversationHandler")
+                temp = temp.replace("APIMANAGER", "ConversationHandlerManager")
+            createStr += temp
+        return createStr
+
+    def getIncludeStr(self, split='', addAllowed='y'):
+        includesStr = ""
+        self.newNames = []
+        while (addAllowed == "y" or addAllowed == "Y"):
+            addAllowed = ''
+            functionName = ""
+            while(functionName == "" or functionName in self.names):
+                functionName = input("\nChose a functionality name: ")
+                functionName = pattern.sub('', functionName)
+            apiType = ''
+            while (apiType not in PLUGINS_APIS.values()):
+                print(f"\nChoose a API for functionality \"{functionName}\".")
+                print("\nAvailable APIs: ")
+                print("(1) video during a call (Media Handler API)")
+                print(
+                    colored(
+                        "For more information about the API, call help preferences.",
+                        "yellow"))
+                # or (2) to chat messages: ")
+                apiType = input("\nEnter a data type number: ")
+                if (apiType not in PLUGINS_APIS.values()):
+                    print(colored(f"Data type '{apiType}' not valid!", "red"))
+            self.names.append(functionName)
+            self.newNames.append(functionName)
+            self.apis.append(apiType)
+            while (addAllowed not in ['y', 'N', 'Y', 'n']):
+                addAllowed = input("\nAdd another functionaliy? [y/N] ")
+                if not addAllowed:
+                    addAllowed = 'N'
+                    break
+        for j, item in enumerate(self.apis):
+            temp = ''
+            localNames = self.names.copy()
+            localApis = self.apis.copy()
+            if (item == PLUGINS_APIS["MEDIA_HANDLER"]):
+                localNames[j] = self.names[j] + "MediaHandler"
+                if (split):
+                    temp = split.replace("INCLUDESAPI", localNames[j])
+            elif (item == PLUGINS_APIS["CONVERSATION_HANDLER"]):
+                localNames[j] = self.names[j] + "ConversationHandler"
+                if (split):
+                    temp = split.replace("INCLUDESAPI", localNames[j])
+            includesStr += temp
+        if (self.newNames != []):
+            self.createAPIHeaderFiles()
+            self.createAPIImplFiles()
+        return includesStr
+
+    def createAPIHeaderFiles(self):
+        for j, item in enumerate(self.apis):
+            if (self.names[j] in self.newNames):
+                if (item == PLUGINS_APIS["MEDIA_HANDLER"]):
+                    with open('./Templates/genericMediaHandler.h', 'r') as f:
+                        data = f.read()
+                        data = data.replace("HEADER", self.header)
+                        data = data.replace("GENERIC", self.names[j])
+                        f.close()
+                    with open(f"{self.pluginDirectory}/{self.names[j]}MediaHandler.h", 'w') as f:
+                        f.write(data)
+                        f.close()
+                    with open('./Templates/genericVideoSubscriber.h', 'r') as f:
+                        data = f.read()
+                        data = data.replace("HEADER", self.header)
+                        data = data.replace("GENERIC", self.names[j])
+                        f.close()
+                    with open(f"{self.pluginDirectory}/{self.names[j]}VideoSubscriber.h", 'w') as f:
+                        f.write(data)
+                        f.close()
+                elif (item == PLUGINS_APIS["CONVERSATION_HANDLER"]):
+                    with open('./Templates/genericConversationHandler.h', 'r') as f:
+                        data = f.read()
+                        data = data.replace("HEADER", self.header)
+                        data = data.replace('GENERIC', self.names[j])
+                        f.close()
+                    with open(f"{self.pluginDirectory}/{self.names[j]}ConversationHandler.h", 'w') as f:
+                        f.write(data)
+                        f.close()
+
+    def createAPIImplFiles(self):
+        for j, item in enumerate(self.apis):
+            if (self.names[j] in self.newNames):
+                if (item == PLUGINS_APIS["MEDIA_HANDLER"]):
+                    with open('./Templates/genericMediaHandler.cpp', 'r') as f:
+                        data = f.read()
+                        data = data.replace("HEADER", self.header)
+                        data = data.replace("PLUGINNAME", self.pluginName)
+                        data = data.replace("GENERIC", self.names[j])
+                        f.close()
+                    with open(f"{self.pluginDirectory}/{self.names[j]}MediaHandler.cpp", 'w') as f:
+                        f.write(data)
+                        f.close()
+                    with open('./Templates/genericVideoSubscriber.cpp', 'r') as f:
+                        data = f.read()
+                        data = data.replace("HEADER", self.header)
+                        data = data.replace("GENERIC", self.names[j])
+                        f.close()
+                    with open(f"{self.pluginDirectory}/{self.names[j]}VideoSubscriber.cpp", 'w') as f:
+                        f.write(data)
+                        f.close()
+                elif (item == PLUGINS_APIS["CONVERSATION_HANDLER"]):
+                    with open('./Templates/genericConversationHandler.cpp', 'r') as f:
+                        data = f.read()
+                        data = data.replace("HEADER", self.header)
+                        data = data.replace("GENERIC", self.names[j])
+                        f.close()
+                    with open(f"{self.pluginDirectory}/{self.names[j]}ConversationHandler.cpp", 'w') as f:
+                        f.write(data)
+                        f.close()
+        self.createPreferences(self.names)
+        self.setRuntimePreferences()
+
+    def setRuntimePreferences(self):
+        for j, item in enumerate(self.apis):
+            baseStr1 = ''
+            baseStr2 = ''
+            if (len(self.newNames) > 0 and self.names[j] in self.newNames):
+                if (item == PLUGINS_APIS["MEDIA_HANDLER"]):
+                    with open(f"{self.pluginDirectory}/{self.names[j]}MediaHandler.cpp", 'r') as f:
+                        data = f.read()
+                        parts = data.split("----------------")
+                        for part in parts:
+                            if "PREFERENCE1" in part:
+                                baseStr1 = part
+                            if "PREFERENCE2" in part:
+                                baseStr2 = part
+                        newStr1 = ''
+                        newStr2 = ''
+                        for preference in self.preferences:
+                            if (self.names[j] in preference['scope']):
+                                editable = ''
+                                while(editable not in ['y', 'n']):
+                                    editable = input(
+                                        f"\nThe preference {preference['title']} will be changeable during running time\nfor {self.names[j]} functionality? [y/n] ")
+                                if (editable == 'y'):
+                                    newStr1 += baseStr1.replace(
+                                        "PREFERENCE1", preference['key'])
+                                    newStr2 += baseStr2.replace(
+                                        "PREFERENCE2", preference['key'])
+                        data = data.replace(baseStr1, newStr1)
+                        data = data.replace(baseStr2, newStr2)
+                        f.close()
+                    with open(f"{self.pluginDirectory}/{self.names[j]}MediaHandler.cpp", 'w') as f:
+                        parts = data.split("----------------")
+                        f.write(''.join(parts))
+                        f.close()
+                elif (item == PLUGINS_APIS["CONVERSATION_HANDLER"]):
+                    with open(f"{self.pluginDirectory}/{self.names[j]}ConversationHandler.cpp", 'r') as f:
+                        data = f.read()
+                        parts = data.split("----------------")
+                        for part in parts:
+                            if "PREFERENCE1" in part:
+                                baseStr1 = part
+                            if "PREFERENCE2" in part:
+                                baseStr2 = part
+                        newStr1 = ''
+                        newStr2 = ''
+                        for preference in self.preferences:
+                            if (self.names[j] in preference['scope']):
+                                editable = ''
+                                while(editable not in ['y', 'n']):
+                                    editable = input(
+                                        f"\nThe preference {preference['title']} will be changeable during running time\nfor {self.names[j]} functionality? [y/n] ")
+                                if (editable == 'y'):
+                                    newStr1 += baseStr1.replace(
+                                        "PREFERENCE1", preference['key'])
+                                    newStr2 += baseStr2.replace(
+                                        "PREFERENCE1", preference['key'])
+                        data = data.replace(baseStr1, newStr1)
+                        data = data.replace(baseStr2, newStr2)
+                        f.close()
+                    with open(f"{self.pluginDirectory}/{self.names[j]}ConversationHandler.cpp", 'w') as f:
+                        parts = data.split("----------------")
+                        f.write(''.join(parts))
+                        f.close()
+
+    def createFunctionality(self):
+        if (not self.isManifest()):
+            print("\nBefore creating a functionality, you must define your manifest.")
+            self.createManifest()
+            print("\nManifest ok, continuing functionality creation.")
+        self.getIncludeStr()
+        self.createMain(addAllowed='n')
+        self.createBuildFiles()
+
+    def modifyManifest(self, action):
+        if (self.modifyManifestInternal(action)):
+            self.updateVersion()
+
+    def createPackageJson(self):
+        if (not self.isManifest()):
+            print("\nBefore creating a package, you must define your manifest.")
+            self.createManifest()
+            print("\nManifest ok, continuing package creation.")
+        with open("./Templates/package.json") as f:
+            self.package = json.load(f)
+            self.package["name"] = self.pluginName
+            self.package["version"] = self.version
+            if (not self.checkVersionFormat(self.version)):
+                self.package["version"] = '0.0.0'
+            f.close()
+
+        with open('./Templates/defaultDependenciesStrings.json') as f:
+            defaultStrings = json.load(f)
+            f.close()
+
+        for api in self.apis:
+            if api == PLUGINS_APIS["MEDIA_HANDLER"]:
+                for dep in defaultStrings["package.json"]["MediaHandler"]["deps"]:
+                    if dep not in self.package["deps"]:
+                        self.package["deps"].append(dep)
+        with open(f"{self.pluginDirectory}/package.json", 'w') as f:
+            json.dump(self.package, f, indent=4)
+            f.close()
+
+        print("\nPackage ok.")
+
+    def globFiles(self, baseStr, key, ext):
+        import glob
+        files = glob.glob(f"{self.pluginDirectory}/**.{ext}", recursive=True)
+        outputStr = ""
+        for item in files:
+            name = os.path.split(item)[1]
+            outputStr += baseStr.replace(key, name)
+        return outputStr
+
+    def fillBuildFile(self, inputStr):
+        inputStr = inputStr.replace('PLUGINNAME', self.pluginName)
+        inputStr = inputStr.replace('MANIFESTVERSION', self.version)
+        splits = inputStr.split('---')
+        tempPart = []
+        for i, split in enumerate(splits):
+            if (("FFMPEG" in split or "avutil" in split)
+                    and PLUGINS_APIS['MEDIA_HANDLER'] not in self.apis):
+                splits[i] = ''
+            elif ("CPPFILENAME" in split):
+                splits[i] = self.globFiles(split, "CPPFILENAME", "cpp")
+            elif ("HFILENAME" in split):
+                splits[i] = self.globFiles(split, "HFILENAME", "h")
+            elif ("FFMPEGCPP" in split):
+                splits[i] = split.replace("FFMPEGCPP", '')
+            elif ("FFMPEGH" in split):
+                splits[i] = split.replace("FFMPEGH", '')
+        inputStr = ''.join(splits)
+        return inputStr
+
+    def createBuildFiles(self):
+        with open("./Templates/CMakeLists.txt", 'r') as f:
+            cmakelists = f.read()
+            f.close()
+        cmakelists = self.fillBuildFile(cmakelists)
+        with open(self.cmakelistsFile, 'w') as f:
+            f.write(cmakelists)
+            f.close()
+
+        with open("./Templates/build.sh", 'r') as f:
+            build = f.read()
+            f.close()
+        build = self.fillBuildFile(build)
+        with open(self.buildFile, 'w') as f:
+            f.write(build)
+            f.close()
+
+        print("\nCMakeLists.txt and build.sh ok.")
+        return
diff --git a/SDK/userProfile.py b/SDK/userProfile.py
new file mode 100644
index 0000000..387f81a
--- /dev/null
+++ b/SDK/userProfile.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+from datetime import datetime
+
+from sdkConstants import *
+
+
+class UserInfos:
+    def __init__(self, action):
+        self.name = "AUTHORNAME"
+        self.mail = "AUTHORMAIL"
+        self.header = ''
+        self.initHeaderSkeleton()
+        if (action == ACTIONS["CREATE"] or action ==
+                ACTIONS["CREATE_FUNCTIONALITY"] or action == ACTIONS["CREATE_MAIN"]):
+            self.createCopyRight()
+
+    def initHeaderSkeleton(self):
+        with open('./Templates/copyright.txt') as f:
+            self.header = f.read()
+            f.close()
+
+    def createCopyRight(self):
+        global NAME
+        global EMAIL
+        with open('./Templates/copyright.txt') as f:
+            data = f.read()
+            today = datetime.today()
+            data = data.replace("YEAR", str(today.year))
+            authorName = NAME
+            if (authorName == ""):
+                authorName = input("\nWhat's your name? ")
+            if (authorName != ""):
+                self.name = authorName
+                NAME = self.name
+            data = data.replace("AUTHORNAME", self.name)
+            authorMail = EMAIL
+            if (authorMail == ""):
+                authorMail = input("\nWhat's your e-mail? ")
+            if (authorMail != ""):
+                self.mail = authorMail
+                EMAIL = self.mail
+            data = data.replace("AUTHORMAIL", self.mail)
+        self.header = data
diff --git a/SDK/utils.py b/SDK/utils.py
new file mode 100644
index 0000000..926e232
--- /dev/null
+++ b/SDK/utils.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
+import os
+import glob
+import platform
+
+from sdkConstants import *
+from skeletonSrcProfile import SkeletonSourceFiles
+
+
+def initializeClasses(action):
+    setSkeletonSourceFiles(action)
+    return bool(globalSkeletonSourceFiles.pluginName)
+
+
+globalSkeletonSourceFiles = ''
+
+
+def setSkeletonSourceFiles(action):
+    global globalSkeletonSourceFiles
+    globalSkeletonSourceFiles = SkeletonSourceFiles(action)
+
+
+def getSkeletonSourceFiles():
+    return globalSkeletonSourceFiles
diff --git a/assemble-plugin.py b/assemble-plugin.py
deleted file mode 100644
index 96e307c..0000000
--- a/assemble-plugin.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import os
-import argparse
-import sys
-import subprocess
-from zipfile import ZipFile
-import platform
-
-
-def parser():
-    parser = argparse.ArgumentParser(description='Build some plugins.')
-    parser.add_argument('--plugins', type=str,
-                        help='Names of plugins to be build')
-    parser.add_argument('--extraPath', type=str, default="",
-                        help='output intermediate Path')
-
-    args = parser.parse_args()
-    return args
-
-
-def main():
-    args = parser()
-
-    system = platform.system().lower()
-    if system == "linux" or system == "linux2":
-        osBuildPath = "/build-local"
-        distribution = "x86_64-linux-gnu"
-    elif system == "darwin":
-        sys.exit("Plugins not supported on MacOS and IOS")
-    elif system == "windows":
-        osBuildPath = "/msvc"
-        distribution = "x64-windows"
-
-    plugins = args.plugins.split(',')
-    for plugin in plugins:
-        root = os.path.dirname(os.path.abspath(__file__))
-        outputJPL = root + "/build/" + distribution + \
-            "/" + args.extraPath + "/" + plugin + ".jpl"
-        outputBuild = root + "/" + plugin + osBuildPath + "/jpl"
-
-        with ZipFile(outputJPL, 'w') as zipObj:
-            for folderName, subfolders, filenames in os.walk(outputBuild):
-                for filename in filenames:
-                    filePath = os.path.join(folderName, filename)
-                    zipObj.write(filePath, folderName.split("/")
-                                 [-1][4:] + "/" + filename)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/build-plugin.py b/build-plugin.py
index d96e575..6190afd 100644
--- a/build-plugin.py
+++ b/build-plugin.py
@@ -1,10 +1,32 @@
 #!/usr/bin/env python3
 #
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# Creates packaging targets for a distribution and architecture.
+# This helps reduce the length of the top Makefile.
+#
+
 # This script must unify the Plugins build for
 # every project and Operational System
 
 import os
 import sys
+import json
 import platform
 import argparse
 import subprocess
@@ -28,6 +50,8 @@ def parse():
     parser.add_argument('--distribution')
     parser.add_argument('--processor', type=str, default="GPU",
                         help='Runtime plugin CPU/GPU setting.')
+    parser.add_argument('--buildOptions', default='', type=str,
+                        help="Type all build optionsto pass to package.json 'defines' property.\nThis argument consider that you're using cmake.")
 
     dist = choose_distribution()
 
@@ -50,6 +74,10 @@ def parse():
         args.processor *= len(args.projects)
 
     validate_args(args)
+
+    if dist != WIN32_DISTRIBUTION_NAME:
+        args.toolset = ''
+        args.sdk = ''
     return args
 
 
@@ -75,6 +103,9 @@ def validate_args(parsed_args):
         if (processor not in ['GPU', 'CPU']):
             sys.exit('Processor can only be GPU or CPU.')
 
+    if (parsed_args.buildOptions):
+        parsed_args.buildOptions = parsed_args.buildOptions.split(',')
+
 
 def choose_distribution():
     system = platform.system().lower()
@@ -95,14 +126,32 @@ def choose_distribution():
     return 'Unknown'
 
 
-def buildPlugin(pluginPath, processor, distribution):
+def buildPlugin(pluginPath, processor, distribution, toolset='', sdk='', buildOptions=''):
     if distribution == WIN32_DISTRIBUTION_NAME:
-        return subprocess.run([
+        if (buildOptions):
+            with open(f"{pluginPath}/package.json") as f:
+                defaultPackage = json.load(f)
+                package = defaultPackage.copy()
+                package['defines'] = []
+                for option in buildOptions:
+                    package['defines'].append(option)
+                f.close()
+            with open(f"{pluginPath}/package.json", 'w') as f:
+                json.dump(package, f, indent=4)
+                f.close()
+        subprocess.run([
             sys.executable, os.path.join(
-                os.getcwd(), pluginPath + "/build-windows.py"),
-            "--toolset", args.toolset,
-            "--sdk", args.sdk
+                os.getcwd(), "../../daemon/compat/msvc/winmake.py"),
+            "-P",
+            "--toolset", toolset,
+            "--sdk", sdk,
+            "-fb", pluginPath.split('/')[-1]
         ], check=True)
+        if (buildOptions):
+            with open(f"{pluginPath}/package.json", "w") as f:
+                json.dump(defaultPackage, f, indent=4)
+                f.close()
+        return
 
     environ = os.environ.copy()
 
@@ -117,6 +166,7 @@ def buildPlugin(pluginPath, processor, distribution):
     install_args.append('-p')
     install_args.append(str(multiprocessing.cpu_count()))
 
+    subprocess.check_call(['chmod', '+x', pluginPath + "/build.sh"])
     return subprocess.run([pluginPath + "/build.sh"] +
                           install_args, env=environ, check=True)
 
@@ -130,7 +180,10 @@ def main():
         buildPlugin(
             currentDir + "/" + plugin,
             args.processor[i],
-            args.distribution)
+            args.distribution,
+            args.toolset,
+            args.sdk,
+            args.buildOptions)
 
 
 if __name__ == "__main__":
diff --git a/docker/Dockerfile_android b/docker/Dockerfile_android
index 4723c09..e9684ac 100644
--- a/docker/Dockerfile_android
+++ b/docker/Dockerfile_android
@@ -6,50 +6,50 @@ ENV HOME /home/gradle
 ENV SSH_AUTH_SOCK /home/gradle/.sockets/ssh
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
-	asciidoc \
-	autogen \
-	automake \
-	autoconf \
-	autopoint \
-	gettext \
-	ca-certificates \
-	cmake \
-	bc \
-	bison \
-	build-essential \
-	bzip2 \
-	doxygen \
-	git \
-	lib32stdc++6 \
-	lib32z1 \
-	libtool \
-	locales \
-	m4 \
-	pkg-config \
-	software-properties-common \
-	ssh \
-	unzip \
-	wget \
-	curl \
-	yasm \
-	nasm \
-	zip \
-	libpcre3 \
-	libpcre3-dev \
-	&& locale-gen $LANG $LC_ALL && update-locale $LANG $LC_ALL
+    asciidoc \
+    autogen \
+    automake \
+    autoconf \
+    autopoint \
+    gettext \
+    ca-certificates \
+    cmake \
+    bc \
+    bison \
+    build-essential \
+    bzip2 \
+    doxygen \
+    git \
+    lib32stdc++6 \
+    lib32z1 \
+    libtool \
+    locales \
+    m4 \
+    pkg-config \
+    software-properties-common \
+    ssh \
+    unzip \
+    wget \
+    curl \
+    yasm \
+    nasm \
+    zip \
+    libpcre3 \
+    libpcre3-dev \
+    && locale-gen $LANG $LC_ALL && update-locale $LANG $LC_ALL
 
 # Android SDK tools
 RUN wget -O /tmp/android-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip && \
-	mkdir -p /opt/android-sdk && \
-	unzip -q -d /opt/android-sdk /tmp/android-tools.zip && \
-	rm -f /tmp/android-tools.zip && \
-	chown -R root:root /opt/android-sdk
+    mkdir -p /opt/android-sdk && \
+    unzip -q -d /opt/android-sdk /tmp/android-tools.zip && \
+    rm -f /tmp/android-tools.zip && \
+    chown -R root:root /opt/android-sdk
 
 # Swig 4.0.1
 RUN wget -O /tmp/swig.tar.gz https://github.com/swig/swig/archive/rel-4.0.1.tar.gz && \
-	tar xzf  /tmp/swig.tar.gz -C /opt && \
-	cd /opt/swig-rel-4.0.1/ && ./autogen.sh && ./configure && make && make install && \
-	cd .. && rm -rf /opt/swig-rel-4.0.1 /tmp/swig.tar.gz
+    tar xzf  /tmp/swig.tar.gz -C /opt && \
+    cd /opt/swig-rel-4.0.1/ && ./autogen.sh && ./configure && make && make install && \
+    cd .. && rm -rf /opt/swig-rel-4.0.1 /tmp/swig.tar.gz
 
 ENV ANDROID_HOME /opt/android-sdk
 ENV PATH ${PATH}:${ANDROID_HOME}/tools/bin
@@ -57,10 +57,10 @@ ENV PATH ${PATH}:${ANDROID_HOME}/tools/bin
 # Android SDK libraries, NDK
 RUN sdkmanager --sdk_root=${ANDROID_HOME} --update
 RUN (while sleep 1; do echo "y"; done) | sdkmanager --sdk_root=${ANDROID_HOME} 'build-tools;30.0.2' \
-	'platforms;android-29'\
-	'extras;android;m2repository'\
-	'extras;google;m2repository'\
-	'ndk;21.3.6528147'
+    'platforms;android-29'\
+    'extras;android;m2repository'\
+    'extras;google;m2repository'\
+    'ndk;21.3.6528147'
 
 ENV ANDROID_SDK ${ANDROID_HOME}
 ENV ANDROID_NDK ${ANDROID_HOME}/ndk/21.3.6528147
diff --git a/lib/accel.cpp b/lib/accel.cpp
new file mode 100644
index 0000000..d5d92aa
--- /dev/null
+++ b/lib/accel.cpp
@@ -0,0 +1,67 @@
+/**
+ *  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.
+ */
+
+#ifndef ACCEL_H
+#define ACCEL_H
+
+#include "accel.h"
+
+extern "C" {
+#if LIBAVUTIL_VERSION_MAJOR < 56
+AVFrameSideData*
+av_frame_new_side_data_from_buf(AVFrame* frame, enum AVFrameSideDataType type, AVBufferRef* buf)
+{
+    auto side_data = av_frame_new_side_data(frame, type, 0);
+    av_buffer_unref(&side_data->buf);
+    side_data->buf = buf;
+    side_data->data = side_data->buf->data;
+    side_data->size = side_data->buf->size;
+    return side_data;
+}
+#endif
+}
+
+AVFrame*
+transferToMainMemory(AVFrame* framePtr, AVPixelFormat desiredFormat)
+{
+    AVFrame* out = av_frame_alloc();
+    auto desc = av_pix_fmt_desc_get(static_cast<AVPixelFormat>(framePtr->format));
+
+    if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
+        out = framePtr;
+        return out;
+    }
+
+    out->format = desiredFormat;
+    if (av_hwframe_transfer_data(out, framePtr, 0) < 0) {
+        out = framePtr;
+        return out;
+    }
+
+    out->pts = framePtr->pts;
+    if (AVFrameSideData* side_data = av_frame_get_side_data(framePtr, AV_FRAME_DATA_DISPLAYMATRIX)) {
+        av_frame_new_side_data_from_buf(out,
+                                        AV_FRAME_DATA_DISPLAYMATRIX,
+                                        av_buffer_ref(side_data->buf));
+    }
+    return out;
+}
+
+#endif
diff --git a/lib/accel.h b/lib/accel.h
index 0f3ceec..4d78abe 100644
--- a/lib/accel.h
+++ b/lib/accel.h
@@ -37,41 +37,10 @@ using VideoFrame = DRing::VideoFrame;
 
 extern "C" {
 #if LIBAVUTIL_VERSION_MAJOR < 56
-AVFrameSideData*
-av_frame_new_side_data_from_buf(AVFrame* frame, enum AVFrameSideDataType type, AVBufferRef* buf)
-{
-    auto side_data = av_frame_new_side_data(frame, type, 0);
-    av_buffer_unref(&side_data->buf);
-    side_data->buf = buf;
-    side_data->data = side_data->buf->data;
-    side_data->size = side_data->buf->size;
-    return side_data;
-}
+AVFrameSideData* av_frame_new_side_data_from_buf(AVFrame* frame,
+                                                 enum AVFrameSideDataType type,
+                                                 AVBufferRef* buf);
 #endif
 }
 
-AVFrame*
-transferToMainMemory(AVFrame* framePtr, AVPixelFormat desiredFormat)
-{
-    AVFrame* out = av_frame_alloc();
-    auto desc = av_pix_fmt_desc_get(static_cast<AVPixelFormat>(framePtr->format));
-
-    if (desc && not(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
-        out = framePtr;
-        return out;
-    }
-
-    out->format = desiredFormat;
-    if (av_hwframe_transfer_data(out, framePtr, 0) < 0) {
-        out = framePtr;
-        return out;
-    }
-
-    out->pts = framePtr->pts;
-    if (AVFrameSideData* side_data = av_frame_get_side_data(framePtr, AV_FRAME_DATA_DISPLAYMATRIX)) {
-        av_frame_new_side_data_from_buf(out,
-                                        AV_FRAME_DATA_DISPLAYMATRIX,
-                                        av_buffer_ref(side_data->buf));
-    }
-    return out;
-}
+AVFrame* transferToMainMemory(AVFrame* framePtr, AVPixelFormat desiredFormat);
diff --git a/lib/framescaler.h b/lib/framescaler.h
index 7ee271c..285b3ec 100644
--- a/lib/framescaler.h
+++ b/lib/framescaler.h
@@ -18,7 +18,9 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
-#pragma once
+#ifndef FRAMESCALER_H
+#define FRAMESCALER_H
+
 extern "C" {
 #include <libavutil/avutil.h>
 #include <libavutil/frame.h>
@@ -131,3 +133,5 @@ protected:
     SwsContext* ctx_;
     int mode_;
 };
+
+#endif
-- 
GitLab