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 "thumbnail_source_loading.h"
17
18 #include <charconv>
19 #include <fcntl.h>
20
21 #include "dfx_manager.h"
22 #include "dfx_utils.h"
23 #include "directory_ex.h"
24 #include "image_source.h"
25 #include "media_exif.h"
26 #include "media_file_utils.h"
27 #include "media_image_framework_utils.h"
28 #include "medialibrary_tracer.h"
29 #include "post_proc.h"
30 #include "thumbnail_image_framework_utils.h"
31 #include "thumbnail_utils.h"
32 #include "thumbnail_const.h"
33
34 using namespace std;
35
36 namespace OHOS {
37 namespace Media {
38
39 const std::string LOCAL_MEDIA_PATH = "/storage/media/local/files/";
40
41 const std::unordered_map<SourceState, SourceState> SourceLoader::LOCAL_SOURCE_LOADING_STATES = {
42 { SourceState::BEGIN, SourceState::LOCAL_ORIGIN },
43 { SourceState::LOCAL_ORIGIN, SourceState::FINISH },
44 };
45
46 const std::unordered_map<SourceState, SourceState> SourceLoader::LOCAL_THUMB_SOURCE_LOADING_STATES = {
47 { SourceState::BEGIN, SourceState::LOCAL_THUMB },
48 { SourceState::LOCAL_THUMB, SourceState::LOCAL_LCD },
49 { SourceState::LOCAL_LCD, SourceState::LOCAL_ORIGIN },
50 { SourceState::LOCAL_ORIGIN, SourceState::FINISH },
51 };
52
53 const std::unordered_map<SourceState, SourceState> SourceLoader::CLOUD_SOURCE_LOADING_STATES = {
54 { SourceState::BEGIN, SourceState::CLOUD_THUMB },
55 { SourceState::CLOUD_THUMB, SourceState::CLOUD_LCD },
56 { SourceState::CLOUD_LCD, SourceState::CLOUD_ORIGIN },
57 { SourceState::CLOUD_ORIGIN, SourceState::FINISH },
58 };
59
60 const std::unordered_map<SourceState, SourceState> SourceLoader::CLOUD_ORIGIN_SOURCE_LOADING_STATES = {
61 { SourceState::BEGIN, SourceState::CLOUD_ORIGIN },
62 { SourceState::CLOUD_ORIGIN, SourceState::FINISH },
63 };
64
65 const std::unordered_map<SourceState, SourceState> SourceLoader::ALL_SOURCE_LOADING_STATES = {
66 { SourceState::BEGIN, SourceState::LOCAL_THUMB },
67 { SourceState::LOCAL_THUMB, SourceState::LOCAL_LCD },
68 { SourceState::LOCAL_LCD, SourceState::LOCAL_ORIGIN },
69 { SourceState::LOCAL_ORIGIN, SourceState::CLOUD_LCD },
70 { SourceState::CLOUD_LCD, SourceState::CLOUD_ORIGIN },
71 { SourceState::CLOUD_ORIGIN, SourceState::FINISH },
72 };
73
74 // For cloud video, generating thumbnail foreground in LOCAL_ORIGIN state may download its source video.
75 const std::unordered_map<SourceState, SourceState> SourceLoader::ALL_SOURCE_LOADING_CLOUD_VIDEO_STATES = {
76 { SourceState::BEGIN, SourceState::LOCAL_THUMB },
77 { SourceState::LOCAL_THUMB, SourceState::LOCAL_LCD },
78 { SourceState::LOCAL_LCD, SourceState::CLOUD_LCD },
79 { SourceState::CLOUD_LCD, SourceState::CLOUD_ORIGIN },
80 { SourceState::CLOUD_ORIGIN, SourceState::FINISH },
81 };
82
83 const std::unordered_map<SourceState, SourceState> SourceLoader::CLOUD_LCD_SOURCE_LOADING_STATES = {
84 { SourceState::BEGIN, SourceState::CLOUD_LCD },
85 { SourceState::CLOUD_LCD, SourceState::CLOUD_ORIGIN },
86 { SourceState::CLOUD_ORIGIN, SourceState::FINISH },
87 };
88
89 const std::unordered_map<SourceState, SourceState> SourceLoader::LOCAL_LCD_SOURCE_LOADING_STATES = {
90 { SourceState::BEGIN, SourceState::LOCAL_LCD },
91 { SourceState::LOCAL_LCD, SourceState::LOCAL_ORIGIN },
92 { SourceState::LOCAL_ORIGIN, SourceState::FINISH },
93 };
94
95 const std::unordered_map<SourceState, SourceState> SourceLoader::UPGRADE_SOURCE_LOADING_STATES = {
96 { SourceState::BEGIN, SourceState::LOCAL_ORIGIN },
97 { SourceState::LOCAL_ORIGIN, SourceState::CLOUD_ORIGIN },
98 { SourceState::CLOUD_ORIGIN, SourceState::FINISH },
99 };
100
101 const std::unordered_map<SourceState, SourceState> SourceLoader::UPGRADE_VIDEO_SOURCE_LOADING_STATES = {
102 { SourceState::BEGIN, SourceState::LOCAL_ORIGIN },
103 { SourceState::LOCAL_ORIGIN, SourceState::CLOUD_LCD },
104 { SourceState::CLOUD_LCD, SourceState::FINISH },
105 };
106
GetLocalThumbnailPath(const std::string & path,const std::string & key)107 std::string GetLocalThumbnailPath(const std::string &path, const std::string &key)
108 {
109 if (path.length() < ROOT_MEDIA_DIR.length()) {
110 return "";
111 }
112 std::string suffix = (key == "") ? "" : "/" + key + ".jpg";
113 return LOCAL_MEDIA_PATH + ((key == "") ? "" : ".thumbs/") + path.substr(ROOT_MEDIA_DIR.length()) + suffix;
114 }
115
GetLocalKeyFrameThumbnailPath(const std::string & path,const std::string & key,const std::string & timeStamp)116 std::string GetLocalKeyFrameThumbnailPath(const std::string &path, const std::string &key, const std::string &timeStamp)
117 {
118 if (path.length() < ROOT_MEDIA_DIR.length()) {
119 return "";
120 }
121 std::string suffix = (key == "") ? "" : "/" + key + ".jpg";
122 return LOCAL_MEDIA_PATH + ((key == "") ? "" : ".thumbs/") + path.substr(ROOT_MEDIA_DIR.length()) +
123 "/beginTimeStamp" + timeStamp + "/" + suffix;
124 }
125
GetLcdExPath(const std::string & path)126 std::string GetLcdExPath(const std::string &path)
127 {
128 if (path.length() < ROOT_MEDIA_DIR.length()) {
129 return "";
130 }
131 std::string suffix = "/THM_EX/" + THUMBNAIL_LCD_SUFFIX + ".jpg";
132 return ROOT_MEDIA_DIR + ".thumbs/" + path.substr(ROOT_MEDIA_DIR.length()) + suffix;
133 }
134
IsLocalSourceAvailable(const std::string & path)135 bool IsLocalSourceAvailable(const std::string& path)
136 {
137 char tmpPath[PATH_MAX] = { 0 };
138 if (realpath(path.c_str(), tmpPath) == nullptr) {
139 // it's alright if source loading fails here, just move on to next source
140 MEDIA_ERR_LOG("SourceLoader path to realPath is nullptr: errno: %{public}d, %{public}s",
141 errno, DfxUtils::GetSafePath(path).c_str());
142 return false;
143 }
144
145 FILE* filePtr = fopen(tmpPath, "rb");
146 if (filePtr == nullptr) {
147 MEDIA_ERR_LOG("SourceLoader open local file fail: errno: %{public}d, %{public}s",
148 errno, DfxUtils::GetSafePath(path).c_str());
149 return false;
150 }
151 if (fclose(filePtr) != E_OK) {
152 MEDIA_ERR_LOG("SourceLoader close filePtr fail: errno: %{public}d, %{public}s",
153 errno, DfxUtils::GetSafePath(path).c_str());
154 return false;
155 }
156 return true;
157 }
158
IsCloudSourceAvailable(const std::string & path)159 bool IsCloudSourceAvailable(const std::string& path)
160 {
161 string absFilePath;
162 if (!PathToRealPath(path, absFilePath)) {
163 MEDIA_ERR_LOG("Not real file path: errno: %{public}d, %{public}s", errno, DfxUtils::GetSafePath(path).c_str());
164 return false;
165 }
166
167 int fd = open(absFilePath.c_str(), O_RDONLY);
168 if (fd < 0) {
169 MEDIA_ERR_LOG("open cloud file fail: errno: %{public}d, %{public}s",
170 errno, DfxUtils::GetSafePath(path).c_str());
171 return false;
172 }
173 close(fd);
174 return true;
175 }
176
NeedAutoResize(const Size & size)177 bool NeedAutoResize(const Size &size)
178 {
179 // Only small thumbnails need to be scaled after decoding, others should resized while decoding.
180 return size.width > SHORT_SIDE_THRESHOLD && size.height > SHORT_SIDE_THRESHOLD;
181 }
182
GenDecodeOpts(const Size & sourceSize,const Size & targetSize,DecodeOptions & decodeOpts)183 bool GenDecodeOpts(const Size &sourceSize, const Size &targetSize, DecodeOptions &decodeOpts)
184 {
185 if (targetSize.width == 0) {
186 MEDIA_ERR_LOG("Failed to generate decodeOpts, scale size contains zero");
187 return false;
188 }
189 decodeOpts.desiredPixelFormat = PixelFormat::RGBA_8888;
190 if (NeedAutoResize(targetSize)) {
191 decodeOpts.desiredSize = targetSize;
192 return true;
193 }
194
195 int32_t decodeScale = 1;
196 int32_t scaleFactor = sourceSize.width / targetSize.width;
197 while (scaleFactor /= DECODE_SCALE_BASE) {
198 decodeScale *= DECODE_SCALE_BASE;
199 }
200 decodeOpts.desiredSize = {
201 std::ceil(sourceSize.width / decodeScale),
202 std::ceil(sourceSize.height / decodeScale),
203 };
204 return true;
205 }
206
ConvertDecodeSize(ThumbnailData & data,const Size & sourceSize,Size & desiredSize)207 Size ConvertDecodeSize(ThumbnailData &data, const Size &sourceSize, Size &desiredSize)
208 {
209 int width = sourceSize.width;
210 int height = sourceSize.height;
211 if (!ThumbnailUtils::ResizeThumb(width, height)) {
212 MEDIA_ERR_LOG("ResizeThumb failed");
213 return {0, 0};
214 }
215 Size thumbDesiredSize = {width, height};
216 data.thumbDesiredSize = thumbDesiredSize;
217 float desiredScale = static_cast<float>(thumbDesiredSize.height) / static_cast<float>(thumbDesiredSize.width);
218 float sourceScale = static_cast<float>(sourceSize.height) / static_cast<float>(sourceSize.width);
219 float scale = 1.0f;
220 Size thumbDecodeSize = thumbDesiredSize;
221 if (sourceScale - desiredScale <= EPSILON) {
222 scale = min((float)thumbDesiredSize.height / sourceSize.height, 1.0f);
223 thumbDecodeSize.width = static_cast<int32_t> (scale * sourceSize.width);
224 } else {
225 scale = min((float)thumbDesiredSize.width / sourceSize.width, 1.0f);
226 thumbDecodeSize.height = static_cast<int32_t> (scale * sourceSize.height);
227 }
228
229 width = sourceSize.width;
230 height = sourceSize.height;
231 if (!ThumbnailUtils::ResizeLcd(width, height)) {
232 MEDIA_ERR_LOG("ResizeLcd failed");
233 return {0, 0};
234 }
235 Size lcdDesiredSize = {width, height};
236 data.lcdDesiredSize = lcdDesiredSize;
237
238 int lcdMinSide = std::min(lcdDesiredSize.width, lcdDesiredSize.height);
239 int thumbMinSide = std::min(thumbDesiredSize.width, thumbDesiredSize.height);
240 Size lcdDecodeSize = lcdMinSide < thumbMinSide ? thumbDecodeSize : lcdDesiredSize;
241
242 if (data.loaderOpts.decodeInThumbSize) {
243 desiredSize = thumbDesiredSize;
244 return thumbDecodeSize;
245 } else if (data.needResizeLcd) {
246 desiredSize = lcdDesiredSize;
247 return lcdDecodeSize;
248 } else {
249 desiredSize = lcdDesiredSize;
250 return lcdDesiredSize;
251 }
252 }
253
ParseDesiredMinSide(const ThumbnailType & type)254 int32_t ParseDesiredMinSide(const ThumbnailType &type)
255 {
256 switch (type) {
257 case ThumbnailType::THUMB:
258 return SHORT_SIDE_THRESHOLD;
259 break;
260 case ThumbnailType::MTH_ASTC:
261 return MONTH_SHORT_SIDE_THRESHOLD;
262 break;
263 default:
264 break;
265 }
266 return std::numeric_limits<int32_t>::max();
267 }
268
SwitchToNextState(ThumbnailData & data,SourceState & state)269 void SwitchToNextState(ThumbnailData &data, SourceState &state)
270 {
271 if (state == SourceState::FINISH) {
272 MEDIA_INFO_LOG("SwitchToNextState current state is FINISH.");
273 return;
274 }
275 auto iter = data.loaderOpts.loadingStates.find(state);
276 if (iter == data.loaderOpts.loadingStates.end()) {
277 MEDIA_WARN_LOG("SwitchToNextState find next state error, current state:%{public}d",
278 static_cast<int32_t>(state));
279 return;
280 }
281 state = iter->second;
282 }
283
LoadImageSource(const std::string & path,uint32_t & err)284 unique_ptr<ImageSource> LoadImageSource(const std::string &path, uint32_t &err)
285 {
286 MediaLibraryTracer tracer;
287 tracer.Start("LoadImageSource");
288
289 SourceOptions opts;
290 unique_ptr<ImageSource> imageSource = ImageSource::CreateImageSource(path, opts, err);
291 if (err != E_OK || !imageSource) {
292 MEDIA_ERR_LOG("Failed to LoadImageSource, pixelmap path: %{public}s exists: %{public}d",
293 path.c_str(), MediaFileUtils::IsFileExists(path));
294 DfxManager::GetInstance()->HandleThumbnailError(path, DfxType::IMAGE_SOURCE_CREATE, err);
295 return imageSource;
296 }
297 return imageSource;
298 }
299
SafeStoll(const std::string & str)300 int64_t SafeStoll(const std::string &str)
301 {
302 int64_t value;
303 auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
304
305 if (ec != std::errc()) {
306 MEDIA_ERR_LOG("Failed to convert string to int64_t: %{public}s", str.c_str());
307 return 0;
308 }
309 return value;
310 }
311
CreateVideoFramePixelMap()312 bool SourceLoader::CreateVideoFramePixelMap()
313 {
314 MediaLibraryTracer tracer;
315 tracer.Start("CreateVideoFramePixelMap");
316 int64_t timeStamp = AV_FRAME_TIME;
317 if (!data_.tracks.empty()) {
318 timeStamp = SafeStoll(data_.timeStamp) * MS_TRANSFER_US;
319 }
320 if (state_ == SourceState::CLOUD_ORIGIN && timeStamp != AV_FRAME_TIME) {
321 MEDIA_ERR_LOG("Avoid reading specific frame from cloud video, path %{public}s",
322 DfxUtils::GetSafePath(data_.path).c_str());
323 return false;
324 }
325 return ThumbnailUtils::LoadVideoFrame(data_, desiredSize_, timeStamp);
326 }
327
SetCurrentStateFunction()328 void SourceLoader::SetCurrentStateFunction()
329 {
330 StateFunc stateFunc = STATE_FUNC_MAP.at(state_);
331 GetSourcePath = stateFunc.GetSourcePath;
332 IsSizeLargeEnough = stateFunc.IsSizeLargeEnough;
333 }
334
GeneratePictureSource(std::unique_ptr<ImageSource> & imageSource,const Size & targetSize)335 bool SourceLoader::GeneratePictureSource(std::unique_ptr<ImageSource> &imageSource, const Size &targetSize)
336 {
337 DecodingOptionsForPicture pictureOpts;
338 pictureOpts.desireAuxiliaryPictures = {AuxiliaryPictureType::GAINMAP};
339 uint32_t errorCode = ERR_OK;
340 auto picturePtr = imageSource->CreatePicture(pictureOpts, errorCode);
341 if (errorCode != E_OK || picturePtr == nullptr) {
342 MEDIA_ERR_LOG("Failed to GeneratePictureSource, CreatePicture failed, err: %{public}d, path %{public}s",
343 errorCode, DfxUtils::GetSafePath(data_.path).c_str());
344 return false;
345 }
346 std::shared_ptr<Picture> picture = std::move(picturePtr);
347 auto pixelMap = picture->GetMainPixel();
348 auto gainMap = picture->GetGainmapPixelMap();
349 if (pixelMap == nullptr) {
350 MEDIA_ERR_LOG("Failed to GeneratePictureSource, main pixelMap is nullptr, path %{public}s",
351 DfxUtils::GetSafePath(data_.path).c_str());
352 return false;
353 }
354 if (gainMap == nullptr) {
355 MEDIA_ERR_LOG("Failed to GeneratePictureSource, gainmap is nullptr, path %{public}s",
356 DfxUtils::GetSafePath(data_.path).c_str());
357 return false;
358 }
359 if (pixelMap->GetWidth() * pixelMap->GetHeight() == 0) {
360 MEDIA_ERR_LOG("Failed to GeneratePictureSource, pixelMap size invalid, width: %{public}d, height: %{public}d,"
361 "path %{public}s", pixelMap->GetWidth(), pixelMap->GetHeight(), DfxUtils::GetSafePath(data_.path).c_str());
362 return false;
363 }
364 float widthScale = (1.0f * targetSize.width) / pixelMap->GetWidth();
365 float heightScale = (1.0f * targetSize.height) / pixelMap->GetHeight();
366 pixelMap->resize(widthScale, heightScale);
367 gainMap->resize(widthScale, heightScale);
368 data_.source.SetPicture(picture);
369 return true;
370 }
371
GeneratePixelMapSource(std::unique_ptr<ImageSource> & imageSource,const Size & sourceSize,const Size & targetSize)372 bool SourceLoader::GeneratePixelMapSource(std::unique_ptr<ImageSource> &imageSource, const Size &sourceSize,
373 const Size &targetSize)
374 {
375 DecodeOptions decodeOpts;
376 uint32_t err = ERR_OK;
377 if (!GenDecodeOpts(sourceSize, targetSize, decodeOpts)) {
378 MEDIA_ERR_LOG("SourceLoader failed to generate decodeOpts, pixelmap path %{public}s",
379 DfxUtils::GetSafePath(data_.path).c_str());
380 return false;
381 }
382 std::shared_ptr<PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOpts, err);
383 if (err != E_OK || pixelMap == nullptr) {
384 MEDIA_ERR_LOG("SourceLoader failed to CreatePixelMap, err: %{public}d, pixelmap path %{public}s",
385 err, DfxUtils::GetSafePath(data_.path).c_str());
386 DfxManager::GetInstance()->HandleThumbnailError(data_.path, DfxType::IMAGE_SOURCE_CREATE_PIXELMAP, err);
387 return false;
388 }
389 if (!NeedAutoResize(targetSize) && !ThumbnailUtils::ScaleTargetPixelMap(pixelMap, targetSize,
390 Media::AntiAliasingOption::HIGH)) {
391 MEDIA_ERR_LOG("SourceLoader fail to scaleTarget, path %{public}s", DfxUtils::GetSafePath(data_.path).c_str());
392 return false;
393 }
394 data_.source.SetPixelMap(pixelMap);
395 return true;
396 }
397
IsLoadingOriginalSource()398 bool SourceLoader::IsLoadingOriginalSource()
399 {
400 return state_ == SourceState::LOCAL_ORIGIN || state_ == SourceState::CLOUD_ORIGIN;
401 }
402
CreateImagePixelMap(const std::string & sourcePath)403 bool SourceLoader::CreateImagePixelMap(const std::string &sourcePath)
404 {
405 MediaLibraryTracer tracer;
406 tracer.Start("SourceLoader CreateImagePixelMap");
407 uint32_t err = 0;
408 std::unique_ptr<ImageSource> imageSource = LoadImageSource(sourcePath, err);
409 if (err != E_OK || imageSource == nullptr) {
410 MEDIA_ERR_LOG("SourceLoader LoadImageSource error:%{public}d, errorno:%{public}d", err, errno);
411 return false;
412 }
413
414 ImageInfo imageInfo;
415 if (!IsSizeAcceptable(imageSource, imageInfo)) {
416 MEDIA_ERR_LOG("SourceLoader CreateImagePixelMap size not acceptable, status:%{public}s, path:%{public}s",
417 STATE_NAME_MAP.at(state_).c_str(), DfxUtils::GetSafePath(data_.path).c_str());
418 return false;
419 }
420
421 Size targetSize = ConvertDecodeSize(data_, imageInfo.size, desiredSize_);
422 Size sourceSize = imageInfo.size;
423
424 // When encode picture, if mainPixel width or height is odd, hardware encode would fail.
425 // For the safety of encode process, only those of even desiredSize are allowed to generate througth picture.
426 bool shouldGeneratePicture = data_.loaderOpts.isHdr && imageSource->IsHdrImage();
427 bool isGenerateSucceed = shouldGeneratePicture ?
428 GeneratePictureSource(imageSource, targetSize) : GeneratePixelMapSource(imageSource, sourceSize, targetSize);
429 if (!isGenerateSucceed && shouldGeneratePicture) {
430 MEDIA_ERR_LOG("SourceLoader generate picture source failed, generate pixelmap instead, path:%{public}s",
431 DfxUtils::GetSafePath(data_.path).c_str());
432 std::unique_ptr<ImageSource> retryImageSource = LoadImageSource(sourcePath, err);
433 if (err != E_OK || retryImageSource == nullptr) {
434 MEDIA_ERR_LOG("LoadImageSource retryImageSource error:%{public}d, errorno:%{public}d", err, errno);
435 return false;
436 }
437 isGenerateSucceed = GeneratePixelMapSource(retryImageSource, sourceSize, targetSize);
438 }
439 if (!isGenerateSucceed) {
440 MEDIA_ERR_LOG("ImagePixelMap generate failed, path:%{public}s", DfxUtils::GetSafePath(data_.path).c_str());
441 return false;
442 }
443 tracer.Finish();
444
445 if (data_.isLocalFile && data_.exifRotate == 0 && IsLoadingOriginalSource()) {
446 data_.exifRotate = static_cast<int32_t>(ExifRotateType::TOP_LEFT);
447 CHECK_AND_WARN_LOG(MediaImageFrameWorkUtils::GetExifRotate(imageSource, data_.exifRotate) == E_OK,
448 "SourceLoader Failed to get exif rotate, path: %{public}s", DfxUtils::GetSafePath(data_.path).c_str());
449 }
450
451 DfxManager::GetInstance()->HandleHighMemoryThumbnail(data_.path, MEDIA_TYPE_IMAGE, imageInfo.size.width,
452 imageInfo.size.height);
453 return true;
454 }
455
CreateSourcePixelMap()456 bool SourceLoader::CreateSourcePixelMap()
457 {
458 if (GetSourcePath == nullptr) {
459 MEDIA_ERR_LOG("GetSourcePath is nullptr.");
460 return false;
461 }
462
463 std::string sourcePath = GetSourcePath(data_, error_);
464 if (sourcePath.empty()) {
465 MEDIA_ERR_LOG("SourceLoader source path unavailable, status:%{public}s, path:%{public}s",
466 STATE_NAME_MAP.at(state_).c_str(), DfxUtils::GetSafePath(data_.path).c_str());
467 return false;
468 }
469
470 CHECK_AND_RETURN_RET(!((state_ == SourceState::LOCAL_ORIGIN || state_ == SourceState::CLOUD_ORIGIN) &&
471 data_.mediaType == MEDIA_TYPE_VIDEO), CreateVideoFramePixelMap());
472
473 if (!CreateImagePixelMap(sourcePath)) {
474 MEDIA_ERR_LOG("SourceLoader fail to create image pixel map, status:%{public}s, path:%{public}s",
475 STATE_NAME_MAP.at(state_).c_str(), DfxUtils::GetSafePath(data_.path).c_str());
476 return false;
477 }
478 return true;
479 }
480
CreateSourceFromOriginalPhotoPicture()481 bool SourceLoader::CreateSourceFromOriginalPhotoPicture()
482 {
483 CHECK_AND_RETURN_RET_LOG(data_.originalPhotoPicture != nullptr, false, "OriginalPhotoPicture is nullptr");
484 if (data_.loaderOpts.decodeInThumbSize ||
485 !ThumbnailImageFrameWorkUtils::IsSupportCopyPixelMap(data_.originalPhotoPicture->GetMainPixel())) {
486 data_.originalPhotoPicture = nullptr;
487 return false;
488 }
489
490 int32_t exifRotate = 0;
491 int32_t err = MediaImageFrameWorkUtils::GetExifRotate(data_.originalPhotoPicture, exifRotate);
492 CHECK_AND_WARN_LOG(err == E_OK, "SourceLoader Failed to get picture orientation, path: %{public}s",
493 DfxUtils::GetSafePath(data_.path).c_str());
494
495 bool isCreateSource = false;
496 if (data_.originalPhotoPicture->GetGainmapPixelMap() == nullptr) {
497 isCreateSource = CreateSourceWithOriginalPictureMainPixel();
498 } else {
499 isCreateSource = CreateSourceWithWholeOriginalPicture();
500 }
501 CHECK_AND_RETURN_RET_LOG(isCreateSource, false, "Create source failed");
502 CHECK_AND_EXECUTE(!(data_.exifRotate == 0 && exifRotate > 0), data_.exifRotate = exifRotate);
503 data_.lastLoadSource = SourceState::LOCAL_ORIGIN;
504 return true;
505 }
506
CreateSourceWithWholeOriginalPicture()507 bool SourceLoader::CreateSourceWithWholeOriginalPicture()
508 {
509 CHECK_AND_RETURN_RET_LOG(data_.originalPhotoPicture != nullptr, false, "OriginalPhotoPicture is nullptr");
510 MediaLibraryTracer tracer;
511 tracer.Start("CreateSourceWithWholeOriginalPicture");
512
513 std::shared_ptr<Picture> picture = ThumbnailImageFrameWorkUtils::CopyPictureSource(data_.originalPhotoPicture);
514 data_.originalPhotoPicture = nullptr;
515 CHECK_AND_RETURN_RET_LOG(picture != nullptr, false, "CopyPictureSource failed");
516 auto pixelMap = picture->GetMainPixel();
517 auto gainMap = picture->GetGainmapPixelMap();
518 CHECK_AND_RETURN_RET_LOG(pixelMap != nullptr && gainMap != nullptr, false,
519 "PixelMap or gainMap is nullptr");
520 CHECK_AND_RETURN_RET_LOG(pixelMap->GetWidth() * pixelMap->GetHeight() != 0, false,
521 "Picture is invalid");
522
523 Size originSize = { .width = pixelMap->GetWidth(),
524 .height = pixelMap->GetHeight() };
525 Size targetSize = ConvertDecodeSize(data_, originSize, desiredSize_);
526
527 float widthScale = (1.0f * targetSize.width) / pixelMap->GetWidth();
528 float heightScale = (1.0f * targetSize.height) / pixelMap->GetHeight();
529 pixelMap->resize(widthScale, heightScale);
530 gainMap->resize(widthScale, heightScale);
531 data_.source.SetPicture(picture);
532
533 DfxManager::GetInstance()->HandleHighMemoryThumbnail(data_.path, data_.mediaType, originSize.width,
534 originSize.height);
535 return true;
536 }
537
CreateSourceWithOriginalPictureMainPixel()538 bool SourceLoader::CreateSourceWithOriginalPictureMainPixel()
539 {
540 CHECK_AND_RETURN_RET_LOG(data_.originalPhotoPicture != nullptr, false, "OriginalPhotoPicture is nullptr");
541 MediaLibraryTracer tracer;
542 tracer.Start("CreateSourceWithOriginalPictureMainPixel");
543 auto mainPixel = data_.originalPhotoPicture->GetMainPixel();
544 CHECK_AND_RETURN_RET_LOG(mainPixel != nullptr, false, "Main pixel is nullptr");
545 MEDIA_INFO_LOG("Main pixelMap format:%{public}d isHdr:%{public}d allocatorType:%{public}d, "
546 "size: %{public}d * %{public}d", mainPixel->GetPixelFormat(), mainPixel->IsHdr(),
547 mainPixel->GetAllocatorType(), mainPixel->GetWidth(), mainPixel->GetHeight());
548
549 std::shared_ptr<PixelMap> pixelMap = ThumbnailImageFrameWorkUtils::CopyPixelMapSource(mainPixel);
550 mainPixel = nullptr;
551 data_.originalPhotoPicture = nullptr;
552 CHECK_AND_RETURN_RET_LOG(pixelMap != nullptr, false, "Copy main pixel failed");
553 CHECK_AND_RETURN_RET_LOG(pixelMap->GetWidth() * pixelMap->GetHeight() != 0, false,
554 "PixelMap is invalid");
555
556 Size originSize = { .width = pixelMap->GetWidth(),
557 .height = pixelMap->GetHeight() };
558 Size targetSize = ConvertDecodeSize(data_, originSize, desiredSize_);
559
560 float widthScale = (1.0f * targetSize.width) / pixelMap->GetWidth();
561 float heightScale = (1.0f * targetSize.height) / pixelMap->GetHeight();
562 pixelMap->resize(widthScale, heightScale);
563 data_.source.SetPixelMap(pixelMap);
564
565 DfxManager::GetInstance()->HandleHighMemoryThumbnail(data_.path, data_.mediaType, originSize.width,
566 originSize.height);
567 return true;
568 }
569
RunLoading()570 bool SourceLoader::RunLoading()
571 {
572 if (data_.originalPhotoPicture != nullptr && CreateSourceFromOriginalPhotoPicture()) {
573 return true;
574 }
575
576 if (data_.loaderOpts.loadingStates.empty()) {
577 MEDIA_ERR_LOG("source loading run loading failed, the given states is empty");
578 return false;
579 }
580 state_ = SourceState::BEGIN;
581 data_.source.ClearAllSource();
582 SetCurrentStateFunction();
583
584 // always check state not final after every state switch
585 while (!IsFinal()) {
586 SwitchToNextState(data_, state_);
587 MEDIA_DEBUG_LOG("SourceLoader new cycle status:%{public}s", STATE_NAME_MAP.at(state_).c_str());
588 if (IsFinal()) {
589 break;
590 }
591 SetCurrentStateFunction();
592 do {
593 if (state_ < SourceState::CLOUD_THUMB) {
594 data_.stats.sourceType = LoadSourceType::LOCAL_PHOTO;
595 } else if (state_ >= SourceState::CLOUD_THUMB && state_ <= SourceState::CLOUD_ORIGIN) {
596 data_.stats.sourceType = static_cast<LoadSourceType>(state_);
597 }
598
599 if (!CreateSourcePixelMap()) {
600 MEDIA_ERR_LOG("SourceLoader fail to create source pixel map, status:%{public}s, path:%{public}s",
601 STATE_NAME_MAP.at(state_).c_str(), DfxUtils::GetSafePath(data_.path).c_str());
602 break;
603 }
604 state_ = SourceState::FINISH;
605 DfxManager::GetInstance()->HandleThumbnailGeneration(data_.stats);
606 } while (0);
607 };
608 if (state_ == SourceState::ERROR) {
609 data_.source.ClearAllSource();
610 }
611 if (data_.source.IsEmptySource()) {
612 MEDIA_ERR_LOG("SourceLoader source is nullptr, status:%{public}s, path:%{public}s",
613 STATE_NAME_MAP.at(state_).c_str(), DfxUtils::GetSafePath(data_.path).c_str());
614 return false;
615 }
616 data_.lastLoadSource = state_;
617 return true;
618 }
619
IsSizeAcceptable(std::unique_ptr<ImageSource> & imageSource,ImageInfo & imageInfo)620 bool SourceLoader::IsSizeAcceptable(std::unique_ptr<ImageSource>& imageSource, ImageInfo& imageInfo)
621 {
622 uint32_t err = imageSource->GetImageInfo(0, imageInfo);
623 if (err != E_OK) {
624 DfxManager::GetInstance()->HandleThumbnailError(data_.path, DfxType::IMAGE_SOURCE_GET_INFO, err);
625 error_ = E_ERR;
626 return false;
627 }
628
629 if (IsSizeLargeEnough == nullptr) {
630 MEDIA_ERR_LOG("IsSizeLargeEnough is nullptr.");
631 return false;
632 }
633
634 int32_t minSize = imageInfo.size.width < imageInfo.size.height ? imageInfo.size.width : imageInfo.size.height;
635 if (!IsSizeLargeEnough(data_, minSize)) {
636 MEDIA_DEBUG_LOG("SourceLoader size not acceptable, width:%{public}d, height:%{public}d", imageInfo.size.width,
637 imageInfo.size.height);
638 return false;
639 }
640
641 data_.stats.sourceWidth = imageInfo.size.width;
642 data_.stats.sourceHeight = imageInfo.size.height;
643 return true;
644 }
645
IsFinal()646 bool SourceLoader::IsFinal()
647 {
648 if (error_ != E_OK) {
649 state_ = SourceState::ERROR;
650 data_.stats.errorCode = error_;
651 return true;
652 }
653 if (state_ == SourceState::FINISH) {
654 return true;
655 }
656 return false;
657 }
658
GetSourcePath(ThumbnailData & data,int32_t & error)659 std::string LocalThumbSource::GetSourcePath(ThumbnailData &data, int32_t &error)
660 {
661 CHECK_AND_RETURN_RET_WARN_LOG(!(ThumbnailUtils::IsImageWithExifRotate(data) && data.needGenerateExThumbnail),
662 "", "Need generate Ex thumbnail, can not use rotated thumbnail");
663 CHECK_AND_RETURN_RET_WARN_LOG(data.loaderOpts.decodeInThumbSize, "", "Can not use thumb");
664 std::string tmpPath = GetLocalThumbnailPath(data.path, THUMBNAIL_THUMB_SUFFIX);
665 if (!IsLocalSourceAvailable(tmpPath)) {
666 return "";
667 }
668 return tmpPath;
669 }
670
IsSizeLargeEnough(ThumbnailData & data,int32_t & minSize)671 bool LocalThumbSource::IsSizeLargeEnough(ThumbnailData &data, int32_t &minSize)
672 {
673 if (minSize < SHORT_SIDE_THRESHOLD) {
674 return false;
675 }
676 return true;
677 }
678
GetSourcePath(ThumbnailData & data,int32_t & error)679 std::string LocalLcdSource::GetSourcePath(ThumbnailData &data, int32_t &error)
680 {
681 CHECK_AND_RETURN_RET_WARN_LOG(!(ThumbnailUtils::IsImageWithExifRotate(data) && data.needGenerateExThumbnail),
682 "", "Need generate Ex thumbnail, can not use rotated thumbnail");
683 std::string tmpPath = GetLocalThumbnailPath(data.path, THUMBNAIL_LCD_SUFFIX);
684 if (!IsLocalSourceAvailable(tmpPath)) {
685 return "";
686 }
687 return tmpPath;
688 }
689
IsSizeLargeEnough(ThumbnailData & data,int32_t & minSize)690 bool LocalLcdSource::IsSizeLargeEnough(ThumbnailData &data, int32_t &minSize)
691 {
692 if (minSize < SHORT_SIDE_THRESHOLD) {
693 return false;
694 }
695 return true;
696 }
697
GetSourcePath(ThumbnailData & data,int32_t & error)698 std::string LocalOriginSource::GetSourcePath(ThumbnailData &data, int32_t &error)
699 {
700 std::string tmpPath = GetLocalThumbnailPath(data.path, "");
701 if (!IsLocalSourceAvailable(tmpPath)) {
702 return "";
703 }
704 return tmpPath;
705 }
706
IsSizeLargeEnough(ThumbnailData & data,int32_t & minSize)707 bool LocalOriginSource::IsSizeLargeEnough(ThumbnailData &data, int32_t &minSize)
708 {
709 return true;
710 }
711
GetSourcePath(ThumbnailData & data,int32_t & error)712 std::string CloudThumbSource::GetSourcePath(ThumbnailData &data, int32_t &error)
713 {
714 CHECK_AND_RETURN_RET_WARN_LOG(data.loaderOpts.decodeInThumbSize, "", "Can not use thumb");
715 std::string tmpPath = ThumbnailUtils::IsExCloudThumbnail(data) ?
716 GetThumbnailPath(data.path, THUMBNAIL_THUMB_EX_SUFFIX) : GetThumbnailPath(data.path, THUMBNAIL_THUMB_SUFFIX);
717 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
718 if (!IsCloudSourceAvailable(tmpPath)) {
719 return "";
720 }
721 int32_t totalCost = static_cast<int32_t>(MediaFileUtils::UTCTimeMilliSeconds() - startTime);
722 data.stats.openThumbCost = totalCost;
723 return tmpPath;
724 }
725
IsSizeLargeEnough(ThumbnailData & data,int32_t & minSize)726 bool CloudThumbSource::IsSizeLargeEnough(ThumbnailData &data, int32_t &minSize)
727 {
728 int minDesiredSide = ParseDesiredMinSide(data.loaderOpts.desiredType);
729 if (minSize < minDesiredSide) {
730 return false;
731 }
732 return true;
733 }
734
GetSourcePath(ThumbnailData & data,int32_t & error)735 std::string CloudLcdSource::GetSourcePath(ThumbnailData &data, int32_t &error)
736 {
737 std::string tmpPath;
738 if (ThumbnailUtils::IsExCloudThumbnail(data)) {
739 tmpPath = GetLcdExPath(data.path);
740 } else {
741 tmpPath = GetThumbnailPath(data.path, THUMBNAIL_LCD_SUFFIX);
742 }
743 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
744 if (!IsCloudSourceAvailable(tmpPath)) {
745 return "";
746 }
747 int32_t totalCost = static_cast<int32_t>(MediaFileUtils::UTCTimeMilliSeconds() - startTime);
748 data.stats.openLcdCost = totalCost;
749 return tmpPath;
750 }
751
IsSizeLargeEnough(ThumbnailData & data,int32_t & minSize)752 bool CloudLcdSource::IsSizeLargeEnough(ThumbnailData &data, int32_t &minSize)
753 {
754 if (data.mediaType == MEDIA_TYPE_VIDEO) {
755 return true;
756 }
757 int photoShorterSide = data.photoWidth < data.photoHeight ? data.photoWidth : data.photoHeight;
758 if (photoShorterSide != 0 && photoShorterSide < SHORT_SIDE_THRESHOLD) {
759 return true;
760 }
761 if (minSize < SHORT_SIDE_THRESHOLD) {
762 return false;
763 }
764 return true;
765 }
766
GetSourcePath(ThumbnailData & data,int32_t & error)767 std::string CloudOriginSource::GetSourcePath(ThumbnailData &data, int32_t &error)
768 {
769 if (data.mediaType == MEDIA_TYPE_VIDEO) {
770 MEDIA_ERR_LOG("Opening cloud origin video file, path:%{public}s",
771 DfxUtils::GetSafePath(data.path).c_str());
772 return data.path;
773 }
774 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
775 if (!IsCloudSourceAvailable(data.path)) {
776 return "";
777 }
778 int32_t totalCost = static_cast<int32_t>(MediaFileUtils::UTCTimeMilliSeconds() - startTime);
779 data.stats.openOriginCost = totalCost;
780 return data.path;
781 }
782
IsSizeLargeEnough(ThumbnailData & data,int32_t & minSize)783 bool CloudOriginSource::IsSizeLargeEnough(ThumbnailData &data, int32_t &minSize)
784 {
785 return true;
786 }
787
788 } // namespace Media
789 } // namespace OHOS