1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "thumbnail_manager.h"
17
18 #include <memory>
19 #include <mutex>
20 #include <sys/mman.h>
21 #include <sys/stat.h>
22 #include <uuid/uuid.h>
23
24 #include "ashmem.h"
25 #include "directory_ex.h"
26 #include "image_source.h"
27 #include "image_type.h"
28 #include "js_native_api.h"
29 #include "media_file_uri.h"
30 #include "medialibrary_errno.h"
31 #include "medialibrary_napi_log.h"
32 #include "medialibrary_napi_utils.h"
33 #include "medialibrary_tracer.h"
34 #include "pixel_map.h"
35 #include "pixel_map_napi.h"
36 #include "post_proc.h"
37 #include "securec.h"
38 #include "string_ex.h"
39 #include "thumbnail_const.h"
40 #include "unique_fd.h"
41 #include "userfile_manager_types.h"
42 #include "uv.h"
43 #include "userfile_client.h"
44
45 #ifdef IMAGE_PURGEABLE_PIXELMAP
46 #include "purgeable_pixelmap_builder.h"
47 #endif
48
49 using namespace std;
50 const int UUID_STR_LENGTH = 37;
51
52 namespace OHOS {
53 namespace Media {
54 shared_ptr<ThumbnailManager> ThumbnailManager::instance_ = nullptr;
55 mutex ThumbnailManager::mutex_;
56 bool ThumbnailManager::init_ = false;
57 static constexpr int32_t DEFAULT_FD = -1;
58
ThumbnailRequest(const RequestPhotoParams & params,napi_env env,napi_ref callback)59 ThumbnailRequest::ThumbnailRequest(const RequestPhotoParams ¶ms, napi_env env,
60 napi_ref callback) : callback_(env, callback), requestPhotoType(params.type), uri_(params.uri),
61 path_(params.path), requestSize_(params.size)
62 {
63 }
64
~ThumbnailRequest()65 ThumbnailRequest::~ThumbnailRequest()
66 {
67 }
68
ReleaseCallbackRef()69 void ThumbnailRequest::ReleaseCallbackRef()
70 {
71 std::lock_guard<std::mutex> lock(mutex_);
72 if (callback_.callBackRef_) {
73 napi_delete_reference(callback_.env_, callback_.callBackRef_);
74 callback_.callBackRef_ = nullptr;
75 }
76 }
77
UpdateStatus(ThumbnailStatus status)78 bool ThumbnailRequest::UpdateStatus(ThumbnailStatus status)
79 {
80 std::lock_guard<std::mutex> lock(mutex_);
81 if (status <= status_) {
82 return false;
83 }
84 status_ = status;
85 return true;
86 }
87
GetStatus()88 ThumbnailStatus ThumbnailRequest::GetStatus()
89 {
90 std::lock_guard<std::mutex> lock(mutex_);
91 return status_;
92 }
93
NeedContinue()94 bool ThumbnailRequest::NeedContinue()
95 {
96 return GetStatus() < ThumbnailStatus::THUMB_REMOVE;
97 }
98
IsPhotoSizeThumb(const Size & size)99 static bool IsPhotoSizeThumb(const Size &size)
100 {
101 return ((size.width >= DEFAULT_THUMB_SIZE || size.height >= DEFAULT_THUMB_SIZE) ||
102 (size.width == DEFAULT_MTH_SIZE || size.height == DEFAULT_MTH_SIZE));
103 }
104
NeedFastThumb(const Size & size,RequestPhotoType type)105 static bool NeedFastThumb(const Size &size, RequestPhotoType type)
106 {
107 return IsPhotoSizeThumb(size) && (type != RequestPhotoType::REQUEST_QUALITY_THUMBNAIL);
108 }
109
NeedQualityThumb(const Size & size,RequestPhotoType type)110 static bool NeedQualityThumb(const Size &size, RequestPhotoType type)
111 {
112 return IsPhotoSizeThumb(size) && (type != RequestPhotoType::REQUEST_FAST_THUMBNAIL);
113 }
114
MMapFdPtr(int32_t fd,bool isNeedRelease)115 MMapFdPtr::MMapFdPtr(int32_t fd, bool isNeedRelease)
116 {
117 if (fd < 0) {
118 NAPI_ERR_LOG("Fd is invalid: %{public}d", fd);
119 return;
120 }
121
122 struct stat st;
123 if (fstat(fd, &st) == -1) {
124 NAPI_ERR_LOG("fstat error, errno:%{public}d", errno);
125 return;
126 }
127 size_ = st.st_size;
128
129 // mmap ptr from fd
130 fdPtr_ = mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd, 0);
131 if (fdPtr_ == MAP_FAILED || fdPtr_ == nullptr) {
132 NAPI_ERR_LOG("mmap uniqueFd failed, errno = %{public}d", errno);
133 return;
134 }
135
136 isValid_ = true;
137 isNeedRelease_ = isNeedRelease;
138 }
139
~MMapFdPtr()140 MMapFdPtr::~MMapFdPtr()
141 {
142 // munmap ptr from fd
143 if (isNeedRelease_) {
144 munmap(fdPtr_, size_);
145 }
146 }
147
GetFdPtr()148 void* MMapFdPtr::GetFdPtr()
149 {
150 return fdPtr_;
151 }
152
GetFdSize()153 off_t MMapFdPtr::GetFdSize()
154 {
155 return size_;
156 }
157
IsValid()158 bool MMapFdPtr::IsValid()
159 {
160 return isValid_;
161 }
162
GenerateRequestId()163 static string GenerateRequestId()
164 {
165 uuid_t uuid;
166 uuid_generate(uuid);
167 char str[UUID_STR_LENGTH] = {};
168 uuid_unparse(uuid, str);
169 return str;
170 }
171
GetInstance()172 shared_ptr<ThumbnailManager> ThumbnailManager::GetInstance()
173 {
174 if (instance_ == nullptr) {
175 lock_guard<mutex> lock(mutex_);
176 if (instance_ == nullptr) {
177 instance_ = shared_ptr<ThumbnailManager>(new ThumbnailManager());
178 }
179 }
180
181 return instance_;
182 }
183
Init()184 void ThumbnailManager::Init()
185 {
186 std::lock_guard<std::mutex> lock(mutex_);
187 if (init_) {
188 return;
189 }
190 init_ = true;
191 isThreadRunning_ = true;
192 for (auto i = 0; i < THREAD_NUM; i++) {
193 threads_.emplace_back(
194 std::thread([this, num = i]() { this->ImageWorker(num); })
195 );
196 threads_[i].detach();
197 }
198 return;
199 }
200
AddPhotoRequest(const RequestPhotoParams & params,napi_env env,napi_ref callback)201 string ThumbnailManager::AddPhotoRequest(const RequestPhotoParams ¶ms, napi_env env, napi_ref callback)
202 {
203 shared_ptr<ThumbnailRequest> request = make_shared<ThumbnailRequest>(params, env, callback);
204 string requestId = GenerateRequestId();
205 request->SetUUID(requestId);
206 if (!thumbRequest_.Insert(requestId, request)) {
207 return "";
208 }
209 // judge from request option
210 if (NeedFastThumb(params.size, params.type)) {
211 AddFastPhotoRequest(request);
212 } else {
213 AddQualityPhotoRequest(request);
214 }
215 return requestId;
216 }
217
RemovePhotoRequest(const string & requestId)218 void ThumbnailManager::RemovePhotoRequest(const string &requestId)
219 {
220 RequestSharedPtr ptr;
221 if (thumbRequest_.Find(requestId, ptr)) {
222 if (ptr == nullptr) {
223 return;
224 }
225 // do not need delete from queue, just update remove status.
226 ptr->UpdateStatus(ThumbnailStatus::THUMB_REMOVE);
227 ptr->ReleaseCallbackRef();
228 }
229 thumbRequest_.Erase(requestId);
230 }
231
~ThumbnailManager()232 ThumbnailManager::~ThumbnailManager()
233 {
234 isThreadRunning_ = false;
235 queueCv_.notify_all();
236 for (auto &thread : threads_) {
237 if (thread.joinable()) {
238 thread.join();
239 }
240 }
241 }
242
SetThreadName(const string & threadName,int num)243 void SetThreadName(const string &threadName, int num)
244 {
245 string name = threadName;
246 name.append(to_string(num));
247 pthread_setname_np(pthread_self(), name.c_str());
248 }
249
AddFastPhotoRequest(const RequestSharedPtr & request)250 void ThumbnailManager::AddFastPhotoRequest(const RequestSharedPtr &request)
251 {
252 request->UpdateStatus(ThumbnailStatus::THUMB_FAST);
253 fastQueue_.Push(request);
254 queueCv_.notify_one();
255 }
256
AddQualityPhotoRequest(const RequestSharedPtr & request)257 void ThumbnailManager::AddQualityPhotoRequest(const RequestSharedPtr &request)
258 {
259 request->UpdateStatus(ThumbnailStatus::THUMB_QUALITY);
260 qualityQueue_.Push(request);
261 queueCv_.notify_one();
262 }
263
GetFastThumbNewSize(const Size & size,Size & newSize)264 static bool GetFastThumbNewSize(const Size &size, Size &newSize)
265 {
266 // if thumbnail size is YEAR SIZE, do not need to request fast thumb
267 // if thumbnail size is MTH SIZE, return YEAR SIZE
268 // if thumbnail size is THUMB SIZE, return MTH SIZE
269 // else return THUMB SIZE
270 if (size.width == DEFAULT_YEAR_SIZE && size.height == DEFAULT_YEAR_SIZE) {
271 newSize.height = DEFAULT_YEAR_SIZE;
272 newSize.width = DEFAULT_YEAR_SIZE;
273 return false;
274 } else if (size.width == DEFAULT_MTH_SIZE && size.height == DEFAULT_MTH_SIZE) {
275 newSize.height = DEFAULT_YEAR_SIZE;
276 newSize.width = DEFAULT_YEAR_SIZE;
277 return true;
278 } else if (size.width <= DEFAULT_THUMB_SIZE && size.height <= DEFAULT_THUMB_SIZE) {
279 newSize.height = DEFAULT_MTH_SIZE;
280 newSize.width = DEFAULT_MTH_SIZE;
281 return true;
282 } else {
283 newSize.height = DEFAULT_THUMB_SIZE;
284 newSize.width = DEFAULT_THUMB_SIZE;
285 return true;
286 }
287 }
288
OpenThumbnail(const string & path,ThumbnailType type)289 static int OpenThumbnail(const string &path, ThumbnailType type)
290 {
291 if (!path.empty()) {
292 string sandboxPath = GetSandboxPath(path, type);
293 int fd = -1;
294 if (!sandboxPath.empty()) {
295 fd = open(sandboxPath.c_str(), O_RDONLY);
296 }
297 if (fd > 0) {
298 return fd;
299 }
300 }
301 return E_ERR;
302 }
303
OpenKeyFrameThumbnail(const string & path,const int32_t & beginStamp,const int32_t & type)304 static int OpenKeyFrameThumbnail(const string &path, const int32_t &beginStamp, const int32_t &type)
305 {
306 if (!path.empty()) {
307 string sandboxPath = GetKeyFrameSandboxPath(path, beginStamp, type);
308 int fd = -1;
309 string absFilePath;
310 if (!sandboxPath.empty() && PathToRealPath(sandboxPath, absFilePath)) {
311 fd = open(absFilePath.c_str(), O_RDONLY);
312 }
313 if (fd > 0) {
314 return fd;
315 }
316 NAPI_ERR_LOG("OpenKeyFrameThumbnail failed, fd: %{public}d, errno:%{public}d", fd, errno);
317 }
318 return E_ERR;
319 }
320
IfSizeEqualsRatio(const Size & imageSize,const Size & targetSize)321 static bool IfSizeEqualsRatio(const Size &imageSize, const Size &targetSize)
322 {
323 if (imageSize.height <= 0 || targetSize.height <= 0) {
324 return false;
325 }
326
327 float imageSizeScale = static_cast<float>(imageSize.width) / static_cast<float>(imageSize.height);
328 float targetSizeScale = static_cast<float>(targetSize.width) / static_cast<float>(targetSize.height);
329 if (imageSizeScale - targetSizeScale > FLOAT_EPSILON || targetSizeScale - imageSizeScale > FLOAT_EPSILON) {
330 return false;
331 } else {
332 return true;
333 }
334 }
335
CreateThumbnailByAshmem(UniqueFd & uniqueFd,const Size & size)336 static PixelMapPtr CreateThumbnailByAshmem(UniqueFd &uniqueFd, const Size &size)
337 {
338 MediaLibraryTracer tracer;
339 tracer.Start("CreateThumbnailByAshmem");
340
341 Media::InitializationOptions option = {
342 .size = size,
343 };
344 PixelMapPtr pixel = Media::PixelMap::Create(option);
345 if (pixel == nullptr) {
346 NAPI_ERR_LOG("Can not create pixel");
347 return nullptr;
348 }
349
350 UniqueFd dupFd = UniqueFd(dup(uniqueFd.Get()));
351 MMapFdPtr mmapFd(dupFd.Get(), false);
352 if (!mmapFd.IsValid()) {
353 NAPI_ERR_LOG("Can not mmap by fd");
354 return nullptr;
355 }
356 auto memSize = static_cast<int32_t>(mmapFd.GetFdSize());
357
358 void* fdPtr = new int32_t();
359 *static_cast<int32_t*>(fdPtr) = dupFd.Release();
360 pixel->SetPixelsAddr(mmapFd.GetFdPtr(), fdPtr, memSize, Media::AllocatorType::SHARE_MEM_ALLOC, nullptr);
361 return pixel;
362 }
363
DecodeThumbnailData(napi_env env,const UniqueFd & uniqueFd,const Size & size)364 static napi_value DecodeThumbnailData(napi_env env, const UniqueFd &uniqueFd, const Size &size)
365 {
366 MediaLibraryTracer tracer;
367 tracer.Start("DecodeThumbnailData");
368
369 napi_value result = nullptr;
370 off_t fileLen = lseek(uniqueFd.Get(), 0, SEEK_END);
371 if (fileLen < 0) {
372 NAPI_ERR_LOG("Failed to get file size");
373 return result;
374 }
375 off_t ret = lseek(uniqueFd.Get(), 0, SEEK_SET);
376 if (ret < 0) {
377 NAPI_ERR_LOG("Failed to reset file offset");
378 return result;
379 }
380
381 void* arrayBufferData = nullptr;
382 napi_value arrayBuffer;
383 if (napi_create_arraybuffer(env, fileLen, &arrayBufferData, &arrayBuffer) != napi_ok) {
384 NAPI_ERR_LOG("failed to create napi arraybuffer");
385 return result;
386 }
387
388 ssize_t readBytes = read(uniqueFd.Get(), arrayBufferData, fileLen);
389 if (readBytes != fileLen) {
390 NAPI_ERR_LOG("read file failed");
391 return result;
392 }
393
394 return arrayBuffer;
395 }
396
DecodeThumbnail(const UniqueFd & uniqueFd,const Size & size)397 static PixelMapPtr DecodeThumbnail(const UniqueFd &uniqueFd, const Size &size)
398 {
399 MediaLibraryTracer tracer;
400 tracer.Start("ImageSource::CreateImageSource");
401 SourceOptions opts;
402 uint32_t err = 0;
403 unique_ptr<ImageSource> imageSource = ImageSource::CreateImageSource(uniqueFd.Get(), opts, err);
404 if (imageSource == nullptr) {
405 NAPI_ERR_LOG("CreateImageSource err %{public}d", err);
406 return nullptr;
407 }
408
409 ImageInfo imageInfo;
410 err = imageSource->GetImageInfo(0, imageInfo);
411 if (err != E_OK) {
412 NAPI_ERR_LOG("GetImageInfo err %{public}d", err);
413 return nullptr;
414 }
415
416 bool isEqualsRatio = IfSizeEqualsRatio(imageInfo.size, size);
417 DecodeOptions decodeOpts;
418 decodeOpts.desiredSize = isEqualsRatio ? size : imageInfo.size;
419 unique_ptr<PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOpts, err);
420 if (pixelMap == nullptr) {
421 NAPI_ERR_LOG("CreatePixelMap err %{public}d", err);
422 return nullptr;
423 }
424
425 PostProc postProc;
426 if (size.width != DEFAULT_ORIGINAL && !isEqualsRatio && !postProc.CenterScale(size, *pixelMap)) {
427 NAPI_ERR_LOG("CenterScale failed, size: %{public}d * %{public}d, imageInfo size: %{public}d * %{public}d",
428 size.width, size.height, imageInfo.size.width, imageInfo.size.height);
429 return nullptr;
430 }
431
432 // Make the ashmem of pixelmap to be purgeable after the operation on ashmem.
433 // And then make the pixelmap subject to PurgeableManager's control.
434 #ifdef IMAGE_PURGEABLE_PIXELMAP
435 PurgeableBuilder::MakePixelMapToBePurgeable(pixelMap, imageSource, decodeOpts, size);
436 #endif
437 return pixelMap;
438 }
439
GetArrayBufferFromServer(const string & uriStr,const string & path,const int32_t & type)440 static int32_t GetArrayBufferFromServer(const string &uriStr, const string &path, const int32_t &type)
441 {
442 int32_t lcdSize = -1;
443 int32_t thmSize = 0;
444 int32_t size = 0;
445 if (type == 1) {
446 size = lcdSize;
447 } else {
448 size = thmSize;
449 }
450 string openUriStr = uriStr + "?" + MEDIA_OPERN_KEYWORD + "=" + MEDIA_DATA_DB_THUMBNAIL + "&" +
451 MEDIA_DATA_DB_WIDTH + "=" + to_string(size) + "&" + MEDIA_DATA_DB_HEIGHT + "=" +
452 to_string(size);
453 NAPI_DEBUG_LOG("GetArrayBufferFromServer openUriStr = %{public}s", openUriStr.c_str());
454 if (IsAsciiString(path)) {
455 openUriStr += "&" + THUMBNAIL_PATH + "=" + path;
456 }
457 Uri openUri(openUriStr);
458 return UserFileClient::OpenFile(openUri, "R");
459 }
460
GetPixelMapFromServer(const string & uriStr,const Size & size,const string & path)461 static int32_t GetPixelMapFromServer(const string &uriStr, const Size &size, const string &path)
462 {
463 string openUriStr = uriStr + "?" + MEDIA_OPERN_KEYWORD + "=" + MEDIA_DATA_DB_THUMBNAIL + "&" +
464 MEDIA_DATA_DB_WIDTH + "=" + to_string(size.width) + "&" + MEDIA_DATA_DB_HEIGHT + "=" +
465 to_string(size.height);
466 if (IsAsciiString(path)) {
467 openUriStr += "&" + THUMBNAIL_PATH + "=" + path;
468 }
469 Uri openUri(openUriStr);
470 return UserFileClient::OpenFile(openUri, "R");
471 }
472
GetKeyFramePixelMapFromServer(const string & uriStr,const string & path,const int32_t & beginStamp,const int32_t & type)473 static int32_t GetKeyFramePixelMapFromServer(const string &uriStr, const string &path,
474 const int32_t &beginStamp, const int32_t &type)
475 {
476 string openUriStr = uriStr + "?" + MEDIA_OPERN_KEYWORD + "=" + MEDIA_DATA_DB_KEY_FRAME + "&" +
477 MEDIA_DATA_DB_BEGIN_STAMP + "=" + to_string(beginStamp) + "&" + MEDIA_DATA_DB_TYPE + "=" + to_string(type);
478 if (IsAsciiString(path)) {
479 openUriStr += "&" + THUMBNAIL_PATH + "=" + path;
480 }
481 Uri openUri(openUriStr);
482 return UserFileClient::OpenFile(openUri, "R");
483 }
484
QueryThumbnailData(napi_env env,const string & uriStr,const int & type,const string & path)485 napi_ref ThumbnailManager::QueryThumbnailData(napi_env env, const string &uriStr, const int &type, const string &path)
486 {
487 const int32_t KEY_LCD = 1;
488 const int32_t KEY_THM = 2;
489 MediaLibraryTracer tracer;
490 tracer.Start("QueryThumbnailData uri:" + uriStr);
491
492 napi_ref result = nullptr;
493 ThumbnailType thumbnailType = ThumbnailType::LCD;
494 if (type == KEY_LCD) {
495 thumbnailType = ThumbnailType::LCD;
496 } else if (type == KEY_THM) {
497 thumbnailType = ThumbnailType::THUMB;
498 }
499 UniqueFd uniqueFd(OpenThumbnail(path, thumbnailType));
500 Size size;
501 size.width = DEFAULT_THUMB_SIZE;
502 size.height = DEFAULT_THUMB_SIZE;
503 if (uniqueFd.Get() == E_ERR) {
504 uniqueFd = UniqueFd(GetArrayBufferFromServer(uriStr, path, type));
505 if (uniqueFd.Get() < 0) {
506 NAPI_ERR_LOG("queryThumbData is null, errCode is %{public}d", uniqueFd.Get());
507 return result;
508 }
509 }
510 tracer.Finish();
511 napi_ref g_ref;
512 const int32_t NUM = 1;
513 napi_create_reference(env, DecodeThumbnailData(env, uniqueFd, size), NUM, &g_ref);
514 return g_ref;
515 }
516
QueryThumbnail(const string & uriStr,const Size & size,const string & path)517 unique_ptr<PixelMap> ThumbnailManager::QueryThumbnail(const string &uriStr, const Size &size, const string &path)
518 {
519 MediaLibraryTracer tracer;
520 tracer.Start("QueryThumbnail uri:" + uriStr);
521 tracer.Start("DataShare::OpenFile");
522 ThumbnailType thumbType = GetThumbType(size.width, size.height);
523 if (MediaFileUri::GetMediaTypeFromUri(uriStr) == MediaType::MEDIA_TYPE_AUDIO &&
524 (thumbType == ThumbnailType::MTH || thumbType == ThumbnailType::YEAR)) {
525 thumbType = ThumbnailType::THUMB;
526 }
527 UniqueFd uniqueFd(OpenThumbnail(path, thumbType));
528 if (uniqueFd.Get() == E_ERR) {
529 uniqueFd = UniqueFd(GetPixelMapFromServer(uriStr, size, path));
530 if (uniqueFd.Get() < 0) {
531 NAPI_ERR_LOG("queryThumb is null, errCode is %{public}d", uniqueFd.Get());
532 return nullptr;
533 }
534 return DecodeThumbnail(uniqueFd, size);
535 }
536 tracer.Finish();
537 if (thumbType == ThumbnailType::MTH || thumbType == ThumbnailType::YEAR) {
538 return CreateThumbnailByAshmem(uniqueFd, size);
539 } else {
540 return DecodeThumbnail(uniqueFd, size);
541 }
542 }
543
QueryKeyFrameThumbnail(const string & uriStr,const int & beginStamp,const int & type,const string & path)544 unique_ptr<PixelMap> ThumbnailManager::QueryKeyFrameThumbnail(const string &uriStr, const int &beginStamp,
545 const int &type, const string &path)
546 {
547 MediaLibraryTracer tracer;
548 tracer.Start("QueryKeyFrameThumbnail uri:" + uriStr);
549
550 UniqueFd uniqueFd(OpenKeyFrameThumbnail(path, beginStamp, type));
551 Size size;
552 size.width = DEFAULT_THUMB_SIZE;
553 size.height = DEFAULT_THUMB_SIZE;
554 if (uniqueFd.Get() == E_ERR) {
555 uniqueFd = UniqueFd(GetKeyFramePixelMapFromServer(uriStr, path, beginStamp, type));
556 if (uniqueFd.Get() < 0) {
557 NAPI_ERR_LOG("queryKeyFrameThumb is null, errCode is %{public}d", uniqueFd.Get());
558 return nullptr;
559 }
560 }
561 tracer.Finish();
562 return DecodeThumbnail(uniqueFd, size);
563 }
564
DeleteRequestIdFromMap(const string & requestId)565 void ThumbnailManager::DeleteRequestIdFromMap(const string &requestId)
566 {
567 thumbRequest_.Erase(requestId);
568 }
569
RequestFastImage(const RequestSharedPtr & request)570 bool ThumbnailManager::RequestFastImage(const RequestSharedPtr &request)
571 {
572 MediaLibraryTracer tracer;
573 tracer.Start("ThumbnailManager::RequestFastImage");
574 request->SetFd(DEFAULT_FD);
575 Size fastSize;
576 if (!GetFastThumbNewSize(request->GetRequestSize(), fastSize)) {
577 return false;
578 }
579 UniqueFd uniqueFd(OpenThumbnail(request->GetPath(), GetThumbType(fastSize.width, fastSize.height)));
580 if (uniqueFd.Get() < 0) {
581 // Can not get fast image in sandbox
582 int32_t outFd = GetPixelMapFromServer(request->GetUri(), request->GetRequestSize(), request->GetPath());
583 if (outFd <= 0) {
584 NAPI_ERR_LOG("Can not get thumbnail from server, uri=%{private}s", request->GetUri().c_str());
585 request->error = E_FAIL;
586 return false;
587 }
588 request->SetFd(outFd);
589 }
590
591 ThumbnailType thumbType = GetThumbType(fastSize.width, fastSize.height);
592 PixelMapPtr pixelMap = nullptr;
593 if (request->GetFd().Get() == DEFAULT_FD &&
594 (thumbType == ThumbnailType::MTH || thumbType == ThumbnailType::YEAR)) {
595 pixelMap = CreateThumbnailByAshmem(uniqueFd, fastSize);
596 } else {
597 pixelMap = DecodeThumbnail(request->GetFd(), fastSize);
598 }
599 if (pixelMap == nullptr) {
600 request->error = E_FAIL;
601 return false;
602 }
603 request->SetFastPixelMap(move(pixelMap));
604 return true;
605 }
606
DealWithFastRequest(const RequestSharedPtr & request)607 void ThumbnailManager::DealWithFastRequest(const RequestSharedPtr &request)
608 {
609 MediaLibraryTracer tracer;
610 tracer.Start("ThumbnailManager::DealWithFastRequest");
611
612 if (request == nullptr) {
613 return;
614 }
615
616 if (!RequestFastImage(request) && request->error != E_FAIL) {
617 // when local pixelmap not exit, must add QualityThread
618 AddQualityPhotoRequest(request);
619 return;
620 }
621
622 // callback
623 NotifyImage(request);
624 }
625
DealWithQualityRequest(const RequestSharedPtr & request)626 void ThumbnailManager::DealWithQualityRequest(const RequestSharedPtr &request)
627 {
628 MediaLibraryTracer tracer;
629 tracer.Start("ThumbnailManager::DealWithQualityRequest");
630
631 unique_ptr<PixelMap> pixelMapPtr = nullptr;
632 if (request->GetFd().Get() > 0) {
633 pixelMapPtr = DecodeThumbnail(request->GetFd(), request->GetRequestSize());
634 } else {
635 pixelMapPtr = QueryThumbnail(request->GetUri(), request->GetRequestSize(), request->GetPath());
636 }
637
638 if (pixelMapPtr == nullptr) {
639 NAPI_ERR_LOG("Can not get pixelMap");
640 request->error = E_FAIL;
641 }
642 request->SetPixelMap(move(pixelMapPtr));
643
644 // callback
645 NotifyImage(request);
646 }
647
ImageWorker(int num)648 void ThumbnailManager::ImageWorker(int num)
649 {
650 SetThreadName("ImageWorker", num);
651 while (true) {
652 if (!isThreadRunning_) {
653 return;
654 }
655 if (!fastQueue_.Empty()) {
656 RequestSharedPtr request;
657 if (fastQueue_.Pop(request) && request->NeedContinue()) {
658 DealWithFastRequest(request);
659 }
660 } else if (!qualityQueue_.Empty()) {
661 RequestSharedPtr request;
662 if (qualityQueue_.Pop(request) && request->NeedContinue()) {
663 DealWithQualityRequest(request);
664 }
665 } else {
666 std::unique_lock<std::mutex> lock(queueLock_);
667 queueCv_.wait(lock, [this]() {
668 return !isThreadRunning_ || !(qualityQueue_.Empty() && fastQueue_.Empty());
669 });
670 }
671 }
672 }
673
HandlePixelCallback(const RequestSharedPtr & request)674 static void HandlePixelCallback(const RequestSharedPtr &request)
675 {
676 napi_env env = request->callback_.env_;
677 napi_value jsCallback = nullptr;
678 napi_status status = napi_get_reference_value(env, request->callback_.callBackRef_, &jsCallback);
679 if (status != napi_ok) {
680 NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
681 return;
682 }
683
684 napi_value retVal = nullptr;
685 napi_value result[ARGS_TWO];
686 if (request->GetStatus() == ThumbnailStatus::THUMB_REMOVE) {
687 return;
688 }
689
690 if (request->error == E_FAIL) {
691 int32_t errorNum = MediaLibraryNapiUtils::TransErrorCode("requestPhoto", request->error);
692 MediaLibraryNapiUtils::CreateNapiErrorObject(env, result[PARAM0], errorNum,
693 "Failed to request Photo");
694 } else {
695 result[PARAM0] = nullptr;
696 }
697 if (request->GetStatus() == ThumbnailStatus::THUMB_FAST) {
698 result[PARAM1] = Media::PixelMapNapi::CreatePixelMap(env,
699 shared_ptr<PixelMap>(request->GetFastPixelMap()));
700 } else {
701 result[PARAM1] = Media::PixelMapNapi::CreatePixelMap(env,
702 shared_ptr<PixelMap>(request->GetPixelMap()));
703 }
704
705 status = napi_call_function(env, nullptr, jsCallback, ARGS_TWO, result, &retVal);
706 if (status != napi_ok) {
707 NAPI_ERR_LOG("CallJs napi_call_function fail, status: %{public}d", status);
708 return;
709 }
710 }
711
UvJsExecute(uv_work_t * work)712 static void UvJsExecute(uv_work_t *work)
713 {
714 // js thread
715 if (work == nullptr) {
716 return;
717 }
718
719 ThumnailUv *uvMsg = reinterpret_cast<ThumnailUv *>(work->data);
720 if (uvMsg == nullptr) {
721 delete work;
722 return;
723 }
724 if (uvMsg->request_ == nullptr) {
725 delete uvMsg;
726 delete work;
727 return;
728 }
729 do {
730 napi_env env = uvMsg->request_->callback_.env_;
731 if (!uvMsg->request_->NeedContinue()) {
732 break;
733 }
734 NapiScopeHandler scopeHandler(env);
735 if (!scopeHandler.IsValid()) {
736 break;
737 }
738 HandlePixelCallback(uvMsg->request_);
739 } while (0);
740 if (uvMsg->manager_ == nullptr) {
741 delete uvMsg;
742 delete work;
743 return;
744 }
745 if (uvMsg->request_->GetStatus() == ThumbnailStatus::THUMB_FAST &&
746 NeedQualityThumb(uvMsg->request_->GetRequestSize(), uvMsg->request_->requestPhotoType)) {
747 uvMsg->manager_->AddQualityPhotoRequest(uvMsg->request_);
748 } else {
749 uvMsg->manager_->DeleteRequestIdFromMap(uvMsg->request_->GetUUID());
750 uvMsg->request_->ReleaseCallbackRef();
751 }
752
753 delete uvMsg;
754 delete work;
755 }
756
NotifyImage(const RequestSharedPtr & request)757 void ThumbnailManager::NotifyImage(const RequestSharedPtr &request)
758 {
759 MediaLibraryTracer tracer;
760 tracer.Start("ThumbnailManager::NotifyImage");
761
762 if (!request->NeedContinue()) {
763 DeleteRequestIdFromMap(request->GetUUID());
764 return;
765 }
766
767 uv_loop_s *loop = nullptr;
768 napi_get_uv_event_loop(request->callback_.env_, &loop);
769 if (loop == nullptr) {
770 DeleteRequestIdFromMap(request->GetUUID());
771 return;
772 }
773
774 uv_work_t *work = new (nothrow) uv_work_t;
775 if (work == nullptr) {
776 DeleteRequestIdFromMap(request->GetUUID());
777 return;
778 }
779
780 ThumnailUv *msg = new (nothrow) ThumnailUv(request, this);
781 if (msg == nullptr) {
782 delete work;
783 DeleteRequestIdFromMap(request->GetUUID());
784 return;
785 }
786
787 work->data = reinterpret_cast<void *>(msg);
788 int ret = uv_queue_work(loop, work, [](uv_work_t *w) {}, [](uv_work_t *w, int s) {
789 UvJsExecute(w);
790 });
791 if (ret != 0) {
792 NAPI_ERR_LOG("Failed to execute libuv work queue, ret: %{public}d", ret);
793 delete msg;
794 delete work;
795 return;
796 }
797 return;
798 }
799 }
800 }
801