1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "avimagegenerator_napi.h"
17 #include "media_log.h"
18 #include "media_errors.h"
19 #include "common_napi.h"
20 #include "pixel_map_napi.h"
21 #include "string_ex.h"
22 #include "player_xcollie.h"
23 #include "media_dfx.h"
24 #ifdef SUPPORT_JSSTACK
25 #include "xpower_event_js.h"
26 #endif
27 #include "av_common.h"
28 #if !defined(CROSS_PLATFORM)
29 #include "ipc_skeleton.h"
30 #include "tokenid_kit.h"
31 #endif
32
33 using namespace OHOS::AudioStandard;
34
35 namespace {
36 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_METADATA, "AVImageGeneratorNapi" };
37 constexpr uint8_t ARG_ZERO = 0;
38 constexpr uint8_t ARG_ONE = 1;
39 constexpr uint8_t ARG_TWO = 2;
40 constexpr uint8_t ARG_THREE = 3;
41 constexpr uint8_t ARG_FOUR = 4;
42 }
43
44 namespace OHOS {
45 namespace Media {
46 thread_local napi_ref AVImageGeneratorNapi::constructor_ = nullptr;
47 const std::string CLASS_NAME = "AVImageGenerator";
48
AVImageGeneratorNapi()49 AVImageGeneratorNapi::AVImageGeneratorNapi()
50 {
51 MEDIA_LOGI("0x%{public}06" PRIXPTR " Instances create", FAKE_POINTER(this));
52 }
53
~AVImageGeneratorNapi()54 AVImageGeneratorNapi::~AVImageGeneratorNapi()
55 {
56 MEDIA_LOGI("0x%{public}06" PRIXPTR " Instances destroy", FAKE_POINTER(this));
57 }
58
Init(napi_env env,napi_value exports)59 napi_value AVImageGeneratorNapi::Init(napi_env env, napi_value exports)
60 {
61 napi_property_descriptor staticProperty[] = {
62 DECLARE_NAPI_STATIC_FUNCTION("createAVImageGenerator", JsCreateAVImageGenerator),
63 };
64
65 napi_property_descriptor properties[] = {
66 DECLARE_NAPI_FUNCTION("fetchFrameByTime", JsFetchFrameAtTime),
67 DECLARE_NAPI_FUNCTION("release", JsRelease),
68
69 DECLARE_NAPI_GETTER_SETTER("fdSrc", JsGetAVFileDescriptor, JsSetAVFileDescriptor),
70 };
71
72 napi_value constructor = nullptr;
73 CHECK_AND_RETURN_RET_LOG(sizeof(properties[0]) != 0, nullptr, "Failed to define calss");
74
75 napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
76 sizeof(properties) / sizeof(properties[0]), properties, &constructor);
77 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define AVImageGenerator class");
78
79 status = napi_create_reference(env, constructor, 1, &constructor_);
80 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to create reference of constructor");
81
82 status = napi_set_named_property(env, exports, CLASS_NAME.c_str(), constructor);
83 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to set constructor");
84
85 status = napi_define_properties(env, exports, sizeof(staticProperty) / sizeof(staticProperty[0]), staticProperty);
86 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define static function");
87
88 MEDIA_LOGD("AVImageGeneratorNapi Init success");
89 return exports;
90 }
91
Constructor(napi_env env,napi_callback_info info)92 napi_value AVImageGeneratorNapi::Constructor(napi_env env, napi_callback_info info)
93 {
94 napi_value result = nullptr;
95 napi_get_undefined(env, &result);
96
97 size_t argCount = 0;
98 napi_value jsThis = nullptr;
99 napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
100 CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "failed to napi_get_cb_info");
101
102 AVImageGeneratorNapi *generator = new(std::nothrow) AVImageGeneratorNapi();
103 CHECK_AND_RETURN_RET_LOG(generator != nullptr, result, "failed to new AVImageGeneratorNapi");
104
105 generator->env_ = env;
106 generator->helper_ = AVMetadataHelperFactory::CreateAVMetadataHelper();
107 CHECK_AND_RETURN_RET_LOG(generator->helper_ != nullptr, result, "failed to CreateMetadataHelper");
108
109 status = napi_wrap(env, jsThis, reinterpret_cast<void *>(generator),
110 AVImageGeneratorNapi::Destructor, nullptr, nullptr);
111 if (status != napi_ok) {
112 delete generator;
113 MEDIA_LOGE("Failed to wrap native instance");
114 return result;
115 }
116
117 MEDIA_LOGI("Constructor success");
118 return jsThis;
119 }
120
Destructor(napi_env env,void * nativeObject,void * finalize)121 void AVImageGeneratorNapi::Destructor(napi_env env, void *nativeObject, void *finalize)
122 {
123 MEDIA_LOGI("0x%{public}06" PRIXPTR " Destructor", FAKE_POINTER(nativeObject));
124 (void)finalize;
125 CHECK_AND_RETURN(nativeObject != nullptr);
126 AVImageGeneratorNapi *napi = reinterpret_cast<AVImageGeneratorNapi *>(nativeObject);
127 if (napi != nullptr && napi->helper_ != nullptr) {
128 napi->helper_->Release();
129 }
130 delete napi;
131 }
132
JsCreateAVImageGenerator(napi_env env,napi_callback_info info)133 napi_value AVImageGeneratorNapi::JsCreateAVImageGenerator(napi_env env, napi_callback_info info)
134 {
135 MediaTrace trace("AVImageGeneratorNapi::JsCreateAVImageGenerator");
136 napi_value result = nullptr;
137 napi_get_undefined(env, &result);
138 MEDIA_LOGI("JsCreateAVImageGenerator In");
139
140 std::unique_ptr<MediaAsyncContext> asyncContext = std::make_unique<MediaAsyncContext>(env);
141
142 // get args
143 napi_value jsThis = nullptr;
144 napi_value args[1] = { nullptr };
145 size_t argCount = 1;
146 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
147 CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr, "failed to napi_get_cb_info");
148
149 asyncContext->callbackRef = CommonNapi::CreateReference(env, args[0]);
150 asyncContext->deferred = CommonNapi::CreatePromise(env, asyncContext->callbackRef, result);
151 asyncContext->JsResult = std::make_unique<MediaJsResultInstance>(constructor_);
152 asyncContext->ctorFlag = true;
153
154 napi_value resource = nullptr;
155 napi_create_string_utf8(env, "JsCreateAVImageGenerator", NAPI_AUTO_LENGTH, &resource);
156 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {},
157 MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncContext.get()), &asyncContext->work));
158 NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work));
159 asyncContext.release();
160 MEDIA_LOGI("JsCreateAVImageGenerator Out");
161 return result;
162 }
163
GetFetchFrameArgs(std::unique_ptr<AVImageGeneratorAsyncContext> & asyncCtx,napi_env env,napi_value timeUs,napi_value option,napi_value params)164 int32_t AVImageGeneratorNapi::GetFetchFrameArgs(std::unique_ptr<AVImageGeneratorAsyncContext> &asyncCtx, napi_env env,
165 napi_value timeUs, napi_value option, napi_value params)
166 {
167 napi_status ret = napi_get_value_int64(env, timeUs, &asyncCtx->timeUs_);
168 if (ret != napi_ok) {
169 asyncCtx->SignError(MSERR_INVALID_VAL, "failed to get timeUs");
170 return MSERR_INVALID_VAL;
171 }
172 ret = napi_get_value_int32(env, option, &asyncCtx->option_);
173 if (ret != napi_ok) {
174 asyncCtx->SignError(MSERR_INVALID_VAL, "failed to get option");
175 return MSERR_INVALID_VAL;
176 }
177 int32_t width = 0;
178 CommonNapi::GetPropertyInt32(env, params, "width", width);
179 int32_t height = 0;
180 CommonNapi::GetPropertyInt32(env, params, "height", height);
181 int32_t formatVal = 3;
182 CommonNapi::GetPropertyInt32(env, params, "colorFormat", formatVal);
183 PixelFormat colorFormat = static_cast<PixelFormat>(formatVal);
184 if (colorFormat != PixelFormat::RGB_565 && colorFormat != PixelFormat::RGB_888 &&
185 colorFormat != PixelFormat::RGBA_8888) {
186 asyncCtx->SignError(MSERR_INVALID_VAL, "formatVal is invalid");
187 return MSERR_INVALID_VAL;
188 }
189
190 asyncCtx->param_.dstWidth = width;
191 asyncCtx->param_.dstHeight = height;
192 asyncCtx->param_.colorFormat = colorFormat;
193 return MSERR_OK;
194 }
195
JsFetchFrameAtTime(napi_env env,napi_callback_info info)196 napi_value AVImageGeneratorNapi::JsFetchFrameAtTime(napi_env env, napi_callback_info info)
197 {
198 MediaTrace trace("AVImageGeneratorNapi::JsFetchFrameAtTime");
199 MEDIA_LOGI("JsFetchFrameAtTime in");
200 const int32_t maxArgs = ARG_FOUR; // args + callback
201 const int32_t argCallback = ARG_THREE; // index three, the 4th param if exist
202 const int32_t argPixelParam = ARG_TWO; // index 2, the 3rd param
203 size_t argCount = maxArgs;
204 napi_value args[maxArgs] = { nullptr };
205 napi_value result = nullptr;
206 napi_get_undefined(env, &result);
207
208 AVImageGeneratorNapi *napi = AVImageGeneratorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
209 CHECK_AND_RETURN_RET_LOG(napi != nullptr, result, "failed to GetJsInstance");
210
211 auto asyncCtx = std::make_unique<AVImageGeneratorAsyncContext>(env);
212 asyncCtx->innerHelper_ = napi->helper_;
213 asyncCtx->callbackRef = CommonNapi::CreateReference(env, args[argCallback]);
214 asyncCtx->deferred = CommonNapi::CreatePromise(env, asyncCtx->callbackRef, result);
215 napi_valuetype valueType = napi_undefined;
216 bool notParamValid = argCount < argCallback || napi_typeof(env, args[argPixelParam], &valueType) != napi_ok ||
217 valueType != napi_object ||
218 napi->GetFetchFrameArgs(asyncCtx, env, args[ARG_ZERO], args[ARG_ONE], args[ARG_TWO]) != MSERR_OK;
219 if (notParamValid) {
220 asyncCtx->SignError(MSERR_EXT_API9_INVALID_PARAMETER, "JsFetchFrameAtTime");
221 }
222
223 if (napi->state_ != HelperState::HELPER_STATE_RUNNABLE && !asyncCtx->errFlag) {
224 asyncCtx->SignError(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Current state is not runnable, can't fetchFrame.");
225 }
226
227 napi_value resource = nullptr;
228 napi_create_string_utf8(env, "JsFetchFrameAtTime", NAPI_AUTO_LENGTH, &resource);
229 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
230 auto asyncCtx = reinterpret_cast<AVImageGeneratorAsyncContext *>(data);
231 CHECK_AND_RETURN_LOG(asyncCtx && asyncCtx->innerHelper_, "Invalid AVImageGeneratorAsyncContext.");
232 auto pixelMap = asyncCtx->innerHelper_->
233 FetchFrameYuv(asyncCtx->timeUs_, asyncCtx->option_, asyncCtx->param_);
234 asyncCtx->pixel_ = pixelMap;
235 CHECK_AND_RETURN(asyncCtx->pixel_ == nullptr);
236 asyncCtx->SignError(MSERR_EXT_API9_UNSUPPORT_FORMAT, "FetchFrameByTime failed.");
237 }, CreatePixelMapComplete, static_cast<void *>(asyncCtx.get()), &asyncCtx->work));
238 NAPI_CALL(env, napi_queue_async_work(env, asyncCtx->work));
239 asyncCtx.release();
240 MEDIA_LOGI("JsFetchFrameAtTime Out");
241 return result;
242 }
243
CreatePixelMapComplete(napi_env env,napi_status status,void * data)244 void AVImageGeneratorNapi::CreatePixelMapComplete(napi_env env, napi_status status, void *data)
245 {
246 napi_value result = nullptr;
247
248 MEDIA_LOGI("CreatePixelMapComplete In");
249 auto context = static_cast<AVImageGeneratorAsyncContext*>(data);
250
251 if (status == napi_ok && context->errCode == napi_ok) {
252 MEDIA_LOGI("set pixel map success");
253 context->status = MSERR_OK;
254 result = Media::PixelMapNapi::CreatePixelMap(env, context->pixel_);
255 } else {
256 context->status = context->errCode == napi_ok ? MSERR_INVALID_VAL : context->errCode;
257 MEDIA_LOGW("set pixel map failed");
258 napi_get_undefined(env, &result);
259 }
260
261 CommonCallbackRoutine(env, context, result);
262 }
263
CommonCallbackRoutine(napi_env env,AVImageGeneratorAsyncContext * & asyncContext,const napi_value & valueParam)264 void AVImageGeneratorNapi::CommonCallbackRoutine(napi_env env, AVImageGeneratorAsyncContext* &asyncContext,
265 const napi_value &valueParam)
266 {
267 napi_value result[2] = {0};
268 napi_value retVal;
269 napi_value callback = nullptr;
270
271 napi_get_undefined(env, &result[0]);
272 napi_get_undefined(env, &result[1]);
273
274 napi_handle_scope scope = nullptr;
275 napi_open_handle_scope(env, &scope);
276 CHECK_AND_RETURN(scope != nullptr && asyncContext != nullptr);
277 if (asyncContext->status == ERR_OK) {
278 result[1] = valueParam;
279 }
280 napi_create_uint32(env, asyncContext->status, &result[0]);
281
282 if (asyncContext->errFlag) {
283 (void)CommonNapi::CreateError(env, asyncContext->errCode, asyncContext->errMessage, callback);
284 result[0] = callback;
285 }
286 if (asyncContext->deferred && asyncContext->status == ERR_OK) {
287 napi_resolve_deferred(env, asyncContext->deferred, result[1]);
288 } else if (asyncContext->deferred) {
289 napi_reject_deferred(env, asyncContext->deferred, result[0]);
290 } else {
291 napi_get_reference_value(env, asyncContext->callbackRef, &callback);
292 napi_call_function(env, nullptr, callback, ARG_TWO, result, &retVal);
293 napi_delete_reference(env, asyncContext->callbackRef);
294 }
295
296 napi_delete_async_work(env, asyncContext->work);
297 napi_close_handle_scope(env, scope);
298
299 delete asyncContext;
300 asyncContext = nullptr;
301 }
302
JsRelease(napi_env env,napi_callback_info info)303 napi_value AVImageGeneratorNapi::JsRelease(napi_env env, napi_callback_info info)
304 {
305 MediaTrace trace("AVImageGeneratorNapi::release");
306 napi_value result = nullptr;
307 napi_get_undefined(env, &result);
308 MEDIA_LOGI("JsRelease In");
309
310 auto promiseCtx = std::make_unique<AVImageGeneratorAsyncContext>(env);
311 napi_value args[1] = { nullptr };
312 size_t argCount = 1;
313 AVImageGeneratorNapi *generator = AVImageGeneratorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
314 CHECK_AND_RETURN_RET_LOG(generator != nullptr, result, "failed to GetJsInstance");
315 promiseCtx->innerHelper_ = generator->helper_;
316 promiseCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
317 promiseCtx->deferred = CommonNapi::CreatePromise(env, promiseCtx->callbackRef, result);
318
319 if (generator->state_ == HelperState::HELPER_STATE_RELEASED) {
320 promiseCtx->SignError(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Has released once, can't release again.");
321 }
322
323 napi_value resource = nullptr;
324 napi_create_string_utf8(env, "JsRelease", NAPI_AUTO_LENGTH, &resource);
325 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
326 auto promiseCtx = reinterpret_cast<AVImageGeneratorAsyncContext *>(data);
327 CHECK_AND_RETURN_LOG(promiseCtx && promiseCtx->innerHelper_, "Invalid promiseCtx.");
328 promiseCtx->innerHelper_->Release();
329 }, MediaAsyncContext::CompleteCallback, static_cast<void *>(promiseCtx.get()), &promiseCtx->work));
330 napi_queue_async_work_with_qos(env, promiseCtx->work, napi_qos_user_initiated);
331 promiseCtx.release();
332 MEDIA_LOGI("JsRelease Out");
333 return result;
334 }
335
JsSetAVFileDescriptor(napi_env env,napi_callback_info info)336 napi_value AVImageGeneratorNapi::JsSetAVFileDescriptor(napi_env env, napi_callback_info info)
337 {
338 MediaTrace trace("AVImageGeneratorNapi::set fd");
339 napi_value result = nullptr;
340 napi_get_undefined(env, &result);
341 MEDIA_LOGI("JsSetAVFileDescriptor In");
342
343 napi_value args[1] = { nullptr };
344 size_t argCount = 1; // url: string
345 AVImageGeneratorNapi *generator = AVImageGeneratorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
346 CHECK_AND_RETURN_RET_LOG(generator != nullptr, result, "failed to GetJsInstanceWithParameter");
347
348 CHECK_AND_RETURN_RET_LOG(
349 generator->state_ == HelperState::HELPER_STATE_IDLE, result, "Has set source once, unsupport set again");
350
351 napi_valuetype valueType = napi_undefined;
352 bool notValidParam = argCount < 1 || napi_typeof(env, args[0], &valueType) != napi_ok || valueType != napi_object ||
353 !CommonNapi::GetFdArgument(env, args[0], generator->fileDescriptor_);
354 CHECK_AND_RETURN_RET_LOG(!notValidParam, result, "Invalid file descriptor, return");
355 CHECK_AND_RETURN_RET_LOG(generator->helper_, result, "Invalid AVImageGeneratorNapi.");
356
357 auto fileDescriptor = generator->fileDescriptor_;
358 auto res = generator->helper_->SetSource(fileDescriptor.fd, fileDescriptor.offset, fileDescriptor.length);
359 generator->state_ = res == MSERR_OK ? HelperState::HELPER_STATE_RUNNABLE : HelperState::HELPER_ERROR;
360 return result;
361 }
362
JsGetAVFileDescriptor(napi_env env,napi_callback_info info)363 napi_value AVImageGeneratorNapi::JsGetAVFileDescriptor(napi_env env, napi_callback_info info)
364 {
365 MediaTrace trace("AVImageGeneratorNapi::get fd");
366 napi_value result = nullptr;
367 napi_get_undefined(env, &result);
368 MEDIA_LOGI("JsGetAVFileDescriptor In");
369
370 AVImageGeneratorNapi *generator = AVImageGeneratorNapi::GetJsInstance(env, info);
371 CHECK_AND_RETURN_RET_LOG(generator != nullptr, result, "failed to GetJsInstance");
372
373 napi_value value = nullptr;
374 (void)napi_create_object(env, &value);
375 (void)CommonNapi::AddNumberPropInt32(env, value, "fd", generator->fileDescriptor_.fd);
376 (void)CommonNapi::AddNumberPropInt64(env, value, "offset", generator->fileDescriptor_.offset);
377 (void)CommonNapi::AddNumberPropInt64(env, value, "length", generator->fileDescriptor_.length);
378
379 MEDIA_LOGI("JsGetAVFileDescriptor Out");
380 return value;
381 }
382
GetJsInstance(napi_env env,napi_callback_info info)383 AVImageGeneratorNapi* AVImageGeneratorNapi::GetJsInstance(napi_env env, napi_callback_info info)
384 {
385 size_t argCount = 0;
386 napi_value jsThis = nullptr;
387 napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
388 CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr, "failed to napi_get_cb_info");
389
390 AVImageGeneratorNapi *generator = nullptr;
391 status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&generator));
392 CHECK_AND_RETURN_RET_LOG(status == napi_ok && generator != nullptr, nullptr, "failed to napi_unwrap");
393
394 return generator;
395 }
396
GetJsInstanceWithParameter(napi_env env,napi_callback_info info,size_t & argc,napi_value * argv)397 AVImageGeneratorNapi* AVImageGeneratorNapi::GetJsInstanceWithParameter(napi_env env, napi_callback_info info,
398 size_t &argc, napi_value *argv)
399 {
400 napi_value jsThis = nullptr;
401 napi_status status = napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr);
402 CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr, "failed to napi_get_cb_info");
403
404 AVImageGeneratorNapi *generator = nullptr;
405 status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&generator));
406 CHECK_AND_RETURN_RET_LOG(status == napi_ok && generator != nullptr, nullptr, "failed to napi_unwrap");
407
408 return generator;
409 }
410 } // namespace Media
411 } // namespace OHOS