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 15namespace media { 16 17namespace { 18 19void OnError(bool* succeeded) { 20 *succeeded = false; 21} 22 23// Returns true if the |tag| matches |expected_key|. 24bool 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|. 36bool 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. 51const int kAttachedImageSizeLimit = 4 * 1024 * 1024; 52 53} // namespace 54 55AudioVideoMetadataExtractor::StreamInfo::StreamInfo() {} 56 57AudioVideoMetadataExtractor::StreamInfo::~StreamInfo() {} 58 59AudioVideoMetadataExtractor::AudioVideoMetadataExtractor() 60 : extracted_(false), 61 duration_(-1), 62 width_(-1), 63 height_(-1), 64 disc_(-1), 65 rotation_(-1), 66 track_(-1) { 67} 68 69AudioVideoMetadataExtractor::~AudioVideoMetadataExtractor() { 70} 71 72bool 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 142double AudioVideoMetadataExtractor::duration() const { 143 DCHECK(extracted_); 144 return duration_; 145} 146 147int AudioVideoMetadataExtractor::width() const { 148 DCHECK(extracted_); 149 return width_; 150} 151 152int AudioVideoMetadataExtractor::height() const { 153 DCHECK(extracted_); 154 return height_; 155} 156 157int AudioVideoMetadataExtractor::rotation() const { 158 DCHECK(extracted_); 159 return rotation_; 160} 161 162const std::string& AudioVideoMetadataExtractor::album() const { 163 DCHECK(extracted_); 164 return album_; 165} 166 167const std::string& AudioVideoMetadataExtractor::artist() const { 168 DCHECK(extracted_); 169 return artist_; 170} 171 172const std::string& AudioVideoMetadataExtractor::comment() const { 173 DCHECK(extracted_); 174 return comment_; 175} 176 177const std::string& AudioVideoMetadataExtractor::copyright() const { 178 DCHECK(extracted_); 179 return copyright_; 180} 181 182const std::string& AudioVideoMetadataExtractor::date() const { 183 DCHECK(extracted_); 184 return date_; 185} 186 187int AudioVideoMetadataExtractor::disc() const { 188 DCHECK(extracted_); 189 return disc_; 190} 191 192const std::string& AudioVideoMetadataExtractor::encoder() const { 193 DCHECK(extracted_); 194 return encoder_; 195} 196 197const std::string& AudioVideoMetadataExtractor::encoded_by() const { 198 DCHECK(extracted_); 199 return encoded_by_; 200} 201 202const std::string& AudioVideoMetadataExtractor::genre() const { 203 DCHECK(extracted_); 204 return genre_; 205} 206 207const std::string& AudioVideoMetadataExtractor::language() const { 208 DCHECK(extracted_); 209 return language_; 210} 211 212const std::string& AudioVideoMetadataExtractor::title() const { 213 DCHECK(extracted_); 214 return title_; 215} 216 217int AudioVideoMetadataExtractor::track() const { 218 DCHECK(extracted_); 219 return track_; 220} 221 222const std::vector<AudioVideoMetadataExtractor::StreamInfo>& 223AudioVideoMetadataExtractor::stream_infos() const { 224 DCHECK(extracted_); 225 return stream_infos_; 226} 227 228const std::vector<std::string>& 229AudioVideoMetadataExtractor::attached_images_bytes() const { 230 DCHECK(extracted_); 231 return attached_images_bytes_; 232} 233 234void 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", ©right_)) 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