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