• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 #define MLOG_TAG "Scanner"
16 
17 #include "metadata_extractor.h"
18 
19 #include <charconv>
20 #include <fcntl.h>
21 #include "directory_ex.h"
22 #include "hitrace_meter.h"
23 #include "media_exif.h"
24 #include "media_file_utils.h"
25 #include "media_log.h"
26 #include "medialibrary_db_const.h"
27 #include "medialibrary_errno.h"
28 #include "medialibrary_tracer.h"
29 #include "media_image_framework_utils.h"
30 #include "meta.h"
31 #include "meta_key.h"
32 #include "nlohmann/json.hpp"
33 #include "sandbox_helper.h"
34 #include "shooting_mode_column.h"
35 #include "moving_photo_file_utils.h"
36 #include "directory_ex.h"
37 
38 namespace OHOS {
39 namespace Media {
40 using namespace std;
41 
42 const double DEGREES2MINUTES = 60.0;
43 const double DEGREES2SECONDS = 3600.0;
44 constexpr int32_t OFFSET_NUM = 2;
45 constexpr int32_t HOURSTOSECOND = 60 * 60;
46 constexpr int32_t MINUTESTOSECOND = 60;
47 const string ZEROTIMESTRING = "0000:00:00 00:00:00";
48 
49 template <class Type>
stringToNum(const string & str)50 static Type stringToNum(const string &str)
51 {
52     std::istringstream iss(str);
53     Type num;
54     iss >> num;
55     return num;
56 }
57 // LCOV_EXCL_START
IsMovingPhoto(unique_ptr<Metadata> & data)58 static bool IsMovingPhoto(unique_ptr<Metadata> &data)
59 {
60     return data->GetPhotoSubType() == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) ||
61         data->GetMovingPhotoEffectMode() == static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY);
62 }
63 
GetLongitudeLatitude(string inputStr,const string & ref="")64 double GetLongitudeLatitude(string inputStr, const string& ref = "")
65 {
66     auto pos = inputStr.find(',');
67     CHECK_AND_RETURN_RET(pos != string::npos, 0);
68 
69     double ret = stringToNum<double>(inputStr.substr(0, pos));
70     inputStr = inputStr.substr(pos + OFFSET_NUM);
71     pos = inputStr.find(',');
72     CHECK_AND_RETURN_RET(pos != string::npos, 0);
73 
74     ret += stringToNum<double>(inputStr.substr(0, pos)) / DEGREES2MINUTES;
75     inputStr = inputStr.substr(pos + OFFSET_NUM);
76     ret += stringToNum<double>(inputStr) / DEGREES2SECONDS;
77     return (ref.compare("W") == 0 || ref.compare("S") == 0) ? -ret : ret;
78 }
79 
80 /* used for video */
convertTimeStr2TimeStamp(string & timeStr)81 static time_t convertTimeStr2TimeStamp(string &timeStr)
82 {
83     struct tm timeinfo;
84     strptime(timeStr.c_str(), "%Y-%m-%d %H:%M:%S",  &timeinfo);
85     time_t timeStamp = mktime(&timeinfo);
86     return timeStamp;
87 }
88 
convertUTCTimeInformat(const string & timeStr,const string & format)89 static time_t convertUTCTimeInformat(const string &timeStr, const string &format)
90 {
91     MEDIA_DEBUG_LOG("convertUTCTimeInformat time:%{public}s format:%{public}s", timeStr.c_str(), format.c_str());
92     struct tm timeinfo;
93     strptime(timeStr.c_str(), format.c_str(), &timeinfo);
94     time_t convertOnceTime = mktime(&timeinfo);
95     time_t convertTwiceTime = mktime(gmtime(&convertOnceTime));
96 
97     bool cond = (convertOnceTime == -1 || convertTwiceTime == -1);
98     CHECK_AND_RETURN_RET(!cond, 0);
99 
100     time_t offset = convertOnceTime - convertTwiceTime;
101     time_t utcTimeStamp = convertOnceTime + offset;
102     MEDIA_DEBUG_LOG("convertUTCTimeInformat result utcTimeStamp:%{public}ld", static_cast<long>(utcTimeStamp));
103     return utcTimeStamp;
104 }
105 
106 /* used for Image */
convertTimeStrToTimeStamp(string & timeStr)107 static time_t convertTimeStrToTimeStamp(string &timeStr)
108 {
109     struct tm timeinfo;
110     strptime(timeStr.c_str(), "%Y:%m:%d %H:%M:%S",  &timeinfo);
111     time_t timeStamp = mktime(&timeinfo);
112     return timeStamp;
113 }
114 
convertUTCTimeStrToTimeStamp(string & timeStr)115 static time_t convertUTCTimeStrToTimeStamp(string &timeStr)
116 {
117     return convertUTCTimeInformat(timeStr, "%Y:%m:%d %H:%M:%S");
118 }
119 
offsetTimeToSeconds(const string & offsetStr,int32_t & offsetTime)120 static int32_t offsetTimeToSeconds(const string& offsetStr, int32_t& offsetTime)
121 {
122     char sign = offsetStr[0];
123     const int32_t offsetTimeSize = 6;
124     if (offsetStr.size() != offsetTimeSize || (sign != '+' && sign != '-')) {
125         MEDIA_WARN_LOG("Invalid offset format, Offset string must be in format +HH:MM or -HH:MM");
126         return E_ERR;
127     }
128 
129     const int32_t colonPosition = 3;
130     for (size_t i = 1; i < offsetStr.size(); i++) {
131         if (i == colonPosition) {
132             continue;
133         }
134         if (!isdigit(offsetStr[i])) {
135             MEDIA_WARN_LOG("Invalid hour or minute format");
136             return E_ERR;
137         }
138     }
139     int32_t hours = stoi(offsetStr.substr(1, 2));
140     int32_t minutes = stoi(offsetStr.substr(colonPosition + 1, 2));
141 
142     int totalSeconds = hours * HOURSTOSECOND + minutes * MINUTESTOSECOND;
143     offsetTime = (sign == '-') ? totalSeconds : -totalSeconds;
144     MEDIA_DEBUG_LOG("get offset success offsetTime=%{public}d", offsetTime);
145     return E_OK;
146 }
147 
SetSubSecondTime(const unique_ptr<ImageSource> & imageSource,const std::string & key,int64_t & timeStamp)148 static void SetSubSecondTime(const unique_ptr<ImageSource> &imageSource, const std::string &key, int64_t &timeStamp)
149 {
150     string subTimeStr;
151     uint32_t err = imageSource->GetImagePropertyString(0, key, subTimeStr);
152     if (err == E_OK && !subTimeStr.empty()) {
153         const size_t millisecondPrecision = 3;
154         const size_t subTimeSize = std::min(millisecondPrecision, subTimeStr.size());
155         int32_t subTime = 0;
156         auto [ptr, ec] = std::from_chars(subTimeStr.data(), subTimeStr.data() + subTimeSize, subTime);
157         if (ec == std::errc() && ptr == subTimeStr.data() + subTimeSize) {
158             MEDIA_DEBUG_LOG("subTime:%{public}d from %{public}s in exif", subTime, key.c_str());
159             timeStamp += subTime;
160         } else {
161             MEDIA_WARN_LOG("Invalid subTime format:%{public}s", subTimeStr.c_str());
162         }
163     }
164 }
165 
ExtractDetailTimeMetadata(const unique_ptr<ImageSource> & imageSource,unique_ptr<Metadata> & data)166 static void ExtractDetailTimeMetadata(const unique_ptr<ImageSource> &imageSource, unique_ptr<Metadata> &data)
167 {
168     string timeString;
169     uint32_t err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_DATE_TIME_ORIGINAL, timeString);
170     if (err == E_OK && !timeString.empty() && timeString.compare(ZEROTIMESTRING) != 0) {
171         data->SetDetailTime(timeString);
172         MEDIA_DEBUG_LOG("Set detail_time from DateTimeOriginal in exif");
173         return;
174     }
175     if (data->GetForAdd()) {
176         err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_DATE_TIME, timeString);
177         if (err == E_OK && !timeString.empty() && timeString.compare(ZEROTIMESTRING) != 0) {
178             data->SetDetailTime(timeString);
179             MEDIA_DEBUG_LOG("Set detail_time from DateTime in exif");
180             return;
181         }
182     }
183     if (data->GetDetailTime().empty()) {
184         int64_t dateTaken = data->GetDateTaken() / MSEC_TO_SEC;
185         data->SetDetailTime(MediaFileUtils::StrCreateTime(PhotoColumn::PHOTO_DETAIL_TIME_FORMAT, dateTaken));
186     }
187 }
188 
GetShootingTimeStampByExif(const unique_ptr<ImageSource> & imageSource)189 static int64_t GetShootingTimeStampByExif(const unique_ptr<ImageSource> &imageSource)
190 {
191     string timeString;
192     int64_t timeStamp = 0;
193     int32_t offsetTime = 0;
194     string offsetString;
195     uint32_t err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_DATE_TIME_ORIGINAL, timeString);
196     if (err == E_OK && !timeString.empty() && timeString.compare(ZEROTIMESTRING) != 0) {
197         err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_OFFSET_TIME_ORIGINAL, offsetString);
198         if (err == E_OK && offsetTimeToSeconds(offsetString, offsetTime) == E_OK) {
199             timeStamp = (convertUTCTimeStrToTimeStamp(timeString) + offsetTime) * MSEC_TO_SEC;
200             MEDIA_DEBUG_LOG("Get timeStamp from DateTimeOriginal and OffsetTimeOriginal in exif");
201         } else {
202             timeStamp = (convertTimeStrToTimeStamp(timeString)) * MSEC_TO_SEC;
203             MEDIA_DEBUG_LOG("Get timeStamp from DateTimeOriginal in exif");
204         }
205         if (timeStamp > 0) {
206             SetSubSecondTime(imageSource, PHOTO_DATA_IMAGE_SUBSEC_TIME_ORIGINAL, timeStamp);
207             MEDIA_DEBUG_LOG("OriginalTimeStamp:%{public}ld in exif", static_cast<long>(timeStamp));
208             return timeStamp;
209         }
210     }
211 
212     string dateString;
213     err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_DATE_STAMP, dateString);
214     if (err == E_OK && !dateString.empty()) {
215         err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_TIME_STAMP, timeString);
216         string fullTimeString = dateString + " " + timeString;
217         if (err == E_OK && !timeString.empty() && fullTimeString.compare(ZEROTIMESTRING) != 0) {
218             timeStamp = convertUTCTimeStrToTimeStamp(fullTimeString) * MSEC_TO_SEC;
219             if (timeStamp > 0) {
220                 SetSubSecondTime(imageSource, PHOTO_DATA_IMAGE_SUBSEC_TIME_ORIGINAL, timeStamp);
221                 MEDIA_DEBUG_LOG("GPSTimeStamp:%{public}ld in exif", static_cast<long>(timeStamp));
222                 return timeStamp;
223             }
224         }
225     }
226     return timeStamp;
227 }
228 
GetModifiedTimeStampByExif(const unique_ptr<ImageSource> & imageSource)229 static int64_t GetModifiedTimeStampByExif(const unique_ptr<ImageSource> &imageSource)
230 {
231     string dateString;
232     string timeString;
233     int64_t timeStamp = 0;
234     int32_t offsetTime = 0;
235     string offsetString;
236     uint32_t err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_DATE_TIME, timeString);
237     if (err == E_OK && !timeString.empty() && timeString.compare(ZEROTIMESTRING) != 0) {
238         err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_OFFSET_TIME, offsetString);
239         if (err == E_OK && offsetTimeToSeconds(offsetString, offsetTime) == E_OK) {
240             timeStamp = (convertUTCTimeStrToTimeStamp(timeString) + offsetTime) * MSEC_TO_SEC;
241             MEDIA_DEBUG_LOG("Get timeStamp from DateTime and OffsetTime in exif");
242         } else {
243             timeStamp = (convertTimeStrToTimeStamp(timeString)) * MSEC_TO_SEC;
244             MEDIA_DEBUG_LOG("Get timeStamp from DateTime in exif");
245         }
246         if (timeStamp > 0) {
247             SetSubSecondTime(imageSource, PHOTO_DATA_IMAGE_SUBSEC_TIME, timeStamp);
248             MEDIA_DEBUG_LOG("TimeStamp:%{public}ld in exif", static_cast<long>(timeStamp));
249             return timeStamp;
250         }
251     }
252     return timeStamp;
253 }
254 
ExtractDateTakenMetadata(const unique_ptr<ImageSource> & imageSource,unique_ptr<Metadata> & data)255 static void ExtractDateTakenMetadata(const unique_ptr<ImageSource> &imageSource, unique_ptr<Metadata> &data)
256 {
257     int64_t shootingTimeStamp = GetShootingTimeStampByExif(imageSource);
258     if (shootingTimeStamp > 0) {
259         data->SetDateTaken(shootingTimeStamp);
260         MEDIA_INFO_LOG("forAdd:%{public}d, shootingTimeStamp:%{public}ld",
261             data->GetForAdd(),
262             static_cast<long>(shootingTimeStamp));
263         return;
264     }
265     int64_t dateTaken = data->GetDateTaken();
266     if (dateTaken > 0 && !data->GetForAdd()) {
267         MEDIA_WARN_LOG("Set date_taken use old date_taken: %{public}ld", static_cast<long>(dateTaken));
268         return;
269     }
270     int64_t dateAdded = data->GetFileDateAdded();
271     if (dateAdded > 0) {
272         dateTaken = dateTaken > 0 ? min(dateTaken, dateAdded) : dateAdded;
273     }
274     int64_t dateModified = data->GetFileDateModified();
275     if (dateModified > 0) {
276         dateTaken = dateTaken > 0 ? min(dateTaken, dateModified) : dateModified;
277     }
278     int64_t modifiedTimeStamp = GetModifiedTimeStampByExif(imageSource);
279     if (modifiedTimeStamp > 0) {
280         dateTaken = dateTaken > 0 ? min(dateTaken, modifiedTimeStamp) : modifiedTimeStamp;
281     }
282     if (dateTaken <= 0) {
283         dateTaken = MediaFileUtils::UTCTimeMilliSeconds();
284     }
285     data->SetDateTaken(dateTaken);
286     MEDIA_WARN_LOG("Set date_taken use dateAdded:%{public}ld or dateModified:%{public}ld or "
287                    "modifiedTimeStamp:%{public}ld, dateTaken:%{public}ld",
288         static_cast<long>(dateAdded),
289         static_cast<long>(dateModified),
290         static_cast<long>(modifiedTimeStamp),
291         static_cast<long>(dateTaken));
292 }
293 
GetCompatibleUserComment(const string & userComment)294 static string GetCompatibleUserComment(const string& userComment)
295 {
296     const string startFlag = "<mgzn-content>";
297     const string endFlag = "<mgzn-worksdes>";
298     size_t posStart = userComment.find(startFlag);
299     size_t posEnd = userComment.find(endFlag);
300     if (posStart == string::npos || posEnd == string::npos || posStart >= posEnd) {
301         return userComment;
302     }
303 
304     posStart += startFlag.length();
305     return userComment.substr(posStart, posEnd - posStart);
306 }
307 
ExtractImageExif(std::unique_ptr<ImageSource> & imageSource,std::unique_ptr<Metadata> & data)308 int32_t MetadataExtractor::ExtractImageExif(std::unique_ptr<ImageSource> &imageSource, std::unique_ptr<Metadata> &data)
309 {
310     if (imageSource == nullptr) {
311         MEDIA_ERR_LOG("Failed to obtain image source");
312         return E_OK;
313     }
314 
315     int32_t intTempMeta = 0;
316     string propertyStr;
317     uint32_t err;
318 
319     nlohmann::json exifJson;
320     err = imageSource->GetImagePropertyInt(0, PHOTO_DATA_IMAGE_ORIENTATION, intTempMeta);
321     exifJson[PHOTO_DATA_IMAGE_ORIENTATION] = (err == 0) ? intTempMeta: 0;
322 
323     err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_LONGITUDE, propertyStr);
324     exifJson[PHOTO_DATA_IMAGE_GPS_LONGITUDE] = (err == 0) ? GetLongitudeLatitude(propertyStr): 0;
325 
326     err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_LATITUDE, propertyStr);
327     exifJson[PHOTO_DATA_IMAGE_GPS_LATITUDE] = (err == 0) ? GetLongitudeLatitude(propertyStr): 0;
328 
329     err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_FRONT_CAMERA, propertyStr);
330     data->SetFrontCamera(err == 0 ? propertyStr : "0");
331 
332     for (auto &exifKey : exifInfoKeys) {
333         err = imageSource->GetImagePropertyString(0, exifKey, propertyStr);
334         exifJson[exifKey] = (err == 0) ? propertyStr: "";
335     }
336     exifJson[PHOTO_DATA_IMAGE_IMAGE_DESCRIPTION] =
337         AppFileService::SandboxHelper::Encode(exifJson[PHOTO_DATA_IMAGE_IMAGE_DESCRIPTION]);
338     data->SetAllExif(exifJson.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace));
339 
340     err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_USER_COMMENT, propertyStr);
341     if (err == 0 && !propertyStr.empty()) {
342         data->SetUserComment(GetCompatibleUserComment(propertyStr));
343     }
344     err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_PHOTO_MODE, propertyStr);
345     if (err != 0 || propertyStr == "default_exif_value") {
346         err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_ISO_SPEED_LATITUDE_ZZZ, propertyStr);
347     }
348     if (err == 0 && !propertyStr.empty()) {
349         data->SetShootingModeTag(propertyStr);
350         data->SetShootingMode(ShootingModeAlbum::MapShootingModeTagToShootingMode(propertyStr));
351     }
352 
353     int64_t timeNow = MediaFileUtils::UTCTimeMilliSeconds();
354     data->SetLastVisitTime(timeNow);
355 
356     return E_OK;
357 }
358 
ExtractLocationMetadata(unique_ptr<ImageSource> & imageSource,unique_ptr<Metadata> & data)359 static void ExtractLocationMetadata(unique_ptr<ImageSource>& imageSource, unique_ptr<Metadata>& data)
360 {
361     string propertyStr;
362     string refStr;
363     double tempLocation = -1;
364     uint32_t err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_LONGITUDE, propertyStr);
365     uint32_t refErr = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_LONGITUDE_REF, refStr);
366     if (err == 0 && refErr == 0) {
367         tempLocation = GetLongitudeLatitude(propertyStr, refStr);
368         data->SetLongitude(tempLocation);
369     }
370 
371     err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_LATITUDE, propertyStr);
372     refErr = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_LATITUDE_REF, refStr);
373     if (err == 0 && refErr == 0) {
374         tempLocation = GetLongitudeLatitude(propertyStr, refStr);
375         data->SetLatitude(tempLocation);
376     }
377 }
378 
ExtractImageExifRotate(std::unique_ptr<ImageSource> & imageSource,std::unique_ptr<Metadata> & data)379 static void ExtractImageExifRotate(
380     std::unique_ptr<ImageSource> &imageSource, std::unique_ptr<Metadata> &data)
381 {
382     int32_t exifRotate = static_cast<int32_t>(ExifRotateType::TOP_LEFT);
383     MediaImageFrameWorkUtils::GetExifRotate(imageSource, exifRotate);
384     data->SetExifRotate(exifRotate);
385 }
386 
ExtractImageMetadata(std::unique_ptr<Metadata> & data)387 int32_t MetadataExtractor::ExtractImageMetadata(std::unique_ptr<Metadata> &data)
388 {
389     uint32_t err = 0;
390 
391     SourceOptions opts;
392     opts.formatHint = "image/" + data->GetFileExtension();
393     std::unique_ptr<ImageSource> imageSource =
394         ImageSource::CreateImageSource(data->GetFilePath(), opts, err);
395     if (err != 0 || imageSource == nullptr) {
396         MEDIA_ERR_LOG("Failed to obtain image source, err = %{public}d", err);
397         return E_IMAGE;
398     }
399 
400     ImageInfo imageInfo;
401     err = imageSource->GetImageInfoFromExif(0, imageInfo);
402     if (err == 0) {
403         data->SetFileWidth(imageInfo.size.width);
404         data->SetFileHeight(imageInfo.size.height);
405     } else {
406         MEDIA_ERR_LOG("Failed to get image info, err = %{public}d", err);
407     }
408 
409     ExtractDateTakenMetadata(imageSource, data);
410     ExtractDetailTimeMetadata(imageSource, data);
411     auto const [dateYear, dateMonth, dateDay] = PhotoFileUtils::ExtractYearMonthDay(data->GetDetailTime());
412     data->SetDateYear(dateYear);
413     data->SetDateMonth(dateMonth);
414     data->SetDateDay(dateDay);
415 
416     int32_t intTempMeta = 0;
417     err = imageSource->GetImagePropertyInt(0, PHOTO_DATA_IMAGE_ORIENTATION, intTempMeta);
418     if (err == 0) {
419         data->SetOrientation(intTempMeta);
420     }
421     ExtractImageExifRotate(imageSource, data);
422 
423     if (imageSource->IsHdrImage()) {
424         data->SetDynamicRangeType(static_cast<int32_t>(DynamicRangeType::HDR));
425     } else {
426         data->SetDynamicRangeType(static_cast<int32_t>(DynamicRangeType::SDR));
427     }
428 
429     ExtractLocationMetadata(imageSource, data);
430     ExtractImageExif(imageSource, data);
431     return E_OK;
432 }
433 
ExtractVideoShootingMode(const std::string & genreJson)434 static std::string ExtractVideoShootingMode(const std::string &genreJson)
435 {
436     if (genreJson.empty()) {
437         return "";
438     }
439     size_t pos = genreJson.find("param-use-tag");
440     if (pos != std::string::npos) {
441         size_t start = genreJson.find(":", pos);
442         size_t end = genreJson.find(",", pos);
443         if (end == std::string::npos) {
444             end = genreJson.find("}", pos);
445         }
446         return genreJson.substr(start + 1, end - start - 1); // 1: length offset
447     }
448     return "";
449 }
450 
PopulateExtractedAVMetadataOne(const std::unordered_map<int32_t,std::string> & resultMap,std::unique_ptr<Metadata> & data)451 void PopulateExtractedAVMetadataOne(const std::unordered_map<int32_t, std::string> &resultMap,
452     std::unique_ptr<Metadata> &data)
453 {
454     int32_t intTempMeta;
455 
456     string strTemp = resultMap.at(AV_KEY_ALBUM);
457     if (strTemp != "") {
458         data->SetAlbum(strTemp);
459     }
460 
461     strTemp = resultMap.at(AV_KEY_ARTIST);
462     if (strTemp != "") {
463         data->SetFileArtist(strTemp);
464     }
465 
466     strTemp = resultMap.at(AV_KEY_DURATION);
467     if (strTemp != "") {
468         intTempMeta = stringToNum<int32_t>(strTemp);
469         data->SetFileDuration(intTempMeta);
470     }
471 
472     strTemp = resultMap.at(AV_KEY_VIDEO_HEIGHT);
473     if (strTemp != "") {
474         intTempMeta = stringToNum<int32_t>(strTemp);
475         data->SetFileHeight(intTempMeta);
476     }
477 
478     strTemp = resultMap.at(AV_KEY_VIDEO_WIDTH);
479     if (strTemp != "") {
480         intTempMeta = stringToNum<int32_t>(strTemp);
481         data->SetFileWidth(intTempMeta);
482     }
483 
484     strTemp = resultMap.at(AV_KEY_MIME_TYPE);
485     if (strTemp != "") {
486         data->SetFileMimeType(strTemp);
487     }
488 }
489 
ExtractedAVMetadataExifRotate(const std::unordered_map<int32_t,std::string> & resultMap)490 int32_t ExtractedAVMetadataExifRotate(const std::unordered_map<int32_t, std::string> &resultMap)
491 {
492     int32_t exifRotate = static_cast<int32_t>(ExifRotateType::TOP_LEFT);
493     auto it = resultMap.find(AV_KEY_VIDEO_ROTATE_ORIENTATION);
494     CHECK_AND_RETURN_RET(it != resultMap.end(), exifRotate);
495 
496     std::string exifRotateStr = it->second;
497     CHECK_AND_RETURN_RET(!exifRotateStr.empty(), exifRotate);
498 
499     exifRotate = stringToNum<int32_t>(exifRotateStr);
500     return exifRotate;
501 }
502 
ParseDetailTime(const string & timeStr,const string & format)503 std::string ParseDetailTime(const string &timeStr, const string &format)
504 {
505     std::tm timeInfo{};
506     std::istringstream iss(timeStr);
507     iss >> std::get_time(&timeInfo, format.c_str());
508     if (iss.fail()) {
509         MEDIA_ERR_LOG(
510             "Parse DetailTime failed, timeStr: %{public}s, format: %{public}s", timeStr.c_str(), format.c_str());
511         return "";
512     }
513 
514     std::ostringstream oss;
515     oss << std::put_time(&timeInfo, PhotoColumn::PHOTO_DETAIL_TIME_FORMAT.c_str());
516     return oss.str();
517 }
518 
PopulateAVMetadataDateTaken(const std::unordered_map<int32_t,std::string> & resultMap,std::unique_ptr<Metadata> & data)519 void PopulateAVMetadataDateTaken(
520     const std::unordered_map<int32_t, std::string> &resultMap, std::unique_ptr<Metadata> &data)
521 {
522     // first take utc time
523     string timeStr = resultMap.at(AV_KEY_DATE_TIME_ISO8601);
524     int64_t dateTaken = convertUTCTimeInformat(timeStr, "%Y-%m-%dT%H:%M:%S");
525     if (dateTaken > 0) {
526         data->SetDateTaken(dateTaken * MSEC_TO_SEC);
527         return;
528     }
529     timeStr = resultMap.at(AV_KEY_DATE_TIME_FORMAT);
530     dateTaken = convertTimeStr2TimeStamp(timeStr);
531     if (dateTaken > 0) {
532         data->SetDateTaken(dateTaken * MSEC_TO_SEC);
533         MEDIA_WARN_LOG("Set date_taken use local time");
534         return;
535     }
536     dateTaken = data->GetDateTaken();
537     if (dateTaken > 0 && !data->GetForAdd()) {
538         MEDIA_WARN_LOG("Set date_taken use old date_taken: %{public}ld", static_cast<long>(dateTaken));
539         return;
540     }
541     int64_t dateAdded = data->GetFileDateAdded();
542     if (dateAdded > 0) {
543         dateTaken = dateTaken > 0 ? min(dateTaken, dateAdded) : dateAdded;
544     }
545     int64_t dateModified = data->GetFileDateModified();
546     if (dateModified > 0) {
547         dateTaken = dateTaken > 0 ? min(dateTaken, dateModified) : dateModified;
548     }
549     if (dateTaken <= 0) {
550         dateTaken = MediaFileUtils::UTCTimeMilliSeconds();
551     }
552     data->SetDateTaken(dateTaken);
553     MEDIA_WARN_LOG("Set date_taken use dateAdded:%{public}ld or dateModified:%{public}ld, dateTaken:%{public}ld",
554         static_cast<long>(dateAdded),
555         static_cast<long>(dateModified),
556         static_cast<long>(dateTaken));
557 }
558 
PopulateAVMetadataDetailTime(const std::unordered_map<int32_t,std::string> & resultMap,std::unique_ptr<Metadata> & data)559 void PopulateAVMetadataDetailTime(
560     const std::unordered_map<int32_t, std::string> &resultMap, std::unique_ptr<Metadata> &data)
561 {
562     string timeStr = resultMap.at(AV_KEY_DATE_TIME_FORMAT);
563     string detailTime = ParseDetailTime(timeStr, "%Y-%m-%d %H:%M:%S");
564     if (!detailTime.empty()) {
565         data->SetDetailTime(detailTime);
566         return;
567     }
568     detailTime = data->GetDetailTime();
569     if (detailTime.empty() || data->GetForAdd()) {
570         int64_t dateTaken = data->GetDateTaken() / MSEC_TO_SEC;
571         detailTime = MediaFileUtils::StrCreateTime(PhotoColumn::PHOTO_DETAIL_TIME_FORMAT, dateTaken);
572     }
573     data->SetDetailTime(detailTime);
574 }
575 
PopulateExtractedAVMetadataTwo(const std::unordered_map<int32_t,std::string> & resultMap,std::unique_ptr<Metadata> & data)576 void PopulateExtractedAVMetadataTwo(
577     const std::unordered_map<int32_t, std::string> &resultMap, std::unique_ptr<Metadata> &data)
578 {
579     int32_t intTempMeta{0};
580     string strTemp = resultMap.at(AV_KEY_VIDEO_ORIENTATION);
581     if (!strTemp.empty()) {
582         intTempMeta = stringToNum<int32_t>(strTemp);
583     }
584     data->SetOrientation(intTempMeta);
585 
586     intTempMeta = ExtractedAVMetadataExifRotate(resultMap);
587     data->SetExifRotate(intTempMeta);
588 
589     strTemp = resultMap.at(AV_KEY_GENRE);
590     if (!strTemp.empty()) {
591         std::string videoShootingMode = ExtractVideoShootingMode(strTemp);
592         data->SetShootingModeTag(videoShootingMode);
593         data->SetShootingMode(ShootingModeAlbum::MapShootingModeTagToShootingMode(videoShootingMode));
594     }
595     strTemp = resultMap.at(AV_KEY_VIDEO_IS_HDR_VIVID);
596     const string isHdr = "yes";
597     if (strcmp(strTemp.c_str(), isHdr.c_str()) == 0) {
598         data->SetDynamicRangeType(static_cast<int32_t>(DynamicRangeType::HDR));
599     } else {
600         data->SetDynamicRangeType(static_cast<int32_t>(DynamicRangeType::SDR));
601     }
602 }
603 
PopulateExtractedAVLocationMeta(std::shared_ptr<Meta> & meta,std::unique_ptr<Metadata> & data)604 void PopulateExtractedAVLocationMeta(std::shared_ptr<Meta> &meta, std::unique_ptr<Metadata> &data)
605 {
606     float floatTempMeta;
607 
608     if (meta->GetData(Tag::MEDIA_LATITUDE, floatTempMeta)) {
609         data->SetLatitude((double)floatTempMeta);
610     }
611     if (meta->GetData(Tag::MEDIA_LONGITUDE, floatTempMeta)) {
612         data->SetLongitude((double)floatTempMeta);
613     }
614 }
615 
ParseLivePhotoCoverPosition(std::unique_ptr<Metadata> & data)616 static void ParseLivePhotoCoverPosition(std::unique_ptr<Metadata> &data)
617 {
618     string extraPath = MovingPhotoFileUtils::GetMovingPhotoExtraDataPath(data->GetMovingPhotoImagePath());
619     string absExtraPath;
620     if (!PathToRealPath(extraPath, absExtraPath)) {
621         MEDIA_ERR_LOG("file is not real path: %{private}s, errno: %{public}d", extraPath.c_str(), errno);
622         return;
623     }
624     UniqueFd fd(open(absExtraPath.c_str(), O_RDONLY));
625     uint32_t version{0};
626     uint32_t frameIndex{0};
627     bool hasCinemagraphInfo{false};
628     if (MovingPhotoFileUtils::GetVersionAndFrameNum(fd, version, frameIndex, hasCinemagraphInfo) != E_OK) {
629         return;
630     }
631     uint64_t coverPosition;
632     if (MovingPhotoFileUtils::GetCoverPosition(data->GetFilePath(), frameIndex, coverPosition) != E_OK) {
633         return;
634     }
635     data->SetCoverPosition(static_cast<int64_t>(coverPosition));
636 }
637 
ParseMovingPhotoCoverPosition(std::shared_ptr<Meta> & meta,std::unique_ptr<Metadata> & data)638 static void ParseMovingPhotoCoverPosition(std::shared_ptr<Meta> &meta, std::unique_ptr<Metadata> &data)
639 {
640     shared_ptr<Meta> customMeta = make_shared<Meta>();
641     bool isValid = meta->GetData(PHOTO_DATA_VIDEO_CUSTOM_INFO, customMeta);
642     if (!isValid) {
643         MEDIA_INFO_LOG("Video of moving photo does not contain customInfo");
644         return ParseLivePhotoCoverPosition(data);
645     }
646 
647     float coverPosition = 0.0f;
648     isValid = customMeta->GetData(PHOTO_DATA_VIDEO_COVER_TIME, coverPosition);
649     if (!isValid) {
650         MEDIA_INFO_LOG("Video of moving photo does not contain cover position");
651         return ParseLivePhotoCoverPosition(data);
652     }
653     // convert cover position from ms(float) to us(int64_t)
654     constexpr int32_t MS_TO_US = 1000;
655     data->SetCoverPosition(static_cast<int64_t>(coverPosition * MS_TO_US));
656 }
657 
FillExtractedMetadata(const std::unordered_map<int32_t,std::string> & resultMap,std::shared_ptr<Meta> & meta,std::unique_ptr<Metadata> & data)658 void MetadataExtractor::FillExtractedMetadata(const std::unordered_map<int32_t, std::string> &resultMap,
659     std::shared_ptr<Meta> &meta, std::unique_ptr<Metadata> &data)
660 {
661     PopulateExtractedAVMetadataOne(resultMap, data);
662     PopulateExtractedAVMetadataTwo(resultMap, data);
663     PopulateAVMetadataDateTaken(resultMap, data);
664     PopulateAVMetadataDetailTime(resultMap, data);
665     auto const [dateYear, dateMonth, dateDay] = PhotoFileUtils::ExtractYearMonthDay(data->GetDetailTime());
666     data->SetDateYear(dateYear);
667     data->SetDateMonth(dateMonth);
668     data->SetDateDay(dateDay);
669     PopulateExtractedAVLocationMeta(meta, data);
670 
671     int64_t timeNow = MediaFileUtils::UTCTimeMilliSeconds();
672     data->SetLastVisitTime(timeNow);
673 
674     if (IsMovingPhoto(data)) {
675         ParseMovingPhotoCoverPosition(meta, data);
676     }
677 }
678 
FillFrameIndex(std::shared_ptr<AVMetadataHelper> & avMetadataHelper,std::unique_ptr<Metadata> & data)679 static void FillFrameIndex(std::shared_ptr<AVMetadataHelper> &avMetadataHelper,
680     std::unique_ptr<Metadata> &data)
681 {
682     CHECK_AND_RETURN_WARN_LOG(IsMovingPhoto(data), "data is not moving photo");
683     uint64_t coverPosition = static_cast<uint64_t>(data->GetCoverPosition());
684     uint32_t frameIndex = 0;
685 
686     int32_t err = avMetadataHelper->GetFrameIndexByTime(coverPosition, frameIndex);
687     CHECK_AND_RETURN_LOG(err == E_OK, "Failed to get frame index, err: %{public}d", err);
688     data->SetFrameIndex(static_cast<int32_t>(frameIndex));
689 }
690 
ExtractAVMetadata(std::unique_ptr<Metadata> & data,int32_t scene)691 int32_t MetadataExtractor::ExtractAVMetadata(std::unique_ptr<Metadata> &data, int32_t scene)
692 {
693     MediaLibraryTracer tracer;
694     tracer.Start("ExtractAVMetadata");
695 
696     tracer.Start("CreateAVMetadataHelper");
697     std::shared_ptr<AVMetadataHelper> avMetadataHelper = AVMetadataHelperFactory::CreateAVMetadataHelper();
698     tracer.Finish();
699     CHECK_AND_RETURN_RET_LOG(avMetadataHelper != nullptr, E_AVMETADATA, "AV metadata helper is null");
700 
701     // notify media_service clone event.
702     if (scene == Scene::AV_META_SCENE_CLONE) {
703         avMetadataHelper->SetScene(static_cast<Scene>(scene));
704     }
705 
706     string filePath = data->GetFilePath();
707     CHECK_AND_RETURN_RET_LOG(!filePath.empty(), E_AVMETADATA, "AV metadata file path is empty");
708     int32_t fd = open(filePath.c_str(), O_RDONLY);
709     CHECK_AND_RETURN_RET_LOG(fd > 0, E_SYSCALL, "Open file descriptor failed, errno = %{public}d", errno);
710 
711     struct stat64 st;
712     if (fstat64(fd, &st) != 0) {
713         MEDIA_ERR_LOG("Get file state failed for the given fd");
714         (void)close(fd);
715         return E_SYSCALL;
716     }
717     data->SetFileSize(st.st_size);
718 
719     tracer.Start("avMetadataHelper->SetSource");
720     int32_t err = avMetadataHelper->SetSource(fd, 0, static_cast<int64_t>(st.st_size), AV_META_USAGE_META_ONLY);
721     tracer.Finish();
722     if (err != 0) {
723         MEDIA_ERR_LOG("SetSource failed for the given file descriptor, err = %{public}d", err);
724         (void)close(fd);
725         return E_AVMETADATA;
726     }
727     tracer.Start("avMetadataHelper->ResolveMetadata");
728     std::shared_ptr<Meta> meta = avMetadataHelper->GetAVMetadata();
729     std::unordered_map<int32_t, std::string> resultMap = avMetadataHelper->ResolveMetadata();
730     tracer.Finish();
731     if (!resultMap.empty()) {
732         FillExtractedMetadata(resultMap, meta, data);
733         if (IsMovingPhoto(data)) {
734             FillFrameIndex(avMetadataHelper, data);
735         }
736     }
737 
738     (void)close(fd);
739 
740     return E_OK;
741 }
742 
CombineMovingPhotoMetadata(std::unique_ptr<Metadata> & data,bool isCameraShotMovingPhoto)743 int32_t MetadataExtractor::CombineMovingPhotoMetadata(std::unique_ptr<Metadata> &data,
744     bool isCameraShotMovingPhoto)
745 {
746     // if video of moving photo does not exist, just return
747     string videoPath = MediaFileUtils::GetMovingPhotoVideoPath(data->GetFilePath());
748     if (!MediaFileUtils::IsFileExists(videoPath)) {
749         MEDIA_INFO_LOG("Video of moving photo does not exist, path: %{private}s", videoPath.c_str());
750         return E_OK;
751     }
752 
753     size_t videoSize = 0;
754     if (!MediaFileUtils::GetFileSize(videoPath, videoSize) || videoSize == 0) {
755         MEDIA_INFO_LOG("Video of moving photo is empty now, path: %{private}s", videoPath.c_str());
756         return E_OK;
757     }
758 
759     unique_ptr<Metadata> videoData = make_unique<Metadata>();
760     videoData->SetMovingPhotoImagePath(data->GetFilePath());
761     videoData->SetFilePath(videoPath);
762     videoData->SetPhotoSubType(static_cast<int32_t>(PhotoSubType::MOVING_PHOTO));
763     int32_t err = ExtractAVMetadata(videoData);
764     CHECK_AND_RETURN_RET_LOG(err == E_OK, err,
765         "Failed to extract video metadata for moving photo: %{private}s", videoPath.c_str());
766 
767     data->SetCoverPosition(videoData->GetCoverPosition());
768 
769     uint32_t frameIndex = MovingPhotoFileUtils::GetFrameIndex(videoData->GetCoverPosition(),
770         UniqueFd(open(videoPath.c_str(), O_RDONLY)));
771     off_t extraDataSize{0};
772     if (MovingPhotoFileUtils::GetExtraDataLen(data->GetFilePath(), videoPath,
773         frameIndex, videoData->GetCoverPosition(), extraDataSize, isCameraShotMovingPhoto) != E_OK) {
774         MEDIA_WARN_LOG("Failed to get extra data file size");
775     }
776     data->SetFileSize(data->GetFileSize() + videoData->GetFileSize() + extraDataSize);
777     int64_t videoDateModified = videoData->GetFileDateModified();
778     if (videoDateModified > data->GetFileDateModified()) {
779         data->SetFileDateModified(videoDateModified);
780     }
781 
782     int32_t duration = videoData->GetFileDuration();
783     if (!MediaFileUtils::CheckMovingPhotoVideoDuration(duration)) {
784         MEDIA_ERR_LOG("Failed to check video duration (%{public}d ms) of moving photo", duration);
785         return E_MOVING_PHOTO;
786     }
787     return E_OK;
788 }
789 
Extract(std::unique_ptr<Metadata> & data,bool isCameraShotMovingPhoto)790 int32_t MetadataExtractor::Extract(std::unique_ptr<Metadata> &data, bool isCameraShotMovingPhoto)
791 {
792     if (data->GetFileMediaType() == MEDIA_TYPE_IMAGE) {
793         int32_t ret = ExtractImageMetadata(data);
794         CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Failed to extract image metadata");
795         if (IsMovingPhoto(data)) {
796             return CombineMovingPhotoMetadata(data, isCameraShotMovingPhoto);
797         }
798         return ret;
799     } else {
800         return ExtractAVMetadata(data);
801     }
802 }
803 // LCOV_EXCL_STOP
804 } // namespace Media
805 } // namespace OHOS
806