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