summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/common.gypi8
-rw-r--r--chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc6
-rw-r--r--chrome/chrome.gyp6
-rw-r--r--chrome/chrome.isolate2
-rw-r--r--chrome/chrome_dll_bundle.gypi8
-rw-r--r--chrome/chrome_tests_unit.gypi2
-rw-r--r--chrome/common/extensions/api/media_galleries.idl15
-rw-r--r--chrome/installer/linux/debian/expected_deps1
-rw-r--r--chrome/installer/linux/rpm/expected_deps_i3861
-rw-r--r--chrome/installer/linux/rpm/expected_deps_x86_641
-rw-r--r--chrome/installer/linux/sysroot_scripts/packagelist.debian.wheezy.amd642
-rw-r--r--chrome/installer/mini_installer/chrome.release1
-rw-r--r--chrome/interactive_ui_tests.isolate2
-rw-r--r--chrome/sync_integration_tests.isolate1
-rw-r--r--chrome/test/data/extensions/api_test/media_galleries/media_metadata/test.js24
-rw-r--r--chrome/tools/build/chromeos/FILES.cfg4
-rw-r--r--chrome/tools/build/mac/TESTS1
-rwxr-xr-xchrome/tools/build/mac/dump_product_syms1
-rw-r--r--chrome/tools/build/win/FILES.cfg5
-rw-r--r--chrome/unit_tests.isolate2
-rw-r--r--chrome/utility/media_galleries/image_metadata_extractor.cc279
-rw-r--r--chrome/utility/media_galleries/image_metadata_extractor.h84
-rw-r--r--chrome/utility/media_galleries/image_metadata_extractor_unittest.cc66
-rw-r--r--chrome/utility/media_galleries/media_metadata_parser.cc57
-rw-r--r--content/content_browsertests.isolate2
-rw-r--r--third_party/libexif/libexif.gyp43
26 files changed, 605 insertions, 19 deletions
diff --git a/build/common.gypi b/build/common.gypi
index 02851d7..bde2a3a 100644
--- a/build/common.gypi
+++ b/build/common.gypi
@@ -2236,6 +2236,14 @@
}, { # else: branding!="Chrome"
'defines': ['CHROMIUM_BUILD'],
}],
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ 'LD_RUNPATH_SEARCH_PATHS': [
+ # To find dylibs such as libexif.
+ '@loader_path/.',
+ ],
+ },
+ }],
['OS=="mac" and component=="shared_library"', {
'xcode_settings': {
'DYLIB_INSTALL_NAME_BASE': '@rpath',
diff --git a/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc b/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc
index b8ce165..845f51e 100644
--- a/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc
+++ b/chrome/browser/extensions/api/media_galleries/media_galleries_apitest.cc
@@ -372,6 +372,11 @@ class MediaGalleriesPlatformAppBrowserTest : public PlatformAppBrowserTest {
.AppendASCII("common");
}
+ base::FilePath GetWallpaperTestDataDir() const {
+ return test_data_dir_.AppendASCII("api_test")
+ .AppendASCII("wallpaper");
+ }
+
int num_galleries() const {
return ensure_media_directories_exists_->num_galleries();
}
@@ -607,6 +612,7 @@ IN_PROC_BROWSER_TEST_F(MediaGalleriesPlatformAppBrowserTest, GetMetadata) {
AddFileToSingleFakeGallery(media::GetTestDataFilePath("90rotation.mp4"));
AddFileToSingleFakeGallery(media::GetTestDataFilePath("id3_png_test.mp3"));
+ AddFileToSingleFakeGallery(GetWallpaperTestDataDir().AppendASCII("test.jpg"));
base::ListValue custom_args;
#if defined(USE_PROPRIETARY_CODECS)
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 4d33e66..29b6813 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -250,6 +250,7 @@
'../content/content.gyp:content_utility',
'../media/media.gyp:media',
'../skia/skia.gyp:skia',
+ '../third_party/libexif/libexif.gyp:libexif',
'../third_party/libxml/libxml.gyp:libxml',
'common',
'<(DEPTH)/chrome/chrome_resources.gyp:chrome_resources',
@@ -296,6 +297,8 @@
'utility/importer/nss_decryptor_win.h',
'utility/importer/safari_importer.h',
'utility/importer/safari_importer.mm',
+ 'utility/media_galleries/image_metadata_extractor.cc',
+ 'utility/media_galleries/image_metadata_extractor.h',
'utility/media_galleries/ipc_data_source.cc',
'utility/media_galleries/ipc_data_source.h',
'utility/media_galleries/itunes_pref_parser_win.cc',
@@ -356,6 +359,9 @@
],
}],
['OS=="android"', {
+ 'dependencies!': [
+ '../third_party/libexif/libexif.gyp:libexif',
+ ],
'sources/': [
['exclude', '^utility/importer/'],
['exclude', '^utility/media_galleries/'],
diff --git a/chrome/chrome.isolate b/chrome/chrome.isolate
index 6a92211..2c51ac7 100644
--- a/chrome/chrome.isolate
+++ b/chrome/chrome.isolate
@@ -45,6 +45,7 @@
],
'isolate_dependency_tracked': [
'<(PRODUCT_DIR)/ffmpegsumo.so',
+ '<(PRODUCT_DIR)/libexif.dylib',
],
'isolate_dependency_untracked': [
'<(PRODUCT_DIR)/<(mac_product_name) Framework.framework/',
@@ -64,6 +65,7 @@
'<(PRODUCT_DIR)/d3dcompiler_46.dll',
'<(PRODUCT_DIR)/ffmpegsumo.dll',
'<(PRODUCT_DIR)/libEGL.dll',
+ '<(PRODUCT_DIR)/libexif.dll',
'<(PRODUCT_DIR)/libGLESv2.dll',
'<(PRODUCT_DIR)/nacl64<(EXECUTABLE_SUFFIX)',
'<(PRODUCT_DIR)/osmesa.dll',
diff --git a/chrome/chrome_dll_bundle.gypi b/chrome/chrome_dll_bundle.gypi
index 33aeb42..7dcd347 100644
--- a/chrome/chrome_dll_bundle.gypi
+++ b/chrome/chrome_dll_bundle.gypi
@@ -37,6 +37,10 @@
'$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(WRAPPER_NAME)/$(PRODUCT_NAME)',
'INFOPLIST_FILE': 'app/framework-Info.plist',
+ 'LD_RUNPATH_SEARCH_PATHS': [
+ # To find dylibs such as libexif.
+ '@loader_path/Libraries/.',
+ ],
},
'includes': [
'chrome_nibs.gypi',
@@ -138,10 +142,10 @@
],
'copies': [
{
- # Copy FFmpeg binaries for audio/video support.
'destination': '<(PRODUCT_DIR)/$(CONTENTS_FOLDER_PATH)/Libraries',
'files': [
- '<(PRODUCT_DIR)/ffmpegsumo.so',
+ '<(PRODUCT_DIR)/ffmpegsumo.so', # Copy FFmpeg binaries for audio/video support.
+ '<(PRODUCT_DIR)/libexif.dylib',
],
},
{
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index bac3bef..4e62a12 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1944,6 +1944,7 @@
'utility/importer/firefox_importer_unittest_utils.h',
'utility/importer/firefox_importer_unittest_utils_mac.cc',
'utility/importer/safari_importer_unittest.mm',
+ 'utility/media_galleries/image_metadata_extractor_unittest.cc',
# Duplicate these tests here because PathService has more items in
# unit_tests than in base_unittests.
@@ -2614,6 +2615,7 @@
['exclude', '^browser/ui/webui/sync_promo'],
['exclude', '^tools/profile_reset/'],
['exclude', '^utility/importer/'],
+ ['exclude', '^utility/media_galleries/'],
],
'conditions': [
['gtest_target_type == "shared_library"', {
diff --git a/chrome/common/extensions/api/media_galleries.idl b/chrome/common/extensions/api/media_galleries.idl
index 941c7b7..a636881 100644
--- a/chrome/common/extensions/api/media_galleries.idl
+++ b/chrome/common/extensions/api/media_galleries.idl
@@ -104,13 +104,26 @@ namespace mediaGalleries {
long? height;
long? width;
+ // Defined for images only.
+ double? xResolution;
+ double? yResolution;
+
// Defined for audio and video. In seconds.
double? duration;
// Defined for images and video. In degrees.
long? rotation;
- // Generic metadata tags.
+ // Defined for images only.
+ DOMString? cameraMake;
+ DOMString? cameraModel;
+ double? exposureTimeSeconds;
+ boolean? flashFired;
+ double? fNumber;
+ double? focalLengthMm;
+ double? isoEquivalent;
+
+ // Defined for audio and video only.
DOMString? album;
DOMString? artist;
DOMString? comment;
diff --git a/chrome/installer/linux/debian/expected_deps b/chrome/installer/linux/debian/expected_deps
index a0a49c9..15a7194 100644
--- a/chrome/installer/linux/debian/expected_deps
+++ b/chrome/installer/linux/debian/expected_deps
@@ -5,6 +5,7 @@ libcairo2 (>= 1.6.0)
libcap2 (>= 2.10)
libcups2 (>= 1.4.0)
libdbus-1-3 (>= 1.2.14)
+libexif12
libexpat1 (>= 1.95.8)
libfontconfig1 (>= 2.8.0)
libfreetype6 (>= 2.3.9)
diff --git a/chrome/installer/linux/rpm/expected_deps_i386 b/chrome/installer/linux/rpm/expected_deps_i386
index 52fd504..920c3e3 100644
--- a/chrome/installer/linux/rpm/expected_deps_i386
+++ b/chrome/installer/linux/rpm/expected_deps_i386
@@ -24,6 +24,7 @@ libdbus-1.so.3
libdl.so.2
libdl.so.2(GLIBC_2.0)
libdl.so.2(GLIBC_2.1)
+libexif.so.12
libexpat.so.1
libfontconfig.so.1
libfreetype.so.6
diff --git a/chrome/installer/linux/rpm/expected_deps_x86_64 b/chrome/installer/linux/rpm/expected_deps_x86_64
index 1435e39..1e3f536 100644
--- a/chrome/installer/linux/rpm/expected_deps_x86_64
+++ b/chrome/installer/linux/rpm/expected_deps_x86_64
@@ -19,6 +19,7 @@ libcups.so.2()(64bit)
libdbus-1.so.3()(64bit)
libdl.so.2()(64bit)
libdl.so.2(GLIBC_2.2.5)(64bit)
+libexif.so.12()(64bit)
libexpat.so.1()(64bit)
libfontconfig.so.1()(64bit)
libfreetype.so.6()(64bit)
diff --git a/chrome/installer/linux/sysroot_scripts/packagelist.debian.wheezy.amd64 b/chrome/installer/linux/sysroot_scripts/packagelist.debian.wheezy.amd64
index c93eccb..3573518 100644
--- a/chrome/installer/linux/sysroot_scripts/packagelist.debian.wheezy.amd64
+++ b/chrome/installer/linux/sysroot_scripts/packagelist.debian.wheezy.amd64
@@ -61,6 +61,8 @@ main/libc/libcap2/libcap-dev_2.22-1.2_amd64.deb
main/libe/libexif/libexif12_0.6.20-3_amd64.deb
main/libe/libexif/libexif-dev_0.6.20-3_amd64.deb
main/libf/libffi/libffi5_3.0.10-3_amd64.deb
+main/libe/libexif/libexif12_0.6.20-3_amd64.deb
+main/libe/libexif/libexif-dev_0.6.20-3_amd64.deb
main/libg/libgcrypt11/libgcrypt11_1.5.0-5+deb7u1_amd64.deb
main/libg/libgcrypt11/libgcrypt11-dev_1.5.0-5+deb7u1_amd64.deb
main/libg/libgnome-keyring/libgnome-keyring0_3.4.1-1_amd64.deb
diff --git a/chrome/installer/mini_installer/chrome.release b/chrome/installer/mini_installer/chrome.release
index 66ab4de..fcacc38 100644
--- a/chrome/installer/mini_installer/chrome.release
+++ b/chrome/installer/mini_installer/chrome.release
@@ -28,6 +28,7 @@ ffmpegsumo.dll: %(VersionDir)s\
icudt.dll: %(VersionDir)s\
icudtl.dat: %(VersionDir)s\
libEGL.dll: %(VersionDir)s\
+libexif.dll: %(VersionDir)s\
libGLESv2.dll: %(VersionDir)s\
nacl64.exe: %(VersionDir)s\
nacl_irt_x86_32.nexe: %(VersionDir)s\
diff --git a/chrome/interactive_ui_tests.isolate b/chrome/interactive_ui_tests.isolate
index 492bcc2..d737995 100644
--- a/chrome/interactive_ui_tests.isolate
+++ b/chrome/interactive_ui_tests.isolate
@@ -57,6 +57,7 @@
'variables': {
'isolate_dependency_tracked': [
'<(PRODUCT_DIR)/ffmpegsumo.so',
+ '<(PRODUCT_DIR)/libexif.dylib',
'<(PRODUCT_DIR)/osmesa.so',
],
'isolate_dependency_untracked': [
@@ -83,6 +84,7 @@
'<(PRODUCT_DIR)/d3dcompiler_46.dll',
'<(PRODUCT_DIR)/ffmpegsumo.dll',
'<(PRODUCT_DIR)/libEGL.dll',
+ '<(PRODUCT_DIR)/libexif.dll',
'<(PRODUCT_DIR)/libGLESv2.dll',
'<(PRODUCT_DIR)/ppGoogleNaClPluginChrome.dll',
'<(PRODUCT_DIR)/osmesa.dll',
diff --git a/chrome/sync_integration_tests.isolate b/chrome/sync_integration_tests.isolate
index 4736fe8..104fd2f 100644
--- a/chrome/sync_integration_tests.isolate
+++ b/chrome/sync_integration_tests.isolate
@@ -56,6 +56,7 @@
],
'isolate_dependency_tracked': [
'<(PRODUCT_DIR)/ffmpegsumo.so',
+ '<(PRODUCT_DIR)/libexif.dylib',
],
'isolate_dependency_untracked': [
'<(PRODUCT_DIR)/<(mac_product_name) Framework.framework/',
diff --git a/chrome/test/data/extensions/api_test/media_galleries/media_metadata/test.js b/chrome/test/data/extensions/api_test/media_galleries/media_metadata/test.js
index 140a9ce..aadad30 100644
--- a/chrome/test/data/extensions/api_test/media_galleries/media_metadata/test.js
+++ b/chrome/test/data/extensions/api_test/media_galleries/media_metadata/test.js
@@ -32,6 +32,27 @@ function ImageMIMETypeOnlyTest() {
RunMetadataTest("test.jpg", {metadataType: 'mimeTypeOnly'}, verifyMetadata);
}
+function ImageTagsTest() {
+ function verifyMetadata(metadata) {
+ chrome.test.assertEq("image/jpeg", metadata.mimeType);
+ chrome.test.assertEq(5616, metadata.width);
+ chrome.test.assertEq(3744, metadata.height);
+ chrome.test.assertEq(0, metadata.rotation);
+ chrome.test.assertEq(300.0, metadata.xResolution);
+ chrome.test.assertEq(300.0, metadata.yResolution);
+ chrome.test.assertEq("Canon", metadata.cameraMake);
+ chrome.test.assertEq("Canon EOS 5D Mark II", metadata.cameraModel);
+ chrome.test.assertEq(0.01, metadata.exposureTimeSeconds);
+ chrome.test.assertFalse(metadata.flashFired);
+ chrome.test.assertEq(3.2, metadata.fNumber);
+ chrome.test.assertEq(100, metadata.focalLengthMm);
+ chrome.test.assertEq(1600, metadata.isoEquivalent);
+ chrome.test.succeed();
+ }
+
+ RunMetadataTest("test.jpg", {}, verifyMetadata);
+}
+
function MP3MIMETypeOnlyTest() {
function verifyMetadata(metadata) {
chrome.test.assertEq("audio/mpeg", metadata.mimeType);
@@ -92,7 +113,8 @@ chrome.test.getConfig(function(config) {
// Should still be able to sniff MP3 MIME type without proprietary codecs.
var testsToRun = [
- ImageMIMETypeOnlyTest
+ ImageMIMETypeOnlyTest,
+ ImageTagsTest
];
if (useProprietaryCodecs) {
diff --git a/chrome/tools/build/chromeos/FILES.cfg b/chrome/tools/build/chromeos/FILES.cfg
index 97e002a..2088df8 100644
--- a/chrome/tools/build/chromeos/FILES.cfg
+++ b/chrome/tools/build/chromeos/FILES.cfg
@@ -57,6 +57,10 @@ FILES = [
'buildtype': ['dev', 'official'],
},
{
+ 'filename': 'lib/libexif.so',
+ 'buildtype': ['dev', 'official'],
+ },
+ {
'filename': 'lib/libpeerconnection.so',
'buildtype': ['dev', 'official'],
'optional': ['dev', 'official'],
diff --git a/chrome/tools/build/mac/TESTS b/chrome/tools/build/mac/TESTS
index 331ac56..72af0ae2 100644
--- a/chrome/tools/build/mac/TESTS
+++ b/chrome/tools/build/mac/TESTS
@@ -1,2 +1,3 @@
sync_integration_tests
ffmpegsumo.so
+libexif.dylib
diff --git a/chrome/tools/build/mac/dump_product_syms b/chrome/tools/build/mac/dump_product_syms
index ad816f5..1f70c4c 100755
--- a/chrome/tools/build/mac/dump_product_syms
+++ b/chrome/tools/build/mac/dump_product_syms
@@ -82,6 +82,7 @@ SRC_NAMES=(
"crash_inspector"
"crash_report_sender.app"
"ffmpegsumo.so"
+ "libexif.dylib"
"libplugin_carbon_interpose.dylib"
"ppGoogleNaClPluginChrome.plugin"
"remoting_host_plugin.plugin"
diff --git a/chrome/tools/build/win/FILES.cfg b/chrome/tools/build/win/FILES.cfg
index df48704..9fe6352 100644
--- a/chrome/tools/build/win/FILES.cfg
+++ b/chrome/tools/build/win/FILES.cfg
@@ -100,6 +100,11 @@ FILES = [
'optional': ['dev', 'official'],
},
{
+ 'filename': 'libexif.dll',
+ 'buildtype': ['dev', 'official'],
+ 'filegroup': ['default', 'symsrc'],
+ },
+ {
'filename': 'libpeerconnection.dll',
'buildtype': ['dev', 'official'],
'optional': ['dev', 'official'],
diff --git a/chrome/unit_tests.isolate b/chrome/unit_tests.isolate
index 03c0be9..f803337 100644
--- a/chrome/unit_tests.isolate
+++ b/chrome/unit_tests.isolate
@@ -83,6 +83,7 @@
'variables': {
'isolate_dependency_tracked': [
'<(PRODUCT_DIR)/ffmpegsumo.so',
+ '<(PRODUCT_DIR)/libexif.dylib',
'<(PRODUCT_DIR)/osmesa.so',
],
'isolate_dependency_untracked': [
@@ -111,6 +112,7 @@
'isolate_dependency_tracked': [
'<(PRODUCT_DIR)/chrome_elf.dll',
'<(PRODUCT_DIR)/ffmpegsumo.dll',
+ '<(PRODUCT_DIR)/libexif.dll',
'<(PRODUCT_DIR)/osmesa.dll',
],
'isolate_dependency_untracked': [
diff --git a/chrome/utility/media_galleries/image_metadata_extractor.cc b/chrome/utility/media_galleries/image_metadata_extractor.cc
new file mode 100644
index 0000000..5329df3
--- /dev/null
+++ b/chrome/utility/media_galleries/image_metadata_extractor.cc
@@ -0,0 +1,279 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/utility/media_galleries/image_metadata_extractor.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "media/base/data_source.h"
+#include "net/base/io_buffer.h"
+
+extern "C" {
+#include <libexif/exif-data.h>
+} // extern "C"
+
+namespace metadata {
+
+namespace {
+
+const int kMaxBufferSize = 50 * 1024 * 1024; // Arbitrary maximum of 50MB.
+
+void FinishGetImageBytes(
+ net::DrainableIOBuffer* buffer,
+ media::DataSource* source,
+ const base::Callback<void(net::DrainableIOBuffer*)>& callback,
+ int bytes_read) {
+ if (bytes_read == media::DataSource::kReadError) {
+ callback.Run(NULL);
+ return;
+ }
+
+ buffer->DidConsume(bytes_read);
+ // Didn't get the whole file. Continue reading to get the rest.
+ if (buffer->BytesRemaining() > 0) {
+ source->Read(0, buffer->BytesRemaining(), (uint8*)buffer->data(),
+ base::Bind(&FinishGetImageBytes, make_scoped_refptr(buffer),
+ base::Unretained(source), callback));
+ return;
+ }
+
+ buffer->SetOffset(0);
+ callback.Run(make_scoped_refptr(buffer));
+}
+
+void GetImageBytes(
+ media::DataSource* source,
+ const base::Callback<void(net::DrainableIOBuffer*)>& callback) {
+ int64 size64 = 0;
+ if (!source->GetSize(&size64) || size64 > kMaxBufferSize)
+ return callback.Run(NULL);
+ int size = base::checked_cast<int>(size64);
+
+ scoped_refptr<net::DrainableIOBuffer> buffer(
+ new net::DrainableIOBuffer(new net::IOBuffer(size), size));
+ source->Read(0, buffer->BytesRemaining(), (uint8*)buffer->data(),
+ base::Bind(&FinishGetImageBytes, buffer,
+ base::Unretained(source), callback));
+}
+
+void ExtractInt(ExifData* data, ExifTag tag, int* result) {
+ DCHECK(result);
+
+ ExifEntry* entry = exif_data_get_entry(data, tag);
+
+ if (entry) {
+ ExifByteOrder order = exif_data_get_byte_order(data);
+
+ switch (entry->format) {
+ case EXIF_FORMAT_SHORT: {
+ ExifShort v = exif_get_short(entry->data, order);
+ *result = base::checked_cast<int>(v);
+ break;
+ }
+ case EXIF_FORMAT_LONG: {
+ ExifLong v = exif_get_long(entry->data, order);
+ // Ignore values that don't fit in a signed int - probably invalid data.
+ if (base::IsValueInRangeForNumericType<int>(v))
+ *result = base::checked_cast<int>(v);
+ break;
+ }
+ default: {
+ // Ignore all other entry formats.
+ }
+ }
+ }
+}
+
+void ExtractDouble(ExifData* data, ExifTag tag, double* result) {
+ DCHECK(result);
+
+ ExifEntry* entry = exif_data_get_entry(data, tag);
+
+ if (entry) {
+ ExifByteOrder order = exif_data_get_byte_order(data);
+
+ if (entry->format == EXIF_FORMAT_RATIONAL) {
+ ExifRational v = exif_get_rational(entry->data, order);
+ *result = base::checked_cast<double>(v.numerator) /
+ base::checked_cast<double>(v.denominator);
+ }
+ }
+}
+
+void ExtractString(ExifData* data, ExifTag tag, std::string* result) {
+ DCHECK(result);
+
+ ExifEntry* entry = exif_data_get_entry(data, tag);
+
+ if (entry) {
+ char buf[1024];
+ exif_entry_get_value(entry, buf, sizeof(buf));
+ *result = buf;
+ }
+}
+
+} // namespace
+
+ImageMetadataExtractor::ImageMetadataExtractor()
+ : extracted_(false),
+ width_(-1),
+ height_(-1),
+ rotation_(-1),
+ x_resolution_(-1),
+ y_resolution_(-1),
+ exposure_time_sec_(-1),
+ flash_fired_(false),
+ f_number_(-1),
+ focal_length_mm_(-1),
+ iso_equivalent_(-1) {
+}
+
+ImageMetadataExtractor::~ImageMetadataExtractor() {
+}
+
+void ImageMetadataExtractor::Extract(media::DataSource* source,
+ const DoneCallback& callback) {
+ DCHECK(!extracted_);
+
+ GetImageBytes(source, base::Bind(&ImageMetadataExtractor::FinishExtraction,
+ base::Unretained(this), callback));
+
+}
+
+int ImageMetadataExtractor::width() const {
+ DCHECK(extracted_);
+ return width_;
+}
+
+int ImageMetadataExtractor::height() const {
+ DCHECK(extracted_);
+ return height_;
+}
+
+int ImageMetadataExtractor::rotation() const {
+ DCHECK(extracted_);
+ return rotation_;
+}
+
+double ImageMetadataExtractor::x_resolution() const {
+ DCHECK(extracted_);
+ return x_resolution_;
+}
+
+double ImageMetadataExtractor::y_resolution() const {
+ DCHECK(extracted_);
+ return y_resolution_;
+}
+
+const std::string& ImageMetadataExtractor::date() const {
+ DCHECK(extracted_);
+ return date_;
+}
+
+const std::string& ImageMetadataExtractor::camera_make() const {
+ DCHECK(extracted_);
+ return camera_make_;
+}
+
+const std::string& ImageMetadataExtractor::camera_model() const {
+ DCHECK(extracted_);
+ return camera_model_;
+}
+
+double ImageMetadataExtractor::exposure_time_sec() const {
+ DCHECK(extracted_);
+ return exposure_time_sec_;
+}
+
+bool ImageMetadataExtractor::flash_fired() const {
+ DCHECK(extracted_);
+ return flash_fired_;
+}
+
+double ImageMetadataExtractor::f_number() const {
+ DCHECK(extracted_);
+ return f_number_;
+}
+
+double ImageMetadataExtractor::focal_length_mm() const {
+ DCHECK(extracted_);
+ return focal_length_mm_;
+}
+
+int ImageMetadataExtractor::iso_equivalent() const {
+ DCHECK(extracted_);
+ return iso_equivalent_;
+}
+
+void ImageMetadataExtractor::FinishExtraction(
+ const DoneCallback& callback, net::DrainableIOBuffer* buffer) {
+ if (!buffer) {
+ callback.Run(false);
+ return;
+ }
+
+ ExifData* data = exif_data_new_from_data(
+ (unsigned char*)buffer->data(), buffer->BytesRemaining());
+
+ if (!data) {
+ callback.Run(false);
+ return;
+ }
+
+ ExtractInt(data, EXIF_TAG_IMAGE_WIDTH, &width_);
+ ExtractInt(data, EXIF_TAG_IMAGE_LENGTH, &height_);
+
+ // We ignore the mirrored-aspect of the mirrored-orientations and just
+ // indicate the rotation. Mirrored-orientations are very rare.
+ int orientation = 0;
+ ExtractInt(data, EXIF_TAG_ORIENTATION, &orientation);
+ switch (orientation) {
+ case 1:
+ case 2:
+ rotation_ = 0;
+ break;
+ case 3:
+ case 4:
+ rotation_ = 180;
+ break;
+ case 5:
+ case 6:
+ rotation_ = 90;
+ break;
+ case 7:
+ case 8:
+ rotation_ = 270;
+ break;
+ }
+
+ ExtractDouble(data, EXIF_TAG_X_RESOLUTION, &x_resolution_);
+ ExtractDouble(data, EXIF_TAG_Y_RESOLUTION, &y_resolution_);
+
+ ExtractString(data, EXIF_TAG_DATE_TIME, &date_);
+
+ ExtractString(data, EXIF_TAG_MAKE, &camera_make_);
+ ExtractString(data, EXIF_TAG_MODEL, &camera_model_);
+ ExtractDouble(data, EXIF_TAG_EXPOSURE_TIME, &exposure_time_sec_);
+
+ int flash_value = -1;
+ ExtractInt(data, EXIF_TAG_FLASH, &flash_value);
+ if (flash_value >= 0) {
+ flash_fired_ = (flash_value & 0x1) != 0;
+ }
+
+ ExtractDouble(data, EXIF_TAG_FNUMBER, &f_number_);
+ ExtractDouble(data, EXIF_TAG_FOCAL_LENGTH, &focal_length_mm_);
+ ExtractInt(data, EXIF_TAG_ISO_SPEED_RATINGS, &iso_equivalent_);
+
+ exif_data_free(data);
+
+ extracted_ = true;
+
+ callback.Run(true);
+}
+
+} // namespace metadata
diff --git a/chrome/utility/media_galleries/image_metadata_extractor.h b/chrome/utility/media_galleries/image_metadata_extractor.h
new file mode 100644
index 0000000..6c059d0
--- /dev/null
+++ b/chrome/utility/media_galleries/image_metadata_extractor.h
@@ -0,0 +1,84 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_UTILITY_MEDIA_GALLERIES_IMAGE_METADATA_EXTRACTOR_H_
+#define CHROME_UTILITY_MEDIA_GALLERIES_IMAGE_METADATA_EXTRACTOR_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+
+namespace media {
+class DataSource;
+}
+
+namespace net {
+class DrainableIOBuffer;
+}
+
+namespace metadata {
+
+class ImageMetadataExtractor {
+ public:
+ typedef base::Callback<void(bool)> DoneCallback;
+
+ ImageMetadataExtractor();
+ ~ImageMetadataExtractor();
+
+ // |callback| called with whether or not the extraction succeeded. Should
+ // only be called once.
+ void Extract(media::DataSource* source, const DoneCallback& callback);
+
+ // Returns -1 if this could not be extracted.
+ int width() const;
+ int height() const;
+
+ // In degrees.
+ int rotation() const;
+
+ // In pixels per inch.
+ double x_resolution() const;
+ double y_resolution() const;
+
+ const std::string& date() const;
+
+ const std::string& camera_make() const;
+ const std::string& camera_model() const;
+ double exposure_time_sec() const;
+ bool flash_fired() const;
+ double f_number() const;
+ double focal_length_mm() const;
+ int iso_equivalent() const;
+
+ private:
+ void FinishExtraction(const DoneCallback& callback,
+ net::DrainableIOBuffer* buffer);
+
+ bool extracted_;
+
+ int width_;
+ int height_;
+
+ int rotation_;
+
+ double x_resolution_;
+ double y_resolution_;
+
+ std::string date_;
+
+ std::string camera_make_;
+ std::string camera_model_;
+ double exposure_time_sec_;
+ bool flash_fired_;
+ double f_number_;
+ double focal_length_mm_;
+ int iso_equivalent_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageMetadataExtractor);
+};
+
+} // namespace metadata
+
+#endif // CHROME_UTILITY_MEDIA_GALLERIES_IMAGE_METADATA_EXTRACTOR_H_
diff --git a/chrome/utility/media_galleries/image_metadata_extractor_unittest.cc b/chrome/utility/media_galleries/image_metadata_extractor_unittest.cc
new file mode 100644
index 0000000..04d3b1d
--- /dev/null
+++ b/chrome/utility/media_galleries/image_metadata_extractor_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/utility/media_galleries/image_metadata_extractor.h"
+#include "media/filters/file_data_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metadata {
+
+void QuitLoop(base::RunLoop* loop, bool* output, bool success) {
+ loop->Quit();
+ *output = success;
+}
+
+base::FilePath GetTestDataFilePath(const std::string& filename) {
+ base::FilePath path;
+ EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
+ path = path.AppendASCII("extensions").AppendASCII("api_test")
+ .AppendASCII("wallpaper").AppendASCII(filename);
+ return path;
+}
+
+scoped_ptr<ImageMetadataExtractor> GetExtractor(
+ const std::string& filename,
+ bool expected_result) {
+ media::FileDataSource source;
+ base::FilePath test_path;
+
+ EXPECT_TRUE(source.Initialize(GetTestDataFilePath(filename)));
+
+ scoped_ptr<ImageMetadataExtractor> extractor(new ImageMetadataExtractor);
+
+ base::RunLoop loop;
+ bool extracted = false;
+ extractor->Extract(&source, base::Bind(&QuitLoop, &loop, &extracted));
+ EXPECT_EQ(expected_result, extracted);
+
+ return extractor.Pass();
+}
+
+TEST(ImageMetadataExtractorTest, JPGFile) {
+ scoped_ptr<ImageMetadataExtractor> extractor =
+ GetExtractor("test.jpg", true);
+
+ EXPECT_EQ(5616, extractor->width());
+ EXPECT_EQ(3744, extractor->height());
+ EXPECT_EQ(0, extractor->rotation());
+ EXPECT_EQ(300.0, extractor->x_resolution());
+ EXPECT_EQ(300.0, extractor->y_resolution());
+ EXPECT_EQ("2012:03:01 17:06:07", extractor->date());
+ EXPECT_EQ("Canon", extractor->camera_make());
+ EXPECT_EQ("Canon EOS 5D Mark II", extractor->camera_model());
+ EXPECT_EQ(0.01, extractor->exposure_time_sec());
+ EXPECT_FALSE(extractor->flash_fired());
+ EXPECT_EQ(3.2, extractor->f_number());
+ EXPECT_EQ(100, extractor->focal_length_mm());
+ EXPECT_EQ(1600, extractor->iso_equivalent());
+}
+
+} // namespace metadata
diff --git a/chrome/utility/media_galleries/media_metadata_parser.cc b/chrome/utility/media_galleries/media_metadata_parser.cc
index 84b56af..07c54dc 100644
--- a/chrome/utility/media_galleries/media_metadata_parser.cc
+++ b/chrome/utility/media_galleries/media_metadata_parser.cc
@@ -11,6 +11,7 @@
#include "base/strings/string_util.h"
#include "base/task_runner_util.h"
#include "base/threading/thread.h"
+#include "chrome/utility/media_galleries/image_metadata_extractor.h"
#include "media/base/audio_video_metadata_extractor.h"
#include "media/base/data_source.h"
@@ -20,15 +21,28 @@ namespace {
void SetStringScopedPtr(const std::string& value,
scoped_ptr<std::string>* destination) {
+ DCHECK(destination);
if (!value.empty())
destination->reset(new std::string(value));
}
void SetIntScopedPtr(int value, scoped_ptr<int>* destination) {
+ DCHECK(destination);
if (value >= 0)
destination->reset(new int(value));
}
+void SetDoubleScopedPtr(double value, scoped_ptr<double>* destination) {
+ DCHECK(destination);
+ if (value >= 0)
+ destination->reset(new double(value));
+}
+
+void SetBoolScopedPtr(bool value, scoped_ptr<bool>* destination) {
+ DCHECK(destination);
+ destination->reset(new bool(value));
+}
+
// This runs on |media_thread_|, as the underlying FFmpeg operation is
// blocking, and the utility thread must not be blocked, so the media file
// bytes can be sent from the browser process to the utility process.
@@ -36,6 +50,7 @@ scoped_ptr<MediaMetadataParser::MediaMetadata> ParseAudioVideoMetadata(
media::DataSource* source,
scoped_ptr<MediaMetadataParser::MediaMetadata> metadata) {
DCHECK(source);
+ DCHECK(metadata.get());
media::AudioVideoMetadataExtractor extractor;
if (!extractor.Extract(source))
@@ -70,6 +85,39 @@ scoped_ptr<MediaMetadataParser::MediaMetadata> ParseAudioVideoMetadata(
return metadata.Pass();
}
+void FinishParseImageMetadata(
+ ImageMetadataExtractor* extractor,
+ scoped_ptr<MediaMetadataParser::MediaMetadata> metadata,
+ MediaMetadataParser::MetadataCallback callback,
+ bool extract_success) {
+ DCHECK(extractor);
+ DCHECK(metadata.get());
+
+ if (!extract_success) {
+ callback.Run(metadata.Pass());
+ return;
+ }
+
+ SetIntScopedPtr(extractor->height(), &metadata->height);
+ SetIntScopedPtr(extractor->width(), &metadata->width);
+
+ SetIntScopedPtr(extractor->rotation(), &metadata->rotation);
+
+ SetDoubleScopedPtr(extractor->x_resolution(), &metadata->x_resolution);
+ SetDoubleScopedPtr(extractor->y_resolution(), &metadata->y_resolution);
+ SetBoolScopedPtr(extractor->flash_fired(), &metadata->flash_fired);
+ SetStringScopedPtr(extractor->camera_make(), &metadata->camera_make);
+ SetStringScopedPtr(extractor->camera_model(), &metadata->camera_model);
+ SetDoubleScopedPtr(extractor->exposure_time_sec(),
+ &metadata->exposure_time_seconds);
+
+ SetDoubleScopedPtr(extractor->f_number(), &metadata->f_number);
+ SetDoubleScopedPtr(extractor->focal_length_mm(), &metadata->focal_length_mm);
+ SetDoubleScopedPtr(extractor->iso_equivalent(), &metadata->iso_equivalent);
+
+ callback.Run(metadata.Pass());
+}
+
} // namespace
MediaMetadataParser::MediaMetadataParser(media::DataSource* source,
@@ -96,6 +144,15 @@ void MediaMetadataParser::Start(const MetadataCallback& callback) {
return;
}
+ if (StartsWithASCII(mime_type_, "image/", true)) {
+ ImageMetadataExtractor* extractor = new ImageMetadataExtractor;
+ extractor->Extract(
+ source_,
+ base::Bind(&FinishParseImageMetadata, base::Owned(extractor),
+ base::Passed(&metadata), callback));
+ return;
+ }
+
// TODO(tommycli): Implement for image mime types.
callback.Run(metadata.Pass());
}
diff --git a/content/content_browsertests.isolate b/content/content_browsertests.isolate
index de3675f..8efed02 100644
--- a/content/content_browsertests.isolate
+++ b/content/content_browsertests.isolate
@@ -60,6 +60,7 @@
'<(PRODUCT_DIR)/clearkeycdmadapter.plugin',
'<(PRODUCT_DIR)/content_resources.pak',
'<(PRODUCT_DIR)/ffmpegsumo.so',
+ '<(PRODUCT_DIR)/libexif.dylib',
'<(PRODUCT_DIR)/plugins/TestNestscapePlugin.plugin',
'<(PRODUCT_DIR)/plugins/npapi_test_plugin.plugin',
],
@@ -81,6 +82,7 @@
'variables': {
'isolate_dependency_tracked': [
'<(PRODUCT_DIR)/ffmpegsumo.dll',
+ '<(PRODUCT_DIR)/libexif.dll',
'<(PRODUCT_DIR)/chrome_100_percent.pak',
'<(PRODUCT_DIR)/clearkeycdm.dll',
'<(PRODUCT_DIR)/clearkeycdmadapter.dll',
diff --git a/third_party/libexif/libexif.gyp b/third_party/libexif/libexif.gyp
index 20aa227..5da9a1f 100644
--- a/third_party/libexif/libexif.gyp
+++ b/third_party/libexif/libexif.gyp
@@ -5,15 +5,16 @@
{
'variables': {
'conditions': [
- # TODO(kmadhusu): We are not ready to build this library on Android.
- # Resolve the issues and build on Android.
- ['os_posix==1 and OS!="mac"', {
- 'use_system_libexif%': 0,
- }, { # os_posix != 1 or OS == "mac"
+ ['OS == "linux"', {
+ 'use_system_libexif%': 1,
+ }, { # OS != "linux"
'use_system_libexif%': 0,
}],
],
},
+ 'includes': [
+ '../../build/util/version.gypi',
+ ],
'conditions': [
['use_system_libexif==0', {
'targets': [
@@ -56,6 +57,19 @@
],
},
'conditions': [
+ ['clang==1', {
+ 'cflags': [
+ '-Wno-enum-conversion',
+ '-Wno-switch'
+ ],
+ 'xcode_settings': {
+ 'WARNING_CFLAGS': [
+ '-Wno-enum-conversion',
+ '-Wno-switch',
+ '-Wno-format',
+ ],
+ },
+ }],
['os_posix==1 and OS!="mac"', {
'cflags!': ['-fvisibility=hidden'],
}],
@@ -66,26 +80,25 @@
'mac_real_dsym': 1,
},
}],
- ],
- 'xcode_settings': {
+ ],
+ 'xcode_settings': {
'GCC_SYMBOLS_PRIVATE_EXTERN': 'NO', # no -fvisibility=hidden
- # TODO(kmadhusu): Copy this dylib to Versions folder.
- # (Do something similar to libplugin_carbon_interpose.dylib).
- 'DYLIB_INSTALL_NAME_BASE': '@executable_path/../../..',
+ 'DYLIB_INSTALL_NAME_BASE': '@rpath',
},
}],
['OS=="win"', {
'product_name': 'libexif',
- 'msvs_settings': {
- 'VCLinkerTool': {
- 'ModuleDefinitionFile': 'libexif.def',
- },
- },
+ 'sources': [
+ 'libexif.def',
+ ],
'defines': [
# This seems like a hack, but this is what WebKit Win does.
'snprintf=_snprintf',
'inline=__inline',
],
+ 'msvs_disabled_warnings': [
+ 4267, # size_t -> ExifLong truncation on amd64
+ ],
}],
],
},