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.
5 #include "media/base/audio_video_metadata_extractor.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"
19 void OnError(bool* succeeded
) {
23 // Returns true if the |tag| matches |expected_key|.
24 bool ExtractString(AVDictionaryEntry
* tag
, const char* expected_key
,
25 std::string
* destination
) {
26 if (!LowerCaseEqualsASCII(std::string(tag
->key
), expected_key
))
29 if (destination
->empty())
30 *destination
= tag
->value
;
35 // Returns true if the |tag| matches |expected_key|.
36 bool ExtractInt(AVDictionaryEntry
* tag
, const char* expected_key
,
38 if (!LowerCaseEqualsASCII(std::string(tag
->key
), expected_key
))
42 if (*destination
< 0 && base::StringToInt(tag
->value
, &temporary
) &&
44 *destination
= temporary
;
52 AudioVideoMetadataExtractor::AudioVideoMetadataExtractor()
61 AudioVideoMetadataExtractor::~AudioVideoMetadataExtractor() {
64 bool AudioVideoMetadataExtractor::Extract(DataSource
* source
) {
68 media::BlockingUrlProtocol
protocol(source
, base::Bind(&OnError
, &read_ok
));
69 media::FFmpegGlue
glue(&protocol
);
70 AVFormatContext
* format_context
= glue
.format_context();
72 if (!glue
.OpenContext())
78 if (!format_context
->iformat
)
81 if (avformat_find_stream_info(format_context
, NULL
) < 0)
84 if (format_context
->duration
!= AV_NOPTS_VALUE
)
85 duration_
= static_cast<double>(format_context
->duration
) / AV_TIME_BASE
;
87 ExtractDictionary(format_context
->metadata
);
89 for (unsigned int i
= 0; i
< format_context
->nb_streams
; ++i
) {
90 AVStream
* stream
= format_context
->streams
[i
];
94 // Ignore attached pictures for metadata extraction.
95 if ((stream
->disposition
& AV_DISPOSITION_ATTACHED_PIC
) != 0)
98 // Extract dictionary from streams also. Needed for containers that attach
99 // metadata to contained streams instead the container itself, like OGG.
100 ExtractDictionary(stream
->metadata
);
105 // Extract dimensions of largest stream that's not an attached picture.
106 if (stream
->codec
->width
> 0 && stream
->codec
->width
> width_
&&
107 stream
->codec
->height
> 0 && stream
->codec
->height
> height_
) {
108 width_
= stream
->codec
->width
;
109 height_
= stream
->codec
->height
;
117 double AudioVideoMetadataExtractor::duration() const {
122 int AudioVideoMetadataExtractor::width() const {
127 int AudioVideoMetadataExtractor::height() const {
132 const std::string
& AudioVideoMetadataExtractor::album() const {
137 const std::string
& AudioVideoMetadataExtractor::artist() const {
142 const std::string
& AudioVideoMetadataExtractor::comment() const {
147 const std::string
& AudioVideoMetadataExtractor::copyright() const {
152 const std::string
& AudioVideoMetadataExtractor::date() const {
157 int AudioVideoMetadataExtractor::disc() const {
162 const std::string
& AudioVideoMetadataExtractor::encoder() const {
167 const std::string
& AudioVideoMetadataExtractor::encoded_by() const {
172 const std::string
& AudioVideoMetadataExtractor::genre() const {
177 const std::string
& AudioVideoMetadataExtractor::language() const {
182 const std::string
& AudioVideoMetadataExtractor::title() const {
187 int AudioVideoMetadataExtractor::track() const {
192 void AudioVideoMetadataExtractor::ExtractDictionary(AVDictionary
* metadata
) {
196 AVDictionaryEntry
* tag
= NULL
;
197 while ((tag
= av_dict_get(metadata
, "", tag
, AV_DICT_IGNORE_SUFFIX
))) {
198 if (ExtractString(tag
, "album", &album_
)) continue;
199 if (ExtractString(tag
, "artist", &artist_
)) continue;
200 if (ExtractString(tag
, "comment", &comment_
)) continue;
201 if (ExtractString(tag
, "copyright", ©right_
)) continue;
202 if (ExtractString(tag
, "date", &date_
)) continue;
203 if (ExtractInt(tag
, "disc", &disc_
)) continue;
204 if (ExtractString(tag
, "encoder", &encoder_
)) continue;
205 if (ExtractString(tag
, "encoded_by", &encoded_by_
)) continue;
206 if (ExtractString(tag
, "genre", &genre_
)) continue;
207 if (ExtractString(tag
, "language", &language_
)) continue;
208 if (ExtractString(tag
, "title", &title_
)) continue;
209 if (ExtractInt(tag
, "track", &track_
)) continue;