// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/base/audio_video_metadata_extractor.h" #include "base/bind.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/time/time.h" #include "media/ffmpeg/ffmpeg_common.h" #include "media/filters/blocking_url_protocol.h" #include "media/filters/ffmpeg_glue.h" namespace media { namespace { void OnError(bool* succeeded) { *succeeded = false; } // Returns true if the |tag| matches |expected_key|. bool ExtractString(AVDictionaryEntry* tag, const char* expected_key, std::string* destination) { if (!base::LowerCaseEqualsASCII(std::string(tag->key), expected_key)) return false; if (destination->empty()) *destination = tag->value; return true; } // Returns true if the |tag| matches |expected_key|. bool ExtractInt(AVDictionaryEntry* tag, const char* expected_key, int* destination) { if (!base::LowerCaseEqualsASCII(std::string(tag->key), expected_key)) return false; int temporary = -1; if (*destination < 0 && base::StringToInt(tag->value, &temporary) && temporary >= 0) { *destination = temporary; } return true; } // Set attached image size limit to 4MB. Chosen arbitrarily. const int kAttachedImageSizeLimit = 4 * 1024 * 1024; } // namespace AudioVideoMetadataExtractor::StreamInfo::StreamInfo() {} AudioVideoMetadataExtractor::StreamInfo::~StreamInfo() {} AudioVideoMetadataExtractor::AudioVideoMetadataExtractor() : extracted_(false), duration_(-1), width_(-1), height_(-1), disc_(-1), rotation_(-1), track_(-1) { } AudioVideoMetadataExtractor::~AudioVideoMetadataExtractor() { } bool AudioVideoMetadataExtractor::Extract(DataSource* source, bool extract_attached_images) { DCHECK(!extracted_); bool read_ok = true; media::BlockingUrlProtocol protocol(source, base::Bind(&OnError, &read_ok)); media::FFmpegGlue glue(&protocol); AVFormatContext* format_context = glue.format_context(); if (!glue.OpenContext()) return false; if (!read_ok) return false; if (!format_context->iformat) return false; if (avformat_find_stream_info(format_context, NULL) < 0) return false; if (format_context->duration != AV_NOPTS_VALUE) duration_ = static_cast(format_context->duration) / AV_TIME_BASE; stream_infos_.push_back(StreamInfo()); StreamInfo& container_info = stream_infos_.back(); container_info.type = format_context->iformat->name; ExtractDictionary(format_context->metadata, &container_info.tags); for (unsigned int i = 0; i < format_context->nb_streams; ++i) { stream_infos_.push_back(StreamInfo()); StreamInfo& info = stream_infos_.back(); AVStream* stream = format_context->streams[i]; if (!stream) continue; // Extract dictionary from streams also. Needed for containers that attach // metadata to contained streams instead the container itself, like OGG. ExtractDictionary(stream->metadata, &info.tags); if (!stream->codec) continue; info.type = avcodec_get_name(stream->codec->codec_id); // Extract dimensions of largest stream that's not an attached image. if (stream->codec->width > 0 && stream->codec->width > width_ && stream->codec->height > 0 && stream->codec->height > height_) { width_ = stream->codec->width; height_ = stream->codec->height; } // Extract attached image if requested. if (extract_attached_images && stream->disposition == AV_DISPOSITION_ATTACHED_PIC && stream->attached_pic.size > 0 && stream->attached_pic.size <= kAttachedImageSizeLimit && stream->attached_pic.data != NULL) { attached_images_bytes_.push_back(std::string()); attached_images_bytes_.back().assign( reinterpret_cast(stream->attached_pic.data), stream->attached_pic.size); } } extracted_ = true; return true; } double AudioVideoMetadataExtractor::duration() const { DCHECK(extracted_); return duration_; } int AudioVideoMetadataExtractor::width() const { DCHECK(extracted_); return width_; } int AudioVideoMetadataExtractor::height() const { DCHECK(extracted_); return height_; } int AudioVideoMetadataExtractor::rotation() const { DCHECK(extracted_); return rotation_; } const std::string& AudioVideoMetadataExtractor::album() const { DCHECK(extracted_); return album_; } const std::string& AudioVideoMetadataExtractor::artist() const { DCHECK(extracted_); return artist_; } const std::string& AudioVideoMetadataExtractor::comment() const { DCHECK(extracted_); return comment_; } const std::string& AudioVideoMetadataExtractor::copyright() const { DCHECK(extracted_); return copyright_; } const std::string& AudioVideoMetadataExtractor::date() const { DCHECK(extracted_); return date_; } int AudioVideoMetadataExtractor::disc() const { DCHECK(extracted_); return disc_; } const std::string& AudioVideoMetadataExtractor::encoder() const { DCHECK(extracted_); return encoder_; } const std::string& AudioVideoMetadataExtractor::encoded_by() const { DCHECK(extracted_); return encoded_by_; } const std::string& AudioVideoMetadataExtractor::genre() const { DCHECK(extracted_); return genre_; } const std::string& AudioVideoMetadataExtractor::language() const { DCHECK(extracted_); return language_; } const std::string& AudioVideoMetadataExtractor::title() const { DCHECK(extracted_); return title_; } int AudioVideoMetadataExtractor::track() const { DCHECK(extracted_); return track_; } const std::vector& AudioVideoMetadataExtractor::stream_infos() const { DCHECK(extracted_); return stream_infos_; } const std::vector& AudioVideoMetadataExtractor::attached_images_bytes() const { DCHECK(extracted_); return attached_images_bytes_; } void AudioVideoMetadataExtractor::ExtractDictionary( AVDictionary* metadata, TagDictionary* raw_tags) { if (!metadata) return; for (AVDictionaryEntry* tag = av_dict_get(metadata, "", NULL, AV_DICT_IGNORE_SUFFIX); tag; tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX)) { if (raw_tags->find(tag->key) == raw_tags->end()) (*raw_tags)[tag->key] = tag->value; if (ExtractInt(tag, "rotate", &rotation_)) continue; if (ExtractString(tag, "album", &album_)) continue; if (ExtractString(tag, "artist", &artist_)) continue; if (ExtractString(tag, "comment", &comment_)) continue; if (ExtractString(tag, "copyright", ©right_)) continue; if (ExtractString(tag, "date", &date_)) continue; if (ExtractInt(tag, "disc", &disc_)) continue; if (ExtractString(tag, "encoder", &encoder_)) continue; if (ExtractString(tag, "encoded_by", &encoded_by_)) continue; if (ExtractString(tag, "genre", &genre_)) continue; if (ExtractString(tag, "language", &language_)) continue; if (ExtractString(tag, "title", &title_)) continue; if (ExtractInt(tag, "track", &track_)) continue; } } } // namespace media