From 5cfb3f3ec345fcdd4517ed1a8563e016efe8deb2 Mon Sep 17 00:00:00 2001 From: yangfan Date: Wed, 15 May 2024 13:58:35 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0image=E7=9A=84sendable?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: yangfan Change-Id: I70924f97c33a28ad99dc0e4d7669cb94e1819145 --- .../common/sendable/sendable_image_napi.cpp | 736 ++++++++++++ .../sendable/sendable_image_receiver_napi.cpp | 1036 +++++++++++++++++ .../sendable/sendable_image_source_napi.cpp | 923 +++++++++++++++ interfaces/kits/js/common/BUILD.gn | 3 + .../include/sendable/sendable_image_napi.h | 57 + .../sendable/sendable_image_receiver_napi.h | 149 +++ .../sendable/sendable_image_source_napi.h | 75 ++ 7 files changed, 2979 insertions(+) create mode 100644 frameworks/kits/js/common/sendable/sendable_image_napi.cpp create mode 100644 frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp create mode 100644 frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp create mode 100644 interfaces/kits/js/common/include/sendable/sendable_image_napi.h create mode 100644 interfaces/kits/js/common/include/sendable/sendable_image_receiver_napi.h create mode 100644 interfaces/kits/js/common/include/sendable/sendable_image_source_napi.h diff --git a/frameworks/kits/js/common/sendable/sendable_image_napi.cpp b/frameworks/kits/js/common/sendable/sendable_image_napi.cpp new file mode 100644 index 000000000..d548fb2e4 --- /dev/null +++ b/frameworks/kits/js/common/sendable/sendable_image_napi.cpp @@ -0,0 +1,736 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sendable_image_napi.h" + +#include "napi/native_node_api.h" +#include "image_log.h" +#include "media_errors.h" +#include "image_format.h" +#include "image_napi_utils.h" + +#undef LOG_DOMAIN +#define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE + +#undef LOG_TAG +#define LOG_TAG "SendableImageNapi" + +namespace { + constexpr int NUM0 = 0; + constexpr int NUM1 = 1; + constexpr int NUM2 = 2; + const std::string MY_NAME = "SendableImageNapi"; +} + +namespace OHOS { +namespace Media { +struct SendableImageAsyncContext { + napi_env env = nullptr; + napi_async_work work = nullptr; + napi_deferred deferred = nullptr; + napi_ref callbackRef = nullptr; + napi_ref thisRef = nullptr; + SendableImageNapi *napi = nullptr; + uint32_t status; + int32_t componentType; + NativeImage* image = nullptr; + NativeComponent* component = nullptr; + bool isTestContext = false; +}; +ImageHolderManager SendableImageNapi::sNativeImageHolder_; +thread_local napi_ref SendableImageNapi::sConstructor_ = nullptr; + +SendableImageNapi::SendableImageNapi() +{} + +SendableImageNapi::~SendableImageNapi() +{ + NativeRelease(); +} + +void SendableImageNapi::NativeRelease() +{ + if (native_ != nullptr) { + native_->release(); + native_ = nullptr; + } +} + +napi_value SendableImageNapi::Init(napi_env env, napi_value exports) +{ + IMAGE_FUNCTION_IN(); + napi_property_descriptor props[] = { + DECLARE_NAPI_GETTER("clipRect", JSGetClipRect), + DECLARE_NAPI_GETTER("size", JsGetSize), + DECLARE_NAPI_GETTER("format", JsGetFormat), + DECLARE_NAPI_GETTER("timestamp", JsGetTimestamp), + DECLARE_NAPI_FUNCTION("getComponent", JsGetComponent), + DECLARE_NAPI_FUNCTION("release", JsRelease), + }; + size_t size = IMG_ARRAY_SIZE(props); + napi_value thisVar = nullptr; + auto name = MY_NAME.c_str(); + if (napi_define_sendable_class(env, name, SIZE_MAX, Constructor, nullptr, size, props, nullptr, &thisVar) != napi_ok) { + IMAGE_ERR("Define class failed"); + return exports; + } + + if (sConstructor_ != nullptr) { + napi_delete_reference(env, sConstructor_); + sConstructor_ = nullptr; + } + + if (napi_create_reference(env, thisVar, NUM1, &sConstructor_) != napi_ok) { + IMAGE_ERR("Create reference failed"); + return exports; + } + + if (napi_set_named_property(env, exports, name, thisVar) != napi_ok) { + IMAGE_ERR("Define class failed"); + return exports; + } + + IMAGE_DEBUG("Init success"); + return exports; +} + + +std::shared_ptr SendableImageNapi::GetNativeImage(napi_env env, napi_value image) +{ + SendableImageNapi* napi = nullptr; + + napi_status status = napi_unwrap_sendable(env, image, reinterpret_cast(&napi)); + if (!IMG_IS_OK(status) || napi == nullptr) { + IMAGE_ERR("GetImage napi unwrap failed"); + return nullptr; + } + return napi->native_; +} + +napi_value SendableImageNapi::Constructor(napi_env env, napi_callback_info info) +{ + napi_status status; + napi_value thisVar = nullptr; + napi_value undefineVar; + size_t argc = NUM1; + napi_value argv[NUM1]; + + IMAGE_FUNCTION_IN(); + napi_get_undefined(env, &undefineVar); + status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr); + if (status != napi_ok || thisVar == nullptr || argc != NUM1) { + IMAGE_ERR("Constructor Failed to napi_get_cb_info"); + return undefineVar; + } + std::string id; + if (!ImageNapiUtils::GetUtf8String(env, argv[NUM0], id) || (id.size() == NUM0)) { + IMAGE_ERR("Failed to parse native image id"); + return undefineVar; + } + std::unique_ptr napi = std::make_unique(); + napi->native_ = sNativeImageHolder_.get(id); + napi->isTestImage_ = false; + if (napi->native_ == nullptr) { + if (MY_NAME.compare(id.c_str()) == 0) { + napi->isTestImage_ = true; + } else { + IMAGE_ERR("Failed to get native image"); + return undefineVar; + } + } + status = napi_wrap_sendable(env, thisVar, + reinterpret_cast(napi.get()), SendableImageNapi::Destructor, nullptr, nullptr); + if (status != napi_ok) { + IMAGE_ERR("Failure wrapping js to native napi"); + return undefineVar; + } + + napi.release(); + IMAGE_FUNCTION_OUT(); + return thisVar; +} + +void SendableImageNapi::Destructor(napi_env env, void *nativeObject, void *finalize) +{ + if (nativeObject != nullptr) { + delete reinterpret_cast(nativeObject); + } +} + +napi_value SendableImageNapi::Create(napi_env env) +{ + napi_value constructor = nullptr; + napi_value result = nullptr; + napi_value argv[NUM1]; + + IMAGE_FUNCTION_IN(); + if (env == nullptr) { + IMAGE_ERR("Input args is invalid"); + return nullptr; + } + if (napi_get_reference_value(env, sConstructor_, &constructor) == napi_ok && constructor != nullptr) { + if (napi_create_string_utf8(env, MY_NAME.c_str(), NAPI_AUTO_LENGTH, &(argv[NUM0])) != napi_ok) { + IMAGE_ERR("Create native image id Failed"); + } + if (napi_new_instance(env, constructor, NUM1, argv, &result) != napi_ok) { + IMAGE_ERR("New instance could not be obtained"); + } + } + IMAGE_FUNCTION_OUT(); + return result; +} +napi_value SendableImageNapi::Create(napi_env env, std::shared_ptr nativeImage) +{ + napi_value constructor = nullptr; + napi_value result = nullptr; + napi_value argv[NUM1]; + + IMAGE_FUNCTION_IN(); + if (env == nullptr || nativeImage == nullptr) { + IMAGE_ERR("Input args is invalid %{public}p vs %{public}p", env, nativeImage.get()); + return nullptr; + } + if (napi_get_reference_value(env, sConstructor_, &constructor) == napi_ok && constructor != nullptr) { + auto id = sNativeImageHolder_.save(nativeImage); + if (napi_create_string_utf8(env, id.c_str(), NAPI_AUTO_LENGTH, &(argv[NUM0])) != napi_ok) { + IMAGE_ERR("Create native image id Failed"); + } + if (napi_new_instance(env, constructor, NUM1, argv, &result) != napi_ok) { + IMAGE_ERR("New instance could not be obtained"); + } + } + IMAGE_FUNCTION_OUT(); + return result; +} +static inline bool JsCheckObjectType(napi_env env, napi_value value, napi_valuetype type) +{ + return (ImageNapiUtils::getType(env, value) == type); +} + +static inline bool JsGetCallbackFunc(napi_env env, napi_value value, napi_ref *result) +{ + if (JsCheckObjectType(env, value, napi_function)) { + napi_create_reference(env, value, NUM1, result); + return true; + } + return false; +} + +static inline bool JsGetInt32Args(napi_env env, napi_value value, int *result) +{ + if (JsCheckObjectType(env, value, napi_number)) { + napi_get_value_int32(env, value, result); + return true; + } + return false; +} +using AsyncExecCallback = void (*)(napi_env env, SendableImageAsyncContext* ctx); +using AsyncCompleteCallback = void (*)(napi_env env, napi_status status, SendableImageAsyncContext* ctx); +static bool JsCreateWork(napi_env env, const char* name, AsyncExecCallback exec, + AsyncCompleteCallback complete, SendableImageAsyncContext* ctx) +{ + napi_value resource = nullptr; + napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &resource); + napi_status status = napi_create_async_work( + env, nullptr, resource, reinterpret_cast(exec), + reinterpret_cast(complete), static_cast(ctx), &(ctx->work)); + if (status != napi_ok) { + IMAGE_ERR("fail to create async work %{public}d", status); + return false; + } + + if (napi_queue_async_work(env, ctx->work) != napi_ok) { + IMAGE_ERR("fail to queue async work"); + return false; + } + return true; +} + +NativeImage* SendableImageNapi::GetNative() +{ + if (native_ != nullptr) { + return native_.get(); + } + return nullptr; +} + +static std::unique_ptr UnwrapContext(napi_env env, napi_callback_info info, + size_t* argc = nullptr, napi_value* argv = nullptr) +{ + napi_value thisVar = nullptr; + size_t tmp = NUM0; + + IMAGE_FUNCTION_IN(); + + if (napi_get_cb_info(env, info, (argc == nullptr)?&tmp:argc, argv, &thisVar, nullptr) != napi_ok) { + IMAGE_ERR("Fail to napi_get_cb_info"); + return nullptr; + } + + std::unique_ptr ctx = std::make_unique(); + if (napi_unwrap_sendable(env, thisVar, reinterpret_cast(&ctx->napi)) != napi_ok || ctx->napi == nullptr) { + IMAGE_ERR("fail to unwrap constructor_"); + return nullptr; + } + ctx->image = ctx->napi->GetNative(); + napi_create_reference(env, thisVar, NUM1, &(ctx->thisRef)); + return ctx; +} + +static inline void ProcessPromise(napi_env env, napi_deferred deferred, napi_value* result, bool resolved) +{ + if (resolved) { + napi_resolve_deferred(env, deferred, result[NUM1]); + } else { + napi_reject_deferred(env, deferred, result[NUM0]); + } +} +static inline void ProcessCallback(napi_env env, napi_ref ref, napi_value* result) +{ + napi_value retVal; + napi_value callback; + napi_get_reference_value(env, ref, &callback); + napi_call_function(env, nullptr, callback, NUM2, result, &retVal); + napi_delete_reference(env, ref); +} +static void CommonCallbackRoutine(napi_env env, SendableImageAsyncContext* &context, const napi_value &valueParam) +{ + IMAGE_FUNCTION_IN(); + napi_value result[2] = {0}; + + if (context == nullptr) { + IMAGE_ERR("context is nullptr"); + return; + } + + if (context->status == SUCCESS) { + napi_create_uint32(env, context->status, &result[0]); + result[1] = valueParam; + } else { + ImageNapiUtils::CreateErrorObj(env, result[0], context->status, + "There is generic napi failure!"); + napi_get_undefined(env, &result[1]); + } + + if (context->deferred) { + ProcessPromise(env, context->deferred, result, context->status == SUCCESS); + } else { + ProcessCallback(env, context->callbackRef, result); + } + + napi_delete_async_work(env, context->work); + + delete context; + context = nullptr; + IMAGE_FUNCTION_OUT(); +} + +static void BuildIntProperty(napi_env env, const std::string &name, + int32_t val, napi_value result) +{ + napi_value nVal; + napi_create_int32(env, val, &nVal); + napi_set_named_property(env, result, name.c_str(), nVal); +} + +static napi_value BuildJsSize(napi_env env, int32_t width, int32_t height) +{ + napi_value result = nullptr; + + napi_create_object(env, &result); + + BuildIntProperty(env, "width", width, result); + BuildIntProperty(env, "height", height, result); + return result; +} + +static napi_value BuildJsRegion(napi_env env, int32_t width, + int32_t height, int32_t x, int32_t y) +{ + napi_value result = nullptr; + + napi_create_object(env, &result); + + napi_set_named_property(env, result, "size", BuildJsSize(env, width, height)); + + BuildIntProperty(env, "x", x, result); + BuildIntProperty(env, "y", y, result); + return result; +} + +napi_value SendableImageNapi::JSGetClipRect(napi_env env, napi_callback_info info) +{ + napi_value result = nullptr; + + IMAGE_FUNCTION_IN(); + napi_get_undefined(env, &result); + std::unique_ptr context = UnwrapContext(env, info); + if (context != nullptr && context->napi != nullptr && context->napi->isTestImage_) { + const int32_t WIDTH = 8192; + const int32_t HEIGHT = 8; + return BuildJsRegion(env, WIDTH, HEIGHT, NUM0, NUM0); + } + if (context == nullptr || context->image == nullptr) { + IMAGE_ERR("Image surface buffer is nullptr"); + return result; + } + + int32_t width = NUM0; + int32_t height = NUM0; + if (context->image->GetSize(width, height) != SUCCESS) { + IMAGE_ERR("Image native get size failed"); + return result; + } + return BuildJsRegion(env, width, height, NUM0, NUM0); +} + +napi_value SendableImageNapi::JsGetSize(napi_env env, napi_callback_info info) +{ + napi_value result = nullptr; + + IMAGE_FUNCTION_IN(); + napi_get_undefined(env, &result); + std::unique_ptr context = UnwrapContext(env, info); + if (context != nullptr && context->napi != nullptr && context->napi->isTestImage_) { + const int32_t WIDTH = 8192; + const int32_t HEIGHT = 8; + return BuildJsSize(env, WIDTH, HEIGHT); + } + if (context == nullptr || context->image == nullptr) { + IMAGE_ERR("Image surface buffer is nullptr"); + return result; + } + + int32_t width = NUM0; + int32_t height = NUM0; + if (context->image->GetSize(width, height) != SUCCESS) { + IMAGE_ERR("Image native get size failed"); + return result; + } + return BuildJsSize(env, width, height); +} + +napi_value SendableImageNapi::JsGetFormat(napi_env env, napi_callback_info info) +{ + napi_value result = nullptr; + + IMAGE_FUNCTION_IN(); + napi_get_undefined(env, &result); + std::unique_ptr context = UnwrapContext(env, info); + if (context != nullptr && context->napi != nullptr && context->napi->isTestImage_) { + const int32_t FORMAT = 12; + napi_create_int32(env, FORMAT, &result); + return result; + } + if (context == nullptr || context->image == nullptr) { + IMAGE_ERR("Image surface buffer is nullptr"); + return result; + } + + int32_t format = NUM0; + if (context->image->GetFormat(format) != SUCCESS) { + IMAGE_ERR("Image native get format failed"); + return result; + } + + napi_create_int32(env, format, &result); + return result; +} + +napi_value SendableImageNapi::JsGetTimestamp(napi_env env, napi_callback_info info) +{ + napi_value result = nullptr; + + IMAGE_FUNCTION_IN(); + napi_get_undefined(env, &result); + std::unique_ptr context = UnwrapContext(env, info); + if (context == nullptr || context->image == nullptr) { + IMAGE_ERR("context is nullptr or Image native is nullptr"); + return result; + } + + int64_t timestamp = 0; + if (context->image->GetTimestamp(timestamp) != SUCCESS) { + IMAGE_ERR("Image native get timestamp failed"); + return result; + } + + napi_create_int64(env, timestamp, &result); + return result; +} + +static void JSReleaseCallBack(napi_env env, napi_status status, + SendableImageAsyncContext* context) +{ + IMAGE_FUNCTION_IN(); + napi_value result = nullptr; + napi_get_undefined(env, &result); + + if (context == nullptr) { + IMAGE_ERR("context is nullptr"); + return; + } + + if (context->thisRef != nullptr) { + napi_value thisVar; + napi_get_reference_value(env, context->thisRef, &thisVar); + napi_delete_reference(env, context->thisRef); + if (thisVar != nullptr) { + SendableImageNapi *tmp = nullptr; + auto status_ = napi_remove_wrap(env, thisVar, reinterpret_cast(&tmp)); + if (status_ != napi_ok) { + IMAGE_ERR("NAPI remove wrap failed status %{public}d", status_); + } + } + } + + context->status = SUCCESS; + IMAGE_FUNCTION_OUT(); + CommonCallbackRoutine(env, context, result); +} + +napi_value SendableImageNapi::JsRelease(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + napi_value result = nullptr; + size_t argc = NUM1; + napi_value argv[NUM1] = {0}; + + napi_get_undefined(env, &result); + auto context = UnwrapContext(env, info, &argc, argv); + if (context == nullptr) { + IMAGE_ERR("fail to unwrap constructor_"); + return result; + } + if (argc == NUM1) { + if (!JsGetCallbackFunc(env, argv[NUM0], &(context->callbackRef))) { + IMAGE_ERR("Unsupport arg 0 type"); + return result; + } + } else { + napi_create_promise(env, &(context->deferred), &result); + } + + if (JsCreateWork(env, "JsRelease", [](napi_env env, SendableImageAsyncContext* data) {}, + JSReleaseCallBack, context.get())) { + context.release(); + } + IMAGE_FUNCTION_OUT(); + return result; +} + +static bool CreateArrayBuffer(napi_env env, uint8_t* src, size_t srcLen, napi_value *res) +{ + if (src == nullptr || srcLen == 0) { + return false; + } + auto status = napi_create_external_arraybuffer(env, src, srcLen, + [](napi_env env, void* data, void* hint) { }, nullptr, res); + if (status != napi_ok) { + return false; + } + return true; +} + +static inline bool IsEqual(const int32_t& check, ImageFormat format) +{ + return (check == int32_t(format)); +} +static inline bool IsEqual(const int32_t& check, ComponentType type) +{ + return (check == int32_t(type)); +} +static inline bool IsYUVComponent(const int32_t& type) +{ + return (IsEqual(type, ComponentType::YUV_Y) || + IsEqual(type, ComponentType::YUV_U) || + IsEqual(type, ComponentType::YUV_V)); +} +static inline bool IsYUV422SPImage(int32_t format) +{ + return (IsEqual(format, ImageFormat::YCBCR_422_SP) || + (format == int32_t(GRAPHIC_PIXEL_FMT_YCBCR_422_SP))); +} +static inline bool CheckComponentType(const int32_t& type, int32_t format) +{ + return ((IsYUV422SPImage(format) && IsYUVComponent(type)) || + (!IsYUV422SPImage(format) && IsEqual(type, ComponentType::JPEG))); +} + +static bool BuildJsComponentObject(napi_env env, int32_t type, uint8_t* buffer, + NativeComponent* component, napi_value* result) +{ + napi_value array; + if (!CreateArrayBuffer(env, buffer, component->size, &array)) { + return false; + } + napi_create_object(env, result); + napi_set_named_property(env, *result, "byteBuffer", array); + BuildIntProperty(env, "componentType", type, *result); + BuildIntProperty(env, "rowStride", component->rowStride, *result); + BuildIntProperty(env, "pixelStride", component->pixelStride, *result); + return true; +} +static void TestGetComponentCallBack(napi_env env, napi_status status, SendableImageAsyncContext* context) +{ + if (context == nullptr) { + IMAGE_ERR("Invalid input context"); + return; + } + napi_value result; + napi_value array; + void *nativePtr = nullptr; + if (napi_create_arraybuffer(env, NUM1, &nativePtr, &array) != napi_ok || nativePtr == nullptr) { + return; + } + napi_create_object(env, &result); + napi_set_named_property(env, result, "byteBuffer", array); + BuildIntProperty(env, "componentType", context->componentType, result); + BuildIntProperty(env, "rowStride", NUM0, result); + BuildIntProperty(env, "pixelStride", NUM0, result); + context->status = SUCCESS; + CommonCallbackRoutine(env, context, result); +} + +static void JsGetComponentCallBack(napi_env env, napi_status status, SendableImageAsyncContext* context) +{ + IMAGE_FUNCTION_IN(); + napi_value result; + napi_get_undefined(env, &result); + + if (context != nullptr && context->napi != nullptr && context->isTestContext) { + TestGetComponentCallBack(env, status, context); + return; + } + + if (context == nullptr) { + IMAGE_ERR("Invalid input context"); + return; + } + context->status = ERROR; + NativeComponent* component = context->component; + if (component == nullptr) { + IMAGE_ERR("Invalid component"); + CommonCallbackRoutine(env, context, result); + return; + } + + uint8_t *buffer = nullptr; + if (component->virAddr != nullptr) { + buffer = component->virAddr; + } else { + buffer = component->raw.data(); + } + + if (buffer == nullptr || component->size == NUM0) { + IMAGE_ERR("Invalid buffer"); + CommonCallbackRoutine(env, context, result); + return; + } + + if (BuildJsComponentObject(env, context->componentType, buffer, component, &result)) { + context->status = SUCCESS; + } else { + IMAGE_ERR("napi_create_arraybuffer failed!"); + } + + IMAGE_FUNCTION_OUT(); + CommonCallbackRoutine(env, context, result); +} +static void JsGetComponentExec(napi_env env, SendableImageAsyncContext* context) +{ + if (context == nullptr || context->napi == nullptr) { + IMAGE_ERR("Invalid input context"); + return; + } + + auto native = context->napi->GetNative(); + if (native == nullptr) { + IMAGE_ERR("Empty native"); + return; + } + context->component = native->GetComponent(context->componentType); +} + +static bool JsGetComponentArgs(napi_env env, size_t argc, napi_value* argv, SendableImageAsyncContext* context) +{ + if (argv == nullptr || context == nullptr || argc < NUM1 || context->napi == nullptr) { + IMAGE_ERR("argv is nullptr"); + return false; + } + + if (!JsGetInt32Args(env, argv[NUM0], &(context->componentType))) { + IMAGE_ERR("Unsupport arg 0 type"); + return false; + } + + auto native = context->napi->GetNative(); + if (native == nullptr && !context->isTestContext) { + IMAGE_ERR("native is nullptr"); + return false; + } + + int32_t format = NUM0; + if (context->isTestContext) { + const int32_t TEST_FORMAT = 12; + format = TEST_FORMAT; + } else { + native->GetFormat(format); + } + + if (!CheckComponentType(context->componentType, format)) { + IMAGE_ERR("Unsupport component type 0 value: %{public}d", context->componentType); + return false; + } + + if (argc == NUM2 && !JsGetCallbackFunc(env, argv[NUM1], &(context->callbackRef))) { + IMAGE_ERR("Unsupport arg 1 type"); + return false; + } + return true; +} + +napi_value SendableImageNapi::JsGetComponent(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + napi_value result = nullptr; + size_t argc = NUM2; + napi_value argv[NUM2] = {0}; + + napi_get_undefined(env, &result); + auto context = UnwrapContext(env, info, &argc, argv); + if (context == nullptr) { + return ImageNapiUtils::ThrowExceptionError(env, static_cast(napi_invalid_arg), + "fail to unwrap constructor_ "); + } + context->isTestContext = context->napi->isTestImage_; + if (!JsGetComponentArgs(env, argc, argv, context.get())) { + return ImageNapiUtils::ThrowExceptionError(env, static_cast(napi_invalid_arg), + "Unsupport arg type!"); + } + + if (context->callbackRef == nullptr) { + napi_create_promise(env, &(context->deferred), &result); + } + + if (JsCreateWork(env, "JsGetComponent", JsGetComponentExec, JsGetComponentCallBack, context.get())) { + context.release(); + } + + IMAGE_FUNCTION_OUT(); + return result; +} +} // namespace Media +} // namespace OHOS diff --git a/frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp b/frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp new file mode 100644 index 000000000..145380877 --- /dev/null +++ b/frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp @@ -0,0 +1,1036 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sendable_image_receiver_napi.h" +#include +#include "media_errors.h" +#include "image_log.h" +#include "image_napi_utils.h" +#include "sendable_image_napi.h" +#include "image_receiver_context.h" +#include "image_receiver_manager.h" + +#undef LOG_DOMAIN +#define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE + +#undef LOG_TAG +#define LOG_TAG "SendableImageReceiverNapi" + +using std::string; +using std::shared_ptr; +using std::unique_ptr; +using std::vector; +using std::make_shared; +using std::make_unique; + +namespace OHOS { +namespace Media { +static const std::string CLASS_NAME = "ImageReceiver"; +static const std::string DEVICE_ERRCODE = "001"; +shared_ptr SendableImageReceiverNapi::staticInstance_ = nullptr; +thread_local napi_ref SendableImageReceiverNapi::sConstructor_ = nullptr; +using SurfaceListener = SurfaceBufferAvaliableListener; + +const int ARGS0 = 0; +const int ARGS1 = 1; +const int ARGS2 = 2; +const int ARGS3 = 3; +const int ARGS4 = 4; +const int PARAM0 = 0; +const int PARAM1 = 1; +const int PARAM2 = 2; +const int PARAM3 = 3; + +struct ImageEnum { + std::string name; + int32_t numVal; + std::string strVal; +}; + +static std::vector sImageFormatMap = { + {"CAMERA_APP_INNER", 4, ""}, + {"JPEG", 2000, ""}, +}; + +SendableImageReceiverNapi::SendableImageReceiverNapi():env_(nullptr) +{} + +SendableImageReceiverNapi::~SendableImageReceiverNapi() +{ + release(); +} + +static void CommonCallbackRoutine(napi_env env, Context &context, const napi_value &valueParam, bool isRelease = true) +{ + IMAGE_FUNCTION_IN(); + napi_value result[2] = {0}; + napi_value retVal; + napi_value callback = nullptr; + + napi_get_undefined(env, &result[0]); + napi_get_undefined(env, &result[1]); + + if (context == nullptr) { + IMAGE_ERR("context is nullptr"); + return; + } + + if (context->status == SUCCESS) { + result[1] = valueParam; + } + + if (context->deferred) { + if (context->status == SUCCESS) { + napi_resolve_deferred(env, context->deferred, result[1]); + } else { + napi_reject_deferred(env, context->deferred, result[0]); + } + } else { + napi_create_uint32(env, context->status, &result[0]); + napi_get_reference_value(env, context->callbackRef, &callback); + napi_call_function(env, nullptr, callback, PARAM2, result, &retVal); + } + + if (isRelease) { + if (context->callbackRef != nullptr) { + napi_delete_reference(env, context->callbackRef); + context->callbackRef = nullptr; + } + + napi_delete_async_work(env, context->work); + + delete context; + context = nullptr; + } + IMAGE_FUNCTION_OUT(); +} + +void SendableImageReceiverNapi::NativeRelease() +{ + imageReceiver_ = nullptr; +} + +ImageReceiver* SendableImageReceiverNapi::GetNative() +{ + if (imageReceiver_ != nullptr) { + return imageReceiver_.get(); + } + return nullptr; +} +napi_value SendableImageReceiverNapi::Init(napi_env env, napi_value exports) +{ + IMAGE_FUNCTION_IN(); + napi_property_descriptor props[] = { + DECLARE_NAPI_FUNCTION("getReceivingSurfaceId", JsGetReceivingSurfaceId), + DECLARE_NAPI_FUNCTION("readLatestImage", JsReadLatestImage), + DECLARE_NAPI_FUNCTION("readNextImage", JsReadNextImage), + DECLARE_NAPI_FUNCTION("on", JsOn), + DECLARE_NAPI_FUNCTION("release", JsRelease), +#ifdef IMAGE_DEBUG_FLAG + DECLARE_NAPI_GETTER("test", JsTest), + DECLARE_NAPI_GETTER("checkDeviceTest", JsCheckDeviceTest), + DECLARE_NAPI_GETTER("testYUV", JsTestYUV), +#endif + DECLARE_NAPI_GETTER("size", JsGetSize), + DECLARE_NAPI_GETTER("capacity", JsGetCapacity), + DECLARE_NAPI_GETTER("format", JsGetFormat), + }; + napi_property_descriptor static_prop[] = { + DECLARE_NAPI_STATIC_FUNCTION("createImageReceiver", JSCreateImageReceiver), + }; + + napi_value constructor = nullptr; + size_t props_count = IMG_ARRAY_SIZE(props); + size_t static_props_count = IMG_ARRAY_SIZE(static_prop); + + IMG_NAPI_CHECK_RET_D(IMG_IS_OK( + napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, + nullptr, props_count, props, &constructor)), + nullptr, + IMAGE_ERR("define class fail") + ); + + IMG_NAPI_CHECK_RET_D(IMG_IS_OK( + napi_create_reference(env, constructor, 1, &sConstructor_)), + nullptr, + IMAGE_ERR("create reference fail") + ); + + IMG_NAPI_CHECK_RET_D(IMG_IS_OK( + napi_set_named_property(env, exports, CLASS_NAME.c_str(), constructor)), + nullptr, + IMAGE_ERR("set named property fail") + ); + IMG_NAPI_CHECK_RET_D(IMG_IS_OK( + napi_define_properties(env, exports, static_props_count, static_prop)), + nullptr, + IMAGE_ERR("define properties fail") + ); + + IMAGE_DEBUG("Init success"); + + IMAGE_FUNCTION_OUT(); + return exports; +} + +struct ImageReceiverInputArgs { + napi_value thisVar; + size_t argc; + napi_value argv[ARGS4]; + int32_t args[ARGS4]; +}; + +static bool parseImageReceiverArgs(napi_env env, napi_callback_info info, + ImageReceiverInputArgs &args, std::string &errMsg) +{ + napi_status status = napi_get_cb_info(env, info, &(args.argc), args.argv, &(args.thisVar), nullptr); + if (status != napi_ok) { + IMAGE_ERR("fail to napi_get_cb_info %{public}d", status); + errMsg = "Fail to napi_get_cb_info"; + return false; + } + + if (args.argc != ARGS3 && args.argc != ARGS4) { + errMsg = "Invalid arg counts "; + errMsg.append(std::to_string(args.argc)); + return false; + } + + return ImageNapiUtils::ParseImageCreatorReceiverArgs(env, args.argc, args.argv, args.args, errMsg); +} + +napi_value SendableImageReceiverNapi::Constructor(napi_env env, napi_callback_info info) +{ + napi_value undefineVar = nullptr; + napi_get_undefined(env, &undefineVar); + + IMAGE_FUNCTION_IN(); + std::string errMsg; + ImageReceiverInputArgs inputArgs; + inputArgs.argc = ARGS4; + if (!parseImageReceiverArgs(env, info, inputArgs, errMsg) || inputArgs.thisVar == nullptr) { + IMAGE_ERR("Failure. %{public}s", errMsg.c_str()); + return undefineVar; + } + auto reference = std::make_unique(); + reference->env_ = env; + reference->imageReceiver_ = ImageReceiver::CreateImageReceiver((inputArgs.args)[PARAM0], + (inputArgs.args)[PARAM1], (inputArgs.args)[PARAM2], (inputArgs.args)[PARAM3]); + if (reference->imageReceiver_ == nullptr) { + IMAGE_ERR("Create native image receiver failed"); + return undefineVar; + } + napi_status status = napi_wrap(env, inputArgs.thisVar, reinterpret_cast(reference.get()), + SendableImageReceiverNapi::Destructor, nullptr, nullptr); + if (status == napi_ok) { + reference.release(); + return inputArgs.thisVar; + } + IMAGE_ERR("Failure wrapping js to native napi"); + return undefineVar; +} + +void SendableImageReceiverNapi::Destructor(napi_env env, void *nativeObject, void *finalize) +{ + if (nativeObject != nullptr) { + delete reinterpret_cast(nativeObject); + nativeObject = nullptr; + } +} + +static bool checkFormat(int32_t format) +{ + for (auto imgEnum : sImageFormatMap) { + if (imgEnum.numVal == format) { + return true; + } + } + return false; +} + +napi_value SendableImageReceiverNapi::CreateImageReceiverJsObject(napi_env env, struct SendableImageReceiverCreateArgs args) +{ + napi_status status; + napi_value constructor = nullptr; + napi_value result = nullptr; + ImageReceiverInputArgs inputArgs; + inputArgs.argc = ARGS4; + + IMAGE_FUNCTION_IN(); + if (!checkFormat(args.format)) { + IMAGE_ERR("Invalid type"); + return nullptr; + } + napi_create_int32(env, args.width, &(inputArgs.argv[PARAM0])); + napi_create_int32(env, args.height, &(inputArgs.argv[PARAM1])); + napi_create_int32(env, args.format, &(inputArgs.argv[PARAM2])); + napi_create_int32(env, args.capicity, &(inputArgs.argv[PARAM3])); + status = napi_get_reference_value(env, sConstructor_, &constructor); + if (status != napi_ok || constructor == nullptr) { + IMAGE_ERR("Failed to get reference of constructor"); + return nullptr; + } + + status = napi_new_instance(env, constructor, inputArgs.argc, inputArgs.argv, &result); + if (status != napi_ok || result == nullptr) { + IMAGE_ERR("New instance could not be obtained"); + return nullptr; + } + + IMAGE_FUNCTION_OUT(); + return result; +} + +napi_value SendableImageReceiverNapi::JSCreateImageReceiver(napi_env env, napi_callback_info info) +{ + napi_status status; + napi_value constructor = nullptr; + napi_value result = nullptr; + ImageReceiverInputArgs inputArgs; + inputArgs.argc = ARGS4; + + IMAGE_FUNCTION_IN(); + napi_get_undefined(env, &result); + + std::string errMsg; + if (!parseImageReceiverArgs(env, info, inputArgs, errMsg)) { + return ImageNapiUtils::ThrowExceptionError(env, COMMON_ERR_INVALID_PARAMETER, errMsg); + } + + if (!checkFormat(inputArgs.args[PARAM2])) { + return ImageNapiUtils::ThrowExceptionError(env, COMMON_ERR_INVALID_PARAMETER, "Invalid type"); + } + + status = napi_get_reference_value(env, sConstructor_, &constructor); + if (IMG_IS_OK(status)) { + status = napi_new_instance(env, constructor, inputArgs.argc, inputArgs.argv, &result); + if (status == napi_ok) { + IMAGE_FUNCTION_OUT(); + return result; + } else { + IMAGE_ERR("New instance could not be obtained"); + } + } + + IMAGE_ERR("Failed to get reference of constructor"); + return result; +} + +static bool CheckArgs(const SendableImageReceiverCommonArgs &args) +{ + if (args.async != CallType::GETTER) { + if (args.queryArgs == nullptr) { + IMAGE_ERR("No query args function"); + return false; + } + } + + if (args.async != CallType::ASYNC || args.asyncLater) { + if (args.nonAsyncBack == nullptr) { + IMAGE_ERR("No non async back function"); + return false; + } + } + return true; +} + +static bool PrepareOneArg(SendableImageReceiverCommonArgs &args, struct SendableImageReceiverInnerContext &ic) +{ + if (ic.argc == ARGS1) { + auto argType = ImageNapiUtils::getType(args.env, ic.argv[PARAM0]); + if (argType == napi_function) { + napi_create_reference(args.env, ic.argv[PARAM0], ic.refCount, &(ic.context->callbackRef)); + } else { + IMAGE_ERR("Unsupport arg 0 type: %{public}d", argType); + return false; + } + } + + if (ic.context->callbackRef == nullptr) { + napi_create_promise(args.env, &(ic.context->deferred), &(ic.result)); + } else { + napi_get_undefined(args.env, &ic.result); + } + return true; +} + +napi_value SendableImageReceiverNapi::JSCommonProcess(SendableImageReceiverCommonArgs &args) +{ + IMAGE_FUNCTION_IN(); + struct SendableImageReceiverInnerContext ic; + ic.argc = args.argc; + ic.argv.resize(ic.argc); + napi_get_undefined(args.env, &ic.result); + + IMG_NAPI_CHECK_RET(CheckArgs(args), ic.result); + + IMG_JS_ARGS(args.env, args.info, ic.status, ic.argc, &(ic.argv[0]), ic.thisVar); + + IMG_NAPI_CHECK_RET_D(IMG_IS_OK(ic.status), ic.result, IMAGE_ERR("fail to napi_get_cb_info")); + + if (args.async != CallType::STATIC) { + ic.context = std::make_unique(); + if (ic.context == nullptr) { + return ic.result; + } + ic.status = napi_unwrap(args.env, ic.thisVar, reinterpret_cast(&(ic.context->constructor_))); + + IMG_NAPI_CHECK_RET_D(IMG_IS_READY(ic.status, ic.context->constructor_), + ic.result, IMAGE_ERR("fail to unwrap context")); + + if (ic.context->constructor_ == nullptr) { + return ic.result; + } + + IMG_NAPI_CHECK_RET_D(IMG_IS_READY(ic.status, ic.context->constructor_->imageReceiver_), + ic.result, IMAGE_ERR("empty native receiver")); + } + if (args.async != CallType::GETTER) { + if (!args.queryArgs(args, ic)) { + return ic.result; + } + } + + if (args.async == CallType::ASYNC) { + if (args.asyncLater) { + args.nonAsyncBack(args, ic); + } else { + napi_value _resource = nullptr; + napi_create_string_utf8((args.env), (args.name.c_str()), NAPI_AUTO_LENGTH, &_resource); + (ic.status) = napi_create_async_work(args.env, nullptr, _resource, + ([](napi_env env, void *data) {}), + (reinterpret_cast(args.callBack)), + static_cast((ic.context).get()), &(ic.context->work)); + napi_queue_async_work((args.env), (ic.context->work)); + ic.context.release(); + } + } else { + args.nonAsyncBack(args, ic); + } + + IMAGE_FUNCTION_OUT(); + return ic.result; +} + +static napi_value BuildJsSize(napi_env env, int32_t width, int32_t height) +{ + napi_value result = nullptr; + napi_value sizeWith = nullptr; + napi_value sizeHeight = nullptr; + + napi_create_object(env, &result); + + napi_create_int32(env, width, &sizeWith); + napi_set_named_property(env, result, "width", sizeWith); + + napi_create_int32(env, height, &sizeHeight); + napi_set_named_property(env, result, "height", sizeHeight); + return result; +} + +napi_value SendableImageReceiverNapi::JsGetSize(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::GETTER, + }; + args.argc = ARGS0; + + args.nonAsyncBack = [](SendableImageReceiverCommonArgs &args, SendableImageReceiverInnerContext &ic) -> bool { + napi_get_undefined(args.env, &(ic.result)); + auto native = ic.context->constructor_->imageReceiver_; + if (native == nullptr) { + IMAGE_ERR("Native instance is nullptr"); + return false; + } + + if (native->iraContext_ == nullptr) { + IMAGE_ERR("Image receiver context is nullptr"); + return false; + } + ic.result = BuildJsSize(args.env, + native->iraContext_->GetWidth(), + native->iraContext_->GetHeight()); + return true; + }; + + return JSCommonProcess(args); +} + +napi_value SendableImageReceiverNapi::JsGetCapacity(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::GETTER, + }; + args.argc = ARGS0; + + args.nonAsyncBack = [](SendableImageReceiverCommonArgs &args, SendableImageReceiverInnerContext &ic) -> bool { + napi_get_undefined(args.env, &(ic.result)); + auto native = ic.context->constructor_->imageReceiver_; + if (native == nullptr) { + IMAGE_ERR("Native instance is nullptr"); + return false; + } + + if (native->iraContext_ == nullptr) { + IMAGE_ERR("Image receiver context is nullptr"); + return false; + } + napi_create_int32(args.env, native->iraContext_->GetCapicity(), &(ic.result)); + return true; + }; + + return JSCommonProcess(args); +} + +napi_value SendableImageReceiverNapi::JsGetFormat(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::GETTER, + }; + args.argc = ARGS0; + + args.nonAsyncBack = [](SendableImageReceiverCommonArgs &args, SendableImageReceiverInnerContext &ic) -> bool { + napi_get_undefined(args.env, &(ic.result)); + auto native = ic.context->constructor_->imageReceiver_; + if (native == nullptr) { + IMAGE_ERR("Native instance is nullptr"); + return false; + } + + if (native->iraContext_ == nullptr) { + IMAGE_ERR("Image receiver context is nullptr"); + return false; + } + napi_create_int32(args.env, native->iraContext_->GetFormat(), &(ic.result)); + return true; + }; + + return JSCommonProcess(args); +} + +#ifdef IMAGE_DEBUG_FLAG +static void TestRequestBuffer(OHOS::sptr &receiverSurface, + OHOS::BufferRequestConfig requestConfig, + OHOS::BufferFlushConfig flushConfig) +{ + OHOS::sptr buffer; + int32_t releaseFence; + if (receiverSurface == nullptr) { + IMAGE_ERR("Image receiver receiverSurface is nullptr"); + return; + } + requestConfig.width = receiverSurface->GetDefaultWidth(); + requestConfig.height = receiverSurface->GetDefaultHeight(); + receiverSurface->RequestBuffer(buffer, releaseFence, requestConfig); + if (buffer == nullptr) { + IMAGE_ERR("Image receiver buffer is nullptr"); + return; + } + IMAGE_ERR("RequestBuffer"); + int32_t *p = reinterpret_cast(buffer->GetVirAddr()); + int32_t size = static_cast(buffer->GetSize() / 4); + if (p != nullptr) { + for (int32_t i = 0; i < size; i++) { + p[i] = i; + } + } + receiverSurface->FlushBuffer(buffer, -1, flushConfig); + IMAGE_ERR("FlushBuffer"); +} + +static void DoTest(std::shared_ptr imageReceiver, int pixelFormat) +{ + OHOS::BufferRequestConfig requestConfig = { + .width = 0x100, + .height = 0x100, + .strideAlignment = 0x8, + .format = pixelFormat, + .usage = HBM_USE_CPU_READ | HBM_USE_CPU_WRITE | HBM_USE_MEM_DMA, + .timeout = 0, + }; + + OHOS::BufferFlushConfig flushConfig = { + .damage = { + .w = 0x100, + .h = 0x100, + }, + }; + + if (imageReceiver == nullptr || imageReceiver->iraContext_ == nullptr) { + IMAGE_ERR("Image receiver DoTest imageReceiver is nullptr"); + return; + } + std::string receiveKey = imageReceiver->iraContext_->GetReceiverKey(); + IMAGE_ERR("ReceiverKey = %{public}s", receiveKey.c_str()); + OHOS::sptr receiverSurface = ImageReceiver::getSurfaceById(receiveKey); + if (receiverSurface == nullptr) { + IMAGE_ERR("Image receiver DoTest receiverSurface is nullptr"); + return; + } + IMAGE_ERR("getDefaultWidth = %{public}d", receiverSurface->GetDefaultWidth()); + IMAGE_ERR("getDefaultHeight = %{public}d", receiverSurface->GetDefaultHeight()); + IMAGE_ERR("TestRequestBuffer 1 ..."); + TestRequestBuffer(receiverSurface, requestConfig, flushConfig); + IMAGE_ERR("TestRequestBuffer 2 ..."); + TestRequestBuffer(receiverSurface, requestConfig, flushConfig); + IMAGE_ERR("TestRequestBuffer 3 ..."); + TestRequestBuffer(receiverSurface, requestConfig, flushConfig); +} + +napi_value SendableImageReceiverNapi::JsTest(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::GETTER, + }; + args.argc = ARGS0; + + args.nonAsyncBack = [](SendableImageReceiverCommonArgs &args, SendableImageReceiverInnerContext &ic) -> bool { + ic.context->constructor_->isCallBackTest = true; + DoTest(ic.context->constructor_->imageReceiver_, PIXEL_FMT_RGBA_8888); + return true; + }; + + return JSCommonProcess(args); +} + +napi_value SendableImageReceiverNapi::JsCheckDeviceTest(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::GETTER, + }; + args.argc = ARGS0; + + args.nonAsyncBack = [](SendableImageReceiverCommonArgs &args, SendableImageReceiverInnerContext &ic) -> bool { + napi_get_undefined(args.env, &(ic.result)); + napi_value mess = nullptr; + ic.context->constructor_->isCallBackTest = true; + napi_create_string_utf8(args.env, DEVICE_ERRCODE.c_str(), NAPI_AUTO_LENGTH, &mess); + ic.result = mess; + if (args.async != CallType::GETTER) { + DoTest(ic.context->constructor_->imageReceiver_, PIXEL_FMT_RGBA_8888); + } + return true; + }; + + return JSCommonProcess(args); +} + +napi_value SendableImageReceiverNapi::JsTestYUV(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::GETTER, + }; + args.argc = ARGS0; + + args.nonAsyncBack = [](SendableImageReceiverCommonArgs &args, SendableImageReceiverInnerContext &ic) -> bool { + ic.context->constructor_->isCallBackTest = true; + DoTest(ic.context->constructor_->imageReceiver_, PIXEL_FMT_YCBCR_422_SP); + return true; + }; + + return JSCommonProcess(args); +} +#endif + +napi_value SendableImageReceiverNapi::JsGetReceivingSurfaceId(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::ASYNC, + .name = "JsGetReceivingSurfaceId", + .callBack = nullptr, + .argc = ARGS1, + .queryArgs = PrepareOneArg, + }; + + args.callBack = [](napi_env env, napi_status status, Context context) { + IMAGE_LINE_IN(); + napi_value result = nullptr; + napi_get_undefined(env, &result); + + auto native = context->constructor_->imageReceiver_; + if (native == nullptr) { + IMAGE_ERR("Native instance is nullptr"); + context->status = ERR_IMAGE_INIT_ABNORMAL; + } else if (native->iraContext_ == nullptr) { + IMAGE_ERR("Image receiver context is nullptr"); + context->status = ERR_IMAGE_INIT_ABNORMAL; + } else { + napi_create_string_utf8(env, native->iraContext_->GetReceiverKey().c_str(), + NAPI_AUTO_LENGTH, &result); + context->status = SUCCESS; + } + + IMAGE_LINE_OUT(); + CommonCallbackRoutine(env, context, result); + }; + + return JSCommonProcess(args); +} + +#ifdef IMAGE_SAVE_BUFFER_TO_PIC +static void DoCallBackTest(OHOS::sptr surfaceBuffer1) +{ + if (surfaceBuffer1 == nullptr) { + IMAGE_ERR("surfaceBuffer1 is null"); + return; + } + + ImageReceiverManager& imageReceiverManager = ImageReceiverManager::getInstance(); + shared_ptr imageReceiver1 = imageReceiverManager.getImageReceiverByKeyId("1"); + if (imageReceiver1 == nullptr || imageReceiver1->iraContext_ == nullptr) { + return; + } + IMAGE_ERR("DoCallBackTest format %{public}d", imageReceiver1->iraContext_->GetFormat()); + + InitializationOptions opts; + opts.size.width = surfaceBuffer1->GetWidth(); + opts.size.height = surfaceBuffer1->GetHeight(); + opts.pixelFormat = OHOS::Media::PixelFormat::BGRA_8888; + opts.alphaType = OHOS::Media::AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN; + opts.scaleMode = OHOS::Media::ScaleMode::CENTER_CROP; + opts.editable = true; + IMAGE_ERR("DoCallBackTest Width %{public}d", opts.size.width); + IMAGE_ERR("DoCallBackTest Height %{public}d", opts.size.height); + int fd = open("/data/receiver/test.jpg", O_RDWR | O_CREAT); + imageReceiver1->SaveBufferAsImage(fd, surfaceBuffer1, opts); +} +#endif +static void FailedCallbackRoutine(napi_env env, Context &context, uint32_t errCode) +{ + napi_value result = nullptr; + napi_get_undefined(env, &result); + if (context != nullptr) { + context->status = ERR_IMAGE_INIT_ABNORMAL; + } + CommonCallbackRoutine(env, context, result); +} +napi_value SendableImageReceiverNapi::JsReadLatestImage(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::ASYNC, + .name = "JsReadLatestImage", + .callBack = nullptr, + .argc = ARGS1, + .queryArgs = PrepareOneArg, + }; + + args.callBack = [](napi_env env, napi_status status, Context context) { + IMAGE_LINE_IN(); + auto native = context->constructor_->imageReceiver_; + if (native == nullptr) { + IMAGE_ERR("Native instance is nullptr"); + FailedCallbackRoutine(env, context, ERR_IMAGE_INIT_ABNORMAL); + return; + } + auto image = native->LastNativeImage(); + if (image == nullptr) { + IMAGE_ERR("LastNativeImage is nullptr"); + FailedCallbackRoutine(env, context, ERR_IMAGE_INIT_ABNORMAL); + return; + } +#ifdef IMAGE_DEBUG_FLAG + if (context->constructor_->isCallBackTest) { + context->constructor_->isCallBackTest = false; +#ifdef IMAGE_SAVE_BUFFER_TO_PIC + DoCallBackTest(image->GetBuffer()); +#endif + } +#endif + napi_value result = SendableImageNapi::Create(env, image); + if (result == nullptr) { + IMAGE_ERR("SendableImageNapi Create is nullptr"); + FailedCallbackRoutine(env, context, ERR_IMAGE_INIT_ABNORMAL); + return; + } + context->status = SUCCESS; + IMAGE_LINE_OUT(); + CommonCallbackRoutine(env, context, result); + }; + + return JSCommonProcess(args); +} + +napi_value SendableImageReceiverNapi::JsReadNextImage(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::ASYNC, + .name = "JsReadNextImage", + .callBack = nullptr, + .argc = ARGS1, + .queryArgs = PrepareOneArg, + }; + + args.callBack = [](napi_env env, napi_status status, Context context) { + IMAGE_LINE_IN(); + auto native = context->constructor_->imageReceiver_; + if (native == nullptr) { + IMAGE_ERR("Native instance is nullptr"); + FailedCallbackRoutine(env, context, ERR_IMAGE_INIT_ABNORMAL); + return; + } + auto image = native->NextNativeImage(); + if (image == nullptr) { + IMAGE_ERR("NextNativeImage is nullptr"); + FailedCallbackRoutine(env, context, ERR_IMAGE_INIT_ABNORMAL); + return; + } +#ifdef IMAGE_DEBUG_FLAG + if (context->constructor_->isCallBackTest) { + context->constructor_->isCallBackTest = false; +#ifdef IMAGE_SAVE_BUFFER_TO_PIC + DoCallBackTest(image->GetBuffer()); +#endif + } +#endif + napi_value result = SendableImageNapi::Create(env, image); + if (result == nullptr) { + IMAGE_ERR("SendableImageNapi Create is nullptr"); + FailedCallbackRoutine(env, context, ERR_IMAGE_INIT_ABNORMAL); + return; + } + context->status = SUCCESS; + IMAGE_LINE_OUT(); + CommonCallbackRoutine(env, context, result); + }; + + return JSCommonProcess(args); +} + +static bool CheckOnParam0(napi_env env, napi_value value, const std::string& refStr) +{ + bool ret = true; + size_t bufLength = 0; + napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &bufLength); + if (status != napi_ok || bufLength > PATH_MAX) { + return false; + } + + char *buffer = static_cast(malloc((bufLength + 1) * sizeof(char))); + if (buffer == nullptr) { + return false; + } + + status = napi_get_value_string_utf8(env, value, buffer, bufLength + 1, &bufLength); + if (status != napi_ok) { + ret = false; + } else { + std::string strValue = buffer; + if (strValue.compare(refStr) != 0) { + IMAGE_ERR("strValue is %{public}s", strValue.c_str()); + ret = false; + } + } + + free(buffer); + buffer = nullptr; + return ret; +} + +static bool JsOnQueryArgs(SendableImageReceiverCommonArgs &args, SendableImageReceiverInnerContext &ic) +{ + if (ic.argc == ARGS2) { + auto argType0 = ImageNapiUtils::getType(args.env, ic.argv[PARAM0]); + auto argType1 = ImageNapiUtils::getType(args.env, ic.argv[PARAM1]); + if (argType0 == napi_string && argType1 == napi_function) { + if (!ImageNapiUtils::GetUtf8String(args.env, ic.argv[PARAM0], ic.onType)) { + ImageNapiUtils::ThrowExceptionError(args.env, static_cast(napi_invalid_arg), + "Could not get On type string"); + return false; + } + + if (!CheckOnParam0(args.env, ic.argv[PARAM0], "imageArrival")) { + ImageNapiUtils::ThrowExceptionError(args.env, static_cast(napi_invalid_arg), + "Unsupport PARAM0"); + return false; + } + + napi_create_reference(args.env, ic.argv[PARAM1], ic.refCount, &(ic.context->callbackRef)); + } else { + std::string errMsg = "Unsupport args type: "; + ImageNapiUtils::ThrowExceptionError(args.env, static_cast(napi_invalid_arg), + errMsg.append(std::to_string(argType0)).append(std::to_string(argType1))); + return false; + } + } else { + std::string errMsg = "Invalid argc: "; + ImageNapiUtils::ThrowExceptionError(args.env, static_cast(napi_invalid_arg), + errMsg.append(std::to_string(ic.argc))); + return false; + } + + napi_get_undefined(args.env, &ic.result); + return true; +} + +static void Callback(uv_work_t *work, int status) +{ + IMAGE_LINE_IN(); + Context context = reinterpret_cast(work->data); + if (context == nullptr) { + IMAGE_ERR("context is empty"); + } else { + napi_value result[PARAM2] = {0}; + napi_value retVal; + napi_value callback = nullptr; + if (context->env != nullptr && context->callbackRef != nullptr) { + napi_handle_scope scope = nullptr; + napi_open_handle_scope(context->env, &scope); + if (scope == nullptr) { + delete work; + return; + } + napi_create_uint32(context->env, SUCCESS, &result[0]); + napi_get_undefined(context->env, &result[1]); + napi_get_reference_value(context->env, context->callbackRef, &callback); + if (callback != nullptr) { + napi_call_function(context->env, nullptr, callback, PARAM2, result, &retVal); + } else { + IMAGE_ERR("napi_get_reference_value callback is empty"); + } + napi_close_handle_scope(context->env, scope); + } else { + IMAGE_ERR("env or callbackRef is empty"); + } + } + delete work; + IMAGE_LINE_OUT(); +} + +void SendableImageReceiverNapi::DoCallBack(shared_ptr context, + string name, CompleteCallback callBack) +{ + IMAGE_FUNCTION_IN(); + if (context == nullptr) { + IMAGE_ERR("gContext is empty"); + return; + } + if (context->env == nullptr) { + IMAGE_ERR("env is empty"); + return; + } + + uv_loop_s *loop = nullptr; + napi_get_uv_event_loop(context->env, &loop); + if (loop == nullptr) { + IMAGE_ERR("napi_get_uv_event_loop failed"); + return; + } + + unique_ptr work = make_unique(); + if (work == nullptr) { + IMAGE_ERR("DoCallBack: No memory"); + return; + } + work->data = reinterpret_cast(context.get()); + int ret = uv_queue_work(loop, work.get(), [] (uv_work_t *work) {}, [] (uv_work_t *work, int status) { + Callback(work, status); + }); + if (ret != 0) { + IMAGE_ERR("Failed to execute DoCallBack work queue"); + } else { + work.release(); + } + IMAGE_FUNCTION_OUT(); +} + +napi_value SendableImageReceiverNapi::JsOn(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::ASYNC, + .name = "JsOn", + }; + args.argc = ARGS2; + args.asyncLater = true; + args.queryArgs = JsOnQueryArgs; + args.nonAsyncBack = [](SendableImageReceiverCommonArgs &args, SendableImageReceiverInnerContext &ic) -> bool { + IMAGE_LINE_IN(); + napi_get_undefined(args.env, &(ic.result)); + + auto native = ic.context->constructor_->imageReceiver_; + if (native == nullptr) { + IMAGE_ERR("Native instance is nullptr"); + ic.context->status = ERR_IMAGE_INIT_ABNORMAL; + return false; + } + shared_ptr listener = make_shared(); + listener->context = std::move(ic.context); + listener->context->env = args.env; + listener->name = args.name; + + native->RegisterBufferAvaliableListener((std::shared_ptr &)listener); + + IMAGE_LINE_OUT(); + return true; + }; + + return JSCommonProcess(args); +} + +napi_value SendableImageReceiverNapi::JsRelease(napi_env env, napi_callback_info info) +{ + IMAGE_FUNCTION_IN(); + SendableImageReceiverCommonArgs args = { + .env = env, .info = info, + .async = CallType::ASYNC, + .name = "JsRelease", + .callBack = nullptr, + .argc = ARGS1, + .queryArgs = PrepareOneArg, + }; + + args.callBack = [](napi_env env, napi_status status, Context context) { + IMAGE_LINE_IN(); + napi_value result = nullptr; + napi_get_undefined(env, &result); + + context->constructor_->NativeRelease(); + context->status = SUCCESS; + + IMAGE_LINE_OUT(); + CommonCallbackRoutine(env, context, result); + }; + + return JSCommonProcess(args); +} + +void SendableImageReceiverNapi::release() +{ + if (!isRelease) { + NativeRelease(); + isRelease = true; + } +} +} // namespace Media +} // namespace OHOS diff --git a/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp b/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp new file mode 100644 index 000000000..e82269f8b --- /dev/null +++ b/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp @@ -0,0 +1,923 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sendable_image_source_napi.h" +#include +#if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM) +#include "color_space_object_convertor.h" +#endif + +#undef LOG_DOMAIN +#define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE + +#undef LOG_TAG +#define LOG_TAG "SendableImageSourceNapi" + +namespace { + constexpr int INVALID_FD = -1; + constexpr uint32_t NUM_0 = 0; + constexpr uint32_t NUM_1 = 1; + constexpr uint32_t NUM_2 = 2; +} + +namespace OHOS { +namespace Media { +thread_local napi_ref SendableImageSourceNapi::sConstructor_ = nullptr; +thread_local std::shared_ptr SendableImageSourceNapi::sImgSrc_ = nullptr; +std::shared_ptr SendableImageSourceNapi::sIncPixelMap_ = nullptr; +static const std::string CLASS_NAME = "ImageSourceSendable"; +static const std::string FILE_URL_PREFIX = "file://"; +std::string SendableImageSourceNapi::filePath_ = ""; +int SendableImageSourceNapi::fileDescriptor_ = -1; +void* SendableImageSourceNapi::fileBuffer_ = nullptr; +size_t SendableImageSourceNapi::fileBufferSize_ = 0; +napi_ref SendableImageSourceNapi::pixelMapFormatRef_ = nullptr; +napi_ref SendableImageSourceNapi::propertyKeyRef_ = nullptr; +napi_ref SendableImageSourceNapi::imageFormatRef_ = nullptr; +napi_ref SendableImageSourceNapi::alphaTypeRef_ = nullptr; +napi_ref SendableImageSourceNapi::scaleModeRef_ = nullptr; +napi_ref SendableImageSourceNapi::componentTypeRef_ = nullptr; +napi_ref SendableImageSourceNapi::decodingDynamicRangeRef_ = nullptr; +napi_ref SendableImageSourceNapi::decodingResolutionQualityRef_ = nullptr; + +struct RawFileDescriptorInfo { + int32_t fd = INVALID_FD; + int32_t offset; + int32_t length; +}; + +struct ImageSourceAsyncContext { + napi_env env; + napi_async_work work; + napi_deferred deferred; + napi_ref callbackRef = nullptr; + SendableImageSourceNapi *constructor_; + uint32_t status; + std::string pathName = ""; + int fdIndex = INVALID_FD; + void* sourceBuffer = nullptr; + size_t sourceBufferSize = NUM_0; + std::string keyStr; + std::string valueStr; + std::vector keyStrArray; + std::vector> kVStrArray; + std::string defaultValueStr; + int32_t valueInt; + int32_t deufltValueInt; + void *updataBuffer; + size_t updataBufferSize; + uint32_t updataBufferOffset = 0; + uint32_t updataLength = 0; + bool isCompleted = false; + bool isSuccess = false; + bool isBatch = false; + size_t pathNameLength; + SourceOptions opts; + uint32_t index = 0; + ImageInfo imageInfo; + DecodeOptions decodeOpts; + std::shared_ptr rImageSource; + std::shared_ptr rPixelMap; + std::string errMsg; + std::multimap errMsgArray; + std::unique_ptr>> pixelMaps; + std::unique_ptr> delayTimes; + std::unique_ptr> disposalType; + uint32_t frameCount = 0; + struct RawFileDescriptorInfo rawFileInfo; +}; + +struct ImageEnum { + std::string name; + int32_t numVal; + std::string strVal; +}; + +static std::vector sPixelMapFormatMap = { + {"UNKNOWN", 0, ""}, + {"ARGB_8888", 1, ""}, + {"RGB_565", 2, ""}, + {"RGBA_8888", 3, ""}, + {"BGRA_8888", 4, ""}, + {"RGB_888", 5, ""}, + {"ALPHA_8", 6, ""}, + {"RGBA_F16", 7, ""}, + {"NV21", 8, ""}, + {"NV12", 9, ""}, +}; + +static std::vector sPropertyKeyMap = { + {"BITS_PER_SAMPLE", 0, "BitsPerSample"}, + {"ORIENTATION", 0, "Orientation"}, + {"IMAGE_LENGTH", 0, "ImageLength"}, + {"IMAGE_WIDTH", 0, "ImageWidth"}, + {"GPS_LATITUDE", 0, "GPSLatitude"}, + {"GPS_LONGITUDE", 0, "GPSLongitude"}, + {"GPS_LATITUDE_REF", 0, "GPSLatitudeRef"}, + {"GPS_LONGITUDE_REF", 0, "GPSLongitudeRef"}, + {"DATE_TIME_ORIGINAL", 0, "DateTimeOriginal"}, + {"EXPOSURE_TIME", 0, "ExposureTime"}, + {"SCENE_TYPE", 0, "SceneType"}, + {"ISO_SPEED_RATINGS", 0, "ISOSpeedRatings"}, + {"F_NUMBER", 0, "FNumber"}, + {"COMPRESSED_BITS_PER_PIXEL", 0, "CompressedBitsPerPixel"}, + {"DATE_TIME", 0, "DateTime"}, + {"GPS_TIME_STAMP", 0, "GPSTimeStamp"}, + {"GPS_DATE_STAMP", 0, "GPSDateStamp"}, + {"IMAGE_DESCRIPTION", 0, "ImageDescription"}, + {"MAKE", 0, "Make"}, + {"MODEL", 0, "Model"}, + {"PHOTO_MODE", 0, "PhotoMode"}, + {"SENSITIVITY_TYPE", 0, "SensitivityType"}, + {"STANDARD_OUTPUT_SENSITIVITY", 0, "StandardOutputSensitivity"}, + {"RECOMMENDED_EXPOSURE_INDEX", 0, "RecommendedExposureIndex"}, + {"ISO_SPEED", 0, "ISOSpeedRatings"}, + {"APERTURE_VALUE", 0, "ApertureValue"}, + {"EXPOSURE_BIAS_VALUE", 0, "ExposureBiasValue"}, + {"METERING_MODE", 0, "MeteringMode"}, + {"LIGHT_SOURCE", 0, "LightSource"}, + {"FLASH", 0, "Flash"}, + {"FOCAL_LENGTH", 0, "FocalLength"}, + {"USER_COMMENT", 0, "UserComment"}, + {"PIXEL_X_DIMENSION", 0, "PixelXDimension"}, + {"PIXEL_Y_DIMENSION", 0, "PixelYDimension"}, + {"WHITE_BALANCE", 0, "WhiteBalance"}, + {"FOCAL_LENGTH_IN_35_MM_FILM", 0, "FocalLengthIn35mmFilm"}, + {"CAPTURE_MODE", 0, "HwMnoteCaptureMode"}, + {"PHYSICAL_APERTURE", 0, "HwMnotePhysicalAperture"}, + {"ROLL_ANGLE", 0, "HwMnoteRollAngle"}, + {"PITCH_ANGLE", 0, "HwMnotePitchAngle"}, + {"SCENE_FOOD_CONF", 0, "HwMnoteSceneFoodConf"}, + {"SCENE_STAGE_CONF", 0, "HwMnoteSceneStageConf"}, + {"SCENE_BLUE_SKY_CONF", 0, "HwMnoteSceneBlueSkyConf"}, + {"SCENE_GREEN_PLANT_CONF", 0, "HwMnoteSceneGreenPlantConf"}, + {"SCENE_BEACH_CONF", 0, "HwMnoteSceneBeachConf"}, + {"SCENE_SNOW_CONF", 0, "HwMnoteSceneSnowConf"}, + {"SCENE_SUNSET_CONF", 0, "HwMnoteSceneSunsetConf"}, + {"SCENE_FLOWERS_CONF", 0, "HwMnoteSceneFlowersConf"}, + {"SCENE_NIGHT_CONF", 0, "HwMnoteSceneNightConf"}, + {"SCENE_TEXT_CONF", 0, "HwMnoteSceneTextConf"}, + {"FACE_COUNT", 0, "HwMnoteFaceCount"}, + {"FOCUS_MODE", 0, "HwMnoteFocusMode"}, +}; +static std::vector sImageFormatMap = { + {"YCBCR_422_SP", 1000, ""}, + {"JPEG", 2000, ""}, +}; +static std::vector sAlphaTypeMap = { + {"UNKNOWN", 0, ""}, + {"OPAQUE", 1, ""}, + {"PREMUL", 2, ""}, + {"UNPREMUL", 3, ""}, +}; +static std::vector sScaleModeMap = { + {"FIT_TARGET_SIZE", 0, ""}, + {"CENTER_CROP", 1, ""}, +}; +static std::vector sComponentTypeMap = { + {"YUV_Y", 1, ""}, + {"YUV_U", 2, ""}, + {"YUV_V", 3, ""}, + {"JPEG", 4, ""}, +}; +static std::vector sDecodingDynamicRangeMap = { + {"AUTO", 0, ""}, + {"SDR", 1, ""}, + {"HDR", 2, ""}, +}; +static std::vector sDecodingResolutionQualityMap = { + {"LOW", 1, ""}, + {"MEDIUM", 2, ""}, + {"HIGH", 3, ""}, +}; + +static bool ParseSize(napi_env env, napi_value root, Size* size) +{ + if (size == nullptr) { + IMAGE_LOGE("size is nullptr"); + return false; + } + if (!GET_INT32_BY_NAME(root, "height", size->height)) { + return false; + } + + if (!GET_INT32_BY_NAME(root, "width", size->width)) { + return false; + } + + return true; +} + +static void parseSourceOptions(napi_env env, napi_value root, SourceOptions* opts) +{ + if (!ImageNapiUtils::GetInt32ByName(env, root, "sourceDensity", &(opts->baseDensity))) { + IMAGE_LOGD("no sourceDensity"); + } + + int32_t pixelFormat = 0; + if (!ImageNapiUtils::GetInt32ByName(env, root, "sourcePixelFormat", &pixelFormat)) { + IMAGE_LOGD("no sourcePixelFormat"); + } else { + opts->pixelFormat = static_cast(pixelFormat); + IMAGE_LOGI("sourcePixelFormat:%{public}d", static_cast(opts->pixelFormat)); + } + + napi_value tmpValue = nullptr; + if (!GET_NODE_BY_NAME(root, "sourceSize", tmpValue)) { + IMAGE_LOGD("no sourceSize"); + } else { + if (!ParseSize(env, tmpValue, &(opts->size))) { + IMAGE_LOGD("ParseSize error"); + } + IMAGE_LOGI("sourceSize:(%{public}d, %{public}d)", opts->size.width, opts->size.height); + } +} + +static void PrepareNapiEnv(napi_env env) +{ + napi_value globalValue; + napi_get_global(env, &globalValue); + napi_value func; + napi_get_named_property(env, globalValue, "requireNapi", &func); + + napi_value imageInfo; + napi_create_string_utf8(env, "multimedia.sendableimage", NAPI_AUTO_LENGTH, &imageInfo); + napi_value funcArgv[1] = { imageInfo }; + napi_value returnValue; + napi_call_function(env, globalValue, func, 1, funcArgv, &returnValue); +} + +static bool hasNamedProperty(napi_env env, napi_value object, std::string name) +{ + bool res = false; + return (napi_has_named_property(env, object, name.c_str(), &res) == napi_ok) && res; +} + +static bool parseRawFileItem(napi_env env, napi_value argValue, std::string item, int32_t* value) +{ + napi_value nItem; + if (napi_get_named_property(env, argValue, item.c_str(), &nItem) != napi_ok) { + IMAGE_LOGE("Failed to parse RawFileDescriptor item %{public}s", item.c_str()); + return false; + } + if (napi_get_value_int32(env, nItem, value) != napi_ok) { + IMAGE_LOGE("Failed to get RawFileDescriptor item %{public}s value", item.c_str()); + return false; + } + return true; +} + +static bool isRawFileDescriptor(napi_env env, napi_value argValue, ImageSourceAsyncContext* context) +{ + if (env == nullptr || argValue == nullptr || context == nullptr) { + IMAGE_LOGE("isRawFileDescriptor invalid input"); + return false; + } + if (!hasNamedProperty(env, argValue, "fd") || + !hasNamedProperty(env, argValue, "offset") || + !hasNamedProperty(env, argValue, "length")) { + IMAGE_LOGD("RawFileDescriptor mismatch"); + return false; + } + if (parseRawFileItem(env, argValue, "fd", &(context->rawFileInfo.fd)) && + parseRawFileItem(env, argValue, "offset", &(context->rawFileInfo.offset)) && + parseRawFileItem(env, argValue, "length", &(context->rawFileInfo.length))) { + return true; + } + + IMAGE_LOGE("Failed to parse RawFileDescriptor item"); + return false; +} + +static std::string FileUrlToRawPath(const std::string &path) +{ + if (path.size() > FILE_URL_PREFIX.size() && + (path.compare(0, FILE_URL_PREFIX.size(), FILE_URL_PREFIX) == 0)) { + return path.substr(FILE_URL_PREFIX.size()); + } + return path; +} + +static bool ParseRegion(napi_env env, napi_value root, Rect* region) +{ + napi_value tmpValue = nullptr; + + if (region == nullptr) { + IMAGE_LOGE("region is nullptr"); + return false; + } + + if (!GET_INT32_BY_NAME(root, "x", region->left)) { + return false; + } + + if (!GET_INT32_BY_NAME(root, "y", region->top)) { + return false; + } + + if (!GET_NODE_BY_NAME(root, "size", tmpValue)) { + return false; + } + + if (!GET_INT32_BY_NAME(tmpValue, "height", region->height)) { + return false; + } + + if (!GET_INT32_BY_NAME(tmpValue, "width", region->width)) { + return false; + } + + return true; +} + +static bool IsSupportPixelFormat(int32_t val) +{ + if (val >= static_cast(PixelFormat::UNKNOWN) && + val <= static_cast(PixelFormat::NV12)) { + return true; + } + + return false; +} + +static PixelFormat ParsePixlForamt(int32_t val) +{ + if (val <= static_cast(PixelFormat::CMYK)) { + return PixelFormat(val); + } + + return PixelFormat::UNKNOWN; +} + +static ResolutionQuality ParseResolutionQuality(napi_env env, napi_value root) +{ + uint32_t resolutionQuality = NUM_0; + if (!GET_UINT32_BY_NAME(root, "resolutionQuality", resolutionQuality)) { + IMAGE_LOGD("no resolutionQuality"); + return ResolutionQuality::LOW; + } + if (resolutionQuality <= static_cast(ResolutionQuality::HIGH)) { + return ResolutionQuality(resolutionQuality); + } + return ResolutionQuality::LOW; +} + +static DecodeDynamicRange ParseDynamicRange(napi_env env, napi_value root) +{ + uint32_t tmpNumber = 0; + if (!GET_UINT32_BY_NAME(root, "desiredDynamicRange", tmpNumber)) { + IMAGE_LOGD("no desiredDynamicRange"); + return DecodeDynamicRange::SDR; + } + if (tmpNumber <= static_cast(DecodeDynamicRange::HDR)) { + return DecodeDynamicRange(tmpNumber); + } + return DecodeDynamicRange::SDR; +} + +static bool ParseDecodeOptions2(napi_env env, napi_value root, DecodeOptions* opts, std::string &error) +{ + uint32_t tmpNumber = 0; + if (!GET_UINT32_BY_NAME(root, "desiredPixelFormat", tmpNumber)) { + IMAGE_LOGD("no desiredPixelFormat"); + } else { + if (IsSupportPixelFormat(tmpNumber)) { + opts->desiredPixelFormat = ParsePixlForamt(tmpNumber); + } else { + IMAGE_LOGD("Invalid desiredPixelFormat %{public}d", tmpNumber); + error = "DecodeOptions mismatch"; + return false; + } + } + + if (!GET_INT32_BY_NAME(root, "fitDensity", opts->fitDensity)) { + IMAGE_LOGD("no fitDensity"); + } + + if (GET_UINT32_BY_NAME(root, "fillColor", opts->SVGOpts.fillColor.color)) { + opts->SVGOpts.fillColor.isValidColor = true; + IMAGE_LOGD("fillColor %{public}x", opts->SVGOpts.fillColor.color); + } else { + IMAGE_LOGD("no fillColor"); + } + + if (GET_UINT32_BY_NAME(root, "SVGResize", opts->SVGOpts.SVGResize.resizePercentage)) { + opts->SVGOpts.SVGResize.isValidPercentage = true; + IMAGE_LOGD("SVGResize percentage %{public}x", opts->SVGOpts.SVGResize.resizePercentage); + } else { + IMAGE_LOGD("no SVGResize percentage"); + } + napi_value nDesiredColorSpace = nullptr; + if (napi_get_named_property(env, root, "desiredColorSpace", &nDesiredColorSpace) == napi_ok) { + opts->desiredColorSpaceInfo = OHOS::ColorManager::GetColorSpaceByJSObject(env, nDesiredColorSpace); + IMAGE_LOGD("desiredColorSpace parse finished"); + } + if (opts->desiredColorSpaceInfo == nullptr) { + IMAGE_LOGD("no desiredColorSpace"); + } + opts->desiredDynamicRange = ParseDynamicRange(env, root); + opts->resolutionQuality = ParseResolutionQuality(env, root); + return true; +} + +static bool ParseDecodeOptions(napi_env env, napi_value root, DecodeOptions* opts, + uint32_t* pIndex, std::string &error) +{ + napi_value tmpValue = nullptr; + + if (!ImageNapiUtils::GetUint32ByName(env, root, "index", pIndex)) { + IMAGE_LOGD("no index"); + } + + if (opts == nullptr) { + IMAGE_LOGE("opts is nullptr"); + return false; + } + + if (!GET_UINT32_BY_NAME(root, "sampleSize", opts->sampleSize)) { + IMAGE_LOGD("no sampleSize"); + } + + if (!GET_UINT32_BY_NAME(root, "rotate", opts->rotateNewDegrees)) { + IMAGE_LOGD("no rotate"); + } else { + if (opts->rotateNewDegrees >= 0 && + opts->rotateNewDegrees <= 360) { // 360 is the maximum rotation angle. + opts->rotateDegrees = static_cast(opts->rotateNewDegrees); + } else { + IMAGE_LOGD("Invalid rotate %{public}d", opts->rotateNewDegrees); + error = "DecodeOptions mismatch"; + return false; + } + } + + if (!GET_BOOL_BY_NAME(root, "editable", opts->editable)) { + IMAGE_LOGD("no editable"); + } + + if (!GET_NODE_BY_NAME(root, "desiredSize", tmpValue)) { + IMAGE_LOGD("no desiredSize"); + } else { + if (!ParseSize(env, tmpValue, &(opts->desiredSize))) { + IMAGE_LOGD("ParseSize error"); + } + } + + if (!GET_NODE_BY_NAME(root, "desiredRegion", tmpValue)) { + IMAGE_LOGD("no desiredRegion"); + } else { + if (!ParseRegion(env, tmpValue, &(opts->CropRect))) { + IMAGE_LOGD("ParseRegion error"); + } + } + return ParseDecodeOptions2(env, root, opts, error); +} + +static void ImageSourceCallbackRoutine(napi_env env, ImageSourceAsyncContext* &context, const napi_value &valueParam) +{ + napi_value result[NUM_2] = {0}; + napi_value retVal; + napi_value callback = nullptr; + + napi_get_undefined(env, &result[NUM_0]); + napi_get_undefined(env, &result[NUM_1]); + + if (context == nullptr) { + IMAGE_LOGE("context is nullptr"); + return; + } + + if (context->status == SUCCESS) { + result[NUM_1] = valueParam; + } else if (context->errMsg.size() > 0) { + napi_create_string_utf8(env, context->errMsg.c_str(), NAPI_AUTO_LENGTH, &result[NUM_0]); + } else { + IMAGE_LOGD("error status, no message"); + napi_create_string_utf8(env, "error status, no message", NAPI_AUTO_LENGTH, &result[NUM_0]); + } + + if (context->deferred) { + if (context->status == SUCCESS) { + napi_resolve_deferred(env, context->deferred, result[NUM_1]); + } else { + napi_reject_deferred(env, context->deferred, result[NUM_0]); + } + } else { + IMAGE_LOGD("call callback function"); + napi_get_reference_value(env, context->callbackRef, &callback); + napi_call_function(env, nullptr, callback, NUM_2, result, &retVal); + napi_delete_reference(env, context->callbackRef); + } + + napi_delete_async_work(env, context->work); + + delete context; + context = nullptr; +} + +static std::shared_ptr CreatePixelMapInner(SendableImageSourceNapi *thisPtr, + std::shared_ptr imageSource, uint32_t index, DecodeOptions decodeOpts, uint32_t &status) +{ + if (thisPtr == nullptr || imageSource == nullptr) { + IMAGE_LOGE("Invailed args"); + status = ERROR; + } + + std::shared_ptr pixelMap; + auto incPixelMap = thisPtr->GetIncrementalPixelMap(); + if (incPixelMap != nullptr) { + IMAGE_LOGD("Get Incremental PixelMap!!!"); + pixelMap = incPixelMap; + } else { + decodeOpts.invokeType = JS_INTERFACE; + pixelMap = imageSource->CreatePixelMapEx((index >= NUM_0) ? index : NUM_0, + decodeOpts, status); + } + + if (status != SUCCESS || !IMG_NOT_NULL(pixelMap)) { + IMAGE_LOGE("Create PixelMap error"); + } + + return pixelMap; +} + +static void CreatePixelMapExecute(napi_env env, void *data) +{ + IMAGE_LOGD("CreatePixelMapExecute IN"); + if (data == nullptr) { + IMAGE_LOGE("data is nullptr"); + return; + } + auto context = static_cast(data); + if (context == nullptr) { + IMAGE_LOGE("empty context"); + return; + } + + if (context->errMsg.size() > 0) { + IMAGE_LOGE("mismatch args"); + context->status = ERROR; + return; + } + + context->rPixelMap = CreatePixelMapInner(context->constructor_, context->rImageSource, + context->index, context->decodeOpts, context->status); + + if (context->status != SUCCESS) { + context->errMsg = "Create PixelMap error"; + IMAGE_LOGE("Create PixelMap error"); + } + IMAGE_LOGD("CreatePixelMapExecute OUT"); +} + +static void CreatePixelMapComplete(napi_env env, napi_status status, void *data) +{ + IMAGE_LOGD("CreatePixelMapComplete IN"); + napi_value result = nullptr; + auto context = static_cast(data); + + if (context->status == SUCCESS) { + result = PixelMapNapi::CreatePixelMap(env, context->rPixelMap); + } else { + napi_get_undefined(env, &result); + } + IMAGE_LOGD("CreatePixelMapComplete OUT"); + ImageSourceCallbackRoutine(env, context, result); +} + +static std::unique_ptr CreateNativeImageSource(napi_env env, napi_value argValue, + SourceOptions &opts, ImageSourceAsyncContext* context) +{ + std::unique_ptr imageSource = nullptr; + uint32_t errorCode = ERR_MEDIA_INVALID_VALUE; + napi_status status; + + auto inputType = ImageNapiUtils::getType(env, argValue); + if (napi_string == inputType) { // File Path + if (!ImageNapiUtils::GetUtf8String(env, argValue, context->pathName)) { + IMAGE_LOGE("fail to get pathName"); + return imageSource; + } + context->pathName = FileUrlToRawPath(context->pathName); + context->pathNameLength = context->pathName.size(); + IMAGE_LOGD("pathName is [%{public}s]", context->pathName.c_str()); + imageSource = ImageSource::CreateImageSource(context->pathName, opts, errorCode); + } else if (napi_number == inputType) { // Fd + napi_get_value_int32(env, argValue, &context->fdIndex); + IMAGE_LOGD("CreateImageSource fdIndex is [%{public}d]", context->fdIndex); + imageSource = ImageSource::CreateImageSource(context->fdIndex, opts, errorCode); + } else if (isRawFileDescriptor(env, argValue, context)) { + IMAGE_LOGE( + "CreateImageSource RawFileDescriptor fd: %{public}d, offset: %{public}d, length: %{public}d", + context->rawFileInfo.fd, context->rawFileInfo.offset, context->rawFileInfo.length); + int32_t fileSize = context->rawFileInfo.offset + context->rawFileInfo.length; + imageSource = ImageSource::CreateImageSource(context->rawFileInfo.fd, + context->rawFileInfo.offset, fileSize, opts, errorCode); + } else { // Input Buffer + uint32_t refCount = NUM_1; + napi_ref arrayRef = nullptr; + napi_create_reference(env, argValue, refCount, &arrayRef); + status = napi_get_arraybuffer_info(env, argValue, &(context->sourceBuffer), &(context->sourceBufferSize)); + if (status != napi_ok) { + napi_delete_reference(env, arrayRef); + IMAGE_LOGE("fail to get arraybufferinfo"); + return nullptr; + } + imageSource = ImageSource::CreateImageSource(static_cast(context->sourceBuffer), + context->sourceBufferSize, opts, errorCode); + napi_delete_reference(env, arrayRef); + } + return imageSource; +} + +napi_value SendableImageSourceNapi::CreatePixelMap(napi_env env, napi_callback_info info) +{ + napi_value result = nullptr; + napi_get_undefined(env, &result); + + int32_t refCount = 1; + napi_status status; + napi_value thisVar = nullptr; + napi_value argValue[NUM_2] = {0}; + size_t argCount = NUM_2; + IMG_JS_ARGS(env, info, status, argCount, argValue, thisVar); + IMG_NAPI_CHECK_RET_D(IMG_IS_READY(status, thisVar), nullptr, IMAGE_LOGE("fail to get thisVar")); + IMG_NAPI_CHECK_RET_D(IMG_IS_OK(status), nullptr, IMAGE_LOGE("fail to napi_get_cb_info")); + + std::unique_ptr asyncContext = std::make_unique(); + + status = napi_unwrap(env, thisVar, reinterpret_cast(&asyncContext->constructor_)); + IMG_NAPI_CHECK_RET_D(IMG_IS_READY(status, asyncContext->constructor_), + nullptr, IMAGE_LOGE("fail to unwrap context")); + IMG_NAPI_CHECK_RET_D(IMG_IS_READY(status, asyncContext->constructor_->nativeImgSrc), + nullptr, IMAGE_LOGE("fail to unwrap nativeImgSrc")); + asyncContext->rImageSource = asyncContext->constructor_->nativeImgSrc; + IMG_NAPI_CHECK_RET_D(IMG_IS_READY(status, asyncContext->rImageSource), + nullptr, IMAGE_LOGE("empty native rImageSource")); + + if (argCount == NUM_0) { + IMAGE_LOGD("CreatePixelMap with no arg"); + } else if (argCount == NUM_1 || argCount == NUM_2) { + if (ImageNapiUtils::getType(env, argValue[NUM_0]) == napi_object) { + if (!ParseDecodeOptions(env, argValue[NUM_0], &(asyncContext->decodeOpts), + &(asyncContext->index), asyncContext->errMsg)) { + IMAGE_LOGE("DecodeOptions mismatch"); + } + } + if (ImageNapiUtils::getType(env, argValue[argCount - 1]) == napi_function) { + napi_create_reference(env, argValue[argCount - 1], refCount, &asyncContext->callbackRef); + } + } else { + IMAGE_LOGE("argCount mismatch"); + return result; + } + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &(asyncContext->deferred), &result); + } else { + napi_get_undefined(env, &result); + } + + ImageNapiUtils::HicheckerReport(); + IMG_CREATE_CREATE_ASYNC_WORK_WITH_QOS(env, status, "CreatePixelMap", CreatePixelMapExecute, + CreatePixelMapComplete, asyncContext, asyncContext->work, napi_qos_user_initiated); + + IMG_NAPI_CHECK_RET_D(IMG_IS_OK(status), + nullptr, IMAGE_LOGE("fail to create async work")); + return result; +} + +napi_value SendableImageSourceNapi::CreateImageSource(napi_env env, napi_callback_info info) +{ + PrepareNapiEnv(env); + napi_value result = nullptr; + napi_get_undefined(env, &result); + + napi_status status; + napi_value thisVar = nullptr; + napi_value argValue[NUM_2] = {0}; + size_t argCount = 2; + IMG_JS_ARGS(env, info, status, argCount, argValue, thisVar); + IMG_NAPI_CHECK_RET_D(IMG_IS_OK(status), nullptr, IMAGE_LOGE("fail to napi_get_cb_info")); + NAPI_ASSERT(env, argCount > 0, "No arg!"); + + std::unique_ptr asyncContext = std::make_unique(); + SourceOptions opts; + if (argCount > NUM_1) { + parseSourceOptions(env, argValue[NUM_1], &opts); + } + std::unique_ptr imageSource = CreateNativeImageSource(env, argValue[NUM_0], + opts, asyncContext.get()); + if (imageSource == nullptr) { + IMAGE_LOGE("CreateImageSourceExec error"); + napi_get_undefined(env, &result); + return result; + } + filePath_ = asyncContext->pathName; + fileDescriptor_ = asyncContext->fdIndex; + fileBuffer_ = asyncContext->sourceBuffer; + fileBufferSize_ = asyncContext->sourceBufferSize; + + napi_value constructor = nullptr; + status = napi_get_reference_value(env, sConstructor_, &constructor); + if (IMG_IS_OK(status)) { + sImgSrc_ = std::move(imageSource); + status = napi_new_instance(env, constructor, NUM_0, nullptr, &result); + } + if (!IMG_IS_OK(status)) { + IMAGE_LOGE("New instance could not be obtained"); + napi_get_undefined(env, &result); + } + return result; +} + +struct ImageConstructorInfo { + std::string className; + napi_ref* classRef; + napi_callback constructor; + const napi_property_descriptor* property; + size_t propertyCount; + const napi_property_descriptor* staticProperty; + size_t staticPropertyCount; +}; + +SendableImageSourceNapi::SendableImageSourceNapi():env_(nullptr) +{ } + +SendableImageSourceNapi::~SendableImageSourceNapi() +{ + release(); +} + +static napi_value DoInit(napi_env env, napi_value exports, struct ImageConstructorInfo info) +{ + napi_value constructor = nullptr; + napi_status status = napi_define_class(env, info.className.c_str(), NAPI_AUTO_LENGTH, + info.constructor, nullptr, info.propertyCount, info.property, &constructor); + if (status != napi_ok) { + IMAGE_LOGE("define class fail"); + return nullptr; + } + + status = napi_create_reference(env, constructor, NUM_1, info.classRef); + if (status != napi_ok) { + IMAGE_LOGE("create reference fail"); + return nullptr; + } + + napi_value global = nullptr; + status = napi_get_global(env, &global); + if (status != napi_ok) { + IMAGE_LOGE("Init:get global fail"); + return nullptr; + } + + status = napi_set_named_property(env, global, info.className.c_str(), constructor);//remove + if (status != napi_ok) { + IMAGE_LOGE("Init:set global named property fail"); + return nullptr; + } + + status = napi_define_properties(env, exports, info.staticPropertyCount, info.staticProperty); + if (status != napi_ok) { + IMAGE_LOGE("define properties fail"); + return nullptr; + } + return exports; +} + +napi_value SendableImageSourceNapi::Init(napi_env env, napi_value exports) { + napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION("createPixelMap", CreatePixelMap), + DECLARE_NAPI_FUNCTION("release", Release), + }; + + napi_property_descriptor static_prop[] = { + DECLARE_NAPI_STATIC_FUNCTION("createImageSource", CreateImageSource), + }; + + struct ImageConstructorInfo info = { + .className = CLASS_NAME, + .classRef = &sConstructor_, + .constructor = Constructor, + .property = properties, + .propertyCount = sizeof(properties) / sizeof(properties[NUM_0]), + .staticProperty = static_prop, + .staticPropertyCount = sizeof(static_prop) / sizeof(static_prop[NUM_0]), + }; + + if (DoInit(env, exports, info)) { + return nullptr; + } + return exports; +} + +napi_value SendableImageSourceNapi::Constructor(napi_env env, napi_callback_info info) +{ + napi_value undefineValue = nullptr; + napi_get_undefined(env, &undefineValue); + + napi_status status; + napi_value thisVar = nullptr; + status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr); + if (status == napi_ok && thisVar != nullptr) { + std::unique_ptr pImgSrcNapi = std::make_unique(); + if (pImgSrcNapi != nullptr) { + pImgSrcNapi->env_ = env; + pImgSrcNapi->nativeImgSrc = sImgSrc_; + if (pImgSrcNapi->nativeImgSrc == nullptr) { + IMAGE_LOGE("Failed to set nativeImageSource with null. Maybe a reentrancy error"); + } + pImgSrcNapi->navIncPixelMap_ = sIncPixelMap_; + sIncPixelMap_ = nullptr; + sImgSrc_ = nullptr; + status = napi_wrap(env, thisVar, reinterpret_cast(pImgSrcNapi.get()), + SendableImageSourceNapi::Destructor, nullptr, nullptr); + if (status == napi_ok) { + pImgSrcNapi.release(); + return thisVar; + } else { + IMAGE_LOGE("Failure wrapping js to native napi"); + } + } + } + + return undefineValue; +} + +void SendableImageSourceNapi::Destructor(napi_env env, void *nativeObject, void *finalize) +{ + reinterpret_cast(nativeObject)->nativeImgSrc = nullptr; + IMAGE_LOGD("ImageSourceNapi::Destructor"); +} + +static void ReleaseComplete(napi_env env, napi_status status, void *data) +{ + napi_value result = nullptr; + napi_get_undefined(env, &result); + + auto context = static_cast(data); + delete context->constructor_; + context->constructor_ = nullptr; + ImageSourceCallbackRoutine(env, context, result); +} + +napi_value SendableImageSourceNapi::Release(napi_env env, napi_callback_info info) +{ + IMAGE_LOGD("Release enter"); + napi_value result = nullptr; + napi_get_undefined(env, &result); + + int32_t refCount = 1; + napi_status status; + napi_value thisVar = nullptr; + napi_value argValue[NUM_1] = {0}; + size_t argCount = 1; + + IMG_JS_ARGS(env, info, status, argCount, argValue, thisVar); + IMAGE_LOGD("Release argCount is [%{public}zu]", argCount); + IMG_NAPI_CHECK_RET_D(IMG_IS_OK(status), result, IMAGE_LOGE("fail to napi_get_cb_info")); + + std::unique_ptr asyncContext = std::make_unique(); + status = napi_remove_wrap(env, thisVar, reinterpret_cast(&asyncContext->constructor_)); + + IMG_NAPI_CHECK_RET_D(IMG_IS_READY(status, asyncContext->constructor_), result, + IMAGE_LOGE("fail to unwrap context")); + + IMAGE_LOGD("Release argCount is [%{public}zu]", argCount); + if (argCount == 1 && ImageNapiUtils::getType(env, argValue[NUM_0]) == napi_function) { + napi_create_reference(env, argValue[NUM_0], refCount, &asyncContext->callbackRef); + } + + if (asyncContext->callbackRef == nullptr) { + napi_create_promise(env, &(asyncContext->deferred), &result); + } + + IMG_CREATE_CREATE_ASYNC_WORK(env, status, "Release", + [](napi_env env, void *data) {}, ReleaseComplete, asyncContext, asyncContext->work); + IMAGE_LOGD("Release exit"); + return result; +} + +void SendableImageSourceNapi::release() +{ + if (!isRelease) { + if (nativeImgSrc != nullptr) { + nativeImgSrc = nullptr; + } + isRelease = true; + } +} + +} +} \ No newline at end of file diff --git a/interfaces/kits/js/common/BUILD.gn b/interfaces/kits/js/common/BUILD.gn index 1a7d62ac1..2f22fff9e 100644 --- a/interfaces/kits/js/common/BUILD.gn +++ b/interfaces/kits/js/common/BUILD.gn @@ -209,6 +209,9 @@ if (use_clang_ios) { "$image_subsystem/frameworks/kits/js/common/image_receiver_mdk_kits.cpp", "$image_subsystem/frameworks/kits/js/common/image_source_mdk_kits.cpp", "${image_subsystem}/frameworks/kits/js/common/sendable/sendable_pixel_map_napi.cpp", + "${image_subsystem}/frameworks/kits/js/common/sendable/sendable_image_napi.cpp", + "${image_subsystem}/frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp", + "${image_subsystem}/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp", "//foundation/multimedia/image_framework/frameworks/kits/js/common/image_creator_napi.cpp", "//foundation/multimedia/image_framework/frameworks/kits/js/common/image_napi.cpp", "//foundation/multimedia/image_framework/frameworks/kits/js/common/image_napi_utils.cpp", diff --git a/interfaces/kits/js/common/include/sendable/sendable_image_napi.h b/interfaces/kits/js/common/include/sendable/sendable_image_napi.h new file mode 100644 index 000000000..b5760db59 --- /dev/null +++ b/interfaces/kits/js/common/include/sendable/sendable_image_napi.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERFACES_KITS_JS_COMMON_INCLUDE_IMAGE_SENDABLE_NAPI_H +#define INTERFACES_KITS_JS_COMMON_INCLUDE_IMAGE_SENDABLE_NAPI_H + +#include "native_image.h" +#include "napi/native_api.h" +#include "image_holder_manager.h" + +namespace OHOS { +namespace Media { +struct SendableImageAsyncContext; +class SendableImageNapi { +public: + SendableImageNapi(); + ~SendableImageNapi(); + static napi_value Init(napi_env env, napi_value exports); + static napi_value Create(napi_env env); + static napi_value Create(napi_env env, std::shared_ptr nativeImage); + static std::shared_ptr GetNativeImage(napi_env env, napi_value image); + + NativeImage* GetNative(); + void NativeRelease(); +private: + static napi_value Constructor(napi_env env, napi_callback_info info); + static void Destructor(napi_env env, void *nativeObject, void *finalize); + + static napi_value JSGetClipRect(napi_env env, napi_callback_info info); + static napi_value JsGetSize(napi_env env, napi_callback_info info); + static napi_value JsGetFormat(napi_env env, napi_callback_info info); + static napi_value JsGetComponent(napi_env env, napi_callback_info info); + static napi_value JsRelease(napi_env env, napi_callback_info info); + static napi_value JsGetTimestamp(napi_env env, napi_callback_info info); + + static thread_local napi_ref sConstructor_; + static ImageHolderManager sNativeImageHolder_; + std::shared_ptr native_; + bool isTestImage_; +}; + +} // namespace Media +} // namespace OHOS + +#endif //INTERFACES_KITS_JS_COMMON_INCLUDE_IMAGE_SENDABLE_NAPI_H \ No newline at end of file diff --git a/interfaces/kits/js/common/include/sendable/sendable_image_receiver_napi.h b/interfaces/kits/js/common/include/sendable/sendable_image_receiver_napi.h new file mode 100644 index 000000000..dcb10d211 --- /dev/null +++ b/interfaces/kits/js/common/include/sendable/sendable_image_receiver_napi.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERFACES_KITS_JS_COMMON_INCLUDE_SENDABLE_IMAGE_RECEIVER_NAPI_H +#define INTERFACES_KITS_JS_COMMON_INCLUDE_SENDABLE_IMAGE_RECEIVER_NAPI_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "pixel_map.h" +#include "image_receiver.h" +#include "image_napi_utils.h" + +namespace OHOS { +namespace Media { +struct SendableImageReceiverCommonArgs; +struct SendableImageReceiverAsyncContext; +using Context = SendableImageReceiverAsyncContext* ; +using CompleteCallback = void (*)(napi_env env, napi_status status, Context context); +struct SendableImageReceiverCreateArgs { + int32_t width; + int32_t height; + int32_t format; + int32_t capicity; +}; +class SendableImageReceiverNapi { +public: + SendableImageReceiverNapi(); + ~SendableImageReceiverNapi(); + static napi_value Init(napi_env env, napi_value exports); + static void DoCallBack(std::shared_ptr context, + std::string name, + CompleteCallback callBack); + ImageReceiver* GetNative(); + static napi_value CreateImageReceiverJsObject(napi_env env, struct SendableImageReceiverCreateArgs args); + void NativeRelease(); +#ifdef IMAGE_DEBUG_FLAG + bool isCallBackTest = false; +#endif + +private: + static napi_value Constructor(napi_env env, napi_callback_info info); + static void Destructor(napi_env env, void *nativeObject, void *finalize); + + static napi_value JSCreateImageReceiver(napi_env env, napi_callback_info info); + static napi_value JsGetSize(napi_env env, napi_callback_info info); + static napi_value JsGetCapacity(napi_env env, napi_callback_info info); + static napi_value JsGetFormat(napi_env env, napi_callback_info info); + static napi_value JsGetReceivingSurfaceId(napi_env env, napi_callback_info info); + static napi_value JsReadLatestImage(napi_env env, napi_callback_info info); + static napi_value JsReadNextImage(napi_env env, napi_callback_info info); + static napi_value JsOn(napi_env env, napi_callback_info info); + static napi_value JsRelease(napi_env env, napi_callback_info info); + + static bool GetNativeFromEnv(napi_env env, napi_callback_info info, std::shared_ptr &native); + static napi_value JSCommonProcess(SendableImageReceiverCommonArgs &args); +#ifdef IMAGE_DEBUG_FLAG + static napi_value JsTest(napi_env env, napi_callback_info info); + static napi_value JsCheckDeviceTest(napi_env env, napi_callback_info info); + static napi_value JsTestYUV(napi_env env, napi_callback_info info); +#endif + void release(); + static thread_local napi_ref sConstructor_; + static std::shared_ptr staticInstance_; + + napi_env env_ = nullptr; + std::shared_ptr imageReceiver_; + bool isRelease = false; +}; +struct SendableImageReceiverAsyncContext { + napi_env env = nullptr; + napi_async_work work = nullptr; + napi_deferred deferred = nullptr; + napi_ref callbackRef = nullptr; + SendableImageReceiverNapi *constructor_ = nullptr; + uint32_t status; +}; +struct SendableImageReceiverInnerContext { + napi_status status; + napi_value result = nullptr; + napi_value thisVar = nullptr; + size_t argc; + std::vector argv; + std::string onType; + int32_t refCount = 1; + std::unique_ptr context = nullptr; +}; + +using CommonFunc = bool (*)(SendableImageReceiverCommonArgs &args, SendableImageReceiverInnerContext &ic); + +enum class CallType : uint32_t { + STATIC = 0, + GETTER = 1, + ASYNC = 2, +}; + +struct SendableImageReceiverCommonArgs { + napi_env env; + napi_callback_info info; + CallType async; + const std::string name; + CompleteCallback callBack; + size_t argc; + CommonFunc queryArgs; + CommonFunc nonAsyncBack; + bool asyncLater = false; +}; + +class ImageReceiverAvaliableListener : public SurfaceBufferAvaliableListener { +public: + ~ImageReceiverAvaliableListener() override + { + if (context && context->env && context->callbackRef) { + napi_delete_reference(context->env, context->callbackRef); + } + context = nullptr; + callBack = nullptr; + } + void OnSurfaceBufferAvaliable() override + { + SendableImageReceiverNapi::DoCallBack(context, name, callBack); + } + std::shared_ptr context = nullptr; + std::string name; + CompleteCallback callBack = nullptr; +}; +} // namespace Media +} // namespace OHOS +#endif // INTERFACES_KITS_JS_COMMON_INCLUDE_SENDABLE_IMAGE_RECEIVER_NAPI_H diff --git a/interfaces/kits/js/common/include/sendable/sendable_image_source_napi.h b/interfaces/kits/js/common/include/sendable/sendable_image_source_napi.h new file mode 100644 index 000000000..eda7391ec --- /dev/null +++ b/interfaces/kits/js/common/include/sendable/sendable_image_source_napi.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERFACES_KITS_JS_COMMON_INCLUDE_IMAGE_SOURCE_SENDABLE_NAPI_H +#define INTERFACES_KITS_JS_COMMON_INCLUDE_IMAGE_SOURCE_SENDABLE_NAPI_H + +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "image_type.h" +#include "image_log.h" +#include "image_source.h" +#include "pixel_map.h" +#include "sendable_pixel_map_napi.h" +#include "media_errors.h" +#include "image_napi_utils.h" +#include "image_dfx.h" + +namespace OHOS { +namespace Media { +class SendableImageSourceNapi { +public: + SendableImageSourceNapi(); + ~SendableImageSourceNapi(); + std::shared_ptr nativeImgSrc = nullptr; + static std::string filePath_; + static int fileDescriptor_; + static void* fileBuffer_; + static size_t fileBufferSize_; + + std::shared_ptr GetIncrementalPixelMap() + { + return navIncPixelMap_; + } + static napi_value Init(napi_env env, napi_value exports); +private: + static napi_value Constructor(napi_env env, napi_callback_info info); + static void Destructor(napi_env env, void *nativeObject, void *finalize); + + static napi_value CreateImageSource(napi_env env, napi_callback_info info); + static napi_value CreatePixelMap(napi_env env, napi_callback_info info); + static napi_value Release(napi_env env, napi_callback_info info); + + void release(); + static thread_local napi_ref sConstructor_; + static thread_local std::shared_ptr sImgSrc_; + static std::shared_ptr sIncPixelMap_; + std::shared_ptr navIncPixelMap_ = nullptr; + static napi_ref pixelMapFormatRef_; + static napi_ref propertyKeyRef_; + static napi_ref imageFormatRef_; + static napi_ref alphaTypeRef_; + static napi_ref scaleModeRef_; + static napi_ref componentTypeRef_; + static napi_ref decodingDynamicRangeRef_; + static napi_ref decodingResolutionQualityRef_; + + napi_env env_ = nullptr; + bool isRelease = false; +}; +} +} + +#endif \ No newline at end of file -- Gitee From b39e03f3f25437905f43ba93ea96cf65c63c6f07 Mon Sep 17 00:00:00 2001 From: yangfan Date: Wed, 15 May 2024 14:17:40 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96gn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: yangfan Change-Id: Iaeb5277cf59635194f4a4773c40051439c857058 --- interfaces/kits/js/common/BUILD.gn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfaces/kits/js/common/BUILD.gn b/interfaces/kits/js/common/BUILD.gn index 2f22fff9e..f3a3c3ff6 100644 --- a/interfaces/kits/js/common/BUILD.gn +++ b/interfaces/kits/js/common/BUILD.gn @@ -208,10 +208,10 @@ if (use_clang_ios) { "$image_subsystem/frameworks/kits/js/common/image_pixel_map_napi_kits.cpp", "$image_subsystem/frameworks/kits/js/common/image_receiver_mdk_kits.cpp", "$image_subsystem/frameworks/kits/js/common/image_source_mdk_kits.cpp", - "${image_subsystem}/frameworks/kits/js/common/sendable/sendable_pixel_map_napi.cpp", "${image_subsystem}/frameworks/kits/js/common/sendable/sendable_image_napi.cpp", "${image_subsystem}/frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp", "${image_subsystem}/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp", + "${image_subsystem}/frameworks/kits/js/common/sendable/sendable_pixel_map_napi.cpp", "//foundation/multimedia/image_framework/frameworks/kits/js/common/image_creator_napi.cpp", "//foundation/multimedia/image_framework/frameworks/kits/js/common/image_napi.cpp", "//foundation/multimedia/image_framework/frameworks/kits/js/common/image_napi_utils.cpp", -- Gitee From 1e37aa30f99f044e2ff27b0a88260c5fcc6fe955 Mon Sep 17 00:00:00 2001 From: yangfan Date: Wed, 15 May 2024 14:28:45 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=9B=E5=BB=BAsendabl?= =?UTF-8?q?epixelmap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: yangfan Change-Id: Idd21d9e6766208e1ff7bdd58ed756dd425e1793a --- .../kits/js/common/sendable/sendable_image_source_napi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp b/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp index e82269f8b..9317c1085 100644 --- a/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp +++ b/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp @@ -588,7 +588,7 @@ static void CreatePixelMapComplete(napi_env env, napi_status status, void *data) auto context = static_cast(data); if (context->status == SUCCESS) { - result = PixelMapNapi::CreatePixelMap(env, context->rPixelMap); + result = SendablePixelMapNapi::CreateSendablePixelMap(env, context->rPixelMap); } else { napi_get_undefined(env, &result); } -- Gitee From 17298193eaca9bc4777094ec50fb9b6555027c2c Mon Sep 17 00:00:00 2001 From: yangfan Date: Wed, 15 May 2024 15:04:02 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: yangfan Change-Id: Ie032f3f5d0da78ee119458dda9e82ac16a02d6af --- .../kits/js/common/sendable/native_module_image_sendable.cpp | 3 +++ .../js/common/include/sendable/native_module_image_sendable.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/frameworks/kits/js/common/sendable/native_module_image_sendable.cpp b/frameworks/kits/js/common/sendable/native_module_image_sendable.cpp index b2ffd6221..d5ec00ff4 100644 --- a/frameworks/kits/js/common/sendable/native_module_image_sendable.cpp +++ b/frameworks/kits/js/common/sendable/native_module_image_sendable.cpp @@ -31,6 +31,9 @@ static napi_value Export(napi_env env, napi_value exports) { IMAGE_LOGE("SendablePixelMapNapi call"); SendablePixelMapNapi::Init(env, exports); + SendableImageNapi::Init(env, exports); + SendableImageReceiverNapi::Init(env, exports); + SendableImageSourceNapi::Init(env, exports); return exports; } diff --git a/interfaces/kits/js/common/include/sendable/native_module_image_sendable.h b/interfaces/kits/js/common/include/sendable/native_module_image_sendable.h index e037d737d..71d428bc1 100644 --- a/interfaces/kits/js/common/include/sendable/native_module_image_sendable.h +++ b/interfaces/kits/js/common/include/sendable/native_module_image_sendable.h @@ -18,5 +18,8 @@ #include "napi/native_node_api.h" #include "sendable_pixel_map_napi.h" +#include "sendable_image_receiver_napi.h" +#include "sendable_image_source_napi.h" +#include "sendable_image_napi.h" #endif // INTERFACES_KITS_JS_COMMON_INCLUDE_NATIVE_MODULE_IMAGE_SENDABLE_H -- Gitee From d1c8e27b809690905db4a08debc84c1b9075817a Mon Sep 17 00:00:00 2001 From: yangfan Date: Wed, 15 May 2024 15:37:43 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E8=A7=84=E8=8C=83=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: yangfan Change-Id: Ib63c2f3145f02d788c46aa91c30f529c411a6028 --- frameworks/kits/js/common/sendable/sendable_image_napi.cpp | 6 +++--- .../js/common/sendable/sendable_image_receiver_napi.cpp | 2 +- .../kits/js/common/sendable/sendable_image_source_napi.cpp | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frameworks/kits/js/common/sendable/sendable_image_napi.cpp b/frameworks/kits/js/common/sendable/sendable_image_napi.cpp index d548fb2e4..7ebd077f9 100644 --- a/frameworks/kits/js/common/sendable/sendable_image_napi.cpp +++ b/frameworks/kits/js/common/sendable/sendable_image_napi.cpp @@ -383,7 +383,7 @@ napi_value SendableImageNapi::JSGetClipRect(napi_env env, napi_callback_info inf return BuildJsRegion(env, WIDTH, HEIGHT, NUM0, NUM0); } if (context == nullptr || context->image == nullptr) { - IMAGE_ERR("Image surface buffer is nullptr"); + IMAGE_ERR("Image surfacebuffer is nullptr"); return result; } @@ -409,7 +409,7 @@ napi_value SendableImageNapi::JsGetSize(napi_env env, napi_callback_info info) return BuildJsSize(env, WIDTH, HEIGHT); } if (context == nullptr || context->image == nullptr) { - IMAGE_ERR("Image surface buffer is nullptr"); + IMAGE_ERR("Image surfacebuffer is nullptr"); return result; } @@ -435,7 +435,7 @@ napi_value SendableImageNapi::JsGetFormat(napi_env env, napi_callback_info info) return result; } if (context == nullptr || context->image == nullptr) { - IMAGE_ERR("Image surface buffer is nullptr"); + IMAGE_ERR("Image surfacebuffer is nullptr"); return result; } diff --git a/frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp b/frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp index 145380877..41063bd78 100644 --- a/frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp +++ b/frameworks/kits/js/common/sendable/sendable_image_receiver_napi.cpp @@ -927,7 +927,7 @@ static void Callback(uv_work_t *work, int status) } void SendableImageReceiverNapi::DoCallBack(shared_ptr context, - string name, CompleteCallback callBack) + string name, CompleteCallback callBack) { IMAGE_FUNCTION_IN(); if (context == nullptr) { diff --git a/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp b/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp index 9317c1085..5d07b5436 100644 --- a/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp +++ b/frameworks/kits/js/common/sendable/sendable_image_source_napi.cpp @@ -782,7 +782,7 @@ static napi_value DoInit(napi_env env, napi_value exports, struct ImageConstruct return nullptr; } - status = napi_set_named_property(env, global, info.className.c_str(), constructor);//remove + status = napi_set_named_property(env, global, info.className.c_str(), constructor); if (status != napi_ok) { IMAGE_LOGE("Init:set global named property fail"); return nullptr; @@ -796,7 +796,8 @@ static napi_value DoInit(napi_env env, napi_value exports, struct ImageConstruct return exports; } -napi_value SendableImageSourceNapi::Init(napi_env env, napi_value exports) { +napi_value SendableImageSourceNapi::Init(napi_env env, napi_value exports) +{ napi_property_descriptor properties[] = { DECLARE_NAPI_FUNCTION("createPixelMap", CreatePixelMap), DECLARE_NAPI_FUNCTION("release", Release), -- Gitee