• 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 "MovingPhotoFileUtils"
17 
18 #include "moving_photo_file_utils.h"
19 
20 #include <fcntl.h>
21 #include <regex>
22 #include <sstream>
23 #include <sys/sendfile.h>
24 #include <sys/stat.h>
25 #include <map>
26 #include <unistd.h>
27 
28 #include "avmetadatahelper.h"
29 #include "directory_ex.h"
30 #include "media_file_utils.h"
31 #include "media_log.h"
32 #include "medialibrary_errno.h"
33 #include "medialibrary_tracer.h"
34 #include "medialibrary_type_const.h"
35 #include "string_ex.h"
36 #include "unique_fd.h"
37 
38 using namespace std;
39 
40 namespace OHOS::Media {
41 const std::string MEDIA_EXTRA_DATA_DIR = MEDIA_EDIT_DATA_DIR;
42 
43 const std::string LIVE_PHOTO_CINEMAGRAPH_INFO = "CinemagraphInfo";
44 const std::string LIVE_PHOTO_VIDEO_INFO_METADATA = "VideoInfoMetadata";
45 const std::string LIVE_PHOTO_SIGHT_TREMBLE_META_DATA = "SightTrembleMetadata";
46 const std::string LIVE_PHOTO_VERSION_AND_FRAME_NUM = "VersionAndFrameNum";
47 constexpr int32_t HEX_BASE = 16;
48 
GetVersionPositionTag(uint32_t frame,bool hasExtraData,const string & data="")49 static string GetVersionPositionTag(uint32_t frame, bool hasExtraData, const string& data = "")
50 {
51     string buffer;
52     bool hasCinemagraph{false};
53     if (data.size() != 0) {
54         uint32_t version{0};
55         uint32_t frameIndex{0};
56         if (MovingPhotoFileUtils::GetVersionAndFrameNum(data, version, frameIndex, hasCinemagraph) != E_OK) {
57             return buffer;
58         }
59         buffer = "v" + to_string(version) + "_f";
60     } else if (hasExtraData) {
61         return buffer;
62     } else {
63         buffer += "v3_f";
64     }
65     buffer += to_string(frame);
66     if (hasCinemagraph) {
67         buffer += "_c";
68     }
69     uint32_t left = LIVE_TAG_LEN - buffer.length();
70     for (uint32_t i = 0; i < left; ++i) {
71         buffer += ' ';
72     }
73     return buffer;
74 }
75 
GetDurationTag(const string & data="")76 static string GetDurationTag(const string& data = "")
77 {
78     string buffer;
79     if (data.size() != 0) {
80         buffer += data;
81     } else {
82         buffer += "0:0";
83     }
84     uint16_t left = PLAY_INFO_LEN - buffer.length();
85     for (uint16_t i = 0; i < left; ++i) {
86         buffer += ' ';
87     }
88     return buffer;
89 }
90 
GetVideoInfoTag(off_t fileSize)91 static string GetVideoInfoTag(off_t fileSize)
92 {
93     string buffer = "LIVE_" + to_string(fileSize);
94     uint16_t left = VERSION_TAG_LEN - buffer.length();
95     for (uint16_t i = 0; i < left; ++i) {
96         buffer += ' ';
97     }
98     return buffer;
99 }
100 
GetFileSize(const int32_t fd)101 static off_t GetFileSize(const int32_t fd)
102 {
103     if (fd < 0) {
104         MEDIA_ERR_LOG("file is error");
105         return E_ERR;
106     }
107     struct stat st;
108     if (fstat(fd, &st) != E_OK) {
109         MEDIA_ERR_LOG("failed to get file size, errno: %{public}d", errno);
110         return E_ERR;
111     }
112     return st.st_size;
113 }
114 
GetFileSize(const string & path)115 static off_t GetFileSize(const string& path)
116 {
117     struct stat st;
118     if (stat(path.c_str(), &st) != E_OK) {
119         MEDIA_ERR_LOG("failed to get file size, errno: %{public}d", errno);
120         return E_ERR;
121     }
122     return st.st_size;
123 }
124 
WriteContentTofile(const UniqueFd & destFd,const UniqueFd & srcFd)125 static int32_t WriteContentTofile(const UniqueFd& destFd, const UniqueFd& srcFd)
126 {
127     const uint32_t BUFFER_LENGTH = 16 * 1024; // 16KB
128     if (lseek(srcFd.Get(), 0, SEEK_SET) == E_ERR) {
129         MEDIA_ERR_LOG("failed to lseek file, errno: %{public}d", errno);
130         return E_ERR;
131     }
132     char buffer[BUFFER_LENGTH];
133     ssize_t bytesRead = 0;
134     ssize_t bytesWritten = 0;
135     while ((bytesRead = read(srcFd.Get(), buffer, BUFFER_LENGTH)) > 0) {
136         bytesWritten = write(destFd.Get(), buffer, bytesRead);
137         if (bytesWritten != bytesRead) {
138             MEDIA_ERR_LOG("failed to write file, errno: %{public}d", errno);
139             return E_ERR;
140         }
141     }
142     if (bytesRead < 0) {
143         MEDIA_ERR_LOG("failed to read from srcFd:%{public}d, errno:%{public}d", srcFd.Get(), errno);
144         return E_ERR;
145     }
146     return E_OK;
147 }
148 
AddStringToFile(const UniqueFd & destFd,const string & temp)149 static int32_t AddStringToFile(const UniqueFd& destFd, const string& temp)
150 {
151     ssize_t ret = write(destFd.Get(), temp.c_str(), temp.size());
152     if (ret < 0 || static_cast<size_t>(ret) != temp.size()) {
153         MEDIA_ERR_LOG("failed to write file, errno: %{public}d, ret: %{public}" PRId64, errno,
154             static_cast<int64_t>(ret));
155         return E_ERR;
156     }
157     return E_OK;
158 }
159 
GetExtraData(const UniqueFd & fd,off_t fileSize,off_t offset,off_t needSize)160 static string GetExtraData(const UniqueFd& fd, off_t fileSize, off_t offset, off_t needSize)
161 {
162     off_t readPosition = fileSize >= offset ? fileSize - offset : 0;
163     if (lseek(fd.Get(), readPosition, SEEK_SET) == E_ERR) {
164         MEDIA_ERR_LOG("failed to lseek extra file errno: %{public}d", errno);
165         return "";
166     }
167     char* buffer = new (std::nothrow) char[needSize + 1];
168     if (buffer == nullptr) {
169         MEDIA_ERR_LOG("failed to allocate buffer");
170         return "";
171     }
172     memset_s(buffer, needSize + 1, 0, needSize + 1);
173     ssize_t bytesRead;
174     if ((bytesRead = read(fd.Get(), buffer, needSize)) < 0) {
175         MEDIA_ERR_LOG("failed to read extra file errno: %{public}d", errno);
176         delete[] buffer;
177         buffer = nullptr;
178         return "";
179     }
180     string content(buffer, bytesRead);
181     delete[] buffer;
182     buffer = nullptr;
183     return content;
184 }
185 
ReadExtraFile(const std::string & extraPath,map<string,string> & extraData)186 static int32_t ReadExtraFile(const std::string& extraPath, map<string, string>& extraData)
187 {
188     string absExtraPath;
189     if (!PathToRealPath(extraPath, absExtraPath)) {
190         MEDIA_ERR_LOG("file is not real path: %{private}s, errno: %{public}d", extraPath.c_str(), errno);
191         return E_HAS_FS_ERROR;
192     }
193     UniqueFd fd(open(absExtraPath.c_str(), O_RDONLY));
194     if (fd.Get() == E_ERR) {
195         MEDIA_ERR_LOG("failed to open extra file, errno: %{public}d", errno);
196         return E_ERR;
197     }
198     uint32_t version{0};
199     uint32_t frameIndex{0};
200     bool hasCinemagraphInfo{false};
201     bool hasVersion = MovingPhotoFileUtils::GetVersionAndFrameNum(
202         fd.Get(), version, frameIndex, hasCinemagraphInfo) == E_OK;
203     off_t fileSize = GetFileSize(fd.Get());
204     extraData[LIVE_PHOTO_VIDEO_INFO_METADATA] = GetExtraData(fd, fileSize, LIVE_TAG_LEN, LIVE_TAG_LEN);
205     extraData[LIVE_PHOTO_SIGHT_TREMBLE_META_DATA] = GetExtraData(fd, fileSize, LIVE_TAG_LEN + PLAY_INFO_LEN,
206         PLAY_INFO_LEN);
207     if (hasVersion) {
208         extraData[LIVE_PHOTO_VERSION_AND_FRAME_NUM] = GetExtraData(fd, fileSize, MIN_STANDARD_SIZE, VERSION_TAG_LEN);
209         if (hasCinemagraphInfo) {
210             extraData[LIVE_PHOTO_CINEMAGRAPH_INFO] = GetExtraData(fd, fileSize, fileSize, fileSize - MIN_STANDARD_SIZE);
211         }
212     } else if (fileSize > LIVE_TAG_LEN + PLAY_INFO_LEN) {
213         extraData[LIVE_PHOTO_CINEMAGRAPH_INFO] = GetExtraData(fd, fileSize, fileSize,
214             fileSize - LIVE_TAG_LEN - PLAY_INFO_LEN);
215     }
216     return E_OK;
217 }
218 
WriteExtraData(const string & extraPath,const UniqueFd & livePhotoFd,const UniqueFd & videoFd,uint32_t frameIndex)219 static int32_t WriteExtraData(const string& extraPath, const UniqueFd& livePhotoFd, const UniqueFd& videoFd,
220     uint32_t frameIndex)
221 {
222     map<string, string> extraData;
223     bool hasExtraData{false};
224     if (MediaFileUtils::IsFileValid(extraPath)) {
225         hasExtraData = true;
226         if (ReadExtraFile(extraPath, extraData) == E_ERR) {
227             MEDIA_ERR_LOG("read extra file err");
228             return E_ERR;
229         }
230         if (AddStringToFile(livePhotoFd, extraData[LIVE_PHOTO_CINEMAGRAPH_INFO]) == E_ERR) {
231             MEDIA_ERR_LOG("write cinemagraph info err");
232             return E_ERR;
233         }
234     }
235     string versonAndFrameNum = GetVersionPositionTag(frameIndex, hasExtraData,
236         extraData[LIVE_PHOTO_VERSION_AND_FRAME_NUM]);
237     if (AddStringToFile(livePhotoFd, versonAndFrameNum) == E_ERR) {
238         MEDIA_ERR_LOG("write version position tag err");
239         return E_ERR;
240     }
241     if (AddStringToFile(livePhotoFd, GetDurationTag(extraData[LIVE_PHOTO_SIGHT_TREMBLE_META_DATA])) == E_ERR) {
242         MEDIA_ERR_LOG("write duration tag err");
243         return E_ERR;
244     }
245     off_t fileSize = GetFileSize(videoFd.Get());
246     if (fileSize <= 0) {
247         MEDIA_ERR_LOG("Failed to check fileSize: %{public}" PRId64, fileSize);
248         return E_ERR;
249     }
250     if (AddStringToFile(livePhotoFd, GetVideoInfoTag(static_cast<size_t>(fileSize) +
251         versonAndFrameNum.size() + extraData[LIVE_PHOTO_CINEMAGRAPH_INFO].size())) == E_ERR) {
252         MEDIA_ERR_LOG("write video info tag err");
253         return E_ERR;
254     }
255     return E_OK;
256 }
257 
GetExtraDataLen(const string & imagePath,const string & videoPath,uint32_t frameIndex,off_t & fileSize)258 int32_t MovingPhotoFileUtils::GetExtraDataLen(const string& imagePath, const string& videoPath,
259     uint32_t frameIndex, off_t& fileSize)
260 {
261     string absImagePath;
262     if (!PathToRealPath(imagePath, absImagePath)) {
263         MEDIA_ERR_LOG("file is not real path: %{private}s, errno: %{public}d", imagePath.c_str(), errno);
264         return E_HAS_FS_ERROR;
265     }
266     string extraDir = MovingPhotoFileUtils::GetMovingPhotoExtraDataDir(absImagePath);
267     string extraPath = MovingPhotoFileUtils::GetMovingPhotoExtraDataPath(absImagePath);
268     if (MediaFileUtils::IsFileValid(extraPath)) {
269         fileSize = GetFileSize(extraPath);
270         return E_OK;
271     }
272     CHECK_AND_RETURN_RET_LOG(
273         MediaFileUtils::CreateDirectory(extraDir), E_ERR, "Cannot create dir %{private}s, errno:%{public}d",
274         extraDir.c_str(), errno);
275     if (!MediaFileUtils::IsFileExists(extraPath) && MediaFileUtils::CreateAsset(extraPath) != E_OK) {
276         MEDIA_ERR_LOG("Failed to create file, path:%{private}s, errno:%{public}d", extraPath.c_str(), errno);
277         return E_HAS_FS_ERROR;
278     }
279     UniqueFd extraDataFd(open(extraPath.c_str(), O_WRONLY | O_TRUNC));
280     if (extraDataFd.Get() == E_ERR) {
281         MEDIA_ERR_LOG("failed to open extra data, errno:%{public}d", errno);
282         return E_ERR;
283     }
284     if (AddStringToFile(extraDataFd, GetVersionPositionTag(frameIndex, false)) == E_ERR) {
285         MEDIA_ERR_LOG("write version position tag err");
286         return E_ERR;
287     }
288     if (AddStringToFile(extraDataFd, GetDurationTag()) == E_ERR) {
289         MEDIA_ERR_LOG("write duration tag err");
290         return E_ERR;
291     }
292     if (AddStringToFile(extraDataFd, GetVideoInfoTag(GetFileSize(videoPath) + VERSION_TAG_LEN)) == E_ERR) {
293         MEDIA_ERR_LOG("write video info tag err");
294         return E_ERR;
295     }
296     fileSize = MIN_STANDARD_SIZE;
297     return E_OK;
298 }
299 
MergeFile(const UniqueFd & imageFd,const UniqueFd & videoFd,const UniqueFd & livePhotoFd,const string & extraPath,uint32_t frameIndex)300 static int32_t MergeFile(const UniqueFd& imageFd, const UniqueFd& videoFd, const UniqueFd& livePhotoFd,
301     const string& extraPath, uint32_t frameIndex)
302 {
303     if (WriteContentTofile(livePhotoFd, imageFd) == E_ERR) {
304         MEDIA_ERR_LOG("failed to sendfile from image file");
305         return E_ERR;
306     }
307     if (WriteContentTofile(livePhotoFd, videoFd) == E_ERR) {
308         MEDIA_ERR_LOG("failed to sendfile from video file");
309         return E_ERR;
310     }
311     if (WriteExtraData(extraPath, livePhotoFd, videoFd, frameIndex) == E_ERR) {
312         MEDIA_ERR_LOG("write cinemagraph info err");
313         return E_ERR;
314     }
315     return E_OK;
316 }
317 
GetFrameIndex(int64_t time,const int32_t fd)318 uint32_t MovingPhotoFileUtils::GetFrameIndex(int64_t time, const int32_t fd)
319 {
320     uint32_t index{0};
321     if (fd < 0) {
322         MEDIA_ERR_LOG("file is error");
323         return index;
324     }
325     std::shared_ptr<AVMetadataHelper> avMetadataHelper = AVMetadataHelperFactory::CreateAVMetadataHelper();
326     if (avMetadataHelper == nullptr) {
327         MEDIA_ERR_LOG("AV metadata helper is null");
328         return index;
329     }
330     if (avMetadataHelper->SetSource(fd, 0, static_cast<int64_t>(GetFileSize(fd)),
331         AV_META_USAGE_FRAME_INDEX_CONVERT) != E_OK) {
332         MEDIA_ERR_LOG("failed to set source");
333         return index;
334     }
335     if (avMetadataHelper->GetFrameIndexByTime(time, index) != E_OK) {
336         MEDIA_ERR_LOG("failed to get frame index");
337         return index;
338     }
339     return index;
340 }
341 
ConvertToLivePhoto(const string & movingPhotoImagepath,int64_t coverPosition,std::string & livePhotoPath,int32_t userId)342 int32_t MovingPhotoFileUtils::ConvertToLivePhoto(const string& movingPhotoImagepath, int64_t coverPosition,
343     std::string &livePhotoPath, int32_t userId)
344 {
345     string imagePath = AppendUserId(movingPhotoImagepath, userId);
346     string videoPath = GetMovingPhotoVideoPath(movingPhotoImagepath, userId);
347     string cacheDir = GetLivePhotoCacheDir(movingPhotoImagepath, userId);
348     string extraPath = GetMovingPhotoExtraDataPath(movingPhotoImagepath, userId);
349     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::CreateDirectory(cacheDir),
350         E_HAS_FS_ERROR, "Cannot create dir %{private}s, errno %{public}d", cacheDir.c_str(), errno);
351     string cachePath = GetLivePhotoCachePath(movingPhotoImagepath, userId);
352     if (MediaFileUtils::IsFileExists(cachePath)) {
353         livePhotoPath = cachePath;
354         return E_OK;
355     }
356     string absImagePath;
357     CHECK_AND_RETURN_RET_LOG(PathToRealPath(imagePath, absImagePath),
358         E_HAS_FS_ERROR, "file is not real path: %{private}s, errno: %{public}d", imagePath.c_str(), errno);
359     UniqueFd imageFd(open(absImagePath.c_str(), O_RDONLY));
360     if (imageFd.Get() == E_ERR) {
361         MEDIA_ERR_LOG("failed to open image file, errno: %{public}d", errno);
362         return E_ERR;
363     }
364     string absVideoPath;
365     CHECK_AND_RETURN_RET_LOG(PathToRealPath(videoPath, absVideoPath),
366         E_HAS_FS_ERROR, "file is not real path: %{private}s, errno: %{public}d", videoPath.c_str(), errno);
367     UniqueFd videoFd(open(absVideoPath.c_str(), O_RDONLY));
368     if (videoFd.Get() == E_ERR) {
369         MEDIA_ERR_LOG("failed to open video file, errno: %{public}d", errno);
370         return E_ERR;
371     }
372     if (MediaFileUtils::CreateAsset(cachePath) != E_OK) {
373         MEDIA_ERR_LOG("Failed to create file, path:%{private}s", cachePath.c_str());
374         return E_ERR;
375     }
376     string absCachePath;
377     CHECK_AND_RETURN_RET_LOG(PathToRealPath(cachePath, absCachePath),
378         E_HAS_FS_ERROR, "file is not real path: %{private}s, errno: %{public}d", cachePath.c_str(), errno);
379     UniqueFd livePhotoFd(open(absCachePath.c_str(), O_WRONLY | O_TRUNC));
380     if (livePhotoFd.Get() == E_ERR) {
381         MEDIA_ERR_LOG("failed to open live photo file, errno: %{public}d", errno);
382         return E_ERR;
383     }
384     if (MergeFile(imageFd, videoFd, livePhotoFd, extraPath, GetFrameIndex(coverPosition, videoFd.Get())) == E_ERR) {
385         MEDIA_ERR_LOG("failed to MergeFile file");
386         if (!MediaFileUtils::DeleteFile(absCachePath)) {
387             MEDIA_ERR_LOG("failed to delete cache file, errno: %{public}d", errno);
388         }
389         return E_ERR;
390     }
391     livePhotoPath = absCachePath;
392     return E_OK;
393 }
394 
ConvertToSourceLivePhoto(const string & movingPhotoImagePath,string & sourceLivePhotoPath,int32_t userId)395 int32_t MovingPhotoFileUtils::ConvertToSourceLivePhoto(const string& movingPhotoImagePath,
396     string& sourceLivePhotoPath, int32_t userId)
397 {
398     string sourceImagePath = GetSourceMovingPhotoImagePath(movingPhotoImagePath, userId);
399     string sourceVideoPath = GetSourceMovingPhotoVideoPath(movingPhotoImagePath, userId);
400     if (!MediaFileUtils::IsFileExists(sourceVideoPath)) {
401         sourceVideoPath = GetMovingPhotoVideoPath(movingPhotoImagePath, userId);
402     }
403     string extraDataPath = GetMovingPhotoExtraDataPath(movingPhotoImagePath, userId);
404     string cacheDir = GetLivePhotoCacheDir(movingPhotoImagePath, userId);
405     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::CreateDirectory(cacheDir), E_HAS_FS_ERROR,
406         "Cannot create dir %{private}s, errno %{public}d", cacheDir.c_str(), errno);
407     string sourceCachePath = GetSourceLivePhotoCachePath(movingPhotoImagePath, userId);
408     if (MediaFileUtils::IsFileExists(sourceCachePath)) {
409         sourceLivePhotoPath = sourceCachePath;
410         MEDIA_INFO_LOG("source live photo exists: %{private}s", sourceCachePath.c_str());
411         return E_OK;
412     }
413     string absSourceImagePath;
414     CHECK_AND_RETURN_RET_LOG(PathToRealPath(sourceImagePath, absSourceImagePath),
415         E_HAS_FS_ERROR, "file is not real path: %{private}s, errno: %{public}d", sourceImagePath.c_str(), errno);
416     UniqueFd imageFd(open(absSourceImagePath.c_str(), O_RDONLY));
417     CHECK_AND_RETURN_RET_LOG(imageFd.Get() >= 0, E_HAS_FS_ERROR,
418         "Failed to open source image:%{private}s, errno:%{public}d", sourceImagePath.c_str(), errno);
419     string absSourceVideoPath;
420     CHECK_AND_RETURN_RET_LOG(PathToRealPath(sourceVideoPath, absSourceVideoPath),
421         E_HAS_FS_ERROR, "file is not real path: %{private}s, errno: %{public}d", sourceVideoPath.c_str(), errno);
422     UniqueFd videoFd(open(absSourceVideoPath.c_str(), O_RDONLY));
423     CHECK_AND_RETURN_RET_LOG(videoFd.Get() >= 0, E_HAS_FS_ERROR,
424         "Failed to open source video:%{private}s, errno:%{public}d", sourceVideoPath.c_str(), errno);
425     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::CreateAsset(sourceCachePath) == E_OK, E_HAS_FS_ERROR,
426         "Failed to create source live photo:%{private}s, errno:%{public}d", sourceCachePath.c_str(), errno);
427     string absSourceCachePath;
428     CHECK_AND_RETURN_RET_LOG(PathToRealPath(sourceCachePath, absSourceCachePath),
429         E_HAS_FS_ERROR, "file is not real path: %{private}s, errno: %{public}d", sourceCachePath.c_str(), errno);
430     UniqueFd livePhotoFd(open(absSourceCachePath.c_str(), O_WRONLY | O_TRUNC));
431     CHECK_AND_RETURN_RET_LOG(livePhotoFd.Get() >= 0, E_HAS_FS_ERROR,
432         "Failed to open source live photo:%{private}s, errno:%{public}d", absSourceCachePath.c_str(), errno);
433 
434     if (MergeFile(imageFd, videoFd, livePhotoFd, extraDataPath, 0) != E_OK) {
435         MEDIA_ERR_LOG("Failed to merge file of sourve live photo");
436         return E_ERR;
437     }
438     sourceLivePhotoPath = absSourceCachePath;
439     return E_OK;
440 }
441 
IsLivePhoto(const string & path)442 bool MovingPhotoFileUtils::IsLivePhoto(const string& path)
443 {
444     string absPath;
445     if (!PathToRealPath(path, absPath)) {
446         MEDIA_ERR_LOG("file is not real path: %{private}s, errno: %{public}d", path.c_str(), errno);
447         return false;
448     }
449     UniqueFd livePhotoFd(open(absPath.c_str(), O_RDONLY));
450     if (GetFileSize(livePhotoFd.Get()) < LIVE_TAG_LEN) {
451         MEDIA_ERR_LOG("failed to get file size errno: %{public}d", errno);
452         return false;
453     }
454     off_t offset = lseek(livePhotoFd.Get(), -LIVE_TAG_LEN, SEEK_END);
455     if (offset == E_ERR) {
456         MEDIA_ERR_LOG("failed to lseek file errno: %{public}d", errno);
457         return false;
458     }
459     char buffer[LIVE_TAG_LEN + 1];
460     ssize_t bytesRead = read(livePhotoFd.Get(), buffer, LIVE_TAG_LEN);
461     if (bytesRead == E_ERR) {
462         MEDIA_ERR_LOG("failed to read file errno: %{public}d", errno);
463         return false;
464     }
465     buffer[bytesRead] = '\0';
466     for (uint16_t i = 0; i < LIVE_TAG.size(); i++) {
467         if (LIVE_TAG[i] != buffer[i]) {
468             return false;
469         }
470     }
471     return true;
472 }
473 
SendLivePhoto(const UniqueFd & livePhotoFd,const string & destPath,int64_t sizeToSend,off_t & offset)474 static int32_t SendLivePhoto(const UniqueFd &livePhotoFd, const string &destPath, int64_t sizeToSend, off_t &offset)
475 {
476     struct stat64 statSrc {};
477     CHECK_AND_RETURN_RET_LOG(livePhotoFd.Get() >= 0, livePhotoFd.Get(), "Failed to check src fd of live photo");
478     CHECK_AND_RETURN_RET_LOG(fstat64(livePhotoFd.Get(), &statSrc) == 0, E_HAS_FS_ERROR,
479         "Failed to get file state of live photo, errno = %{public}d", errno);
480     off_t totalSize = statSrc.st_size;
481     CHECK_AND_RETURN_RET_LOG(sizeToSend <= totalSize - offset, E_INVALID_LIVE_PHOTO, "Failed to check sizeToSend");
482 
483     if (!MediaFileUtils::IsFileExists(destPath) && MediaFileUtils::CreateAsset(destPath) != E_OK) {
484         MEDIA_ERR_LOG("Failed to create file, path:%{private}s", destPath.c_str());
485         return E_HAS_FS_ERROR;
486     }
487     string absDestPath;
488     if (!PathToRealPath(destPath, absDestPath)) {
489         MEDIA_ERR_LOG("file is not real path: %{private}s, errno: %{public}d", destPath.c_str(), errno);
490         return E_HAS_FS_ERROR;
491     }
492     UniqueFd destFd(open(absDestPath.c_str(), O_WRONLY));
493     if (destFd.Get() < 0) {
494         MEDIA_ERR_LOG("Failed to open dest path:%{private}s, errno:%{public}d", absDestPath.c_str(), errno);
495         return destFd.Get();
496     }
497 
498     while (sizeToSend > 0) {
499         ssize_t sent = sendfile(destFd.Get(), livePhotoFd.Get(), &offset, sizeToSend);
500         if (sent < 0) {
501             MEDIA_ERR_LOG("Failed to sendfile with errno=%{public}d", errno);
502             return sent;
503         }
504         sizeToSend -= sent;
505     }
506     return E_OK;
507 }
508 
IsValidHexInteger(const string & hexStr)509 static bool IsValidHexInteger(const string &hexStr)
510 {
511     constexpr int32_t HEX_INT_LENGTH = 8;
512     if (hexStr.length() > HEX_INT_LENGTH) {
513         return false;
514     }
515     uint64_t num = stoull(hexStr, nullptr, HEX_BASE);
516     if (num > numeric_limits<uint32_t>::max()) {
517         return false;
518     }
519     return true;
520 }
521 
GetExtraDataSize(const UniqueFd & livePhotoFd,int64_t & extraDataSize,int64_t maxFileSize)522 static int32_t GetExtraDataSize(const UniqueFd &livePhotoFd, int64_t &extraDataSize, int64_t maxFileSize)
523 {
524     struct stat64 st;
525     CHECK_AND_RETURN_RET_LOG(fstat64(livePhotoFd.Get(), &st) == 0, E_HAS_FS_ERROR,
526         "Failed to get file state of live photo, errno:%{public}d", errno);
527     int64_t totalSize = st.st_size;
528     CHECK_AND_RETURN_RET_LOG(totalSize > MIN_STANDARD_SIZE, E_INVALID_LIVE_PHOTO,
529         "Failed to check live photo, total size is %{public}" PRId64, totalSize);
530 
531     char versionTag[VERSION_TAG_LEN + 1] = {0};
532     CHECK_AND_RETURN_RET_LOG(lseek(livePhotoFd.Get(), -MIN_STANDARD_SIZE, SEEK_END) != -1, E_HAS_FS_ERROR,
533         "Failed to lseek version tag, errno:%{public}d", errno);
534     CHECK_AND_RETURN_RET_LOG(read(livePhotoFd.Get(), versionTag, VERSION_TAG_LEN) != -1, E_HAS_FS_ERROR,
535         "Failed to read version tag, errno:%{public}d", errno);
536 
537     uint32_t version = 0;
538     uint32_t frameIndex = 0;
539     bool hasCinemagraph = false;
540     int32_t ret = MovingPhotoFileUtils::GetVersionAndFrameNum(versionTag, version, frameIndex, hasCinemagraph);
541     if (ret != E_OK) { // not standard version tag
542         extraDataSize = LIVE_TAG_LEN + PLAY_INFO_LEN;
543         return E_OK;
544     }
545 
546     if (!hasCinemagraph) { // extra data without cinemagraph
547         extraDataSize = MIN_STANDARD_SIZE;
548         return E_OK;
549     }
550 
551     // extra data with cinemagraph
552     CHECK_AND_RETURN_RET_LOG(totalSize > MIN_STANDARD_SIZE + CINEMAGRAPH_INFO_SIZE_LEN, E_INVALID_LIVE_PHOTO,
553         "Failed to check live photo with cinemagraph, total size is %{public}" PRId64, totalSize);
554     char cinemagraphSize[CINEMAGRAPH_INFO_SIZE_LEN] = {0};
555     CHECK_AND_RETURN_RET_LOG(lseek(livePhotoFd.Get(), -(MIN_STANDARD_SIZE + CINEMAGRAPH_INFO_SIZE_LEN), SEEK_END) != -1,
556         E_HAS_FS_ERROR, "Failed to lseek cinemagraph size, errno:%{public}d", errno);
557     CHECK_AND_RETURN_RET_LOG(read(livePhotoFd.Get(), cinemagraphSize, CINEMAGRAPH_INFO_SIZE_LEN) != -1, E_HAS_FS_ERROR,
558         "Failed to read cinemagraph size, errno:%{public}d", errno);
559     stringstream cinemagraphSizeStream;
560     for (int32_t i = 0; i < CINEMAGRAPH_INFO_SIZE_LEN; i++) {
561         cinemagraphSizeStream << hex << static_cast<int32_t>(cinemagraphSize[i]);
562     }
563     if (!IsValidHexInteger(cinemagraphSizeStream.str())) {
564         extraDataSize = MIN_STANDARD_SIZE;
565         MEDIA_WARN_LOG("hex string over int max %{public}s", cinemagraphSizeStream.str().c_str());
566         return E_OK;
567     }
568     extraDataSize = MIN_STANDARD_SIZE + std::stoi(cinemagraphSizeStream.str(), 0, HEX_BASE);
569     if (extraDataSize >= maxFileSize) {
570         extraDataSize = MIN_STANDARD_SIZE;
571         MEDIA_WARN_LOG("extra data size over total file size %{public}" PRId64, extraDataSize);
572     }
573     return E_OK;
574 }
575 
ConvertToMovingPhoto(const std::string & livePhotoPath,const string & movingPhotoImagePath,const string & movingPhotoVideoPath,const string & extraDataPath)576 int32_t MovingPhotoFileUtils::ConvertToMovingPhoto(const std::string &livePhotoPath, const string &movingPhotoImagePath,
577     const string &movingPhotoVideoPath, const string &extraDataPath)
578 {
579     string absLivePhotoPath;
580     if (!PathToRealPath(livePhotoPath, absLivePhotoPath)) {
581         MEDIA_ERR_LOG("file is not real path: %{private}s, errno: %{public}d", livePhotoPath.c_str(), errno);
582         return E_HAS_FS_ERROR;
583     }
584     CHECK_AND_RETURN_RET_LOG(livePhotoPath.compare(movingPhotoVideoPath) != 0 &&
585         livePhotoPath.compare(extraDataPath) != 0, E_INVALID_VALUES, "Failed to check dest path");
586     UniqueFd livePhotoFd(open(absLivePhotoPath.c_str(), O_RDONLY));
587     CHECK_AND_RETURN_RET_LOG(livePhotoFd.Get() >= 0, E_HAS_FS_ERROR,
588         "Failed to open live photo:%{private}s, errno:%{public}d", absLivePhotoPath.c_str(), errno);
589 
590     struct stat64 st;
591     CHECK_AND_RETURN_RET_LOG(fstat64(livePhotoFd.Get(), &st) == 0, E_HAS_FS_ERROR,
592         "Failed to get file state of live photo, errno:%{public}d", errno);
593     int64_t totalSize = st.st_size;
594     CHECK_AND_RETURN_RET_LOG(totalSize > MIN_STANDARD_SIZE, E_INVALID_LIVE_PHOTO,
595         "Failed to check live photo, total size is %{public}" PRId64, totalSize);
596     char liveTag[LIVE_TAG_LEN + 1] = {0};
597     CHECK_AND_RETURN_RET_LOG(lseek(livePhotoFd.Get(), -LIVE_TAG_LEN, SEEK_END) != -1, E_HAS_FS_ERROR,
598         "Failed to lseek live tag, errno:%{public}d", errno);
599     CHECK_AND_RETURN_RET_LOG(read(livePhotoFd.Get(), liveTag, LIVE_TAG_LEN) != -1, E_HAS_FS_ERROR,
600         "Failed to read live tag, errno:%{public}d", errno);
601     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::StartsWith(liveTag, LIVE_TAG), E_INVALID_VALUES, "Invalid live photo");
602 
603     int64_t liveSize = atoi(liveTag + LIVE_TAG.length());
604     int64_t imageSize = totalSize - liveSize - LIVE_TAG_LEN - PLAY_INFO_LEN;
605     int64_t extraDataSize = 0;
606     int32_t err = GetExtraDataSize(livePhotoFd, extraDataSize, totalSize - imageSize);
607     CHECK_AND_RETURN_RET_LOG(err == E_OK, E_INVALID_LIVE_PHOTO,
608         "Failed to get size of extra data, err:%{public}" PRId64, extraDataSize);
609     int64_t videoSize = totalSize - imageSize - extraDataSize;
610     CHECK_AND_RETURN_RET_LOG(imageSize > 0 && videoSize > 0, E_INVALID_LIVE_PHOTO,
611         "Failed to check live photo, image size:%{public}" PRId64 "video size:%{public}" PRId64, imageSize, videoSize);
612     off_t offset = 0;
613     bool isSameImagePath = livePhotoPath.compare(movingPhotoImagePath) == 0;
614     string tempImagePath = isSameImagePath ? movingPhotoImagePath + ".temp" : movingPhotoImagePath;
615     CHECK_AND_RETURN_RET_LOG((err = SendLivePhoto(livePhotoFd, tempImagePath, imageSize, offset)) == E_OK, err,
616         "Failed to copy image of live photo");
617     CHECK_AND_RETURN_RET_LOG((err = SendLivePhoto(livePhotoFd, movingPhotoVideoPath, videoSize, offset)) == E_OK, err,
618         "Failed to copy video of live photo");
619     if (!extraDataPath.empty()) {
620         CHECK_AND_RETURN_RET_LOG((err = SendLivePhoto(livePhotoFd, extraDataPath, extraDataSize, offset)) == E_OK, err,
621             "Failed to copy extra data of live photo");
622     }
623     if (isSameImagePath) {
624         err = rename(tempImagePath.c_str(), movingPhotoImagePath.c_str());
625         CHECK_AND_RETURN_RET_LOG(err == E_OK, err, "Failed to rename image:%{public}d, errno:%{public}d", err, errno);
626     }
627     return E_OK;
628 }
629 
GetMovingPhotoCoverPosition(const UniqueFd & uniqueFd,const int64_t size,const uint32_t frameIndex,uint64_t & coverPosition,int32_t scene)630 static int32_t GetMovingPhotoCoverPosition(const UniqueFd &uniqueFd, const int64_t size,
631     const uint32_t frameIndex, uint64_t &coverPosition, int32_t scene)
632 {
633     MediaLibraryTracer tracer;
634     tracer.Start("AVMetadataHelper");
635     shared_ptr<AVMetadataHelper> helper = AVMetadataHelperFactory::CreateAVMetadataHelper();
636     if (helper == nullptr) {
637         MEDIA_ERR_LOG("AV metadata helper is null");
638         return E_AVMETADATA;
639     }
640 
641     // notify media_service clone event.
642     if (scene == Scene::AV_META_SCENE_CLONE) {
643         helper->SetScene(static_cast<Scene>(scene));
644     }
645     int32_t err = helper->SetSource(uniqueFd.Get(), 0, size, AV_META_USAGE_FRAME_INDEX_CONVERT);
646     tracer.Finish();
647     if (err != 0) {
648         MEDIA_ERR_LOG("SetSource failed for the given fd, err = %{public}d", err);
649         return E_AVMETADATA;
650     }
651 
652     tracer.Start("AVMetadataHelper->GetTimeByFrameIndex");
653     err = helper->GetTimeByFrameIndex(frameIndex, coverPosition);
654     tracer.Finish();
655     if (err != 0) {
656         MEDIA_ERR_LOG("Failed to GetTimeByFrameIndex, err = %{public}d", err);
657         return E_AVMETADATA;
658     }
659     return E_OK;
660 }
661 
GetCoverPosition(const std::string & videoPath,const uint32_t frameIndex,uint64_t & coverPosition,int32_t scene)662 int32_t MovingPhotoFileUtils::GetCoverPosition(const std::string &videoPath, const uint32_t frameIndex,
663     uint64_t &coverPosition, int32_t scene)
664 {
665     string absVideoPath;
666     if (!PathToRealPath(videoPath, absVideoPath)) {
667         MEDIA_ERR_LOG("Failed to get real path: %{private}s, errno: %{public}d", videoPath.c_str(), errno);
668         return E_HAS_FS_ERROR;
669     }
670 
671     UniqueFd uniqueFd(open(absVideoPath.c_str(), O_RDONLY));
672     if (uniqueFd.Get() < 0) {
673         MEDIA_ERR_LOG("Failed to open %{private}s, errno: %{public}d", absVideoPath.c_str(), errno);
674         return E_HAS_FS_ERROR;
675     }
676     struct stat64 st;
677     if (fstat64(uniqueFd.Get(), &st) != 0) {
678         MEDIA_ERR_LOG("Failed to get file state, errno: %{public}d", errno);
679         return E_HAS_FS_ERROR;
680     }
681     return GetMovingPhotoCoverPosition(uniqueFd, st.st_size, frameIndex, coverPosition, scene);
682 }
683 
EndsWith(const string & str,const string & endStr)684 bool EndsWith(const string &str, const string &endStr)
685 {
686     if (str.length() < endStr.length()) {
687         return false;
688     }
689     return str.rfind(endStr) == str.length() - endStr.length();
690 }
691 
GetVersionAndFrameNum(const string & tag,uint32_t & version,uint32_t & frameIndex,bool & hasCinemagraphInfo)692 int32_t MovingPhotoFileUtils::GetVersionAndFrameNum(const string &tag,
693     uint32_t &version, uint32_t &frameIndex, bool &hasCinemagraphInfo)
694 {
695     static const string VERSION_TAG_REGEX = "^[vV](\\d+)_[fF](\\d+).*";
696     std::regex pattern(VERSION_TAG_REGEX);
697     std::smatch result;
698     if (!std::regex_search(tag, result, pattern)) {
699         MEDIA_WARN_LOG("tag is not standard version tag: %{public}s", tag.c_str());
700         return E_INVALID_VALUES;
701     }
702 
703     constexpr int32_t VERSION_POSITION = 1;
704     constexpr int32_t FRAME_INDEX_POSITION = 2;
705     version = static_cast<uint32_t>(atoi(result[VERSION_POSITION].str().c_str()));
706     frameIndex = static_cast<uint32_t>(atoi(result[FRAME_INDEX_POSITION].str().c_str()));
707     size_t blankIndex = tag.find_first_of(' ');
708     string tagTrimmed = tag;
709     if (blankIndex != string::npos) {
710         tagTrimmed = tagTrimmed.substr(0, blankIndex);
711     }
712     hasCinemagraphInfo = EndsWith(tagTrimmed, "_c") || EndsWith(tagTrimmed, "_C");
713     return E_OK;
714 }
715 
GetVersionAndFrameNum(int32_t fd,uint32_t & version,uint32_t & frameIndex,bool & hasCinemagraphInfo)716 int32_t MovingPhotoFileUtils::GetVersionAndFrameNum(int32_t fd,
717     uint32_t &version, uint32_t &frameIndex, bool &hasCinemagraphInfo)
718 {
719     CHECK_AND_RETURN_RET_LOG(fd >= 0, E_HAS_FS_ERROR, "Failed to check fd, errno:%{public}d", errno);
720     struct stat64 st;
721     CHECK_AND_RETURN_RET_LOG(fstat64(fd, &st) == 0, E_HAS_FS_ERROR,
722         "Failed to get file state, errno:%{public}d", errno);
723     int64_t totalSize = st.st_size;
724     CHECK_AND_RETURN_RET_LOG(totalSize >= MIN_STANDARD_SIZE, E_INVALID_LIVE_PHOTO,
725         "Failed to fetch version tag, total size is %{public}" PRId64, totalSize);
726 
727     char versionTag[VERSION_TAG_LEN + 1] = {0};
728     CHECK_AND_RETURN_RET_LOG(lseek(fd, -MIN_STANDARD_SIZE, SEEK_END) != -1, E_HAS_FS_ERROR,
729         "Failed to lseek version tag, errno:%{public}d", errno);
730     CHECK_AND_RETURN_RET_LOG(read(fd, versionTag, VERSION_TAG_LEN) != -1, E_HAS_FS_ERROR,
731         "Failed to read version tag, errno:%{public}d", errno);
732     return MovingPhotoFileUtils::GetVersionAndFrameNum(versionTag, version, frameIndex, hasCinemagraphInfo);
733 }
734 
GetMovingPhotoVideoPath(const string & imagePath,int32_t userId)735 string MovingPhotoFileUtils::GetMovingPhotoVideoPath(const string &imagePath, int32_t userId)
736 {
737     return MediaFileUtils::GetMovingPhotoVideoPath(AppendUserId(imagePath, userId));
738 }
739 
GetMovingPhotoExtraDataDir(const string & imagePath,int32_t userId)740 string MovingPhotoFileUtils::GetMovingPhotoExtraDataDir(const string &imagePath, int32_t userId)
741 {
742     if (imagePath.length() < ROOT_MEDIA_DIR.length() || !MediaFileUtils::StartsWith(imagePath, ROOT_MEDIA_DIR)) {
743         return "";
744     }
745     return AppendUserId(MEDIA_EXTRA_DATA_DIR, userId) + imagePath.substr(ROOT_MEDIA_DIR.length());
746 }
747 
GetMovingPhotoExtraDataPath(const string & imagePath,int32_t userId)748 string MovingPhotoFileUtils::GetMovingPhotoExtraDataPath(const string &imagePath, int32_t userId)
749 {
750     string parentPath = GetMovingPhotoExtraDataDir(imagePath, userId);
751     if (parentPath.empty()) {
752         return "";
753     }
754     return parentPath + "/extraData";
755 }
756 
GetSourceMovingPhotoImagePath(const string & imagePath,int32_t userId)757 string MovingPhotoFileUtils::GetSourceMovingPhotoImagePath(const string& imagePath, int32_t userId)
758 {
759     return GetEditDataSourcePath(imagePath, userId);
760 }
761 
GetSourceMovingPhotoVideoPath(const string & imagePath,int32_t userId)762 string MovingPhotoFileUtils::GetSourceMovingPhotoVideoPath(const string& imagePath, int32_t userId)
763 {
764     return GetMovingPhotoVideoPath(GetSourceMovingPhotoImagePath(imagePath, userId));
765 }
766 
GetLivePhotoCacheDir(const string & imagePath,int32_t userId)767 string MovingPhotoFileUtils::GetLivePhotoCacheDir(const string &imagePath, int32_t userId)
768 {
769     if (imagePath.length() < ROOT_MEDIA_DIR.length() || !MediaFileUtils::StartsWith(imagePath, ROOT_MEDIA_DIR)) {
770         return "";
771     }
772     return AppendUserId(MEDIA_CACHE_DIR, userId) + imagePath.substr(ROOT_MEDIA_DIR.length());
773 }
774 
GetLivePhotoCachePath(const string & imagePath,int32_t userId)775 string MovingPhotoFileUtils::GetLivePhotoCachePath(const string &imagePath, int32_t userId)
776 {
777     string parentPath = GetLivePhotoCacheDir(imagePath, userId);
778     if (parentPath.empty()) {
779         return "";
780     }
781     return parentPath + "/livePhoto." + MediaFileUtils::GetExtensionFromPath(imagePath);
782 }
783 
GetSourceLivePhotoCachePath(const string & imagePath,int32_t userId)784 string MovingPhotoFileUtils::GetSourceLivePhotoCachePath(const string& imagePath, int32_t userId)
785 {
786     string parentPath = GetLivePhotoCacheDir(imagePath, userId);
787     if (parentPath.empty()) {
788         return "";
789     }
790     return parentPath + "/sourceLivePhoto." + MediaFileUtils::GetExtensionFromPath(imagePath);
791 }
792 
IsMovingPhoto(int32_t subtype,int32_t effectMode,int32_t originalSubtype)793 bool MovingPhotoFileUtils::IsMovingPhoto(int32_t subtype, int32_t effectMode, int32_t originalSubtype)
794 {
795     return subtype == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) ||
796            effectMode == static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY) ||
797            IsGraffiti(subtype, originalSubtype);
798 }
799 
IsGraffiti(int32_t subtype,int32_t originalSubtype)800 bool MovingPhotoFileUtils::IsGraffiti(int32_t subtype, int32_t originalSubtype)
801 {
802     return subtype == static_cast<int32_t>(PhotoSubType::DEFAULT) &&
803            originalSubtype == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO);
804 }
805 } // namespace OHOS::Media