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