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
20namespace MediaGalleries = extensions::api::media_galleries;
21
22namespace metadata {
23
24namespace {
25
26void 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
33void SetIntScopedPtr(int value, scoped_ptr<int>* destination) {
34  DCHECK(destination);
35  if (value >= 0)
36    destination->reset(new int(value));
37}
38
39void SetDoubleScopedPtr(double value, scoped_ptr<double>* destination) {
40  DCHECK(destination);
41  if (value >= 0)
42    destination->reset(new double(value));
43}
44
45void 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.
53void 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
113void 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
124void 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
158MediaMetadataParser::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
166MediaMetadataParser::~MediaMetadataParser() {}
167
168void 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