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