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