From cc19a7e5900e145c01e9b7ba21c2f2bb76ec42f6 Mon Sep 17 00:00:00 2001 From: Pawel Wasowski Date: Thu, 24 Dec 2020 11:36:26 +0100 Subject: [PATCH] [ML][Pipeline] Add Pipeline and PipelineManager ACR: TWDAPI-274 [Verification] createPipeline() tested in Chrome DevTools with the snippet below works fine var pipeline = tizen.ml.pipeline.createPipeline( 'videoteststrc ! tizenwlsink', function(state) { console.log(state); }) //a moment later: // READY // PAUSED tizen.ml.createPipeline() // WebAPIException: InvalidValuesError Change-Id: I68beaebf7e248b61bba9dc06ce6124c187c45fae Signed-off-by: Pawel Wasowski --- src/ml/js/ml_common.js | 3 + src/ml/js/ml_pipeline.js | 96 ++++++++++++++++++++++++++- src/ml/ml_instance.cc | 63 ++++++++++++++++-- src/ml/ml_instance.h | 5 +- src/ml/ml_pipeline.cc | 120 +++++++++++++++++++++++++++++++++- src/ml/ml_pipeline.h | 32 ++++++++- src/ml/ml_pipeline_manager.cc | 44 ++++++++++++- src/ml/ml_pipeline_manager.h | 18 ++++- 8 files changed, 364 insertions(+), 17 deletions(-) diff --git a/src/ml/js/ml_common.js b/src/ml/js/ml_common.js index 21819da3..c8bc9811 100755 --- a/src/ml/js/ml_common.js +++ b/src/ml/js/ml_common.js @@ -17,6 +17,9 @@ var privUtils_ = xwalk.utils; var validator_ = privUtils_.validator; var types_ = validator_.Types; +var native_ = new xwalk.utils.NativeManager(extension); + +var AbortError = new WebAPIException('AbortError', 'An unknown error occurred'); // TensorRawData diff --git a/src/ml/js/ml_pipeline.js b/src/ml/js/ml_pipeline.js index fdd1a2de..f50b97ab 100755 --- a/src/ml/js/ml_pipeline.js +++ b/src/ml/js/ml_pipeline.js @@ -14,12 +14,101 @@ * limitations under the License. */ -//PipelineManager::createPipeline() begin +var kPipelineStateChangeListenerNamePrefix = 'MLPipelineStateChangeListener'; +//PipelineManager::createPipeline() begin +var nextPipelineId = 1; +function NextPipelineId() { + return nextPipelineId++; +} + +var ValidPipelineManagerCreatePipelineExceptions = [ + 'InvalidValuesError', + 'TypeMismatchError', + 'NotSupportedError', + 'SecurityError', + 'AbortError' +]; + +var CreatePipeline = function() { + privUtils_.log('Entered PipelineManager.createPipeline()'); + var args = validator_.validateArgs(arguments, [ + { + name: 'definition', + type: validator_.Types.STRING + }, + { + name: 'listener', + type: validator_.Types.FUNCTION, + optional: true, + nullable: true + } + ]); + + if (!args.has.definition) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'Invalid parameter: pipeline definition is mandatory' + ); + } + + var pipeline = new Pipeline(NextPipelineId()); + var nativeArgs = { + id: pipeline._id, + definition: args.definition + }; + + if (args.listener) { + nativeArgs.listenerName = kPipelineStateChangeListenerNamePrefix + pipeline._id; + var stateChangeListener = function(stateObject) { + args.listener(stateObject.state); + }; + native_.addListener(nativeArgs.listenerName, stateChangeListener); + } + + var result = native_.callSync('MLPipelineManagerCreatePipeline', nativeArgs); + + if (native_.isFailure(result)) { + if (nativeArgs.listenerName) { + native_.removeListener(nativeArgs.listenerName); + } + throw native_.getErrorObjectAndValidate( + result, + ValidPipelineManagerCreatePipelineExceptions, + AbortError + ); + } + + return pipeline; +}; //PipelineManager::createPipeline() end //Pipeline::state begin - +var ValidPipelineStateExceptions = ['NotSupportedError', 'AbortError']; +var Pipeline = function(id) { + Object.defineProperties(this, { + state: { + enumerable: true, + get: function() { + var result = native_.callSync('MLPipelineGetState', { + id: id + }); + if (native_.isFailure(result)) { + throw native_.getErrorObjectAndValidate( + result, + ValidPipelineStateExceptions, + AbortError + ); + } + + return result.state; + } + }, + _id: { + value: id + } + }); +}; //Pipeline::state end //Pipeline::start() begin @@ -89,7 +178,8 @@ //Valve::setOpen() begin //Valve::setOpen() end - var MachineLearningPipeline = function() {}; +MachineLearningPipeline.prototype.createPipeline = CreatePipeline; + // ML Pipeline API diff --git a/src/ml/ml_instance.cc b/src/ml/ml_instance.cc index 5706267d..5d2f242d 100644 --- a/src/ml/ml_instance.cc +++ b/src/ml/ml_instance.cc @@ -24,21 +24,22 @@ namespace ml { using namespace common; -MlInstance::MlInstance() { +MlInstance::MlInstance() : pipeline_manager_{this} { ScopeLogger(); using namespace std::placeholders; #define REGISTER_METHOD(M) RegisterSyncHandler(#M, std::bind(&MlInstance::M, this, _1, _2)) -// Common ML API begin + // Common ML API begin -// Common ML API end + // Common ML API end -// Single API begin + // Single API begin -// Single API end + // Single API end -// Pipeline API begin + // Pipeline API begin + REGISTER_METHOD(MLPipelineManagerCreatePipeline); // Pipeline API end @@ -58,8 +59,57 @@ MlInstance::~MlInstance() { // Single API end // Pipeline API begin + +namespace { + +const std::string kId = "id"; +const std::string kDefinition = "definition"; +const std::string kPipelineStateChangeListenerName = "listenerName"; + +} // namespace + // PipelineManager::createPipeline() begin +namespace { + +bool CreatePipelineArgumentsAreInvalid(const picojson::value& args) { + ScopeLogger(); + auto arguments_valid = args.get(kId).is(); + arguments_valid &= args.get(kDefinition).is(); + arguments_valid &= (args.get(kPipelineStateChangeListenerName).is() || + args.get(kPipelineStateChangeListenerName).is()); + LoggerD("CreatePipeline arguments are %s", arguments_valid ? "valid" : "invalid"); + + return !arguments_valid; +} + +}; // namespace + +void MlInstance::MLPipelineManagerCreatePipeline(const picojson::value& args, + picojson::object& out) { + ScopeLogger("args: %s", args.serialize().c_str()); + + if (CreatePipelineArgumentsAreInvalid(args)) { + ReportError(PlatformResult{ErrorCode::ABORT_ERR, "Could not create pipeline"}, &out); + return; + } + + auto id = static_cast(args.get(kId).get()); + auto definition = args.get(kDefinition).get(); + auto state_change_listener_name = + args.get(kPipelineStateChangeListenerName).is() + ? args.get(kPipelineStateChangeListenerName).get() + : ""; + + auto ret = pipeline_manager_.CreatePipeline(id, definition, state_change_listener_name); + + if (!ret) { + ReportError(ret, &out); + return; + } + + ReportSuccess(out); +} // PipelineManager::createPipeline() end // Pipeline::state begin @@ -133,6 +183,7 @@ MlInstance::~MlInstance() { // Valve::setOpen() begin // Valve::setOpen() end + // Pipeline API end } // namespace ml diff --git a/src/ml/ml_instance.h b/src/ml/ml_instance.h index e7a19989..622bcc8a 100644 --- a/src/ml/ml_instance.h +++ b/src/ml/ml_instance.h @@ -19,6 +19,7 @@ #include "common/extension.h" +#include "ml/ml_pipeline_manager.h" #include "nnstreamer/nnstreamer-single.h" #include "nnstreamer/nnstreamer.h" @@ -40,9 +41,10 @@ class MlInstance : public common::ParsedInstance { // Single API end // Pipeline API begin + PipelineManager pipeline_manager_; // PipelineManager::createPipeline() begin - + void MLPipelineManagerCreatePipeline(const picojson::value& args, picojson::object& out); // PipelineManager::createPipeline() end // Pipeline::state begin @@ -116,7 +118,6 @@ class MlInstance : public common::ParsedInstance { // Valve::setOpen() begin // Valve::setOpen() end - // Pipeline API end }; diff --git a/src/ml/ml_pipeline.cc b/src/ml/ml_pipeline.cc index 60a01088..caa63a50 100644 --- a/src/ml/ml_pipeline.cc +++ b/src/ml/ml_pipeline.cc @@ -13,12 +13,125 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include + +#include "common/logger.h" +#include "common/picojson.h" #include "ml_pipeline.h" +#include "ml_utils.h" + +using common::PlatformResult; +using common::ErrorCode; + +namespace { + +const std::string kListenerId = "listenerId"; +const std::string kState = "state"; + +std::string StateToString(ml_pipeline_state_e state) { + ScopeLogger("state: [%d]", state); + + std::string state_str; + switch (state) { + case ML_PIPELINE_STATE_UNKNOWN: + state_str = "UNKNOWN"; + break; + case ML_PIPELINE_STATE_NULL: + state_str = "NULL"; + break; + case ML_PIPELINE_STATE_READY: + state_str = "READY"; + break; + case ML_PIPELINE_STATE_PAUSED: + state_str = "PAUSED"; + break; + case ML_PIPELINE_STATE_PLAYING: + state_str = "PLAYING"; + break; + default: + LoggerE("Illegal ml_pipeline_state_e value: [%d]", state); + state_str = "UNKNOWN"; + } + + LoggerD("state_str: [%s]", state_str.c_str()); + return state_str; +} + +} // namespace + +namespace extension { +namespace ml { + +Pipeline::Pipeline(int id, const std::string& state_change_listener_name, + common::Instance* instance_ptr) + : id_{id}, + pipeline_{nullptr}, // this will be set to a proper pointer in CreatePipeline() + state_change_listener_name_{state_change_listener_name}, + instance_ptr_{instance_ptr} { + ScopeLogger("id: [%d], state_change_listener_name: [%s]", id, state_change_listener_name.c_str()); +} // PipelineManager::createPipeline() begin - +PlatformResult Pipeline::CreatePipeline(int id, const std::string& definition, + const std::string& state_change_listener_name, + common::Instance* instance_ptr, + std::unique_ptr* out) { + ScopeLogger("id: [%d], definition: [%s], state_change_listener_name: [%s]", id, + definition.c_str(), state_change_listener_name.c_str()); + + /* We need to create the Pipeline object before setting its pipeline_ member, + * because Pipeline is the user data for the listener registered by + * ml_pipeline_construct(). + */ + std::unique_ptr pipeline_ptr{ + new (std::nothrow) Pipeline{id, state_change_listener_name, instance_ptr}}; + if (!pipeline_ptr) { + return LogAndCreateResult(ErrorCode::ABORT_ERR, "An unknown occurred.", + ("Could not allocate memory for Pipeline")); + } + + int ret = ML_ERROR_UNKNOWN; + if (state_change_listener_name == "") { + ret = ml_pipeline_construct(definition.c_str(), nullptr, nullptr, &pipeline_ptr->pipeline_); + } else { + ret = ml_pipeline_construct(definition.c_str(), PipelineStateChangeListener, + static_cast(pipeline_ptr.get()), &pipeline_ptr->pipeline_); + } + + if (ML_ERROR_NONE != ret) { + LoggerE("ml_pipeline_construct() failed: [%d] (%s)", ret, get_error_message(ret)); + return util::ToPlatformResult(ret, "Could not create a pipeline"); + } + LoggerD("ml_pipeline_construct() succeeded"); + + *out = std::move(pipeline_ptr); + return PlatformResult{}; +} // PipelineManager::createPipeline() end +Pipeline::~Pipeline() { + ScopeLogger("Destroying pipeline: [%d]", id_); + + auto ret = ml_pipeline_destroy(pipeline_); + if (ML_ERROR_NONE != ret) { + LoggerE("ml_pipeline_destroy() failed: [%d] (%s)", ret, get_error_message(ret)); + } + LoggerD("ml_pipeline_destroy() succeeded"); +} + +void Pipeline::PipelineStateChangeListener(ml_pipeline_state_e state, void* user_data) { + ScopeLogger("state: [%s]", StateToString(state).c_str()); + + Pipeline* pipeline = static_cast(user_data); + + picojson::value response{picojson::object{}}; + response.get()[kListenerId] = + picojson::value{pipeline->state_change_listener_name_}; + response.get()[kState] = picojson::value{StateToString(state)}; + + common::Instance::PostMessage(pipeline->instance_ptr_, response); +} + // Pipeline::state begin // Pipeline::state end @@ -89,4 +202,7 @@ // Valve::setOpen() begin -// Valve::setOpen() end \ No newline at end of file +// Valve::setOpen() end + +} // namespace extension +} // namespace ml diff --git a/src/ml/ml_pipeline.h b/src/ml/ml_pipeline.h index 029269e2..b5834f4f 100644 --- a/src/ml/ml_pipeline.h +++ b/src/ml/ml_pipeline.h @@ -17,18 +17,40 @@ #ifndef ML_ML_PIPELINE_H_ #define ML_ML_PIPELINE_H_ +#include +#include + +#include + +#include "common/extension.h" #include "common/picojson.h" #include "common/platform_result.h" +using common::PlatformResult; + namespace extension { namespace ml { class Pipeline { public: // PipelineManager::createPipeline() begin - + /* + * We use a static function to create Pipeline objects, instead of + * a typical constructor, to be able to return an error without + * throwing a C++ exception. + */ + static PlatformResult CreatePipeline(int id, const std::string& definition, + const std::string& state_change_listener_name, + common::Instance* instance_ptr, + std::unique_ptr* out); // PipelineManager::createPipeline() end + Pipeline() = delete; + Pipeline(const Pipeline&) = delete; + Pipeline& operator=(const Pipeline&) = delete; + + ~Pipeline(); + // Pipeline::state begin // Pipeline::state end @@ -101,6 +123,14 @@ class Pipeline { // Valve::setOpen() end private: + Pipeline(int id, const std::string& state_change_listener_name, common::Instance* instance_ptr); + + const int id_; + ml_pipeline_h pipeline_; + const std::string state_change_listener_name_; + common::Instance* instance_ptr_; + + static void PipelineStateChangeListener(ml_pipeline_state_e state, void* user_data); }; } // namespace ml diff --git a/src/ml/ml_pipeline_manager.cc b/src/ml/ml_pipeline_manager.cc index b33a7711..1a215162 100644 --- a/src/ml/ml_pipeline_manager.cc +++ b/src/ml/ml_pipeline_manager.cc @@ -15,9 +15,46 @@ */ #include "ml_pipeline_manager.h" +#include "common/tools.h" -// PipelineManager::createPipeline() begin +using common::PlatformResult; +using common::ErrorCode; +using common::tools::ReportError; +using common::tools::ReportSuccess; + +namespace extension { +namespace ml { + +PipelineManager::PipelineManager(common::Instance* instance_ptr) : instance_ptr_{instance_ptr} { + ScopeLogger(); +} +PipelineManager::~PipelineManager() { + ScopeLogger(); +} + +// PipelineManager::createPipeline() begin +PlatformResult PipelineManager::CreatePipeline(int id, const std::string& definition, + const std::string& state_change_listener_name) { + ScopeLogger("id: [%d], definition: [%s], state_change_listener_name: [%s]", id, + definition.c_str(), state_change_listener_name.c_str()); + + if (pipelines_.count(id)) { + LoggerD("The pipeline already exists: [%d]", id); + return PlatformResult{ErrorCode::ABORT_ERR, "Could not create pipeline"}; + } + + std::unique_ptr pipeline_ptr; + auto ret = Pipeline::CreatePipeline(id, definition, state_change_listener_name, instance_ptr_, + &pipeline_ptr); + if (!ret) { + return ret; + } + + pipelines_.insert({id, std::move(pipeline_ptr)}); + + return PlatformResult{}; +} // PipelineManager::createPipeline() end // Pipeline::state begin @@ -90,4 +127,7 @@ // Valve::setOpen() begin -// Valve::setOpen() end \ No newline at end of file +// Valve::setOpen() end + +} // namespace ml +} // namespace extension diff --git a/src/ml/ml_pipeline_manager.h b/src/ml/ml_pipeline_manager.h index 68891e30..1ecf79af 100644 --- a/src/ml/ml_pipeline_manager.h +++ b/src/ml/ml_pipeline_manager.h @@ -17,16 +17,30 @@ #ifndef ML_ML_PIPELINE_MANAGER_H_ #define ML_ML_PIPELINE_MANAGER_H_ +#include "common/extension.h" #include "common/picojson.h" #include "common/platform_result.h" +#include "ml_pipeline.h" + +using common::PlatformResult; + namespace extension { namespace ml { class PipelineManager { public: - // PipelineManager::createPipeline() begin + PipelineManager(common::Instance* instance_ptr); + ~PipelineManager(); + + PipelineManager() = delete; + PipelineManager(const PipelineManager&) = delete; + PipelineManager& operator=(const PipelineManager&) = delete; + + // PipelineManager::createPipeline() begin + PlatformResult CreatePipeline(int id, const std::string& definition, + const std::string& state_change_listener_name); // PipelineManager::createPipeline() end // Pipeline::state begin @@ -101,6 +115,8 @@ class PipelineManager { // Valve::setOpen() end private: + common::Instance* instance_ptr_; + std::map> pipelines_; }; } // namespace ml -- 2.34.1