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 "avmetadataextractor_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 #include "ipc_skeleton.h"
29 #include "tokenid_kit.h"
30
31 using namespace OHOS::AudioStandard;
32
33 namespace {
34 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "AVMetadataExtractorNapi"};
35 }
36
37 namespace OHOS {
38 namespace Media {
39 static const std::map<HelperStates, std::string> stateMap = {
40 {HELPER_IDLE, AVMetadataHelperState::STATE_IDLE},
41 {HELPER_PREPARED, AVMetadataHelperState::STATE_PREPARED},
42 {HELPER_RELEASED, AVMetadataHelperState::STATE_RELEASED},
43 {HELPER_CALL_DONE, AVMetadataHelperState::STATE_CALL_DONE},
44 };
45
46 thread_local napi_ref AVMetadataExtractorNapi::constructor_ = nullptr;
47 const std::string CLASS_NAME = "AVMetadataExtractor";
48
AVMetadataExtractorNapi()49 AVMetadataExtractorNapi::AVMetadataExtractorNapi()
50 {
51 MEDIA_LOGI("0x%{public}06" PRIXPTR " Instances create", FAKE_POINTER(this));
52 }
53
~AVMetadataExtractorNapi()54 AVMetadataExtractorNapi::~AVMetadataExtractorNapi()
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 AVMetadataExtractorNapi::Init(napi_env env, napi_value exports)
60 {
61 napi_property_descriptor staticProperty[] = {
62 DECLARE_NAPI_STATIC_FUNCTION("createAVMetadataExtractor", JsCreateAVMetadataExtractor),
63 };
64
65 napi_property_descriptor properties[] = {
66 DECLARE_NAPI_FUNCTION("fetchMetadata", JsResolveMetadata),
67 DECLARE_NAPI_FUNCTION("fetchAlbumCover", JsFetchArtPicture),
68 DECLARE_NAPI_FUNCTION("release", JsRelease),
69
70 DECLARE_NAPI_GETTER_SETTER("url", JsGetUrl, JsSetUrl),
71 DECLARE_NAPI_GETTER_SETTER("fdSrc", JsGetAVFileDescriptor, JsSetAVFileDescriptor),
72 DECLARE_NAPI_GETTER_SETTER("dataSrc", JsGetDataSrc, JsSetDataSrc),
73 };
74
75 napi_value constructor = nullptr;
76 napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
77 sizeof(properties) / sizeof(properties[0]), properties, &constructor);
78 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define AVMetadataHelper class");
79
80 status = napi_create_reference(env, constructor, 1, &constructor_);
81 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to create reference of constructor");
82
83 status = napi_set_named_property(env, exports, CLASS_NAME.c_str(), constructor);
84 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to set constructor");
85
86 status = napi_define_properties(env, exports, sizeof(staticProperty) / sizeof(staticProperty[0]), staticProperty);
87 CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define static function");
88
89 MEDIA_LOGD("Init success");
90 return exports;
91 }
92
Constructor(napi_env env,napi_callback_info info)93 napi_value AVMetadataExtractorNapi::Constructor(napi_env env, napi_callback_info info)
94 {
95 napi_value result = nullptr;
96 napi_get_undefined(env, &result);
97
98 size_t argCount = 0;
99 napi_value jsThis = nullptr;
100 napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
101 CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "failed to napi_get_cb_info");
102
103 AVMetadataExtractorNapi *extractor = new(std::nothrow) AVMetadataExtractorNapi();
104 CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to new AVMetadataExtractorNapi");
105
106 extractor->env_ = env;
107 extractor->helper_ = AVMetadataHelperFactory::CreateAVMetadataHelper();
108 CHECK_AND_RETURN_RET_LOG(extractor->helper_ != nullptr, result, "failed to CreateMetadataHelper");
109
110 extractor->taskQue_ = std::make_unique<TaskQueue>("AVMetadataExtractorNapi");
111 (void)extractor->taskQue_->Start();
112
113 extractor->extractorCb_ = std::make_shared<AVMetadataHelperCallback>(env, extractor);
114 (void)extractor->helper_->SetHelperCallback(extractor->extractorCb_);
115
116 status = napi_wrap(env, jsThis, reinterpret_cast<void *>(extractor),
117 AVMetadataExtractorNapi::Destructor, nullptr, nullptr);
118 if (status != napi_ok) {
119 delete extractor;
120 MEDIA_LOGE("Failed to wrap native instance");
121 return result;
122 }
123
124 MEDIA_LOGI("Constructor success");
125 return jsThis;
126 }
127
Destructor(napi_env env,void * nativeObject,void * finalize)128 void AVMetadataExtractorNapi::Destructor(napi_env env, void *nativeObject, void *finalize)
129 {
130 (void)finalize;
131 if (nativeObject != nullptr) {
132 AVMetadataExtractorNapi *extractor = reinterpret_cast<AVMetadataExtractorNapi *>(nativeObject);
133 auto task = extractor->ReleaseTask();
134 if (task != nullptr) {
135 MEDIA_LOGI("Destructor Wait Release Task Start");
136 task->GetResult(); // sync release
137 MEDIA_LOGI("Destructor Wait Release Task End");
138 }
139 extractor->WaitTaskQueStop();
140 delete extractor;
141 }
142 MEDIA_LOGI("Destructor success");
143 }
144
JsCreateAVMetadataExtractor(napi_env env,napi_callback_info info)145 napi_value AVMetadataExtractorNapi::JsCreateAVMetadataExtractor(napi_env env, napi_callback_info info)
146 {
147 MediaTrace trace("AVMetadataExtractorNapi::JsCreateAVMetadataExtractor");
148 napi_value result = nullptr;
149 napi_get_undefined(env, &result);
150 MEDIA_LOGI("JsCreateAVMetadataExtractor In");
151
152 std::unique_ptr<MediaAsyncContext> asyncContext = std::make_unique<MediaAsyncContext>(env);
153
154 // get args
155 napi_value jsThis = nullptr;
156 napi_value args[1] = { nullptr };
157 size_t argCount = 1;
158 napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
159 CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr, "failed to napi_get_cb_info");
160
161 asyncContext->callbackRef = CommonNapi::CreateReference(env, args[0]);
162 asyncContext->deferred = CommonNapi::CreatePromise(env, asyncContext->callbackRef, result);
163 asyncContext->JsResult = std::make_unique<MediaJsResultInstance>(constructor_);
164 asyncContext->ctorFlag = true;
165
166 napi_value resource = nullptr;
167 napi_create_string_utf8(env, "JsCreateAVMetadataExtractor", NAPI_AUTO_LENGTH, &resource);
168 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {},
169 MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncContext.get()), &asyncContext->work));
170 NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work));
171 asyncContext.release();
172 MEDIA_LOGI("JsCreateAVMetadataExtractor Out");
173 return result;
174 }
175
ResolveMetadataTask(std::unique_ptr<AVMetadataExtractorAsyncContext> & promiseCtx)176 std::shared_ptr<TaskHandler<TaskRet>> AVMetadataExtractorNapi::ResolveMetadataTask(
177 std::unique_ptr<AVMetadataExtractorAsyncContext> &promiseCtx)
178 {
179 auto task = std::make_shared<TaskHandler<TaskRet>>([this, &metadata = promiseCtx->metadata_]() {
180 MEDIA_LOGI("ResolveMetadata Task In");
181 std::unique_lock<std::mutex> lock(taskMutex_);
182 auto state = GetCurrentState();
183 if (state == AVMetadataHelperState::STATE_PREPARED || state == AVMetadataHelperState::STATE_CALL_DONE) {
184 std::unordered_map<int32_t, std::string> res = helper_->ResolveMetadata();
185 MEDIA_LOGD("ResolveMetadata Task end resolve: %{public}zu", res.size());
186 metadata = std::make_shared<std::unordered_map<int32_t, std::string>>(res);
187
188 stopWait_ = false;
189 LISTENER(stateChangeCond_.wait(lock, [this]() { return stopWait_.load(); }), "ResolveMetadataTask", false)
190
191 if (GetCurrentState() == AVMetadataHelperState::STATE_ERROR) {
192 return TaskRet(MSERR_EXT_API9_OPERATE_NOT_PERMIT,
193 "failed to resolve metadata, metadata helper enter error status!");
194 }
195 } else {
196 return TaskRet(MSERR_EXT_API9_OPERATE_NOT_PERMIT,
197 "current state is not initialized, unsupport resolve metadata operation");
198 }
199
200 MEDIA_LOGI("ResolveMetadata Task Out");
201 return TaskRet(MSERR_EXT_API9_OK, "Success");
202 });
203
204 (void)taskQue_->EnqueueTask(task);
205 return task;
206 }
207
JsResolveMetadata(napi_env env,napi_callback_info info)208 napi_value AVMetadataExtractorNapi::JsResolveMetadata(napi_env env, napi_callback_info info)
209 {
210 MediaTrace trace("AVMetadataExtractorNapi::resolveMetadata");
211 napi_value result = nullptr;
212 napi_get_undefined(env, &result);
213 MEDIA_LOGI("JsResolveMetadata In");
214
215 auto promiseCtx = std::make_unique<AVMetadataExtractorAsyncContext>(env);
216 napi_value args[1] = { nullptr };
217 size_t argCount = 1;
218
219 AVMetadataExtractorNapi* extractor
220 = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
221 CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
222 promiseCtx->napi = extractor;
223 promiseCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
224 promiseCtx->deferred = CommonNapi::CreatePromise(env, promiseCtx->callbackRef, result);
225
226 promiseCtx->task_ = extractor->ResolveMetadataTask(promiseCtx);
227
228 // async work
229 napi_value resource = nullptr;
230 napi_create_string_utf8(env, "JsResolveMetadata", NAPI_AUTO_LENGTH, &resource);
231 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource,
232 [](napi_env env, void *data) {
233 auto promiseCtx = reinterpret_cast<AVMetadataExtractorAsyncContext *>(data);
234 CHECK_AND_RETURN_LOG(promiseCtx != nullptr, "promiseCtx is nullptr!");
235
236 if (promiseCtx->task_) {
237 auto result = promiseCtx->task_->GetResult();
238 MEDIA_LOGD("JsResolveMetadata get result end");
239 if (result.Value().first != MSERR_EXT_API9_OK) {
240 MEDIA_LOGE("JsResolveMetadata get result SignError");
241 promiseCtx->SignError(result.Value().first, result.Value().second);
242 }
243 }
244 MEDIA_LOGI("The js thread of resolving meta data finishes execution and returns");
245 },
246 ResolveMetadataComplete, static_cast<void *>(promiseCtx.get()), &promiseCtx->work));
247 NAPI_CALL(env, napi_queue_async_work(env, promiseCtx->work));
248 promiseCtx.release();
249 MEDIA_LOGI("JsResolveMetadata Out");
250 return result;
251 }
252
ResolveMetadataComplete(napi_env env,napi_status status,void * data)253 void AVMetadataExtractorNapi::ResolveMetadataComplete(napi_env env, napi_status status, void *data)
254 {
255 MEDIA_LOGI("ResolveMetadataComplete In");
256 auto promiseCtx = static_cast<AVMetadataExtractorAsyncContext*>(data);
257 CHECK_AND_RETURN_LOG(promiseCtx != nullptr, "promiseCtx is nullptr!");
258
259 napi_value result = nullptr;
260 napi_create_object(env, &result);
261
262 if (status == napi_ok && promiseCtx->errCode == napi_ok) {
263 for (auto iter = promiseCtx->metadata_->begin(); iter != promiseCtx->metadata_->end(); ++iter) {
264 MEDIA_LOGI("Resolve metadata completed, key: %{public}d, val: %{public}s",
265 iter->first, iter->second.c_str());
266
267 napi_value keyNapi = nullptr;
268 auto it = g_MetadataCodeMap.find(iter->first);
269 const char* key = nullptr;
270 if (it != g_MetadataCodeMap.end()) {
271 key = it->second;
272 }
273 if (key == nullptr) {
274 MEDIA_LOGW("failed to get key: %{public}d", iter->first);
275 continue;
276 }
277 napi_status st = napi_create_string_utf8(env, key, NAPI_AUTO_LENGTH, &keyNapi);
278
279 napi_value valueNapi = nullptr;
280 st = napi_create_string_utf8(env, iter->second.c_str(), NAPI_AUTO_LENGTH, &valueNapi);
281 napi_set_property(env, result, keyNapi, valueNapi);
282 if (st != napi_ok) {
283 MEDIA_LOGW("failed to set property: %{public}d", iter->first);
284 continue;
285 }
286 }
287 promiseCtx->status = ERR_OK;
288 } else {
289 promiseCtx->status = promiseCtx->errCode == napi_ok ? MSERR_INVALID_VAL : promiseCtx->errCode;
290 MEDIA_LOGI("Resolve meta data failed");
291 napi_get_undefined(env, &result);
292 }
293
294 CommonCallbackRoutine(env, promiseCtx, result);
295 }
296
FetchArtPictureTask(std::unique_ptr<AVMetadataExtractorAsyncContext> & promiseCtx)297 std::shared_ptr<TaskHandler<TaskRet>> AVMetadataExtractorNapi::FetchArtPictureTask(
298 std::unique_ptr<AVMetadataExtractorAsyncContext> &promiseCtx)
299 {
300 auto task = std::make_shared<TaskHandler<TaskRet>>(
301 [this, &napi = promiseCtx->napi, &picture = promiseCtx->artPicture_]() {
302 MEDIA_LOGI("FetchArtPicture Task In");
303 std::unique_lock<std::mutex> lock(taskMutex_);
304 auto state = GetCurrentState();
305 if (state == AVMetadataHelperState::STATE_PREPARED || state == AVMetadataHelperState::STATE_CALL_DONE) {
306 auto mem = helper_->FetchArtPicture();
307 if (mem == nullptr) {
308 return TaskRet(MSERR_EXT_API9_OPERATE_NOT_PERMIT,
309 "FetchArtPicture result is nullptr.");
310 }
311 MEDIA_LOGD("FetchArtPicture Task end size: %{public}d", mem->GetSize());
312 SourceOptions options;
313 uint32_t errCode;
314 std::unique_ptr<ImageSource> imageSource
315 = ImageSource::CreateImageSource(mem->GetBase(), mem->GetSize(), options, errCode);
316 if (imageSource == nullptr) {
317 return TaskRet(MSERR_EXT_API9_INVALID_PARAMETER,
318 "Create image source failed.");
319 }
320
321 DecodeOptions decodeParam;
322 std::unique_ptr<PixelMap> pixelMap = imageSource->CreatePixelMap(0, decodeParam, errCode);
323 if (pixelMap == nullptr) {
324 return TaskRet(MSERR_EXT_API9_INVALID_PARAMETER,
325 "Create pixel map failed.");
326 }
327 picture = std::move(pixelMap);
328
329 stopWait_ = false;
330 LISTENER(stateChangeCond_.wait(lock, [this]() { return stopWait_.load(); }), "FetchArtPictureTask", false)
331
332 if (GetCurrentState() == AVMetadataHelperState::STATE_ERROR) {
333 return TaskRet(MSERR_EXT_API9_OPERATE_NOT_PERMIT,
334 "failed to FetchArtPicture, metadata helper enter error status!");
335 }
336 } else {
337 return TaskRet(MSERR_EXT_API9_OPERATE_NOT_PERMIT,
338 "current state is not initialized, unsupport FetchArtPicture operation");
339 }
340
341 MEDIA_LOGI("FetchArtPicture Task Out");
342 return TaskRet(MSERR_EXT_API9_OK, "Success");
343 });
344
345 (void)taskQue_->EnqueueTask(task);
346 return task;
347 }
348
JsFetchArtPicture(napi_env env,napi_callback_info info)349 napi_value AVMetadataExtractorNapi::JsFetchArtPicture(napi_env env, napi_callback_info info)
350 {
351 MediaTrace trace("AVMetadataExtractorNapi::fetchArtPicture");
352 napi_value result = nullptr;
353 napi_get_undefined(env, &result);
354 MEDIA_LOGI("JsFetchArtPicture In");
355
356 auto promiseCtx = std::make_unique<AVMetadataExtractorAsyncContext>(env);
357 napi_value args[1] = { nullptr };
358 size_t argCount = 1;
359
360 AVMetadataExtractorNapi* extractor
361 = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
362 CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
363 promiseCtx->napi = extractor;
364 promiseCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
365 promiseCtx->deferred = CommonNapi::CreatePromise(env, promiseCtx->callbackRef, result);
366
367 promiseCtx->task_ = extractor->FetchArtPictureTask(promiseCtx);
368
369 // async work
370 napi_value resource = nullptr;
371 napi_create_string_utf8(env, "JsFetchArtPicture", NAPI_AUTO_LENGTH, &resource);
372 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource,
373 [](napi_env env, void *data) {
374 MEDIA_LOGI("JsFetchArtPicture task start");
375 auto promiseCtx = reinterpret_cast<AVMetadataExtractorAsyncContext *>(data);
376 CHECK_AND_RETURN_LOG(promiseCtx != nullptr, "promiseCtx is nullptr!");
377
378 if (promiseCtx->task_) {
379 auto result = promiseCtx->task_->GetResult();
380 MEDIA_LOGD("JsFetchArtPicture get result end");
381 if (result.Value().first != MSERR_EXT_API9_OK) {
382 MEDIA_LOGI("JsFetchArtPicture get result SignError");
383 promiseCtx->SignError(result.Value().first, result.Value().second);
384 }
385 }
386 MEDIA_LOGI("The js thread of resolving meta data finishes execution and returns");
387 },
388 FetchArtPictureComplete, static_cast<void *>(promiseCtx.get()), &promiseCtx->work));
389 NAPI_CALL(env, napi_queue_async_work(env, promiseCtx->work));
390 promiseCtx.release();
391 MEDIA_LOGI("JsFetchArtPicture Out");
392 return result;
393 }
394
FetchArtPictureComplete(napi_env env,napi_status status,void * data)395 void AVMetadataExtractorNapi::FetchArtPictureComplete(napi_env env, napi_status status, void *data)
396 {
397 napi_value result = nullptr;
398
399 MEDIA_LOGI("FetchArtPictureComplete In");
400 auto context = static_cast<AVMetadataExtractorAsyncContext*>(data);
401
402 if (status == napi_ok && context->errCode == napi_ok) {
403 result = Media::PixelMapNapi::CreatePixelMap(env, context->artPicture_);
404 context->status = ERR_OK;
405 } else {
406 context->status = context->errCode == napi_ok ? MSERR_INVALID_VAL : context->errCode;
407 napi_get_undefined(env, &result);
408 }
409
410 CommonCallbackRoutine(env, context, result);
411 }
412
CommonCallbackRoutine(napi_env env,AVMetadataExtractorAsyncContext * & asyncContext,const napi_value & valueParam)413 void AVMetadataExtractorNapi::CommonCallbackRoutine(napi_env env, AVMetadataExtractorAsyncContext* &asyncContext,
414 const napi_value &valueParam)
415 {
416 napi_value result[2] = {0};
417 napi_value retVal;
418 napi_value callback = nullptr;
419
420 napi_get_undefined(env, &result[0]);
421 napi_get_undefined(env, &result[1]);
422
423 napi_handle_scope scope = nullptr;
424 napi_open_handle_scope(env, &scope);
425 if (scope == nullptr) {
426 MEDIA_LOGW("scope is null");
427 return;
428 }
429
430 if (asyncContext == nullptr) {
431 MEDIA_LOGW("asyncContext is null");
432 return;
433 }
434
435 if (asyncContext->status == ERR_OK) {
436 result[1] = valueParam;
437 napi_create_uint32(env, asyncContext->status, &result[0]);
438 } else {
439 napi_create_uint32(env, asyncContext->status, &result[0]);
440 }
441
442 if (asyncContext->deferred) {
443 MEDIA_LOGD("deferred in");
444 if (asyncContext->status == ERR_OK) {
445 napi_resolve_deferred(env, asyncContext->deferred, result[1]);
446 } else {
447 napi_reject_deferred(env, asyncContext->deferred, result[0]);
448 }
449 } else {
450 MEDIA_LOGD("callback in");
451 napi_get_reference_value(env, asyncContext->callbackRef, &callback);
452 napi_call_function(env, nullptr, callback, 2, result, &retVal); // 2
453 napi_delete_reference(env, asyncContext->callbackRef);
454 }
455
456 napi_delete_async_work(env, asyncContext->work);
457 napi_close_handle_scope(env, scope);
458
459 delete asyncContext;
460 asyncContext = nullptr;
461 }
462
WaitTaskQueStop()463 void AVMetadataExtractorNapi::WaitTaskQueStop()
464 {
465 MEDIA_LOGI("WaitTaskQueStop In");
466 std::unique_lock<std::mutex> lock(taskMutex_);
467 LISTENER(stopTaskQueCond_.wait(lock, [this]() { return taskQueStoped_; }), "StopTaskQue", false)
468 MEDIA_LOGI("WaitTaskQueStop Out");
469 }
470
StopTaskQue()471 void AVMetadataExtractorNapi::StopTaskQue()
472 {
473 MEDIA_LOGI("StopTaskQue In");
474 taskQue_->Stop();
475 taskQueStoped_ = true;
476 stopTaskQueCond_.notify_all();
477 MEDIA_LOGI("StopTaskQue Out");
478 }
479
ReleaseTask()480 std::shared_ptr<TaskHandler<TaskRet>> AVMetadataExtractorNapi::ReleaseTask()
481 {
482 std::shared_ptr<TaskHandler<TaskRet>> task = nullptr;
483 if (isReleased_.load()) {
484 MEDIA_LOGE("Instance is released.");
485 return task;
486 }
487
488 task = std::make_shared<TaskHandler<TaskRet>>([this]() {
489 MEDIA_LOGI("Release Task In");
490 PauseListenCurrentResource(); // Pause event listening for the current resource
491 ResetUserParameters();
492
493 if (helper_ != nullptr) {
494 (void)helper_->Release();
495 helper_ = nullptr;
496 }
497
498 if (extractorCb_ != nullptr) {
499 extractorCb_->Release();
500 }
501
502 std::thread(&AVMetadataExtractorNapi::StopTaskQue, this).detach();
503
504 MEDIA_LOGI("Release Task Out");
505 return TaskRet(MSERR_EXT_API9_OK, "Success");
506 });
507
508 std::unique_lock<std::mutex> lock(taskMutex_);
509 isReleased_.store(true);
510 (void)taskQue_->EnqueueTask(task, true); // CancelNotExecutedTask
511 stopWait_ = true;
512 stateChangeCond_.notify_all();
513 return task;
514 }
515
JsRelease(napi_env env,napi_callback_info info)516 napi_value AVMetadataExtractorNapi::JsRelease(napi_env env, napi_callback_info info)
517 {
518 MediaTrace trace("AVMetadataExtractorNapi::release");
519 napi_value result = nullptr;
520 napi_get_undefined(env, &result);
521 MEDIA_LOGI("JsRelease In");
522
523 auto promiseCtx = std::make_unique<AVMetadataExtractorAsyncContext>(env);
524 napi_value args[1] = { nullptr };
525 size_t argCount = 1;
526 AVMetadataExtractorNapi *extractor
527 = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
528 CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
529 promiseCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
530 promiseCtx->deferred = CommonNapi::CreatePromise(env, promiseCtx->callbackRef, result);
531
532 promiseCtx->task_ = extractor->ReleaseTask();
533 if (extractor->dataSrcCb_ != nullptr) {
534 extractor->dataSrcCb_->ClearCallbackReference();
535 extractor->dataSrcCb_ = nullptr;
536 }
537
538 napi_value resource = nullptr;
539 napi_create_string_utf8(env, "JsRelease", NAPI_AUTO_LENGTH, &resource);
540 NAPI_CALL(env, napi_create_async_work(env, nullptr, resource,
541 [](napi_env env, void *data) {
542 auto promiseCtx = reinterpret_cast<AVMetadataExtractorAsyncContext *>(data);
543 CHECK_AND_RETURN_LOG(promiseCtx != nullptr, "promiseCtx is nullptr!");
544 if (promiseCtx->task_ == nullptr) {
545 promiseCtx->SignError(MSERR_INVALID_VAL, "Async release task is invalid.");
546 return;
547 }
548
549 auto result = promiseCtx->task_->GetResult();
550 MEDIA_LOGD("Release task GetResult end.");
551 if (result.HasResult() && result.Value().first != MSERR_EXT_API9_OK) {
552 promiseCtx->SignError(result.Value().first, result.Value().second);
553 return;
554 }
555 MEDIA_LOGI("Release task completed.");
556 },
557 MediaAsyncContext::CompleteCallback, static_cast<void *>(promiseCtx.get()), &promiseCtx->work));
558 napi_queue_async_work_with_qos(env, promiseCtx->work, napi_qos_user_initiated);
559 promiseCtx.release();
560 MEDIA_LOGI("JsRelease Out");
561 return result;
562 }
563
SetSource(std::string url)564 void AVMetadataExtractorNapi::SetSource(std::string url)
565 {
566 MEDIA_LOGI("input url is %{public}s!", url.c_str());
567 bool isFd = (url.find("fd://") != std::string::npos) ? true : false;
568 bool isNetwork = (url.find("http") != std::string::npos) ? true : false;
569 if (isNetwork) {
570 auto task = std::make_shared<TaskHandler<void>>([this, url]() {
571 std::unique_lock<std::mutex> lock(taskMutex_);
572 auto state = GetCurrentState();
573 if (state != AVMetadataHelperState::STATE_IDLE) {
574 OnErrorCb(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "current state is not idle, unsupport set url");
575 return;
576 }
577 if (helper_ != nullptr) {
578 if (helper_->SetSource(url, AVMetadataUsage::AV_META_USAGE_META_ONLY) != MSERR_OK) {
579 OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER, "failed to SetSourceNetWork");
580 }
581 stopWait_ = false;
582 LISTENER(stateChangeCond_.wait(lock, [this]() { return stopWait_.load(); }),
583 "SetSourceNetWork", false)
584 }
585 });
586 (void)taskQue_->EnqueueTask(task);
587 } else if (isFd) {
588 std::string inputFd = url.substr(sizeof("fd://") - 1);
589 int32_t fd = -1;
590 if (!StrToInt(inputFd, fd) || fd < 0) {
591 OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER, "The input parameter is not a fd://+numeric string");
592 return;
593 }
594 auto task = std::make_shared<TaskHandler<void>>([this, fd]() {
595 std::unique_lock<std::mutex> lock(taskMutex_);
596 auto state = GetCurrentState();
597 if (state != AVMetadataHelperState::STATE_IDLE) {
598 OnErrorCb(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "current state is not idle, unsupport set source fd");
599 return;
600 }
601 if (helper_ != nullptr) {
602 if (helper_->SetSource(fd, 0, -1, AVMetadataUsage::AV_META_USAGE_META_ONLY) != MSERR_OK) {
603 OnErrorCb(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "failed to SetSourceFd");
604 }
605 stopWait_ = false;
606 LISTENER(stateChangeCond_.wait(lock, [this]() { return stopWait_.load(); }), "SetSourceFd", false)
607 }
608 });
609 (void)taskQue_->EnqueueTask(task);
610 } else {
611 OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER, "The input parameter is not fd:// or network address");
612 }
613 }
614
JsSetUrl(napi_env env,napi_callback_info info)615 napi_value AVMetadataExtractorNapi::JsSetUrl(napi_env env, napi_callback_info info)
616 {
617 MediaTrace trace("AVMetadataExtractorNapi::set url");
618 napi_value result = nullptr;
619 napi_get_undefined(env, &result);
620 MEDIA_LOGI("JsSetUrl In");
621
622 napi_value args[1] = { nullptr };
623 size_t argCount = 1; // url: string
624 AVMetadataExtractorNapi *extractor
625 = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
626 CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstanceWithParameter");
627
628 if (extractor->GetCurrentState() != AVMetadataHelperState::STATE_IDLE) {
629 extractor->OnErrorCb(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "current state is not idle, unsupport set url");
630 return result;
631 }
632
633 napi_valuetype valueType = napi_undefined;
634 if (argCount < 1 || napi_typeof(env, args[0], &valueType) != napi_ok || valueType != napi_string) {
635 extractor->OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER, "url is not string");
636 return result;
637 }
638
639 // get url from js
640 extractor->url_ = CommonNapi::GetStringArgument(env, args[0]);
641 extractor->SetSource(extractor->url_);
642
643 MEDIA_LOGI("JsSetUrl Out");
644 return result;
645 }
646
JsGetUrl(napi_env env,napi_callback_info info)647 napi_value AVMetadataExtractorNapi::JsGetUrl(napi_env env, napi_callback_info info)
648 {
649 MediaTrace trace("AVMetadataExtractorNapi::get url");
650 napi_value result = nullptr;
651 napi_get_undefined(env, &result);
652 MEDIA_LOGI("JsGetUrl In");
653
654 AVMetadataExtractorNapi *extractor = AVMetadataExtractorNapi::GetJsInstance(env, info);
655 CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
656
657 napi_value value = nullptr;
658 (void)napi_create_string_utf8(env, extractor->url_.c_str(), NAPI_AUTO_LENGTH, &value);
659
660 MEDIA_LOGI("JsGetUrl Out Current Url: %{public}s", extractor->url_.c_str());
661 return value;
662 }
663
SetAVFileDescriptorTask(std::shared_ptr<AVMetadataHelper> & avHelper,AVFileDescriptor & fileDescriptor)664 void AVMetadataExtractorNapi::SetAVFileDescriptorTask(std::shared_ptr<AVMetadataHelper>& avHelper,
665 AVFileDescriptor& fileDescriptor)
666 {
667 auto task = std::make_shared<TaskHandler<void>>([this, &helper_ = avHelper, &fileDescriptor_ = fileDescriptor]() {
668 MEDIA_LOGI("SetAVFileDescriptor Task");
669 std::unique_lock<std::mutex> lock(taskMutex_);
670 auto state = GetCurrentState();
671 if (state != AVMetadataHelperState::STATE_IDLE) {
672 OnErrorCb(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "current state is not idle, unsupport set source fd");
673 return;
674 }
675
676 if (helper_ != nullptr) {
677 if (helper_->SetSource(fileDescriptor_.fd, fileDescriptor_.offset, fileDescriptor_.length,
678 AVMetadataUsage::AV_META_USAGE_META_ONLY) != MSERR_OK) {
679 OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER, "Helper SetSource FileDescriptor failed");
680 }
681 stopWait_ = false;
682 LISTENER(stateChangeCond_.wait(lock, [this]() { return stopWait_.load(); }),
683 "SetSource FileDescriptor", false)
684 }
685 MEDIA_LOGI("SetSource FileDescriptor end");
686 });
687 (void)taskQue_->EnqueueTask(task);
688 }
689
JsSetAVFileDescriptor(napi_env env,napi_callback_info info)690 napi_value AVMetadataExtractorNapi::JsSetAVFileDescriptor(napi_env env, napi_callback_info info)
691 {
692 MediaTrace trace("AVMetadataExtractorNapi::set fd");
693 napi_value result = nullptr;
694 napi_get_undefined(env, &result);
695 MEDIA_LOGI("JsSetAVFileDescriptor In");
696
697 napi_value args[1] = { nullptr };
698 size_t argCount = 1; // url: string
699 AVMetadataExtractorNapi *extractor
700 = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
701 CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstanceWithParameter");
702
703 if (extractor->GetCurrentState() != AVMetadataHelperState::STATE_IDLE) {
704 extractor->OnErrorCb(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "current state is not idle, unsupport set fd");
705 return result;
706 }
707
708 extractor->StartListenCurrentResource(); // Listen to the events of the current resource
709 napi_valuetype valueType = napi_undefined;
710 if (argCount < 1 || napi_typeof(env, args[0], &valueType) != napi_ok || valueType != napi_object) {
711 extractor->OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER, "SetAVFileDescriptor is not napi_object");
712 return result;
713 }
714
715 if (!CommonNapi::GetFdArgument(env, args[0], extractor->fileDescriptor_)) {
716 MEDIA_LOGE("get fileDescriptor argument failed!");
717 extractor->OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER,
718 "invalid parameters, please check the input parameters(fileDescriptor)");
719 return result;
720 }
721 extractor->SetAVFileDescriptorTask(extractor->helper_, extractor->fileDescriptor_);
722 MEDIA_LOGI("JsSetAVFileDescriptor Out");
723 return result;
724 }
725
JsGetAVFileDescriptor(napi_env env,napi_callback_info info)726 napi_value AVMetadataExtractorNapi::JsGetAVFileDescriptor(napi_env env, napi_callback_info info)
727 {
728 MediaTrace trace("AVMetadataExtractorNapi::get fd");
729 napi_value result = nullptr;
730 napi_get_undefined(env, &result);
731 MEDIA_LOGI("JsGetAVFileDescriptor In");
732
733 AVMetadataExtractorNapi *extractor = AVMetadataExtractorNapi::GetJsInstance(env, info);
734 CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
735
736 napi_value value = nullptr;
737 (void)napi_create_object(env, &value);
738 (void)CommonNapi::AddNumberPropInt32(env, value, "fd", extractor->fileDescriptor_.fd);
739 (void)CommonNapi::AddNumberPropInt64(env, value, "offset", extractor->fileDescriptor_.offset);
740 (void)CommonNapi::AddNumberPropInt64(env, value, "length", extractor->fileDescriptor_.length);
741
742 MEDIA_LOGI("JsGetAVFileDescriptor Out");
743 return value;
744 }
745
SetDataSrcTask(std::shared_ptr<AVMetadataHelper> & avHelper,std::shared_ptr<HelperDataSourceCallback> & dataSrcCb)746 void AVMetadataExtractorNapi::SetDataSrcTask(std::shared_ptr<AVMetadataHelper>& avHelper,
747 std::shared_ptr<HelperDataSourceCallback>& dataSrcCb)
748 {
749 auto task = std::make_shared<TaskHandler<void>>([this, &helper_ = avHelper, &dataSrcCb_ = dataSrcCb]() {
750 MEDIA_LOGI("SetDataSrc Task");
751 std::unique_lock<std::mutex> lock(taskMutex_);
752 auto state = GetCurrentState();
753 if (state != AVMetadataHelperState::STATE_IDLE) {
754 OnErrorCb(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "current state is not idle, unsupport set data source");
755 return;
756 }
757
758 if (helper_ != nullptr) {
759 MEDIA_LOGI("SetDataSrc Task SetSource");
760 if (helper_->SetSource(dataSrcCb_) != MSERR_OK) {
761 OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER, "Meta helper SetSource DataSrc failed");
762 }
763
764 stopWait_ = false;
765 LISTENER(stateChangeCond_.wait(lock, [this]() { return stopWait_.load(); }), "Set data source", false)
766 }
767 MEDIA_LOGI("SetDataSrc Task SetSource end");
768 });
769 (void)taskQue_->EnqueueTask(task);
770 }
771
JsSetDataSrc(napi_env env,napi_callback_info info)772 napi_value AVMetadataExtractorNapi::JsSetDataSrc(napi_env env, napi_callback_info info)
773 {
774 MediaTrace trace("AVMetadataExtractorNapi::set dataSrc");
775 napi_value result = nullptr;
776 napi_get_undefined(env, &result);
777 MEDIA_LOGI("JsSetDataSrc In");
778
779 napi_value args[1] = { nullptr };
780 size_t argCount = 1;
781 AVMetadataExtractorNapi *jsMetaHelper
782 = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
783 CHECK_AND_RETURN_RET_LOG(jsMetaHelper != nullptr, result, "failed to GetJsInstanceWithParameter");
784
785 if (jsMetaHelper->GetCurrentState() != AVMetadataHelperState::STATE_IDLE) {
786 jsMetaHelper->OnErrorCb(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "current state is not idle, unsupport set dataSrc");
787 return result;
788 }
789 jsMetaHelper->StartListenCurrentResource(); // Listen to the events of the current resource
790
791 napi_valuetype valueType = napi_undefined;
792 if (argCount < 1 || napi_typeof(env, args[0], &valueType) != napi_ok || valueType != napi_object) {
793 jsMetaHelper->OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER, "args[0] is not napi_object");
794 return result;
795 }
796 (void)CommonNapi::GetPropertyInt64(env, args[0], "fileSize", jsMetaHelper->dataSrcDescriptor_.fileSize);
797 if (jsMetaHelper->dataSrcDescriptor_.fileSize < -1 || jsMetaHelper->dataSrcDescriptor_.fileSize == 0) {
798 jsMetaHelper->OnErrorCb(MSERR_EXT_API9_INVALID_PARAMETER,
799 "invalid parameters, please check parameter fileSize");
800 return result;
801 }
802 MEDIA_LOGI("Recvive filesize is %{public}" PRId64 "", jsMetaHelper->dataSrcDescriptor_.fileSize);
803 jsMetaHelper->dataSrcCb_
804 = std::make_shared<HelperDataSourceCallback>(env, jsMetaHelper->dataSrcDescriptor_.fileSize);
805
806 napi_value callback = nullptr;
807 napi_ref ref = nullptr;
808 napi_get_named_property(env, args[0], "callback", &callback);
809 jsMetaHelper->dataSrcDescriptor_.callback = callback;
810 napi_status status = napi_create_reference(env, callback, 1, &ref);
811 CHECK_AND_RETURN_RET_LOG(status == napi_ok && ref != nullptr, result, "failed to create reference!");
812 std::shared_ptr<AutoRef> autoRef = std::make_shared<AutoRef>(env, ref);
813 jsMetaHelper->dataSrcCb_->SaveCallbackReference(HELPER_READAT_CALLBACK_NAME, autoRef);
814
815 jsMetaHelper->SetDataSrcTask(jsMetaHelper->helper_, jsMetaHelper->dataSrcCb_);
816
817 MEDIA_LOGI("JsSetDataSrc Out");
818 return result;
819 }
820
JsGetDataSrc(napi_env env,napi_callback_info info)821 napi_value AVMetadataExtractorNapi::JsGetDataSrc(napi_env env, napi_callback_info info)
822 {
823 MediaTrace trace("AVMetadataExtractorNapi::get dataSrc");
824 napi_value result = nullptr;
825 napi_get_undefined(env, &result);
826 MEDIA_LOGI("JsGetDataSrc In");
827
828 AVMetadataExtractorNapi *jsMetaHelper = AVMetadataExtractorNapi::GetJsInstance(env, info);
829 CHECK_AND_RETURN_RET_LOG(jsMetaHelper != nullptr, result, "failed to GetJsInstance");
830 CHECK_AND_RETURN_RET_LOG(jsMetaHelper->dataSrcCb_ != nullptr, result, "failed to check dataSrcCb_");
831
832 napi_value value = nullptr;
833 int64_t fileSize;
834 napi_value callback = nullptr;
835 (void)napi_create_object(env, &value);
836 (void)jsMetaHelper->dataSrcCb_->GetSize(fileSize);
837 (void)CommonNapi::AddNumberPropInt64(env, value, "fileSize", fileSize);
838 int32_t ret = jsMetaHelper->dataSrcCb_->GetCallback(HELPER_READAT_CALLBACK_NAME, &callback);
839 CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, result, "failed to GetCallback");
840 (void)HelperDataSourceCallback::AddNapiValueProp(env, value, "callback", callback);
841
842 MEDIA_LOGI("JsGetDataSrc Out");
843 return value;
844 }
845
GetCurrentState()846 std::string AVMetadataExtractorNapi::GetCurrentState()
847 {
848 if (isReleased_.load()) {
849 return AVMetadataHelperState::STATE_RELEASED;
850 } else {
851 std::string curState = AVMetadataHelperState::STATE_ERROR;
852
853 if (stateMap.find(state_) != stateMap.end()) {
854 curState = stateMap.at(state_);
855 }
856 return curState;
857 }
858 }
859
SaveCallbackReference(const std::string & callbackName,std::shared_ptr<AutoRef> ref)860 void AVMetadataExtractorNapi::SaveCallbackReference(const std::string &callbackName, std::shared_ptr<AutoRef> ref)
861 {
862 std::lock_guard<std::mutex> lock(mutex_);
863 refMap_[callbackName] = ref;
864 if (extractorCb_ != nullptr) {
865 extractorCb_->SaveCallbackReference(callbackName, ref);
866 }
867 }
868
ClearCallbackReference()869 void AVMetadataExtractorNapi::ClearCallbackReference()
870 {
871 std::lock_guard<std::mutex> lock(mutex_);
872 if (extractorCb_ != nullptr) {
873 extractorCb_->ClearCallbackReference();
874 }
875 refMap_.clear();
876 }
877
ClearCallbackReference(const std::string & callbackName)878 void AVMetadataExtractorNapi::ClearCallbackReference(const std::string &callbackName)
879 {
880 std::lock_guard<std::mutex> lock(mutex_);
881 if (extractorCb_ != nullptr) {
882 extractorCb_->ClearCallbackReference(callbackName);
883 }
884 refMap_.erase(callbackName);
885 }
886
NotifyState(HelperStates state)887 void AVMetadataExtractorNapi::NotifyState(HelperStates state)
888 {
889 std::lock_guard<std::mutex> lock(taskMutex_);
890 state_ = state;
891 MEDIA_LOGI("notify completed, current state: %{public}s", GetCurrentState().c_str());
892 stopWait_ = true;
893 stateChangeCond_.notify_all();
894 }
895
ResetUserParameters()896 void AVMetadataExtractorNapi::ResetUserParameters()
897 {
898 url_.clear();
899 fileDescriptor_.fd = 0;
900 fileDescriptor_.offset = 0;
901 fileDescriptor_.length = -1;
902 fileDescriptor_.length = -1;
903 }
904
StartListenCurrentResource()905 void AVMetadataExtractorNapi::StartListenCurrentResource()
906 {
907 std::lock_guard<std::mutex> lock(mutex_);
908 if (extractorCb_ != nullptr) {
909 extractorCb_->Start();
910 }
911 }
912
PauseListenCurrentResource()913 void AVMetadataExtractorNapi::PauseListenCurrentResource()
914 {
915 std::lock_guard<std::mutex> lock(mutex_);
916 if (extractorCb_ != nullptr) {
917 extractorCb_->Pause();
918 }
919 }
920
OnErrorCb(MediaServiceExtErrCodeAPI9 errorCode,const std::string & errorMsg)921 void AVMetadataExtractorNapi::OnErrorCb(MediaServiceExtErrCodeAPI9 errorCode, const std::string &errorMsg)
922 {
923 std::lock_guard<std::mutex> lock(mutex_);
924 if (extractorCb_ != nullptr) {
925 extractorCb_->OnErrorCb(errorCode, errorMsg);
926 }
927 }
928
GetJsInstance(napi_env env,napi_callback_info info)929 AVMetadataExtractorNapi* AVMetadataExtractorNapi::GetJsInstance(napi_env env, napi_callback_info info)
930 {
931 size_t argCount = 0;
932 napi_value jsThis = nullptr;
933 napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
934 CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr, "failed to napi_get_cb_info");
935
936 AVMetadataExtractorNapi *extractor = nullptr;
937 status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&extractor));
938 CHECK_AND_RETURN_RET_LOG(status == napi_ok && extractor != nullptr, nullptr, "failed to napi_unwrap");
939
940 return extractor;
941 }
942
GetJsInstanceWithParameter(napi_env env,napi_callback_info info,size_t & argc,napi_value * argv)943 AVMetadataExtractorNapi* AVMetadataExtractorNapi::GetJsInstanceWithParameter(napi_env env, napi_callback_info info,
944 size_t &argc, napi_value *argv)
945 {
946 napi_value jsThis = nullptr;
947 napi_status status = napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr);
948 CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr, "failed to napi_get_cb_info");
949
950 AVMetadataExtractorNapi *extractor = nullptr;
951 status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&extractor));
952 CHECK_AND_RETURN_RET_LOG(status == napi_ok && extractor != nullptr, nullptr, "failed to napi_unwrap");
953
954 return extractor;
955 }
956 } // namespace Media
957 } // namespace OHOS