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