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
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 0000000..2ce0aab
--- /dev/null
+++ b/SDK/Templates/icon.png
Binary files differ
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": "*/*"
+ }
+]