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