• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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