Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / base / audio_video_metadata_extractor.cc
blob0ba36e12062dbb79425181a911214f0a9fc67acf
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.
5 #include "media/base/audio_video_metadata_extractor.h"
7 #include "base/bind.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/string_util.h"
10 #include "base/time/time.h"
11 #include "media/ffmpeg/ffmpeg_common.h"
12 #include "media/filters/blocking_url_protocol.h"
13 #include "media/filters/ffmpeg_glue.h"
15 namespace media {
17 namespace {
19 void OnError(bool* succeeded) {
20 *succeeded = false;
23 // Returns true if the |tag| matches |expected_key|.
24 bool ExtractString(AVDictionaryEntry* tag, const char* expected_key,
25 std::string* destination) {
26 if (!base::LowerCaseEqualsASCII(std::string(tag->key), expected_key))
27 return false;
29 if (destination->empty())
30 *destination = tag->value;
32 return true;
35 // Returns true if the |tag| matches |expected_key|.
36 bool ExtractInt(AVDictionaryEntry* tag, const char* expected_key,
37 int* destination) {
38 if (!base::LowerCaseEqualsASCII(std::string(tag->key), expected_key))
39 return false;
41 int temporary = -1;
42 if (*destination < 0 && base::StringToInt(tag->value, &temporary) &&
43 temporary >= 0) {
44 *destination = temporary;
47 return true;
50 // Set attached image size limit to 4MB. Chosen arbitrarily.
51 const int kAttachedImageSizeLimit = 4 * 1024 * 1024;
53 } // namespace
55 AudioVideoMetadataExtractor::StreamInfo::StreamInfo() {}
57 AudioVideoMetadataExtractor::StreamInfo::~StreamInfo() {}
59 AudioVideoMetadataExtractor::AudioVideoMetadataExtractor()
60 : extracted_(false),
61 duration_(-1),
62 width_(-1),
63 height_(-1),
64 disc_(-1),
65 rotation_(-1),
66 track_(-1) {
69 AudioVideoMetadataExtractor::~AudioVideoMetadataExtractor() {
72 bool AudioVideoMetadataExtractor::Extract(DataSource* source,
73 bool extract_attached_images) {
74 DCHECK(!extracted_);
76 bool read_ok = true;
77 media::BlockingUrlProtocol protocol(source, base::Bind(&OnError, &read_ok));
78 media::FFmpegGlue glue(&protocol);
79 AVFormatContext* format_context = glue.format_context();
81 if (!glue.OpenContext())
82 return false;
84 if (!read_ok)
85 return false;
87 if (!format_context->iformat)
88 return false;
90 if (avformat_find_stream_info(format_context, NULL) < 0)
91 return false;
93 if (format_context->duration != AV_NOPTS_VALUE)
94 duration_ = static_cast<double>(format_context->duration) / AV_TIME_BASE;
96 stream_infos_.push_back(StreamInfo());
97 StreamInfo& container_info = stream_infos_.back();
98 container_info.type = format_context->iformat->name;
99 ExtractDictionary(format_context->metadata, &container_info.tags);
101 for (unsigned int i = 0; i < format_context->nb_streams; ++i) {
102 stream_infos_.push_back(StreamInfo());
103 StreamInfo& info = stream_infos_.back();
105 AVStream* stream = format_context->streams[i];
106 if (!stream)
107 continue;
109 // Extract dictionary from streams also. Needed for containers that attach
110 // metadata to contained streams instead the container itself, like OGG.
111 ExtractDictionary(stream->metadata, &info.tags);
113 if (!stream->codec)
114 continue;
116 info.type = avcodec_get_name(stream->codec->codec_id);
118 // Extract dimensions of largest stream that's not an attached image.
119 if (stream->codec->width > 0 && stream->codec->width > width_ &&
120 stream->codec->height > 0 && stream->codec->height > height_) {
121 width_ = stream->codec->width;
122 height_ = stream->codec->height;
125 // Extract attached image if requested.
126 if (extract_attached_images &&
127 stream->disposition == AV_DISPOSITION_ATTACHED_PIC &&
128 stream->attached_pic.size > 0 &&
129 stream->attached_pic.size <= kAttachedImageSizeLimit &&
130 stream->attached_pic.data != NULL) {
131 attached_images_bytes_.push_back(std::string());
132 attached_images_bytes_.back().assign(
133 reinterpret_cast<const char*>(stream->attached_pic.data),
134 stream->attached_pic.size);
138 extracted_ = true;
139 return true;
142 double AudioVideoMetadataExtractor::duration() const {
143 DCHECK(extracted_);
144 return duration_;
147 int AudioVideoMetadataExtractor::width() const {
148 DCHECK(extracted_);
149 return width_;
152 int AudioVideoMetadataExtractor::height() const {
153 DCHECK(extracted_);
154 return height_;
157 int AudioVideoMetadataExtractor::rotation() const {
158 DCHECK(extracted_);
159 return rotation_;
162 const std::string& AudioVideoMetadataExtractor::album() const {
163 DCHECK(extracted_);
164 return album_;
167 const std::string& AudioVideoMetadataExtractor::artist() const {
168 DCHECK(extracted_);
169 return artist_;
172 const std::string& AudioVideoMetadataExtractor::comment() const {
173 DCHECK(extracted_);
174 return comment_;
177 const std::string& AudioVideoMetadataExtractor::copyright() const {
178 DCHECK(extracted_);
179 return copyright_;
182 const std::string& AudioVideoMetadataExtractor::date() const {
183 DCHECK(extracted_);
184 return date_;
187 int AudioVideoMetadataExtractor::disc() const {
188 DCHECK(extracted_);
189 return disc_;
192 const std::string& AudioVideoMetadataExtractor::encoder() const {
193 DCHECK(extracted_);
194 return encoder_;
197 const std::string& AudioVideoMetadataExtractor::encoded_by() const {
198 DCHECK(extracted_);
199 return encoded_by_;
202 const std::string& AudioVideoMetadataExtractor::genre() const {
203 DCHECK(extracted_);
204 return genre_;
207 const std::string& AudioVideoMetadataExtractor::language() const {
208 DCHECK(extracted_);
209 return language_;
212 const std::string& AudioVideoMetadataExtractor::title() const {
213 DCHECK(extracted_);
214 return title_;
217 int AudioVideoMetadataExtractor::track() const {
218 DCHECK(extracted_);
219 return track_;
222 const std::vector<AudioVideoMetadataExtractor::StreamInfo>&
223 AudioVideoMetadataExtractor::stream_infos() const {
224 DCHECK(extracted_);
225 return stream_infos_;
228 const std::vector<std::string>&
229 AudioVideoMetadataExtractor::attached_images_bytes() const {
230 DCHECK(extracted_);
231 return attached_images_bytes_;
234 void AudioVideoMetadataExtractor::ExtractDictionary(
235 AVDictionary* metadata, TagDictionary* raw_tags) {
236 if (!metadata)
237 return;
239 for (AVDictionaryEntry* tag =
240 av_dict_get(metadata, "", NULL, AV_DICT_IGNORE_SUFFIX);
241 tag; tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX)) {
242 if (raw_tags->find(tag->key) == raw_tags->end())
243 (*raw_tags)[tag->key] = tag->value;
245 if (ExtractInt(tag, "rotate", &rotation_)) continue;
246 if (ExtractString(tag, "album", &album_)) continue;
247 if (ExtractString(tag, "artist", &artist_)) continue;
248 if (ExtractString(tag, "comment", &comment_)) continue;
249 if (ExtractString(tag, "copyright", &copyright_)) continue;
250 if (ExtractString(tag, "date", &date_)) continue;
251 if (ExtractInt(tag, "disc", &disc_)) continue;
252 if (ExtractString(tag, "encoder", &encoder_)) continue;
253 if (ExtractString(tag, "encoded_by", &encoded_by_)) continue;
254 if (ExtractString(tag, "genre", &genre_)) continue;
255 if (ExtractString(tag, "language", &language_)) continue;
256 if (ExtractString(tag, "title", &title_)) continue;
257 if (ExtractInt(tag, "track", &track_)) continue;
261 } // namespace media