| // Copyright 2014 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 "cobalt/media/formats/webm/webm_tracks_parser.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include "base/logging.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "cobalt/media/base/channel_layout.h" |
| #include "cobalt/media/base/mock_media_log.h" |
| #include "cobalt/media/base/timestamp_constants.h" |
| #include "cobalt/media/formats/webm/tracks_builder.h" |
| #include "cobalt/media/formats/webm/webm_constants.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::HasSubstr; |
| using ::testing::InSequence; |
| using ::testing::Return; |
| using ::testing::StrictMock; |
| using ::testing::_; |
| |
| namespace media { |
| |
| static const double kDefaultTimecodeScaleInUs = 1000.0; // 1 ms resolution |
| |
| class WebMTracksParserTest : public testing::Test { |
| public: |
| WebMTracksParserTest() : media_log_(new StrictMock<MockMediaLog>()) {} |
| |
| protected: |
| void VerifyTextTrackInfo(const uint8_t* buffer, int buffer_size, |
| TextKind text_kind, const std::string& name, |
| const std::string& language) { |
| scoped_ptr<WebMTracksParser> parser( |
| new WebMTracksParser(media_log_, false)); |
| |
| int result = parser->Parse(buffer, buffer_size); |
| EXPECT_GT(result, 0); |
| EXPECT_EQ(result, buffer_size); |
| |
| const WebMTracksParser::TextTracks& text_tracks = parser->text_tracks(); |
| EXPECT_EQ(text_tracks.size(), WebMTracksParser::TextTracks::size_type(1)); |
| |
| const WebMTracksParser::TextTracks::const_iterator itr = |
| text_tracks.begin(); |
| EXPECT_EQ(itr->first, 1); // track num |
| |
| const TextTrackConfig& config = itr->second; |
| EXPECT_EQ(config.kind(), text_kind); |
| EXPECT_TRUE(config.label() == name); |
| EXPECT_TRUE(config.language() == language); |
| } |
| |
| scoped_refptr<StrictMock<MockMediaLog>> media_log_; |
| }; |
| |
| TEST_F(WebMTracksParserTest, SubtitleNoNameNoLang) { |
| InSequence s; |
| |
| TracksBuilder tb; |
| tb.AddTextTrack(1, 1, kWebMCodecSubtitles, "", ""); |
| |
| const std::vector<uint8_t> buf = tb.Finish(); |
| VerifyTextTrackInfo(&buf[0], buf.size(), kTextSubtitles, "", ""); |
| } |
| |
| TEST_F(WebMTracksParserTest, SubtitleYesNameNoLang) { |
| InSequence s; |
| |
| TracksBuilder tb; |
| tb.AddTextTrack(1, 1, kWebMCodecSubtitles, "Spock", ""); |
| |
| const std::vector<uint8_t> buf = tb.Finish(); |
| VerifyTextTrackInfo(&buf[0], buf.size(), kTextSubtitles, "Spock", ""); |
| } |
| |
| TEST_F(WebMTracksParserTest, SubtitleNoNameYesLang) { |
| InSequence s; |
| |
| TracksBuilder tb; |
| tb.AddTextTrack(1, 1, kWebMCodecSubtitles, "", "eng"); |
| |
| const std::vector<uint8_t> buf = tb.Finish(); |
| VerifyTextTrackInfo(&buf[0], buf.size(), kTextSubtitles, "", "eng"); |
| } |
| |
| TEST_F(WebMTracksParserTest, SubtitleYesNameYesLang) { |
| InSequence s; |
| |
| TracksBuilder tb; |
| tb.AddTextTrack(1, 1, kWebMCodecSubtitles, "Picard", "fre"); |
| |
| const std::vector<uint8_t> buf = tb.Finish(); |
| VerifyTextTrackInfo(&buf[0], buf.size(), kTextSubtitles, "Picard", "fre"); |
| } |
| |
| TEST_F(WebMTracksParserTest, IgnoringTextTracks) { |
| InSequence s; |
| |
| TracksBuilder tb; |
| tb.AddTextTrack(1, 1, kWebMCodecSubtitles, "Subtitles", "fre"); |
| tb.AddTextTrack(2, 2, kWebMCodecSubtitles, "Commentary", "fre"); |
| |
| const std::vector<uint8_t> buf = tb.Finish(); |
| scoped_ptr<WebMTracksParser> parser(new WebMTracksParser(media_log_, true)); |
| |
| EXPECT_MEDIA_LOG(HasSubstr("Ignoring text track 1")); |
| EXPECT_MEDIA_LOG(HasSubstr("Ignoring text track 2")); |
| |
| int result = parser->Parse(&buf[0], buf.size()); |
| EXPECT_GT(result, 0); |
| EXPECT_EQ(result, static_cast<int>(buf.size())); |
| |
| EXPECT_EQ(parser->text_tracks().size(), 0u); |
| |
| const std::set<int64_t>& ignored_tracks = parser->ignored_tracks(); |
| EXPECT_TRUE(ignored_tracks.find(1) != ignored_tracks.end()); |
| EXPECT_TRUE(ignored_tracks.find(2) != ignored_tracks.end()); |
| |
| // Test again w/o ignoring the test tracks. |
| parser.reset(new WebMTracksParser(media_log_, false)); |
| |
| result = parser->Parse(&buf[0], buf.size()); |
| EXPECT_GT(result, 0); |
| |
| EXPECT_EQ(parser->ignored_tracks().size(), 0u); |
| EXPECT_EQ(parser->text_tracks().size(), 2u); |
| } |
| |
| TEST_F(WebMTracksParserTest, AudioVideoDefaultDurationUnset) { |
| // Other audio/video decoder config fields are necessary in the test |
| // audio/video TrackEntry configurations. This method does only very minimal |
| // verification of their inclusion and parsing; the goal is to confirm |
| // TrackEntry DefaultDuration defaults to -1 if not included in audio or |
| // video TrackEntry. |
| TracksBuilder tb; |
| tb.AddAudioTrack(1, 1, "A_VORBIS", "audio", "", -1, 2, 8000); |
| tb.AddVideoTrack(2, 2, "V_VP8", "video", "", -1, 320, 240); |
| const std::vector<uint8_t> buf = tb.Finish(); |
| |
| scoped_ptr<WebMTracksParser> parser(new WebMTracksParser(media_log_, true)); |
| int result = parser->Parse(&buf[0], buf.size()); |
| EXPECT_LE(0, result); |
| EXPECT_EQ(static_cast<int>(buf.size()), result); |
| |
| EXPECT_EQ(kNoTimestamp, |
| parser->GetAudioDefaultDuration(kDefaultTimecodeScaleInUs)); |
| EXPECT_EQ(kNoTimestamp, |
| parser->GetVideoDefaultDuration(kDefaultTimecodeScaleInUs)); |
| |
| const VideoDecoderConfig& video_config = parser->video_decoder_config(); |
| EXPECT_TRUE(video_config.IsValidConfig()); |
| EXPECT_EQ(320, video_config.coded_size().width()); |
| EXPECT_EQ(240, video_config.coded_size().height()); |
| |
| const AudioDecoderConfig& audio_config = parser->audio_decoder_config(); |
| EXPECT_TRUE(audio_config.IsValidConfig()); |
| EXPECT_EQ(CHANNEL_LAYOUT_STEREO, audio_config.channel_layout()); |
| EXPECT_EQ(8000, audio_config.samples_per_second()); |
| } |
| |
| TEST_F(WebMTracksParserTest, AudioVideoDefaultDurationSet) { |
| // Confirm audio or video TrackEntry DefaultDuration values are parsed, if |
| // present. |
| TracksBuilder tb; |
| tb.AddAudioTrack(1, 1, "A_VORBIS", "audio", "", 12345678, 2, 8000); |
| tb.AddVideoTrack(2, 2, "V_VP8", "video", "", 987654321, 320, 240); |
| const std::vector<uint8_t> buf = tb.Finish(); |
| |
| scoped_ptr<WebMTracksParser> parser(new WebMTracksParser(media_log_, true)); |
| int result = parser->Parse(&buf[0], buf.size()); |
| EXPECT_LE(0, result); |
| EXPECT_EQ(static_cast<int>(buf.size()), result); |
| |
| EXPECT_EQ(base::TimeDelta::FromMicroseconds(12000), |
| parser->GetAudioDefaultDuration(kDefaultTimecodeScaleInUs)); |
| EXPECT_EQ(base::TimeDelta::FromMicroseconds(985000), |
| parser->GetVideoDefaultDuration(5000.0)); // 5 ms resolution |
| EXPECT_EQ(kNoTimestamp, parser->GetAudioDefaultDuration(12346.0)); |
| EXPECT_EQ(base::TimeDelta::FromMicroseconds(12345), |
| parser->GetAudioDefaultDuration(12345.0)); |
| EXPECT_EQ(base::TimeDelta::FromMicroseconds(12003), |
| parser->GetAudioDefaultDuration(1000.3)); // 1.0003 ms resolution |
| } |
| |
| TEST_F(WebMTracksParserTest, InvalidZeroDefaultDurationSet) { |
| // Confirm parse error if TrackEntry DefaultDuration is present, but is 0ns. |
| TracksBuilder tb(true); |
| tb.AddAudioTrack(1, 1, "A_VORBIS", "audio", "", 0, 2, 8000); |
| const std::vector<uint8_t> buf = tb.Finish(); |
| |
| scoped_ptr<WebMTracksParser> parser(new WebMTracksParser(media_log_, true)); |
| |
| EXPECT_MEDIA_LOG(HasSubstr("Illegal 0ns audio TrackEntry DefaultDuration")); |
| |
| EXPECT_EQ(-1, parser->Parse(&buf[0], buf.size())); |
| } |
| |
| TEST_F(WebMTracksParserTest, HighTrackUID) { |
| // Confirm no parse error if TrackEntry TrackUID has MSb set |
| // (http://crbug.com/397067). |
| TracksBuilder tb(true); |
| tb.AddAudioTrack(1, 1ULL << 31, "A_VORBIS", "audio", "", 40, 2, 8000); |
| const std::vector<uint8_t> buf = tb.Finish(); |
| |
| scoped_ptr<WebMTracksParser> parser(new WebMTracksParser(media_log_, true)); |
| EXPECT_GT(parser->Parse(&buf[0], buf.size()), 0); |
| } |
| |
| } // namespace media |