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