• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "media/base/audio_video_metadata_extractor.h"
6 
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"
14 
15 namespace media {
16 
17 namespace {
18 
OnError(bool * succeeded)19 void OnError(bool* succeeded) {
20   *succeeded = false;
21 }
22 
23 // Returns true if the |tag| matches |expected_key|.
ExtractString(AVDictionaryEntry * tag,const char * expected_key,std::string * destination)24 bool ExtractString(AVDictionaryEntry* tag, const char* expected_key,
25                    std::string* destination) {
26   if (!LowerCaseEqualsASCII(std::string(tag->key), expected_key))
27     return false;
28 
29   if (destination->empty())
30     *destination = tag->value;
31 
32   return true;
33 }
34 
35 // Returns true if the |tag| matches |expected_key|.
ExtractInt(AVDictionaryEntry * tag,const char * expected_key,int * destination)36 bool ExtractInt(AVDictionaryEntry* tag, const char* expected_key,
37                 int* destination) {
38   if (!LowerCaseEqualsASCII(std::string(tag->key), expected_key))
39     return false;
40 
41   int temporary = -1;
42   if (*destination < 0 && base::StringToInt(tag->value, &temporary) &&
43       temporary >= 0) {
44     *destination = temporary;
45   }
46 
47   return true;
48 }
49 
50 // Set attached image size limit to 4MB. Chosen arbitrarily.
51 const int kAttachedImageSizeLimit = 4 * 1024 * 1024;
52 
53 }  // namespace
54 
StreamInfo()55 AudioVideoMetadataExtractor::StreamInfo::StreamInfo() {}
56 
~StreamInfo()57 AudioVideoMetadataExtractor::StreamInfo::~StreamInfo() {}
58 
AudioVideoMetadataExtractor()59 AudioVideoMetadataExtractor::AudioVideoMetadataExtractor()
60     : extracted_(false),
61       duration_(-1),
62       width_(-1),
63       height_(-1),
64       disc_(-1),
65       rotation_(-1),
66       track_(-1) {
67 }
68 
~AudioVideoMetadataExtractor()69 AudioVideoMetadataExtractor::~AudioVideoMetadataExtractor() {
70 }
71 
Extract(DataSource * source,bool extract_attached_images)72 bool AudioVideoMetadataExtractor::Extract(DataSource* source,
73                                           bool extract_attached_images) {
74   DCHECK(!extracted_);
75 
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();
80 
81   if (!glue.OpenContext())
82     return false;
83 
84   if (!read_ok)
85     return false;
86 
87   if (!format_context->iformat)
88     return false;
89 
90   if (avformat_find_stream_info(format_context, NULL) < 0)
91     return false;
92 
93   if (format_context->duration != AV_NOPTS_VALUE)
94     duration_ = static_cast<double>(format_context->duration) / AV_TIME_BASE;
95 
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);
100 
101   for (unsigned int i = 0; i < format_context->nb_streams; ++i) {
102     stream_infos_.push_back(StreamInfo());
103     StreamInfo& info = stream_infos_.back();
104 
105     AVStream* stream = format_context->streams[i];
106     if (!stream)
107       continue;
108 
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);
112 
113     if (!stream->codec)
114       continue;
115 
116     info.type = avcodec_get_name(stream->codec->codec_id);
117 
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;
123     }
124 
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);
135     }
136   }
137 
138   extracted_ = true;
139   return true;
140 }
141 
duration() const142 double AudioVideoMetadataExtractor::duration() const {
143   DCHECK(extracted_);
144   return duration_;
145 }
146 
width() const147 int AudioVideoMetadataExtractor::width() const {
148   DCHECK(extracted_);
149   return width_;
150 }
151 
height() const152 int AudioVideoMetadataExtractor::height() const {
153   DCHECK(extracted_);
154   return height_;
155 }
156 
rotation() const157 int AudioVideoMetadataExtractor::rotation() const {
158   DCHECK(extracted_);
159   return rotation_;
160 }
161 
album() const162 const std::string& AudioVideoMetadataExtractor::album() const {
163   DCHECK(extracted_);
164   return album_;
165 }
166 
artist() const167 const std::string& AudioVideoMetadataExtractor::artist() const {
168   DCHECK(extracted_);
169   return artist_;
170 }
171 
comment() const172 const std::string& AudioVideoMetadataExtractor::comment() const {
173   DCHECK(extracted_);
174   return comment_;
175 }
176 
copyright() const177 const std::string& AudioVideoMetadataExtractor::copyright() const {
178   DCHECK(extracted_);
179   return copyright_;
180 }
181 
date() const182 const std::string& AudioVideoMetadataExtractor::date() const {
183   DCHECK(extracted_);
184   return date_;
185 }
186 
disc() const187 int AudioVideoMetadataExtractor::disc() const {
188   DCHECK(extracted_);
189   return disc_;
190 }
191 
encoder() const192 const std::string& AudioVideoMetadataExtractor::encoder() const {
193   DCHECK(extracted_);
194   return encoder_;
195 }
196 
encoded_by() const197 const std::string& AudioVideoMetadataExtractor::encoded_by() const {
198   DCHECK(extracted_);
199   return encoded_by_;
200 }
201 
genre() const202 const std::string& AudioVideoMetadataExtractor::genre() const {
203   DCHECK(extracted_);
204   return genre_;
205 }
206 
language() const207 const std::string& AudioVideoMetadataExtractor::language() const {
208   DCHECK(extracted_);
209   return language_;
210 }
211 
title() const212 const std::string& AudioVideoMetadataExtractor::title() const {
213   DCHECK(extracted_);
214   return title_;
215 }
216 
track() const217 int AudioVideoMetadataExtractor::track() const {
218   DCHECK(extracted_);
219   return track_;
220 }
221 
222 const std::vector<AudioVideoMetadataExtractor::StreamInfo>&
stream_infos() const223 AudioVideoMetadataExtractor::stream_infos() const {
224   DCHECK(extracted_);
225   return stream_infos_;
226 }
227 
228 const std::vector<std::string>&
attached_images_bytes() const229 AudioVideoMetadataExtractor::attached_images_bytes() const {
230   DCHECK(extracted_);
231   return attached_images_bytes_;
232 }
233 
ExtractDictionary(AVDictionary * metadata,TagDictionary * raw_tags)234 void AudioVideoMetadataExtractor::ExtractDictionary(
235     AVDictionary* metadata, TagDictionary* raw_tags) {
236   if (!metadata)
237     return;
238 
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;
244 
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;
258   }
259 }
260 
261 }  // namespace media
262