• 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 #define MLOG_TAG "MovingPhotoProcessor"
17 
18 #include "moving_photo_processor.h"
19 
20 #include <fcntl.h>
21 
22 #include "abs_rdb_predicates.h"
23 #include "cloud_sync_helper.h"
24 #include "dfx_utils.h"
25 #include "directory_ex.h"
26 #include "media_column.h"
27 #include "media_file_utils.h"
28 #include "media_log.h"
29 #include "medialibrary_db_const.h"
30 #include "medialibrary_errno.h"
31 #include "medialibrary_rdb_utils.h"
32 #include "medialibrary_rdbstore.h"
33 #include "medialibrary_unistore_manager.h"
34 #include "metadata_extractor.h"
35 #include "mimetype_utils.h"
36 #include "moving_photo_file_utils.h"
37 #include "parameters.h"
38 #include "rdb_store.h"
39 #include "rdb_utils.h"
40 #include "result_set_utils.h"
41 #include "scanner_utils.h"
42 #include "userfile_manager_types.h"
43 #include "values_bucket.h"
44 
45 using namespace std;
46 using namespace OHOS::NativeRdb;
47 
48 namespace OHOS {
49 namespace Media {
50 static constexpr int32_t MOVING_PHOTO_PROCESS_NUM = 100;
51 static constexpr int32_t DIRTY_NOT_UPLOADING = -1;
52 static constexpr int32_t DEFAULT_EXTRA_DATA_SIZE = MIN_STANDARD_SIZE;
53 static constexpr int32_t LIVE_PHOTO_QUERY_NUM = 3000;
54 static constexpr int32_t LIVE_PHOTO_PROCESS_NUM = 200;
55 static constexpr int32_t COVER_POSITION_PROCESS_NUM = 500;
56 
57 static const string MOVING_PHOTO_PROCESS_FLAG = "multimedia.medialibrary.cloneFlag";
58 static const string LIVE_PHOTO_COMPAT_DONE = "0";
59 
60 bool MovingPhotoProcessor::isProcessing_ = false;
61 
IsCloudLivePhotoRefreshed()62 static bool IsCloudLivePhotoRefreshed()
63 {
64     string refreshStatus = system::GetParameter(REFRESH_CLOUD_LIVE_PHOTO_FLAG, CLOUD_LIVE_PHOTO_REFRESHED);
65     return refreshStatus.compare(CLOUD_LIVE_PHOTO_REFRESHED) == 0;
66 }
67 
StartProcessMovingPhoto()68 void MovingPhotoProcessor::StartProcessMovingPhoto()
69 {
70     CHECK_AND_RETURN_LOG(isProcessing_, "stop compating moving photo");
71     auto resultSet = QueryMovingPhoto();
72     CHECK_AND_RETURN_LOG(resultSet != nullptr, "Failed to query moving photo");
73 
74     MovingPhotoDataList dataList;
75     ParseMovingPhotoData(resultSet, dataList);
76     CHECK_AND_RETURN_LOG(!dataList.movingPhotos.empty(), "No moving photo need to be processed");
77 
78     CompatMovingPhoto(dataList);
79 }
80 
GetLivePhotoCompatId()81 static string GetLivePhotoCompatId()
82 {
83     return system::GetParameter(COMPAT_LIVE_PHOTO_FILE_ID, LIVE_PHOTO_COMPAT_DONE);
84 }
85 
IsLivePhotoCompatDone()86 static bool IsLivePhotoCompatDone()
87 {
88     string currentFileId = GetLivePhotoCompatId();
89     return currentFileId.compare(LIVE_PHOTO_COMPAT_DONE) == 0;
90 }
91 
SetLivePhotoCompatId(string fileId)92 static void SetLivePhotoCompatId(string fileId)
93 {
94     bool ret = system::SetParameter(COMPAT_LIVE_PHOTO_FILE_ID, fileId);
95     CHECK_AND_PRINT_LOG(ret, "Failed to set parameter for compating local live photo: %{public}s",
96         fileId.c_str());
97 }
98 
StartProcessLivePhoto()99 void MovingPhotoProcessor::StartProcessLivePhoto()
100 {
101     CHECK_AND_RETURN_LOG(isProcessing_, "Stop compating live photo");
102     CHECK_AND_RETURN_LOG(!IsLivePhotoCompatDone(), "Live photo compat done or no need to compat");
103     auto resultSet = QueryCandidateLivePhoto();
104     CHECK_AND_RETURN_LOG(resultSet != nullptr, "Failed to query candidate live photo");
105 
106     LivePhotoDataList dataList;
107     ParseLivePhotoData(resultSet, dataList);
108     if (dataList.livePhotos.empty()) {
109         SetLivePhotoCompatId(LIVE_PHOTO_COMPAT_DONE);
110         MEDIA_INFO_LOG("No live photo need to compat");
111         return;
112     }
113 
114     CompatLivePhoto(dataList);
115 }
116 
StartProcessCoverPosition()117 void MovingPhotoProcessor::StartProcessCoverPosition()
118 {
119     CHECK_AND_RETURN_LOG(isProcessing_, "Stop ProcessCoverPosition!");
120     auto resultSet = QueryInvalidCoverPosition();
121     CHECK_AND_RETURN_LOG(resultSet != nullptr, "Failed to query moving photo with invalid cover_position");
122 
123     ProcessCoverPosition(resultSet);
124 }
125 
QueryInvalidCoverPosition()126 std::shared_ptr<NativeRdb::ResultSet> MovingPhotoProcessor::QueryInvalidCoverPosition()
127 {
128     const vector<string> columns = {
129         MediaColumn::MEDIA_FILE_PATH,
130         PhotoColumn::MEDIA_ID,
131     };
132     RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
133     predicates.EqualTo(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::MOVING_PHOTO));
134     predicates.EqualTo(PhotoColumn::PHOTO_COVER_POSITION, 0);
135     predicates.EqualTo(PhotoColumn::PHOTO_IS_RECTIFICATION_COVER, 0);
136     predicates.BeginWrap();
137     predicates.EqualTo(PhotoColumn::PHOTO_POSITION, static_cast<int32_t>(PhotoPositionType::LOCAL));
138     predicates.Or();
139     predicates.EqualTo(PhotoColumn::PHOTO_POSITION, static_cast<int32_t>(PhotoPositionType::LOCAL_AND_CLOUD));
140     predicates.EndWrap();
141     predicates.Limit(COVER_POSITION_PROCESS_NUM);
142 
143     return MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
144 }
145 
ProcessCoverPosition(shared_ptr<NativeRdb::ResultSet> resultSet)146 void MovingPhotoProcessor::ProcessCoverPosition(shared_ptr<NativeRdb::ResultSet> resultSet)
147 {
148     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
149         CHECK_AND_RETURN_LOG(isProcessing_, "Stop ProcessCoverPosition!");
150 
151         string filePath =
152             get<string>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_FILE_PATH, resultSet, TYPE_STRING));
153         int32_t fileId = get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoColumn::MEDIA_ID, resultSet, TYPE_INT32));
154 
155         unique_ptr<Metadata> videoData = make_unique<Metadata>();
156         string videoPath = MediaFileUtils::GetMovingPhotoVideoPath(filePath);
157         videoData->SetMovingPhotoImagePath(filePath);
158         videoData->SetFilePath(videoPath);
159         videoData->SetPhotoSubType(static_cast<int32_t>(PhotoSubType::MOVING_PHOTO));
160         int32_t err = MetadataExtractor::ExtractAVMetadata(videoData);
161         if (err != E_OK) {
162             MEDIA_ERR_LOG("Failed to extract metadata for moving photo: %{public}s",
163                           DfxUtils::GetSafePath(videoPath).c_str());
164             continue;
165         }
166 
167         int64_t coverPosition = videoData->GetCoverPosition();
168         AbsRdbPredicates predicates = AbsRdbPredicates(PhotoColumn::PHOTOS_TABLE);
169         predicates.EqualTo(PhotoColumn::MEDIA_ID, fileId);
170         ValuesBucket values;
171         values.PutLong(PhotoColumn::PHOTO_COVER_POSITION, coverPosition);
172         values.PutInt(PhotoColumn::PHOTO_IS_RECTIFICATION_COVER, 1);
173 
174         auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
175         CHECK_AND_RETURN_LOG(rdbStore != nullptr, "rdbStore is null");
176         int32_t changeRows = -1;
177         int32_t ret = rdbStore->Update(changeRows, values, predicates);
178         CHECK_AND_PRINT_LOG((ret == E_OK && changeRows > 0), "failed to update cover_position, ret = %{public}d", ret);
179         MEDIA_INFO_LOG("ProcessCoverPosition done: %{public}d %{public}s", fileId,
180                        DfxUtils::GetSafePath(filePath).c_str());
181     }
182 }
183 
StartProcess()184 void MovingPhotoProcessor::StartProcess()
185 {
186     MEDIA_DEBUG_LOG("Start processing moving photo task");
187     isProcessing_ = true;
188 
189     // 1. compat old moving photo
190     StartProcessMovingPhoto();
191 
192     // 2. compat local live photo
193     StartProcessLivePhoto();
194 
195     // 3. refresh cloud live photo if needed
196     if (!IsCloudLivePhotoRefreshed()) {
197         MEDIA_INFO_LOG("Strat reset cloud cursor for cloud live photo");
198         FileManagement::CloudSync::CloudSyncManager::GetInstance().ResetCursor();
199         MEDIA_INFO_LOG("End reset cloud cursor for cloud live photo");
200         bool ret = system::SetParameter(REFRESH_CLOUD_LIVE_PHOTO_FLAG, CLOUD_LIVE_PHOTO_REFRESHED);
201         MEDIA_INFO_LOG("Set parameter of isRefreshed to 1, ret: %{public}d", ret);
202     }
203 
204     // 4. refresh cover_position
205     StartProcessCoverPosition();
206 
207     isProcessing_ = false;
208     MEDIA_DEBUG_LOG("Finsh processing moving photo task");
209 }
210 
StopProcess()211 void MovingPhotoProcessor::StopProcess()
212 {
213     isProcessing_ = false;
214 }
215 
QueryMovingPhoto()216 shared_ptr<NativeRdb::ResultSet> MovingPhotoProcessor::QueryMovingPhoto()
217 {
218     const vector<string> columns = {
219         PhotoColumn::MEDIA_ID,
220         PhotoColumn::PHOTO_SUBTYPE,
221         PhotoColumn::MOVING_PHOTO_EFFECT_MODE,
222         PhotoColumn::MEDIA_SIZE,
223         PhotoColumn::MEDIA_FILE_PATH,
224     };
225     RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
226     predicates.EqualTo(PhotoColumn::PHOTO_DIRTY, DIRTY_NOT_UPLOADING)
227         ->And()
228         ->BeginWrap()
229         ->EqualTo(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::MOVING_PHOTO))
230         ->Or()
231         ->EqualTo(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY))
232         ->EndWrap()
233         ->And()
234         ->EqualTo(PhotoColumn::PHOTO_IS_TEMP, 0)
235         ->And()
236         ->EqualTo(PhotoColumn::MEDIA_TIME_PENDING, 0)
237         ->And()
238         ->BeginWrap()
239         ->EqualTo(PhotoColumn::PHOTO_QUALITY, static_cast<int32_t>(MultiStagesPhotoQuality::FULL))
240         ->Or()
241         ->IsNull(PhotoColumn::PHOTO_QUALITY)
242         ->EndWrap()
243         ->Limit(MOVING_PHOTO_PROCESS_NUM);
244     return MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
245 }
246 
ParseMovingPhotoData(shared_ptr<NativeRdb::ResultSet> & resultSet,MovingPhotoDataList & dataList)247 void MovingPhotoProcessor::ParseMovingPhotoData(shared_ptr<NativeRdb::ResultSet>& resultSet,
248     MovingPhotoDataList& dataList)
249 {
250     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
251         int32_t fileId = get<int32_t>(
252             ResultSetUtils::GetValFromColumn(PhotoColumn::MEDIA_ID, resultSet, TYPE_INT32));
253         int32_t subtype = get<int32_t>(
254             ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_SUBTYPE, resultSet, TYPE_INT32));
255         int32_t effectMode = get<int32_t>(
256             ResultSetUtils::GetValFromColumn(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, resultSet, TYPE_INT32));
257         int64_t size = get<int64_t>(
258             ResultSetUtils::GetValFromColumn(PhotoColumn::MEDIA_SIZE, resultSet, TYPE_INT64));
259         std::string path = get<std::string>(
260             ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_FILE_PATH, resultSet, TYPE_STRING));
261 
262         MovingPhotoData movingPhotoData;
263         movingPhotoData.fileId = fileId;
264         movingPhotoData.subtype = subtype;
265         movingPhotoData.effectMode = effectMode;
266         movingPhotoData.size = size;
267         movingPhotoData.path = path;
268         dataList.movingPhotos.push_back(movingPhotoData);
269     }
270 }
271 
UpdateMovingPhotoData(const MovingPhotoData & movingPhotoData)272 void MovingPhotoProcessor::UpdateMovingPhotoData(const MovingPhotoData& movingPhotoData)
273 {
274     ValuesBucket values;
275     string whereClause = PhotoColumn::MEDIA_ID + " = ? AND " + PhotoColumn::PHOTO_DIRTY + " = ?";
276     vector<string> whereArgs = { to_string(movingPhotoData.fileId), to_string(DIRTY_NOT_UPLOADING) };
277     values.PutInt(PhotoColumn::PHOTO_SUBTYPE, movingPhotoData.subtype);
278     values.PutLong(PhotoColumn::MEDIA_SIZE, movingPhotoData.size);
279     values.PutInt(PhotoColumn::PHOTO_DIRTY, static_cast<int32_t>(DirtyTypes::TYPE_NEW));
280     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
281 
282     CHECK_AND_RETURN_LOG(rdbStore != nullptr, "rdbStore is null");
283     CHECK_AND_RETURN_LOG(isProcessing_, "stop updateing moving photo data");
284     int32_t updateCount = 0;
285     int32_t result = rdbStore->Update(updateCount, PhotoColumn::PHOTOS_TABLE, values, whereClause, whereArgs);
286     bool cond = (result != NativeRdb::E_OK || updateCount <= 0);
287     CHECK_AND_RETURN_LOG(!cond, "Update failed. result: %{public}d, updateCount: %{public}d", result, updateCount);
288 }
289 
GetDefaultExtraData()290 static string GetDefaultExtraData()
291 {
292     static string defaultExtraData = "v3_f0               0:0                 LIVE_10000000       ";
293     return defaultExtraData;
294 }
295 
GetUpdatedMovingPhotoData(const MovingPhotoData & currentData,MovingPhotoData & newData)296 int32_t MovingPhotoProcessor::GetUpdatedMovingPhotoData(const MovingPhotoData& currentData,
297     MovingPhotoData& newData)
298 {
299     newData = currentData;
300     string imagePath = currentData.path;
301     string videoPath = MovingPhotoFileUtils::GetMovingPhotoVideoPath(imagePath);
302     string extraDataPath = MovingPhotoFileUtils::GetMovingPhotoExtraDataPath(imagePath);
303     size_t imageSize = 0;
304     size_t videoSize = 0;
305     size_t extraSize = 0;
306     if (!MediaFileUtils::GetFileSize(imagePath, imageSize) || imageSize == 0) {
307         MEDIA_WARN_LOG("Failed to get image of moving photo, id: %{public}d", currentData.fileId);
308         newData.size = -1; // set abnormal size to -1 if original size is 0
309         newData.subtype = static_cast<int32_t>(PhotoSubType::DEFAULT);
310         return E_OK;
311     }
312 
313     if (!MediaFileUtils::GetFileSize(videoPath, videoSize) || videoSize == 0) {
314         MEDIA_WARN_LOG("Failed to get video of moving photo, id: %{public}d", currentData.fileId);
315         newData.size = static_cast<int64_t>(imageSize);
316         newData.subtype = static_cast<int32_t>(PhotoSubType::DEFAULT);
317         return E_OK;
318     }
319 
320     if (MediaFileUtils::GetFileSize(extraDataPath, extraSize) && extraSize > 0) {
321         newData.size = static_cast<int64_t>(imageSize + videoSize + extraSize);
322         return E_OK;
323     }
324 
325     string extraDataDir = MovingPhotoFileUtils::GetMovingPhotoExtraDataDir(imagePath);
326     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::CreateDirectory(extraDataDir), E_HAS_FS_ERROR,
327         "Cannot create dir %{private}s, errno:%{public}d", extraDataDir.c_str(), errno);
328     bool cond = (!MediaFileUtils::IsFileExists(extraDataPath) && MediaFileUtils::CreateAsset(extraDataPath) != E_OK);
329     CHECK_AND_RETURN_RET_LOG(!cond, E_HAS_FS_ERROR,
330         "Failed to create extraData:%{private}s, errno:%{public}d", extraDataPath.c_str(), errno);
331     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::WriteStrToFile(extraDataPath, GetDefaultExtraData()),
332         E_HAS_FS_ERROR, "Failed to write extraData, errno:%{public}d", errno);
333     newData.size = static_cast<int64_t>(imageSize + videoSize + DEFAULT_EXTRA_DATA_SIZE);
334     return E_OK;
335 }
336 
CompatMovingPhoto(const MovingPhotoDataList & dataList)337 void MovingPhotoProcessor::CompatMovingPhoto(const MovingPhotoDataList& dataList)
338 {
339     MEDIA_INFO_LOG("Start processing %{public}zu moving photos", dataList.movingPhotos.size());
340     int32_t count = 0;
341     for (const auto& movingPhoto : dataList.movingPhotos) {
342         CHECK_AND_RETURN_LOG(isProcessing_, "stop compating moving photo");
343         MovingPhotoData newData;
344         if (GetUpdatedMovingPhotoData(movingPhoto, newData) != E_OK) {
345             MEDIA_INFO_LOG("Failed to get updated data of moving photo, id: %{public}d", movingPhoto.fileId);
346             continue;
347         }
348         UpdateMovingPhotoData(newData);
349         count += 1;
350     }
351     MEDIA_INFO_LOG("Finish processing %{public}d moving photos", count);
352 }
353 
QueryCandidateLivePhoto()354 shared_ptr<NativeRdb::ResultSet> MovingPhotoProcessor::QueryCandidateLivePhoto()
355 {
356     const vector<string> columns = {
357         PhotoColumn::MEDIA_ID,
358         PhotoColumn::MEDIA_TYPE,
359         PhotoColumn::PHOTO_SUBTYPE,
360         PhotoColumn::PHOTO_POSITION,
361         PhotoColumn::PHOTO_EDIT_TIME,
362         PhotoColumn::MEDIA_FILE_PATH,
363     };
364     RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
365     string currentFileIdStr = GetLivePhotoCompatId();
366     int32_t currentFileId = std::atoi(currentFileIdStr.c_str());
367     MEDIA_INFO_LOG("Start query candidate live photo from file_id: %{public}d", currentFileId);
368     predicates.GreaterThanOrEqualTo(PhotoColumn::MEDIA_ID, currentFileId)
369         ->And()
370         ->EqualTo(PhotoColumn::MEDIA_TYPE, static_cast<int32_t>(MEDIA_TYPE_IMAGE))
371         ->And()
372         ->EqualTo(PhotoColumn::PHOTO_IS_TEMP, 0)
373         ->And()
374         ->EqualTo(PhotoColumn::MEDIA_TIME_PENDING, 0)
375         ->And()
376         ->BeginWrap()
377         ->EqualTo(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::DEFAULT))
378         ->Or()
379         ->EqualTo(PhotoColumn::PHOTO_SUBTYPE, static_cast<int32_t>(PhotoSubType::CAMERA))
380         ->EndWrap()
381         ->And()
382         ->BeginWrap()
383         ->EqualTo(PhotoColumn::PHOTO_POSITION, static_cast<int32_t>(PhotoPositionType::LOCAL))
384         ->Or()
385         ->EqualTo(PhotoColumn::PHOTO_POSITION, static_cast<int32_t>(PhotoPositionType::LOCAL_AND_CLOUD))
386         ->EndWrap()
387         ->OrderByAsc(PhotoColumn::MEDIA_ID)
388         ->Limit(LIVE_PHOTO_QUERY_NUM);
389     return MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
390 }
391 
ParseLivePhotoData(shared_ptr<NativeRdb::ResultSet> & resultSet,LivePhotoDataList & dataList)392 void MovingPhotoProcessor::ParseLivePhotoData(shared_ptr<NativeRdb::ResultSet>& resultSet,
393     LivePhotoDataList& dataList)
394 {
395     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
396         int32_t fileId = get<int32_t>(
397             ResultSetUtils::GetValFromColumn(PhotoColumn::MEDIA_ID, resultSet, TYPE_INT32));
398         int32_t mediaType = get<int32_t>(
399             ResultSetUtils::GetValFromColumn(PhotoColumn::MEDIA_TYPE, resultSet, TYPE_INT32));
400         int32_t subtype = get<int32_t>(
401             ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_SUBTYPE, resultSet, TYPE_INT32));
402         int32_t position = get<int32_t>(
403             ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_POSITION, resultSet, TYPE_INT32));
404         int64_t editTime = get<int64_t>(
405             ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_EDIT_TIME, resultSet, TYPE_INT64));
406         std::string path = get<std::string>(
407             ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_FILE_PATH, resultSet, TYPE_STRING));
408 
409         LivePhotoData livePhotoData;
410         livePhotoData.isLivePhoto = false;
411         livePhotoData.fileId = fileId;
412         livePhotoData.mediaType = mediaType;
413         livePhotoData.subtype = subtype;
414         livePhotoData.position = position;
415         livePhotoData.editTime = editTime;
416         livePhotoData.path = path;
417         dataList.livePhotos.push_back(livePhotoData);
418     }
419 }
420 
CompatLivePhoto(const LivePhotoDataList & dataList)421 void MovingPhotoProcessor::CompatLivePhoto(const LivePhotoDataList& dataList)
422 {
423     MEDIA_INFO_LOG("Start processing %{public}zu candidate live photos", dataList.livePhotos.size());
424     int32_t count = 0;
425     int32_t livePhotoCount = 0;
426     int32_t processedFileId = 0;
427     for (const auto& livePhoto : dataList.livePhotos) {
428         if (!isProcessing_) {
429             SetLivePhotoCompatId(std::to_string(livePhoto.fileId));
430             MEDIA_INFO_LOG("Stop compating live photo, file_id: %{public}d", livePhoto.fileId);
431             return;
432         }
433         processedFileId = livePhoto.fileId;
434         LivePhotoData newData;
435         if (GetUpdatedLivePhotoData(livePhoto, newData) != E_OK) {
436             MEDIA_INFO_LOG("Failed to get updated data of candidate live photo, id: %{public}d", livePhoto.fileId);
437             continue;
438         }
439         if (newData.isLivePhoto) {
440             UpdateLivePhotoData(newData);
441             livePhotoCount += 1;
442         }
443         count += 1;
444 
445         if (livePhotoCount >= LIVE_PHOTO_PROCESS_NUM) {
446             SetLivePhotoCompatId(std::to_string(livePhoto.fileId + 1));
447             MEDIA_INFO_LOG("Stop compating live photo, %{public}d processed", livePhotoCount);
448             return;
449         }
450     }
451     SetLivePhotoCompatId(std::to_string(processedFileId + 1));
452     MEDIA_INFO_LOG("Finish processing %{public}d candidates, contains %{public}d live photos, file_id: %{public}d",
453         count, livePhotoCount, processedFileId);
454 }
455 
addCompatPathSuffix(const string & oldPath,const string & suffix,string & newPath)456 static void addCompatPathSuffix(const string &oldPath, const string &suffix, string &newPath)
457 {
458     bool cond = (oldPath.empty() || suffix.empty());
459     CHECK_AND_RETURN_LOG(!cond, "oldPath or suffix is empty");
460     newPath = oldPath + ".compat" + suffix;
461     while (MediaFileUtils::IsFileExists(newPath)) {
462         newPath += ".dup" + suffix;
463     }
464 }
465 
MoveMovingPhoto(const string & path,const string & compatImagePath,const string & compatVideoPath,const string & compatExtraDataPath)466 static int32_t MoveMovingPhoto(const string &path,
467     const string &compatImagePath, const string &compatVideoPath, const string &compatExtraDataPath)
468 {
469     string movingPhotoVideoPath = MovingPhotoFileUtils::GetMovingPhotoVideoPath(path);
470     string movingPhotoExtraDataPath = MovingPhotoFileUtils::GetMovingPhotoExtraDataPath(path);
471     CHECK_AND_RETURN_RET_LOG(!movingPhotoVideoPath.empty(), E_INVALID_VALUES, "Failed to get video path");
472     CHECK_AND_RETURN_RET_LOG(!movingPhotoExtraDataPath.empty(), E_INVALID_VALUES, "Failed to get extraData path");
473     CHECK_AND_RETURN_RET_LOG(
474         !MediaFileUtils::IsFileExists(movingPhotoVideoPath), E_INVALID_VALUES, "Video path exists!");
475     CHECK_AND_RETURN_RET_LOG(
476         !MediaFileUtils::IsFileExists(movingPhotoExtraDataPath), E_INVALID_VALUES, "extraData path exists");
477     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::CreateDirectory(MovingPhotoFileUtils::GetMovingPhotoExtraDataDir(path)),
478         E_HAS_FS_ERROR, "Failed to create extraData dir of %{private}s", path.c_str());
479 
480     int32_t ret = rename(compatExtraDataPath.c_str(), movingPhotoExtraDataPath.c_str());
481     CHECK_AND_RETURN_RET_LOG(ret >= 0, ret, "Failed to rename extraData, src:"
482         " %{public}s, dest: %{public}s, errno: %{public}d",
483         compatExtraDataPath.c_str(), movingPhotoExtraDataPath.c_str(), errno);
484 
485     ret = rename(compatVideoPath.c_str(), movingPhotoVideoPath.c_str());
486     CHECK_AND_RETURN_RET_LOG(ret >= 0, ret, "Failed to rename video, src: %{public}s,"
487         " dest: %{public}s, errno: %{public}d",
488         compatVideoPath.c_str(), movingPhotoVideoPath.c_str(), errno);
489 
490     ret = rename(compatImagePath.c_str(), path.c_str());
491     CHECK_AND_RETURN_RET_LOG(ret >= 0, ret, "Failed to rename image, src: %{public}s,"
492         " dest: %{public}s, errno: %{public}d",
493         compatImagePath.c_str(), path.c_str(), errno);
494     return ret;
495 }
496 
ProcessLocalLivePhoto(LivePhotoData & data)497 int32_t MovingPhotoProcessor::ProcessLocalLivePhoto(LivePhotoData& data)
498 {
499     data.isLivePhoto = false;
500     bool isLivePhoto = MovingPhotoFileUtils::IsLivePhoto(data.path);
501     CHECK_AND_RETURN_RET(isLivePhoto, E_OK);
502 
503     string livePhotoPath = data.path;
504     string compatImagePath;
505     string compatVideoPath;
506     string compatExtraDataPath;
507     addCompatPathSuffix(livePhotoPath, ".jpg", compatImagePath);
508     addCompatPathSuffix(livePhotoPath, ".mp4", compatVideoPath);
509     addCompatPathSuffix(livePhotoPath, ".extra", compatExtraDataPath);
510     int32_t ret = MovingPhotoFileUtils::ConvertToMovingPhoto(
511         livePhotoPath, compatImagePath, compatVideoPath, compatExtraDataPath);
512     if (ret != E_OK) {
513         MEDIA_ERR_LOG("Failed to convert live photo, ret:%{public}d, file_id:%{public}d", ret, data.fileId);
514         (void)MediaFileUtils::DeleteFile(compatImagePath);
515         (void)MediaFileUtils::DeleteFile(compatVideoPath);
516         (void)MediaFileUtils::DeleteFile(compatExtraDataPath);
517         return ret;
518     }
519 
520     uint64_t coverPosition = 0;
521     uint32_t version = 0;
522     uint32_t frameIndex = 0;
523     bool hasCinemagraphInfo = false;
524     string absExtraDataPath;
525 
526     CHECK_AND_RETURN_RET_LOG(PathToRealPath(compatExtraDataPath, absExtraDataPath), E_HAS_FS_ERROR,
527         "extraData is not real path: %{private}s, errno: %{public}d", compatExtraDataPath.c_str(), errno);
528     UniqueFd extraDataFd(open(absExtraDataPath.c_str(), O_RDONLY));
529     (void)MovingPhotoFileUtils::GetVersionAndFrameNum(extraDataFd.Get(), version, frameIndex, hasCinemagraphInfo);
530     (void)MovingPhotoFileUtils::GetCoverPosition(compatVideoPath, frameIndex, coverPosition);
531     data.coverPosition = static_cast<int64_t>(coverPosition);
532 
533     ret = MoveMovingPhoto(livePhotoPath, compatImagePath, compatVideoPath, compatExtraDataPath);
534     CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Failed to move moving photo, file_id:%{public}d", data.fileId);
535     data.subtype = static_cast<int32_t>(PhotoSubType::MOVING_PHOTO);
536     data.isLivePhoto = true;
537     return E_OK;
538 }
539 
ProcessLocalCloudLivePhoto(LivePhotoData & data)540 int32_t MovingPhotoProcessor::ProcessLocalCloudLivePhoto(LivePhotoData& data)
541 {
542     CHECK_AND_RETURN_RET(data.editTime != 0, ProcessLocalLivePhoto(data));
543     data.isLivePhoto = false;
544     string sourcePath = PhotoFileUtils::GetEditDataSourcePath(data.path);
545     bool isLivePhotoEdited = MovingPhotoFileUtils::IsLivePhoto(sourcePath);
546     CHECK_AND_RETURN_RET(isLivePhotoEdited, E_OK);
547     data.metaDateModified = MediaFileUtils::UTCTimeMilliSeconds();
548     data.isLivePhoto = true;
549     return E_OK;
550 }
551 
GetUpdatedLivePhotoData(const LivePhotoData & currentData,LivePhotoData & newData)552 int32_t MovingPhotoProcessor::GetUpdatedLivePhotoData(const LivePhotoData& currentData, LivePhotoData& newData)
553 {
554     newData = currentData;
555     string path = currentData.path;
556     string extension = ScannerUtils::GetFileExtension(path);
557     string mimeType = MimeTypeUtils::GetMimeTypeFromExtension(extension);
558     if (mimeType.compare("image/jpeg") != 0) {
559         newData.isLivePhoto = false;
560         return E_OK;
561     }
562 
563     if (currentData.position == static_cast<int32_t>(PhotoPositionType::LOCAL)) {
564         return ProcessLocalLivePhoto(newData);
565     } else if (currentData.position == static_cast<int32_t>(PhotoPositionType::LOCAL_AND_CLOUD)) {
566         return ProcessLocalCloudLivePhoto(newData);
567     } else {
568         MEDIA_ERR_LOG("Invalid position to process: %{public}d", currentData.position);
569         return E_INVALID_VALUES;
570     }
571 }
572 
UpdateLivePhotoData(const LivePhotoData & livePhotoData)573 void MovingPhotoProcessor::UpdateLivePhotoData(const LivePhotoData& livePhotoData)
574 {
575     CHECK_AND_RETURN_LOG(livePhotoData.isLivePhoto, "Not a live photo Update Failed");
576     bool cond = (livePhotoData.position != static_cast<int32_t>(PhotoPositionType::LOCAL) &&
577         livePhotoData.position != static_cast<int32_t>(PhotoPositionType::LOCAL_AND_CLOUD));
578     CHECK_AND_RETURN_LOG(!cond, "Invalid position: %{public}d", livePhotoData.position);
579 
580     ValuesBucket values;
581     string whereClause = PhotoColumn::MEDIA_ID + " = ?";
582     vector<string> whereArgs = { to_string(livePhotoData.fileId) };
583     auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
584     CHECK_AND_RETURN_LOG(rdbStore != nullptr, "rdbStore is null");
585 
586     if (livePhotoData.editTime == 0) {
587         values.PutInt(PhotoColumn::PHOTO_SUBTYPE, livePhotoData.subtype);
588         values.PutLong(PhotoColumn::PHOTO_COVER_POSITION, livePhotoData.coverPosition);
589     } else {
590         values.PutLong(PhotoColumn::PHOTO_META_DATE_MODIFIED, livePhotoData.metaDateModified);
591     }
592 
593     int32_t updateCount = 0;
594     int32_t result = rdbStore->Update(updateCount, PhotoColumn::PHOTOS_TABLE, values, whereClause, whereArgs);
595     cond = (result != NativeRdb::E_OK || updateCount <= 0);
596     CHECK_AND_RETURN_LOG(!cond, "Update failed. result: %{public}d, updateCount: %{public}d", result, updateCount);
597 }
598 } // namespace Media
599 } // namespace OHOS
600