diff options
author | servolk <servolk@chromium.org> | 2016-03-10 21:33:05 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-11 05:34:29 +0000 |
commit | d307b5f1d4fe75cb8a3d1e080d4890ae757d1634 (patch) | |
tree | 433cd00cb9e59f431f027a3e1af6f4bdf5c18442 | |
parent | 1a185fff90e864af30a7ce6a0ec6057056ddbd5f (diff) | |
download | chromium_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.gn | 1 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.cc | 23 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer_unittest.cc | 48 | ||||
-rw-r--r-- | media/formats/mp4/box_definitions.cc | 69 | ||||
-rw-r--r-- | media/formats/mp4/box_definitions.h | 4 | ||||
-rw-r--r-- | media/formats/mp4/mp4_stream_parser.cc | 17 | ||||
-rw-r--r-- | media/formats/mp4/mp4_stream_parser_unittest.cc | 30 | ||||
-rw-r--r-- | media/formats/webm/webm_stream_parser.cc | 12 | ||||
-rw-r--r-- | media/formats/webm/webm_stream_parser.h | 3 | ||||
-rw-r--r-- | media/formats/webm/webm_stream_parser_unittest.cc | 99 | ||||
-rw-r--r-- | media/formats/webm/webm_tracks_parser.cc | 17 | ||||
-rw-r--r-- | media/formats/webm/webm_tracks_parser.h | 12 | ||||
-rw-r--r-- | media/media.gyp | 1 |
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(×cale) && - reader->Read8(&duration)); + RCHECK(reader->Read8(&creation_time) && reader->Read8(&modification_time) && + reader->Read4(×cale) && reader->Read8(&duration) && + reader->Read2(&language_code)); } else { RCHECK(reader->Read4Into8(&creation_time) && reader->Read4Into8(&modification_time) && - reader->Read4(×cale) && - reader->Read4Into8(&duration)); + reader->Read4(×cale) && 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', |