Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(260)

Side by Side Diff: chrome/utility/media_galleries/image_metadata_extractor.cc

Issue 1791623002: Revert of Media Galleries Partial Deprecation: Remove image metadata & libexif. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Manual revert/rebase. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/utility/media_galleries/image_metadata_extractor.h"
6
7 extern "C" {
8 #include <libexif/exif-data.h>
9 #include <libexif/exif-loader.h>
10 } // extern "C"
11 #include <stddef.h>
12 #include <stdint.h>
13
14 #include "base/bind.h"
15 #include "base/callback.h"
16 #include "base/files/file_path.h"
17 #include "base/lazy_instance.h"
18 #include "base/macros.h"
19 #include "base/memory/ref_counted.h"
20 #include "base/numerics/safe_conversions.h"
21 #include "base/path_service.h"
22 #include "base/scoped_native_library.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "build/build_config.h"
25 #include "content/public/common/content_paths.h"
26 #include "media/base/data_source.h"
27 #include "net/base/io_buffer.h"
28
29 namespace metadata {
30
31 namespace {
32
33 const size_t kMaxBufferSize = 50 * 1024 * 1024; // Arbitrary maximum of 50MB.
34
35 typedef base::Callback<void(const scoped_refptr<net::DrainableIOBuffer>&)>
36 GotImageCallback;
37
38 void FinishGetImageBytes(
39 const scoped_refptr<net::DrainableIOBuffer>& buffer,
40 media::DataSource* source,
41 const GotImageCallback& callback,
42 int bytes_read) {
43 if (bytes_read == media::DataSource::kReadError) {
44 callback.Run(NULL);
45 return;
46 }
47
48 buffer->DidConsume(bytes_read);
49 // Didn't get the whole file. Continue reading to get the rest.
50 if (buffer->BytesRemaining() > 0) {
51 source->Read(0, buffer->BytesRemaining(),
52 reinterpret_cast<uint8_t*>(buffer->data()),
53 base::Bind(&FinishGetImageBytes, buffer,
54 base::Unretained(source), callback));
55 return;
56 }
57
58 buffer->SetOffset(0);
59 callback.Run(buffer);
60 }
61
62 void GetImageBytes(
63 media::DataSource* source,
64 const GotImageCallback& callback) {
65 int64_t size64 = 0;
66 if (!source->GetSize(&size64) ||
67 base::saturated_cast<size_t>(size64) > kMaxBufferSize) {
68 return callback.Run(NULL);
69 }
70 int size = base::checked_cast<int>(size64);
71
72 scoped_refptr<net::DrainableIOBuffer> buffer(
73 new net::DrainableIOBuffer(new net::IOBuffer(size), size));
74 source->Read(0, buffer->BytesRemaining(),
75 reinterpret_cast<uint8_t*>(buffer->data()),
76 base::Bind(&FinishGetImageBytes, buffer,
77 base::Unretained(source), callback));
78 }
79
80 class ExifFunctions {
81 public:
82 ExifFunctions() : exif_loader_write_func_(NULL),
83 exif_loader_new_func_(NULL),
84 exif_loader_unref_func_(NULL),
85 exif_loader_get_data_func_(NULL),
86 exif_data_free_func_(NULL),
87 exif_data_get_byte_order_func_(NULL),
88 exif_get_short_func_(NULL),
89 exif_get_long_func_(NULL),
90 exif_get_rational_func_(NULL),
91 exif_entry_get_value_func_(NULL),
92 exif_content_get_entry_func_(NULL) {
93 }
94
95 bool Initialize(const base::FilePath& module_dir) {
96 if (exif_lib_.is_valid())
97 return true;
98
99 #if defined(OS_WIN)
100 base::FilePath module_path = module_dir.AppendASCII("libexif.dll");
101 #elif defined(OS_MACOSX)
102 base::FilePath module_path = module_dir.AppendASCII("exif.so");
103 #elif defined(OS_CHROMEOS)
104 // On ChromeOS, we build and distribute our own version of libexif.
105 base::FilePath module_path = module_dir.AppendASCII("libexif.so");
106 #else
107 // On Linux-like systems, we use the system libexif.
108 base::FilePath module_path = base::FilePath().AppendASCII("libexif.so.12");
109 #endif
110
111 base::NativeLibraryLoadError error;
112 base::ScopedNativeLibrary lib(base::LoadNativeLibrary(module_path, &error));
113 if (!lib.is_valid()) {
114 LOG(ERROR) << "Couldn't load libexif. " << error.ToString();
115 return false;
116 }
117
118 if (!GetFunctionPointer(lib, &exif_loader_write_func_,
119 "exif_loader_write") ||
120 !GetFunctionPointer(lib, &exif_loader_new_func_, "exif_loader_new") ||
121 !GetFunctionPointer(lib, &exif_loader_unref_func_,
122 "exif_loader_unref") ||
123 !GetFunctionPointer(lib, &exif_loader_get_data_func_,
124 "exif_loader_get_data") ||
125 !GetFunctionPointer(lib, &exif_data_free_func_, "exif_data_free") ||
126 !GetFunctionPointer(lib, &exif_data_get_byte_order_func_,
127 "exif_data_get_byte_order") ||
128 !GetFunctionPointer(lib, &exif_get_short_func_, "exif_get_short") ||
129 !GetFunctionPointer(lib, &exif_get_long_func_, "exif_get_long") ||
130 !GetFunctionPointer(lib, &exif_get_rational_func_,
131 "exif_get_rational") ||
132 !GetFunctionPointer(lib, &exif_entry_get_value_func_,
133 "exif_entry_get_value") ||
134 !GetFunctionPointer(lib, &exif_content_get_entry_func_,
135 "exif_content_get_entry")) {
136 return false;
137 }
138
139 exif_lib_.Reset(lib.Release());
140 return true;
141 }
142
143 ExifData* ParseExifFromBuffer(unsigned char* buffer, unsigned int size) {
144 DCHECK(exif_lib_.is_valid());
145 ExifLoader* loader = exif_loader_new_func_();
146 exif_loader_write_func_(loader, buffer, size);
147
148 ExifData* data = exif_loader_get_data_func_(loader);
149
150 exif_loader_unref_func_(loader);
151 loader = NULL;
152
153 return data;
154 }
155
156 void ExifDataFree(ExifData* data) {
157 DCHECK(exif_lib_.is_valid());
158 return exif_data_free_func_(data);
159 }
160
161 void ExtractInt(ExifData* data, ExifTag tag, int* result) {
162 DCHECK(exif_lib_.is_valid());
163 DCHECK(result);
164
165 ExifEntry* entry = ExifContentGetEntry(data, tag);
166 if (!entry)
167 return;
168
169 ExifByteOrder order = exif_data_get_byte_order_func_(data);
170 switch (entry->format) {
171 case EXIF_FORMAT_SHORT: {
172 ExifShort v = exif_get_short_func_(entry->data, order);
173 *result = base::checked_cast<int>(v);
174 break;
175 }
176 case EXIF_FORMAT_LONG: {
177 ExifLong v = exif_get_long_func_(entry->data, order);
178 // Ignore values that don't fit in a signed int - likely invalid data.
179 if (base::IsValueInRangeForNumericType<int>(v))
180 *result = base::checked_cast<int>(v);
181 break;
182 }
183 default: {
184 // Ignore all other entry formats.
185 }
186 }
187 }
188
189 void ExtractDouble(ExifData* data, ExifTag tag, double* result) {
190 DCHECK(exif_lib_.is_valid());
191 DCHECK(result);
192
193 ExifEntry* entry = ExifContentGetEntry(data, tag);
194 if (!entry)
195 return;
196
197 ExifByteOrder order = exif_data_get_byte_order_func_(data);
198
199 if (entry->format == EXIF_FORMAT_RATIONAL) {
200 ExifRational v = exif_get_rational_func_(entry->data, order);
201 *result = base::checked_cast<double>(v.numerator) /
202 base::checked_cast<double>(v.denominator);
203 }
204 }
205
206 void ExtractString(ExifData* data, ExifTag tag, std::string* result) {
207 DCHECK(exif_lib_.is_valid());
208 DCHECK(result);
209
210 ExifEntry* entry = ExifContentGetEntry(data, tag);
211 if (!entry)
212 return;
213
214 char buf[1024];
215 exif_entry_get_value_func_(entry, buf, sizeof(buf));
216 *result = buf;
217 }
218
219 private:
220 // Exported by libexif.
221 typedef unsigned char (*ExifLoaderWriteFunc)(ExifLoader* eld,
222 unsigned char* buf,
223 unsigned int len);
224 typedef ExifLoader* (*ExifLoaderNewFunc)();
225 typedef void (*ExifLoaderUnrefFunc)(ExifLoader* loader);
226 typedef ExifData* (*ExifLoaderGetDataFunc)(ExifLoader* loader);
227 typedef void (*ExifDataFreeFunc)(ExifData* data);
228 typedef ExifByteOrder (*ExifDataGetByteOrderFunc)(ExifData* data);
229 typedef ExifShort (*ExifGetShortFunc)(const unsigned char *buf,
230 ExifByteOrder order);
231 typedef ExifLong (*ExifGetLongFunc)(const unsigned char *buf,
232 ExifByteOrder order);
233 typedef ExifRational (*ExifGetRationalFunc)(const unsigned char *buf,
234 ExifByteOrder order);
235 typedef const char* (*ExifEntryGetValueFunc)(ExifEntry *e, char *val,
236 unsigned int maxlen);
237 typedef ExifEntry* (*ExifContentGetEntryFunc)(ExifContent* content,
238 ExifTag tag);
239
240 template<typename FunctionType>
241 bool GetFunctionPointer(const base::ScopedNativeLibrary& lib,
242 FunctionType* function, const char* name) {
243 DCHECK(lib.is_valid());
244 DCHECK(function);
245 DCHECK(!(*function));
246 *function = reinterpret_cast<FunctionType>(
247 lib.GetFunctionPointer(name));
248 DLOG_IF(WARNING, !(*function)) << "Missing " << name;
249 return *function != NULL;
250 }
251
252 // Redefines exif_content_get_entry macro in terms of function pointer.
253 ExifEntry* ExifContentGetEntry(ExifData* data, ExifTag tag) {
254 DCHECK(exif_lib_.is_valid());
255 const ExifIfd ifds[] =
256 { EXIF_IFD_0, EXIF_IFD_1, EXIF_IFD_EXIF, EXIF_IFD_GPS };
257
258 for (size_t i = 0; i < arraysize(ifds); ++i) {
259 ExifEntry* entry = exif_content_get_entry_func_(data->ifd[ifds[i]], tag);
260 if (entry)
261 return entry;
262 }
263
264 return NULL;
265 }
266
267 ExifLoaderWriteFunc exif_loader_write_func_;
268 ExifLoaderNewFunc exif_loader_new_func_;
269 ExifLoaderUnrefFunc exif_loader_unref_func_;
270 ExifLoaderGetDataFunc exif_loader_get_data_func_;
271 ExifDataFreeFunc exif_data_free_func_;
272 ExifDataGetByteOrderFunc exif_data_get_byte_order_func_;
273 ExifGetShortFunc exif_get_short_func_;
274 ExifGetLongFunc exif_get_long_func_;
275 ExifGetRationalFunc exif_get_rational_func_;
276 ExifEntryGetValueFunc exif_entry_get_value_func_;
277 ExifContentGetEntryFunc exif_content_get_entry_func_;
278
279 base::ScopedNativeLibrary exif_lib_;
280 DISALLOW_COPY_AND_ASSIGN(ExifFunctions);
281 };
282
283 static base::LazyInstance<ExifFunctions> g_exif_lib = LAZY_INSTANCE_INITIALIZER;
284
285 } // namespace
286
287 // static
288 bool ImageMetadataExtractor::InitializeLibrary() {
289 base::FilePath media_path;
290 if (!PathService::Get(content::DIR_MEDIA_LIBS, &media_path))
291 return false;
292 return g_exif_lib.Get().Initialize(media_path);
293 }
294
295 // static
296 bool ImageMetadataExtractor::InitializeLibraryForTesting() {
297 base::FilePath module_dir;
298 if (!PathService::Get(base::DIR_EXE, &module_dir))
299 return false;
300 return g_exif_lib.Get().Initialize(module_dir);
301 }
302
303 ImageMetadataExtractor::ImageMetadataExtractor()
304 : extracted_(false),
305 width_(-1),
306 height_(-1),
307 rotation_(-1),
308 x_resolution_(-1),
309 y_resolution_(-1),
310 exposure_time_sec_(-1),
311 flash_fired_(false),
312 f_number_(-1),
313 focal_length_mm_(-1),
314 iso_equivalent_(-1) {
315 }
316
317 ImageMetadataExtractor::~ImageMetadataExtractor() {
318 }
319
320 void ImageMetadataExtractor::Extract(media::DataSource* source,
321 const DoneCallback& callback) {
322 DCHECK(!extracted_);
323
324 GetImageBytes(source, base::Bind(&ImageMetadataExtractor::FinishExtraction,
325 base::Unretained(this), callback));
326 }
327
328 int ImageMetadataExtractor::width() const {
329 DCHECK(extracted_);
330 return width_;
331 }
332
333 int ImageMetadataExtractor::height() const {
334 DCHECK(extracted_);
335 return height_;
336 }
337
338 int ImageMetadataExtractor::rotation() const {
339 DCHECK(extracted_);
340 return rotation_;
341 }
342
343 double ImageMetadataExtractor::x_resolution() const {
344 DCHECK(extracted_);
345 return x_resolution_;
346 }
347
348 double ImageMetadataExtractor::y_resolution() const {
349 DCHECK(extracted_);
350 return y_resolution_;
351 }
352
353 const std::string& ImageMetadataExtractor::date() const {
354 DCHECK(extracted_);
355 return date_;
356 }
357
358 const std::string& ImageMetadataExtractor::camera_make() const {
359 DCHECK(extracted_);
360 return camera_make_;
361 }
362
363 const std::string& ImageMetadataExtractor::camera_model() const {
364 DCHECK(extracted_);
365 return camera_model_;
366 }
367
368 double ImageMetadataExtractor::exposure_time_sec() const {
369 DCHECK(extracted_);
370 return exposure_time_sec_;
371 }
372
373 bool ImageMetadataExtractor::flash_fired() const {
374 DCHECK(extracted_);
375 return flash_fired_;
376 }
377
378 double ImageMetadataExtractor::f_number() const {
379 DCHECK(extracted_);
380 return f_number_;
381 }
382
383 double ImageMetadataExtractor::focal_length_mm() const {
384 DCHECK(extracted_);
385 return focal_length_mm_;
386 }
387
388 int ImageMetadataExtractor::iso_equivalent() const {
389 DCHECK(extracted_);
390 return iso_equivalent_;
391 }
392
393 void ImageMetadataExtractor::FinishExtraction(
394 const DoneCallback& callback,
395 const scoped_refptr<net::DrainableIOBuffer>& buffer) {
396 if (!buffer.get()) {
397 callback.Run(false);
398 return;
399 }
400
401 ExifData* data = g_exif_lib.Get().ParseExifFromBuffer(
402 reinterpret_cast<unsigned char*>(buffer->data()),
403 buffer->BytesRemaining());
404
405 if (!data) {
406 callback.Run(false);
407 return;
408 }
409
410 g_exif_lib.Get().ExtractInt(data, EXIF_TAG_IMAGE_WIDTH, &width_);
411 g_exif_lib.Get().ExtractInt(data, EXIF_TAG_IMAGE_LENGTH, &height_);
412
413 // We ignore the mirrored-aspect of the mirrored-orientations and just
414 // indicate the rotation. Mirrored-orientations are very rare.
415 int orientation = 0;
416 g_exif_lib.Get().ExtractInt(data, EXIF_TAG_ORIENTATION, &orientation);
417 switch (orientation) {
418 case 1:
419 case 2:
420 rotation_ = 0;
421 break;
422 case 3:
423 case 4:
424 rotation_ = 180;
425 break;
426 case 5:
427 case 6:
428 rotation_ = 90;
429 break;
430 case 7:
431 case 8:
432 rotation_ = 270;
433 break;
434 }
435
436 g_exif_lib.Get().ExtractDouble(data, EXIF_TAG_X_RESOLUTION, &x_resolution_);
437 g_exif_lib.Get().ExtractDouble(data, EXIF_TAG_Y_RESOLUTION, &y_resolution_);
438
439 g_exif_lib.Get().ExtractString(data, EXIF_TAG_DATE_TIME, &date_);
440
441 g_exif_lib.Get().ExtractString(data, EXIF_TAG_MAKE, &camera_make_);
442 g_exif_lib.Get().ExtractString(data, EXIF_TAG_MODEL, &camera_model_);
443 g_exif_lib.Get().ExtractDouble(data, EXIF_TAG_EXPOSURE_TIME,
444 &exposure_time_sec_);
445
446 int flash_value = -1;
447 g_exif_lib.Get().ExtractInt(data, EXIF_TAG_FLASH, &flash_value);
448 if (flash_value >= 0) {
449 flash_fired_ = (flash_value & 0x1) != 0;
450 }
451
452 g_exif_lib.Get().ExtractDouble(data, EXIF_TAG_FNUMBER, &f_number_);
453 g_exif_lib.Get().ExtractDouble(data, EXIF_TAG_FOCAL_LENGTH,
454 &focal_length_mm_);
455 g_exif_lib.Get().ExtractInt(data, EXIF_TAG_ISO_SPEED_RATINGS,
456 &iso_equivalent_);
457
458 g_exif_lib.Get().ExifDataFree(data);
459
460 extracted_ = true;
461
462 callback.Run(true);
463 }
464
465 } // namespace metadata
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698