1 // Copyright 2014 The Chromium Authors
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/filters/audio_video_metadata_extractor.h"
7 #include "base/functional/bind.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,
25 const char* expected_key,
26 std::string* destination) {
27 if (!base::EqualsCaseInsensitiveASCII(std::string(tag->key), expected_key))
30 if (destination->empty())
31 *destination = tag->value;
36 // Returns true if the |tag| matches |expected_key|.
37 bool ExtractInt(AVDictionaryEntry* tag,
38 const char* expected_key,
40 if (!base::EqualsCaseInsensitiveASCII(std::string(tag->key), expected_key))
44 if (*destination < 0 && base::StringToInt(tag->value, &temporary) &&
46 *destination = temporary;
52 // Set attached image size limit to 4MB. Chosen arbitrarily.
53 const int kAttachedImageSizeLimit = 4 * 1024 * 1024;
57 AudioVideoMetadataExtractor::StreamInfo::StreamInfo() = default;
59 AudioVideoMetadataExtractor::StreamInfo::StreamInfo(const StreamInfo& other) =
62 AudioVideoMetadataExtractor::StreamInfo::~StreamInfo() = default;
64 AudioVideoMetadataExtractor::AudioVideoMetadataExtractor()
74 AudioVideoMetadataExtractor::~AudioVideoMetadataExtractor() = default;
76 bool AudioVideoMetadataExtractor::Extract(DataSource* source,
77 bool extract_attached_images) {
81 media::BlockingUrlProtocol protocol(source,
82 base::BindRepeating(&OnError, &read_ok));
83 media::FFmpegGlue glue(&protocol);
84 AVFormatContext* format_context = glue.format_context();
86 if (!glue.OpenContext())
92 if (!format_context->iformat)
95 if (avformat_find_stream_info(format_context, NULL) < 0)
98 if (format_context->duration != AV_NOPTS_VALUE) {
99 duration_ = static_cast<double>(format_context->duration) / AV_TIME_BASE;
100 has_duration_ = true;
103 stream_infos_.push_back(StreamInfo());
104 StreamInfo& container_info = stream_infos_.back();
105 container_info.type = format_context->iformat->name;
106 ExtractDictionary(format_context->metadata, &container_info.tags);
108 for (unsigned int i = 0; i < format_context->nb_streams; ++i) {
109 stream_infos_.push_back(StreamInfo());
110 StreamInfo& info = stream_infos_.back();
112 AVStream* stream = format_context->streams[i];
116 void* display_matrix =
117 av_stream_get_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, nullptr);
118 if (display_matrix) {
119 rotation_ = VideoTransformation::FromFFmpegDisplayMatrix(
120 static_cast<int32_t*>(display_matrix))
122 info.tags["rotate"] = base::NumberToString(rotation_);
125 // Extract dictionary from streams also. Needed for containers that attach
126 // metadata to contained streams instead the container itself, like OGG.
127 ExtractDictionary(stream->metadata, &info.tags);
129 if (!stream->codecpar)
132 info.type = avcodec_get_name(stream->codecpar->codec_id);
134 // Extract dimensions of largest stream that's not an attached image.
135 if (stream->codecpar->width > 0 && stream->codecpar->width > width_ &&
136 stream->codecpar->height > 0 && stream->codecpar->height > height_) {
137 width_ = stream->codecpar->width;
138 height_ = stream->codecpar->height;
141 // Extract attached image if requested.
142 if (extract_attached_images &&
143 stream->disposition == AV_DISPOSITION_ATTACHED_PIC &&
144 stream->attached_pic.size > 0 &&
145 stream->attached_pic.size <= kAttachedImageSizeLimit &&
146 stream->attached_pic.data != NULL) {
147 attached_images_bytes_.push_back(std::string());
148 attached_images_bytes_.back().assign(
149 reinterpret_cast<const char*>(stream->attached_pic.data),
150 stream->attached_pic.size);
158 bool AudioVideoMetadataExtractor::has_duration() const {
160 return has_duration_;
163 double AudioVideoMetadataExtractor::duration() const {
165 DCHECK(has_duration_);
169 int AudioVideoMetadataExtractor::width() const {
174 int AudioVideoMetadataExtractor::height() const {
179 int AudioVideoMetadataExtractor::rotation() const {
184 const std::string& AudioVideoMetadataExtractor::album() const {
189 const std::string& AudioVideoMetadataExtractor::artist() const {
194 const std::string& AudioVideoMetadataExtractor::comment() const {
199 const std::string& AudioVideoMetadataExtractor::copyright() const {
204 const std::string& AudioVideoMetadataExtractor::date() const {
209 int AudioVideoMetadataExtractor::disc() const {
214 const std::string& AudioVideoMetadataExtractor::encoder() const {
219 const std::string& AudioVideoMetadataExtractor::encoded_by() const {
224 const std::string& AudioVideoMetadataExtractor::genre() const {
229 const std::string& AudioVideoMetadataExtractor::language() const {
234 const std::string& AudioVideoMetadataExtractor::title() const {
239 int AudioVideoMetadataExtractor::track() const {
244 const std::vector<AudioVideoMetadataExtractor::StreamInfo>&
245 AudioVideoMetadataExtractor::stream_infos() const {
247 return stream_infos_;
250 const std::vector<std::string>&
251 AudioVideoMetadataExtractor::attached_images_bytes() const {
253 return attached_images_bytes_;
256 void AudioVideoMetadataExtractor::ExtractDictionary(AVDictionary* metadata,
257 TagDictionary* raw_tags) {
261 for (AVDictionaryEntry* tag =
262 av_dict_get(metadata, "", NULL, AV_DICT_IGNORE_SUFFIX);
263 tag; tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX)) {
264 if (raw_tags->find(tag->key) == raw_tags->end())
265 (*raw_tags)[tag->key] = tag->value;
267 if (ExtractString(tag, "album", &album_))
269 if (ExtractString(tag, "artist", &artist_))
271 if (ExtractString(tag, "comment", &comment_))
273 if (ExtractString(tag, "copyright", ©right_))
275 if (ExtractString(tag, "date", &date_))
277 if (ExtractInt(tag, "disc", &disc_))
279 if (ExtractString(tag, "encoder", &encoder_))
281 if (ExtractString(tag, "encoded_by", &encoded_by_))
283 if (ExtractString(tag, "genre", &genre_))
285 if (ExtractString(tag, "language", &language_))
287 if (ExtractString(tag, "title", &title_))
289 if (ExtractInt(tag, "track", &track_))