summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorservolk <servolk@chromium.org>2016-03-10 21:33:05 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-11 05:34:29 +0000
commitd307b5f1d4fe75cb8a3d1e080d4890ae757d1634 (patch)
tree433cd00cb9e59f431f027a3e1af6f4bdf5c18442
parent1a185fff90e864af30a7ce6a0ec6057056ddbd5f (diff)
downloadchromium_src-d307b5f1d4fe75cb8a3d1e080d4890ae757d1634.zip
chromium_src-d307b5f1d4fe75cb8a3d1e080d4890ae757d1634.tar.gz
chromium_src-d307b5f1d4fe75cb8a3d1e080d4890ae757d1634.tar.bz2
Implement reading of media track info from WebM and MP4 containers
BUG=590085 Review URL: https://codereview.chromium.org/1735003004 Cr-Commit-Position: refs/heads/master@{#380532}
-rw-r--r--media/BUILD.gn1
-rw-r--r--media/filters/ffmpeg_demuxer.cc23
-rw-r--r--media/filters/ffmpeg_demuxer_unittest.cc48
-rw-r--r--media/formats/mp4/box_definitions.cc69
-rw-r--r--media/formats/mp4/box_definitions.h4
-rw-r--r--media/formats/mp4/mp4_stream_parser.cc17
-rw-r--r--media/formats/mp4/mp4_stream_parser_unittest.cc30
-rw-r--r--media/formats/webm/webm_stream_parser.cc12
-rw-r--r--media/formats/webm/webm_stream_parser.h3
-rw-r--r--media/formats/webm/webm_stream_parser_unittest.cc99
-rw-r--r--media/formats/webm/webm_tracks_parser.cc17
-rw-r--r--media/formats/webm/webm_tracks_parser.h12
-rw-r--r--media/media.gyp1
13 files changed, 301 insertions, 35 deletions
diff --git a/media/BUILD.gn b/media/BUILD.gn
index cb13534..34f6759 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -574,6 +574,7 @@ test("media_unittests") {
"formats/webm/webm_cluster_parser_unittest.cc",
"formats/webm/webm_content_encodings_client_unittest.cc",
"formats/webm/webm_parser_unittest.cc",
+ "formats/webm/webm_stream_parser_unittest.cc",
"formats/webm/webm_tracks_parser_unittest.cc",
"formats/webm/webm_webvtt_parser_unittest.cc",
"muxers/webm_muxer_unittest.cc",
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index f768a1a..1d8361b 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -27,6 +27,7 @@
#include "media/base/decrypt_config.h"
#include "media/base/limits.h"
#include "media/base/media_log.h"
+#include "media/base/media_tracks.h"
#include "media/base/timestamp_constants.h"
#include "media/ffmpeg/ffmpeg_common.h"
#include "media/filters/ffmpeg_aac_bitstream_converter.h"
@@ -1089,6 +1090,7 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
}
}
+ scoped_ptr<MediaTracks> media_tracks(new MediaTracks());
AVStream* audio_stream = NULL;
AudioDecoderConfig audio_config;
AVStream* video_stream = NULL;
@@ -1158,6 +1160,19 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
continue;
}
+ std::string track_id = base::IntToString(stream->id);
+ std::string track_label = streams_[i]->GetMetadata("handler_name");
+ std::string track_language = streams_[i]->GetMetadata("language");
+
+ // Some metadata is named differently in FFmpeg for webm files.
+ if (strstr(format_context->iformat->name, "webm") ||
+ strstr(format_context->iformat->name, "matroska")) {
+ // TODO(servolk): FFmpeg doesn't set stream->id correctly for webm files.
+ // Need to fix that and use it as track id. crbug.com/323183
+ track_id = base::UintToString(media_tracks->tracks().size() + 1);
+ track_label = streams_[i]->GetMetadata("title");
+ }
+
// Note when we find our audio/video stream (we only want one of each) and
// record src= playback UMA stats for the stream's decoder config.
if (codec_type == AVMEDIA_TYPE_AUDIO) {
@@ -1165,11 +1180,17 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
audio_stream = stream;
audio_config = streams_[i]->audio_decoder_config();
RecordAudioCodecStats(audio_config);
+
+ media_tracks->AddAudioTrack(audio_config, track_id, "main", track_label,
+ track_language);
} else if (codec_type == AVMEDIA_TYPE_VIDEO) {
CHECK(!video_stream);
video_stream = stream;
video_config = streams_[i]->video_decoder_config();
RecordVideoCodecStats(video_config, stream->codec->color_range);
+
+ media_tracks->AddVideoTrack(video_config, track_id, "main", track_label,
+ track_language);
}
max_duration = std::max(max_duration, streams_[i]->duration());
@@ -1340,6 +1361,8 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
media_log_->SetTimeProperty("start_time", start_time_);
media_log_->SetIntegerProperty("bitrate", bitrate_);
+ media_tracks_updated_cb_.Run(std::move(media_tracks));
+
status_cb.Run(PIPELINE_OK);
}
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index a6b82e2..01f57a1 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -208,7 +208,10 @@ class FFmpegDemuxerTest : public testing::Test {
void(EmeInitDataType init_data_type,
const std::vector<uint8_t>& init_data));
- void OnMediaTracksUpdated(scoped_ptr<MediaTracks> tracks) {}
+ void OnMediaTracksUpdated(scoped_ptr<MediaTracks> tracks) {
+ CHECK(tracks.get());
+ media_tracks_ = std::move(tracks);
+ }
// Accessor to demuxer internals.
void set_duration_known(bool duration_known) {
@@ -225,6 +228,7 @@ class FFmpegDemuxerTest : public testing::Test {
scoped_ptr<FileDataSource> data_source_;
scoped_ptr<FFmpegDemuxer> demuxer_;
StrictMock<MockDemuxerHost> host_;
+ scoped_ptr<MediaTracks> media_tracks_;
base::MessageLoop message_loop_;
AVFormatContext* format_context() {
@@ -1190,6 +1194,48 @@ TEST_F(FFmpegDemuxerTest, Read_EAC3_Audio) {
#endif
}
+TEST_F(FFmpegDemuxerTest, Read_Mp4_Media_Track_Info) {
+ CreateDemuxer("bear.mp4");
+ InitializeDemuxer();
+
+ EXPECT_EQ(media_tracks_->tracks().size(), 2u);
+
+ const MediaTrack& audio_track = *(media_tracks_->tracks()[0]);
+ EXPECT_EQ(audio_track.type(), MediaTrack::Audio);
+ EXPECT_EQ(audio_track.id(), "1");
+ EXPECT_EQ(audio_track.kind(), "main");
+ EXPECT_EQ(audio_track.label(), "GPAC ISO Audio Handler");
+ EXPECT_EQ(audio_track.language(), "und");
+
+ const MediaTrack& video_track = *(media_tracks_->tracks()[1]);
+ EXPECT_EQ(video_track.type(), MediaTrack::Video);
+ EXPECT_EQ(video_track.id(), "2");
+ EXPECT_EQ(video_track.kind(), "main");
+ EXPECT_EQ(video_track.label(), "GPAC ISO Video Handler");
+ EXPECT_EQ(video_track.language(), "und");
+}
+
#endif // defined(USE_PROPRIETARY_CODECS)
+TEST_F(FFmpegDemuxerTest, Read_Webm_Media_Track_Info) {
+ CreateDemuxer("bear.webm");
+ InitializeDemuxer();
+
+ EXPECT_EQ(media_tracks_->tracks().size(), 2u);
+
+ const MediaTrack& video_track = *(media_tracks_->tracks()[0]);
+ EXPECT_EQ(video_track.type(), MediaTrack::Video);
+ EXPECT_EQ(video_track.id(), "1");
+ EXPECT_EQ(video_track.kind(), "main");
+ EXPECT_EQ(video_track.label(), "");
+ EXPECT_EQ(video_track.language(), "");
+
+ const MediaTrack& audio_track = *(media_tracks_->tracks()[1]);
+ EXPECT_EQ(audio_track.type(), MediaTrack::Audio);
+ EXPECT_EQ(audio_track.id(), "2");
+ EXPECT_EQ(audio_track.kind(), "main");
+ EXPECT_EQ(audio_track.label(), "");
+ EXPECT_EQ(audio_track.language(), "");
+}
+
} // namespace media
diff --git a/media/formats/mp4/box_definitions.cc b/media/formats/mp4/box_definitions.cc
index 705232f..a95c78f 100644
--- a/media/formats/mp4/box_definitions.cc
+++ b/media/formats/mp4/box_definitions.cc
@@ -434,8 +434,29 @@ FourCC HandlerReference::BoxType() const { return FOURCC_HDLR; }
bool HandlerReference::Parse(BoxReader* reader) {
FourCC hdlr_type;
- RCHECK(reader->SkipBytes(8) && reader->ReadFourCC(&hdlr_type));
- // Note: remaining fields in box ignored
+ RCHECK(reader->ReadFullBoxHeader() && reader->SkipBytes(4) &&
+ reader->ReadFourCC(&hdlr_type) && reader->SkipBytes(12));
+
+ // Now we should be at the beginning of the |name| field of HDLR box. The
+ // |name| is a zero-terminated ASCII string in ISO BMFF, but it was a
+ // Pascal-style counted string in older QT/Mov formats. So we'll read the
+ // remaining box bytes first, then if the last one is zero, we strip the last
+ // zero byte, otherwise we'll string the first byte (containing the length of
+ // the Pascal-style string).
+ std::vector<uint8_t> name_bytes;
+ RCHECK(reader->ReadVec(&name_bytes, reader->size() - reader->pos()));
+ if (name_bytes.size() == 0) {
+ name = "";
+ } else if (name_bytes.back() == 0) {
+ // This is a zero-terminated C-style string, exclude the last byte.
+ name = std::string(name_bytes.begin(), name_bytes.end() - 1);
+ } else {
+ // Check that the length of the Pascal-style string is correct.
+ RCHECK(name_bytes[0] == (name_bytes.size() - 1));
+ // Skip the first byte, containing the length of the Pascal-string.
+ name = std::string(name_bytes.begin() + 1, name_bytes.end());
+ }
+
if (hdlr_type == FOURCC_VIDE) {
type = kVideo;
} else if (hdlr_type == FOURCC_SOUN) {
@@ -691,7 +712,8 @@ MediaHeader::MediaHeader()
: creation_time(0),
modification_time(0),
timescale(0),
- duration(0) {}
+ duration(0),
+ language_code(0) {}
MediaHeader::~MediaHeader() {}
FourCC MediaHeader::BoxType() const { return FOURCC_MDHD; }
@@ -699,18 +721,43 @@ bool MediaHeader::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
if (reader->version() == 1) {
- RCHECK(reader->Read8(&creation_time) &&
- reader->Read8(&modification_time) &&
- reader->Read4(&timescale) &&
- reader->Read8(&duration));
+ RCHECK(reader->Read8(&creation_time) && reader->Read8(&modification_time) &&
+ reader->Read4(&timescale) && reader->Read8(&duration) &&
+ reader->Read2(&language_code));
} else {
RCHECK(reader->Read4Into8(&creation_time) &&
reader->Read4Into8(&modification_time) &&
- reader->Read4(&timescale) &&
- reader->Read4Into8(&duration));
+ reader->Read4(&timescale) && reader->Read4Into8(&duration) &&
+ reader->Read2(&language_code));
}
- // Skip language information
- return reader->SkipBytes(4);
+ // ISO 639-2/T language code only uses 15 lower bits, so reset the 16th bit.
+ language_code &= 0x7fff;
+ // Skip playback quality information
+ return reader->SkipBytes(2);
+}
+
+std::string MediaHeader::language() const {
+ if (language_code == 0x7fff || language_code < 0x400) {
+ return "und";
+ }
+ char lang_chars[4];
+ lang_chars[3] = 0;
+ lang_chars[2] = 0x60 + (language_code & 0x1f);
+ lang_chars[1] = 0x60 + ((language_code >> 5) & 0x1f);
+ lang_chars[0] = 0x60 + ((language_code >> 10) & 0x1f);
+
+ if (lang_chars[0] < 'a' || lang_chars[0] > 'z' || lang_chars[1] < 'a' ||
+ lang_chars[1] > 'z' || lang_chars[2] < 'a' || lang_chars[2] > 'z') {
+ // Got unexpected characteds in ISO 639-2/T language code. Something must be
+ // wrong with the input file, report 'und' language to be safe.
+ DVLOG(2) << "Ignoring MDHD language_code (non ISO 639-2 compliant): "
+ << lang_chars;
+ lang_chars[0] = 'u';
+ lang_chars[1] = 'n';
+ lang_chars[2] = 'd';
+ }
+
+ return lang_chars;
}
MediaInformation::MediaInformation() {}
diff --git a/media/formats/mp4/box_definitions.h b/media/formats/mp4/box_definitions.h
index 542f4de..24b585b 100644
--- a/media/formats/mp4/box_definitions.h
+++ b/media/formats/mp4/box_definitions.h
@@ -197,6 +197,7 @@ struct MEDIA_EXPORT HandlerReference : Box {
DECLARE_BOX_METHODS(HandlerReference);
TrackType type;
+ std::string name;
};
struct MEDIA_EXPORT AVCDecoderConfigurationRecord : Box {
@@ -310,10 +311,13 @@ struct MEDIA_EXPORT SampleTable : Box {
struct MEDIA_EXPORT MediaHeader : Box {
DECLARE_BOX_METHODS(MediaHeader);
+ std::string language() const;
+
uint64_t creation_time;
uint64_t modification_time;
uint32_t timescale;
uint64_t duration;
+ uint16_t language_code;
};
struct MEDIA_EXPORT MediaInformation : Box {
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index 95abf83..37b5396 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -11,6 +11,7 @@
#include "base/callback_helpers.h"
#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "media/base/audio_decoder_config.h"
@@ -187,6 +188,7 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) {
has_audio_ = false;
has_video_ = false;
+ scoped_ptr<MediaTracks> media_tracks(new MediaTracks());
AudioDecoderConfig audio_config;
VideoDecoderConfig video_config;
@@ -307,6 +309,9 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) {
is_audio_track_encrypted_, base::TimeDelta(), 0);
has_audio_ = true;
audio_track_id_ = track->header.track_id;
+ media_tracks->AddAudioTrack(
+ audio_config, base::UintToString(audio_track_id_), "main",
+ track->media.handler.name, track->media.header.language());
}
if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) {
RCHECK(!samp_descr.video_entries.empty());
@@ -348,21 +353,15 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) {
std::vector<uint8_t>(), is_video_track_encrypted_);
has_video_ = true;
video_track_id_ = track->header.track_id;
+ media_tracks->AddVideoTrack(
+ video_config, base::UintToString(video_track_id_), "main",
+ track->media.handler.name, track->media.header.language());
}
}
if (!moov_->pssh.empty())
OnEncryptedMediaInitData(moov_->pssh);
- scoped_ptr<MediaTracks> media_tracks(new MediaTracks());
- // TODO(servolk): Implement proper sourcing of media track info as described
- // in crbug.com/590085
- if (audio_config.IsValidConfig()) {
- media_tracks->AddAudioTrack(audio_config, "audio", "", "", "");
- }
- if (video_config.IsValidConfig()) {
- media_tracks->AddVideoTrack(video_config, "video", "", "", "");
- }
RCHECK(config_cb_.Run(std::move(media_tracks), TextTrackConfigMap()));
StreamParser::InitParameters params(kInfiniteDuration());
diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc
index c7d41fe..26144ee 100644
--- a/media/formats/mp4/mp4_stream_parser_unittest.cc
+++ b/media/formats/mp4/mp4_stream_parser_unittest.cc
@@ -69,6 +69,7 @@ class MP4StreamParserTest : public testing::Test {
scoped_refptr<StrictMock<MockMediaLog>> media_log_;
scoped_ptr<MP4StreamParser> parser_;
bool configs_received_;
+ scoped_ptr<MediaTracks> media_tracks_;
AudioDecoderConfig audio_decoder_config_;
VideoDecoderConfig video_decoder_config_;
DecodeTimestamp lower_bound_;
@@ -102,9 +103,11 @@ class MP4StreamParserTest : public testing::Test {
bool NewConfigF(scoped_ptr<MediaTracks> tracks,
const StreamParser::TextTrackConfigMap& tc) {
configs_received_ = true;
- audio_decoder_config_ = tracks->getFirstAudioConfig();
- video_decoder_config_ = tracks->getFirstVideoConfig();
- DVLOG(1) << "NewConfigF: track count=" << tracks->tracks().size()
+ CHECK(tracks.get());
+ media_tracks_ = std::move(tracks);
+ audio_decoder_config_ = media_tracks_->getFirstAudioConfig();
+ video_decoder_config_ = media_tracks_->getFirstVideoConfig();
+ DVLOG(1) << "NewConfigF: track count=" << media_tracks_->tracks().size()
<< " audio=" << audio_decoder_config_.IsValidConfig()
<< " video=" << video_decoder_config_.IsValidConfig();
return true;
@@ -417,5 +420,26 @@ TEST_F(MP4StreamParserTest, FourCCToString) {
EXPECT_EQ("0x66616b00", FourCCToString(static_cast<FourCC>(0x66616b00)));
}
+TEST_F(MP4StreamParserTest, MediaTrackInfoSourcing) {
+ EXPECT_MEDIA_LOG(VideoCodecLog("avc1.64001F"));
+ EXPECT_MEDIA_LOG(AudioCodecLog("mp4a.40.2"));
+ ParseMP4File("bear-1280x720-av_frag.mp4", 4096);
+
+ EXPECT_EQ(media_tracks_->tracks().size(), 2u);
+ const MediaTrack& video_track = *(media_tracks_->tracks()[0]);
+ EXPECT_EQ(video_track.type(), MediaTrack::Video);
+ EXPECT_EQ(video_track.id(), "1");
+ EXPECT_EQ(video_track.kind(), "main");
+ EXPECT_EQ(video_track.label(), "VideoHandler");
+ EXPECT_EQ(video_track.language(), "und");
+
+ const MediaTrack& audio_track = *(media_tracks_->tracks()[1]);
+ EXPECT_EQ(audio_track.type(), MediaTrack::Audio);
+ EXPECT_EQ(audio_track.id(), "2");
+ EXPECT_EQ(audio_track.kind(), "main");
+ EXPECT_EQ(audio_track.label(), "SoundHandler");
+ EXPECT_EQ(audio_track.language(), "und");
+}
+
} // namespace mp4
} // namespace media
diff --git a/media/formats/webm/webm_stream_parser.cc b/media/formats/webm/webm_stream_parser.cc
index 3090768..0e82ca2 100644
--- a/media/formats/webm/webm_stream_parser.cc
+++ b/media/formats/webm/webm_stream_parser.cc
@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
#include "media/base/media_track.h"
#include "media/base/media_tracks.h"
#include "media/base/timestamp_constants.h"
@@ -224,15 +225,8 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8_t* data, int size) {
if (video_config.is_encrypted())
OnEncryptedMediaInitData(tracks_parser.video_encryption_key_id());
- scoped_ptr<MediaTracks> media_tracks(new MediaTracks());
- // TODO(servolk): Implement proper sourcing of media track info as described
- // in crbug.com/590085
- if (audio_config.IsValidConfig()) {
- media_tracks->AddAudioTrack(audio_config, "audio", "", "", "");
- }
- if (video_config.IsValidConfig()) {
- media_tracks->AddVideoTrack(video_config, "video", "", "", "");
- }
+ scoped_ptr<MediaTracks> media_tracks = tracks_parser.media_tracks();
+ CHECK(media_tracks.get());
if (!config_cb_.Run(std::move(media_tracks), tracks_parser.text_tracks())) {
DVLOG(1) << "New config data isn't allowed.";
return -1;
diff --git a/media/formats/webm/webm_stream_parser.h b/media/formats/webm/webm_stream_parser.h
index 6de1deda..78f1497 100644
--- a/media/formats/webm/webm_stream_parser.h
+++ b/media/formats/webm/webm_stream_parser.h
@@ -12,6 +12,7 @@
#include "base/memory/ref_counted.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/byte_queue.h"
+#include "media/base/media_export.h"
#include "media/base/stream_parser.h"
#include "media/base/video_decoder_config.h"
@@ -19,7 +20,7 @@ namespace media {
class WebMClusterParser;
-class WebMStreamParser : public StreamParser {
+class MEDIA_EXPORT WebMStreamParser : public StreamParser {
public:
WebMStreamParser();
~WebMStreamParser() override;
diff --git a/media/formats/webm/webm_stream_parser_unittest.cc b/media/formats/webm/webm_stream_parser_unittest.cc
new file mode 100644
index 0000000..0386e8f
--- /dev/null
+++ b/media/formats/webm/webm_stream_parser_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2016 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/bind.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/media_tracks.h"
+#include "media/base/mock_media_log.h"
+#include "media/base/test_data_util.h"
+#include "media/base/text_track_config.h"
+#include "media/formats/webm/webm_stream_parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace media {
+
+class WebMStreamParserTest : public testing::Test {
+ public:
+ WebMStreamParserTest()
+ : media_log_(new testing::StrictMock<MockMediaLog>()) {}
+
+ protected:
+ void ParseWebMFile(const std::string& filename) {
+ scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
+ parser_.reset(new WebMStreamParser());
+ Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb =
+ base::Bind(&WebMStreamParserTest::OnEncryptedMediaInitData,
+ base::Unretained(this));
+
+ EXPECT_CALL(*this, InitCB(_));
+ EXPECT_CALL(*this, NewMediaSegmentCB()).Times(testing::AnyNumber());
+ EXPECT_CALL(*this, EndMediaSegmentCB()).Times(testing::AnyNumber());
+ EXPECT_CALL(*this, NewBuffersCB(_, _, _))
+ .Times(testing::AnyNumber())
+ .WillRepeatedly(testing::Return(true));
+ parser_->Init(
+ base::Bind(&WebMStreamParserTest::InitCB, base::Unretained(this)),
+ base::Bind(&WebMStreamParserTest::NewConfigCB, base::Unretained(this)),
+ base::Bind(&WebMStreamParserTest::NewBuffersCB, base::Unretained(this)),
+ true, // ignore_text_track
+ encrypted_media_init_data_cb,
+ base::Bind(&WebMStreamParserTest::NewMediaSegmentCB,
+ base::Unretained(this)),
+ base::Bind(&WebMStreamParserTest::EndMediaSegmentCB,
+ base::Unretained(this)),
+ media_log_);
+ bool result = parser_->Parse(buffer->data(), buffer->data_size());
+ EXPECT_TRUE(result);
+ }
+
+ MOCK_METHOD1(InitCB, void(const StreamParser::InitParameters& params));
+
+ bool NewConfigCB(scoped_ptr<MediaTracks> tracks,
+ const StreamParser::TextTrackConfigMap& text_track_map) {
+ DCHECK(tracks.get());
+ media_tracks_ = std::move(tracks);
+ return true;
+ }
+
+ MOCK_METHOD3(NewBuffersCB,
+ bool(const StreamParser::BufferQueue&,
+ const StreamParser::BufferQueue&,
+ const StreamParser::TextBufferQueueMap&));
+ MOCK_METHOD2(OnEncryptedMediaInitData,
+ void(EmeInitDataType init_data_type,
+ const std::vector<uint8_t>& init_data));
+ MOCK_METHOD0(NewMediaSegmentCB, void());
+ MOCK_METHOD0(EndMediaSegmentCB, void());
+
+ scoped_refptr<testing::StrictMock<MockMediaLog>> media_log_;
+ scoped_ptr<WebMStreamParser> parser_;
+ scoped_ptr<MediaTracks> media_tracks_;
+};
+
+TEST_F(WebMStreamParserTest, VerifyMediaTrackMetadata) {
+ EXPECT_MEDIA_LOG(testing::HasSubstr("Estimating WebM block duration"))
+ .Times(testing::AnyNumber());
+ ParseWebMFile("bear.webm");
+ EXPECT_NE(media_tracks_.get(), nullptr);
+
+ EXPECT_EQ(media_tracks_->tracks().size(), 2u);
+
+ const MediaTrack& video_track = *(media_tracks_->tracks()[0]);
+ EXPECT_EQ(video_track.type(), MediaTrack::Video);
+ EXPECT_EQ(video_track.id(), "1");
+ EXPECT_EQ(video_track.kind(), "main");
+ EXPECT_EQ(video_track.label(), "");
+ EXPECT_EQ(video_track.language(), "und");
+
+ const MediaTrack& audio_track = *(media_tracks_->tracks()[1]);
+ EXPECT_EQ(audio_track.type(), MediaTrack::Audio);
+ EXPECT_EQ(audio_track.id(), "2");
+ EXPECT_EQ(audio_track.kind(), "main");
+ EXPECT_EQ(audio_track.label(), "");
+ EXPECT_EQ(audio_track.language(), "und");
+}
+
+} // namespace media
diff --git a/media/formats/webm/webm_tracks_parser.cc b/media/formats/webm/webm_tracks_parser.cc
index 464a77ff..5ffa0aa 100644
--- a/media/formats/webm/webm_tracks_parser.cc
+++ b/media/formats/webm/webm_tracks_parser.cc
@@ -77,6 +77,7 @@ int WebMTracksParser::Parse(const uint8_t* buf, int size) {
video_decoder_config_ = VideoDecoderConfig();
text_tracks_.clear();
ignored_tracks_.clear();
+ media_tracks_.reset(new MediaTracks());
WebMListParser parser(kWebMIdTracks, this);
int result = parser.Parse(buf, size);
@@ -210,6 +211,9 @@ bool WebMTracksParser::OnListEnd(int id) {
!audio_encryption_key_id_.empty(), &audio_decoder_config_)) {
return false;
}
+ media_tracks_->AddAudioTrack(audio_decoder_config_,
+ base::Uint64ToString(track_num_), "main",
+ track_name_, track_language_);
} else {
MEDIA_LOG(DEBUG, media_log_) << "Ignoring audio track " << track_num_;
ignored_tracks_.insert(track_num_);
@@ -232,6 +236,9 @@ bool WebMTracksParser::OnListEnd(int id) {
&video_decoder_config_)) {
return false;
}
+ media_tracks_->AddVideoTrack(video_decoder_config_,
+ base::Uint64ToString(track_num_), "main",
+ track_name_, track_language_);
} else {
MEDIA_LOG(DEBUG, media_log_) << "Ignoring video track " << track_num_;
ignored_tracks_.insert(track_num_);
@@ -335,7 +342,15 @@ bool WebMTracksParser::OnString(int id, const std::string& str) {
}
if (id == kWebMIdLanguage) {
- track_language_ = str;
+ // Check that the language string is in ISO 639-2 format (3 letter code of a
+ // language, all lower-case letters).
+ if (str.size() != 3 || str[0] < 'a' || str[0] > 'z' || str[1] < 'a' ||
+ str[1] > 'z' || str[2] < 'a' || str[2] > 'z') {
+ VLOG(2) << "Ignoring kWebMIdLanguage (not ISO 639-2 compliant): " << str;
+ track_language_ = "und";
+ } else {
+ track_language_ = str;
+ }
return true;
}
diff --git a/media/formats/webm/webm_tracks_parser.h b/media/formats/webm/webm_tracks_parser.h
index 3569907..6286b23 100644
--- a/media/formats/webm/webm_tracks_parser.h
+++ b/media/formats/webm/webm_tracks_parser.h
@@ -18,6 +18,7 @@
#include "base/time/time.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/media_log.h"
+#include "media/base/media_tracks.h"
#include "media/base/text_track_config.h"
#include "media/base/video_decoder_config.h"
#include "media/formats/webm/webm_audio_client.h"
@@ -77,6 +78,15 @@ class MEDIA_EXPORT WebMTracksParser : public WebMParserClient {
return text_tracks_;
}
+ // Note: Calling media_tracks() method passes the ownership of the MediaTracks
+ // object from WebMTracksParser to the caller (which is typically
+ // WebMStreamParser object). So this method must be called only once, after
+ // track parsing has been completed.
+ scoped_ptr<MediaTracks> media_tracks() {
+ CHECK(media_tracks_.get());
+ return std::move(media_tracks_);
+ }
+
private:
// WebMParserClient implementation.
WebMParserClient* OnListStart(int id) override;
@@ -114,6 +124,8 @@ class MEDIA_EXPORT WebMTracksParser : public WebMParserClient {
WebMVideoClient video_client_;
VideoDecoderConfig video_decoder_config_;
+ scoped_ptr<MediaTracks> media_tracks_;
+
DISALLOW_COPY_AND_ASSIGN(WebMTracksParser);
};
diff --git a/media/media.gyp b/media/media.gyp
index e7780ae..f254b8f 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -1279,6 +1279,7 @@
'formats/webm/webm_cluster_parser_unittest.cc',
'formats/webm/webm_content_encodings_client_unittest.cc',
'formats/webm/webm_parser_unittest.cc',
+ 'formats/webm/webm_stream_parser_unittest.cc',
'formats/webm/webm_tracks_parser_unittest.cc',
'formats/webm/webm_webvtt_parser_unittest.cc',
'muxers/webm_muxer_unittest.cc',