1 // Copyright 2013 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/media_metadata_parser.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/memory/linked_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/string_util.h"
13 #include "base/task_runner_util.h"
14 #include "base/threading/thread.h"
15 #include "chrome/utility/media_galleries/image_metadata_extractor.h"
16 #include "media/base/audio_video_metadata_extractor.h"
17 #include "media/base/data_source.h"
18 #include "net/base/mime_sniffer.h"
19
20 namespace MediaGalleries = extensions::api::media_galleries;
21
22 namespace metadata {
23
24 namespace {
25
SetStringScopedPtr(const std::string & value,scoped_ptr<std::string> * destination)26 void SetStringScopedPtr(const std::string& value,
27 scoped_ptr<std::string>* destination) {
28 DCHECK(destination);
29 if (!value.empty())
30 destination->reset(new std::string(value));
31 }
32
SetIntScopedPtr(int value,scoped_ptr<int> * destination)33 void SetIntScopedPtr(int value, scoped_ptr<int>* destination) {
34 DCHECK(destination);
35 if (value >= 0)
36 destination->reset(new int(value));
37 }
38
SetDoubleScopedPtr(double value,scoped_ptr<double> * destination)39 void SetDoubleScopedPtr(double value, scoped_ptr<double>* destination) {
40 DCHECK(destination);
41 if (value >= 0)
42 destination->reset(new double(value));
43 }
44
SetBoolScopedPtr(bool value,scoped_ptr<bool> * destination)45 void SetBoolScopedPtr(bool value, scoped_ptr<bool>* destination) {
46 DCHECK(destination);
47 destination->reset(new bool(value));
48 }
49
50 // This runs on |media_thread_|, as the underlying FFmpeg operation is
51 // blocking, and the utility thread must not be blocked, so the media file
52 // bytes can be sent from the browser process to the utility process.
ParseAudioVideoMetadata(media::DataSource * source,bool get_attached_images,MediaMetadataParser::MediaMetadata * metadata,std::vector<AttachedImage> * attached_images)53 void ParseAudioVideoMetadata(
54 media::DataSource* source, bool get_attached_images,
55 MediaMetadataParser::MediaMetadata* metadata,
56 std::vector<AttachedImage>* attached_images) {
57 DCHECK(source);
58 DCHECK(metadata);
59 media::AudioVideoMetadataExtractor extractor;
60
61 if (!extractor.Extract(source, get_attached_images))
62 return;
63
64 if (extractor.duration() >= 0)
65 metadata->duration.reset(new double(extractor.duration()));
66
67 if (extractor.height() >= 0 && extractor.width() >= 0) {
68 metadata->height.reset(new int(extractor.height()));
69 metadata->width.reset(new int(extractor.width()));
70 }
71
72 SetStringScopedPtr(extractor.artist(), &metadata->artist);
73 SetStringScopedPtr(extractor.album(), &metadata->album);
74 SetStringScopedPtr(extractor.artist(), &metadata->artist);
75 SetStringScopedPtr(extractor.comment(), &metadata->comment);
76 SetStringScopedPtr(extractor.copyright(), &metadata->copyright);
77 SetIntScopedPtr(extractor.disc(), &metadata->disc);
78 SetStringScopedPtr(extractor.genre(), &metadata->genre);
79 SetStringScopedPtr(extractor.language(), &metadata->language);
80 SetIntScopedPtr(extractor.rotation(), &metadata->rotation);
81 SetStringScopedPtr(extractor.title(), &metadata->title);
82 SetIntScopedPtr(extractor.track(), &metadata->track);
83
84 for (media::AudioVideoMetadataExtractor::StreamInfoVector::const_iterator it =
85 extractor.stream_infos().begin();
86 it != extractor.stream_infos().end(); ++it) {
87 linked_ptr<MediaGalleries::StreamInfo> stream_info(
88 new MediaGalleries::StreamInfo);
89 stream_info->type = it->type;
90
91 for (std::map<std::string, std::string>::const_iterator tag_it =
92 it->tags.begin();
93 tag_it != it->tags.end(); ++tag_it) {
94 stream_info->tags.additional_properties.SetString(tag_it->first,
95 tag_it->second);
96 }
97
98 metadata->raw_tags.push_back(stream_info);
99 }
100
101 if (get_attached_images) {
102 for (std::vector<std::string>::const_iterator it =
103 extractor.attached_images_bytes().begin();
104 it != extractor.attached_images_bytes().end(); ++it) {
105 attached_images->push_back(AttachedImage());
106 attached_images->back().data = *it;
107 net::SniffMimeTypeFromLocalData(it->c_str(), it->length(),
108 &attached_images->back().type);
109 }
110 }
111 }
112
FinishParseAudioVideoMetadata(MediaMetadataParser::MetadataCallback callback,MediaMetadataParser::MediaMetadata * metadata,std::vector<AttachedImage> * attached_images)113 void FinishParseAudioVideoMetadata(
114 MediaMetadataParser::MetadataCallback callback,
115 MediaMetadataParser::MediaMetadata* metadata,
116 std::vector<AttachedImage>* attached_images) {
117 DCHECK(!callback.is_null());
118 DCHECK(metadata);
119 DCHECK(attached_images);
120
121 callback.Run(*metadata, *attached_images);
122 }
123
FinishParseImageMetadata(ImageMetadataExtractor * extractor,const std::string & mime_type,MediaMetadataParser::MetadataCallback callback,bool extract_success)124 void FinishParseImageMetadata(
125 ImageMetadataExtractor* extractor, const std::string& mime_type,
126 MediaMetadataParser::MetadataCallback callback, bool extract_success) {
127 DCHECK(extractor);
128 MediaMetadataParser::MediaMetadata metadata;
129 metadata.mime_type = mime_type;
130
131 if (!extract_success) {
132 callback.Run(metadata, std::vector<AttachedImage>());
133 return;
134 }
135
136 SetIntScopedPtr(extractor->height(), &metadata.height);
137 SetIntScopedPtr(extractor->width(), &metadata.width);
138
139 SetIntScopedPtr(extractor->rotation(), &metadata.rotation);
140
141 SetDoubleScopedPtr(extractor->x_resolution(), &metadata.x_resolution);
142 SetDoubleScopedPtr(extractor->y_resolution(), &metadata.y_resolution);
143 SetBoolScopedPtr(extractor->flash_fired(), &metadata.flash_fired);
144 SetStringScopedPtr(extractor->camera_make(), &metadata.camera_make);
145 SetStringScopedPtr(extractor->camera_model(), &metadata.camera_model);
146 SetDoubleScopedPtr(extractor->exposure_time_sec(),
147 &metadata.exposure_time_seconds);
148
149 SetDoubleScopedPtr(extractor->f_number(), &metadata.f_number);
150 SetDoubleScopedPtr(extractor->focal_length_mm(), &metadata.focal_length_mm);
151 SetDoubleScopedPtr(extractor->iso_equivalent(), &metadata.iso_equivalent);
152
153 callback.Run(metadata, std::vector<AttachedImage>());
154 }
155
156 } // namespace
157
MediaMetadataParser(media::DataSource * source,const std::string & mime_type,bool get_attached_images)158 MediaMetadataParser::MediaMetadataParser(media::DataSource* source,
159 const std::string& mime_type,
160 bool get_attached_images)
161 : source_(source),
162 mime_type_(mime_type),
163 get_attached_images_(get_attached_images) {
164 }
165
~MediaMetadataParser()166 MediaMetadataParser::~MediaMetadataParser() {}
167
Start(const MetadataCallback & callback)168 void MediaMetadataParser::Start(const MetadataCallback& callback) {
169 if (StartsWithASCII(mime_type_, "audio/", true) ||
170 StartsWithASCII(mime_type_, "video/", true)) {
171 MediaMetadata* metadata = new MediaMetadata;
172 metadata->mime_type = mime_type_;
173 std::vector<AttachedImage>* attached_images =
174 new std::vector<AttachedImage>;
175
176 media_thread_.reset(new base::Thread("media_thread"));
177 CHECK(media_thread_->Start());
178 media_thread_->message_loop_proxy()->PostTaskAndReply(
179 FROM_HERE,
180 base::Bind(&ParseAudioVideoMetadata, source_, get_attached_images_,
181 metadata, attached_images),
182 base::Bind(&FinishParseAudioVideoMetadata, callback,
183 base::Owned(metadata), base::Owned(attached_images)));
184 return;
185 }
186
187 if (StartsWithASCII(mime_type_, "image/", true)) {
188 ImageMetadataExtractor* extractor = new ImageMetadataExtractor;
189 extractor->Extract(
190 source_,
191 base::Bind(&FinishParseImageMetadata, base::Owned(extractor),
192 mime_type_, callback));
193 return;
194 }
195
196 callback.Run(MediaMetadata(), std::vector<AttachedImage>());
197 }
198
199 } // namespace metadata
200