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