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