summaryrefslogtreecommitdiffstats
path: root/media/base
diff options
context:
space:
mode:
authortommycli@chromium.org <tommycli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-08 02:16:38 +0000
committertommycli@chromium.org <tommycli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-08 02:16:38 +0000
commit6c49c27b7b34f6e03aed1e5ef1163a4b11e404e1 (patch)
tree48ce283ddde661edda8eaa6feff1b3d1ee20ef31 /media/base
parenta7af82ac5dc57673d20d39075cc0ad2790b7112e (diff)
downloadchromium_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.cc213
-rw-r--r--media/base/audio_video_metadata_extractor.h78
-rw-r--r--media/base/audio_video_metadata_extractor_unittest.cc78
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", &copyright_)) 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