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