Commit e677dfa5 authored by tommycli@chromium.org's avatar tommycli@chromium.org

Media: Add thumbnail extraction to AudioVideoMetadataExtractor.

Spinoff of patch https://codereview.chromium.org/250143002/

For trivial change in chrome/utility/media_galleries/media_metadata_parser.cc:
TBR=vandebo

BUG=304290

Review URL: https://codereview.chromium.org/270873003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269588 0039d316-1c4b-4281-b951-d872f2087c98
parent 8c3b86ea
...@@ -56,7 +56,8 @@ scoped_ptr<MediaMetadataParser::MediaMetadata> ParseAudioVideoMetadata( ...@@ -56,7 +56,8 @@ scoped_ptr<MediaMetadataParser::MediaMetadata> ParseAudioVideoMetadata(
DCHECK(metadata.get()); DCHECK(metadata.get());
media::AudioVideoMetadataExtractor extractor; media::AudioVideoMetadataExtractor extractor;
if (!extractor.Extract(source)) // TODO(tommycli): Add attached picture extraction.
if (!extractor.Extract(source, false /* extract_attached_pics */))
return metadata.Pass(); return metadata.Pass();
if (extractor.duration() >= 0) if (extractor.duration() >= 0)
......
...@@ -47,6 +47,9 @@ bool ExtractInt(AVDictionaryEntry* tag, const char* expected_key, ...@@ -47,6 +47,9 @@ bool ExtractInt(AVDictionaryEntry* tag, const char* expected_key,
return true; return true;
} }
// Set attached image size limit to 4MB. Chosen arbitrarily.
const int kAttachedImageSizeLimit = 4 * 1024 * 1024;
} // namespace } // namespace
AudioVideoMetadataExtractor::StreamInfo::StreamInfo() {} AudioVideoMetadataExtractor::StreamInfo::StreamInfo() {}
...@@ -66,7 +69,8 @@ AudioVideoMetadataExtractor::AudioVideoMetadataExtractor() ...@@ -66,7 +69,8 @@ AudioVideoMetadataExtractor::AudioVideoMetadataExtractor()
AudioVideoMetadataExtractor::~AudioVideoMetadataExtractor() { AudioVideoMetadataExtractor::~AudioVideoMetadataExtractor() {
} }
bool AudioVideoMetadataExtractor::Extract(DataSource* source) { bool AudioVideoMetadataExtractor::Extract(DataSource* source,
bool extract_attached_images) {
DCHECK(!extracted_); DCHECK(!extracted_);
bool read_ok = true; bool read_ok = true;
...@@ -111,12 +115,24 @@ bool AudioVideoMetadataExtractor::Extract(DataSource* source) { ...@@ -111,12 +115,24 @@ bool AudioVideoMetadataExtractor::Extract(DataSource* source) {
info.type = avcodec_get_name(stream->codec->codec_id); info.type = avcodec_get_name(stream->codec->codec_id);
// Extract dimensions of largest stream that's not an attached picture. // Extract dimensions of largest stream that's not an attached image.
if (stream->codec->width > 0 && stream->codec->width > width_ && if (stream->codec->width > 0 && stream->codec->width > width_ &&
stream->codec->height > 0 && stream->codec->height > height_) { stream->codec->height > 0 && stream->codec->height > height_) {
width_ = stream->codec->width; width_ = stream->codec->width;
height_ = stream->codec->height; 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<const char*>(stream->attached_pic.data),
stream->attached_pic.size);
}
} }
extracted_ = true; extracted_ = true;
...@@ -209,6 +225,12 @@ AudioVideoMetadataExtractor::stream_infos() const { ...@@ -209,6 +225,12 @@ AudioVideoMetadataExtractor::stream_infos() const {
return stream_infos_; return stream_infos_;
} }
const std::vector<std::string>&
AudioVideoMetadataExtractor::attached_images_bytes() const {
DCHECK(extracted_);
return attached_images_bytes_;
}
void AudioVideoMetadataExtractor::ExtractDictionary( void AudioVideoMetadataExtractor::ExtractDictionary(
AVDictionary* metadata, TagDictionary* raw_tags) { AVDictionary* metadata, TagDictionary* raw_tags) {
if (!metadata) if (!metadata)
......
...@@ -38,7 +38,7 @@ class MEDIA_EXPORT AudioVideoMetadataExtractor { ...@@ -38,7 +38,7 @@ class MEDIA_EXPORT AudioVideoMetadataExtractor {
// Returns whether or not the fields were successfully extracted. Should only // Returns whether or not the fields were successfully extracted. Should only
// be called once. // be called once.
bool Extract(DataSource* source); bool Extract(DataSource* source, bool extract_attached_pics);
// Returns -1 if we cannot extract the duration. In seconds. // Returns -1 if we cannot extract the duration. In seconds.
double duration() const; double duration() const;
...@@ -67,6 +67,10 @@ class MEDIA_EXPORT AudioVideoMetadataExtractor { ...@@ -67,6 +67,10 @@ class MEDIA_EXPORT AudioVideoMetadataExtractor {
// First element is the container. Subsequent elements are the child streams. // First element is the container. Subsequent elements are the child streams.
const StreamInfoVector& stream_infos() const; const StreamInfoVector& stream_infos() const;
// Empty if Extract call did not request attached images, or if no attached
// images were found.
const std::vector<std::string>& attached_images_bytes() const;
private: private:
void ExtractDictionary(AVDictionary* metadata, TagDictionary* raw_tags); void ExtractDictionary(AVDictionary* metadata, TagDictionary* raw_tags);
...@@ -92,6 +96,8 @@ class MEDIA_EXPORT AudioVideoMetadataExtractor { ...@@ -92,6 +96,8 @@ class MEDIA_EXPORT AudioVideoMetadataExtractor {
StreamInfoVector stream_infos_; StreamInfoVector stream_infos_;
std::vector<std::string> attached_images_bytes_;
DISALLOW_COPY_AND_ASSIGN(AudioVideoMetadataExtractor); DISALLOW_COPY_AND_ASSIGN(AudioVideoMetadataExtractor);
}; };
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/sha1.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "media/base/audio_video_metadata_extractor.h" #include "media/base/audio_video_metadata_extractor.h"
#include "media/base/test_data_util.h" #include "media/base/test_data_util.h"
...@@ -14,6 +15,7 @@ namespace media { ...@@ -14,6 +15,7 @@ namespace media {
scoped_ptr<AudioVideoMetadataExtractor> GetExtractor( scoped_ptr<AudioVideoMetadataExtractor> GetExtractor(
const std::string& filename, const std::string& filename,
bool extract_attached_images,
bool expected_result, bool expected_result,
double expected_duration, double expected_duration,
int expected_width, int expected_width,
...@@ -23,7 +25,7 @@ scoped_ptr<AudioVideoMetadataExtractor> GetExtractor( ...@@ -23,7 +25,7 @@ scoped_ptr<AudioVideoMetadataExtractor> GetExtractor(
scoped_ptr<AudioVideoMetadataExtractor> extractor( scoped_ptr<AudioVideoMetadataExtractor> extractor(
new AudioVideoMetadataExtractor); new AudioVideoMetadataExtractor);
bool extracted = extractor->Extract(&source); bool extracted = extractor->Extract(&source, extract_attached_images);
EXPECT_EQ(expected_result, extracted); EXPECT_EQ(expected_result, extracted);
if (!extracted) if (!extracted)
...@@ -38,12 +40,12 @@ scoped_ptr<AudioVideoMetadataExtractor> GetExtractor( ...@@ -38,12 +40,12 @@ scoped_ptr<AudioVideoMetadataExtractor> GetExtractor(
} }
TEST(AudioVideoMetadataExtractorTest, InvalidFile) { TEST(AudioVideoMetadataExtractorTest, InvalidFile) {
GetExtractor("ten_byte_file", false, 0, -1, -1); GetExtractor("ten_byte_file", true, false, 0, -1, -1);
} }
TEST(AudioVideoMetadataExtractorTest, AudioOGG) { TEST(AudioVideoMetadataExtractorTest, AudioOGG) {
scoped_ptr<AudioVideoMetadataExtractor> extractor = scoped_ptr<AudioVideoMetadataExtractor> extractor =
GetExtractor("9ch.ogg", true, 0, -1, -1); GetExtractor("9ch.ogg", true, true, 0, -1, -1);
EXPECT_EQ("Processed by SoX", extractor->comment()); EXPECT_EQ("Processed by SoX", extractor->comment());
EXPECT_EQ("ogg", extractor->stream_infos()[0].type); EXPECT_EQ("ogg", extractor->stream_infos()[0].type);
...@@ -55,11 +57,13 @@ TEST(AudioVideoMetadataExtractorTest, AudioOGG) { ...@@ -55,11 +57,13 @@ TEST(AudioVideoMetadataExtractorTest, AudioOGG) {
EXPECT_EQ("vorbis", extractor->stream_infos()[1].type); EXPECT_EQ("vorbis", extractor->stream_infos()[1].type);
EXPECT_EQ("Processed by SoX", EXPECT_EQ("Processed by SoX",
extractor->stream_infos()[1].tags.find("COMMENT")->second); extractor->stream_infos()[1].tags.find("COMMENT")->second);
EXPECT_EQ(0u, extractor->attached_images_bytes().size());
} }
TEST(AudioVideoMetadataExtractorTest, AudioWAV) { TEST(AudioVideoMetadataExtractorTest, AudioWAV) {
scoped_ptr<AudioVideoMetadataExtractor> extractor = scoped_ptr<AudioVideoMetadataExtractor> extractor =
GetExtractor("sfx_u8.wav", true, 0, -1, -1); GetExtractor("sfx_u8.wav", true, true, 0, -1, -1);
EXPECT_EQ("Lavf54.37.100", extractor->encoder()); EXPECT_EQ("Lavf54.37.100", extractor->encoder());
EXPECT_EQ("Amadeus Pro", extractor->encoded_by()); EXPECT_EQ("Amadeus Pro", extractor->encoded_by());
...@@ -74,11 +78,13 @@ TEST(AudioVideoMetadataExtractorTest, AudioWAV) { ...@@ -74,11 +78,13 @@ TEST(AudioVideoMetadataExtractorTest, AudioWAV) {
EXPECT_EQ("pcm_u8", extractor->stream_infos()[1].type); EXPECT_EQ("pcm_u8", extractor->stream_infos()[1].type);
EXPECT_EQ(0u, extractor->stream_infos()[1].tags.size()); EXPECT_EQ(0u, extractor->stream_infos()[1].tags.size());
EXPECT_EQ(0u, extractor->attached_images_bytes().size());
} }
TEST(AudioVideoMetadataExtractorTest, VideoWebM) { TEST(AudioVideoMetadataExtractorTest, VideoWebM) {
scoped_ptr<AudioVideoMetadataExtractor> extractor = scoped_ptr<AudioVideoMetadataExtractor> extractor =
GetExtractor("bear-320x240-multitrack.webm", true, 2, 320, 240); GetExtractor("bear-320x240-multitrack.webm", true, true, 2, 320, 240);
EXPECT_EQ("Lavf53.9.0", extractor->encoder()); EXPECT_EQ("Lavf53.9.0", extractor->encoder());
EXPECT_EQ(6u, extractor->stream_infos().size()); EXPECT_EQ(6u, extractor->stream_infos().size());
...@@ -104,12 +110,14 @@ TEST(AudioVideoMetadataExtractorTest, VideoWebM) { ...@@ -104,12 +110,14 @@ TEST(AudioVideoMetadataExtractorTest, VideoWebM) {
EXPECT_EQ(1u, extractor->stream_infos()[5].tags.size()); EXPECT_EQ(1u, extractor->stream_infos()[5].tags.size());
EXPECT_EQ("Lavc52.32.0", EXPECT_EQ("Lavc52.32.0",
extractor->stream_infos()[5].tags.find("ENCODER")->second); extractor->stream_infos()[5].tags.find("ENCODER")->second);
EXPECT_EQ(0u, extractor->attached_images_bytes().size());
} }
#if defined(USE_PROPRIETARY_CODECS) #if defined(USE_PROPRIETARY_CODECS)
TEST(AudioVideoMetadataExtractorTest, AndroidRotatedMP4Video) { TEST(AudioVideoMetadataExtractorTest, AndroidRotatedMP4Video) {
scoped_ptr<AudioVideoMetadataExtractor> extractor = scoped_ptr<AudioVideoMetadataExtractor> extractor =
GetExtractor("90rotation.mp4", true, 0, 1920, 1080); GetExtractor("90rotation.mp4", true, true, 0, 1920, 1080);
EXPECT_EQ(90, extractor->rotation()); EXPECT_EQ(90, extractor->rotation());
...@@ -144,11 +152,13 @@ TEST(AudioVideoMetadataExtractorTest, AndroidRotatedMP4Video) { ...@@ -144,11 +152,13 @@ TEST(AudioVideoMetadataExtractorTest, AndroidRotatedMP4Video) {
EXPECT_EQ("SoundHandle", EXPECT_EQ("SoundHandle",
extractor->stream_infos()[2].tags.find("handler_name")->second); extractor->stream_infos()[2].tags.find("handler_name")->second);
EXPECT_EQ("eng", extractor->stream_infos()[2].tags.find("language")->second); EXPECT_EQ("eng", extractor->stream_infos()[2].tags.find("language")->second);
EXPECT_EQ(0u, extractor->attached_images_bytes().size());
} }
TEST(AudioVideoMetadataExtractorTest, AudioMP3) { TEST(AudioVideoMetadataExtractorTest, AudioMP3) {
scoped_ptr<AudioVideoMetadataExtractor> extractor = scoped_ptr<AudioVideoMetadataExtractor> extractor =
GetExtractor("id3_png_test.mp3", true, 1, -1, -1); GetExtractor("id3_png_test.mp3", true, true, 1, -1, -1);
EXPECT_EQ("Airbag", extractor->title()); EXPECT_EQ("Airbag", extractor->title());
EXPECT_EQ("Radiohead", extractor->artist()); EXPECT_EQ("Radiohead", extractor->artist());
...@@ -181,6 +191,17 @@ TEST(AudioVideoMetadataExtractorTest, AudioMP3) { ...@@ -181,6 +191,17 @@ TEST(AudioVideoMetadataExtractorTest, AudioMP3) {
EXPECT_EQ(2u, extractor->stream_infos()[2].tags.size()); EXPECT_EQ(2u, extractor->stream_infos()[2].tags.size());
EXPECT_EQ("Other", extractor->stream_infos()[2].tags.find("comment")->second); EXPECT_EQ("Other", extractor->stream_infos()[2].tags.find("comment")->second);
EXPECT_EQ("", extractor->stream_infos()[2].tags.find("title")->second); EXPECT_EQ("", extractor->stream_infos()[2].tags.find("title")->second);
EXPECT_EQ(1u, extractor->attached_images_bytes().size());
EXPECT_EQ(155752u, extractor->attached_images_bytes()[0].size());
EXPECT_EQ("\x89PNG\r\n\x1a\n",
extractor->attached_images_bytes()[0].substr(0, 8));
EXPECT_EQ("IEND\xae\x42\x60\x82",
extractor->attached_images_bytes()[0].substr(
extractor->attached_images_bytes()[0].size() - 8, 8));
EXPECT_EQ("\xF3\xED\x8F\xC7\xC7\x98\xB9V|p\xC0u!\xB5\x82\xCF\x95\xF0\xCD\xCE",
base::SHA1HashString(extractor->attached_images_bytes()[0]));
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment