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 <fcntl.h>
20 #include "hitrace_meter.h"
21 #include "media_exif.h"
22 #include "media_log.h"
23 #include "medialibrary_db_const.h"
24 #include "medialibrary_errno.h"
25 #include "medialibrary_tracer.h"
26 #include "nlohmann/json.hpp"
27
28 namespace OHOS {
29 namespace Media {
30 using namespace std;
31
32 const double DEGREES2MINUTES = 60.0;
33 const double DEGREES2SECONDS = 3600.0;
34 constexpr int32_t OFFSET_NUM = 2;
35
36 template <class Type>
stringToNum(const string & str)37 static Type stringToNum(const string &str)
38 {
39 std::istringstream iss(str);
40 Type num;
41 iss >> num;
42 return num;
43 }
44
GetLongitudeLatitude(string inputStr)45 double GetLongitudeLatitude(string inputStr)
46 {
47 auto pos = inputStr.find(',');
48 if (pos == string::npos) {
49 return 0;
50 }
51 double ret = stringToNum<double>(inputStr.substr(0, pos));
52
53 inputStr = inputStr.substr(pos + OFFSET_NUM);
54 pos = inputStr.find(',');
55 if (pos == string::npos) {
56 return 0;
57 }
58 ret += stringToNum<double>(inputStr.substr(0, pos)) / DEGREES2MINUTES;
59
60 inputStr = inputStr.substr(pos + OFFSET_NUM);
61 ret += stringToNum<double>(inputStr) / DEGREES2SECONDS;
62 return ret;
63 }
64
convertTimeStr2TimeStamp(string & timeStr)65 static time_t convertTimeStr2TimeStamp(string &timeStr)
66 {
67 struct tm timeinfo;
68 strptime(timeStr.c_str(), "%Y-%m-%d %H:%M:%S", &timeinfo);
69 time_t timeStamp = mktime(&timeinfo);
70 return timeStamp;
71 }
72
ExtractImageExif(std::unique_ptr<ImageSource> & imageSource,std::unique_ptr<Metadata> & data)73 int32_t MetadataExtractor::ExtractImageExif(std::unique_ptr<ImageSource> &imageSource, std::unique_ptr<Metadata> &data)
74 {
75 if (imageSource == nullptr) {
76 MEDIA_ERR_LOG("Failed to obtain image source");
77 return E_OK;
78 }
79
80 int32_t intTempMeta = 0;
81 string propertyStr;
82 nlohmann::json exifJson;
83 uint32_t err = imageSource->GetImagePropertyInt(0, PHOTO_DATA_IMAGE_ORIENTATION, intTempMeta);
84 exifJson[PHOTO_DATA_IMAGE_ORIENTATION] = (err == 0) ? intTempMeta: 0;
85
86 err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_LONGITUDE, propertyStr);
87 exifJson[PHOTO_DATA_IMAGE_GPS_LONGITUDE] = (err == 0) ? GetLongitudeLatitude(propertyStr): 0;
88
89 err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_GPS_LATITUDE, propertyStr);
90 exifJson[PHOTO_DATA_IMAGE_GPS_LONGITUDE] = (err == 0) ? GetLongitudeLatitude(propertyStr): 0;
91
92 for (auto &exifKey : exifInfoKeys) {
93 err = imageSource->GetImagePropertyString(0, exifKey, propertyStr);
94 exifJson[exifKey] = (err == 0) ? propertyStr: "";
95 }
96 data->SetAllExif(exifJson.dump());
97
98 err = imageSource->GetImagePropertyString(0, PHOTO_DATA_IMAGE_USER_COMMENT, propertyStr);
99 if (err == 0) {
100 data->SetUserComment(propertyStr);
101 }
102
103 return E_OK;
104 }
105
ExtractImageMetadata(std::unique_ptr<Metadata> & data)106 int32_t MetadataExtractor::ExtractImageMetadata(std::unique_ptr<Metadata> &data)
107 {
108 uint32_t err = 0;
109
110 SourceOptions opts;
111 opts.formatHint = "image/" + data->GetFileExtension();
112 std::unique_ptr<ImageSource> imageSource =
113 ImageSource::CreateImageSource(data->GetFilePath(), opts, err);
114 if (err != 0 || imageSource == nullptr) {
115 MEDIA_ERR_LOG("Failed to obtain image source, err = %{public}d", err);
116 return E_OK;
117 }
118
119 ImageInfo imageInfo;
120 err = imageSource->GetImageInfo(0, imageInfo);
121 if (err == 0) {
122 data->SetFileWidth(imageInfo.size.width);
123 data->SetFileHeight(imageInfo.size.height);
124 } else {
125 MEDIA_ERR_LOG("Failed to get image info, err = %{public}d", err);
126 }
127
128 string propertyStr;
129 int64_t int64TempMeta = 0;
130 err = imageSource->GetImagePropertyString(0, MEDIA_DATA_IMAGE_DATE_TIME_ORIGINAL, propertyStr);
131 if (err == 0) {
132 int64TempMeta = convertTimeStr2TimeStamp(propertyStr);
133 if (int64TempMeta < 0) {
134 data->SetDateTaken(data->GetFileDateModified());
135 } else {
136 data->SetDateTaken(int64TempMeta);
137 }
138 } else {
139 // use modified time as date taken time when date taken not set
140 data->SetDateTaken(data->GetFileDateModified());
141 }
142
143 int32_t intTempMeta = 0;
144 err = imageSource->GetImagePropertyInt(0, MEDIA_DATA_IMAGE_ORIENTATION, intTempMeta);
145 if (err == 0) {
146 data->SetOrientation(intTempMeta);
147 }
148
149 double dbleTempMeta = -1;
150 err = imageSource->GetImagePropertyString(0, MEDIA_DATA_IMAGE_GPS_LONGITUDE, propertyStr);
151 if (err == 0) {
152 dbleTempMeta = GetLongitudeLatitude(propertyStr);
153 data->SetLongitude(dbleTempMeta);
154 }
155
156 err = imageSource->GetImagePropertyString(0, MEDIA_DATA_IMAGE_GPS_LATITUDE, propertyStr);
157 if (err == 0) {
158 dbleTempMeta = GetLongitudeLatitude(propertyStr);
159 data->SetLatitude(dbleTempMeta);
160 }
161
162 ExtractImageExif(imageSource, data);
163
164 return E_OK;
165 }
166
FillExtractedMetadata(const std::unordered_map<int32_t,std::string> & resultMap,std::unique_ptr<Metadata> & data)167 void MetadataExtractor::FillExtractedMetadata(const std::unordered_map<int32_t, std::string> &resultMap,
168 std::unique_ptr<Metadata> &data)
169 {
170 string strTemp;
171 int32_t intTempMeta;
172 int64_t int64TempMeta;
173
174 strTemp = resultMap.at(AV_KEY_ALBUM);
175 if (strTemp != "") {
176 data->SetAlbum(strTemp);
177 }
178
179 strTemp = resultMap.at(AV_KEY_ARTIST);
180 if (strTemp != "") {
181 data->SetFileArtist(strTemp);
182 }
183
184 strTemp = resultMap.at(AV_KEY_DURATION);
185 if (strTemp != "") {
186 intTempMeta = stringToNum<int32_t>(strTemp);
187 data->SetFileDuration(intTempMeta);
188 }
189
190 strTemp = resultMap.at(AV_KEY_VIDEO_HEIGHT);
191 if (strTemp != "") {
192 intTempMeta = stringToNum<int32_t>(strTemp);
193 data->SetFileHeight(intTempMeta);
194 }
195
196 strTemp = resultMap.at(AV_KEY_VIDEO_WIDTH);
197 if (strTemp != "") {
198 intTempMeta = stringToNum<int32_t>(strTemp);
199 data->SetFileWidth(intTempMeta);
200 }
201
202 strTemp = resultMap.at(AV_KEY_MIME_TYPE);
203 if (strTemp != "") {
204 data->SetFileMimeType(strTemp);
205 }
206
207 strTemp = resultMap.at(AV_KEY_DATE_TIME_FORMAT);
208 if (strTemp != "") {
209 int64TempMeta = convertTimeStr2TimeStamp(strTemp);
210 if (int64TempMeta < 0) {
211 data->SetDateTaken(data->GetFileDateModified());
212 } else {
213 data->SetDateTaken(int64TempMeta);
214 }
215 } else {
216 // use modified time as date taken time when date taken not set
217 data->SetDateTaken(data->GetFileDateModified());
218 }
219
220 strTemp = resultMap.at(AV_KEY_VIDEO_ORIENTATION);
221 if (strTemp == "") {
222 intTempMeta = 0;
223 } else {
224 intTempMeta = stringToNum<int32_t>(strTemp);
225 }
226 data->SetOrientation(intTempMeta);
227
228 strTemp = resultMap.at(AV_KEY_TITLE);
229 if (!strTemp.empty()) {
230 data->SetFileTitle(strTemp);
231 }
232 }
233
ExtractAVMetadata(std::unique_ptr<Metadata> & data)234 int32_t MetadataExtractor::ExtractAVMetadata(std::unique_ptr<Metadata> &data)
235 {
236 MediaLibraryTracer tracer;
237 tracer.Start("ExtractAVMetadata");
238
239 tracer.Start("CreateAVMetadataHelper");
240 std::shared_ptr<AVMetadataHelper> avMetadataHelper = AVMetadataHelperFactory::CreateAVMetadataHelper();
241 tracer.Finish();
242 if (avMetadataHelper == nullptr) {
243 MEDIA_ERR_LOG("AV metadata helper is null");
244 return E_AVMETADATA;
245 }
246
247 string filePath = data->GetFilePath();
248 if (filePath.empty()) {
249 MEDIA_ERR_LOG("AV metadata file path is empty");
250 return E_AVMETADATA;
251 }
252
253 int32_t fd = open(filePath.c_str(), O_RDONLY);
254 if (fd <= 0) {
255 MEDIA_ERR_LOG("Open file descriptor failed, errno = %{public}d", errno);
256 return E_SYSCALL;
257 }
258
259 struct stat64 st;
260 if (fstat64(fd, &st) != 0) {
261 MEDIA_ERR_LOG("Get file state failed for the given fd");
262 (void)close(fd);
263 return E_SYSCALL;
264 }
265
266 tracer.Start("avMetadataHelper->SetSource");
267 int32_t err = avMetadataHelper->SetSource(fd, 0, static_cast<int64_t>(st.st_size), AV_META_USAGE_META_ONLY);
268 tracer.Finish();
269 if (err != 0) {
270 MEDIA_ERR_LOG("SetSource failed for the given file descriptor, err = %{public}d", err);
271 (void)close(fd);
272 return E_AVMETADATA;
273 } else {
274 tracer.Start("avMetadataHelper->ResolveMetadata");
275 std::unordered_map<int32_t, std::string> resultMap = avMetadataHelper->ResolveMetadata();
276 tracer.Finish();
277 if (!resultMap.empty()) {
278 FillExtractedMetadata(resultMap, data);
279 }
280 }
281
282 (void)close(fd);
283
284 return E_OK;
285 }
286
Extract(std::unique_ptr<Metadata> & data)287 int32_t MetadataExtractor::Extract(std::unique_ptr<Metadata> &data)
288 {
289 if (data->GetFileMediaType() == MEDIA_TYPE_IMAGE) {
290 return ExtractImageMetadata(data);
291 } else {
292 return ExtractAVMetadata(data);
293 }
294 }
295 } // namespace Media
296 } // namespace OHOS
297