1 /*
2 * Copyright (C) 2024 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 "moving_photo_napi.h"
17
18 #include <fcntl.h>
19 #include <unistd.h>
20
21 #include "access_token.h"
22 #include "accesstoken_kit.h"
23 #include "directory_ex.h"
24 #include "file_uri.h"
25 #include "js_native_api.h"
26 #include "media_file_utils.h"
27 #include "media_file_uri.h"
28 #include "moving_photo_file_utils.h"
29 #include "media_library_napi.h"
30 #include "medialibrary_client_errno.h"
31 #include "medialibrary_db_const.h"
32 #include "medialibrary_errno.h"
33 #include "medialibrary_napi_utils.h"
34 #include "userfile_client.h"
35 #include "userfile_manager_types.h"
36 #include "moving_photo_call_transcoder.h"
37 #include "permission_utils.h"
38 #include "userfilemgr_uri.h"
39 #include "medialibrary_business_code.h"
40 #include "request_content_vo.h"
41 #include "user_define_ipc_client.h"
42 #include "medialibrary_operation.h"
43 #include "media_asset_rdbstore.h"
44
45 using namespace std;
46 using namespace OHOS::Security::AccessToken;
47
48 namespace OHOS {
49 namespace Media {
50
51 static const string MOVING_PHOTO_NAPI_CLASS = "MovingPhoto";
52 static const string URI_TYPE = "uriType";
53 static const string TYPE_PHOTOS = "1";
54
55 thread_local napi_ref MovingPhotoNapi::constructor_ = nullptr;
56 enum class MovingPhotoResourceType : int32_t {
57 DEFAULT = 0,
58 CLOUD_IMAGE,
59 CLOUD_VIDEO,
60 CLOUD_LIVE_PHOTO,
61 CLOUD_METADATA,
62 };
63
Init(napi_env env,napi_value exports)64 napi_value MovingPhotoNapi::Init(napi_env env, napi_value exports)
65 {
66 NapiClassInfo info = {
67 .name = MOVING_PHOTO_NAPI_CLASS,
68 .ref = &constructor_,
69 .constructor = Constructor,
70 .props = {
71 DECLARE_NAPI_FUNCTION("requestContent", JSRequestContent),
72 DECLARE_NAPI_FUNCTION("getUri", JSGetUri),
73 DECLARE_NAPI_FUNCTION("isVideoReady", JSIsVideoReady),
74 }
75 };
76 MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
77 return exports;
78 }
79
Constructor(napi_env env,napi_callback_info info)80 napi_value MovingPhotoNapi::Constructor(napi_env env, napi_callback_info info)
81 {
82 napi_value newTarget = nullptr;
83 CHECK_ARGS(env, napi_get_new_target(env, info, &newTarget), JS_INNER_FAIL);
84 CHECK_COND_RET(newTarget != nullptr, nullptr, "Invalid call to constructor");
85
86 size_t argc = ARGS_ONE;
87 napi_value argv[ARGS_ONE] = { 0 };
88 napi_value thisVar = nullptr;
89 napi_valuetype valueType;
90 CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
91 CHECK_COND_WITH_MESSAGE(env, argc == ARGS_ONE, "Number of args is invalid");
92 CHECK_ARGS(env, napi_typeof(env, argv[PARAM0], &valueType), JS_INNER_FAIL);
93 CHECK_COND_WITH_MESSAGE(env, valueType == napi_string, "Invalid argument type");
94 size_t result;
95 char photoUri[PATH_MAX];
96 CHECK_ARGS(env, napi_get_value_string_utf8(env, argv[PARAM0], photoUri, PATH_MAX, &result), JS_INNER_FAIL);
97
98 unique_ptr<MovingPhotoNapi> obj = make_unique<MovingPhotoNapi>(string(photoUri));
99 CHECK_ARGS(env,
100 napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()), MovingPhotoNapi::Destructor, nullptr,
101 nullptr),
102 JS_INNER_FAIL);
103 obj.release();
104 return thisVar;
105 }
106
Destructor(napi_env env,void * nativeObject,void * finalizeHint)107 void MovingPhotoNapi::Destructor(napi_env env, void* nativeObject, void* finalizeHint)
108 {
109 auto* movingPhotoNapi = reinterpret_cast<MovingPhotoNapi*>(nativeObject);
110 if (movingPhotoNapi == nullptr) {
111 return;
112 }
113 if (env != nullptr && movingPhotoNapi->progressHandlerRef_ != nullptr) {
114 napi_delete_reference(env, movingPhotoNapi->progressHandlerRef_);
115 movingPhotoNapi->progressHandlerRef_ = nullptr;
116 }
117 if (env != nullptr && movingPhotoNapi->threadsafeFunction_ != nullptr) {
118 napi_release_threadsafe_function(movingPhotoNapi->threadsafeFunction_, napi_tsfn_release);
119 movingPhotoNapi->threadsafeFunction_ = nullptr;
120 }
121 delete movingPhotoNapi;
122 movingPhotoNapi = nullptr;
123 }
124
GetUri()125 string MovingPhotoNapi::GetUri()
126 {
127 return photoUri_;
128 }
129
GetSourceMode()130 SourceMode MovingPhotoNapi::GetSourceMode()
131 {
132 return sourceMode_;
133 }
134
SetSourceMode(SourceMode sourceMode)135 void MovingPhotoNapi::SetSourceMode(SourceMode sourceMode)
136 {
137 sourceMode_ = sourceMode;
138 }
139
GetCompatibleMode()140 CompatibleMode MovingPhotoNapi::GetCompatibleMode()
141 {
142 return compatibleMode_;
143 }
144
SetCompatibleMode(const CompatibleMode compatibleMode)145 void MovingPhotoNapi::SetCompatibleMode(const CompatibleMode compatibleMode)
146 {
147 compatibleMode_ = compatibleMode;
148 }
149
GetProgressHandlerRef()150 napi_ref MovingPhotoNapi::GetProgressHandlerRef()
151 {
152 return progressHandlerRef_;
153 }
154
SetProgressHandlerRef(napi_ref & progressHandlerRef)155 void MovingPhotoNapi::SetProgressHandlerRef(napi_ref &progressHandlerRef)
156 {
157 progressHandlerRef_ = progressHandlerRef;
158 }
159
GetRequestId()160 std::string MovingPhotoNapi::GetRequestId()
161 {
162 return requestId_;
163 }
164
SetRequestId(const std::string requestId)165 void MovingPhotoNapi::SetRequestId(const std::string requestId)
166 {
167 requestId_ = requestId;
168 }
169
GetMediaAssetEnv()170 napi_env MovingPhotoNapi::GetMediaAssetEnv()
171 {
172 return media_asset_env_;
173 }
174
SetMediaAssetEnv(napi_env mediaAssetEnv)175 void MovingPhotoNapi::SetMediaAssetEnv(napi_env mediaAssetEnv)
176 {
177 media_asset_env_ = mediaAssetEnv;
178 }
179
OpenReadOnlyVideo(const std::string & videoUri,bool isMediaLibUri,int32_t position)180 static int32_t OpenReadOnlyVideo(const std::string& videoUri, bool isMediaLibUri, int32_t position)
181 {
182 if (isMediaLibUri) {
183 std::string openVideoUri = videoUri;
184 if (position == POSITION_CLOUD) {
185 MediaFileUtils::UriAppendKeyValue(openVideoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
186 OPEN_MOVING_PHOTO_VIDEO_CLOUD);
187 } else {
188 MediaFileUtils::UriAppendKeyValue(openVideoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
189 OPEN_MOVING_PHOTO_VIDEO);
190 }
191 Uri uri(openVideoUri);
192 return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
193 }
194 AppFileService::ModuleFileUri::FileUri fileUri(videoUri);
195 std::string realPath = fileUri.GetRealPath();
196 int32_t fd = open(realPath.c_str(), O_RDONLY);
197 if (fd < 0) {
198 NAPI_ERR_LOG("Failed to open read only video file, errno:%{public}d", errno);
199 return -1;
200 }
201 return fd;
202 }
203
OpenReadOnlyImage(const std::string & imageUri,bool isMediaLibUri,int32_t position)204 static int32_t OpenReadOnlyImage(const std::string& imageUri, bool isMediaLibUri, int32_t position)
205 {
206 if (isMediaLibUri) {
207 std::string openImageUri = imageUri;
208 if (position == POSITION_CLOUD) {
209 MediaFileUtils::UriAppendKeyValue(openImageUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
210 OPEN_MOVING_PHOTO_VIDEO_CLOUD);
211 }
212 Uri uri(openImageUri);
213 return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
214 }
215 AppFileService::ModuleFileUri::FileUri fileUri(imageUri);
216 std::string realPath = fileUri.GetRealPath();
217 int32_t fd = open(realPath.c_str(), O_RDONLY);
218 if (fd < 0) {
219 NAPI_ERR_LOG("Failed to open read only image file, errno: %{public}d", errno);
220 return -1;
221 }
222 return fd;
223 }
224
OpenReadOnlyFile(const std::string & uri,bool isReadImage,int32_t position)225 int32_t MovingPhotoNapi::OpenReadOnlyFile(const std::string& uri, bool isReadImage, int32_t position)
226 {
227 if (uri.empty()) {
228 NAPI_ERR_LOG("Failed to open read only file, uri is empty");
229 return -1;
230 }
231 std::string curUri = uri;
232 bool isMediaLibUri = MediaFileUtils::IsMediaLibraryUri(uri);
233 if (!isMediaLibUri) {
234 std::vector<std::string> uris;
235 if (!MediaFileUtils::SplitMovingPhotoUri(uri, uris)) {
236 NAPI_ERR_LOG("Failed to open read only file, split moving photo failed");
237 return -1;
238 }
239 curUri = uris[isReadImage ? MOVING_PHOTO_IMAGE_POS : MOVING_PHOTO_VIDEO_POS];
240 }
241 return isReadImage ? OpenReadOnlyImage(curUri, isMediaLibUri, position) :
242 OpenReadOnlyVideo(curUri, isMediaLibUri, position);
243 }
244
OpenReadOnlyLivePhoto(const string & destLivePhotoUri,int32_t position)245 int32_t MovingPhotoNapi::OpenReadOnlyLivePhoto(const string& destLivePhotoUri, int32_t position)
246 {
247 if (destLivePhotoUri.empty()) {
248 NAPI_ERR_LOG("Failed to open read only file, uri is empty");
249 return E_ERR;
250 }
251 if (MediaFileUtils::IsMediaLibraryUri(destLivePhotoUri)) {
252 std::string str = destLivePhotoUri;
253 std::string MULTI_USER_URI_FLAG = "user=";
254 size_t pos = str.find(MULTI_USER_URI_FLAG);
255 std::string userId = "";
256 if (pos != std::string::npos) {
257 pos += MULTI_USER_URI_FLAG.length();
258 size_t end = str.find_first_of("&?", pos);
259 if (end == std::string::npos) {
260 end = str.length();
261 }
262 userId = str.substr(pos, end - pos);
263 NAPI_ERR_LOG("ReadMovingPhotoVideo for other user is %{public}s", userId.c_str());
264 }
265 string livePhotoUri = destLivePhotoUri;
266 if (position == POSITION_CLOUD) {
267 MediaFileUtils::UriAppendKeyValue(livePhotoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
268 OPEN_MOVING_PHOTO_VIDEO_CLOUD);
269 } else {
270 MediaFileUtils::UriAppendKeyValue(livePhotoUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD,
271 OPEN_PRIVATE_LIVE_PHOTO);
272 }
273 Uri uri(livePhotoUri);
274 return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY, userId !="" ? stoi(userId) : -1);
275 }
276 return E_ERR;
277 }
278
OpenReadOnlyMetadata(const string & movingPhotoUri)279 int32_t MovingPhotoNapi::OpenReadOnlyMetadata(const string& movingPhotoUri)
280 {
281 if (movingPhotoUri.empty()) {
282 NAPI_ERR_LOG("Failed to open metadata of moving photo, uri is empty");
283 return E_ERR;
284 }
285
286 if (!MediaFileUtils::IsMediaLibraryUri(movingPhotoUri)) {
287 NAPI_ERR_LOG("Failed to check uri of moving photo: %{private}s", movingPhotoUri.c_str());
288 return E_ERR;
289 }
290
291 string movingPhotoMetadataUri = movingPhotoUri;
292 MediaFileUtils::UriAppendKeyValue(
293 movingPhotoMetadataUri, MEDIA_MOVING_PHOTO_OPRN_KEYWORD, OPEN_PRIVATE_MOVING_PHOTO_METADATA);
294 Uri uri(movingPhotoMetadataUri);
295 return UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
296 }
297
CopyFileFromMediaLibrary(int32_t srcFd,int32_t destFd)298 static int32_t CopyFileFromMediaLibrary(int32_t srcFd, int32_t destFd)
299 {
300 constexpr size_t bufferSize = 4096;
301 char buffer[bufferSize];
302 ssize_t bytesRead;
303 ssize_t bytesWritten;
304 while ((bytesRead = read(srcFd, buffer, bufferSize)) > 0) {
305 bytesWritten = write(destFd, buffer, bytesRead);
306 if (bytesWritten != bytesRead) {
307 NAPI_ERR_LOG("Failed to copy file from srcFd=%{public}d to destFd=%{public}d, errno=%{public}d",
308 srcFd, destFd, errno);
309 return E_HAS_FS_ERROR;
310 }
311 }
312
313 if (bytesRead < 0) {
314 NAPI_ERR_LOG("Failed to read from srcFd=%{public}d, errno=%{public}d", srcFd, errno);
315 return E_HAS_FS_ERROR;
316 }
317 return E_OK;
318 }
319
WriteToSandboxUri(int32_t srcFd,const string & sandboxUri,MovingPhotoResourceType type=MovingPhotoResourceType::DEFAULT)320 static int32_t WriteToSandboxUri(int32_t srcFd, const string& sandboxUri,
321 MovingPhotoResourceType type = MovingPhotoResourceType::DEFAULT)
322 {
323 UniqueFd srcUniqueFd(srcFd);
324
325 AppFileService::ModuleFileUri::FileUri fileUri(sandboxUri);
326 string destPath = fileUri.GetRealPath();
327 if (!MediaFileUtils::IsFileExists(destPath) && !MediaFileUtils::CreateFile(destPath)) {
328 NAPI_ERR_LOG("Create empty dest file in sandbox failed, path:%{private}s", destPath.c_str());
329 return E_HAS_FS_ERROR;
330 }
331
332 if (type == MovingPhotoResourceType::CLOUD_IMAGE) {
333 return MovingPhotoFileUtils::ConvertToMovingPhoto(srcFd, destPath, "", "");
334 } else if (type == MovingPhotoResourceType::CLOUD_VIDEO) {
335 return MovingPhotoFileUtils::ConvertToMovingPhoto(srcFd, "", destPath, "");
336 }
337
338 int32_t destFd = MediaFileUtils::OpenFile(destPath, MEDIA_FILEMODE_READWRITE);
339 if (destFd < 0) {
340 NAPI_ERR_LOG("Open dest file failed, error: %{public}d", errno);
341 return E_HAS_FS_ERROR;
342 }
343 UniqueFd destUniqueFd(destFd);
344
345 if (ftruncate(destUniqueFd.Get(), 0) == -1) {
346 NAPI_ERR_LOG("Truncate old file in sandbox failed, error:%{public}d", errno);
347 return E_HAS_FS_ERROR;
348 }
349 return CopyFileFromMediaLibrary(srcUniqueFd.Get(), destUniqueFd.Get());
350 }
351
HandleFd(int32_t & fd)352 static bool HandleFd(int32_t& fd)
353 {
354 if (fd == E_ERR) {
355 fd = E_HAS_FS_ERROR;
356 return false;
357 } else if (fd < 0) {
358 NAPI_ERR_LOG("Open failed due to OpenFile failure, error: %{public}d", fd);
359 return false;
360 }
361 return true;
362 }
363
RequestContentToSandbox(napi_env env,MovingPhotoAsyncContext * context)364 static int32_t RequestContentToSandbox(napi_env env, MovingPhotoAsyncContext* context)
365 {
366 string movingPhotoUri = context->movingPhotoUri;
367 if (context->sourceMode == SourceMode::ORIGINAL_MODE) {
368 MediaFileUtils::UriAppendKeyValue(movingPhotoUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
369 }
370 if (!context->destImageUri.empty()) {
371 int32_t imageFd = MovingPhotoNapi::OpenReadOnlyFile(movingPhotoUri, true, context->position);
372 CHECK_COND_RET(HandleFd(imageFd), imageFd, "Open source image file failed");
373 int32_t ret = WriteToSandboxUri(imageFd, context->destImageUri,
374 context->position == POSITION_CLOUD ? MovingPhotoResourceType::CLOUD_IMAGE
375 : MovingPhotoResourceType::DEFAULT);
376 CHECK_COND_RET(ret == E_OK, ret, "Write image to sandbox failed");
377 }
378 if (!context->destVideoUri.empty()) {
379 int32_t videoFd = MovingPhotoNapi::OpenReadOnlyFile(movingPhotoUri, false, context->position);
380 CHECK_COND_RET(HandleFd(videoFd), videoFd, "Open source video file failed");
381 if (context->compatibleMode == CompatibleMode::COMPATIBLE_FORMAT_MODE) {
382 NAPI_DEBUG_LOG("movingPhoto CompatibleMode COMPATIBLE_FORMAT_MODE");
383 int32_t ret = MovingPhotoNapi::DoMovingPhotoTranscode(env, videoFd, context);
384 CHECK_COND_RET(ret == E_OK, ret, "moving video transcode failed");
385 } else {
386 int32_t ret = WriteToSandboxUri(videoFd, context->destVideoUri,
387 context->position == POSITION_CLOUD ? MovingPhotoResourceType::CLOUD_VIDEO
388 : MovingPhotoResourceType::DEFAULT);
389 CHECK_COND_RET(ret == E_OK, ret, "Write video to sandbox failed");
390 }
391 }
392 if (!context->destLivePhotoUri.empty()) {
393 int32_t livePhotoFd = MovingPhotoNapi::OpenReadOnlyLivePhoto(movingPhotoUri, context->position);
394 CHECK_COND_RET(HandleFd(livePhotoFd), livePhotoFd, "Open source video file failed");
395 int32_t ret = WriteToSandboxUri(livePhotoFd, context->destLivePhotoUri);
396 CHECK_COND_RET(ret == E_OK, ret, "Write video to sandbox failed");
397 }
398 if (!context->destMetadataUri.empty()) {
399 int32_t extraDataFd = MovingPhotoNapi::OpenReadOnlyMetadata(movingPhotoUri);
400 CHECK_COND_RET(HandleFd(extraDataFd), extraDataFd, "Open moving photo metadata failed");
401 int32_t ret = WriteToSandboxUri(extraDataFd, context->destMetadataUri);
402 CHECK_COND_RET(ret == E_OK, ret, "Write metadata to sandbox failed");
403 }
404 return E_OK;
405 }
406
AcquireFdForArrayBuffer(MovingPhotoAsyncContext * context)407 static int32_t AcquireFdForArrayBuffer(MovingPhotoAsyncContext* context)
408 {
409 int32_t fd = 0;
410 string movingPhotoUri = context->movingPhotoUri;
411 if (context->sourceMode == SourceMode::ORIGINAL_MODE) {
412 MediaFileUtils::UriAppendKeyValue(movingPhotoUri, MEDIA_OPERN_KEYWORD, SOURCE_REQUEST);
413 }
414 switch (context->resourceType) {
415 case ResourceType::IMAGE_RESOURCE: {
416 fd = MovingPhotoNapi::OpenReadOnlyFile(movingPhotoUri, true, context->position);
417 CHECK_COND_RET(HandleFd(fd), fd, "Open source image file failed");
418 return fd;
419 }
420 case ResourceType::VIDEO_RESOURCE:
421 fd = MovingPhotoNapi::OpenReadOnlyFile(movingPhotoUri, false, context->position);
422 CHECK_COND_RET(HandleFd(fd), fd, "Open source video file failed");
423 return fd;
424 case ResourceType::PRIVATE_MOVING_PHOTO_RESOURCE:
425 fd = MovingPhotoNapi::OpenReadOnlyLivePhoto(movingPhotoUri, context->position);
426 CHECK_COND_RET(HandleFd(fd), fd, "Open live photo failed");
427 return fd;
428 case ResourceType::PRIVATE_MOVING_PHOTO_METADATA:
429 fd = MovingPhotoNapi::OpenReadOnlyMetadata(movingPhotoUri);
430 CHECK_COND_RET(HandleFd(fd), fd, "Open moving photo metadata failed");
431 return fd;
432 default:
433 NAPI_ERR_LOG("Invalid resource type: %{public}d", static_cast<int32_t>(context->resourceType));
434 return -EINVAL;
435 }
436 }
437
GetFdFromUri(const std::string & uri)438 int32_t MovingPhotoNapi::GetFdFromUri(const std::string &uri)
439 {
440 AppFileService::ModuleFileUri::FileUri destUri(uri);
441 string destPath = destUri.GetRealPath();
442 if (!MediaFileUtils::IsFileExists(destPath) && !MediaFileUtils::CreateFile(destPath)) {
443 NAPI_ERR_LOG("Create empty dest file in sandbox failed, path:%{private}s", destPath.c_str());
444 return E_ERR;
445 }
446 return MediaFileUtils::OpenFile(destPath, MEDIA_FILEMODE_READWRITE);
447 }
448
CallDoTranscoder(shared_ptr<OHOS::Media::MovingPhotoProgressHandler> movingPhotoProgressHandler,bool & isTranscoder)449 static int32_t CallDoTranscoder(shared_ptr<OHOS::Media::MovingPhotoProgressHandler> movingPhotoProgressHandler,
450 bool &isTranscoder)
451 {
452 if (!MovingPhotoCallTranscoder::DoTranscode(movingPhotoProgressHandler)) {
453 NAPI_INFO_LOG("DoTranscode fail");
454 isTranscoder = false;
455 return E_ERR;
456 }
457 return E_OK;
458 }
459
ArrayBufferToTranscode(napi_env env,MovingPhotoAsyncContext * context,int32_t fd)460 static int32_t ArrayBufferToTranscode(napi_env env, MovingPhotoAsyncContext* context, int32_t fd)
461 {
462 CHECK_COND_RET(context != nullptr, E_ERR, "context is null");
463 UniqueFd uniqueFd(fd);
464 int64_t offset = 0;
465 int64_t videoSize = 0;
466 int64_t extraDataSize = 0;
467 if (context->position == POSITION_CLOUD) {
468 int32_t ret = MovingPhotoFileUtils::GetMovingPhotoDetailedSize(uniqueFd.Get(), offset, videoSize,
469 extraDataSize);
470 if (ret != E_OK) {
471 context->error = JS_INNER_FAIL;
472 NAPI_ERR_LOG("get moving photo detailed size fail");
473 return E_ERR;
474 }
475 } else {
476 struct stat statSrc;
477 if (fstat(uniqueFd.Get(), &statSrc) == E_ERR) {
478 NAPI_DEBUG_LOG("File get stat failed, %{public}d", errno);
479 return E_HAS_FS_ERROR;
480 }
481 videoSize = statSrc.st_size;
482 }
483 auto abilityContext = AbilityRuntime::Context::GetApplicationContext();
484 CHECK_COND_RET(abilityContext != nullptr, E_ERR, "abilityContext is null");
485 string cachePath = abilityContext->GetCacheDir();
486 string destUri = cachePath + "/" +context->requestId + ".mp4";
487 NAPI_DEBUG_LOG("destUri:%{public}s", destUri.c_str());
488 int destFd = MovingPhotoNapi::GetFdFromUri(destUri);
489 if (destFd < 0) {
490 context->error = JS_INNER_FAIL;
491 NAPI_ERR_LOG("get destFd fail");
492 return E_ERR;
493 }
494 UniqueFd uniqueDestFd(destFd);
495 context->isTranscoder = true;
496 auto movingPhotoProgressHandler = std::make_shared<OHOS::Media::MovingPhotoProgressHandler>();
497 CHECK_COND_RET(movingPhotoProgressHandler != nullptr, E_ERR, "movingPhotoProgressHandler is null");
498 movingPhotoProgressHandler->env = env;
499 movingPhotoProgressHandler->srcFd = std::move(uniqueFd);
500 movingPhotoProgressHandler->destFd = std::move(uniqueDestFd);
501 movingPhotoProgressHandler->progressHandlerRef = context->progressHandlerRef;
502 movingPhotoProgressHandler->callbackFunc = MovingPhotoNapi::CallRequestContentCallBack;
503 movingPhotoProgressHandler->mediaAssetEnv = context->mediaAssetEnv;
504 movingPhotoProgressHandler->offset = offset;
505 movingPhotoProgressHandler->size = videoSize;
506 movingPhotoProgressHandler->contextData = context;
507 movingPhotoProgressHandler->onProgressFunc = context->threadsafeFunction;
508 return CallDoTranscoder(std::move(movingPhotoProgressHandler), context->isTranscoder);
509 }
510
RequestContentToArrayBuffer(napi_env env,MovingPhotoAsyncContext * context)511 static int32_t RequestContentToArrayBuffer(napi_env env, MovingPhotoAsyncContext* context)
512 {
513 NAPI_INFO_LOG("RequestContentToArrayBuffer");
514 if (context == nullptr) {
515 NAPI_INFO_LOG("context is null");
516 return E_ERR;
517 }
518 int32_t fd = AcquireFdForArrayBuffer(context);
519 if (fd < 0) {
520 return fd;
521 }
522 if (context->resourceType == ResourceType::VIDEO_RESOURCE &&
523 context->compatibleMode == CompatibleMode::COMPATIBLE_FORMAT_MODE) {
524 return ArrayBufferToTranscode(env, context, fd);
525 }
526 MovingPhotoNapi::SubRequestContent(fd, context);
527 return E_OK;
528 }
529
IsValidResourceType(int32_t resourceType)530 static bool IsValidResourceType(int32_t resourceType)
531 {
532 return resourceType == static_cast<int32_t>(ResourceType::IMAGE_RESOURCE) ||
533 resourceType == static_cast<int32_t>(ResourceType::VIDEO_RESOURCE) ||
534 (resourceType == static_cast<int32_t>(ResourceType::PRIVATE_MOVING_PHOTO_RESOURCE) &&
535 MediaLibraryNapiUtils::IsSystemApp()) ||
536 (resourceType == static_cast<int32_t>(ResourceType::PRIVATE_MOVING_PHOTO_METADATA) &&
537 MediaLibraryNapiUtils::IsSystemApp());
538 }
539
QueryPhotoPositionIPCExecute(const string & movingPhotoUri,int32_t userId,int32_t & position)540 static int32_t QueryPhotoPositionIPCExecute(const string &movingPhotoUri, int32_t userId, int32_t &position)
541 {
542 RequestContentRespBody respBody;
543 RequestContentReqBody reqBody;
544 reqBody.mediaId = MediaFileUtils::GetIdFromUri(movingPhotoUri);
545 uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_REQUEST_CONTENT);
546
547 std::unordered_map<std::string, std::string> headerMap{
548 {MediaColumn::MEDIA_ID, reqBody.mediaId}, {URI_TYPE, TYPE_PHOTOS}};
549 int32_t err =
550 IPC::UserDefineIPCClient().SetUserId(userId).SetHeader(headerMap).Call(businessCode, reqBody, respBody);
551 if (err != E_OK) {
552 NAPI_ERR_LOG("get position fail. err:%{public}d", err);
553 return E_ERR;
554 }
555
556 position = respBody.position;
557 return E_OK;
558 }
559
QueryPhotoPosition(const string & movingPhotoUri,bool hasReadPermission,int32_t & position)560 static int32_t QueryPhotoPosition(const string &movingPhotoUri, bool hasReadPermission, int32_t &position)
561 {
562 if (!MediaFileUtils::IsMediaLibraryUri(movingPhotoUri)) {
563 position = static_cast<int32_t>(PhotoPositionType::LOCAL);
564 return E_OK;
565 }
566
567 std::string MULTI_USER_URI_FLAG = "user=";
568 std::string str = movingPhotoUri;
569 size_t pos = str.find(MULTI_USER_URI_FLAG);
570 std::string userIdStr = "";
571 if (pos != std::string::npos) {
572 pos += MULTI_USER_URI_FLAG.length();
573 size_t end = str.find_first_of("&?", pos);
574 if (end == std::string::npos) {
575 end = str.length();
576 }
577 userIdStr = str.substr(pos, end - pos);
578 NAPI_INFO_LOG("QueryPhotoPosition for other user is %{public}s", userIdStr.c_str());
579 }
580 int32_t userId = userIdStr != "" && MediaFileUtils::IsValidInteger(userIdStr) ? atoi(userIdStr.c_str()) : -1;
581
582 DataShare::DataSharePredicates predicates;
583 predicates.EqualTo(MediaColumn::MEDIA_ID, MediaFileUtils::GetIdFromUri(movingPhotoUri));
584 std::vector<std::string> fetchColumn { PhotoColumn::PHOTO_POSITION };
585 string queryUri;
586 if (hasReadPermission) {
587 queryUri = PAH_QUERY_PHOTO;
588 } else {
589 queryUri = movingPhotoUri;
590 MediaFileUri::RemoveAllFragment(queryUri);
591 }
592 Uri uri(queryUri);
593 OperationObject object = OperationObject::UNKNOWN_OBJECT;
594 if (!MediaAssetRdbStore::GetInstance()->IsQueryAccessibleViaSandBox(uri, object, predicates) || userId != -1) {
595 return QueryPhotoPositionIPCExecute(movingPhotoUri, userId, position);
596 }
597
598 int errCode = 0;
599 auto resultSet = UserFileClient::Query(uri, predicates, fetchColumn, errCode, userId);
600 if (resultSet == nullptr || resultSet->GoToFirstRow() != E_OK) {
601 NAPI_ERR_LOG("query resultSet is nullptr");
602 return E_ERR;
603 }
604
605 int index;
606 int err = resultSet->GetColumnIndex(PhotoColumn::PHOTO_POSITION, index);
607 if (err != E_OK) {
608 NAPI_ERR_LOG("Failed to GetColumnIndex");
609 return E_ERR;
610 }
611 resultSet->GetInt(index, position);
612 return E_OK;
613 }
614
HasReadPermission()615 static bool HasReadPermission()
616 {
617 static bool result = (AccessTokenKit::VerifyAccessToken(IPCSkeleton::GetSelfTokenID(), PERM_READ_IMAGEVIDEO)
618 == PermissionState::PERMISSION_GRANTED);
619 return result;
620 }
621
SetContextInfo(unique_ptr<MovingPhotoAsyncContext> & context,MovingPhotoNapi * thisArg)622 static void SetContextInfo(unique_ptr<MovingPhotoAsyncContext>& context, MovingPhotoNapi* thisArg)
623 {
624 if (context != nullptr && thisArg != nullptr) {
625 context->movingPhotoUri = thisArg->GetUri();
626 context->sourceMode = thisArg->GetSourceMode();
627 context->compatibleMode = thisArg->GetCompatibleMode();
628 context->requestId = thisArg->GetRequestId();
629 context->progressHandlerRef = thisArg->GetProgressHandlerRef();
630 context->mediaAssetEnv = thisArg->GetMediaAssetEnv();
631 context->threadsafeFunction = thisArg->GetThreadsafeFunction();
632 }
633 }
634
ParseArgsForRequestContent(napi_env env,size_t argc,const napi_value argv[],MovingPhotoNapi * thisArg,unique_ptr<MovingPhotoAsyncContext> & context)635 static napi_value ParseArgsForRequestContent(napi_env env, size_t argc, const napi_value argv[],
636 MovingPhotoNapi* thisArg, unique_ptr<MovingPhotoAsyncContext>& context)
637 {
638 CHECK_COND_WITH_MESSAGE(env, (argc == ARGS_ONE || argc == ARGS_TWO), "Invalid number of arguments");
639 CHECK_COND(env, thisArg != nullptr, JS_INNER_FAIL);
640 SetContextInfo(context, thisArg);
641 int32_t resourceType = 0;
642 if (argc == ARGS_ONE) {
643 // return by array buffer
644 CHECK_ARGS(env, napi_get_value_int32(env, argv[ARGS_ZERO], &resourceType), JS_INNER_FAIL);
645 CHECK_COND_WITH_MESSAGE(env, IsValidResourceType(resourceType), "Invalid resource type");
646 context->requestContentMode = MovingPhotoAsyncContext::WRITE_TO_ARRAY_BUFFER;
647 context->resourceType = static_cast<ResourceType>(resourceType);
648 } else if (argc == ARGS_TWO) {
649 context->requestContentMode = MovingPhotoAsyncContext::WRITE_TO_SANDBOX;
650 napi_valuetype valueTypeFront;
651 napi_valuetype valueTypeBack;
652 CHECK_ARGS(env, napi_typeof(env, argv[ARGS_ZERO], &valueTypeFront), JS_INNER_FAIL);
653 CHECK_ARGS(env, napi_typeof(env, argv[ARGS_ONE], &valueTypeBack), JS_INNER_FAIL);
654 if (valueTypeFront == napi_string && valueTypeBack == napi_string) {
655 // write both image and video to sandbox
656 CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ZERO], context->destImageUri),
657 JS_INNER_FAIL);
658 CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ONE], context->destVideoUri),
659 JS_INNER_FAIL);
660 } else if (valueTypeFront == napi_number && valueTypeBack == napi_string) {
661 // write specific resource to sandbox
662 CHECK_ARGS(env, napi_get_value_int32(env, argv[ARGS_ZERO], &resourceType), JS_INNER_FAIL);
663 CHECK_COND_WITH_MESSAGE(env, IsValidResourceType(resourceType), "Invalid resource type");
664 if (resourceType == static_cast<int32_t>(ResourceType::IMAGE_RESOURCE)) {
665 CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ONE],
666 context->destImageUri), JS_INNER_FAIL);
667 } else if (resourceType == static_cast<int32_t>(ResourceType::VIDEO_RESOURCE)) {
668 CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ONE],
669 context->destVideoUri), JS_INNER_FAIL);
670 } else if (resourceType == static_cast<int32_t>(ResourceType::PRIVATE_MOVING_PHOTO_RESOURCE)) {
671 CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ONE],
672 context->destLivePhotoUri), JS_INNER_FAIL);
673 } else {
674 CHECK_ARGS(env, MediaLibraryNapiUtils::GetParamStringPathMax(env, argv[ARGS_ONE],
675 context->destMetadataUri), JS_INNER_FAIL);
676 }
677 context->resourceType = static_cast<ResourceType>(resourceType);
678 } else {
679 NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, __FUNCTION__, __LINE__, "Invalid type of arguments");
680 return nullptr;
681 }
682 }
683 RETURN_NAPI_TRUE(env);
684 }
685
RequestContentExecute(napi_env env,void * data)686 static void RequestContentExecute(napi_env env, void *data)
687 {
688 auto* context = static_cast<MovingPhotoAsyncContext*>(data);
689 int32_t ret = QueryPhotoPosition(context->movingPhotoUri, HasReadPermission(), context->position);
690 if (ret != E_OK) {
691 NAPI_ERR_LOG("Failed to query position of moving photo, ret: %{public}d", ret);
692 context->SaveError(ret);
693 return;
694 }
695 switch (context->requestContentMode) {
696 case MovingPhotoAsyncContext::WRITE_TO_SANDBOX:
697 ret = RequestContentToSandbox(env, context);
698 break;
699 case MovingPhotoAsyncContext::WRITE_TO_ARRAY_BUFFER:
700 ret = RequestContentToArrayBuffer(env, context);
701 break;
702 default:
703 NAPI_ERR_LOG("Invalid request content mode: %{public}d", static_cast<int32_t>(context->requestContentMode));
704 context->error = OHOS_INVALID_PARAM_CODE;
705 return;
706 }
707 if (ret != E_OK) {
708 context->SaveError(ret);
709 return;
710 }
711 }
712
RequestContentCompleteImpl(napi_env env,napi_status status,void * data)713 static void RequestContentCompleteImpl(napi_env env, napi_status status, void *data)
714 {
715 MovingPhotoAsyncContext *context = static_cast<MovingPhotoAsyncContext*>(data);
716 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
717
718 napi_value outBuffer = nullptr;
719 if (context->error == E_OK && context->requestContentMode == MovingPhotoAsyncContext::WRITE_TO_ARRAY_BUFFER) {
720 napi_status status = napi_create_external_arraybuffer(
721 env, context->arrayBufferData, context->arrayBufferLength,
722 [](napi_env env, void* data, void* hint) { free(data); }, nullptr, &outBuffer);
723 if (status != napi_ok) {
724 NAPI_ERR_LOG("Failed to create array buffer object, uri is %{public}s, resource type is %{public}d",
725 context->movingPhotoUri.c_str(), context->resourceType);
726 free(context->arrayBufferData);
727 context->arrayBufferData = nullptr;
728 context->error = JS_INNER_FAIL;
729 }
730 } else if (context->arrayBufferData != nullptr) {
731 free(context->arrayBufferData);
732 context->arrayBufferData = nullptr;
733 }
734
735 unique_ptr<JSAsyncContextOutput> outContext = make_unique<JSAsyncContextOutput>();
736 outContext->status = false;
737 napi_get_undefined(env, &outContext->data);
738
739 if (context->error != E_OK) {
740 context->HandleError(env, outContext->error);
741 } else {
742 outContext->status = true;
743 if (context->requestContentMode == MovingPhotoAsyncContext::WRITE_TO_ARRAY_BUFFER) {
744 outContext->data = outBuffer;
745 }
746 }
747 if (context->work != nullptr) {
748 MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, nullptr,
749 context->work, *outContext);
750 } else {
751 NAPI_ERR_LOG("Async work is nullptr");
752 }
753 delete context;
754 }
755
RequestContentComplete(napi_env env,napi_status status,void * data)756 static void RequestContentComplete(napi_env env, napi_status status, void *data)
757 {
758 MovingPhotoAsyncContext *context = static_cast<MovingPhotoAsyncContext*>(data);
759 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
760 if (context->isTranscoder) {
761 return;
762 }
763 RequestContentCompleteImpl(env, status, data);
764 }
765
JSRequestContent(napi_env env,napi_callback_info info)766 napi_value MovingPhotoNapi::JSRequestContent(napi_env env, napi_callback_info info)
767 {
768 size_t argc = ARGS_TWO;
769 napi_value argv[ARGS_TWO] = {0};
770 napi_value thisVar = nullptr;
771 CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
772
773 unique_ptr<MovingPhotoAsyncContext> asyncContext = make_unique<MovingPhotoAsyncContext>();
774 MovingPhotoNapi* nativeObject;
775 CHECK_ARGS(env, napi_unwrap(env, thisVar, reinterpret_cast<void **>(&nativeObject)), JS_INNER_FAIL);
776 CHECK_NULLPTR_RET(ParseArgsForRequestContent(env, argc, argv, nativeObject, asyncContext));
777
778 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSRequestContent",
779 RequestContentExecute, RequestContentComplete);
780 }
781
NewMovingPhotoNapi(napi_env env,const string & photoUri,SourceMode sourceMode,MovingPhotoParam & movingPhotoParam)782 napi_value MovingPhotoNapi::NewMovingPhotoNapi(napi_env env, const string& photoUri, SourceMode sourceMode,
783 MovingPhotoParam &movingPhotoParam)
784 {
785 napi_value constructor = nullptr;
786 napi_value instance = nullptr;
787 napi_value napiStringUri = nullptr;
788 napi_status status = napi_create_string_utf8(env, photoUri.c_str(), NAPI_AUTO_LENGTH, &napiStringUri);
789 CHECK_COND_RET(status == napi_ok, nullptr, "Failed to create napi string, napi status: %{public}d",
790 static_cast<int>(status));
791 status = napi_get_reference_value(env, constructor_, &constructor);
792 CHECK_COND_RET(status == napi_ok, nullptr, "Failed to get reference of constructor, napi status: %{public}d",
793 static_cast<int>(status));
794 status = napi_new_instance(env, constructor, 1, &napiStringUri, &instance);
795 CHECK_COND_RET(status == napi_ok, nullptr, "Failed to get new instance of moving photo, napi status: %{public}d",
796 static_cast<int>(status));
797 CHECK_COND_RET(instance != nullptr, nullptr, "Instance is nullptr");
798
799 MovingPhotoNapi* movingPhotoNapi = nullptr;
800 status = napi_unwrap(env, instance, reinterpret_cast<void**>(&movingPhotoNapi));
801 CHECK_COND_RET(status == napi_ok, nullptr, "Failed to unwarp instance of MovingPhotoNapi");
802 CHECK_COND_RET(movingPhotoNapi != nullptr, nullptr, "movingPhotoNapi is nullptr");
803 movingPhotoNapi->SetSourceMode(sourceMode);
804 movingPhotoNapi->SetRequestId(movingPhotoParam.requestId);
805 movingPhotoNapi->SetCompatibleMode(movingPhotoParam.compatibleMode);
806 movingPhotoNapi->SetProgressHandlerRef(movingPhotoParam.progressHandlerRef);
807 movingPhotoNapi->SetMediaAssetEnv(env);
808 movingPhotoNapi->SetThreadsafeFunction(movingPhotoParam.threadsafeFunction);
809 return instance;
810 }
811
JSGetUri(napi_env env,napi_callback_info info)812 napi_value MovingPhotoNapi::JSGetUri(napi_env env, napi_callback_info info)
813 {
814 MovingPhotoNapi *obj = nullptr;
815 napi_value thisVar = nullptr;
816 CHECK_ARGS(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr), JS_INNER_FAIL);
817 if (thisVar == nullptr) {
818 NapiError::ThrowError(env, JS_INNER_FAIL);
819 return nullptr;
820 }
821
822 CHECK_ARGS(env, napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj)), JS_INNER_FAIL);
823 if (obj == nullptr) {
824 NapiError::ThrowError(env, JS_INNER_FAIL);
825 return nullptr;
826 }
827
828 napi_value jsResult = nullptr;
829 CHECK_ARGS(env, napi_create_string_utf8(env, obj->GetUri().c_str(), NAPI_AUTO_LENGTH, &jsResult), JS_INNER_FAIL);
830 return jsResult;
831 }
832
DoMovingPhotoTranscode(napi_env env,int32_t & videoFd,MovingPhotoAsyncContext * context)833 int32_t MovingPhotoNapi::DoMovingPhotoTranscode(napi_env env, int32_t &videoFd, MovingPhotoAsyncContext* context)
834 {
835 CHECK_COND_RET(context != nullptr, E_ERR, "context is null");
836 if (videoFd == -1) {
837 NAPI_INFO_LOG("videoFd is null");
838 return E_ERR;
839 }
840 int64_t offset = 0;
841 UniqueFd uniqueVideoFd(videoFd);
842 int64_t videoSize = 0;
843 int64_t extraDataSize = 0;
844 if (context->position == POSITION_CLOUD) {
845 int32_t ret = MovingPhotoFileUtils::GetMovingPhotoDetailedSize(uniqueVideoFd.Get(), offset, videoSize,
846 extraDataSize);
847 CHECK_COND_RET(ret == E_OK, E_ERR, "get moving photo detailed size fail");
848 } else {
849 struct stat statSrc;
850 if (fstat(uniqueVideoFd.Get(), &statSrc) == E_ERR) {
851 NAPI_DEBUG_LOG("File get stat failed, %{public}d", errno);
852 return E_HAS_FS_ERROR;
853 }
854 videoSize = statSrc.st_size;
855 }
856 AppFileService::ModuleFileUri::FileUri fileUri(context->destVideoUri);
857 string destPath = fileUri.GetRealPath();
858 if (!MediaFileUtils::IsFileExists(destPath) && !MediaFileUtils::CreateFile(destPath)) {
859 NAPI_ERR_LOG("Create empty dest file in sandbox failed, path:%{private}s", destPath.c_str());
860 return E_HAS_FS_ERROR;
861 }
862 int32_t destFd = MediaFileUtils::OpenFile(destPath, MEDIA_FILEMODE_READWRITE);
863 if (destFd < 0) {
864 close(destFd);
865 NAPI_ERR_LOG("Open dest file failed, error: %{public}d", errno);
866 return E_HAS_FS_ERROR;
867 }
868 UniqueFd uniqueDestFd(destFd);
869 context->isTranscoder = true;
870 auto movingPhotoProgressHandler = std::make_shared<OHOS::Media::MovingPhotoProgressHandler>();
871 CHECK_COND_RET(movingPhotoProgressHandler != nullptr, E_ERR, "movingPhotoProgressHandler is null");
872 movingPhotoProgressHandler->env = env;
873 movingPhotoProgressHandler->srcFd = std::move(uniqueVideoFd);
874 movingPhotoProgressHandler->destFd = std::move(uniqueDestFd);
875 movingPhotoProgressHandler->progressHandlerRef = context->progressHandlerRef;
876 movingPhotoProgressHandler->callbackFunc = MovingPhotoNapi::CallRequestContentCallBack;
877 movingPhotoProgressHandler->mediaAssetEnv = context->mediaAssetEnv;
878 movingPhotoProgressHandler->offset = offset;
879 movingPhotoProgressHandler->size = videoSize;
880 movingPhotoProgressHandler->contextData = context;
881 movingPhotoProgressHandler->onProgressFunc = context->threadsafeFunction;
882 return CallDoTranscoder(std::move(movingPhotoProgressHandler), context->isTranscoder);
883 }
884
RequestCloudContentArrayBuffer(int32_t fd,MovingPhotoAsyncContext * context)885 void MovingPhotoNapi::RequestCloudContentArrayBuffer(int32_t fd, MovingPhotoAsyncContext* context)
886 {
887 if (context->position != POSITION_CLOUD) {
888 NAPI_ERR_LOG("Failed to check postion: %{public}d", context->position);
889 context->arrayBufferData = nullptr;
890 context->error = JS_INNER_FAIL;
891 return;
892 }
893
894 int64_t imageSize = 0;
895 int64_t videoSize = 0;
896 int64_t extraDataSize = 0;
897 int32_t err = MovingPhotoFileUtils::GetMovingPhotoDetailedSize(fd, imageSize, videoSize, extraDataSize);
898 if (err != E_OK) {
899 NAPI_ERR_LOG("Failed to get detailed size of moving photo");
900 context->arrayBufferData = nullptr;
901 context->SaveError(E_HAS_FS_ERROR);
902 return;
903 }
904
905 int32_t ret = E_FAIL;
906 size_t fileSize = 0;
907 switch (context->resourceType) {
908 case ResourceType::IMAGE_RESOURCE:
909 fileSize = static_cast<size_t>(imageSize);
910 context->arrayBufferData = malloc(fileSize);
911 ret = MovingPhotoFileUtils::ConvertToMovingPhoto(fd, context->arrayBufferData, nullptr, nullptr);
912 break;
913 case ResourceType::VIDEO_RESOURCE:
914 fileSize = static_cast<size_t>(videoSize);
915 context->arrayBufferData = malloc(fileSize);
916 ret = MovingPhotoFileUtils::ConvertToMovingPhoto(fd, nullptr, context->arrayBufferData, nullptr);
917 break;
918 default:
919 NAPI_ERR_LOG("Invalid resource type: %{public}d", static_cast<int32_t>(context->resourceType));
920 break;
921 }
922
923 if (!context->arrayBufferData || ret != E_OK) {
924 NAPI_ERR_LOG(
925 "Failed to get arraybuffer, resource type is %{public}d", static_cast<int32_t>(context->resourceType));
926 context->error = JS_INNER_FAIL;
927 return;
928 }
929 context->arrayBufferLength = fileSize;
930 }
931
BufferTranscodeRequestContent(int32_t fd,MovingPhotoAsyncContext * context)932 void BufferTranscodeRequestContent(int32_t fd, MovingPhotoAsyncContext* context)
933 {
934 UniqueFd uniqueFd(fd);
935 off_t fileLen = lseek(uniqueFd.Get(), 0, SEEK_END);
936 if (fileLen < 0) {
937 NAPI_ERR_LOG("Failed to get file length, error: %{public}d", errno);
938 context->SaveError(E_HAS_FS_ERROR);
939 return;
940 }
941
942 off_t ret = lseek(uniqueFd.Get(), 0, SEEK_SET);
943 if (ret < 0) {
944 NAPI_ERR_LOG("Failed to reset file offset, error: %{public}d", errno);
945 context->SaveError(E_HAS_FS_ERROR);
946 return;
947 }
948
949 size_t fileSize = static_cast<size_t>(fileLen);
950 context->arrayBufferData = malloc(fileSize);
951 if (!context->arrayBufferData) {
952 NAPI_ERR_LOG("Failed to malloc array buffer data, moving photo uri is %{public}s, resource type is %{public}d",
953 context->movingPhotoUri.c_str(), static_cast<int32_t>(context->resourceType));
954 context->error = JS_INNER_FAIL;
955 return;
956 }
957 size_t readBytes = static_cast<size_t>(read(uniqueFd.Get(), context->arrayBufferData, fileSize));
958 if (readBytes != fileSize) {
959 NAPI_ERR_LOG("read file failed, read bytes is %{public}zu, actual length is %{public}zu, "
960 "error: %{public}d", readBytes, fileSize, errno);
961 free(context->arrayBufferData);
962 context->arrayBufferData = nullptr;
963 context->SaveError(E_HAS_FS_ERROR);
964 return;
965 }
966 context->arrayBufferLength = fileSize;
967 return;
968 }
969
SubRequestContent(int32_t fd,MovingPhotoAsyncContext * context)970 void MovingPhotoNapi::SubRequestContent(int32_t fd, MovingPhotoAsyncContext* context)
971 {
972 if (context->position == POSITION_CLOUD) {
973 return RequestCloudContentArrayBuffer(fd, context);
974 }
975 BufferTranscodeRequestContent(fd, context);
976 }
977
CallArrayBufferRequestContentComplete(napi_env env,MovingPhotoAsyncContext * context)978 void CallArrayBufferRequestContentComplete(napi_env env, MovingPhotoAsyncContext* context)
979 {
980 auto abilityContext = AbilityRuntime::Context::GetApplicationContext();
981 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
982 CHECK_NULL_PTR_RETURN_VOID(abilityContext, "Async abilityContext is null");
983 string cachePath = abilityContext->GetCacheDir();
984 string destUri = cachePath + "/" +context->requestId + ".mp4";
985 NAPI_INFO_LOG("CallArrayBufferRequestContentComplete start destUri:%{public}s", destUri.c_str());
986 int fd = MovingPhotoNapi::GetFdFromUri(destUri);
987 if (fd < 0) {
988 NAPI_ERR_LOG("get fd fail");
989 context->error = JS_INNER_FAIL;
990 return;
991 }
992 UniqueFd uniqueFd(fd);
993 BufferTranscodeRequestContent(uniqueFd.Get(), context);
994 if (!MediaFileUtils::DeleteFile(destUri)) {
995 NAPI_WARN_LOG("remove fail, errno:%{public}d", errno);
996 }
997 return;
998 }
999
RequestCompletCallback(napi_env env,napi_status status,void * data)1000 static void RequestCompletCallback(napi_env env, napi_status status, void *data)
1001 {
1002 MovingPhotoAsyncContext* asyncContext = static_cast<MovingPhotoAsyncContext*>(data);
1003 CHECK_NULL_PTR_RETURN_VOID(asyncContext, "asyncContext is null");
1004 switch (asyncContext->requestContentMode) {
1005 case MovingPhotoAsyncContext::WRITE_TO_SANDBOX:
1006 RequestContentCompleteImpl(env, status, asyncContext);
1007 return;
1008 case MovingPhotoAsyncContext::WRITE_TO_ARRAY_BUFFER:
1009 CallArrayBufferRequestContentComplete(env, asyncContext);
1010 RequestContentCompleteImpl(env, status, asyncContext);
1011 return;
1012 default:
1013 NAPI_ERR_LOG("Request content mode: %{public}d", static_cast<int32_t>(asyncContext->requestContentMode));
1014 asyncContext->error = OHOS_INVALID_PARAM_CODE;
1015 return;
1016 }
1017 }
1018
CallRequestContentCallBack(napi_env env,void * context,int32_t errorCode)1019 void MovingPhotoNapi::CallRequestContentCallBack(napi_env env, void* context, int32_t errorCode)
1020 {
1021 CHECK_NULL_PTR_RETURN_VOID(context, "context is null");
1022 MovingPhotoAsyncContext* mContext = static_cast<MovingPhotoAsyncContext*>(context);
1023 CHECK_NULL_PTR_RETURN_VOID(mContext, "context is null");
1024 if (errorCode != E_OK) {
1025 NAPI_ERR_LOG("MovingPhotoNapi::CallRequestContentCallBack errorCode is %{public}d", errorCode);
1026 mContext->error = errorCode;
1027 }
1028
1029 napi_status status;
1030 napi_value result = nullptr;
1031 napi_value resource = nullptr;
1032 unique_ptr<MovingPhotoAsyncContext> asyncContext = make_unique<MovingPhotoAsyncContext>();
1033
1034 NAPI_CREATE_PROMISE(env, asyncContext->callbackRef, asyncContext->deferred, result);
1035 NAPI_CREATE_RESOURCE_NAME(env, resource, "CallRequestContentCallBack", asyncContext);
1036 status = napi_create_async_work(
1037 env, nullptr, resource, [](napi_env env, void *data) {
1038 MovingPhotoAsyncContext* asyncWorkContext = static_cast<MovingPhotoAsyncContext*>(data);
1039 CHECK_NULL_PTR_RETURN_VOID(asyncWorkContext, "Async work context is null");
1040 },
1041 reinterpret_cast<napi_async_complete_callback>(RequestCompletCallback),
1042 context, &asyncContext->work);
1043 if (status != napi_ok) {
1044 napi_get_undefined(env, &result);
1045 } else {
1046 napi_queue_async_work_with_qos(env, asyncContext->work, napi_qos_user_initiated);
1047 (void)asyncContext.release();
1048 }
1049 }
1050
IsSandBoxMovingPhotoVideoReady(MovingPhotoAsyncContext * context,string & videoUri)1051 static void IsSandBoxMovingPhotoVideoReady(MovingPhotoAsyncContext *context, string &videoUri)
1052 {
1053 AppFileService::ModuleFileUri::FileUri fileUri(videoUri);
1054 std::string videoPath = fileUri.GetRealPath();
1055 size_t fileSize = 0;
1056 context->isVideoReady = MediaFileUtils::GetFileSize(videoPath, fileSize) && (fileSize > 0);
1057 NAPI_DEBUG_LOG("videoUri:%{public}s, video size:%zu", videoUri.c_str(), fileSize);
1058 }
1059
IsMovingPhotoVideoReady(MovingPhotoAsyncContext * context)1060 static void IsMovingPhotoVideoReady(MovingPhotoAsyncContext *context)
1061 {
1062 DataShare::DataSharePredicates predicates;
1063 string queryId = MediaFileUtils::GetIdFromUri(context->movingPhotoUri);
1064 predicates.EqualTo(MediaColumn::MEDIA_ID, queryId);
1065 vector<string> columns;
1066 Uri uri(MEDIALIBRARY_DATA_URI + "/" + MEDIA_QUERY_OPRN_MOVING_PHOTO_VIDEO_READY + "/"
1067 + MEDIA_QUERY_OPRN_MOVING_PHOTO_VIDEO_READY);
1068 int errCode = 0;
1069 shared_ptr<DataShare::DataShareResultSet> resultSet = UserFileClient::Query(uri, predicates, columns, errCode);
1070 if (errCode == E_PERMISSION_DENIED) {
1071 context->error = OHOS_PERMISSION_DENIED_CODE;
1072 return;
1073 }
1074 if ((resultSet == nullptr) || (resultSet->GoToFirstRow() != NativeRdb::E_OK)) {
1075 NAPI_ERR_LOG("Query movingphoto fail");
1076 context->error = JS_E_INNER_FAIL;
1077 return;
1078 }
1079 int32_t ready;
1080 if (resultSet->GetInt(0, ready) != NativeRdb::E_OK) {
1081 NAPI_ERR_LOG("can not get movingphoto video ready");
1082 context->error = JS_E_INNER_FAIL;
1083 return;
1084 }
1085 NAPI_INFO_LOG("movingphoto video ready:%d", ready);
1086 context->isVideoReady = (ready != 0);
1087 }
1088
IsVideoReadyExecute(napi_env env,void * data)1089 static void IsVideoReadyExecute(napi_env env, void *data)
1090 {
1091 MovingPhotoAsyncContext *context = static_cast<MovingPhotoAsyncContext *>(data);
1092 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1093 if (MediaFileUtils::IsMediaLibraryUri(context->movingPhotoUri)) {
1094 IsMovingPhotoVideoReady(context);
1095 return;
1096 }
1097
1098 std::vector<std::string> uris;
1099 if (MediaFileUtils::SplitMovingPhotoUri(context->movingPhotoUri, uris)) {
1100 IsSandBoxMovingPhotoVideoReady(context, uris[MOVING_PHOTO_VIDEO_POS]);
1101 return;
1102 }
1103
1104 NAPI_ERR_LOG("Failed to check uri of moving photo:%{public}s", context->movingPhotoUri.c_str());
1105 context->error = JS_E_INNER_FAIL;
1106 }
1107
IsVideoReadyComplete(napi_env env,napi_status status,void * data)1108 static void IsVideoReadyComplete(napi_env env, napi_status status, void *data)
1109 {
1110 MovingPhotoAsyncContext *context = static_cast<MovingPhotoAsyncContext *>(data);
1111 CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1112 unique_ptr<JSAsyncContextOutput> outContext = make_unique<JSAsyncContextOutput>();
1113 outContext->status = false;
1114 CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &outContext->data), JS_E_INNER_FAIL);
1115 CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &outContext->error), JS_E_INNER_FAIL);
1116 if (context->error == ERR_DEFAULT) {
1117 CHECK_ARGS_RET_VOID(env, napi_get_boolean(env, context->isVideoReady, &outContext->data), JS_E_INNER_FAIL);
1118 outContext->status = true;
1119 } else {
1120 context->HandleError(env, outContext->error);
1121 }
1122 if (context->work != nullptr) {
1123 MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, nullptr, context->work, *outContext);
1124 }
1125 delete context;
1126 }
1127
JSIsVideoReady(napi_env env,napi_callback_info info)1128 napi_value MovingPhotoNapi::JSIsVideoReady(napi_env env, napi_callback_info info)
1129 {
1130 NAPI_DEBUG_LOG("JSIsVideoReady start");
1131 if (!MediaLibraryNapiUtils::IsSystemApp()) {
1132 NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
1133 return nullptr;
1134 }
1135 auto asyncContext = make_unique<MovingPhotoAsyncContext>();
1136 CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsOnlyCallBack(env, info, asyncContext), JS_E_INNER_FAIL);
1137 asyncContext->movingPhotoUri = asyncContext->objectInfo->GetUri();
1138 return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSIsVideoReady",
1139 IsVideoReadyExecute, IsVideoReadyComplete);
1140 }
1141 } // namespace Media
1142 } // namespace OHOS