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_METADATA, "AVMetadataExtractorNapi"};
35 constexpr uint8_t ARG_TWO = 2;
36 }
37 
38 namespace OHOS {
39 namespace Media {
40 thread_local napi_ref AVMetadataExtractorNapi::constructor_ = nullptr;
41 const std::string CLASS_NAME = "AVMetadataExtractor";
42 
AVMetadataExtractorNapi()43 AVMetadataExtractorNapi::AVMetadataExtractorNapi()
44 {
45     MEDIA_LOGI("0x%{public}06" PRIXPTR " Instances create", FAKE_POINTER(this));
46 }
47 
~AVMetadataExtractorNapi()48 AVMetadataExtractorNapi::~AVMetadataExtractorNapi()
49 {
50     MEDIA_LOGI("0x%{public}06" PRIXPTR " Instances destroy", FAKE_POINTER(this));
51 }
52 
Init(napi_env env, napi_value exports)53 napi_value AVMetadataExtractorNapi::Init(napi_env env, napi_value exports)
54 {
55     napi_property_descriptor staticProperty[] = {
56         DECLARE_NAPI_STATIC_FUNCTION("createAVMetadataExtractor", JsCreateAVMetadataExtractor),
57     };
58 
59     napi_property_descriptor properties[] = {
60         DECLARE_NAPI_FUNCTION("fetchMetadata", JsResolveMetadata),
61         DECLARE_NAPI_FUNCTION("fetchAlbumCover", JsFetchArtPicture),
62         DECLARE_NAPI_FUNCTION("release", JsRelease),
63         DECLARE_NAPI_FUNCTION("getTimeByFrameIndex", JSGetTimeByFrameIndex),
64         DECLARE_NAPI_FUNCTION("getFrameIndexByTime", JSGetFrameIndexByTime),
65         DECLARE_NAPI_GETTER_SETTER("fdSrc", JsGetAVFileDescriptor, JsSetAVFileDescriptor),
66         DECLARE_NAPI_GETTER_SETTER("dataSrc", JsGetDataSrc, JsSetDataSrc),
67     };
68 
69     napi_value constructor = nullptr;
70     napi_status status = napi_define_class(env, CLASS_NAME.c_str(), NAPI_AUTO_LENGTH, Constructor, nullptr,
71         sizeof(properties) / sizeof(properties[0]), properties, &constructor);
72     CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define AVMetadataHelper class");
73 
74     status = napi_create_reference(env, constructor, 1, &constructor_);
75     CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to create reference of constructor");
76 
77     status = napi_set_named_property(env, exports, CLASS_NAME.c_str(), constructor);
78     CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to set constructor");
79 
80     status = napi_define_properties(env, exports, sizeof(staticProperty) / sizeof(staticProperty[0]), staticProperty);
81     CHECK_AND_RETURN_RET_LOG(status == napi_ok, nullptr, "Failed to define static function");
82 
83     MEDIA_LOGD("AVMetadataExtractorNapi Init success");
84     return exports;
85 }
86 
Constructor(napi_env env, napi_callback_info info)87 napi_value AVMetadataExtractorNapi::Constructor(napi_env env, napi_callback_info info)
88 {
89     napi_value result = nullptr;
90     napi_get_undefined(env, &result);
91 
92     size_t argCount = 0;
93     napi_value jsThis = nullptr;
94     napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
95     CHECK_AND_RETURN_RET_LOG(status == napi_ok, result, "failed to napi_get_cb_info");
96 
97     AVMetadataExtractorNapi *extractor = new(std::nothrow) AVMetadataExtractorNapi();
98     CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to new AVMetadataExtractorNapi");
99 
100     extractor->env_ = env;
101     extractor->helper_ = AVMetadataHelperFactory::CreateAVMetadataHelper();
102     CHECK_AND_RETURN_RET_LOG(extractor->helper_ != nullptr, result, "failed to CreateMetadataHelper");
103 
104     status = napi_wrap(env, jsThis, reinterpret_cast<void *>(extractor),
105         AVMetadataExtractorNapi::Destructor, nullptr, nullptr);
106     if (status != napi_ok) {
107         delete extractor;
108         MEDIA_LOGE("Failed to wrap native instance");
109         return result;
110     }
111 
112     MEDIA_LOGI("0x%{public}06" PRIXPTR " Constructor", FAKE_POINTER(extractor));
113     return jsThis;
114 }
115 
Destructor(napi_env env, void *nativeObject, void *finalize)116 void AVMetadataExtractorNapi::Destructor(napi_env env, void *nativeObject, void *finalize)
117 {
118     MEDIA_LOGI("0x%{public}06" PRIXPTR " Destructor", FAKE_POINTER(nativeObject));
119     (void)finalize;
120     CHECK_AND_RETURN(nativeObject != nullptr);
121     AVMetadataExtractorNapi *napi = reinterpret_cast<AVMetadataExtractorNapi *>(nativeObject);
122     if (napi != nullptr && napi->helper_ != nullptr) {
123         napi->helper_->Release();
124     }
125     delete napi;
126 }
127 
JsCreateAVMetadataExtractor(napi_env env, napi_callback_info info)128 napi_value AVMetadataExtractorNapi::JsCreateAVMetadataExtractor(napi_env env, napi_callback_info info)
129 {
130     MediaTrace trace("AVMetadataExtractorNapi::JsCreateAVMetadataExtractor");
131     napi_value result = nullptr;
132     napi_get_undefined(env, &result);
133     MEDIA_LOGI("JsCreateAVMetadataExtractor In");
134 
135     std::unique_ptr<MediaAsyncContext> asyncContext = std::make_unique<MediaAsyncContext>(env);
136 
137     // get args
138     napi_value jsThis = nullptr;
139     napi_value args[1] = { nullptr };
140     size_t argCount = 1;
141     napi_status status = napi_get_cb_info(env, info, &argCount, args, &jsThis, nullptr);
142     CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr, "failed to napi_get_cb_info");
143 
144     asyncContext->callbackRef = CommonNapi::CreateReference(env, args[0]);
145     asyncContext->deferred = CommonNapi::CreatePromise(env, asyncContext->callbackRef, result);
146     asyncContext->JsResult = std::make_unique<MediaJsResultInstance>(constructor_);
147     asyncContext->ctorFlag = true;
148 
149     napi_value resource = nullptr;
150     napi_create_string_utf8(env, "JsCreateAVMetadataExtractor", NAPI_AUTO_LENGTH, &resource);
151     NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {},
152         MediaAsyncContext::CompleteCallback, static_cast<void *>(asyncContext.get()), &asyncContext->work));
153     NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work));
154     asyncContext.release();
155     MEDIA_LOGI("JsCreateAVMetadataExtractor Out");
156     return result;
157 }
158 
JsResolveMetadata(napi_env env, napi_callback_info info)159 napi_value AVMetadataExtractorNapi::JsResolveMetadata(napi_env env, napi_callback_info info)
160 {
161     MediaTrace trace("AVMetadataExtractorNapi::resolveMetadata");
162     napi_value result = nullptr;
163     napi_get_undefined(env, &result);
164     MEDIA_LOGI("JsResolveMetadata In");
165 
166     auto promiseCtx = std::make_unique<AVMetadataExtractorAsyncContext>(env);
167     napi_value args[1] = { nullptr };
168     size_t argCount = 1;
169 
170     AVMetadataExtractorNapi* extractor
171         = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
172     CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
173     promiseCtx->napi = extractor;
174     promiseCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
175     promiseCtx->deferred = CommonNapi::CreatePromise(env, promiseCtx->callbackRef, result);
176 
177     // async work
178     napi_value resource = nullptr;
179     napi_create_string_utf8(env, "JsResolveMetadata", NAPI_AUTO_LENGTH, &resource);
180     NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
181         auto promiseCtx = reinterpret_cast<AVMetadataExtractorAsyncContext *>(data);
182         CHECK_AND_RETURN_LOG(promiseCtx && !promiseCtx->errFlag, "Invalid promiseCtx.");
183         CHECK_AND_RETURN_LOG(promiseCtx->napi != nullptr, "Invalid napi object.");
184         if (promiseCtx->napi->state_ != HelperState::HELPER_STATE_RUNNABLE) {
185             promiseCtx->SignError(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Can't fetchMetadata, please set source.");
186             return;
187         }
188         CHECK_AND_RETURN_LOG(promiseCtx->napi->helper_ != nullptr, "Invalid metadata napi.");
189         promiseCtx->metadata_ = promiseCtx->napi->helper_->GetAVMetadata();
190         CHECK_AND_RETURN(promiseCtx->metadata_ == nullptr);
191         MEDIA_LOGE("ResolveMetadata AVMetadata is nullptr");
192         promiseCtx->SignError(MSERR_EXT_API9_UNSUPPORT_FORMAT, "failed to ResolveMetadata, AVMetadata is nullptr!");
193     }, ResolveMetadataComplete, static_cast<void *>(promiseCtx.get()), &promiseCtx->work));
194     NAPI_CALL(env, napi_queue_async_work(env, promiseCtx->work));
195     promiseCtx.release();
196     MEDIA_LOGI("JsResolveMetadata Out");
197     return result;
198 }
199 
ResolveMetadataComplete(napi_env env, napi_status status, void *data)200 void AVMetadataExtractorNapi::ResolveMetadataComplete(napi_env env, napi_status status, void *data)
201 {
202     MEDIA_LOGI("ResolveMetadataComplete In");
203     auto promiseCtx = static_cast<AVMetadataExtractorAsyncContext*>(data);
204     CHECK_AND_RETURN_LOG(promiseCtx != nullptr, "promiseCtx is nullptr!");
205 
206     bool ret = true;
207     napi_value result = nullptr;
208     napi_value location = nullptr;
209     napi_value customInfo = nullptr;
210     napi_create_object(env, &result);
211     napi_create_object(env, &location);
212     napi_create_object(env, &customInfo);
213     std::shared_ptr<Meta> metadata = promiseCtx->metadata_;
214     if (status != napi_ok || promiseCtx->errCode != napi_ok) {
215         promiseCtx->status = promiseCtx->errCode == napi_ok ? MSERR_INVALID_VAL : promiseCtx->errCode;
216         MEDIA_LOGI("Resolve meta data failed");
217         napi_get_undefined(env, &result);
218         CommonCallbackRoutine(env, promiseCtx, result);
219         return;
220     }
221     for (const auto &key : g_Metadata) {
222         if (metadata->Find(key) == metadata->end()) {
223             MEDIA_LOGE("failed to find key: %{public}s", key.c_str());
224             continue;
225         }
226         MEDIA_LOGE("success to find key: %{public}s", key.c_str());
227         if (key == "latitude" || key == "longitude") {
228             CHECK_AND_CONTINUE_LOG(CommonNapi::SetPropertyByValueType(env, location, metadata, key),
229                 "SetProperty failed, key: %{public}s", key.c_str());
230             continue;
231         }
232         if (key == "customInfo") {
233             std::shared_ptr<Meta> customData = std::make_shared<Meta>();
234             ret = metadata->GetData(key, customData);
235             CHECK_AND_CONTINUE_LOG(ret, "GetData failed, key %{public}s", key.c_str());
236             for (auto iter = customData->begin(); iter != customData->end(); ++iter) {
237                 AnyValueType type = customData->GetValueType(iter->first);
238                 CHECK_AND_CONTINUE_LOG(type == AnyValueType::STRING, "key is not string");
239                 CHECK_AND_CONTINUE_LOG(CommonNapi::SetPropertyByValueType(env, customInfo, customData, iter->first),
240                     "SetProperty failed, key: %{public}s", key.c_str());
241             }
242             continue;
243         }
244         CHECK_AND_CONTINUE_LOG(CommonNapi::SetPropertyByValueType(env, result, metadata, key),
245             "SetProperty failed, key: %{public}s", key.c_str());
246     }
247     napi_set_named_property(env, result, "location", location);
248     napi_set_named_property(env, result, "customInfo", customInfo);
249     promiseCtx->status = ERR_OK;
250     CommonCallbackRoutine(env, promiseCtx, result);
251 }
252 
ConvertMemToPixelMap(std::shared_ptr<AVSharedMemory> sharedMemory)253 static std::unique_ptr<PixelMap> ConvertMemToPixelMap(std::shared_ptr<AVSharedMemory> sharedMemory)
254 {
255     CHECK_AND_RETURN_RET_LOG(sharedMemory != nullptr, nullptr, "SharedMem is nullptr");
256     MEDIA_LOGI("FetchArtPicture size: %{public}d", sharedMemory->GetSize());
257     SourceOptions sourceOptions;
258     uint32_t errorCode = 0;
259     std::unique_ptr<ImageSource> imageSource =
260         ImageSource::CreateImageSource(sharedMemory->GetBase(), sharedMemory->GetSize(), sourceOptions, errorCode);
261     CHECK_AND_RETURN_RET_LOG(imageSource != nullptr, nullptr, "Failed to create imageSource.");
262     DecodeOptions decodeOptions;
263     std::unique_ptr<PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOptions, errorCode);
264     CHECK_AND_RETURN_RET_LOG(pixelMap != nullptr, nullptr, "Failed to decode imageSource");
265     return pixelMap;
266 }
267 
JsFetchArtPicture(napi_env env, napi_callback_info info)268 napi_value AVMetadataExtractorNapi::JsFetchArtPicture(napi_env env, napi_callback_info info)
269 {
270     MediaTrace trace("AVMetadataExtractorNapi::fetchArtPicture");
271     napi_value result = nullptr;
272     napi_get_undefined(env, &result);
273     MEDIA_LOGI("JsFetchArtPicture In");
274 
275     auto promiseCtx = std::make_unique<AVMetadataExtractorAsyncContext>(env);
276     napi_value args[1] = { nullptr };
277     size_t argCount = 1;
278 
279     AVMetadataExtractorNapi* extractor
280         = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
281     CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
282     promiseCtx->napi = extractor;
283     promiseCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
284     promiseCtx->deferred = CommonNapi::CreatePromise(env, promiseCtx->callbackRef, result);
285 
286     // async work
287     napi_value resource = nullptr;
288     napi_create_string_utf8(env, "JsFetchArtPicture", NAPI_AUTO_LENGTH, &resource);
289     NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
290         MEDIA_LOGI("JsFetchArtPicture task start");
291         auto promiseCtx = reinterpret_cast<AVMetadataExtractorAsyncContext *>(data);
292         CHECK_AND_RETURN_LOG(promiseCtx && promiseCtx ->napi, "Invalid context.");
293         if (promiseCtx->napi->state_ != HelperState::HELPER_STATE_RUNNABLE) {
294             promiseCtx->SignError(
295                 MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Can't fetchAlbumCover, please set fdSrc or dataSrc.");
296             return;
297         }
298         auto sharedMemory = promiseCtx ->napi->helper_->FetchArtPicture();
299         promiseCtx ->artPicture_ = ConvertMemToPixelMap(sharedMemory);
300         if (promiseCtx ->artPicture_ == nullptr) {
301             promiseCtx ->SignError(MSERR_EXT_API9_UNSUPPORT_FORMAT, "Failed to fetchAlbumCover");
302         }
303     }, FetchArtPictureComplete, static_cast<void *>(promiseCtx.get()), &promiseCtx->work));
304     NAPI_CALL(env, napi_queue_async_work(env, promiseCtx->work));
305     promiseCtx.release();
306     MEDIA_LOGI("JsFetchArtPicture Out");
307     return result;
308 }
309 
FetchArtPictureComplete(napi_env env, napi_status status, void *data)310 void AVMetadataExtractorNapi::FetchArtPictureComplete(napi_env env, napi_status status, void *data)
311 {
312     napi_value result = nullptr;
313 
314     MEDIA_LOGI("FetchArtPictureComplete In");
315     auto context = static_cast<AVMetadataExtractorAsyncContext*>(data);
316 
317     if (status == napi_ok && context->errCode == napi_ok) {
318         result = Media::PixelMapNapi::CreatePixelMap(env, context->artPicture_);
319         context->status = ERR_OK;
320     } else {
321         context->status = context->errCode == napi_ok ? MSERR_INVALID_VAL : context->errCode;
322         napi_get_undefined(env, &result);
323     }
324 
325     CommonCallbackRoutine(env, context, result);
326 }
327 
CommonCallbackRoutine(napi_env env, AVMetadataExtractorAsyncContext* &asyncContext, const napi_value &valueParam)328 void AVMetadataExtractorNapi::CommonCallbackRoutine(napi_env env, AVMetadataExtractorAsyncContext* &asyncContext,
329     const napi_value &valueParam)
330 {
331     napi_value result[2] = {0};
332     napi_value retVal;
333     napi_value callback = nullptr;
334 
335     napi_get_undefined(env, &result[0]);
336     napi_get_undefined(env, &result[1]);
337 
338     napi_handle_scope scope = nullptr;
339     napi_open_handle_scope(env, &scope);
340     CHECK_AND_RETURN(scope != nullptr && asyncContext != nullptr);
341     if (asyncContext->status == ERR_OK) {
342         result[1] = valueParam;
343     }
344     napi_create_uint32(env, asyncContext->status, &result[0]);
345 
346     if (asyncContext->errFlag) {
347         (void)CommonNapi::CreateError(env, asyncContext->errCode, asyncContext->errMessage, callback);
348         result[0] = callback;
349     }
350     if (asyncContext->deferred) {
351         if (asyncContext->status == ERR_OK) {
352             napi_resolve_deferred(env, asyncContext->deferred, result[1]);
353         } else {
354             napi_reject_deferred(env, asyncContext->deferred, result[0]);
355         }
356     } else {
357         napi_get_reference_value(env, asyncContext->callbackRef, &callback);
358         napi_call_function(env, nullptr, callback, ARG_TWO, result, &retVal); // 2
359         napi_delete_reference(env, asyncContext->callbackRef);
360     }
361 
362     napi_delete_async_work(env, asyncContext->work);
363     napi_close_handle_scope(env, scope);
364 
365     delete asyncContext;
366     asyncContext = nullptr;
367 }
368 
JsRelease(napi_env env, napi_callback_info info)369 napi_value AVMetadataExtractorNapi::JsRelease(napi_env env, napi_callback_info info)
370 {
371     MediaTrace trace("AVMetadataExtractorNapi::release");
372     napi_value result = nullptr;
373     napi_get_undefined(env, &result);
374     MEDIA_LOGI("JsRelease In");
375 
376     auto promiseCtx = std::make_unique<AVMetadataExtractorAsyncContext>(env);
377     napi_value args[1] = { nullptr };
378     size_t argCount = 1;
379     AVMetadataExtractorNapi *extractor
380         = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
381     CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
382     promiseCtx->callbackRef = CommonNapi::CreateReference(env, args[0]);
383     promiseCtx->deferred = CommonNapi::CreatePromise(env, promiseCtx->callbackRef, result);
384 
385     if (extractor->dataSrcCb_ != nullptr) {
386         extractor->dataSrcCb_->ClearCallbackReference();
387         extractor->dataSrcCb_ = nullptr;
388     }
389 
390     napi_value resource = nullptr;
391     napi_create_string_utf8(env, "JsRelease", NAPI_AUTO_LENGTH, &resource);
392     NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
393         auto promiseCtx = reinterpret_cast<AVMetadataExtractorAsyncContext *>(data);
394         CHECK_AND_RETURN_LOG(promiseCtx && !promiseCtx->errFlag, "Invalid promiseCtx.");
395         CHECK_AND_RETURN_LOG(promiseCtx->napi != nullptr, "Invalid napi object.");
396         if (promiseCtx->napi->state_ == HelperState::HELPER_STATE_RELEASED) {
397             promiseCtx->SignError(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Has released once, can't release again.");
398             return;
399         }
400         CHECK_AND_RETURN_LOG(promiseCtx->napi->helper_, "PromiseCtx has invalid data.");
401         promiseCtx->napi->helper_->Release();
402     }, MediaAsyncContext::CompleteCallback, static_cast<void *>(promiseCtx.get()), &promiseCtx->work));
403     napi_queue_async_work_with_qos(env, promiseCtx->work, napi_qos_user_initiated);
404     promiseCtx.release();
405     MEDIA_LOGI("JsRelease Out");
406     return result;
407 }
408 
JsSetAVFileDescriptor(napi_env env, napi_callback_info info)409 napi_value AVMetadataExtractorNapi::JsSetAVFileDescriptor(napi_env env, napi_callback_info info)
410 {
411     MediaTrace trace("AVMetadataExtractorNapi::set fd");
412     napi_value result = nullptr;
413     napi_get_undefined(env, &result);
414     MEDIA_LOGI("JsSetAVFileDescriptor In");
415 
416     napi_value args[1] = { nullptr };
417     size_t argCount = 1;
418     AVMetadataExtractorNapi *extractor = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
419     CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstanceWithParameter");
420     CHECK_AND_RETURN_RET_LOG(
421         extractor->state_ == HelperState::HELPER_STATE_IDLE, result, "Has set source once, unsupport set again");
422     napi_valuetype valueType = napi_undefined;
423     if (argCount < 1 || napi_typeof(env, args[0], &valueType) != napi_ok || valueType != napi_object) {
424         return result;
425     }
426 
427     bool notValidParam = argCount < 1 || napi_typeof(env, args[0], &valueType) != napi_ok || valueType != napi_object ||
428         !CommonNapi::GetFdArgument(env, args[0], extractor->fileDescriptor_);
429     CHECK_AND_RETURN_RET_LOG(!notValidParam, result, "Invalid file descriptor, return");
430     CHECK_AND_RETURN_RET_LOG(extractor->helper_, result, "Invalid AVMetadataExtractorNapi.");
431 
432     auto fileDescriptor = extractor->fileDescriptor_;
433     auto res = extractor->helper_->SetSource(fileDescriptor.fd, fileDescriptor.offset, fileDescriptor.length);
434     extractor->state_ = res == MSERR_OK ? HelperState::HELPER_STATE_RUNNABLE : HelperState::HELPER_ERROR;
435     return result;
436 }
437 
JsGetAVFileDescriptor(napi_env env, napi_callback_info info)438 napi_value AVMetadataExtractorNapi::JsGetAVFileDescriptor(napi_env env, napi_callback_info info)
439 {
440     MediaTrace trace("AVMetadataExtractorNapi::get fd");
441     napi_value result = nullptr;
442     napi_get_undefined(env, &result);
443     MEDIA_LOGI("JsGetAVFileDescriptor In");
444 
445     AVMetadataExtractorNapi *extractor = AVMetadataExtractorNapi::GetJsInstance(env, info);
446     CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
447 
448     napi_value value = nullptr;
449     (void)napi_create_object(env, &value);
450     (void)CommonNapi::AddNumberPropInt32(env, value, "fd", extractor->fileDescriptor_.fd);
451     (void)CommonNapi::AddNumberPropInt64(env, value, "offset", extractor->fileDescriptor_.offset);
452     (void)CommonNapi::AddNumberPropInt64(env, value, "length", extractor->fileDescriptor_.length);
453 
454     MEDIA_LOGI("JsGetAVFileDescriptor Out");
455     return value;
456 }
457 
JsSetDataSrc(napi_env env, napi_callback_info info)458 napi_value AVMetadataExtractorNapi::JsSetDataSrc(napi_env env, napi_callback_info info)
459 {
460     MediaTrace trace("AVMetadataExtractorNapi::set dataSrc");
461     napi_value result = nullptr;
462     napi_get_undefined(env, &result);
463     MEDIA_LOGI("JsSetDataSrc In");
464 
465     napi_value args[1] = { nullptr };
466     size_t argCount = 1;
467     AVMetadataExtractorNapi *jsMetaHelper
468         = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
469     CHECK_AND_RETURN_RET_LOG(jsMetaHelper != nullptr, result, "failed to GetJsInstanceWithParameter");
470 
471     CHECK_AND_RETURN_RET_LOG(
472         jsMetaHelper->state_ == HelperState::HELPER_STATE_IDLE, result, "Has set source once, unsupport set again");
473 
474     napi_valuetype valueType = napi_undefined;
475     bool notValidParam = argCount < 1 || napi_typeof(env, args[0], &valueType) != napi_ok || valueType != napi_object ||
476         !CommonNapi::GetFdArgument(env, args[0], jsMetaHelper->fileDescriptor_);
477     CHECK_AND_RETURN_RET_LOG(!notValidParam, result, "Invalid file descriptor, return");
478     CHECK_AND_RETURN_RET_LOG(jsMetaHelper->helper_, result, "Invalid AVMetadataExtractorNapi.");
479     (void)CommonNapi::GetPropertyInt64(env, args[0], "fileSize", jsMetaHelper->dataSrcDescriptor_.fileSize);
480     CHECK_AND_RETURN_RET(
481         jsMetaHelper->dataSrcDescriptor_.fileSize >= -1 && jsMetaHelper->dataSrcDescriptor_.fileSize != 0, result);
482     MEDIA_LOGI("Recvive filesize is %{public}" PRId64 "", jsMetaHelper->dataSrcDescriptor_.fileSize);
483     jsMetaHelper->dataSrcCb_
484         = std::make_shared<HelperDataSourceCallback>(env, jsMetaHelper->dataSrcDescriptor_.fileSize);
485 
486     napi_value callback = nullptr;
487     napi_ref ref = nullptr;
488     napi_get_named_property(env, args[0], "callback", &callback);
489     jsMetaHelper->dataSrcDescriptor_.callback = callback;
490     napi_status status = napi_create_reference(env, callback, 1, &ref);
491     CHECK_AND_RETURN_RET_LOG(status == napi_ok && ref != nullptr, result, "failed to create reference!");
492     std::shared_ptr<AutoRef> autoRef = std::make_shared<AutoRef>(env, ref);
493     const std::string callbackName = "readAt";
494     jsMetaHelper->dataSrcCb_->SaveCallbackReference(callbackName, autoRef);
495     auto res = jsMetaHelper->helper_->SetSource(jsMetaHelper->dataSrcCb_);
496     jsMetaHelper->state_ = res == MSERR_OK ? HelperState::HELPER_STATE_RUNNABLE : HelperState::HELPER_ERROR;
497     MEDIA_LOGI("JsSetDataSrc Out");
498     return result;
499 }
500 
JsGetDataSrc(napi_env env, napi_callback_info info)501 napi_value AVMetadataExtractorNapi::JsGetDataSrc(napi_env env, napi_callback_info info)
502 {
503     MediaTrace trace("AVMetadataExtractorNapi::get dataSrc");
504     napi_value result = nullptr;
505     napi_get_undefined(env, &result);
506     MEDIA_LOGI("JsGetDataSrc In");
507 
508     AVMetadataExtractorNapi *jsMetaHelper = AVMetadataExtractorNapi::GetJsInstance(env, info);
509     CHECK_AND_RETURN_RET_LOG(jsMetaHelper != nullptr, result, "failed to GetJsInstance");
510     CHECK_AND_RETURN_RET_LOG(jsMetaHelper->dataSrcCb_ != nullptr, result, "failed to check dataSrcCb_");
511 
512     napi_value value = nullptr;
513     int64_t fileSize;
514     napi_value callback = nullptr;
515     (void)napi_create_object(env, &value);
516     (void)jsMetaHelper->dataSrcCb_->GetSize(fileSize);
517     (void)CommonNapi::AddNumberPropInt64(env, value, "fileSize", fileSize);
518     const std::string callbackName = "readAt";
519     int32_t ret = jsMetaHelper->dataSrcCb_->GetCallback(callbackName, &callback);
520     CHECK_AND_RETURN_RET_LOG(ret == MSERR_OK, result, "failed to GetCallback");
521     (void)HelperDataSourceCallback::AddNapiValueProp(env, value, "callback", callback);
522 
523     MEDIA_LOGI("JsGetDataSrc Out");
524     return value;
525 }
526 
GetJsInstance(napi_env env, napi_callback_info info)527 AVMetadataExtractorNapi* AVMetadataExtractorNapi::GetJsInstance(napi_env env, napi_callback_info info)
528 {
529     size_t argCount = 0;
530     napi_value jsThis = nullptr;
531     napi_status status = napi_get_cb_info(env, info, &argCount, nullptr, &jsThis, nullptr);
532     CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr, "failed to napi_get_cb_info");
533 
534     AVMetadataExtractorNapi *extractor = nullptr;
535     status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&extractor));
536     CHECK_AND_RETURN_RET_LOG(status == napi_ok && extractor != nullptr, nullptr, "failed to napi_unwrap");
537 
538     return extractor;
539 }
540 
GetJsInstanceWithParameter(napi_env env, napi_callback_info info, size_t &argc, napi_value *argv)541 AVMetadataExtractorNapi* AVMetadataExtractorNapi::GetJsInstanceWithParameter(napi_env env, napi_callback_info info,
542     size_t &argc, napi_value *argv)
543 {
544     napi_value jsThis = nullptr;
545     napi_status status = napi_get_cb_info(env, info, &argc, argv, &jsThis, nullptr);
546     CHECK_AND_RETURN_RET_LOG(status == napi_ok && jsThis != nullptr, nullptr, "failed to napi_get_cb_info");
547 
548     AVMetadataExtractorNapi *extractor = nullptr;
549     status = napi_unwrap(env, jsThis, reinterpret_cast<void **>(&extractor));
550     CHECK_AND_RETURN_RET_LOG(status == napi_ok && extractor != nullptr, nullptr, "failed to napi_unwrap");
551 
552     return extractor;
553 }
554 
JSGetTimeByFrameIndex(napi_env env, napi_callback_info info)555 napi_value AVMetadataExtractorNapi::JSGetTimeByFrameIndex(napi_env env, napi_callback_info info)
556 {
557     MediaTrace trace("AVMetadataExtractorNapi::JSGetTimeByFrameIndex");
558     napi_value result = nullptr;
559     napi_get_undefined(env, &result);
560     MEDIA_LOGI("frame to time");
561 
562     napi_value args[2] = { nullptr };
563     size_t argCount = 2;
564     AVMetadataExtractorNapi* extractor
565         = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
566     CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
567 
568     auto promiseCtx = std::make_unique<AVMetadataExtractorAsyncContext>(env);
569 
570     if (CommonNapi::CheckValueType(env, args[0], napi_number)) {
571         auto res = napi_get_value_uint32(env, args[0], &promiseCtx->index_);
572         if (res != napi_ok || static_cast<int32_t>(promiseCtx->index_) < 0) {
573             promiseCtx->SignError(MSERR_EXT_API9_INVALID_PARAMETER, "frame index is not valid");
574         }
575     }
576 
577     promiseCtx->napi = extractor;
578     promiseCtx->callbackRef = CommonNapi::CreateReference(env, args[1]);
579     promiseCtx->deferred = CommonNapi::CreatePromise(env, promiseCtx->callbackRef, result);
580 
581     // async work
582     napi_value resource = nullptr;
583     napi_create_string_utf8(env, "JSGetTimeByFrameIndex", NAPI_AUTO_LENGTH, &resource);
584     NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
585         auto promiseCtx = reinterpret_cast<AVMetadataExtractorAsyncContext *>(data);
586         CHECK_AND_RETURN_LOG(promiseCtx && !promiseCtx->errFlag, "Invalid promiseCtx.");
587         CHECK_AND_RETURN_LOG(promiseCtx->napi != nullptr, "Invalid napi object.");
588         if (promiseCtx->napi->state_ != HelperState::HELPER_STATE_RUNNABLE) {
589             promiseCtx->SignError(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Has released once, can't release again.");
590             return;
591         }
592         auto helper = promiseCtx->napi->helper_;
593         CHECK_AND_RETURN_LOG(helper, "PromiseCtx has invalid data.");
594         if (helper->GetTimeByFrameIndex(promiseCtx->index_, promiseCtx->timeStamp_) != MSERR_EXT_API9_OK) {
595             MEDIA_LOGE("JSGetTimeByFrameIndex get result SignError");
596             promiseCtx->SignError(MSERR_EXT_API9_UNSUPPORT_FORMAT, "Demuxer getTimeByFrameIndex failed.");
597         }
598     }, GetTimeByFrameIndexComplete, static_cast<void *>(promiseCtx.get()), &promiseCtx->work));
599     NAPI_CALL(env, napi_queue_async_work(env, promiseCtx->work));
600     promiseCtx.release();
601     return result;
602 }
603 
GetTimeByFrameIndexComplete(napi_env env, napi_status status, void *data)604 void AVMetadataExtractorNapi::GetTimeByFrameIndexComplete(napi_env env, napi_status status, void *data)
605 {
606     napi_value result = nullptr;
607     auto context = static_cast<AVMetadataExtractorAsyncContext*>(data);
608 
609     if (status == napi_ok && context->errCode == napi_ok) {
610         napi_create_int64(env, context->timeStamp_, &result);
611         context->status = ERR_OK;
612     } else {
613         context->status = context->errCode == napi_ok ? MSERR_INVALID_VAL : context->errCode;
614         napi_get_undefined(env, &result);
615     }
616     CommonCallbackRoutine(env, context, result);
617 }
618 
JSGetFrameIndexByTime(napi_env env, napi_callback_info info)619 napi_value AVMetadataExtractorNapi::JSGetFrameIndexByTime(napi_env env, napi_callback_info info)
620 {
621     MediaTrace trace("AVMetadataExtractorNapi::JSGetFrameIndexByTime");
622     napi_value result = nullptr;
623     napi_get_undefined(env, &result);
624     MEDIA_LOGI("time to frame");
625 
626     napi_value args[2] = { nullptr };
627     size_t argCount = 2;
628     AVMetadataExtractorNapi* extractor
629         = AVMetadataExtractorNapi::GetJsInstanceWithParameter(env, info, argCount, args);
630     CHECK_AND_RETURN_RET_LOG(extractor != nullptr, result, "failed to GetJsInstance");
631 
632     auto promiseCtx = std::make_unique<AVMetadataExtractorAsyncContext>(env);
633 
634     if (CommonNapi::CheckValueType(env, args[0], napi_number)) {
635         int64_t timeStamp = 0;
636         auto res = napi_get_value_int64(env, args[0], &timeStamp);
637         if (res != napi_ok) {
638             promiseCtx->SignError(MSERR_EXT_API9_INVALID_PARAMETER, "time stamp is not valid");
639         }
640         promiseCtx->timeStamp_ = static_cast<uint64_t>(timeStamp);
641     }
642     promiseCtx->napi = extractor;
643     promiseCtx->callbackRef = CommonNapi::CreateReference(env, args[1]);
644     promiseCtx->deferred = CommonNapi::CreatePromise(env, promiseCtx->callbackRef, result);
645 
646     // async work
647     napi_value resource = nullptr;
648     napi_create_string_utf8(env, "JSGetFrameIndexByTime", NAPI_AUTO_LENGTH, &resource);
649     NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, [](napi_env env, void *data) {
650         auto promiseCtx = reinterpret_cast<AVMetadataExtractorAsyncContext *>(data);
651         CHECK_AND_RETURN_LOG(promiseCtx && !promiseCtx->errFlag, "Invalid promiseCtx.");
652         CHECK_AND_RETURN_LOG(promiseCtx->napi != nullptr, "Invalid napi object.");
653         if (promiseCtx->napi->state_ != HelperState::HELPER_STATE_RUNNABLE) {
654             promiseCtx->SignError(MSERR_EXT_API9_OPERATE_NOT_PERMIT, "Invalid state, please set source");
655             return;
656         }
657         auto helper = promiseCtx->napi->helper_;
658         CHECK_AND_RETURN_LOG(helper, "PromiseCtx has invalid data.");
659         if (helper->GetFrameIndexByTime(promiseCtx->timeStamp_, promiseCtx->index_) != MSERR_EXT_API9_OK) {
660             MEDIA_LOGE("JSGetFrameIndexByTime get result SignError");
661             promiseCtx->SignError(MSERR_EXT_API9_UNSUPPORT_FORMAT, "Demuxer getFrameIndexByTime failed.");
662         }
663     }, GetFrameIndexByTimeComplete, static_cast<void *>(promiseCtx.get()), &promiseCtx->work));
664     NAPI_CALL(env, napi_queue_async_work(env, promiseCtx->work));
665     promiseCtx.release();
666     return result;
667 }
668 
GetFrameIndexByTimeComplete(napi_env env, napi_status status, void *data)669 void AVMetadataExtractorNapi::GetFrameIndexByTimeComplete(napi_env env, napi_status status, void *data)
670 {
671     napi_value result = nullptr;
672     auto context = static_cast<AVMetadataExtractorAsyncContext*>(data);
673 
674     if (status == napi_ok && context->errCode == napi_ok) {
675         napi_create_uint32(env, context->index_, &result);
676         context->status = ERR_OK;
677     } else {
678         context->status = context->errCode == napi_ok ? MSERR_INVALID_VAL : context->errCode;
679         napi_get_undefined(env, &result);
680     }
681     CommonCallbackRoutine(env, context, result);
682 }
683 } // namespace Media
684 } // namespace OHOS