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