diff options
author | tommycli@chromium.org <tommycli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-08 02:16:38 +0000 |
---|---|---|
committer | tommycli@chromium.org <tommycli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-08 02:16:38 +0000 |
commit | 6c49c27b7b34f6e03aed1e5ef1163a4b11e404e1 (patch) | |
tree | 48ce283ddde661edda8eaa6feff1b3d1ee20ef31 /media/base | |
parent | a7af82ac5dc57673d20d39075cc0ad2790b7112e (diff) | |
download | chromium_src-6c49c27b7b34f6e03aed1e5ef1163a4b11e404e1.zip chromium_src-6c49c27b7b34f6e03aed1e5ef1163a4b11e404e1.tar.gz chromium_src-6c49c27b7b34f6e03aed1e5ef1163a4b11e404e1.tar.bz2 |
Media Galleries API Metadata: Add audio video metadata extractor.
This CL adds a class for extraction of audio and video metadata using ffmpeg.
This is needed to implement the Media Galleries Metadata API for Chrome extensions.
BUG=318450
Review URL: https://codereview.chromium.org/121383002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@243470 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/base')
-rw-r--r-- | media/base/audio_video_metadata_extractor.cc | 213 | ||||
-rw-r--r-- | media/base/audio_video_metadata_extractor.h | 78 | ||||
-rw-r--r-- | media/base/audio_video_metadata_extractor_unittest.cc | 78 |
3 files changed, 369 insertions, 0 deletions
diff --git a/media/base/audio_video_metadata_extractor.cc b/media/base/audio_video_metadata_extractor.cc new file mode 100644 index 0000000..fe26d84 --- /dev/null +++ b/media/base/audio_video_metadata_extractor.cc @@ -0,0 +1,213 @@ +// 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 (!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 (!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; +} + +} // namespace + +AudioVideoMetadataExtractor::AudioVideoMetadataExtractor() + : extracted_(false), + duration_(-1), + width_(-1), + height_(-1), + disc_(-1), + track_(-1) { +} + +AudioVideoMetadataExtractor::~AudioVideoMetadataExtractor() { +} + +bool AudioVideoMetadataExtractor::Extract(DataSource* source) { + 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<double>(format_context->duration) / AV_TIME_BASE; + + ExtractDictionary(format_context->metadata); + + for (unsigned int i = 0; i < format_context->nb_streams; ++i) { + AVStream* stream = format_context->streams[i]; + if (!stream) + continue; + + // Ignore attached pictures for metadata extraction. + if ((stream->disposition & AV_DISPOSITION_ATTACHED_PIC) != 0) + continue; + + // Extract dictionary from streams also. Needed for containers that attach + // metadata to contained streams instead the container itself, like OGG. + ExtractDictionary(stream->metadata); + + if (!stream->codec) + continue; + + // Extract dimensions of largest stream that's not an attached picture. + 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; + } + } + + 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_; +} + +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_; +} + +void AudioVideoMetadataExtractor::ExtractDictionary(AVDictionary* metadata) { + if (!metadata) + return; + + AVDictionaryEntry* tag = NULL; + while ((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + 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 diff --git a/media/base/audio_video_metadata_extractor.h b/media/base/audio_video_metadata_extractor.h new file mode 100644 index 0000000..affe794 --- /dev/null +++ b/media/base/audio_video_metadata_extractor.h @@ -0,0 +1,78 @@ +// 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. + +#ifndef MEDIA_BASE_AUDIO_VIDEO_METADATA_EXTRACTOR_H_ +#define MEDIA_BASE_AUDIO_VIDEO_METADATA_EXTRACTOR_H_ + +#include <string> + +#include "base/basictypes.h" +#include "media/base/media_export.h" + +struct AVDictionary; + +namespace media { + +class DataSource; + +// This class extracts a string dictionary of metadata tags for audio and video +// files. It also provides the format name. +class MEDIA_EXPORT AudioVideoMetadataExtractor { + public: + AudioVideoMetadataExtractor(); + ~AudioVideoMetadataExtractor(); + + // Returns whether or not the fields were successfully extracted. Should only + // be called once. + bool Extract(DataSource* source); + + // Returns -1 if we cannot extract the duration. In seconds. + double duration() const; + + // Returns -1 for containers without video. + int width() const; + int height() const; + + // Returns -1 or an empty string if the value is undefined. + const std::string& album() const; + const std::string& artist() const; + const std::string& comment() const; + const std::string& copyright() const; + const std::string& date() const; + int disc() const; + const std::string& encoder() const; + const std::string& encoded_by() const; + const std::string& genre() const; + const std::string& language() const; + const std::string& title() const; + int track() const; + + private: + void ExtractDictionary(AVDictionary* metadata); + + bool extracted_; + + int duration_; + int width_; + int height_; + + std::string album_; + std::string artist_; + std::string comment_; + std::string copyright_; + std::string date_; + int disc_; + std::string encoder_; + std::string encoded_by_; + std::string genre_; + std::string language_; + std::string title_; + int track_; + + DISALLOW_COPY_AND_ASSIGN(AudioVideoMetadataExtractor); +}; + +} // namespace media + +#endif // MEDIA_BASE_AUDIO_VIDEO_METADATA_EXTRACTOR_H_ diff --git a/media/base/audio_video_metadata_extractor_unittest.cc b/media/base/audio_video_metadata_extractor_unittest.cc new file mode 100644 index 0000000..c55cd5d --- /dev/null +++ b/media/base/audio_video_metadata_extractor_unittest.cc @@ -0,0 +1,78 @@ +// 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 "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "build/build_config.h" +#include "media/base/audio_video_metadata_extractor.h" +#include "media/base/test_data_util.h" +#include "media/filters/file_data_source.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +scoped_ptr<AudioVideoMetadataExtractor> GetExtractor( + const std::string& filename, + bool expected_result, + double expected_duration, + int expected_width, + int expected_height) { + FileDataSource source; + EXPECT_TRUE(source.Initialize(GetTestDataFilePath(filename))); + + scoped_ptr<AudioVideoMetadataExtractor> extractor( + new AudioVideoMetadataExtractor); + bool extracted = extractor->Extract(&source); + EXPECT_EQ(expected_result, extracted); + + if (!extracted) + return extractor.Pass(); + + EXPECT_EQ(expected_duration, extractor->duration()); + + EXPECT_EQ(expected_width, extractor->width()); + EXPECT_EQ(expected_height, extractor->height()); + + return extractor.Pass(); +} + +TEST(AudioVideoMetadataExtractorTest, InvalidFile) { + GetExtractor("ten_byte_file", false, 0, -1, -1); +} + +TEST(AudioVideoMetadataExtractorTest, AudioOGG) { + scoped_ptr<AudioVideoMetadataExtractor> extractor = + GetExtractor("9ch.ogg", true, 0, -1, -1); + EXPECT_EQ("Processed by SoX", extractor->comment()); +} + +TEST(AudioVideoMetadataExtractorTest, AudioWAV) { + scoped_ptr<AudioVideoMetadataExtractor> extractor = + GetExtractor("sfx_u8.wav", true, 0, -1, -1); + EXPECT_EQ("Lavf54.37.100", extractor->encoder()); + EXPECT_EQ("Amadeus Pro", extractor->encoded_by()); +} + +TEST(AudioVideoMetadataExtractorTest, VideoWebM) { + scoped_ptr<AudioVideoMetadataExtractor> extractor = + GetExtractor("bear-320x240-multitrack.webm", true, 2, 320, 240); + EXPECT_EQ("Lavf53.9.0", extractor->encoder()); +} + +#if defined(USE_PROPRIETARY_CODECS) +TEST(AudioVideoMetadataExtractorTest, AudioMP3) { + scoped_ptr<AudioVideoMetadataExtractor> extractor = + GetExtractor("id3_png_test.mp3", true, 1, -1, -1); + + EXPECT_EQ("Airbag", extractor->title()); + EXPECT_EQ("Radiohead", extractor->artist()); + EXPECT_EQ("OK Computer", extractor->album()); + EXPECT_EQ(1, extractor->track()); + EXPECT_EQ("Alternative", extractor->genre()); + EXPECT_EQ("1997", extractor->date()); + EXPECT_EQ("Lavf54.4.100", extractor->encoder()); +} +#endif + +} // namespace media |