diff --git a/.gitignore b/.gitignore index 022c6596c647de0a8651cc683f63e9e27a559a93..548a5c968a3c92a7b55ce26125679425a89f5d6c 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 61f9d401cd93422caa09be5d24e0ab436318a963..9254bd9fb12e4134d4f47eb07811cf92c88e6a12 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 ad60269528332c9c1e366b216a9d66157e0e247d..b046871c7f04970844e04ce7ef2616d8022df3f3 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 b14e28938069d999a4c222ff794c3d57e4dc1f8a..cdf3602e725cb2c9f1d5010ebbff6dbbdfec27a3 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 49d8a7918c186850726907eaab74176ad645dc3e..0b7d078a0dee6c8162e27e0a54e7cdebca755c92 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 b5f0c319aaac1412f5ea5db7db90f88d320d8f7f..295f2ebac2d5bef33f99871ecf27a093c005ee0a 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 a9e4fe0ff037b27131693a748617709d2ae60606..9aae38bee439b8f58be95ad7777d94b96aa2af8b 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 4748609875c5355f8227f86234a891895c731607..0e1d0119cf5955559a2670f15ca7314a46a258ee 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 6815f08e5801630183f0b4f842c63c4ca5ead544..3b38830a53bc6e6c3ef51f4690f897b692f63ef9 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 dc14a40bb2afec03f08915adcb59c8aee3b93390..b7ec336e1486f5aea4334ffb506bc5326c20b7a0 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 04bf28e351b32b7f12115b46e1df734355ef8d7b..39b590fc4d44cd62b1ca83bc2e37076d3875cb05 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 0000000000000000000000000000000000000000..4121eb681f7634e1d159b6b4aa7b4a0ebf3b5390 --- /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 0000000000000000000000000000000000000000..b238fdcd9898e9cdfbeefb5d9c0bef924a07ecbc --- /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 0000000000000000000000000000000000000000..0dd95c62af5fcf986c114581fdb5ae4322738c01 --- /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 0000000000000000000000000000000000000000..d315a3f69e3223928dc48c5093445e0a31b88cc9 --- /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 0000000000000000000000000000000000000000..836001ae8a2976efadcc09b1185cd18e6edad873 --- /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 0000000000000000000000000000000000000000..0c36a2f97e4a807493316871af45adda1c7f09ef --- /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 0000000000000000000000000000000000000000..60422f704bccf90738ad0653e26e511c0b996e56 --- /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 0000000000000000000000000000000000000000..bbe3f26a1f432e936a969eb373a39b66a878a615 --- /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 0000000000000000000000000000000000000000..078c1a5ee7e41e4b073973dcbc31be076608238b --- /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 0000000000000000000000000000000000000000..253e64ee20d1fbe46a93dec8c413ead2b2b35e53 --- /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 0000000000000000000000000000000000000000..f9f78c44da1f8962c3a27ad53db42ad7a7b1c2a5 --- /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 0000000000000000000000000000000000000000..882dafc182feeca8ef48927b6186842252b4423a --- /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 0000000000000000000000000000000000000000..2452de44ff4968559299e01b66efecc1a2c7364e --- /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 0000000000000000000000000000000000000000..cf00a19d150edd923bde0ffd9cf3d8794f279850 --- /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 0000000000000000000000000000000000000000..3488cb831fc9f3c9a41727c1e9969955d3d4cce7 --- /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 0000000000000000000000000000000000000000..1906ce6d01c4ba2281efe780481d497bee630246 --- /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 0000000000000000000000000000000000000000..f3bbb399e1ba2107ada44655eeaa0b941aef4274 --- /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 0000000000000000000000000000000000000000..6b3a70dc06c5c1f6d953293468f553e40811caf8 --- /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 0000000000000000000000000000000000000000..13398cbfe566264a905ebd9f3fc1bf23c70b0f68 --- /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 0000000000000000000000000000000000000000..eaa6649dd2148e3a5bc9879b5da1036c3fc63ac3 --- /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 Binary files /dev/null and b/SDK/Templates/icon.png differ diff --git a/SDK/Templates/main.cpp b/SDK/Templates/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e97416ad76d1603dfc3b8cb107971d0cc12dd56f --- /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 0000000000000000000000000000000000000000..d5c8df64570481afa80405b8cdb8716f73931412 --- /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 0000000000000000000000000000000000000000..0bb6d391edf6bb5df1550705bbf50a2cbb0de2d8 --- /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 0000000000000000000000000000000000000000..4caacdccee262f605a68e6defd32edbc3715d595 --- /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 0000000000000000000000000000000000000000..588f62dc3635549da33d4b1e8a59e9d7d9f6ac0d --- /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 0000000000000000000000000000000000000000..558760a8159c3c00f7d93231e831423a1f1796e6 --- /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 0000000000000000000000000000000000000000..ba59d7c8336c978b1e5af7836d639ae32c3fe197 --- /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 0000000000000000000000000000000000000000..1e73c98f136d21034da6be904ec067c4116cca56 --- /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 0000000000000000000000000000000000000000..d4b5c4382a4b508089923ed30c1b2ec71d8e5a74 --- /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 0000000000000000000000000000000000000000..43a9a9c70a40cc4b1bad2814e0395effda0f9408 --- /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 0000000000000000000000000000000000000000..ee289054ea0ffe67d5159e475bea2e301f6a303c --- /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 0000000000000000000000000000000000000000..5c62f2c70cb9b1ab865d69b55c3dcffd752d1841 --- /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 0000000000000000000000000000000000000000..6e8e7d2f0009aeaeffbf9c6884c18affdbf4fc2a --- /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 0000000000000000000000000000000000000000..387f81ad04b0726b7ca23e107b43db51ea525fb3 --- /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 0000000000000000000000000000000000000000..926e23267f8be8a025cb4fe8813e6349db435a46 --- /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 96e307c6893893a27c1e32d89f93b05dffc99e5c..0000000000000000000000000000000000000000 --- 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 d96e57509ba27f125138b21a67c28fe26093c7c4..6190afdb299d9ae1d9da4a51fab95e72ecdca484 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 4723c09132ee4b10473ec1795cd0c8f2c5e605d5..e9684acd761b0794bff33b3259f1fd126101cebe 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 0000000000000000000000000000000000000000..d5d92aa428a31560beb108b841f894d8fc991db8 --- /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 0f3ceec13f0f7b14a676cc3779dd848b3f1ae694..4d78abe35de6c51519166f5517ed06e023323993 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 7ee271cd16b8f7e9cb83bd8d8339abae8399d85b..285b3ec0e3ef3444bbb8605a714df10c2118a7e3 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