• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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