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