Import Cobalt 13.104689 Change-Id: I2f7f6e501273d71902fab1dbd2d5788adf184327
diff --git a/src/cobalt/bindings/testing/bindings_test_base.h b/src/cobalt/bindings/testing/bindings_test_base.h index 768dfb3..ddb7ca5 100644 --- a/src/cobalt/bindings/testing/bindings_test_base.h +++ b/src/cobalt/bindings/testing/bindings_test_base.h
@@ -67,7 +67,8 @@ scoped_refptr<script::SourceCode> source = script::SourceCode::CreateSourceCode( script, base::SourceLocation("[object BindingsTestBase]", 1, 1)); - return global_environment_->EvaluateScript(source, out_result); + return global_environment_->EvaluateScript(source, out_result, + false /*mute_errors*/); } bool EvaluateScript(const std::string& script, @@ -77,8 +78,8 @@ scoped_refptr<script::SourceCode> source = script::SourceCode::CreateSourceCode( script, base::SourceLocation("[object BindingsTestBase]", 1, 1)); - return global_environment_->EvaluateScript(source, owning_object, - out_opaque_handle); + return global_environment_->EvaluateScript( + source, owning_object, out_opaque_handle, false /*mute_errors*/); } void CollectGarbage() { engine_->CollectGarbage(); }
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc index a946dee..636ebf5 100644 --- a/src/cobalt/browser/application.cc +++ b/src/cobalt/browser/application.cc
@@ -495,18 +495,22 @@ } #endif + base::optional<std::string> partition_key; if (command_line->HasSwitch(browser::switches::kLocalStoragePartitionUrl)) { std::string local_storage_partition_url = command_line->GetSwitchValueASCII( browser::switches::kLocalStoragePartitionUrl); - base::optional<std::string> partition_key = - base::GetApplicationKey(GURL(local_storage_partition_url)); + partition_key = base::GetApplicationKey(GURL(local_storage_partition_url)); CHECK(partition_key) << "local_storage_partition_url is not a valid URL."; + } else { + partition_key = base::GetApplicationKey(initial_url); + } + options.storage_manager_options.savegame_options.id = partition_key; - base::optional<std::string> default_partition_key = - base::GetApplicationKey(GURL(kDefaultURL)); - if (*partition_key != *default_partition_key) { - options.storage_manager_options.savegame_options.id = *partition_key; - } + base::optional<std::string> default_key = + base::GetApplicationKey(GURL(kDefaultURL)); + if (partition_key == default_key) { + options.storage_manager_options.savegame_options.fallback_to_default_id = + true; } // User can specify an extra search path entry for files loaded via file://.
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc index 9333666..4026fb1 100644 --- a/src/cobalt/browser/web_module.cc +++ b/src/cobalt/browser/web_module.cc
@@ -713,8 +713,9 @@ // JavaScript is being run. Track it in the global stats. dom::GlobalStats::GetInstance()->StartJavaScriptEvent(); - *result = script_runner_->Execute(script_utf8, script_location, - out_succeeded); + // This should only be called for Cobalt javascript, errors not expected. + *result = script_runner_->Execute(script_utf8, script_location, out_succeeded, + true /*mute error reports*/); // JavaScript is done running. Stop tracking it in the global stats. dom::GlobalStats::GetInstance()->StopJavaScriptEvent();
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id index d4d1d7c..6125516 100644 --- a/src/cobalt/build/build.id +++ b/src/cobalt/build/build.id
@@ -1 +1 @@ -103922 \ No newline at end of file +104689 \ No newline at end of file
diff --git a/src/cobalt/css_parser/parser.cc b/src/cobalt/css_parser/parser.cc index 98b1521..5397d14 100644 --- a/src/cobalt/css_parser/parser.cc +++ b/src/cobalt/css_parser/parser.cc
@@ -399,9 +399,7 @@ } return false; case 2: - LogError( - "the html put too many queries on bison stack without " - "closing/reducing"); + LogError("bison parser is out of memory"); return false; default: NOTREACHED();
diff --git a/src/cobalt/css_parser/parser_test.cc b/src/cobalt/css_parser/parser_test.cc index 8af545c..aad3f22 100644 --- a/src/cobalt/css_parser/parser_test.cc +++ b/src/cobalt/css_parser/parser_test.cc
@@ -129,7 +129,7 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("", source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(0, style_sheet->css_rules()->length()); + EXPECT_EQ(0, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, HandlesInvalidAtRuleEndsWithSemicolons) { @@ -141,7 +141,7 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("@casino-royale;", source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(0, style_sheet->css_rules()->length()); + EXPECT_EQ(0, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, HandlesInvalidAtRuleWithoutSemicolonsAtTheEndOfFile) { @@ -153,7 +153,7 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("@casino-royale", source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(0, style_sheet->css_rules()->length()); + EXPECT_EQ(0, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, HandlesInvalidAtRuleWithNoMatchingEndBrace) { @@ -165,7 +165,7 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("@casino-royale }", source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(0, style_sheet->css_rules()->length()); + EXPECT_EQ(0, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, ComputesErrorLocationOnFirstLine) { @@ -176,7 +176,7 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet( "cucumber", base::SourceLocation("[object ParserTest]", 10, 10)); ASSERT_TRUE(style_sheet); - EXPECT_EQ(0, style_sheet->css_rules()->length()); + EXPECT_EQ(0, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, ComputesErrorLocationOnSecondLine) { @@ -189,14 +189,14 @@ "cucumber", base::SourceLocation("[object ParserTest]", 10, 10)); ASSERT_TRUE(style_sheet); - EXPECT_EQ(0, style_sheet->css_rules()->length()); + EXPECT_EQ(0, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, IgnoresSgmlCommentDelimiters) { scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("<!-- body {} -->", source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(1, style_sheet->css_rules()->length()); + EXPECT_EQ(1, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, RecoversFromInvalidAtToken) { @@ -208,7 +208,7 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet( "body {} @cobalt-magic; div {}", source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(2, style_sheet->css_rules()->length()); + EXPECT_EQ(2, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, RecoversFromInvalidRuleWhichEndsWithSemicolon) { @@ -219,7 +219,7 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet( "body {} @charset 'utf-8'; div {}", source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(2, style_sheet->css_rules()->length()); + EXPECT_EQ(2, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, RecoversFromInvalidRuleWhichEndsWithBlock) { @@ -230,7 +230,7 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("body {} !important {} div {}", source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(2, style_sheet->css_rules()->length()); + EXPECT_EQ(2, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, SemicolonsDonotEndQualifiedRules) { @@ -241,18 +241,18 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("foo; bar {} div {}", source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(1, style_sheet->css_rules()->length()); + EXPECT_EQ(1, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, ParsesAttributeSelectorNoValue) { scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("[attr] {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -274,11 +274,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("[attr=\"value\"] {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -301,11 +301,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("[attr=value] {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -328,11 +328,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(".my-class {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -353,11 +353,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(":after {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -378,11 +378,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("::after {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -403,11 +403,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(":before {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -428,11 +428,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("::before {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -453,11 +453,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(":active {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -478,11 +478,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(":empty {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -502,11 +502,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(":focus {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -526,11 +526,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(":hover {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -550,11 +550,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(":not(div.my-class) {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -593,7 +593,7 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(":not() {}", source_location_); - EXPECT_EQ(0, style_sheet->css_rules()->length()); + EXPECT_EQ(0, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, ParsesNotPseudoClassShouldNotTakeComplexSelector) { @@ -604,18 +604,18 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet(":not(div span) {}", source_location_); - EXPECT_EQ(0, style_sheet->css_rules()->length()); + EXPECT_EQ(0, style_sheet->css_rules_same_origin()->length()); } TEST_F(ParserTest, ParsesIdSelector) { scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("#my-id {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -636,11 +636,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("div {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -661,11 +661,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("* {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -686,11 +686,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("div.my-class {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -716,11 +716,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("div div {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -737,11 +737,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("div ~ div {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -758,11 +758,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("div > div {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -779,11 +779,11 @@ scoped_refptr<cssom::CSSStyleSheet> style_sheet = parser_.ParseStyleSheet("div + div {}", source_location_); - ASSERT_EQ(1, style_sheet->css_rules()->length()); + ASSERT_EQ(1, style_sheet->css_rules_same_origin()->length()); ASSERT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); cssom::CSSStyleRule* style_rule = static_cast<cssom::CSSStyleRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_EQ(1, style_rule->selectors().size()); cssom::ComplexSelector* complex_selector = dynamic_cast<cssom::ComplexSelector*>( @@ -947,9 +947,9 @@ "#my-id {} \n", source_location_); - EXPECT_EQ(1, style_sheet->css_rules()->length()); + EXPECT_EQ(1, style_sheet->css_rules_same_origin()->length()); EXPECT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); } TEST_F(ParserTest, WarnsAboutInvalidAtRuleCurlyBraceInsideOfAnotherBlock) { @@ -969,9 +969,9 @@ "#my-id {} \n", source_location_); - EXPECT_EQ(1, style_sheet->css_rules()->length()); + EXPECT_EQ(1, style_sheet->css_rules_same_origin()->length()); EXPECT_EQ(cssom::CSSRule::kStyleRule, - style_sheet->css_rules()->Item(0)->type()); + style_sheet->css_rules_same_origin()->Item(0)->type()); } TEST_F(ParserTest, StyleSheetEndsWhileRuleIsStillOpen) { @@ -985,11 +985,11 @@ source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(1, style_sheet->css_rules()->length()); + EXPECT_EQ(1, style_sheet->css_rules_same_origin()->length()); cssom::CSSKeyframesRule* keyframes_rule = dynamic_cast<cssom::CSSKeyframesRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_TRUE(keyframes_rule); EXPECT_EQ("foo3", keyframes_rule->name()); @@ -6937,11 +6937,11 @@ source_location_); ASSERT_TRUE(style_sheet); - EXPECT_EQ(1, style_sheet->css_rules()->length()); + EXPECT_EQ(1, style_sheet->css_rules_same_origin()->length()); cssom::CSSKeyframesRule* keyframes_rule = dynamic_cast<cssom::CSSKeyframesRule*>( - style_sheet->css_rules()->Item(0).get()); + style_sheet->css_rules_same_origin()->Item(0).get()); ASSERT_TRUE(keyframes_rule); EXPECT_EQ("foo3", keyframes_rule->name());
diff --git a/src/cobalt/cssom/cascaded_style_test.cc b/src/cobalt/cssom/cascaded_style_test.cc index 2e8a913..bad631e 100644 --- a/src/cobalt/cssom/cascaded_style_test.cc +++ b/src/cobalt/cssom/cascaded_style_test.cc
@@ -144,6 +144,7 @@ std::make_pair(css_style_rule_2, cascade_precedence_2)); scoped_refptr<CSSStyleSheet> parent_style_sheet(new CSSStyleSheet()); + parent_style_sheet->SetOriginClean(true); parent_style_sheet->SetLocationUrl(GURL("https:///www.youtube.com/tv/img")); css_style_rule_2->set_parent_style_sheet(parent_style_sheet.get()); @@ -187,6 +188,7 @@ std::make_pair(css_style_rule, cascade_precedence)); scoped_refptr<CSSStyleSheet> parent_style_sheet(new CSSStyleSheet()); + parent_style_sheet->SetOriginClean(true); css_style_rule->set_parent_style_sheet(parent_style_sheet.get()); scoped_refptr<CSSComputedStyleData> computed_style = PromoteToCascadedStyle(
diff --git a/src/cobalt/cssom/css_font_face_rule_test.cc b/src/cobalt/cssom/css_font_face_rule_test.cc index 3980882..89dcadb 100644 --- a/src/cobalt/cssom/css_font_face_rule_test.cc +++ b/src/cobalt/cssom/css_font_face_rule_test.cc
@@ -48,6 +48,7 @@ class CSSFontFaceRuleTest : public ::testing::Test { protected: CSSFontFaceRuleTest() : css_style_sheet_(new CSSStyleSheet(&css_parser_)) { + css_style_sheet_->SetOriginClean(true); StyleSheetVector style_sheets; style_sheets.push_back(css_style_sheet_); style_sheet_list_ = new StyleSheetList(style_sheets, &mutation_observer_);
diff --git a/src/cobalt/cssom/css_style_sheet.cc b/src/cobalt/cssom/css_style_sheet.cc index 4318abb..b1575d3 100644 --- a/src/cobalt/cssom/css_style_sheet.cc +++ b/src/cobalt/cssom/css_style_sheet.cc
@@ -24,6 +24,7 @@ #include "cobalt/cssom/css_style_declaration.h" #include "cobalt/cssom/css_style_rule.h" #include "cobalt/cssom/style_sheet_list.h" +#include "cobalt/dom/dom_exception.h" namespace cobalt { namespace cssom { @@ -72,15 +73,32 @@ : parent_style_sheet_list_(NULL), css_parser_(NULL), media_rules_changed_(false), - origin_(kNormalAuthor) {} + origin_(kNormalAuthor), + origin_clean_(false) {} CSSStyleSheet::CSSStyleSheet(CSSParser* css_parser) : parent_style_sheet_list_(NULL), css_parser_(css_parser), media_rules_changed_(false), - origin_(kNormalAuthor) {} + origin_(kNormalAuthor), + origin_clean_(false) {} -const scoped_refptr<CSSRuleList>& CSSStyleSheet::css_rules() { +// https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssrules +const scoped_refptr<CSSRuleList>& CSSStyleSheet::css_rules( + script::ExceptionState* exception_state) { + if (!origin_clean_) { + dom::DOMException::Raise( + dom::DOMException::kSecurityErr, + "Website trys to access css rules from a CSSStyleSheet " + "fetched from another origin.", + exception_state); + DCHECK(!null_css_rule_list_); + return null_css_rule_list_; + } + return css_rules_same_origin(); +} + +const scoped_refptr<CSSRuleList>& CSSStyleSheet::css_rules_same_origin() { if (!css_rule_list_) { set_css_rules(new CSSRuleList()); } @@ -88,9 +106,24 @@ return css_rule_list_; } -unsigned int CSSStyleSheet::InsertRule(const std::string& rule, - unsigned int index) { - return css_rules()->InsertRule(rule, index); +// https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule +unsigned int CSSStyleSheet::InsertRule( + const std::string& rule, unsigned int index, + script::ExceptionState* exception_state) { + if (!origin_clean_) { + dom::DOMException::Raise( + dom::DOMException::kSecurityErr, + "Website trys to insert css rule to a CSSStyleSheet fetched" + "from another origin.", + exception_state); + return 0; + } + return css_rules_same_origin()->InsertRule(rule, index); +} + +unsigned int CSSStyleSheet::InsertRuleSameOrigin(const std::string& rule, + unsigned int index) { + return css_rules_same_origin()->InsertRule(rule, index); } void CSSStyleSheet::OnCSSMutation() {
diff --git a/src/cobalt/cssom/css_style_sheet.h b/src/cobalt/cssom/css_style_sheet.h index aefa0de..1d3dabb 100644 --- a/src/cobalt/cssom/css_style_sheet.h +++ b/src/cobalt/cssom/css_style_sheet.h
@@ -27,6 +27,7 @@ #include "cobalt/cssom/mutation_observer.h" #include "cobalt/cssom/style_sheet.h" #include "cobalt/math/size.h" +#include "cobalt/script/exception_state.h" #include "googleurl/src/gurl.h" namespace cobalt { @@ -48,11 +49,22 @@ // Web API: CSSStyleSheet // // Returns a read-only, live object representing the CSS rules. - const scoped_refptr<CSSRuleList>& css_rules(); + const scoped_refptr<CSSRuleList>& css_rules( + cobalt::script::ExceptionState* exception_state); + + // Bypass same origin policy to get css rules. This assumes that the request + // comes from same origin and should not be accessible to javascript code. + const scoped_refptr<CSSRuleList>& css_rules_same_origin(); // Inserts a new rule into the current style sheet. This Web API takes a // string as input and parses it into a rule. - unsigned int InsertRule(const std::string& rule, unsigned int index); + unsigned int InsertRule(const std::string& rule, unsigned int index, + cobalt::script::ExceptionState* exception_state); + + // Insert css rules without disallowing cross-origin access. This should be + // used internally by Cobalt. + unsigned int InsertRuleSameOrigin(const std::string& rule, + unsigned int index); // Custom, not in any spec. // @@ -82,6 +94,8 @@ // that is reset in EvaluateMediaRules(). void OnMediaRuleMutation() { media_rules_changed_ = true; } + void SetOriginClean(bool origin_clean) { origin_clean_ = origin_clean; } + DEFINE_WRAPPABLE_TYPE(CSSStyleSheet); private: @@ -89,6 +103,9 @@ scoped_refptr<CSSRuleList> css_rule_list_; + // Null scoped_refptr used when access to css rules should be blocked. + scoped_refptr<CSSRuleList> null_css_rule_list_; + StyleSheetList* parent_style_sheet_list_; CSSParser* const css_parser_; GURL location_url_; @@ -104,6 +121,11 @@ // Origin of this style sheet. Origin origin_; + // https://drafts.csswg.org/cssom/#concept-css-style-sheet-origin-clean-flag + // It is used to block cross-origin website's access to the fetched style + // sheet. It is only possible to be set false when creating a HTMLLinkElement + bool origin_clean_; + // Since CSSRuleList is merely a proxy, it needs access to CSS rules stored // in the stylesheet. friend class CSSRuleList;
diff --git a/src/cobalt/cssom/css_style_sheet.idl b/src/cobalt/cssom/css_style_sheet.idl index 49ed8d7..71d88ca 100644 --- a/src/cobalt/cssom/css_style_sheet.idl +++ b/src/cobalt/cssom/css_style_sheet.idl
@@ -15,6 +15,6 @@ // https://www.w3.org/TR/2013/WD-cssom-20131205/#the-cssstylesheet-interface interface CSSStyleSheet : StyleSheet { - [SameObject] readonly attribute CSSRuleList cssRules; - unsigned long insertRule(DOMString rule, unsigned long index); + [SameObject, RaisesException] readonly attribute CSSRuleList cssRules; + [RaisesException] unsigned long insertRule(DOMString rule, unsigned long index); };
diff --git a/src/cobalt/cssom/css_style_sheet_test.cc b/src/cobalt/cssom/css_style_sheet_test.cc index e817f48..25b8a0b 100644 --- a/src/cobalt/cssom/css_style_sheet_test.cc +++ b/src/cobalt/cssom/css_style_sheet_test.cc
@@ -46,6 +46,7 @@ class CSSStyleSheetTest : public ::testing::Test { protected: CSSStyleSheetTest() : css_style_sheet_(new CSSStyleSheet(&css_parser_)) { + css_style_sheet_->SetOriginClean(true); StyleSheetVector style_sheets; style_sheets.push_back(css_style_sheet_); style_sheet_list_ = new StyleSheetList(style_sheets, &mutation_observer_); @@ -63,17 +64,20 @@ EXPECT_CALL(css_parser_, ParseRule(css_text, _)) .WillOnce(Return(scoped_refptr<CSSRule>())); - css_style_sheet_->InsertRule(css_text, 0); + css_style_sheet_->InsertRuleSameOrigin(css_text, 0); } TEST_F(CSSStyleSheetTest, CSSRuleListIsCached) { - scoped_refptr<CSSRuleList> rule_list_1 = css_style_sheet_->css_rules(); - scoped_refptr<CSSRuleList> rule_list_2 = css_style_sheet_->css_rules(); + scoped_refptr<CSSRuleList> rule_list_1 = + css_style_sheet_->css_rules_same_origin(); + scoped_refptr<CSSRuleList> rule_list_2 = + css_style_sheet_->css_rules_same_origin(); ASSERT_EQ(rule_list_1, rule_list_2); } TEST_F(CSSStyleSheetTest, CSSRuleListIsLive) { - scoped_refptr<CSSRuleList> rule_list = css_style_sheet_->css_rules(); + scoped_refptr<CSSRuleList> rule_list = + css_style_sheet_->css_rules_same_origin(); ASSERT_EQ(0, rule_list->length()); ASSERT_FALSE(rule_list->Item(0).get()); @@ -86,7 +90,7 @@ ASSERT_EQ(1, rule_list->length()); ASSERT_EQ(rule, rule_list->Item(0)); ASSERT_FALSE(rule_list->Item(1).get()); - ASSERT_EQ(rule_list, css_style_sheet_->css_rules()); + ASSERT_EQ(rule_list, css_style_sheet_->css_rules_same_origin()); } TEST_F(CSSStyleSheetTest, CSSMutationIsReportedAtStyleSheetList) { @@ -104,7 +108,8 @@ // EvaluateMediaRules(), even when called with the same media parameters as // before. That also tests that OnMediaRuleMutation() should be called and // that the flag it sets should be honored. - scoped_refptr<CSSRuleList> rule_list = css_style_sheet_->css_rules(); + scoped_refptr<CSSRuleList> rule_list = + css_style_sheet_->css_rules_same_origin(); // A CSSMediaRule with no expression always evaluates to true. scoped_refptr<CSSMediaRule> rule = new CSSMediaRule(); @@ -127,7 +132,8 @@ scoped_refptr<MediaList> media_list(new MediaList()); media_list->Append(media_query); scoped_refptr<CSSMediaRule> rule = new CSSMediaRule(media_list, NULL); - scoped_refptr<CSSRuleList> rule_list = css_style_sheet_->css_rules(); + scoped_refptr<CSSRuleList> rule_list = + css_style_sheet_->css_rules_same_origin(); EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); rule_list->AppendCSSRule(rule); @@ -159,7 +165,7 @@ // added to the style sheet. EXPECT_CALL(mutation_observer_, OnCSSMutation()).Times(1); - css_style_sheet_->css_rules()->AppendCSSRule(rule); + css_style_sheet_->css_rules_same_origin()->AppendCSSRule(rule); // This should result in a call to OnCSSMutation(), because the added media // rule evaluates to true, so its rule list needs to be traversed for the next
diff --git a/src/cobalt/cssom/style_sheet_list_test.cc b/src/cobalt/cssom/style_sheet_list_test.cc index 3a0fce2..40b4b70 100644 --- a/src/cobalt/cssom/style_sheet_list_test.cc +++ b/src/cobalt/cssom/style_sheet_list_test.cc
@@ -26,6 +26,7 @@ ASSERT_FALSE(style_sheet_list->Item(0).get()); scoped_refptr<CSSStyleSheet> style_sheet = new CSSStyleSheet(); + style_sheet->SetOriginClean(true); StyleSheetVector style_sheet_vector; style_sheet_vector.push_back(style_sheet); style_sheet_list = new StyleSheetList(style_sheet_vector, NULL);
diff --git a/src/cobalt/cssom/user_agent_style_sheet.cc b/src/cobalt/cssom/user_agent_style_sheet.cc index aa36c3e..3c3fb81 100644 --- a/src/cobalt/cssom/user_agent_style_sheet.cc +++ b/src/cobalt/cssom/user_agent_style_sheet.cc
@@ -42,6 +42,7 @@ static_cast<size_t>(html_css_file_contents.size)), base::SourceLocation(kUserAgentStyleSheetFileName, 1, 1)); user_agent_style_sheet->set_origin(kNormalUserAgent); + user_agent_style_sheet->SetOriginClean(true); return user_agent_style_sheet; }
diff --git a/src/cobalt/debug/debug_script_runner.cc b/src/cobalt/debug/debug_script_runner.cc index 4a61086..36a44ca 100644 --- a/src/cobalt/debug/debug_script_runner.cc +++ b/src/cobalt/debug/debug_script_runner.cc
@@ -82,7 +82,8 @@ script::SourceCode::CreateSourceCode(js_code, GetInlineSourceLocation()); ForceEnableEval(); - bool succeeded = global_environment_->EvaluateScript(source_code, result); + bool succeeded = global_environment_->EvaluateScript(source_code, result, + false /*mute_errors*/); SetEvalAllowedFromCsp(); return succeeded; }
diff --git a/src/cobalt/dom/custom_event_test.cc b/src/cobalt/dom/custom_event_test.cc index 65c85c1..6712aff 100644 --- a/src/cobalt/dom/custom_event_test.cc +++ b/src/cobalt/dom/custom_event_test.cc
@@ -110,7 +110,8 @@ global_environment_->EnableEval(); global_environment_->SetReportEvalCallback(base::Closure()); - bool succeeded = global_environment_->EvaluateScript(source_code, result); + bool succeeded = global_environment_->EvaluateScript(source_code, result, + false /*mute_errors*/); return succeeded; } } // namespace
diff --git a/src/cobalt/dom/document.cc b/src/cobalt/dom/document.cc index 211b221..3249b8c 100644 --- a/src/cobalt/dom/document.cc +++ b/src/cobalt/dom/document.cc
@@ -381,27 +381,84 @@ return style_sheets_; } -void Document::set_cookie(const std::string& cookie) { -#if defined(COBALT_BUILD_TYPE_GOLD) - UNREFERENCED_PARAMETER(cookie); -#else +// https://html.spec.whatwg.org/#cookie-averse-document-object +bool Document::IsCookieAverseDocument() const { + return !HasBrowsingContext() || (!location_->url().SchemeIs("ftp") && + !location_->url().SchemeIs("http") && + !location_->url().SchemeIs("https")); +} + +// https://html.spec.whatwg.org/#dom-document-cookie +void Document::set_cookie(const std::string& cookie, + script::ExceptionState* exception_state) { + if (IsCookieAverseDocument()) { + DLOG(WARNING) << "Document is a cookie-averse Document object, not " + "setting cookie."; + return; + } + if (location_->OriginObject().is_opaque()) { + DOMException::Raise(DOMException::kSecurityErr, + "Document origin is opaque, cookie setting failed", + exception_state); + return; + } if (cookie_jar_) { cookie_jar_->SetCookie(url_as_gurl(), cookie); } -#endif } -std::string Document::cookie() const { -#if defined(COBALT_BUILD_TYPE_GOLD) - return std::string(); -#else +// https://html.spec.whatwg.org/#dom-document-cookie +std::string Document::cookie(script::ExceptionState* exception_state) const { + if (IsCookieAverseDocument()) { + DLOG(WARNING) << "Document is a cookie-averse Document object, returning " + "empty cookie."; + return ""; + } + if (location_->OriginObject().is_opaque()) { + DOMException::Raise(DOMException::kSecurityErr, + "Document origin is opaque, cookie getting failed", + exception_state); + return ""; + } if (cookie_jar_) { return cookie_jar_->GetCookies(url_as_gurl()); } else { DLOG(WARNING) << "Document has no cookie jar"; return ""; } -#endif +} + +void Document::set_cookie(const std::string& cookie) { + if (IsCookieAverseDocument()) { + DLOG(WARNING) << "Document is a cookie-averse Document object, not " + "setting cookie."; + return; + } + if (location_->OriginObject().is_opaque()) { + DLOG(WARNING) << "Document origin is opaque, cookie setting failed"; + return; + } + if (cookie_jar_) { + cookie_jar_->SetCookie(url_as_gurl(), cookie); + } +} + +std::string Document::cookie() const { + if (IsCookieAverseDocument()) { + DLOG(WARNING) << "Document is a cookie-averse Document object, returning " + "empty cookie."; + return ""; + } + if (location_->OriginObject().is_opaque()) { + DLOG(WARNING) << "Document origin is opaque, cookie getting failed"; + return ""; + } + if (cookie_jar_) { + return cookie_jar_->GetCookies(url_as_gurl()); + } else { + DLOG(WARNING) << "Document has no cookie jar"; + return ""; + } } void Document::Accept(NodeVisitor* visitor) { visitor->Visit(this); } @@ -607,13 +664,14 @@ void AppendRulesFromCSSStyleSheetToSelectorTree( const scoped_refptr<cssom::CSSStyleSheet>& style_sheet, cssom::SelectorTree* selector_tree) { - AppendRulesFromCSSRuleListToSelectorTree(style_sheet->css_rules(), + AppendRulesFromCSSRuleListToSelectorTree(style_sheet->css_rules_same_origin(), selector_tree); } void ClearAddedToSelectorTreeFromCSSStyleSheetRules( const scoped_refptr<cssom::CSSStyleSheet>& style_sheet) { - RemoveRulesFromCSSRuleListFromSelectorTree(style_sheet->css_rules(), NULL); + RemoveRulesFromCSSRuleListFromSelectorTree( + style_sheet->css_rules_same_origin(), NULL); } } // namespace @@ -659,8 +717,8 @@ } bool Document::UpdateComputedStyleOnElementAndAncestor(HTMLElement* element) { - TRACE_EVENT0( - "cobalt::dom", "Document::UpdateComputedStyleOnElementAndAncestor"); + TRACE_EVENT0("cobalt::dom", + "Document::UpdateComputedStyleOnElementAndAncestor"); if (!element || element->node_document() != this) { return false; }
diff --git a/src/cobalt/dom/document.h b/src/cobalt/dom/document.h index 42c824f..52a5d30 100644 --- a/src/cobalt/dom/document.h +++ b/src/cobalt/dom/document.h
@@ -218,6 +218,11 @@ } // https://www.w3.org/TR/html5/dom.html#dom-document-cookie + void set_cookie(const std::string& cookie, + script::ExceptionState* exception_state); + std::string cookie(script::ExceptionState* exception_state) const; + + // For Cobalt code use only. Logs warnings instead of raising exceptions. void set_cookie(const std::string& cookie); std::string cookie() const; @@ -265,7 +270,7 @@ // Returns whether the document has browsing context. Having the browsing // context means the document is shown on the screen. // https://www.w3.org/TR/html5/browsers.html#browsing-context - bool HasBrowsingContext() { return !!window_; } + bool HasBrowsingContext() const { return !!window_; } void set_window(Window* window) { window_ = window; } const scoped_refptr<Window> window(); @@ -431,6 +436,8 @@ // Animations, using all the style sheets in the document. void UpdateKeyframes(); + bool IsCookieAverseDocument() const; + // Reference to HTML element context. HTMLElementContext* const html_element_context_; // Reference to the associated window object.
diff --git a/src/cobalt/dom/document.idl b/src/cobalt/dom/document.idl index a2e81ee..0a832e4 100644 --- a/src/cobalt/dom/document.idl +++ b/src/cobalt/dom/document.idl
@@ -34,7 +34,7 @@ [NewObject] Comment createComment(DOMString data); [NewObject, RaisesException] Event createEvent(DOMString interfaceName); - attribute DOMString cookie; + [RaisesException] attribute DOMString cookie; }; Document implements GlobalEventHandlers;
diff --git a/src/cobalt/dom/dom_settings.cc b/src/cobalt/dom/dom_settings.cc index 35bee6d..fab84d0 100644 --- a/src/cobalt/dom/dom_settings.cc +++ b/src/cobalt/dom/dom_settings.cc
@@ -63,7 +63,7 @@ return window()->document()->url_as_gurl(); } -const dom::URLUtils::Origin& DOMSettings::document_origin() const { +const loader::Origin& DOMSettings::document_origin() const { return window()->document()->location()->OriginObject(); }
diff --git a/src/cobalt/dom/dom_settings.h b/src/cobalt/dom/dom_settings.h index 77ebe51..6d9d65e 100644 --- a/src/cobalt/dom/dom_settings.h +++ b/src/cobalt/dom/dom_settings.h
@@ -130,7 +130,7 @@ virtual const GURL& base_url() const; // Return's document's origin. - const URLUtils::Origin& document_origin() const; + const loader::Origin& document_origin() const; private: const int max_dom_element_depth_;
diff --git a/src/cobalt/dom/error_event_test.cc b/src/cobalt/dom/error_event_test.cc index 40a51d2..7995e45 100644 --- a/src/cobalt/dom/error_event_test.cc +++ b/src/cobalt/dom/error_event_test.cc
@@ -110,7 +110,8 @@ global_environment_->EnableEval(); global_environment_->SetReportEvalCallback(base::Closure()); - bool succeeded = global_environment_->EvaluateScript(source_code, result); + bool succeeded = global_environment_->EvaluateScript(source_code, result, + false /*mute_errors*/); return succeeded; } } // namespace
diff --git a/src/cobalt/dom/font_face_updater.cc b/src/cobalt/dom/font_face_updater.cc index 453dea3..1f47d20 100644 --- a/src/cobalt/dom/font_face_updater.cc +++ b/src/cobalt/dom/font_face_updater.cc
@@ -247,8 +247,8 @@ void FontFaceUpdater::ProcessCSSStyleSheet( const scoped_refptr<cssom::CSSStyleSheet>& style_sheet) { - if (style_sheet && style_sheet->css_rules()) { - style_sheet->css_rules()->Accept(this); + if (style_sheet && style_sheet->css_rules_same_origin()) { + style_sheet->css_rules_same_origin()->Accept(this); } }
diff --git a/src/cobalt/dom/html_link_element.cc b/src/cobalt/dom/html_link_element.cc index ab3dfb6..e18a057 100644 --- a/src/cobalt/dom/html_link_element.cc +++ b/src/cobalt/dom/html_link_element.cc
@@ -127,6 +127,8 @@ &CspDelegate::CanLoad, base::Unretained(document->csp_delegate()), GetCspResourceTypeForRel(rel())); + fetched_last_url_origin_ = loader::Origin(); + if (IsRelContentCriticalResource(rel())) { // The element must delay the load event of the element's document until all // the attempts to obtain the resource and its critical subresources are @@ -144,10 +146,15 @@ base::Bind(&HTMLLinkElement::OnLoadingError, base::Unretained(this)))); } -void HTMLLinkElement::OnLoadingDone(const std::string& content) { +void HTMLLinkElement::OnLoadingDone(const std::string& content, + const loader::Origin& last_url_origin) { TRACK_MEMORY_SCOPE("DOM"); DCHECK(thread_checker_.CalledOnValidThread()); TRACE_EVENT0("cobalt::dom", "HTMLLinkElement::OnLoadingDone()"); + + // Get resource's final destination url from loader. + fetched_last_url_origin_ = last_url_origin; + Document* document = node_document(); if (rel() == "stylesheet") { OnStylesheetLoaded(document, content); @@ -211,10 +218,17 @@ void HTMLLinkElement::OnStylesheetLoaded(Document* document, const std::string& content) { - style_sheet_ = + scoped_refptr<cssom::CSSStyleSheet> css_style_sheet = document->html_element_context()->css_parser()->ParseStyleSheet( content, base::SourceLocation(href(), 1, 1)); - style_sheet_->SetLocationUrl(absolute_url_); + css_style_sheet->SetLocationUrl(absolute_url_); + // If not loading from network-fetched resources or fetched resource is same + // origin as the document, set origin-clean flag to true. + if (!loader_ || document->url_as_gurl().SchemeIsFile() || + (fetched_last_url_origin_ == document->location()->OriginObject())) { + css_style_sheet->SetOriginClean(true); + } + style_sheet_ = css_style_sheet; document->OnStyleSheetsModified(); }
diff --git a/src/cobalt/dom/html_link_element.h b/src/cobalt/dom/html_link_element.h index 1390480..f9f41c1 100644 --- a/src/cobalt/dom/html_link_element.h +++ b/src/cobalt/dom/html_link_element.h
@@ -22,6 +22,7 @@ #include "base/threading/thread_checker.h" #include "cobalt/cssom/style_sheet.h" #include "cobalt/dom/html_element.h" +#include "cobalt/dom/url_utils.h" #include "cobalt/loader/fetcher_factory.h" #include "cobalt/loader/loader.h" #include "cobalt/loader/text_decoder.h" @@ -70,7 +71,8 @@ // From the spec: HTMLLinkElement. void Obtain(); - void OnLoadingDone(const std::string& content); + void OnLoadingDone(const std::string& content, + const loader::Origin& last_url_origin); void OnLoadingError(const std::string& error); void OnSplashscreenLoaded(Document* document, const std::string& content); void OnStylesheetLoaded(Document* document, const std::string& content); @@ -90,6 +92,8 @@ // The style sheet associated with this element. scoped_refptr<cssom::StyleSheet> style_sheet_; + + loader::Origin fetched_last_url_origin_; }; } // namespace dom
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc index 652c504..3baa07f 100644 --- a/src/cobalt/dom/html_script_element.cc +++ b/src/cobalt/dom/html_script_element.cc
@@ -239,6 +239,9 @@ CspDelegate::kScript); } + // Clear origin before start. + fetched_last_url_origin_ = loader::Origin(); + switch (load_option_) { case 2: { // If the element has a src attribute, and the element has been flagged as @@ -337,6 +340,7 @@ csp_delegate->AllowInline(CspDelegate::kScript, inline_script_location_, text)) { + fetched_last_url_origin_ = document_->location()->OriginObject(); ExecuteInternal(); } else { PreventGarbageCollectionAndPostToDispatchEvent(FROM_HERE, @@ -347,10 +351,12 @@ } } -void HTMLScriptElement::OnSyncLoadingDone(const std::string& content) { +void HTMLScriptElement::OnSyncLoadingDone( + const std::string& content, const loader::Origin& last_url_origin) { TRACE_EVENT0("cobalt::dom", "HTMLScriptElement::OnSyncLoadingDone()"); content_ = content; is_sync_load_successful_ = true; + fetched_last_url_origin_ = last_url_origin; } void HTMLScriptElement::OnSyncLoadingError(const std::string& error) { @@ -360,7 +366,8 @@ // Algorithm for OnLoadingDone: // https://www.w3.org/TR/html5/scripting-1.html#prepare-a-script -void HTMLScriptElement::OnLoadingDone(const std::string& content) { +void HTMLScriptElement::OnLoadingDone(const std::string& content, + const loader::Origin& last_url_origin) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(load_option_ == 4 || load_option_ == 5); TRACE_EVENT0("cobalt::dom", "HTMLScriptElement::OnLoadingDone()"); @@ -368,6 +375,7 @@ AllowGarbageCollection(); return; } + fetched_last_url_origin_ = last_url_origin; content_ = content; switch (load_option_) { @@ -522,8 +530,10 @@ // script was obtained, the script block's type as the scripting language, and // the script settings object of the script element's Document's Window // object. - html_element_context()->script_runner()->Execute(content, script_location, - NULL /* output: succeeded */); + bool mute_errors = + fetched_last_url_origin_ != document_->location()->OriginObject(); + html_element_context()->script_runner()->Execute( + content, script_location, NULL /* output: succeeded */, mute_errors); // 5. 6. Not needed by Cobalt.
diff --git a/src/cobalt/dom/html_script_element.h b/src/cobalt/dom/html_script_element.h index 8307158..b444426 100644 --- a/src/cobalt/dom/html_script_element.h +++ b/src/cobalt/dom/html_script_element.h
@@ -22,6 +22,7 @@ #include "base/threading/thread_checker.h" #include "cobalt/base/source_location.h" #include "cobalt/dom/html_element.h" +#include "cobalt/dom/url_utils.h" #include "cobalt/loader/loader.h" namespace cobalt { @@ -89,10 +90,12 @@ // void Prepare(); - void OnSyncLoadingDone(const std::string& content); + void OnSyncLoadingDone(const std::string& content, + const loader::Origin& last_url_origin); void OnSyncLoadingError(const std::string& error); - void OnLoadingDone(const std::string& content); + void OnLoadingDone(const std::string& content, + const loader::Origin& last_url_origin); void OnLoadingError(const std::string& error); void ExecuteExternal() { @@ -138,6 +141,11 @@ // Whether or not the script should execute at all. bool should_execute_; + + // Will be compared with document's origin to derive mute_errors flag + // javascript parser takes in to record if the error reqort should be muted + // due to cross-origin fetched script. + loader::Origin fetched_last_url_origin_; }; } // namespace dom
diff --git a/src/cobalt/dom/html_style_element.cc b/src/cobalt/dom/html_style_element.cc index c55a3de..b64b6cd 100644 --- a/src/cobalt/dom/html_style_element.cc +++ b/src/cobalt/dom/html_style_element.cc
@@ -82,10 +82,12 @@ if (bypass_csp || text.empty() || csp_delegate->AllowInline(CspDelegate::kStyle, inline_style_location_, text)) { - style_sheet_ = + scoped_refptr<cssom::CSSStyleSheet> css_style_sheet = document->html_element_context()->css_parser()->ParseStyleSheet( text, inline_style_location_); - style_sheet_->SetLocationUrl(GURL(inline_style_location_.file_path)); + css_style_sheet->SetLocationUrl(GURL(inline_style_location_.file_path)); + css_style_sheet->SetOriginClean(true); + style_sheet_ = css_style_sheet; document->OnStyleSheetsModified(); } else { // Report a violation.
diff --git a/src/cobalt/dom/keyframes_map_updater.cc b/src/cobalt/dom/keyframes_map_updater.cc index de597a4..dc1d636 100644 --- a/src/cobalt/dom/keyframes_map_updater.cc +++ b/src/cobalt/dom/keyframes_map_updater.cc
@@ -38,8 +38,8 @@ const scoped_refptr<cssom::CSSStyleSheet>& style_sheet) { // Iterate through each rule in the stylesheet and add any CSSKeyframesRule // objects to our keyframes map. - if (style_sheet && style_sheet->css_rules()) { - style_sheet->css_rules()->Accept(this); + if (style_sheet && style_sheet->css_rules_same_origin()) { + style_sheet->css_rules_same_origin()->Accept(this); } }
diff --git a/src/cobalt/dom/location.h b/src/cobalt/dom/location.h index 64baf0b..7d93324 100644 --- a/src/cobalt/dom/location.h +++ b/src/cobalt/dom/location.h
@@ -89,7 +89,7 @@ const GURL& url() const { return url_utils_.url(); } void set_url(const GURL& url) { url_utils_.set_url(url); } - const URLUtils::Origin& OriginObject() const { + const loader::Origin& OriginObject() const { return url_utils_.OriginObject(); }
diff --git a/src/cobalt/dom/rule_matching_test.cc b/src/cobalt/dom/rule_matching_test.cc index 6a562fc..d4c7d8c 100644 --- a/src/cobalt/dom/rule_matching_test.cc +++ b/src/cobalt/dom/rule_matching_test.cc
@@ -103,7 +103,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -116,7 +116,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -129,7 +129,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -142,7 +142,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -155,7 +155,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -179,7 +179,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -192,7 +192,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -205,7 +205,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -218,7 +218,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -252,7 +252,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -282,7 +282,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -311,7 +311,7 @@ matching_rules = html_element->pseudo_element(kAfterPseudoElementType)->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); html_element = body_->last_element_child()->AsHTMLElement(); @@ -321,7 +321,7 @@ matching_rules = html_element->pseudo_element(kAfterPseudoElementType)->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -339,7 +339,7 @@ matching_rules = html_element->pseudo_element(kAfterPseudoElementType)->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -370,7 +370,7 @@ matching_rules = html_element->pseudo_element(kBeforePseudoElementType)->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); html_element = body_->last_element_child()->AsHTMLElement(); @@ -380,7 +380,7 @@ matching_rules = html_element->pseudo_element(kBeforePseudoElementType)->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -398,7 +398,7 @@ matching_rules = html_element->pseudo_element(kBeforePseudoElementType)->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -435,7 +435,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -466,7 +466,7 @@ ->AsHTMLElement() ->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -496,7 +496,7 @@ ->AsHTMLElement() ->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -525,7 +525,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->last_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -549,7 +549,7 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->last_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -574,11 +574,11 @@ cssom::RulesWithCascadePrecedence* matching_rules = body_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(3, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[1].first); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[2].first); std::vector<cssom::Specificity> vs; @@ -606,7 +606,7 @@ ->AsHTMLElement() ->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -750,9 +750,9 @@ cssom::RulesWithCascadePrecedence* matching_rules = head_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_NE(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_NE(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); - EXPECT_EQ(GetDocumentStyleSheet(1)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(1)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); head_->RemoveChild(div2); @@ -763,9 +763,9 @@ matching_rules = head_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(1, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); - EXPECT_NE(GetDocumentStyleSheet(1)->css_rules()->Item(0), + EXPECT_NE(GetDocumentStyleSheet(1)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); } @@ -787,9 +787,9 @@ cssom::RulesWithCascadePrecedence* matching_rules = head_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(2, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); - EXPECT_NE(GetDocumentStyleSheet(1)->css_rules()->Item(0), + EXPECT_NE(GetDocumentStyleSheet(1)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); head_->RemoveChild(div2); @@ -800,9 +800,9 @@ matching_rules = head_->first_element_child()->AsHTMLElement()->matching_rules(); ASSERT_EQ(2, matching_rules->size()); - EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules()->Item(0), + EXPECT_EQ(GetDocumentStyleSheet(0)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); - EXPECT_NE(GetDocumentStyleSheet(1)->css_rules()->Item(0), + EXPECT_NE(GetDocumentStyleSheet(1)->css_rules_same_origin()->Item(0), (*matching_rules)[0].first); }
diff --git a/src/cobalt/dom/testing/stub_css_parser.cc b/src/cobalt/dom/testing/stub_css_parser.cc index 55bdbb5..9d4411c 100644 --- a/src/cobalt/dom/testing/stub_css_parser.cc +++ b/src/cobalt/dom/testing/stub_css_parser.cc
@@ -22,7 +22,9 @@ const std::string& input, const base::SourceLocation& input_location) { UNREFERENCED_PARAMETER(input); UNREFERENCED_PARAMETER(input_location); - return new cssom::CSSStyleSheet(); + cssom::CSSStyleSheet* new_style_sheet = new cssom::CSSStyleSheet(); + new_style_sheet->SetOriginClean(true); + return new_style_sheet; } scoped_refptr<cssom::CSSRule> StubCSSParser::ParseRule(
diff --git a/src/cobalt/dom/testing/stub_script_runner.cc b/src/cobalt/dom/testing/stub_script_runner.cc index 148e67f..8ad635a 100644 --- a/src/cobalt/dom/testing/stub_script_runner.cc +++ b/src/cobalt/dom/testing/stub_script_runner.cc
@@ -19,11 +19,11 @@ namespace testing { std::string StubScriptRunner::Execute( - const std::string& script_utf8, - const base::SourceLocation& script_location, - bool* out_succeeded) { + const std::string& script_utf8, const base::SourceLocation& script_location, + bool* out_succeeded, bool mute_errors) { UNREFERENCED_PARAMETER(script_utf8); UNREFERENCED_PARAMETER(script_location); + UNREFERENCED_PARAMETER(mute_errors); if (out_succeeded) { *out_succeeded = true; }
diff --git a/src/cobalt/dom/testing/stub_script_runner.h b/src/cobalt/dom/testing/stub_script_runner.h index 9c68840..fb42205 100644 --- a/src/cobalt/dom/testing/stub_script_runner.h +++ b/src/cobalt/dom/testing/stub_script_runner.h
@@ -27,7 +27,7 @@ public: std::string Execute(const std::string& script_utf8, const base::SourceLocation& script_location, - bool* out_succeeded) OVERRIDE; + bool* out_succeeded, bool mute_errors) OVERRIDE; }; } // namespace testing
diff --git a/src/cobalt/dom/url_utils.cc b/src/cobalt/dom/url_utils.cc index 57604bd..3cdea4e 100644 --- a/src/cobalt/dom/url_utils.cc +++ b/src/cobalt/dom/url_utils.cc
@@ -14,83 +14,16 @@ #include "cobalt/dom/url_utils.h" -namespace { -// Returns true if the url can have non-opaque origin. -// Blob URLs needs to be pre-processed. -bool NonBlobURLCanHaveTupleOrigin(const GURL& url) { - if (url.SchemeIs("https")) { - return true; - } else if (url.SchemeIs("http")) { - return true; - } else if (url.SchemeIs("ftp")) { - return true; - } else if (url.SchemeIs("ws")) { - return true; - } else if (url.SchemeIs("wss")) { - return true; - } - return false; -} -// Returns false if url should be opaque. -// Otherwise, extract origin tuple from the url and assemble them as a string -// for easier access and comparison. -bool NonBlobURLGetOriginStr(const GURL& url, std::string* output) { - if (!NonBlobURLCanHaveTupleOrigin(url)) { - return false; - } - *output = url.scheme() + "://" + url.host() + - (url.has_port() ? ":" + url.port() : ""); - return true; -} -} // namespace - namespace cobalt { namespace dom { -URLUtils::Origin::Origin() : is_opaque_(true) {} - -URLUtils::Origin::Origin(const GURL& url) : is_opaque_(false) { - if (url.is_valid() && url.has_scheme() && url.has_host()) { - if (url.SchemeIs("blob")) { - // Let path_url be the result of parsing URL's path. - // Return a new opaque origin, if url is failure, and url's origin - // otherwise. - GURL path_url(url.path()); - if (path_url.is_valid() && path_url.has_host() && path_url.has_scheme() && - NonBlobURLGetOriginStr(path_url, &origin_str_)) { - return; - } - } else if (NonBlobURLGetOriginStr(url, &origin_str_)) { - // Assign a tuple origin if given url is allowed to have one. - return; - } - } - // Othwise, return a new opaque origin. - is_opaque_ = true; -} - -std::string URLUtils::Origin::SerializedOrigin() const { - if (is_opaque_) { - return "null"; - } - return origin_str_; -} - -bool URLUtils::Origin::operator==(const Origin& rhs) const { - if (is_opaque_ || rhs.is_opaque_) { - return false; - } else { - return origin_str_ == rhs.origin_str_; - } -} - URLUtils::URLUtils(const GURL& url, bool is_opaque) - : url_(url), origin_(is_opaque ? Origin() : Origin(url)) {} + : url_(url), origin_(is_opaque ? loader::Origin() : loader::Origin(url)) {} URLUtils::URLUtils(const GURL& url, const UpdateStepsCallback& update_steps, bool is_opaque) : url_(url), update_steps_(update_steps), - origin_(is_opaque ? Origin() : Origin(url)) {} + origin_(is_opaque ? loader::Origin() : loader::Origin(url)) {} std::string URLUtils::href() const { return url_.possibly_invalid_spec(); }
diff --git a/src/cobalt/dom/url_utils.h b/src/cobalt/dom/url_utils.h index aa66825..9d163f9 100644 --- a/src/cobalt/dom/url_utils.h +++ b/src/cobalt/dom/url_utils.h
@@ -18,6 +18,7 @@ #include <string> #include "base/callback.h" +#include "cobalt/loader/origin.h" #include "googleurl/src/gurl.h" namespace cobalt { @@ -35,34 +36,11 @@ // callback. The callback should call set_url if necessary. class URLUtils { public: - // https://html.spec.whatwg.org/multipage/origin.html#concept-origin - class Origin { - public: - // To create an opaque origin, use Origin(). - Origin(); - // Initialize an origin to the url's origin. - // https://url.spec.whatwg.org/#concept-url-origin - explicit Origin(const GURL& url); - // https://html.spec.whatwg.org/multipage/origin.html#ascii-serialisation-of-an-origin - std::string SerializedOrigin() const; - bool is_opaque() const { return is_opaque_; } - // Only document has an origin and no elements inherit document's origin, so - // opaque origin comparison can always return false. - // https://html.spec.whatwg.org/multipage/origin.html#same-origin - bool operator==(const Origin& rhs) const; - // Returns true if two origins are different(cross-origin). - bool operator!=(const Origin& rhs) const { return !(*this == rhs); } - - private: - bool is_opaque_; - std::string origin_str_; - }; - typedef base::Callback<void(const std::string&)> UpdateStepsCallback; explicit URLUtils(const GURL& url, bool is_opaque = false); explicit URLUtils(const UpdateStepsCallback& update_steps) - : update_steps_(update_steps), origin_(Origin()) {} + : update_steps_(update_steps) {} URLUtils(const GURL& url, const UpdateStepsCallback& update_steps, bool is_opaque = false); @@ -99,7 +77,7 @@ const GURL& url() const { return url_; } void set_url(const GURL& url) { url_ = url; } - const Origin& OriginObject() const { return origin_; } + const loader::Origin& OriginObject() const { return origin_; } private: // From the spec: URLUtils. @@ -107,7 +85,7 @@ GURL url_; UpdateStepsCallback update_steps_; - Origin origin_; + loader::Origin origin_; }; } // namespace dom
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc index 17149cf..e5a0eda 100644 --- a/src/cobalt/dom/window.cc +++ b/src/cobalt/dom/window.cc
@@ -158,6 +158,7 @@ ALLOW_THIS_IN_INITIALIZER_LIST( session_storage_(new Storage(this, Storage::kSessionStorage, NULL))), screen_(new Screen(width, height)), + preflight_cache_(new loader::CORSPreflightCache()), ran_animation_frame_callbacks_callback_( ran_animation_frame_callbacks_callback), window_close_callback_(window_close_callback),
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h index 8da163c..b1fa459 100644 --- a/src/cobalt/dom/window.h +++ b/src/cobalt/dom/window.h
@@ -40,6 +40,7 @@ #include "cobalt/dom/url_registry.h" #include "cobalt/dom/window_timers.h" #include "cobalt/input/camera_3d.h" +#include "cobalt/loader/cors_preflight_cache.h" #include "cobalt/loader/decoder.h" #include "cobalt/loader/fetcher_factory.h" #include "cobalt/loader/font/remote_typeface_cache.h" @@ -340,6 +341,10 @@ // Cache the passed in splash screen content for the window.location URL. void CacheSplashScreen(const std::string& content); + const scoped_refptr<loader::CORSPreflightCache> get_preflight_cache() { + return preflight_cache_; + } + DEFINE_WRAPPABLE_TYPE(Window); private: @@ -395,6 +400,9 @@ scoped_refptr<Screen> screen_; + // Global preflight cache. + scoped_refptr<loader::CORSPreflightCache> preflight_cache_; + const base::Closure ran_animation_frame_callbacks_callback_; const CloseCallback window_close_callback_; const base::Closure window_minimize_callback_;
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt index f07c751..ad81d43 100644 --- a/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt +++ b/src/cobalt/layout_tests/testdata/web-platform-tests/XMLHttpRequest/web_platform_tests.txt
@@ -128,13 +128,13 @@ send-after-setting-document-domain.htm,FAIL send-authentication-basic-cors.htm,PASS # Synchronous -send-authentication-basic-cors-not-enabled.htm,FAIL +send-authentication-basic-cors-not-enabled.htm,DISABLE send-authentication-basic.htm,FAIL send-authentication-basic-repeat-no-args.htm,FAIL send-authentication-basic-setrequestheader-existing-session.htm,FAIL send-authentication-basic-setrequestheader.htm,FAIL send-authentication-competing-names-passwords.htm,FAIL -send-authentication-cors-basic-setrequestheader.htm,FAIL +send-authentication-cors-basic-setrequestheader.htm,DISABLE # js_error: send-authentication-existing-session-manual.htm,FAIL # Synchronous send-authentication-prompt-2-manual.htm,FAIL @@ -170,7 +170,7 @@ send-redirect-infinite.htm,PASS send-redirect-infinite-sync.htm,FAIL send-redirect-no-location.htm,PASS -send-redirect-to-cors.htm,FAIL +send-redirect-to-cors.htm,DISABLE send-redirect-to-non-cors.htm,PASS send-response-event-order.htm,FAIL send-response-upload-event-loadend.htm,PASS @@ -183,7 +183,7 @@ send-sync-no-response-event-order.htm,FAIL send-sync-response-event-order.htm,FAIL send-sync-timeout.htm,FAIL -send-timeout-events.htm,PASS +send-timeout-events.htm,DISABLE # Infinite loop? send-usp.html,DISABLE setrequestheader-after-send.htm,PASS
diff --git a/src/cobalt/layout_tests/web_platform_test_parser.cc b/src/cobalt/layout_tests/web_platform_test_parser.cc index 04f894e..83dc8bb 100644 --- a/src/cobalt/layout_tests/web_platform_test_parser.cc +++ b/src/cobalt/layout_tests/web_platform_test_parser.cc
@@ -108,9 +108,9 @@ std::string result; bool success = global_environment->EvaluateScript( - script::SourceCode::CreateSourceCode(precondition, - base::SourceLocation(__FILE__, __LINE__, 1)), - &result); + script::SourceCode::CreateSourceCode( + precondition, base::SourceLocation(__FILE__, __LINE__, 1)), + &result, false /*mute_errors*/); if (!success) { DLOG(ERROR) << "Failed to evaluate precondition: "
diff --git a/src/cobalt/layout_tests/web_platform_tests.cc b/src/cobalt/layout_tests/web_platform_tests.cc index ddbb94d..2b0a60a 100644 --- a/src/cobalt/layout_tests/web_platform_tests.cc +++ b/src/cobalt/layout_tests/web_platform_tests.cc
@@ -335,6 +335,11 @@ streams, WebPlatformTest, ::testing::ValuesIn(EnumerateWebPlatformTests("streams", "'ReadableStream' in this"))); + +INSTANTIATE_TEST_CASE_P( + cobalt_special, WebPlatformTest, + ::testing::ValuesIn(EnumerateWebPlatformTests("cobalt_special"))); + #endif // !defined(COBALT_WIN) } // namespace layout_tests
diff --git a/src/cobalt/loader/cors_preflight.cc b/src/cobalt/loader/cors_preflight.cc index 81065e6..fe20e54 100644 --- a/src/cobalt/loader/cors_preflight.cc +++ b/src/cobalt/loader/cors_preflight.cc
@@ -39,6 +39,7 @@ const char* kAccessControlAllowMethod = "Access-Control-Allow-Methods"; const char* kAccessControlAllowHeaders = "Access-Control-Allow-Headers"; const char* kAccessControlAllowCredentials = "Access-Control-Allow-Credentials"; +const char* kAccessControlMaxAge = "Access-Control-Max-Age"; // https://fetch.spec.whatwg.org/#http-access-control-expose-headers const char* kAccessControlExposeHeaders = "Access-Control-Expose-Headers"; @@ -96,6 +97,10 @@ return ""; } } +// This constant is an imposed limit on the time an entry can be alive in +// the preflight cache if the provided max-age value is even greater. +// The number is the same as the limit in WebKit. +const int kPreflightCacheMaxAgeLimit = 600; // This helper function checks if 'input_str' is in 'array' up to 'size'. bool IsInArray(const char* input_str, const char* array[], size_t size) { @@ -130,7 +135,8 @@ CORSPreflight::CORSPreflight(GURL url, net::URLFetcher::RequestType method, const network::NetworkModule* network_module, base::Closure success_callback, std::string origin, - base::Closure error_callback) + base::Closure error_callback, + scoped_refptr<CORSPreflightCache> preflight_cache) : credentials_mode_is_include_(false), force_preflight_(false), url_(url), @@ -138,8 +144,10 @@ network_module_(network_module), origin_(origin), error_callback_(error_callback), - success_callback_(success_callback) { + success_callback_(success_callback), + preflight_cache_(preflight_cache) { DCHECK(!url_.is_empty()); + DCHECK(preflight_cache); } // https://fetch.spec.whatwg.org/#cors-safelisted-request-header @@ -180,7 +188,7 @@ // https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name bool CORSPreflight::IsSafeResponseHeader( const std::string& name, - std::vector<std::string>& CORS_exposed_header_name_list, + const std::vector<std::string>& CORS_exposed_header_name_list, bool credentials_mode_is_include) { // Every check in this function is case-insensitive comparison. // Header is safe if it's CORS-safelisted repsonse-header name. @@ -229,20 +237,23 @@ } // Preflight is not needed if the request method is CORS-safelisted request // method and all headers are CORS-safelisted request-header. + std::vector<std::string> unsafe_headers; if (method_ == net::URLFetcher::GET || method_ == net::URLFetcher::HEAD || method_ == net::URLFetcher::POST) { net::HttpRequestHeaders::Iterator it(headers_); while (it.GetNext()) { - if (IsSafeRequestHeader(it.name(), it.value())) { - continue; - } else { - return true; + if (!IsSafeRequestHeader(it.name(), it.value())) { + unsafe_headers.push_back(it.name()); } } - } else { - return true; + if (unsafe_headers.empty()) { + return false; + } } - return false; + // Check preflight cache for match. + return !preflight_cache_->HaveEntry(url_.spec(), origin_, + credentials_mode_is_include_, method_, + unsafe_headers); } bool CORSPreflight::Send() { @@ -365,11 +376,22 @@ return; } } + // step 10-18 for adding entry to preflight cache. + std::string max_age_str; + int max_age = 0; + if (response_headers->GetNormalizedHeader(kAccessControlMaxAge, + &max_age_str)) { + max_age = std::min(SbStringAToI(max_age_str.c_str()), + kPreflightCacheMaxAgeLimit); + } + preflight_cache_->AppendEntry(source->GetURL().spec(), origin_, max_age, + credentials_mode_is_include_, methods_vec, + headernames_vec); } else { DLOG(ERROR) << "CORS preflight did not get response headers"; error_callback_.Run(); } - // step 10-18 are omitted as we haven't implemented preflight cache. + success_callback_.Run(); }
diff --git a/src/cobalt/loader/cors_preflight.h b/src/cobalt/loader/cors_preflight.h index 7899575..f0012af 100644 --- a/src/cobalt/loader/cors_preflight.h +++ b/src/cobalt/loader/cors_preflight.h
@@ -17,7 +17,6 @@ #ifndef COBALT_LOADER_CORS_PREFLIGHT_H_ #define COBALT_LOADER_CORS_PREFLIGHT_H_ -#include <map> #include <string> #include <vector> @@ -27,6 +26,7 @@ #include "base/memory/weak_ptr.h" #include "base/message_loop_proxy.h" #include "base/threading/thread_checker.h" +#include "cobalt/loader/cors_preflight_cache.h" #include "cobalt/network/network_module.h" #include "googleurl/src/gurl.h" #include "net/http/http_request_headers.h" @@ -45,7 +45,8 @@ CORSPreflight(GURL url, net::URLFetcher::RequestType method, const network::NetworkModule* network_module, base::Closure success_callback, std::string origin, - base::Closure error_callback); + base::Closure error_callback, + scoped_refptr<CORSPreflightCache> preflight_cache); void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; void set_force_preflight(bool forcepreflight) { force_preflight_ = forcepreflight; @@ -74,7 +75,7 @@ // Call GetHeadersVector before to put server-allowed headers in vector. static bool IsSafeResponseHeader( const std::string& name, - std::vector<std::string>& CORS_exposed_header_name_list, + const std::vector<std::string>& CORS_exposed_header_name_list, bool credentials_mode_is_include); // This function populates input vector with headers extracted from // Access-Control-Expose-Headers header. @@ -96,6 +97,7 @@ std::string origin_; base::Closure error_callback_; base::Closure success_callback_; + scoped_refptr<CORSPreflightCache> preflight_cache_; }; } // namespace loader
diff --git a/src/cobalt/loader/cors_preflight_cache.cc b/src/cobalt/loader/cors_preflight_cache.cc new file mode 100644 index 0000000..860ba27 --- /dev/null +++ b/src/cobalt/loader/cors_preflight_cache.cc
@@ -0,0 +1,167 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <algorithm> + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "cobalt/loader/cors_preflight_cache.h" + +namespace { +bool MethodNameToRequestType(const std::string& method, + net::URLFetcher::RequestType* request_type) { + if (LowerCaseEqualsASCII(method, "get")) { + *request_type = net::URLFetcher::GET; + } else if (LowerCaseEqualsASCII(method, "post")) { + *request_type = net::URLFetcher::POST; + } else if (LowerCaseEqualsASCII(method, "head")) { + *request_type = net::URLFetcher::HEAD; + } else if (LowerCaseEqualsASCII(method, "delete")) { + *request_type = net::URLFetcher::DELETE_REQUEST; + } else if (LowerCaseEqualsASCII(method, "put")) { + *request_type = net::URLFetcher::PUT; + } else { + return false; + } + return true; +} +const char* kAuthorization = "authorization"; +} // namespace + +namespace cobalt { +namespace loader { + +void CORSPreflightCache::AppendEntry( + const std::string& url_str, const std::string& origin, int max_age, + bool has_credentials, const std::vector<std::string>& methods_vec, + const std::vector<std::string>& headernames_vec) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (max_age <= 0) { + return; + } + base::TimeDelta valid_duration = base::TimeDelta::FromSeconds(max_age); + scoped_refptr<CORSPreflightCacheEntry> new_entry = + new CORSPreflightCacheEntry(); + new_entry->credentials = has_credentials; + new_entry->expiration_time = base::Time::Now() + valid_duration; + + if (methods_vec.size() == 1 && methods_vec.at(0) == "*") { + new_entry->allow_all_methods = true; + } else { + for (auto method : methods_vec) { + net::URLFetcher::RequestType request_type; + if (MethodNameToRequestType(method, &request_type)) { + new_entry->methods.insert(request_type); + } + } + } + + if (headernames_vec.size() == 1 && headernames_vec.at(0) == "*") { + new_entry->allow_all_headers_except_non_wildcard = true; + } + // TODO: Consider change this function to use std::copy with std::inserter. + // Currently compilers on some machines do not support it. + for (auto headername : headernames_vec) { + new_entry->headernames.insert(headername); + } + + auto insert_result = + content_[url_str].insert(std::make_pair(origin, new_entry)); + if (!insert_result.second) { + insert_result.first->second = new_entry; + } + + expiration_time_heap_.push( + ExpirationHeapEntry{new_entry->expiration_time, url_str, origin}); +} + +// https://fetch.spec.whatwg.org/#concept-cache-match +bool CORSPreflightCache::HaveEntry( + const std::string& url_str, const std::string& origin, + bool credentials_mode_is_include, + const net::URLFetcher::RequestType& new_request_method, + const std::vector<std::string>& unsafe_headernames) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (url_str.empty() || origin.empty()) { + return false; + } + ClearObsoleteEntries(); + // There is a cache match for request if origin is request's origin, + // url is request's current url, either credentials is true or request's + // credentials_mode is not "include" + auto url_iter = content_.find(url_str); + if (url_iter == content_.end()) { + return false; + } + auto origin_iter = url_iter->second.find(origin); + if (origin_iter == url_iter->second.end()) { + return false; + } + auto entry_ptr = origin_iter->second; + if (!entry_ptr->credentials && credentials_mode_is_include) { + return false; + } + // Either last preflight's Access-Control-Allow-Methods header has * or + // new request's method, otherwise return false. + if (!entry_ptr->allow_all_methods && + entry_ptr->methods.find(new_request_method) == entry_ptr->methods.end()) { + return false; + } + // Header name is safe if it's not CORS non-wildcard request-header + // name("Authentication") and last preflight allowed * headers. + if (entry_ptr->allow_all_headers_except_non_wildcard) { + bool has_auth_header = false; + for (auto header : unsafe_headernames) { + if (SbStringCompareNoCase(header.c_str(), kAuthorization)) { + has_auth_header = true; + break; + } + } + // wildcard header is allowed if entry's allowed headers include it. + return !has_auth_header || + (entry_ptr->headernames.find(std::string(kAuthorization)) != + entry_ptr->headernames.end()); + } + // If last preflight does not allow arbitrary header, then match each header + // with allowed headers. + for (auto unsafe_headername : unsafe_headernames) { + if (entry_ptr->headernames.find(unsafe_headername) == + entry_ptr->headernames.end()) { + return false; + } + } + return true; +} + +void CORSPreflightCache::ClearObsoleteEntries() { + while (expiration_time_heap_.size() > 0 && + expiration_time_heap_.top().expiration_time < base::Time::Now()) { + DCHECK(content_.find(expiration_time_heap_.top().url_str) != + content_.end()); + auto url_iter = content_.find(expiration_time_heap_.top().url_str); + DCHECK(url_iter->second.find(expiration_time_heap_.top().origin) != + url_iter->second.end()); + auto entry_iter = url_iter->second.find(expiration_time_heap_.top().origin); + // The entry could have been updated and should only delete obselete ones. + if (entry_iter->second->expiration_time < base::Time::Now()) { + url_iter->second.erase(entry_iter); + } + expiration_time_heap_.pop(); + } +} + +} // namespace loader +} // namespace cobalt
diff --git a/src/cobalt/loader/cors_preflight_cache.h b/src/cobalt/loader/cors_preflight_cache.h new file mode 100644 index 0000000..d14a393 --- /dev/null +++ b/src/cobalt/loader/cors_preflight_cache.h
@@ -0,0 +1,122 @@ +/* + * Copyright 2017 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COBALT_LOADER_CORS_PREFLIGHT_CACHE_H_ +#define COBALT_LOADER_CORS_PREFLIGHT_CACHE_H_ + +#include <queue> +#include <set> +#include <string> +#include <unordered_map> +#include <vector> + +#include "base/hash_tables.h" +#include "base/time.h" +#include "googleurl/src/gurl.h" +#include "net/http/http_request_headers.h" +#include "net/url_request/url_fetcher.h" +#include "starboard/string.h" + +namespace cobalt { +namespace loader { + +// https://fetch.spec.whatwg.org/#concept-cache +class CORSPreflightCache : public base::RefCounted<CORSPreflightCache> { + public: + // An entry is appended at the end of every preflight to avoid same + // preflight request in the near future. + void AppendEntry(const std::string& url_str, const std::string& origin, + int max_age, bool has_credentials, + const std::vector<std::string>& methods_vec, + const std::vector<std::string>& headernames_vec); + // Check if there is a preflight cache match. This method does not include + // checking for CORS-safelisted method and request-header. + bool HaveEntry(const std::string& url_str, const std::string& origin, + bool credentials_mode_is_include, + const net::URLFetcher::RequestType& new_request_method, + const std::vector<std::string>& unsafe_headernames); + + private: + // Case-insensitive comparator. + struct CaseInsensitiveCompare { + bool operator()(const std::string& lhs, const std::string& rhs) const { + return SbStringCompareNoCase(lhs.c_str(), rhs.c_str()) != 0; + } + }; + + // The spec wants a cache entry for every method and for every header which is + // a little unnesessarily expensive. We create an entry for each request and + // If there is an old entry in the new entry's pleace we simply push the old + // one out which potentially increases cache misses slightly but reduces + // memory cost. Chromium also takes this approach. + // The map's first key is entry's request url and second is entry's origin. + struct CORSPreflightCacheEntry + : public base::RefCounted<CORSPreflightCacheEntry> { + bool credentials; + // True if response has "Access-Control-Allow-Methods: *". + bool allow_all_methods; + // True if response has "Access-Control-Allow-Headers: *". + // Non-wildcard request-header name is "Authentication". + bool allow_all_headers_except_non_wildcard; + base::Time expiration_time; + std::set<net::URLFetcher::RequestType> methods; + std::set<std::string, CaseInsensitiveCompare> headernames; + + CORSPreflightCacheEntry() + : credentials(false), + allow_all_methods(false), + allow_all_headers_except_non_wildcard(false) {} + }; + + struct ExpirationHeapEntry { + base::Time expiration_time; + std::string url_str; + std::string origin; + bool operator>(const ExpirationHeapEntry& rhs) const { + return expiration_time > rhs.expiration_time; + } + bool operator<(const ExpirationHeapEntry& rhs) const { + return expiration_time < rhs.expiration_time; + } + }; + + // This operator constructs a min-heap. + class ExpirationMinHeapComparator { + public: + bool operator()(const ExpirationHeapEntry& lhs, + const ExpirationHeapEntry& rhs) { + return lhs > rhs; + } + }; + + void ClearObsoleteEntries(); + + // TODO: Replace scoped_refptr with scoped_ptr when possible or replace the + // map as a 'scoped_map'. + base::hash_map< + std::string, + base::hash_map<std::string, scoped_refptr<CORSPreflightCacheEntry> > > + content_; + + std::priority_queue<ExpirationHeapEntry, std::vector<ExpirationHeapEntry>, + ExpirationMinHeapComparator> + expiration_time_heap_; + base::ThreadChecker thread_checker_; +}; + +} // namespace loader +} // namespace cobalt +#endif // COBALT_LOADER_CORS_PREFLIGHT_CACHE_H_
diff --git a/src/cobalt/loader/decoder.h b/src/cobalt/loader/decoder.h index 43d3217..1f51912 100644 --- a/src/cobalt/loader/decoder.h +++ b/src/cobalt/loader/decoder.h
@@ -18,8 +18,10 @@ #include <string> #include "base/memory/scoped_ptr.h" +#include "cobalt/dom/url_utils.h" #include "cobalt/loader/loader_types.h" #include "cobalt/render_tree/resource_provider.h" +#include "googleurl/src/gurl.h" #include "net/http/http_response_headers.h" namespace cobalt { @@ -64,6 +66,10 @@ // Resumes the decode of this resource, starting over from the zero state. virtual void Resume(render_tree::ResourceProvider* resource_provider) = 0; + + // Provides textdecoder with last url to prevent security leak if resource is + // cross-origin. + virtual void SetLastURLOrigin(const loader::Origin&) {} }; } // namespace loader
diff --git a/src/cobalt/loader/fetcher.h b/src/cobalt/loader/fetcher.h index 3e00461..6ce28cf 100644 --- a/src/cobalt/loader/fetcher.h +++ b/src/cobalt/loader/fetcher.h
@@ -18,6 +18,7 @@ #include <string> #include "base/callback.h" +#include "cobalt/dom/url_utils.h" #include "cobalt/loader/loader_types.h" #include "googleurl/src/gurl.h" #include "net/http/http_response_headers.h" @@ -63,12 +64,17 @@ // Concrete Fetcher subclass should start fetching immediately in constructor. explicit Fetcher(Handler* handler) : handler_(handler) {} + const loader::Origin& last_url_origin() { return last_url_origin_; } + // Concrete Fetcher subclass should cancel fetching in destructor. virtual ~Fetcher() = 0; protected: Handler* handler() const { return handler_; } + // used by html elements to check if resource is cross-origin. + loader::Origin last_url_origin_; + private: Handler* handler_; };
diff --git a/src/cobalt/loader/loader.cc b/src/cobalt/loader/loader.cc index d5e453d..58c1ca9 100644 --- a/src/cobalt/loader/loader.cc +++ b/src/cobalt/loader/loader.cc
@@ -13,6 +13,8 @@ // limitations under the License. #include "cobalt/loader/loader.h" +#include "cobalt/base/polymorphic_downcast.h" +#include "cobalt/loader/text_decoder.h" #include "base/bind.h" #include "base/compiler_specific.h" @@ -56,6 +58,8 @@ } void OnDone(Fetcher* fetcher) OVERRIDE { UNREFERENCED_PARAMETER(fetcher); + DCHECK(fetcher); + decoder_->SetLastURLOrigin(fetcher->last_url_origin()); decoder_->Finish(); } void OnError(Fetcher* fetcher, const std::string& error) OVERRIDE {
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp index 1ab0435..a4a6542 100644 --- a/src/cobalt/loader/loader.gyp +++ b/src/cobalt/loader/loader.gyp
@@ -27,6 +27,8 @@ 'cache_fetcher.h', 'cors_preflight.cc', 'cors_preflight.h', + 'cors_preflight_cache.cc', + 'cors_preflight_cache.h', 'decoder.h', 'embedded_fetcher.cc', 'embedded_fetcher.h', @@ -80,6 +82,8 @@ 'mesh/projection_codec/projection_decoder.h', 'net_fetcher.cc', 'net_fetcher.h', + 'origin.cc', + 'origin.h', 'resource_cache.h', 'sync_loader.cc', 'sync_loader.h',
diff --git a/src/cobalt/loader/loader_test.cc b/src/cobalt/loader/loader_test.cc index 1e7872a..0c8d736 100644 --- a/src/cobalt/loader/loader_test.cc +++ b/src/cobalt/loader/loader_test.cc
@@ -19,6 +19,7 @@ #include "base/message_loop.h" #include "base/path_service.h" #include "base/run_loop.h" +#include "cobalt/dom/url_utils.h" #include "cobalt/loader/file_fetcher.h" #include "cobalt/loader/text_decoder.h" #include "testing/gmock/include/gmock/gmock.h" @@ -40,7 +41,7 @@ public: explicit TextDecoderCallback(base::RunLoop* run_loop) : run_loop_(run_loop) {} - void OnDone(const std::string& text) { + void OnDone(const std::string& text, const loader::Origin&) { text_ = text; MessageLoop::current()->PostTask(FROM_HERE, run_loop_->QuitClosure()); }
diff --git a/src/cobalt/loader/net_fetcher.cc b/src/cobalt/loader/net_fetcher.cc index 08fd0a3..5c904ed 100644 --- a/src/cobalt/loader/net_fetcher.cc +++ b/src/cobalt/loader/net_fetcher.cc
@@ -124,6 +124,7 @@ return HandleError(msg).InvalidateThis(); } } + last_url_origin_ = Origin(source->GetURL()); } void NetFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
diff --git a/src/cobalt/loader/origin.cc b/src/cobalt/loader/origin.cc new file mode 100644 index 0000000..b1aca32 --- /dev/null +++ b/src/cobalt/loader/origin.cc
@@ -0,0 +1,88 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cobalt/loader/origin.h" + +namespace { +// Returns true if the url can have non-opaque origin. +// Blob URLs needs to be pre-processed. +bool NonBlobURLCanHaveTupleOrigin(const GURL& url) { + if (url.SchemeIs("https")) { + return true; + } else if (url.SchemeIs("http")) { + return true; + } else if (url.SchemeIs("ftp")) { + return true; + } else if (url.SchemeIs("ws")) { + return true; + } else if (url.SchemeIs("wss")) { + return true; + } + return false; +} +// Returns false if url should be opaque. +// Otherwise, extract origin tuple from the url and assemble them as a string +// for easier access and comparison. +bool NonBlobURLGetOriginStr(const GURL& url, std::string* output) { + if (!NonBlobURLCanHaveTupleOrigin(url)) { + return false; + } + *output = url.scheme() + "://" + url.host() + + (url.has_port() ? ":" + url.port() : ""); + return true; +} +} // namespace + +namespace cobalt { +namespace loader { + +Origin::Origin() : is_opaque_(true) {} + +Origin::Origin(const GURL& url) : is_opaque_(false) { + if (url.is_valid() && url.has_scheme() && url.has_host()) { + if (url.SchemeIs("blob")) { + // Let path_url be the result of parsing URL's path. + // Return a new opaque origin, if url is failure, and url's origin + // otherwise. + GURL path_url(url.path()); + if (path_url.is_valid() && path_url.has_host() && path_url.has_scheme() && + NonBlobURLGetOriginStr(path_url, &origin_str_)) { + return; + } + } else if (NonBlobURLGetOriginStr(url, &origin_str_)) { + // Assign a tuple origin if given url is allowed to have one. + return; + } + } + // Othwise, return a new opaque origin. + is_opaque_ = true; +} + +std::string Origin::SerializedOrigin() const { + if (is_opaque_) { + return "null"; + } + return origin_str_; +} + +bool Origin::operator==(const Origin& rhs) const { + if (is_opaque_ || rhs.is_opaque_) { + return false; + } else { + return origin_str_ == rhs.origin_str_; + } +} + +} // namespace loader +} // namespace cobalt
diff --git a/src/cobalt/loader/origin.h b/src/cobalt/loader/origin.h new file mode 100644 index 0000000..9c11e94 --- /dev/null +++ b/src/cobalt/loader/origin.h
@@ -0,0 +1,49 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COBALT_LOADER_ORIGIN_H_ +#define COBALT_LOADER_ORIGIN_H_ + +#include <string> + +#include "googleurl/src/gurl.h" + +namespace cobalt { +namespace loader { +// https://html.spec.whatwg.org/multipage/origin.html#concept-origin +class Origin { + public: + // To create an opaque origin, use Origin(). + Origin(); + // Initialize an origin to the url's origin. + // https://url.spec.whatwg.org/#concept-url-origin + explicit Origin(const GURL& url); + // https://html.spec.whatwg.org/multipage/origin.html#ascii-serialisation-of-an-origin + std::string SerializedOrigin() const; + bool is_opaque() const { return is_opaque_; } + // Only document has an origin and no elements inherit document's origin, so + // opaque origin comparison can always return false. + // https://html.spec.whatwg.org/multipage/origin.html#same-origin + bool operator==(const Origin& rhs) const; + // Returns true if two origins are different(cross-origin). + bool operator!=(const Origin& rhs) const { return !(*this == rhs); } + + private: + bool is_opaque_; + std::string origin_str_; +}; +} // namespace loader +} // namespace cobalt + +#endif // COBALT_LOADER_ORIGIN_H_
diff --git a/src/cobalt/loader/text_decoder.h b/src/cobalt/loader/text_decoder.h index 11b78d4..702a2bf 100644 --- a/src/cobalt/loader/text_decoder.h +++ b/src/cobalt/loader/text_decoder.h
@@ -23,6 +23,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/threading/thread_checker.h" +#include "cobalt/dom/url_utils.h" #include "cobalt/loader/decoder.h" namespace cobalt { @@ -32,13 +33,16 @@ // results. class TextDecoder : public Decoder { public: - explicit TextDecoder(base::Callback<void(const std::string&)> done_callback) + explicit TextDecoder( + base::Callback<void(const std::string&, const loader::Origin&)> + done_callback) : done_callback_(done_callback), suspended_(false) {} ~TextDecoder() OVERRIDE {} // This function is used for binding callback for creating TextDecoder. static scoped_ptr<Decoder> Create( - base::Callback<void(const std::string&)> done_callback) { + base::Callback<void(const std::string&, const loader::Origin&)> + done_callback) { return scoped_ptr<Decoder>(new TextDecoder(done_callback)); } @@ -70,7 +74,7 @@ if (suspended_) { return; } - done_callback_.Run(text_); + done_callback_.Run(text_, last_url_origin_); } bool Suspend() OVERRIDE { suspended_ = true; @@ -80,12 +84,17 @@ void Resume(render_tree::ResourceProvider* /*resource_provider*/) OVERRIDE { suspended_ = false; } + void SetLastURLOrigin(const loader::Origin& last_url_origin) OVERRIDE { + last_url_origin_ = last_url_origin; + } private: base::ThreadChecker thread_checker_; std::string text_; - base::Callback<void(const std::string&)> done_callback_; + base::Callback<void(const std::string&, const loader::Origin&)> + done_callback_; bool suspended_; + loader::Origin last_url_origin_; }; } // namespace loader
diff --git a/src/cobalt/loader/text_decoder_test.cc b/src/cobalt/loader/text_decoder_test.cc index 7c62696..90e8824 100644 --- a/src/cobalt/loader/text_decoder_test.cc +++ b/src/cobalt/loader/text_decoder_test.cc
@@ -23,8 +23,13 @@ namespace { struct TextDecoderCallback { - void Callback(const std::string& value) { text = value; } + void Callback(const std::string& value, + const loader::Origin& last_url_origin) { + text = value; + last_url_origin_ = last_url_origin; + } std::string text; + loader::Origin last_url_origin_; }; } // namespace
diff --git a/src/cobalt/media/filters/audio_clock.cc b/src/cobalt/media/filters/audio_clock.cc deleted file mode 100644 index 50503cd..0000000 --- a/src/cobalt/media/filters/audio_clock.cc +++ /dev/null
@@ -1,186 +0,0 @@ -// 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/filters/audio_clock.h" - -#include <algorithm> -#include <cmath> - -#include "base/logging.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -AudioClock::AudioClock(base::TimeDelta start_timestamp, int sample_rate) - : start_timestamp_(start_timestamp), - microseconds_per_frame_( - static_cast<double>(base::Time::kMicrosecondsPerSecond) / - sample_rate), - total_buffered_frames_(0), - front_timestamp_micros_(start_timestamp.InMicroseconds()), - back_timestamp_micros_(start_timestamp.InMicroseconds()) {} - -AudioClock::~AudioClock() {} - -void AudioClock::WroteAudio(int frames_written, int frames_requested, - int delay_frames, double playback_rate) { - DCHECK_GE(frames_written, 0); - DCHECK_LE(frames_written, frames_requested); - DCHECK_GE(delay_frames, 0); - DCHECK_GE(playback_rate, 0); - - // First write: initialize buffer with silence. - if (start_timestamp_.InMicroseconds() == front_timestamp_micros_ && - buffered_.empty()) { - PushBufferedAudioData(delay_frames, 0.0); - } - - // Move frames from |buffered_| into the computed timestamp based on - // |delay_frames|. - // - // The ordering of compute -> push -> pop eliminates unnecessary memory - // reallocations in cases where |buffered_| gets emptied. - int64_t frames_played = - std::max(INT64_C(0), total_buffered_frames_ - delay_frames); - PushBufferedAudioData(frames_written, playback_rate); - PushBufferedAudioData(frames_requested - frames_written, 0.0); - PopBufferedAudioData(frames_played); - - // Update our front and back timestamps. The back timestamp is considered the - // authoritative source of truth, so base the front timestamp on range of data - // buffered. Doing so avoids accumulation errors on the front timestamp. - back_timestamp_micros_ += - frames_written * playback_rate * microseconds_per_frame_; - - // Don't let front timestamp move earlier in time, as could occur due to delay - // frames pushed in the first write, above. - front_timestamp_micros_ = - std::max(front_timestamp_micros_, - back_timestamp_micros_ - ComputeBufferedMediaDurationMicros()); - DCHECK_GE(front_timestamp_micros_, start_timestamp_.InMicroseconds()); - DCHECK_LE(front_timestamp_micros_, back_timestamp_micros_); -} - -void AudioClock::CompensateForSuspendedWrites(base::TimeDelta elapsed, - int delay_frames) { - const int64_t frames_elapsed = - elapsed.InMicroseconds() / microseconds_per_frame_ + 0.5; - - // No need to do anything if we're within the limits of our played out audio - // or there are no delay frames, the next WroteAudio() call will expire - // everything correctly. - if (frames_elapsed < total_buffered_frames_ || !delay_frames) return; - - // Otherwise, flush everything and prime with the delay frames. - WroteAudio(0, 0, 0, 0); - DCHECK(buffered_.empty()); - PushBufferedAudioData(delay_frames, 0.0); -} - -base::TimeDelta AudioClock::TimeUntilPlayback(base::TimeDelta timestamp) const { - // Use front/back_timestamp() methods rather than internal members. The public - // methods round to the nearest microsecond for conversion to TimeDelta and - // the rounded value will likely be used by the caller. - DCHECK_GE(timestamp, front_timestamp()); - DCHECK_LE(timestamp, back_timestamp()); - - int64_t frames_until_timestamp = 0; - double timestamp_us = timestamp.InMicroseconds(); - double media_time_us = front_timestamp().InMicroseconds(); - - for (size_t i = 0; i < buffered_.size(); ++i) { - // Leading silence is always accounted prior to anything else. - if (buffered_[i].playback_rate == 0) { - frames_until_timestamp += buffered_[i].frames; - continue; - } - - // Calculate upper bound on media time for current block of buffered frames. - double delta_us = buffered_[i].frames * buffered_[i].playback_rate * - microseconds_per_frame_; - double max_media_time_us = media_time_us + delta_us; - - // Determine amount of media time to convert to frames for current block. If - // target timestamp falls within current block, scale the amount of frames - // based on remaining amount of media time. - if (timestamp_us <= max_media_time_us) { - frames_until_timestamp += - buffered_[i].frames * (timestamp_us - media_time_us) / delta_us; - break; - } - - media_time_us = max_media_time_us; - frames_until_timestamp += buffered_[i].frames; - } - - return base::TimeDelta::FromMicroseconds( - std::round(frames_until_timestamp * microseconds_per_frame_)); -} - -void AudioClock::ContiguousAudioDataBufferedForTesting( - base::TimeDelta* total, base::TimeDelta* same_rate_total) const { - double scaled_frames = 0; - double scaled_frames_at_same_rate = 0; - bool found_silence = false; - for (size_t i = 0; i < buffered_.size(); ++i) { - if (buffered_[i].playback_rate == 0) { - found_silence = true; - continue; - } - - // Any buffered silence breaks our contiguous stretch of audio data. - if (found_silence) break; - - scaled_frames += (buffered_[i].frames * buffered_[i].playback_rate); - - if (i == 0) scaled_frames_at_same_rate = scaled_frames; - } - - *total = base::TimeDelta::FromMicroseconds(scaled_frames * - microseconds_per_frame_); - *same_rate_total = base::TimeDelta::FromMicroseconds( - scaled_frames_at_same_rate * microseconds_per_frame_); -} - -AudioClock::AudioData::AudioData(int64_t frames, double playback_rate) - : frames(frames), playback_rate(playback_rate) {} - -void AudioClock::PushBufferedAudioData(int64_t frames, double playback_rate) { - if (frames == 0) return; - - total_buffered_frames_ += frames; - - // Avoid creating extra elements where possible. - if (!buffered_.empty() && buffered_.back().playback_rate == playback_rate) { - buffered_.back().frames += frames; - return; - } - - buffered_.push_back(AudioData(frames, playback_rate)); -} - -void AudioClock::PopBufferedAudioData(int64_t frames) { - DCHECK_LE(frames, total_buffered_frames_); - - total_buffered_frames_ -= frames; - - while (frames > 0) { - int64_t frames_to_pop = std::min(buffered_.front().frames, frames); - buffered_.front().frames -= frames_to_pop; - if (buffered_.front().frames == 0) buffered_.pop_front(); - - frames -= frames_to_pop; - } -} - -double AudioClock::ComputeBufferedMediaDurationMicros() const { - double scaled_frames = 0; - for (const auto& buffer : buffered_) - scaled_frames += buffer.frames * buffer.playback_rate; - return scaled_frames * microseconds_per_frame_; -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/audio_clock.h b/src/cobalt/media/filters/audio_clock.h deleted file mode 100644 index 23a832d..0000000 --- a/src/cobalt/media/filters/audio_clock.h +++ /dev/null
@@ -1,147 +0,0 @@ -// 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. - -#ifndef COBALT_MEDIA_FILTERS_AUDIO_CLOCK_H_ -#define COBALT_MEDIA_FILTERS_AUDIO_CLOCK_H_ - -#include <cmath> -#include <deque> - -#include "base/basictypes.h" -#include "base/time.h" -#include "cobalt/media/base/media_export.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -// Models a queue of buffered audio in a playback pipeline for use with -// estimating the amount of delay in wall clock time. Takes changes in playback -// rate into account to handle scenarios where multiple rates may be present in -// a playback pipeline with large delay. -// -// -// USAGE -// -// Prior to starting audio playback, construct an AudioClock with an initial -// media timestamp and a sample rate matching the sample rate the audio device -// was opened at. -// -// Each time the audio rendering callback is executed, call WroteAudio() once -// (and only once!) containing information on what was written: -// 1) How many frames of audio data requested -// 2) How many frames of audio data provided -// 3) The playback rate of the audio data provided -// 4) The current amount of delay -// -// After a call to WroteAudio(), clients can inspect the resulting media -// timestamp. This can be used for UI purposes, synchronizing video, etc... -// -// -// DETAILS -// -// Silence (whether caused by the initial audio delay or failing to write the -// amount of requested frames due to underflow) is also modeled and will cause -// the media timestamp to stop increasing until all known silence has been -// played. AudioClock's model is initialized with silence during the first call -// to WroteAudio() using the delay value. -// -// Playback rates are tracked for translating frame durations into media -// durations. Since silence doesn't affect media timestamps, it also isn't -// affected by playback rates. -class MEDIA_EXPORT AudioClock { - public: - AudioClock(base::TimeDelta start_timestamp, int sample_rate); - ~AudioClock(); - - // |frames_written| amount of audio data scaled to |playback_rate| written. - // |frames_requested| amount of audio data requested by hardware. - // |delay_frames| is the current amount of hardware delay. - void WroteAudio(int frames_written, int frames_requested, int delay_frames, - double playback_rate); - - // If WroteAudio() calls are suspended (i.e. due to playback being paused) the - // AudioClock will not properly advance time (even though all data up until - // back_timestamp() will playout on the physical device). - // - // To compensate for this, when calls resume, before the next WroteAudio(), - // callers should call CompensateForSuspendedWrites() to advance the clock for - // audio which continued playing out while WroteAudio() calls were suspended. - // - // |delay_frames| must be provided to properly prime the clock to compensate - // for a new initial delay. - void CompensateForSuspendedWrites(base::TimeDelta elapsed, int delay_frames); - - // Returns the bounds of media data currently buffered by the audio hardware, - // taking silence and changes in playback rate into account. Buffered audio - // structure and timestamps are updated with every call to WroteAudio(). - // - // start_timestamp = 1000 ms sample_rate = 40 Hz - // +-----------------------+-----------------------+-----------------------+ - // | 10 frames silence | 20 frames @ 1.0x | 20 frames @ 0.5x | - // | = 250 ms (wall) | = 500 ms (wall) | = 500 ms (wall) | - // | = 0 ms (media) | = 500 ms (media) | = 250 ms (media) | - // +-----------------------+-----------------------+-----------------------+ - // ^ ^ - // front_timestamp() is equal to back_timestamp() is equal to - // |start_timestamp| since no amount of media frames tracked - // media data has been played yet. by AudioClock, which would be - // 1000 + 500 + 250 = 1750 ms. - base::TimeDelta front_timestamp() const { - return base::TimeDelta::FromMicroseconds( - std::round(front_timestamp_micros_)); - } - base::TimeDelta back_timestamp() const { - return base::TimeDelta::FromMicroseconds( - std::round(back_timestamp_micros_)); - } - - // Returns the amount of wall time until |timestamp| will be played by the - // audio hardware. - // - // |timestamp| must be within front_timestamp() and back_timestamp(). - base::TimeDelta TimeUntilPlayback(base::TimeDelta timestamp) const; - - void ContiguousAudioDataBufferedForTesting( - base::TimeDelta* total, base::TimeDelta* same_rate_total) const; - - private: - // Even with a ridiculously high sample rate of 256kHz, using 64 bits will - // permit tracking up to 416999965 days worth of time (that's 1141 millenia). - // - // 32 bits on the other hand would top out at measly 2 hours and 20 minutes. - struct AudioData { - AudioData(int64_t frames, double playback_rate); - - int64_t frames; - double playback_rate; - }; - - // Helpers for operating on |buffered_|. - void PushBufferedAudioData(int64_t frames, double playback_rate); - void PopBufferedAudioData(int64_t frames); - double ComputeBufferedMediaDurationMicros() const; - - const base::TimeDelta start_timestamp_; - const double microseconds_per_frame_; - - std::deque<AudioData> buffered_; - int64_t total_buffered_frames_; - - // Use double rather than TimeDelta to avoid loss of partial microseconds when - // converting between frames-written/delayed and time-passed (see conversion - // in WroteAudio()). Particularly for |back_timestamp|, which accumulates more - // time with each call to WroteAudio(), the loss of precision can accumulate - // to create noticeable audio/video sync drift for longer (2-3 hr) videos. - // See http://crbug.com/564604. - double front_timestamp_micros_; - double back_timestamp_micros_; - - DISALLOW_COPY_AND_ASSIGN(AudioClock); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_AUDIO_CLOCK_H_
diff --git a/src/cobalt/media/filters/audio_clock_unittest.cc b/src/cobalt/media/filters/audio_clock_unittest.cc deleted file mode 100644 index e6713b4..0000000 --- a/src/cobalt/media/filters/audio_clock_unittest.cc +++ /dev/null
@@ -1,381 +0,0 @@ -// 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/filters/audio_clock.h" - -#include <limits> -#include <memory> - -#include "base/basictypes.h" -#include "cobalt/media/base/audio_timestamp_helper.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cobalt { -namespace media { - -class AudioClockTest : public testing::Test { - public: - AudioClockTest() { SetupClock(base::TimeDelta(), 10); } - - ~AudioClockTest() override {} - - void WroteAudio(int frames_written, int frames_requested, int delay_frames, - double playback_rate) { - clock_->WroteAudio(frames_written, frames_requested, delay_frames, - playback_rate); - } - - void SetupClock(base::TimeDelta start_time, int sample_rate) { - sample_rate_ = sample_rate; - clock_.reset(new AudioClock(start_time, sample_rate_)); - } - - int FrontTimestampInDays() { return clock_->front_timestamp().InDays(); } - - int FrontTimestampInMilliseconds() { - return clock_->front_timestamp().InMilliseconds(); - } - - int BackTimestampInMilliseconds() { - return clock_->back_timestamp().InMilliseconds(); - } - - int TimeUntilPlaybackInMilliseconds(int timestamp_ms) { - return clock_->TimeUntilPlayback( - base::TimeDelta::FromMilliseconds(timestamp_ms)) - .InMilliseconds(); - } - - int ContiguousAudioDataBufferedInDays() { - base::TimeDelta total, same_rate_total; - clock_->ContiguousAudioDataBufferedForTesting(&total, &same_rate_total); - return total.InDays(); - } - - int ContiguousAudioDataBufferedInMilliseconds() { - base::TimeDelta total, same_rate_total; - clock_->ContiguousAudioDataBufferedForTesting(&total, &same_rate_total); - return total.InMilliseconds(); - } - - int ContiguousAudioDataBufferedAtSameRateInMilliseconds() { - base::TimeDelta total, same_rate_total; - clock_->ContiguousAudioDataBufferedForTesting(&total, &same_rate_total); - return same_rate_total.InMilliseconds(); - } - - int sample_rate_; - std::unique_ptr<AudioClock> clock_; - - private: - DISALLOW_COPY_AND_ASSIGN(AudioClockTest); -}; - -TEST_F(AudioClockTest, FrontTimestampStartsAtStartTimestamp) { - base::TimeDelta expected = base::TimeDelta::FromSeconds(123); - AudioClock clock(expected, sample_rate_); - - EXPECT_EQ(expected, clock.front_timestamp()); -} - -TEST_F(AudioClockTest, BackTimestampStartsAtStartTimestamp) { - base::TimeDelta expected = base::TimeDelta::FromSeconds(123); - AudioClock clock(expected, sample_rate_); - - EXPECT_EQ(expected, clock.back_timestamp()); -} - -TEST_F(AudioClockTest, Playback) { - // The first time we write data we should still expect our start timestamp - // due to delay. - WroteAudio(10, 10, 20, 1.0); - EXPECT_EQ(0, FrontTimestampInMilliseconds()); - EXPECT_EQ(1000, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - // The media time should remain at start timestamp as we write data. - WroteAudio(10, 10, 20, 1.0); - EXPECT_EQ(0, FrontTimestampInMilliseconds()); - EXPECT_EQ(2000, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(10, 10, 20, 1.0); - EXPECT_EQ(0, FrontTimestampInMilliseconds()); - EXPECT_EQ(3000, BackTimestampInMilliseconds()); - EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(3000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - // The media time should now start advanced now that delay has been covered. - WroteAudio(10, 10, 20, 1.0); - EXPECT_EQ(1000, FrontTimestampInMilliseconds()); - EXPECT_EQ(4000, BackTimestampInMilliseconds()); - EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(3000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(10, 10, 20, 1.0); - EXPECT_EQ(2000, FrontTimestampInMilliseconds()); - EXPECT_EQ(5000, BackTimestampInMilliseconds()); - EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(3000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - // Introduce a rate change to slow down time: - // - Current time will advance by one second until it hits rate change - // - Contiguous audio data will start shrinking immediately - WroteAudio(10, 10, 20, 0.5); - EXPECT_EQ(3000, FrontTimestampInMilliseconds()); - EXPECT_EQ(5500, BackTimestampInMilliseconds()); - EXPECT_EQ(2500, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(2000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(10, 10, 20, 0.5); - EXPECT_EQ(4000, FrontTimestampInMilliseconds()); - EXPECT_EQ(6000, BackTimestampInMilliseconds()); - EXPECT_EQ(2000, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(1000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(10, 10, 20, 0.5); - EXPECT_EQ(5000, FrontTimestampInMilliseconds()); - EXPECT_EQ(6500, BackTimestampInMilliseconds()); - EXPECT_EQ(1500, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(1500, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(10, 10, 20, 0.5); - EXPECT_EQ(5500, FrontTimestampInMilliseconds()); - EXPECT_EQ(7000, BackTimestampInMilliseconds()); - EXPECT_EQ(1500, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(1500, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - // Introduce a rate change to speed up time: - // - Current time will advance by half a second until it hits rate change - // - Contiguous audio data will start growing immediately - WroteAudio(10, 10, 20, 2); - EXPECT_EQ(6000, FrontTimestampInMilliseconds()); - EXPECT_EQ(9000, BackTimestampInMilliseconds()); - EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(1000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(10, 10, 20, 2); - EXPECT_EQ(6500, FrontTimestampInMilliseconds()); - EXPECT_EQ(11000, BackTimestampInMilliseconds()); - EXPECT_EQ(4500, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(500, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(10, 10, 20, 2); - EXPECT_EQ(7000, FrontTimestampInMilliseconds()); - EXPECT_EQ(13000, BackTimestampInMilliseconds()); - EXPECT_EQ(6000, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(6000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(10, 10, 20, 2); - EXPECT_EQ(9000, FrontTimestampInMilliseconds()); - EXPECT_EQ(15000, BackTimestampInMilliseconds()); - EXPECT_EQ(6000, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(6000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - // Write silence to simulate reaching end of stream: - // - Current time will advance by half a second until it hits silence - // - Contiguous audio data will start shrinking towards zero - WroteAudio(0, 10, 20, 2); - EXPECT_EQ(11000, FrontTimestampInMilliseconds()); - EXPECT_EQ(15000, BackTimestampInMilliseconds()); - EXPECT_EQ(4000, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(4000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(0, 10, 20, 2); - EXPECT_EQ(13000, FrontTimestampInMilliseconds()); - EXPECT_EQ(15000, BackTimestampInMilliseconds()); - EXPECT_EQ(2000, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(2000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - WroteAudio(0, 10, 20, 2); - EXPECT_EQ(15000, FrontTimestampInMilliseconds()); - EXPECT_EQ(15000, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - - // At this point media time should stop increasing. - WroteAudio(0, 10, 20, 2); - EXPECT_EQ(15000, FrontTimestampInMilliseconds()); - EXPECT_EQ(15000, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); -} - -TEST_F(AudioClockTest, AlternatingAudioAndSilence) { - // Buffer #1: [0, 1000) - WroteAudio(10, 10, 20, 1.0); - EXPECT_EQ(0, FrontTimestampInMilliseconds()); - EXPECT_EQ(1000, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - - // Buffer #2: 1000ms of silence - WroteAudio(0, 10, 20, 1.0); - EXPECT_EQ(0, FrontTimestampInMilliseconds()); - EXPECT_EQ(1000, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - - // Buffer #3: [1000, 2000): - // - Buffer #1 is at front with 1000ms of contiguous audio data - WroteAudio(10, 10, 20, 1.0); - EXPECT_EQ(0, FrontTimestampInMilliseconds()); - EXPECT_EQ(2000, BackTimestampInMilliseconds()); - EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); - - // Buffer #4: 1000ms of silence - // - Buffer #1 has been played out - // - Buffer #2 of silence leaves us with 0ms of contiguous audio data - WroteAudio(0, 10, 20, 1.0); - EXPECT_EQ(1000, FrontTimestampInMilliseconds()); - EXPECT_EQ(2000, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - - // Buffer #5: [2000, 3000): - // - Buffer #3 is at front with 1000ms of contiguous audio data - WroteAudio(10, 10, 20, 1.0); - EXPECT_EQ(1000, FrontTimestampInMilliseconds()); - EXPECT_EQ(3000, BackTimestampInMilliseconds()); - EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); -} - -TEST_F(AudioClockTest, ZeroDelay) { - // The first time we write data we should expect the first timestamp - // immediately. - WroteAudio(10, 10, 0, 1.0); - EXPECT_EQ(0, FrontTimestampInMilliseconds()); - EXPECT_EQ(1000, BackTimestampInMilliseconds()); - EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); - - // Ditto for all subsequent buffers. - WroteAudio(10, 10, 0, 1.0); - EXPECT_EQ(1000, FrontTimestampInMilliseconds()); - EXPECT_EQ(2000, BackTimestampInMilliseconds()); - EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); - - WroteAudio(10, 10, 0, 1.0); - EXPECT_EQ(2000, FrontTimestampInMilliseconds()); - EXPECT_EQ(3000, BackTimestampInMilliseconds()); - EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); - - // Ditto for silence. - WroteAudio(0, 10, 0, 1.0); - EXPECT_EQ(3000, FrontTimestampInMilliseconds()); - EXPECT_EQ(3000, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - - WroteAudio(0, 10, 0, 1.0); - EXPECT_EQ(3000, FrontTimestampInMilliseconds()); - EXPECT_EQ(3000, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); -} - -TEST_F(AudioClockTest, TimeUntilPlayback) { - // Construct an audio clock with the following representation: - // - // existing - // |- delay -|------------------ calls to WroteAudio() ------------------| - // +------------+---------+------------+-----------+------------+-----------+ - // | 20 silence | 10 @ 1x | 10 silence | 10 @ 0.5x | 10 silence | 10 @ 2.0x | - // +------------+---------+------------+-----------+------------+-----------+ - // Media: 0 1000 1000 1500 1500 3500 - // Wall: 2000 3000 4000 5000 6000 7000 - WroteAudio(10, 10, 60, 1.0); - WroteAudio(0, 10, 60, 1.0); - WroteAudio(10, 10, 60, 0.5); - WroteAudio(0, 10, 60, 0.5); - WroteAudio(10, 10, 60, 2.0); - EXPECT_EQ(0, FrontTimestampInMilliseconds()); - EXPECT_EQ(3500, BackTimestampInMilliseconds()); - EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - - // Media timestamp zero has to wait for silence to pass. - EXPECT_EQ(2000, TimeUntilPlaybackInMilliseconds(0)); - - // From then on out it's simply adding up the number of frames and taking - // silence into account. - EXPECT_EQ(2500, TimeUntilPlaybackInMilliseconds(500)); - EXPECT_EQ(3000, TimeUntilPlaybackInMilliseconds(1000)); - EXPECT_EQ(4500, TimeUntilPlaybackInMilliseconds(1250)); - EXPECT_EQ(5000, TimeUntilPlaybackInMilliseconds(1500)); - EXPECT_EQ(6500, TimeUntilPlaybackInMilliseconds(2500)); - EXPECT_EQ(7000, TimeUntilPlaybackInMilliseconds(3500)); -} - -TEST_F(AudioClockTest, SupportsYearsWorthOfAudioData) { - // Use number of frames that would be likely to overflow 32-bit integer math. - const int huge_amount_of_frames = std::numeric_limits<int>::max(); - const base::TimeDelta huge = - base::TimeDelta::FromSeconds(huge_amount_of_frames / sample_rate_); - EXPECT_EQ(2485, huge.InDays()); // Just to give some context on how big... - - // Use zero delay to test calculation of current timestamp. - WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0); - EXPECT_EQ(0, FrontTimestampInDays()); - EXPECT_EQ(2485, ContiguousAudioDataBufferedInDays()); - - WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0); - EXPECT_EQ(huge.InDays(), FrontTimestampInDays()); - EXPECT_EQ(huge.InDays(), ContiguousAudioDataBufferedInDays()); - - WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0); - EXPECT_EQ((huge * 2).InDays(), FrontTimestampInDays()); - EXPECT_EQ(huge.InDays(), ContiguousAudioDataBufferedInDays()); - - WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0); - EXPECT_EQ((huge * 3).InDays(), FrontTimestampInDays()); - EXPECT_EQ(huge.InDays(), ContiguousAudioDataBufferedInDays()); - - // Use huge delay to test calculation of buffered data. - WroteAudio(huge_amount_of_frames, huge_amount_of_frames, - huge_amount_of_frames, 1.0); - EXPECT_EQ((huge * 3).InDays(), FrontTimestampInDays()); - EXPECT_EQ((huge * 2).InDays(), ContiguousAudioDataBufferedInDays()); -} - -TEST_F(AudioClockTest, CompensateForSuspendedWrites) { - // Buffer 6 seconds of delay and 1 second of audio data. - WroteAudio(10, 10, 60, 1.0); - - // Media timestamp zero has to wait for silence to pass. - const int kBaseTimeMs = 6000; - EXPECT_EQ(kBaseTimeMs, TimeUntilPlaybackInMilliseconds(0)); - - // Elapsing frames less than we have buffered should do nothing. - const int kDelayFrames = 2; - for (int i = 1000; i <= kBaseTimeMs; i += 1000) { - clock_->CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(i), - kDelayFrames); - EXPECT_EQ(kBaseTimeMs - (i - 1000), TimeUntilPlaybackInMilliseconds(0)); - - // Write silence to simulate maintaining a 7s output buffer. - WroteAudio(0, 10, 60, 1.0); - } - - // Exhausting all frames should advance timestamps and prime the buffer with - // our delay frames value. - clock_->CompensateForSuspendedWrites(base::TimeDelta::FromMilliseconds(7000), - kDelayFrames); - EXPECT_EQ(kDelayFrames * 100, TimeUntilPlaybackInMilliseconds(1000)); -} - -TEST_F(AudioClockTest, FramesToTimePrecision) { - SetupClock(base::TimeDelta(), 48000); - double micros_per_frame = base::Time::kMicrosecondsPerSecond / 48000.0; - int frames_written = 0; - - // Write ~2 hours of data to clock to give any error a significant chance to - // accumulate. - while (clock_->back_timestamp() <= base::TimeDelta::FromHours(2)) { - frames_written += 1024; - WroteAudio(1024, 1024, 0, 1); - } - - // Verify no error accumulated. - EXPECT_EQ(std::round(frames_written * micros_per_frame), - clock_->back_timestamp().InMicroseconds()); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/audio_decoder_selector_unittest.cc b/src/cobalt/media/filters/audio_decoder_selector_unittest.cc deleted file mode 100644 index 6e07827..0000000 --- a/src/cobalt/media/filters/audio_decoder_selector_unittest.cc +++ /dev/null
@@ -1,386 +0,0 @@ -// Copyright (c) 2012 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 <utility> -#include <vector> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/message_loop.h" -#include "base/run_loop.h" -#include "build/build_config.h" -#include "cobalt/media/base/gmock_callback_support.h" -#include "cobalt/media/base/media_util.h" -#include "cobalt/media/base/mock_filters.h" -#include "cobalt/media/base/test_helpers.h" -#include "cobalt/media/filters/decoder_selector.h" -#include "cobalt/media/filters/decrypting_demuxer_stream.h" -#include "starboard/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::InvokeWithoutArgs; -using ::testing::IsNull; -using ::testing::NiceMock; -using ::testing::NotNull; -using ::testing::Return; -using ::testing::StrictMock; - -// Use anonymous namespace here to prevent the actions to be defined multiple -// times across multiple test files. Sadly we can't use static for them. -namespace { - -MATCHER(EncryptedConfig, "") { return arg.is_encrypted(); } -MATCHER(ClearConfig, "") { return !arg.is_encrypted(); } - -} // namespace - -namespace cobalt { -namespace media { - -class AudioDecoderSelectorTest : public ::testing::Test { - public: - enum DecryptorCapability { - kNoCdm, // No CDM. Only possible for clear stream. - kNoDecryptor, // CDM is available but Decryptor is not supported. - kDecryptOnly, - kDecryptAndDecode - }; - - AudioDecoderSelectorTest() - : media_log_(new MediaLog()), - traits_(media_log_), - demuxer_stream_( - new StrictMock<MockDemuxerStream>(DemuxerStream::AUDIO)), - decoder_1_(new StrictMock<MockAudioDecoder>()), - decoder_2_(new StrictMock<MockAudioDecoder>()) { - all_decoders_.push_back(decoder_1_); - all_decoders_.push_back(decoder_2_); - // |cdm_context_| and |decryptor_| are conditionally created in - // InitializeDecoderSelector(). - } - - ~AudioDecoderSelectorTest() { base::RunLoop().RunUntilIdle(); } - - MOCK_METHOD2(OnDecoderSelected, - void(AudioDecoder*, DecryptingDemuxerStream*)); - - void MockOnDecoderSelected(std::unique_ptr<AudioDecoder> decoder, - std::unique_ptr<DecryptingDemuxerStream> stream) { - OnDecoderSelected(decoder.get(), stream.get()); - selected_decoder_ = std::move(decoder); - } - - void UseClearStream() { - AudioDecoderConfig clear_audio_config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, 44100, - EmptyExtraData(), Unencrypted()); - demuxer_stream_->set_audio_decoder_config(clear_audio_config); - } - - void UseEncryptedStream() { - AudioDecoderConfig encrypted_audio_config( - kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, - EmptyExtraData(), AesCtrEncryptionScheme()); - demuxer_stream_->set_audio_decoder_config(encrypted_audio_config); - } - - void InitializeDecoderSelector(DecryptorCapability decryptor_capability, - int num_decoders) { - if (decryptor_capability != kNoCdm) { - cdm_context_.reset(new StrictMock<MockCdmContext>()); - - if (decryptor_capability == kNoDecryptor) { - EXPECT_CALL(*cdm_context_, GetDecryptor()).WillRepeatedly(Return(NULL)); - } else { - decryptor_.reset(new NiceMock<MockDecryptor>()); - EXPECT_CALL(*cdm_context_, GetDecryptor()) - .WillRepeatedly(Return(decryptor_.get())); - EXPECT_CALL(*decryptor_, InitializeAudioDecoder(_, _)) - .WillRepeatedly( - RunCallback<1>(decryptor_capability == kDecryptAndDecode)); - } - } - - DCHECK_GE(all_decoders_.size(), static_cast<size_t>(num_decoders)); - all_decoders_.erase(all_decoders_.begin() + num_decoders, - all_decoders_.end()); - - decoder_selector_.reset(new AudioDecoderSelector( - message_loop_.task_runner(), std::move(all_decoders_), media_log_)); - } - - void SelectDecoder() { - decoder_selector_->SelectDecoder( - &traits_, demuxer_stream_.get(), cdm_context_.get(), - base::Bind(&AudioDecoderSelectorTest::MockOnDecoderSelected, - base::Unretained(this)), - base::Bind(&AudioDecoderSelectorTest::OnDecoderOutput), - base::Bind(&AudioDecoderSelectorTest::OnWaitingForDecryptionKey)); - base::RunLoop().RunUntilIdle(); - } - - void SelectDecoderAndDestroy() { - SelectDecoder(); - - EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); - decoder_selector_.reset(); - base::RunLoop().RunUntilIdle(); - } - - static void OnDecoderOutput(const scoped_refptr<AudioBuffer>& output) { - NOTREACHED(); - } - - static void OnWaitingForDecryptionKey() { NOTREACHED(); } - - scoped_refptr<MediaLog> media_log_; - - // Stream traits specific to audio decoding. - DecoderStreamTraits<DemuxerStream::AUDIO> traits_; - - // Declare |decoder_selector_| after |demuxer_stream_| and |decryptor_| since - // |demuxer_stream_| and |decryptor_| should outlive |decoder_selector_|. - std::unique_ptr<StrictMock<MockDemuxerStream>> demuxer_stream_; - - std::unique_ptr<StrictMock<MockCdmContext>> cdm_context_; - - // Use NiceMock since we don't care about most of calls on the decryptor, e.g. - // RegisterNewKeyCB(). - std::unique_ptr<NiceMock<MockDecryptor>> decryptor_; - - std::unique_ptr<AudioDecoderSelector> decoder_selector_; - - StrictMock<MockAudioDecoder>* decoder_1_; - StrictMock<MockAudioDecoder>* decoder_2_; - ScopedVector<AudioDecoder> all_decoders_; - std::unique_ptr<AudioDecoder> selected_decoder_; - - base::MessageLoop message_loop_; - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderSelectorTest); -}; - -// TODO(xhwang): Add kNoCdm tests for clear stream. - -// The stream is not encrypted but we have no clear decoder. No decoder can be -// selected. -TEST_F(AudioDecoderSelectorTest, ClearStream_NoDecryptor_NoClearDecoder) { - UseClearStream(); - InitializeDecoderSelector(kNoDecryptor, 0); - - EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); - - SelectDecoder(); -} - -// The stream is not encrypted and we have one clear decoder. The decoder -// will be selected. -TEST_F(AudioDecoderSelectorTest, ClearStream_NoDecryptor_OneClearDecoder) { - UseClearStream(); - InitializeDecoderSelector(kNoDecryptor, 1); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(true)); - EXPECT_CALL(*this, OnDecoderSelected(decoder_1_, IsNull())); - - SelectDecoder(); -} - -TEST_F(AudioDecoderSelectorTest, - Destroy_ClearStream_NoDecryptor_OneClearDecoder) { - UseClearStream(); - InitializeDecoderSelector(kNoDecryptor, 1); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)); - - SelectDecoderAndDestroy(); -} - -// The stream is not encrypted and we have multiple clear decoders. The first -// decoder that can decode the input stream will be selected. -TEST_F(AudioDecoderSelectorTest, ClearStream_NoDecryptor_MultipleClearDecoder) { - UseClearStream(); - InitializeDecoderSelector(kNoDecryptor, 2); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(false)); - EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(true)); - EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, IsNull())); - - SelectDecoder(); -} - -TEST_F(AudioDecoderSelectorTest, - Destroy_ClearStream_NoDecryptor_MultipleClearDecoder) { - UseClearStream(); - InitializeDecoderSelector(kNoDecryptor, 2); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(false)); - EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _)); - - SelectDecoderAndDestroy(); -} - -// There is a decryptor but the stream is not encrypted. The decoder will be -// selected. -TEST_F(AudioDecoderSelectorTest, ClearStream_HasDecryptor) { - UseClearStream(); - InitializeDecoderSelector(kDecryptOnly, 1); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(true)); - EXPECT_CALL(*this, OnDecoderSelected(decoder_1_, IsNull())); - - SelectDecoder(); -} - -TEST_F(AudioDecoderSelectorTest, Destroy_ClearStream_HasDecryptor) { - UseClearStream(); - InitializeDecoderSelector(kDecryptOnly, 1); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)); - - SelectDecoderAndDestroy(); -} - -// The stream is encrypted and there's no decryptor. The decoder only supports -// clear streams so no decoder can be selected. -TEST_F(AudioDecoderSelectorTest, EncryptedStream_NoDecryptor_OneClearDecoder) { - UseEncryptedStream(); - InitializeDecoderSelector(kNoDecryptor, 1); - - EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _)) - .WillOnce(RunCallback<2>(false)); - EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); - - SelectDecoder(); -} - -TEST_F(AudioDecoderSelectorTest, - Destroy_EncryptedStream_NoDecryptor_OneClearDecoder) { - UseEncryptedStream(); - InitializeDecoderSelector(kNoDecryptor, 1); - - EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _)); - - SelectDecoderAndDestroy(); -} - -// The stream is encrypted and there's no decryptor. There are multiple decoders -// and the first one that supports encrypted streams is selected. -TEST_F(AudioDecoderSelectorTest, EncryptedStream_NoDecryptor_MultipleDecoders) { - UseEncryptedStream(); - InitializeDecoderSelector(kNoDecryptor, 2); - - EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _)) - .WillOnce(RunCallback<2>(false)); - EXPECT_CALL(*decoder_2_, Initialize(EncryptedConfig(), _, _, _)) - .WillOnce(RunCallback<2>(true)); - EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, IsNull())); - - SelectDecoder(); -} - -TEST_F(AudioDecoderSelectorTest, - Destroy_EncryptedStream_NoDecryptor_MultipleDecoders) { - UseEncryptedStream(); - InitializeDecoderSelector(kNoDecryptor, 2); - - EXPECT_CALL(*decoder_1_, Initialize(EncryptedConfig(), _, _, _)) - .WillOnce(RunCallback<2>(false)); - EXPECT_CALL(*decoder_2_, Initialize(EncryptedConfig(), _, _, _)); - - SelectDecoderAndDestroy(); -} - -// Decryptor can only do decryption and there's no decoder available. No decoder -// can be selected. -TEST_F(AudioDecoderSelectorTest, EncryptedStream_DecryptOnly_NoClearDecoder) { - UseEncryptedStream(); - InitializeDecoderSelector(kDecryptOnly, 0); - - EXPECT_CALL(*this, OnDecoderSelected(IsNull(), IsNull())); - - SelectDecoder(); -} - -// Decryptor can do decryption-only and there's a decoder available. The decoder -// will be selected and a DecryptingDemuxerStream will be created. -TEST_F(AudioDecoderSelectorTest, EncryptedStream_DecryptOnly_OneClearDecoder) { - UseEncryptedStream(); - InitializeDecoderSelector(kDecryptOnly, 1); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(true)); - EXPECT_CALL(*this, OnDecoderSelected(decoder_1_, NotNull())); - - SelectDecoder(); -} - -TEST_F(AudioDecoderSelectorTest, - Destroy_EncryptedStream_DecryptOnly_OneClearDecoder) { - UseEncryptedStream(); - InitializeDecoderSelector(kDecryptOnly, 1); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)); - - SelectDecoderAndDestroy(); -} - -// Decryptor can only do decryption and there are multiple decoders available. -// The first decoder that can decode the input stream will be selected and -// a DecryptingDemuxerStream will be created. -TEST_F(AudioDecoderSelectorTest, - EncryptedStream_DecryptOnly_MultipleClearDecoder) { - UseEncryptedStream(); - InitializeDecoderSelector(kDecryptOnly, 2); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(false)); - EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(true)); - EXPECT_CALL(*this, OnDecoderSelected(decoder_2_, NotNull())); - - SelectDecoder(); -} - -TEST_F(AudioDecoderSelectorTest, - Destroy_EncryptedStream_DecryptOnly_MultipleClearDecoder) { - UseEncryptedStream(); - InitializeDecoderSelector(kDecryptOnly, 2); - - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(false)); - EXPECT_CALL(*decoder_2_, Initialize(ClearConfig(), _, _, _)); - - SelectDecoderAndDestroy(); -} - -// Decryptor can do decryption and decoding. -TEST_F(AudioDecoderSelectorTest, EncryptedStream_DecryptAndDecode) { - UseEncryptedStream(); - InitializeDecoderSelector(kDecryptAndDecode, 1); - -#if !defined(OS_ANDROID) - // A DecryptingVideoDecoder will be created and selected. The clear decoder - // should not be touched at all. No DecryptingDemuxerStream should to be - // created. - EXPECT_CALL(*this, OnDecoderSelected(NotNull(), IsNull())); -#else - // A DecryptingDemuxerStream will be created. The clear decoder will be - // initialized and returned. - EXPECT_CALL(*decoder_1_, Initialize(ClearConfig(), _, _, _)) - .WillOnce(RunCallback<2>(true)); - EXPECT_CALL(*this, OnDecoderSelected(NotNull(), NotNull())); -#endif - - SelectDecoder(); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/audio_decoder_unittest.cc b/src/cobalt/media/filters/audio_decoder_unittest.cc deleted file mode 100644 index f344600..0000000 --- a/src/cobalt/media/filters/audio_decoder_unittest.cc +++ /dev/null
@@ -1,633 +0,0 @@ -// 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 <vector> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/format_macros.h" -#include "base/md5.h" -#include "base/message_loop.h" -#include "base/run_loop.h" -#include "base/stringprintf.h" -#include "base/sys_byteorder.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" -#include "cobalt/media/base/audio_buffer.h" -#include "cobalt/media/base/audio_bus.h" -#include "cobalt/media/base/audio_hash.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/media_util.h" -#include "cobalt/media/base/test_data_util.h" -#include "cobalt/media/base/test_helpers.h" -#include "cobalt/media/base/timestamp_constants.h" -#include "cobalt/media/ffmpeg/ffmpeg_common.h" -#include "cobalt/media/filters/audio_file_reader.h" -#include "cobalt/media/filters/ffmpeg_audio_decoder.h" -#include "cobalt/media/filters/in_memory_url_protocol.h" -#include "cobalt/media/filters/opus_audio_decoder.h" -#include "starboard/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_ANDROID) -#include "base/android/build_info.h" -#include "cobalt/media/base/android/media_codec_util.h" -#include "cobalt/media/filters/android/media_codec_audio_decoder.h" - -#if defined(USE_PROPRIETARY_CODECS) -#include "cobalt/media/formats/mpeg/adts_stream_parser.h" -#endif - -// Helper macro to skip the test if MediaCodec is not available. -#define SKIP_TEST_IF_NO_MEDIA_CODEC() \ - do { \ - if (GetParam().decoder_type == MEDIA_CODEC) { \ - if (!MediaCodecUtil::IsMediaCodecAvailable()) { \ - VLOG(0) << "Could not run test - no MediaCodec on device."; \ - return; \ - } \ - if (GetParam().codec == kCodecOpus && \ - base::android::BuildInfo::GetInstance()->sdk_int() < 21) { \ - VLOG(0) << "Could not run test - Opus is not supported"; \ - return; \ - } \ - } \ - } while (0) -#else -#define SKIP_TEST_IF_NO_MEDIA_CODEC() \ - do { \ - } while (0) -#endif // !defined(OS_ANDROID) - -namespace cobalt { -namespace media { - -// The number of packets to read and then decode from each file. -static const size_t kDecodeRuns = 3; -static const uint8_t kOpusExtraData[] = { - 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64, 0x01, 0x02, - // The next two bytes represent the codec delay. - 0x00, 0x00, 0x80, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00}; - -enum AudioDecoderType { - FFMPEG, - OPUS, -#if defined(OS_ANDROID) - MEDIA_CODEC, -#endif -}; - -struct DecodedBufferExpectations { - const int64_t timestamp; - const int64_t duration; - const char* hash; -}; - -struct DecoderTestData { - const AudioDecoderType decoder_type; - const AudioCodec codec; - const char* filename; - const DecodedBufferExpectations* expectations; - const int first_packet_pts; - const int samples_per_second; - const ChannelLayout channel_layout; -}; - -// Tells gtest how to print our DecoderTestData structure. -std::ostream& operator<<(std::ostream& os, const DecoderTestData& data) { - return os << data.filename; -} - -// Marks negative timestamp buffers for discard or transfers FFmpeg's built in -// discard metadata in favor of setting DiscardPadding on the DecoderBuffer. -// Allows better testing of AudioDiscardHelper usage. -static void SetDiscardPadding(AVPacket* packet, - const scoped_refptr<DecoderBuffer> buffer, - double samples_per_second) { - // Discard negative timestamps. - if (buffer->timestamp() + buffer->duration() < base::TimeDelta()) { - buffer->set_discard_padding( - std::make_pair(kInfiniteDuration, base::TimeDelta())); - return; - } - if (buffer->timestamp() < base::TimeDelta()) { - buffer->set_discard_padding( - std::make_pair(-buffer->timestamp(), base::TimeDelta())); - return; - } - - // If the timestamp is positive, try to use FFmpeg's discard data. - int skip_samples_size = 0; - const uint32_t* skip_samples_ptr = - reinterpret_cast<const uint32_t*>(av_packet_get_side_data( - packet, AV_PKT_DATA_SKIP_SAMPLES, &skip_samples_size)); - if (skip_samples_size < 4) return; - buffer->set_discard_padding(std::make_pair( - base::TimeDelta::FromSecondsD(base::ByteSwapToLE32(*skip_samples_ptr) / - samples_per_second), - base::TimeDelta())); -} - -class AudioDecoderTest : public testing::TestWithParam<DecoderTestData> { - public: - AudioDecoderTest() - : pending_decode_(false), - pending_reset_(false), - last_decode_status_(DecodeStatus::DECODE_ERROR) { - switch (GetParam().decoder_type) { - case FFMPEG: - decoder_.reset(new FFmpegAudioDecoder(message_loop_.task_runner(), - new MediaLog())); - break; - case OPUS: - decoder_.reset(new OpusAudioDecoder(message_loop_.task_runner())); - break; -#if defined(OS_ANDROID) - case MEDIA_CODEC: - decoder_.reset(new MediaCodecAudioDecoder(message_loop_.task_runner())); - break; -#endif - } - } - - virtual ~AudioDecoderTest() { - EXPECT_FALSE(pending_decode_); - EXPECT_FALSE(pending_reset_); - } - - protected: - void DecodeBuffer(const scoped_refptr<DecoderBuffer>& buffer) { - ASSERT_FALSE(pending_decode_); - pending_decode_ = true; - last_decode_status_ = DecodeStatus::DECODE_ERROR; - - base::RunLoop run_loop; - decoder_->Decode( - buffer, base::Bind(&AudioDecoderTest::DecodeFinished, - base::Unretained(this), run_loop.QuitClosure())); - run_loop.Run(); - ASSERT_FALSE(pending_decode_); - } - - void SendEndOfStream() { DecodeBuffer(DecoderBuffer::CreateEOSBuffer()); } - - void Initialize() { - // Load the test data file. - data_ = ReadTestDataFile(GetParam().filename); - protocol_.reset( - new InMemoryUrlProtocol(data_->data(), data_->data_size(), false)); - reader_.reset(new AudioFileReader(protocol_.get())); - ASSERT_TRUE(reader_->OpenDemuxerForTesting()); - - // Load the first packet and check its timestamp. - AVPacket packet; - ASSERT_TRUE(reader_->ReadPacketForTesting(&packet)); - EXPECT_EQ(GetParam().first_packet_pts, packet.pts); - start_timestamp_ = ConvertFromTimeBase( - reader_->GetAVStreamForTesting()->time_base, packet.pts); - - // Seek back to the beginning. - ASSERT_TRUE(reader_->SeekForTesting(start_timestamp_)); - - AudioDecoderConfig config; - ASSERT_TRUE(AVCodecContextToAudioDecoderConfig( - reader_->codec_context_for_testing(), Unencrypted(), &config)); - -#if defined(OS_ANDROID) && defined(USE_PROPRIETARY_CODECS) - // MEDIA_CODEC type requires config->extra_data() for AAC codec. For ADTS - // streams we need to extract it with a separate procedure. - if (GetParam().decoder_type == MEDIA_CODEC && - GetParam().codec == kCodecAAC && config.extra_data().empty()) { - int sample_rate; - ChannelLayout channel_layout; - std::vector<uint8_t> extra_data; - ASSERT_GT(ADTSStreamParser().ParseFrameHeader( - packet.data, packet.size, NULL, &sample_rate, - &channel_layout, NULL, NULL, &extra_data), - 0); - config.Initialize(kCodecAAC, kSampleFormatS16, channel_layout, - sample_rate, extra_data, Unencrypted(), - base::TimeDelta(), 0); - ASSERT_FALSE(config.extra_data().empty()); - } -#endif - - av_packet_unref(&packet); - - EXPECT_EQ(GetParam().codec, config.codec()); - EXPECT_EQ(GetParam().samples_per_second, config.samples_per_second()); - EXPECT_EQ(GetParam().channel_layout, config.channel_layout()); - - InitializeDecoder(config); - } - - void InitializeDecoder(const AudioDecoderConfig& config) { - InitializeDecoderWithResult(config, true); - } - - void InitializeDecoderWithResult(const AudioDecoderConfig& config, - bool success) { - decoder_->Initialize( - config, NULL, NewExpectedBoolCB(success), - base::Bind(&AudioDecoderTest::OnDecoderOutput, base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - void Decode() { - AVPacket packet; - ASSERT_TRUE(reader_->ReadPacketForTesting(&packet)); - - // Split out packet metadata before making a copy. - av_packet_split_side_data(&packet); - - scoped_refptr<DecoderBuffer> buffer = - DecoderBuffer::CopyFrom(packet.data, packet.size); - buffer->set_timestamp(ConvertFromTimeBase( - reader_->GetAVStreamForTesting()->time_base, packet.pts)); - buffer->set_duration(ConvertFromTimeBase( - reader_->GetAVStreamForTesting()->time_base, packet.duration)); - if (packet.flags & AV_PKT_FLAG_KEY) buffer->set_is_key_frame(true); - - // Don't set discard padding for Opus, it already has discard behavior set - // based on the codec delay in the AudioDecoderConfig. - if (GetParam().decoder_type == FFMPEG) - SetDiscardPadding(&packet, buffer, GetParam().samples_per_second); - - // DecodeBuffer() shouldn't need the original packet since it uses the copy. - av_packet_unref(&packet); - DecodeBuffer(buffer); - } - - void Reset() { - ASSERT_FALSE(pending_reset_); - pending_reset_ = true; - decoder_->Reset( - base::Bind(&AudioDecoderTest::ResetFinished, base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - ASSERT_FALSE(pending_reset_); - } - - void Seek(base::TimeDelta seek_time) { - Reset(); - decoded_audio_.clear(); - ASSERT_TRUE(reader_->SeekForTesting(seek_time)); - } - - void OnDecoderOutput(const scoped_refptr<AudioBuffer>& buffer) { - EXPECT_FALSE(buffer->end_of_stream()); - decoded_audio_.push_back(buffer); - } - - void DecodeFinished(const base::Closure& quit_closure, DecodeStatus status) { - EXPECT_TRUE(pending_decode_); - EXPECT_FALSE(pending_reset_); - pending_decode_ = false; - last_decode_status_ = status; - quit_closure.Run(); - } - - void ResetFinished() { - EXPECT_TRUE(pending_reset_); - EXPECT_FALSE(pending_decode_); - pending_reset_ = false; - } - - // Generates an MD5 hash of the audio signal. Should not be used for checks - // across platforms as audio varies slightly across platforms. - std::string GetDecodedAudioMD5(size_t i) { - CHECK_LT(i, decoded_audio_.size()); - const scoped_refptr<AudioBuffer>& buffer = decoded_audio_[i]; - - std::unique_ptr<AudioBus> output = - AudioBus::Create(buffer->channel_count(), buffer->frame_count()); - buffer->ReadFrames(buffer->frame_count(), 0, 0, output.get()); - - base::MD5Context context; - base::MD5Init(&context); - for (int ch = 0; ch < output->channels(); ++ch) { - base::MD5Update( - &context, - base::StringPiece(reinterpret_cast<char*>(output->channel(ch)), - output->frames() * sizeof(*output->channel(ch)))); - } - base::MD5Digest digest; - base::MD5Final(&digest, &context); - return base::MD5DigestToBase16(digest); - } - - // Android MediaCodec returns wrong timestamps (shifted one frame forward) - // for AAC before Android L. Skip the timestamp check in this situation. - bool SkipBufferTimestampCheck() const { -#if defined(OS_ANDROID) - return (base::android::BuildInfo::GetInstance()->sdk_int() < 21) && - GetParam().decoder_type == MEDIA_CODEC && - GetParam().codec == kCodecAAC; -#else - return false; -#endif - } - - void ExpectDecodedAudio(size_t i, const std::string& exact_hash) { - CHECK_LT(i, decoded_audio_.size()); - const scoped_refptr<AudioBuffer>& buffer = decoded_audio_[i]; - - const DecodedBufferExpectations& sample_info = GetParam().expectations[i]; - - // Android MediaCodec returns wrong timestamps (shifted one frame forward) - // for AAC before Android L. Ignore sample_info.timestamp in this situation. - if (!SkipBufferTimestampCheck()) - EXPECT_EQ(sample_info.timestamp, buffer->timestamp().InMicroseconds()); - EXPECT_EQ(sample_info.duration, buffer->duration().InMicroseconds()); - EXPECT_FALSE(buffer->end_of_stream()); - - std::unique_ptr<AudioBus> output = - AudioBus::Create(buffer->channel_count(), buffer->frame_count()); - buffer->ReadFrames(buffer->frame_count(), 0, 0, output.get()); - - // Generate a lossy hash of the audio used for comparison across platforms. - AudioHash audio_hash; - audio_hash.Update(output.get(), output->frames()); - EXPECT_TRUE(audio_hash.IsEquivalent(sample_info.hash, 0.02)) - << "Audio hashes differ. Expected: " << sample_info.hash - << " Actual: " << audio_hash.ToString(); - - if (!exact_hash.empty()) { - EXPECT_EQ(exact_hash, GetDecodedAudioMD5(i)); - - // Verify different hashes are being generated. None of our test data - // files have audio that hashes out exactly the same. - if (i > 0) EXPECT_NE(exact_hash, GetDecodedAudioMD5(i - 1)); - } - } - - size_t decoded_audio_size() const { return decoded_audio_.size(); } - base::TimeDelta start_timestamp() const { return start_timestamp_; } - const scoped_refptr<AudioBuffer>& decoded_audio(size_t i) { - return decoded_audio_[i]; - } - DecodeStatus last_decode_status() const { return last_decode_status_; } - - private: - base::MessageLoop message_loop_; - scoped_refptr<DecoderBuffer> data_; - std::unique_ptr<InMemoryUrlProtocol> protocol_; - std::unique_ptr<AudioFileReader> reader_; - - std::unique_ptr<AudioDecoder> decoder_; - bool pending_decode_; - bool pending_reset_; - DecodeStatus last_decode_status_; - - std::deque<scoped_refptr<AudioBuffer> > decoded_audio_; - base::TimeDelta start_timestamp_; - - DISALLOW_COPY_AND_ASSIGN(AudioDecoderTest); -}; - -class OpusAudioDecoderBehavioralTest : public AudioDecoderTest {}; -class FFmpegAudioDecoderBehavioralTest : public AudioDecoderTest {}; - -TEST_P(AudioDecoderTest, Initialize) { - SKIP_TEST_IF_NO_MEDIA_CODEC(); - ASSERT_NO_FATAL_FAILURE(Initialize()); -} - -// Verifies decode audio as well as the Decode() -> Reset() sequence. -TEST_P(AudioDecoderTest, ProduceAudioSamples) { - SKIP_TEST_IF_NO_MEDIA_CODEC(); - ASSERT_NO_FATAL_FAILURE(Initialize()); - - // Run the test multiple times with a seek back to the beginning in between. - std::vector<std::string> decoded_audio_md5_hashes; - for (int i = 0; i < 2; ++i) { - // Run decoder until we get at least |kDecodeRuns| output buffers. - // Keeping Decode() in a loop seems to be the simplest way to guarantee that - // the predefined number of output buffers are produced without draining - // (i.e. decoding EOS). - do { - Decode(); - ASSERT_EQ(last_decode_status(), DecodeStatus::OK); - } while (decoded_audio_size() < kDecodeRuns); - - // With MediaCodecAudioDecoder the output buffers might appear after - // some delay. Since we keep decoding in a loop, the number of output - // buffers when they eventually appear might exceed |kDecodeRuns|. - ASSERT_LE(kDecodeRuns, decoded_audio_size()); - - // On the first pass record the exact MD5 hash for each decoded buffer. - if (i == 0) { - for (size_t j = 0; j < kDecodeRuns; ++j) - decoded_audio_md5_hashes.push_back(GetDecodedAudioMD5(j)); - } - - // On the first pass verify the basic audio hash and sample info. On the - // second, verify the exact MD5 sum for each packet. It shouldn't change. - for (size_t j = 0; j < kDecodeRuns; ++j) { - SCOPED_TRACE(base::StringPrintf("i = %d, j = %" PRIuS, i, j)); - ExpectDecodedAudio(j, i == 0 ? "" : decoded_audio_md5_hashes[j]); - } - - SendEndOfStream(); - - // Seek back to the beginning. Calls Reset() on the decoder. - Seek(start_timestamp()); - } -} - -TEST_P(AudioDecoderTest, Decode) { - SKIP_TEST_IF_NO_MEDIA_CODEC(); - ASSERT_NO_FATAL_FAILURE(Initialize()); - Decode(); - EXPECT_EQ(DecodeStatus::OK, last_decode_status()); -} - -TEST_P(AudioDecoderTest, Reset) { - SKIP_TEST_IF_NO_MEDIA_CODEC(); - ASSERT_NO_FATAL_FAILURE(Initialize()); - Reset(); -} - -TEST_P(AudioDecoderTest, NoTimestamp) { - SKIP_TEST_IF_NO_MEDIA_CODEC(); - ASSERT_NO_FATAL_FAILURE(Initialize()); - scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(0)); - buffer->set_timestamp(kNoTimestamp); - DecodeBuffer(buffer); - EXPECT_EQ(DecodeStatus::DECODE_ERROR, last_decode_status()); -} - -TEST_P(OpusAudioDecoderBehavioralTest, InitializeWithNoCodecDelay) { - ASSERT_EQ(GetParam().decoder_type, OPUS); - std::vector<uint8_t> extra_data(kOpusExtraData, - kOpusExtraData + arraysize(kOpusExtraData)); - AudioDecoderConfig decoder_config; - decoder_config.Initialize(kCodecOpus, kSampleFormatF32, CHANNEL_LAYOUT_STEREO, - 48000, extra_data, Unencrypted(), - base::TimeDelta::FromMilliseconds(80), 0); - InitializeDecoder(decoder_config); -} - -TEST_P(OpusAudioDecoderBehavioralTest, InitializeWithBadCodecDelay) { - ASSERT_EQ(GetParam().decoder_type, OPUS); - std::vector<uint8_t> extra_data(kOpusExtraData, - kOpusExtraData + arraysize(kOpusExtraData)); - AudioDecoderConfig decoder_config; - decoder_config.Initialize( - kCodecOpus, kSampleFormatF32, CHANNEL_LAYOUT_STEREO, 48000, extra_data, - Unencrypted(), base::TimeDelta::FromMilliseconds(80), - // Use a different codec delay than in the extradata. - 100); - InitializeDecoderWithResult(decoder_config, true); -} - -#if defined(OPUS_FIXED_POINT) -const DecodedBufferExpectations kSfxOpusExpectations[] = { - {0, 13500, "-2.70,-1.41,-0.78,-1.27,-2.56,-3.73,"}, - {13500, 20000, "5.48,5.93,6.05,5.83,5.54,5.46,"}, - {33500, 20000, "-3.44,-3.34,-3.57,-4.11,-4.74,-5.13,"}, -}; -#else -const DecodedBufferExpectations kSfxOpusExpectations[] = { - {0, 13500, "-2.70,-1.41,-0.78,-1.27,-2.56,-3.73,"}, - {13500, 20000, "5.48,5.93,6.04,5.83,5.54,5.45,"}, - {33500, 20000, "-3.45,-3.35,-3.57,-4.12,-4.74,-5.14,"}, -}; -#endif - -const DecodedBufferExpectations kBearOpusExpectations[] = { - {500, 3500, "-0.26,0.87,1.36,0.84,-0.30,-1.22,"}, - {4000, 10000, "0.09,0.23,0.21,0.03,-0.17,-0.24,"}, - {14000, 10000, "0.10,0.24,0.23,0.04,-0.14,-0.23,"}, -}; - -const DecoderTestData kOpusTests[] = { - {OPUS, kCodecOpus, "sfx-opus.ogg", kSfxOpusExpectations, -312, 48000, - CHANNEL_LAYOUT_MONO}, - {OPUS, kCodecOpus, "bear-opus.ogg", kBearOpusExpectations, 24, 48000, - CHANNEL_LAYOUT_STEREO}, -}; - -// Dummy data for behavioral tests. -const DecoderTestData kOpusBehavioralTest[] = { - {OPUS, kUnknownAudioCodec, "", NULL, 0, 0, CHANNEL_LAYOUT_NONE}, -}; - -INSTANTIATE_TEST_CASE_P(OpusAudioDecoderTest, AudioDecoderTest, - testing::ValuesIn(kOpusTests)); -INSTANTIATE_TEST_CASE_P(OpusAudioDecoderBehavioralTest, - OpusAudioDecoderBehavioralTest, - testing::ValuesIn(kOpusBehavioralTest)); - -#if defined(OS_ANDROID) -#if defined(USE_PROPRIETARY_CODECS) -const DecodedBufferExpectations kSfxAdtsMcExpectations[] = { - {0, 23219, "-1.80,-1.49,-0.23,1.11,1.54,-0.11,"}, - {23219, 23219, "-1.90,-1.53,-0.15,1.28,1.23,-0.33,"}, - {46439, 23219, "0.54,0.88,2.19,3.54,3.24,1.63,"}, -}; - -const DecodedBufferExpectations kHeAacMcExpectations[] = { - {0, 42666, "-1.76,-0.12,1.72,1.45,0.10,-1.32,"}, - {42666, 42666, "-1.78,-0.13,1.70,1.44,0.09,-1.32,"}, - {85333, 42666, "-1.78,-0.13,1.70,1.44,0.08,-1.33,"}, -}; -#endif - -const DecoderTestData kMediaCodecTests[] = { - {MEDIA_CODEC, kCodecOpus, "bear-opus.ogg", kBearOpusExpectations, 24, 48000, - CHANNEL_LAYOUT_STEREO}, -#if defined(USE_PROPRIETARY_CODECS) - {MEDIA_CODEC, kCodecAAC, "sfx.adts", kSfxAdtsMcExpectations, 0, 44100, - CHANNEL_LAYOUT_MONO}, - {MEDIA_CODEC, kCodecAAC, "bear-audio-implicit-he-aac-v2.aac", - kHeAacMcExpectations, 0, 24000, CHANNEL_LAYOUT_MONO}, -#endif -}; - -INSTANTIATE_TEST_CASE_P(MediaCodecAudioDecoderTest, AudioDecoderTest, - testing::ValuesIn(kMediaCodecTests)); -#endif // defined(OS_ANDROID) - -#if defined(USE_PROPRIETARY_CODECS) -const DecodedBufferExpectations kSfxMp3Expectations[] = { - {0, 1065, "2.81,3.99,4.53,4.10,3.08,2.46,"}, - {1065, 26122, "-3.81,-4.14,-3.90,-3.36,-3.03,-3.23,"}, - {27188, 26122, "4.24,3.95,4.22,4.78,5.13,4.93,"}, -}; - -const DecodedBufferExpectations kSfxAdtsExpectations[] = { - {0, 23219, "-1.90,-1.53,-0.15,1.28,1.23,-0.33,"}, - {23219, 23219, "0.54,0.88,2.19,3.54,3.24,1.63,"}, - {46439, 23219, "1.42,1.69,2.95,4.23,4.02,2.36,"}, -}; -#endif - -#if defined(OS_CHROMEOS) -const DecodedBufferExpectations kSfxFlacExpectations[] = { - {0, 104489, "-2.42,-1.12,0.71,1.70,1.09,-0.68,"}, - {104489, 104489, "-1.99,-0.67,1.18,2.19,1.60,-0.16,"}, - {208979, 79433, "2.84,2.70,3.23,4.06,4.59,4.44,"}, -}; -#endif - -const DecodedBufferExpectations kSfxWaveExpectations[] = { - {0, 23219, "-1.23,-0.87,0.47,1.85,1.88,0.29,"}, - {23219, 23219, "0.75,1.10,2.43,3.78,3.53,1.93,"}, - {46439, 23219, "1.27,1.56,2.83,4.13,3.87,2.23,"}, -}; - -const DecodedBufferExpectations kFourChannelWaveExpectations[] = { - {0, 11609, "-1.68,1.68,0.89,-3.45,1.52,1.15,"}, - {11609, 11609, "43.26,9.06,18.27,35.98,19.45,7.46,"}, - {23219, 11609, "36.37,9.45,16.04,27.67,18.81,10.15,"}, -}; - -const DecodedBufferExpectations kSfxOggExpectations[] = { - {0, 13061, "-0.33,1.25,2.86,3.26,2.09,0.14,"}, - {13061, 23219, "-2.79,-2.42,-1.06,0.33,0.93,-0.64,"}, - {36281, 23219, "-1.19,-0.80,0.57,1.97,2.08,0.51,"}, -}; - -const DecodedBufferExpectations kBearOgvExpectations[] = { - {0, 13061, "-1.25,0.10,2.11,2.29,1.50,-0.68,"}, - {13061, 23219, "-1.80,-1.41,-0.13,1.30,1.65,0.01,"}, - {36281, 23219, "-1.43,-1.25,0.11,1.29,1.86,0.14,"}, -}; - -const DecoderTestData kFFmpegTests[] = { -#if defined(USE_PROPRIETARY_CODECS) - {FFMPEG, kCodecMP3, "sfx.mp3", kSfxMp3Expectations, 0, 44100, - CHANNEL_LAYOUT_MONO}, - {FFMPEG, kCodecAAC, "sfx.adts", kSfxAdtsExpectations, 0, 44100, - CHANNEL_LAYOUT_MONO}, -#endif -#if defined(OS_CHROMEOS) - {FFMPEG, kCodecFLAC, "sfx.flac", kSfxFlacExpectations, 0, 44100, - CHANNEL_LAYOUT_MONO}, -#endif - {FFMPEG, kCodecPCM, "sfx_f32le.wav", kSfxWaveExpectations, 0, 44100, - CHANNEL_LAYOUT_MONO}, - {FFMPEG, kCodecPCM, "4ch.wav", kFourChannelWaveExpectations, 0, 44100, - CHANNEL_LAYOUT_QUAD}, - {FFMPEG, kCodecVorbis, "sfx.ogg", kSfxOggExpectations, 0, 44100, - CHANNEL_LAYOUT_MONO}, - // Note: bear.ogv is incorrectly muxed such that valid samples are given - // negative timestamps, this marks them for discard per the ogg vorbis spec. - {FFMPEG, kCodecVorbis, "bear.ogv", kBearOgvExpectations, -704, 44100, - CHANNEL_LAYOUT_STEREO}, -}; - -// Dummy data for behavioral tests. -const DecoderTestData kFFmpegBehavioralTest[] = { - {FFMPEG, kUnknownAudioCodec, "", NULL, 0, 0, CHANNEL_LAYOUT_NONE}, -}; - -INSTANTIATE_TEST_CASE_P(FFmpegAudioDecoderTest, AudioDecoderTest, - testing::ValuesIn(kFFmpegTests)); -INSTANTIATE_TEST_CASE_P(FFmpegAudioDecoderBehavioralTest, - FFmpegAudioDecoderBehavioralTest, - testing::ValuesIn(kFFmpegBehavioralTest)); - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/audio_file_reader.cc b/src/cobalt/media/filters/audio_file_reader.cc deleted file mode 100644 index 0aeb83b..0000000 --- a/src/cobalt/media/filters/audio_file_reader.cc +++ /dev/null
@@ -1,299 +0,0 @@ -// Copyright (c) 2012 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/filters/audio_file_reader.h" - -#include <cmath> - -#include "base/logging.h" -#include "base/numerics/safe_math.h" -#include "base/time.h" -#include "cobalt/media/base/audio_bus.h" -#include "cobalt/media/ffmpeg/ffmpeg_common.h" -#include "starboard/memory.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -// AAC(M4A) decoding specific constants. -static const int kAACPrimingFrameCount = 2112; -static const int kAACRemainderFrameCount = 519; - -AudioFileReader::AudioFileReader(FFmpegURLProtocol* protocol) - : codec_context_(NULL), - stream_index_(0), - protocol_(protocol), - audio_codec_(kUnknownAudioCodec), - channels_(0), - sample_rate_(0), - av_sample_format_(0) {} - -AudioFileReader::~AudioFileReader() { Close(); } - -bool AudioFileReader::Open() { - if (!OpenDemuxer()) return false; - return OpenDecoder(); -} - -bool AudioFileReader::OpenDemuxer() { - glue_.reset(new FFmpegGlue(protocol_)); - AVFormatContext* format_context = glue_->format_context(); - - // Open FFmpeg AVFormatContext. - if (!glue_->OpenContext()) { - DLOG(WARNING) << "AudioFileReader::Open() : error in avformat_open_input()"; - return false; - } - - // Get the codec context. - codec_context_ = NULL; - for (size_t i = 0; i < format_context->nb_streams; ++i) { - AVCodecContext* c = format_context->streams[i]->codec; - if (c->codec_type == AVMEDIA_TYPE_AUDIO) { - codec_context_ = c; - stream_index_ = i; - break; - } - } - - // Get the codec. - if (!codec_context_) return false; - - const int result = avformat_find_stream_info(format_context, NULL); - if (result < 0) { - DLOG(WARNING) - << "AudioFileReader::Open() : error in avformat_find_stream_info()"; - return false; - } - - return true; -} - -bool AudioFileReader::OpenDecoder() { - AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); - if (codec) { - // MP3 decodes to S16P which we don't support, tell it to use S16 instead. - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) - codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16; - - const int result = avcodec_open2(codec_context_, codec, NULL); - if (result < 0) { - DLOG(WARNING) << "AudioFileReader::Open() : could not open codec -" - << " result: " << result; - return false; - } - - // Ensure avcodec_open2() respected our format request. - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) { - DLOG(ERROR) << "AudioFileReader::Open() : unable to configure a" - << " supported sample format - " - << codec_context_->sample_fmt; - return false; - } - } else { - DLOG(WARNING) << "AudioFileReader::Open() : could not find codec."; - return false; - } - - // Verify the channel layout is supported by Chrome. Acts as a sanity check - // against invalid files. See http://crbug.com/171962 - if (ChannelLayoutToChromeChannelLayout(codec_context_->channel_layout, - codec_context_->channels) == - CHANNEL_LAYOUT_UNSUPPORTED) { - return false; - } - - // Store initial values to guard against midstream configuration changes. - channels_ = codec_context_->channels; - audio_codec_ = CodecIDToAudioCodec(codec_context_->codec_id); - sample_rate_ = codec_context_->sample_rate; - av_sample_format_ = codec_context_->sample_fmt; - return true; -} - -void AudioFileReader::Close() { - // |codec_context_| is a stream inside glue_->format_context(), so it is - // closed when |glue_| is disposed. - glue_.reset(); - codec_context_ = NULL; -} - -int AudioFileReader::Read(AudioBus* audio_bus) { - DCHECK(glue_.get() && codec_context_) - << "AudioFileReader::Read() : reader is not opened!"; - - DCHECK_EQ(audio_bus->channels(), channels()); - if (audio_bus->channels() != channels()) return 0; - - size_t bytes_per_sample = av_get_bytes_per_sample(codec_context_->sample_fmt); - - // Holds decoded audio. - std::unique_ptr<AVFrame, ScopedPtrAVFreeFrame> av_frame(av_frame_alloc()); - - // Read until we hit EOF or we've read the requested number of frames. - AVPacket packet; - int current_frame = 0; - bool continue_decoding = true; - - while (current_frame < audio_bus->frames() && continue_decoding && - ReadPacket(&packet)) { - // Make a shallow copy of packet so we can slide packet.data as frames are - // decoded from the packet; otherwise av_packet_unref() will corrupt memory. - AVPacket packet_temp = packet; - do { - // Reset frame to default values. - av_frame_unref(av_frame.get()); - - int frame_decoded = 0; - int result = avcodec_decode_audio4(codec_context_, av_frame.get(), - &frame_decoded, &packet_temp); - - if (result < 0) { - DLOG(WARNING) - << "AudioFileReader::Read() : error in avcodec_decode_audio4() -" - << result; - break; - } - - // Update packet size and data pointer in case we need to call the decoder - // with the remaining bytes from this packet. - packet_temp.size -= result; - packet_temp.data += result; - - if (!frame_decoded) continue; - - // Determine the number of sample-frames we just decoded. Check overflow. - int frames_read = av_frame->nb_samples; - if (frames_read < 0) { - continue_decoding = false; - break; - } - -#ifdef CHROMIUM_NO_AVFRAME_CHANNELS - int channels = - av_get_channel_layout_nb_channels(av_frame->channel_layout); -#else - int channels = av_frame->channels; -#endif - if (av_frame->sample_rate != sample_rate_ || channels != channels_ || - av_frame->format != av_sample_format_) { - DLOG(ERROR) << "Unsupported midstream configuration change!" - << " Sample Rate: " << av_frame->sample_rate << " vs " - << sample_rate_ << ", Channels: " << channels << " vs " - << channels_ << ", Sample Format: " << av_frame->format - << " vs " << av_sample_format_; - - // This is an unrecoverable error, so bail out. - continue_decoding = false; - break; - } - - // Truncate, if necessary, if the destination isn't big enough. - if (current_frame + frames_read > audio_bus->frames()) { - DLOG(ERROR) << "Truncating decoded data due to output size."; - frames_read = audio_bus->frames() - current_frame; - } - - // Deinterleave each channel and convert to 32bit floating-point with - // nominal range -1.0 -> +1.0. If the output is already in float planar - // format, just copy it into the AudioBus. - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { - float* decoded_audio_data = reinterpret_cast<float*>(av_frame->data[0]); - int channels = audio_bus->channels(); - for (int ch = 0; ch < channels; ++ch) { - float* bus_data = audio_bus->channel(ch) + current_frame; - for (int i = 0, offset = ch; i < frames_read; - ++i, offset += channels) { - bus_data[i] = decoded_audio_data[offset]; - } - } - } else if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP) { - for (int ch = 0; ch < audio_bus->channels(); ++ch) { - SbMemoryCopy(audio_bus->channel(ch) + current_frame, - av_frame->extended_data[ch], - sizeof(float) * frames_read); - } - } else { - audio_bus->FromInterleavedPartial(av_frame->data[0], current_frame, - frames_read, bytes_per_sample); - } - - current_frame += frames_read; - } while (packet_temp.size > 0); - av_packet_unref(&packet); - } - - // Zero any remaining frames. - audio_bus->ZeroFramesPartial(current_frame, - audio_bus->frames() - current_frame); - - // Returns the actual number of sample-frames decoded. - // Ideally this represents the "true" exact length of the file. - return current_frame; -} - -base::TimeDelta AudioFileReader::GetDuration() const { - const AVRational av_time_base = {1, AV_TIME_BASE}; - - // Estimated duration in micro seconds. - base::CheckedNumeric<int64_t> estimated_duration_us = - glue_->format_context()->duration; - - if (audio_codec_ == kCodecAAC) { - // For certain AAC-encoded files, FFMPEG's estimated frame count might not - // be sufficient to capture the entire audio content that we want. This is - // especially noticeable for short files (< 10ms) resulting in silence - // throughout the decoded buffer. Thus we add the priming frames and the - // remainder frames to the estimation. - // (See: crbug.com/513178) - estimated_duration_us += - ceil(1000000.0 * static_cast<double>(kAACPrimingFrameCount + - kAACRemainderFrameCount) / - sample_rate()); - } else { - // Add one microsecond to avoid rounding-down errors which can occur when - // |duration| has been calculated from an exact number of sample-frames. - // One microsecond is much less than the time of a single sample-frame - // at any real-world sample-rate. - estimated_duration_us += 1; - } - - return ConvertFromTimeBase(av_time_base, estimated_duration_us.ValueOrDie()); -} - -int AudioFileReader::GetNumberOfFrames() const { - return static_cast<int>(ceil(GetDuration().InSecondsF() * sample_rate())); -} - -bool AudioFileReader::OpenDemuxerForTesting() { return OpenDemuxer(); } - -bool AudioFileReader::ReadPacketForTesting(AVPacket* output_packet) { - return ReadPacket(output_packet); -} - -bool AudioFileReader::ReadPacket(AVPacket* output_packet) { - while (av_read_frame(glue_->format_context(), output_packet) >= 0) { - // Skip packets from other streams. - if (output_packet->stream_index != stream_index_) { - av_packet_unref(output_packet); - continue; - } - return true; - } - return false; -} - -bool AudioFileReader::SeekForTesting(base::TimeDelta seek_time) { - return av_seek_frame(glue_->format_context(), stream_index_, - ConvertToTimeBase(codec_context_->time_base, seek_time), - AVSEEK_FLAG_BACKWARD) >= 0; -} - -const AVStream* AudioFileReader::GetAVStreamForTesting() const { - return glue_->format_context()->streams[stream_index_]; -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/audio_file_reader.h b/src/cobalt/media/filters/audio_file_reader.h deleted file mode 100644 index 5ac77af..0000000 --- a/src/cobalt/media/filters/audio_file_reader.h +++ /dev/null
@@ -1,106 +0,0 @@ -// Copyright (c) 2011 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. - -#ifndef COBALT_MEDIA_FILTERS_AUDIO_FILE_READER_H_ -#define COBALT_MEDIA_FILTERS_AUDIO_FILE_READER_H_ - -#include <memory> - -#include "base/basictypes.h" -#include "cobalt/media/base/audio_codecs.h" -#include "cobalt/media/base/media_export.h" -#include "cobalt/media/filters/ffmpeg_glue.h" - -struct AVCodecContext; -struct AVPacket; -struct AVStream; - -namespace base { -class TimeDelta; -} - -namespace cobalt { -namespace media { - -class AudioBus; -class FFmpegURLProtocol; - -class MEDIA_EXPORT AudioFileReader { - public: - // Audio file data will be read using the given protocol. - // The AudioFileReader does not take ownership of |protocol| and - // simply maintains a weak reference to it. - explicit AudioFileReader(FFmpegURLProtocol* protocol); - virtual ~AudioFileReader(); - - // Open() reads the audio data format so that the sample_rate(), - // channels(), GetDuration(), and GetNumberOfFrames() methods can be called. - // It returns |true| on success. - bool Open(); - void Close(); - - // After a call to Open(), attempts to fully fill |audio_bus| with decoded - // audio data. Any unfilled frames will be zeroed out. - // |audio_data| must be of the same size as channels(). - // The audio data will be decoded as floating-point linear PCM with - // a nominal range of -1.0 -> +1.0. - // Returns the number of sample-frames actually read which will always be - // <= audio_bus->frames() - int Read(AudioBus* audio_bus); - - // These methods can be called once Open() has been called. - int channels() const { return channels_; } - int sample_rate() const { return sample_rate_; } - - // Please note that GetDuration() and GetNumberOfFrames() attempt to be - // accurate, but are only estimates. For some encoded formats, the actual - // duration of the file can only be determined once all the file data has been - // read. The Read() method returns the actual number of sample-frames it has - // read. - base::TimeDelta GetDuration() const; - int GetNumberOfFrames() const; - - // The methods below are helper methods which allow AudioFileReader to double - // as a test utility for demuxing audio files. - // -------------------------------------------------------------------------- - - // Similar to Open() but does not initialize the decoder. - bool OpenDemuxerForTesting(); - - // Returns true if a packet could be demuxed from the first audio stream in - // the file, |output_packet| will contain the demuxed packet then. - bool ReadPacketForTesting(AVPacket* output_packet); - - // Seeks to the given point and returns true if successful. |seek_time| will - // be converted to the stream's time base automatically. - bool SeekForTesting(base::TimeDelta seek_time); - - const AVStream* GetAVStreamForTesting() const; - const AVCodecContext* codec_context_for_testing() const { - return codec_context_; - } - - private: - bool OpenDemuxer(); - bool OpenDecoder(); - bool ReadPacket(AVPacket* output_packet); - - std::unique_ptr<FFmpegGlue> glue_; - AVCodecContext* codec_context_; - int stream_index_; - FFmpegURLProtocol* protocol_; - AudioCodec audio_codec_; - int channels_; - int sample_rate_; - - // AVSampleFormat initially requested; not Chrome's SampleFormat. - int av_sample_format_; - - DISALLOW_COPY_AND_ASSIGN(AudioFileReader); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_AUDIO_FILE_READER_H_
diff --git a/src/cobalt/media/filters/audio_file_reader_unittest.cc b/src/cobalt/media/filters/audio_file_reader_unittest.cc deleted file mode 100644 index db93e6f..0000000 --- a/src/cobalt/media/filters/audio_file_reader_unittest.cc +++ /dev/null
@@ -1,197 +0,0 @@ -// Copyright (c) 2012 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/filters/audio_file_reader.h" - -#include <memory> -#include <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/md5.h" -#include "build/build_config.h" -#include "cobalt/media/base/audio_bus.h" -#include "cobalt/media/base/audio_hash.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/test_data_util.h" -#include "cobalt/media/ffmpeg/ffmpeg_common.h" -#include "cobalt/media/filters/in_memory_url_protocol.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cobalt { -namespace media { - -class AudioFileReaderTest : public testing::Test { - public: - AudioFileReaderTest() : packet_verification_disabled_(false) {} - ~AudioFileReaderTest() override {} - - void Initialize(const char* filename) { - data_ = ReadTestDataFile(filename); - protocol_.reset( - new InMemoryUrlProtocol(data_->data(), data_->data_size(), false)); - reader_.reset(new AudioFileReader(protocol_.get())); - } - - // Reads and the entire file provided to Initialize(). - void ReadAndVerify(const char* expected_audio_hash, int expected_frames) { - std::unique_ptr<AudioBus> decoded_audio_data = - AudioBus::Create(reader_->channels(), reader_->GetNumberOfFrames()); - int actual_frames = reader_->Read(decoded_audio_data.get()); - ASSERT_LE(actual_frames, decoded_audio_data->frames()); - ASSERT_EQ(expected_frames, actual_frames); - - AudioHash audio_hash; - audio_hash.Update(decoded_audio_data.get(), actual_frames); - EXPECT_EQ(expected_audio_hash, audio_hash.ToString()); - } - - // Verify packets are consistent across demuxer runs. Reads the first few - // packets and then seeks back to the start timestamp and verifies that the - // hashes match on the packets just read. - void VerifyPackets() { - const int kReads = 3; - const int kTestPasses = 2; - - AVPacket packet; - base::TimeDelta start_timestamp; - std::vector<std::string> packet_md5_hashes_; - for (int i = 0; i < kTestPasses; ++i) { - for (int j = 0; j < kReads; ++j) { - ASSERT_TRUE(reader_->ReadPacketForTesting(&packet)); - - // Remove metadata from the packet data section before hashing. - av_packet_split_side_data(&packet); - - // On the first pass save the MD5 hash of each packet, on subsequent - // passes ensure it matches. - const std::string md5_hash = base::MD5String(base::StringPiece( - reinterpret_cast<char*>(packet.data), packet.size)); - if (i == 0) { - packet_md5_hashes_.push_back(md5_hash); - if (j == 0) { - start_timestamp = ConvertFromTimeBase( - reader_->codec_context_for_testing()->time_base, packet.pts); - } - } else { - EXPECT_EQ(packet_md5_hashes_[j], md5_hash) << "j = " << j; - } - - av_packet_unref(&packet); - } - ASSERT_TRUE(reader_->SeekForTesting(start_timestamp)); - } - } - - void RunTest(const char* fn, const char* hash, int channels, int sample_rate, - base::TimeDelta duration, int frames, int trimmed_frames) { - Initialize(fn); - ASSERT_TRUE(reader_->Open()); - EXPECT_EQ(channels, reader_->channels()); - EXPECT_EQ(sample_rate, reader_->sample_rate()); - EXPECT_EQ(duration.InMicroseconds(), - reader_->GetDuration().InMicroseconds()); - EXPECT_EQ(frames, reader_->GetNumberOfFrames()); - if (!packet_verification_disabled_) - ASSERT_NO_FATAL_FAILURE(VerifyPackets()); - ReadAndVerify(hash, trimmed_frames); - } - - void RunTestFailingDemux(const char* fn) { - Initialize(fn); - EXPECT_FALSE(reader_->Open()); - } - - void RunTestFailingDecode(const char* fn) { - Initialize(fn); - EXPECT_TRUE(reader_->Open()); - std::unique_ptr<AudioBus> decoded_audio_data = - AudioBus::Create(reader_->channels(), reader_->GetNumberOfFrames()); - EXPECT_EQ(reader_->Read(decoded_audio_data.get()), 0); - } - - void disable_packet_verification() { packet_verification_disabled_ = true; } - - protected: - scoped_refptr<DecoderBuffer> data_; - std::unique_ptr<InMemoryUrlProtocol> protocol_; - std::unique_ptr<AudioFileReader> reader_; - bool packet_verification_disabled_; - - private: - DISALLOW_COPY_AND_ASSIGN(AudioFileReaderTest); -}; - -TEST_F(AudioFileReaderTest, WithoutOpen) { Initialize("bear.ogv"); } - -TEST_F(AudioFileReaderTest, InvalidFile) { - RunTestFailingDemux("ten_byte_file"); -} - -TEST_F(AudioFileReaderTest, WithVideo) { - RunTest("bear.ogv", "-2.49,-0.75,0.38,1.60,0.70,-1.22,", 2, 44100, - base::TimeDelta::FromMicroseconds(1011520), 44609, 44609); -} - -TEST_F(AudioFileReaderTest, Vorbis) { - RunTest("sfx.ogg", "4.36,4.81,4.84,4.45,4.61,4.63,", 1, 44100, - base::TimeDelta::FromMicroseconds(350001), 15436, 15436); -} - -TEST_F(AudioFileReaderTest, WaveU8) { - RunTest("sfx_u8.wav", "-1.23,-1.57,-1.14,-0.91,-0.87,-0.07,", 1, 44100, - base::TimeDelta::FromMicroseconds(288414), 12720, 12719); -} - -TEST_F(AudioFileReaderTest, WaveS16LE) { - RunTest("sfx_s16le.wav", "3.05,2.87,3.00,3.32,3.58,4.08,", 1, 44100, - base::TimeDelta::FromMicroseconds(288414), 12720, 12719); -} - -TEST_F(AudioFileReaderTest, WaveS24LE) { - RunTest("sfx_s24le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100, - base::TimeDelta::FromMicroseconds(288414), 12720, 12719); -} - -TEST_F(AudioFileReaderTest, WaveF32LE) { - RunTest("sfx_f32le.wav", "3.03,2.86,2.99,3.31,3.57,4.06,", 1, 44100, - base::TimeDelta::FromMicroseconds(288414), 12720, 12719); -} - -#if defined(USE_PROPRIETARY_CODECS) -TEST_F(AudioFileReaderTest, MP3) { - RunTest("sfx.mp3", "1.30,2.72,4.56,5.08,3.74,2.03,", 1, 44100, - base::TimeDelta::FromMicroseconds(313470), 13825, 11025); -} - -TEST_F(AudioFileReaderTest, CorruptMP3) { - // Disable packet verification since the file is corrupt and FFmpeg does not - // make any guarantees on packet consistency in this case. - disable_packet_verification(); - RunTest("corrupt.mp3", "-4.95,-2.95,-0.44,1.16,0.31,-2.21,", 1, 44100, - base::TimeDelta::FromMicroseconds(1018801), 44930, 44928); -} - -TEST_F(AudioFileReaderTest, AAC) { - RunTest("sfx.m4a", "1.81,1.66,2.32,3.27,4.46,3.36,", 1, 44100, - base::TimeDelta::FromMicroseconds(371660), 16391, 13312); -} - -TEST_F(AudioFileReaderTest, MidStreamConfigChangesFail) { - RunTestFailingDecode("midstream_config_change.mp3"); -} -#endif - -TEST_F(AudioFileReaderTest, VorbisInvalidChannelLayout) { - RunTestFailingDemux("9ch.ogg"); -} - -TEST_F(AudioFileReaderTest, WaveValidFourChannelLayout) { - RunTest("4ch.wav", "131.71,38.02,130.31,44.89,135.98,42.52,", 4, 44100, - base::TimeDelta::FromMicroseconds(100001), 4411, 4410); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/audio_renderer_algorithm.cc b/src/cobalt/media/filters/audio_renderer_algorithm.cc deleted file mode 100644 index 5143d7b..0000000 --- a/src/cobalt/media/filters/audio_renderer_algorithm.cc +++ /dev/null
@@ -1,396 +0,0 @@ -// Copyright (c) 2012 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/filters/audio_renderer_algorithm.h" - -#include <algorithm> -#include <cmath> - -#include "base/logging.h" -#include "cobalt/media/base/audio_bus.h" -#include "cobalt/media/base/limits.h" -#include "cobalt/media/filters/wsola_internals.h" -#include "starboard/memory.h" - -namespace cobalt { -namespace media { - -// Waveform Similarity Overlap-and-add (WSOLA). -// -// One WSOLA iteration -// -// 1) Extract |target_block_| as input frames at indices -// [|target_block_index_|, |target_block_index_| + |ola_window_size_|). -// Note that |target_block_| is the "natural" continuation of the output. -// -// 2) Extract |search_block_| as input frames at indices -// [|search_block_index_|, -// |search_block_index_| + |num_candidate_blocks_| + |ola_window_size_|). -// -// 3) Find a block within the |search_block_| that is most similar -// to |target_block_|. Let |optimal_index| be the index of such block and -// write it to |optimal_block_|. -// -// 4) Update: -// |optimal_block_| = |transition_window_| * |target_block_| + -// (1 - |transition_window_|) * |optimal_block_|. -// -// 5) Overlap-and-add |optimal_block_| to the |wsola_output_|. -// -// 6) Update: -// |target_block_| = |optimal_index| + |ola_window_size_| / 2. -// |output_index_| = |output_index_| + |ola_window_size_| / 2, -// |search_block_center_offset_| = |output_index_| * |playback_rate|, and -// |search_block_index_| = |search_block_center_offset_| - -// |search_block_center_offset_|. - -// Max/min supported playback rates for fast/slow audio. Audio outside of these -// ranges are muted. -// Audio at these speeds would sound better under a frequency domain algorithm. -static const double kMinPlaybackRate = 0.5; -static const double kMaxPlaybackRate = 4.0; - -// Overlap-and-add window size in milliseconds. -static const int kOlaWindowSizeMs = 20; - -// Size of search interval in milliseconds. The search interval is -// [-delta delta] around |output_index_| * |playback_rate|. So the search -// interval is 2 * delta. -static const int kWsolaSearchIntervalMs = 30; - -// The maximum size in seconds for the |audio_buffer_|. Arbitrarily determined. -static const int kMaxCapacityInSeconds = 3; - -// The minimum size in ms for the |audio_buffer_|. Arbitrarily determined. -static const int kStartingCapacityInMs = 200; - -AudioRendererAlgorithm::AudioRendererAlgorithm() - : channels_(0), - samples_per_second_(0), - muted_partial_frame_(0), - capacity_(0), - output_time_(0.0), - search_block_center_offset_(0), - search_block_index_(0), - num_candidate_blocks_(0), - target_block_index_(0), - ola_window_size_(0), - ola_hop_size_(0), - num_complete_frames_(0), - initial_capacity_(0), - max_capacity_(0) {} - -AudioRendererAlgorithm::~AudioRendererAlgorithm() {} - -void AudioRendererAlgorithm::Initialize(const AudioParameters& params) { - CHECK(params.IsValid()); - - channels_ = params.channels(); - samples_per_second_ = params.sample_rate(); - initial_capacity_ = capacity_ = - std::max(params.frames_per_buffer() * 2, - ConvertMillisecondsToFrames(kStartingCapacityInMs)); - max_capacity_ = - std::max(initial_capacity_, kMaxCapacityInSeconds * samples_per_second_); - num_candidate_blocks_ = ConvertMillisecondsToFrames(kWsolaSearchIntervalMs); - ola_window_size_ = ConvertMillisecondsToFrames(kOlaWindowSizeMs); - - // Make sure window size in an even number. - ola_window_size_ += ola_window_size_ & 1; - ola_hop_size_ = ola_window_size_ / 2; - - // |num_candidate_blocks_| / 2 is the offset of the center of the search - // block to the center of the first (left most) candidate block. The offset - // of the center of a candidate block to its left most point is - // |ola_window_size_| / 2 - 1. Note that |ola_window_size_| is even and in - // our convention the center belongs to the left half, so we need to subtract - // one frame to get the correct offset. - // - // Search Block - // <-------------------------------------------> - // - // |ola_window_size_| / 2 - 1 - // <---- - // - // |num_candidate_blocks_| / 2 - // <---------------- - // center - // X----X----------------X---------------X-----X - // <----------> <----------> - // Candidate ... Candidate - // 1, ... |num_candidate_blocks_| - search_block_center_offset_ = - num_candidate_blocks_ / 2 + (ola_window_size_ / 2 - 1); - - ola_window_.reset(new float[ola_window_size_]); - internal::GetSymmetricHanningWindow(ola_window_size_, ola_window_.get()); - - transition_window_.reset(new float[ola_window_size_ * 2]); - internal::GetSymmetricHanningWindow(2 * ola_window_size_, - transition_window_.get()); - - wsola_output_ = AudioBus::Create(channels_, ola_window_size_ + ola_hop_size_); - wsola_output_->Zero(); // Initialize for overlap-and-add of the first block. - - // Auxiliary containers. - optimal_block_ = AudioBus::Create(channels_, ola_window_size_); - search_block_ = AudioBus::Create( - channels_, num_candidate_blocks_ + (ola_window_size_ - 1)); - target_block_ = AudioBus::Create(channels_, ola_window_size_); -} - -int AudioRendererAlgorithm::FillBuffer(AudioBus* dest, int dest_offset, - int requested_frames, - double playback_rate) { - if (playback_rate == 0) return 0; - - DCHECK_GT(playback_rate, 0); - DCHECK_EQ(channels_, dest->channels()); - - // Optimize the muted case to issue a single clear instead of performing - // the full crossfade and clearing each crossfaded frame. - if (playback_rate < kMinPlaybackRate || playback_rate > kMaxPlaybackRate) { - int frames_to_render = - std::min(static_cast<int>(audio_buffer_.frames() / playback_rate), - requested_frames); - - // Compute accurate number of frames to actually skip in the source data. - // Includes the leftover partial frame from last request. However, we can - // only skip over complete frames, so a partial frame may remain for next - // time. - muted_partial_frame_ += frames_to_render * playback_rate; - // Handle the case where muted_partial_frame_ rounds up to - // audio_buffer_.frames()+1. - int seek_frames = std::min(static_cast<int>(muted_partial_frame_), - audio_buffer_.frames()); - dest->ZeroFramesPartial(dest_offset, frames_to_render); - audio_buffer_.SeekFrames(seek_frames); - - // Determine the partial frame that remains to be skipped for next call. If - // the user switches back to playing, it may be off time by this partial - // frame, which would be undetectable. If they subsequently switch to - // another playback rate that mutes, the code will attempt to line up the - // frames again. - muted_partial_frame_ -= seek_frames; - return frames_to_render; - } - - int slower_step = ceil(ola_window_size_ * playback_rate); - int faster_step = ceil(ola_window_size_ / playback_rate); - - // Optimize the most common |playback_rate| ~= 1 case to use a single copy - // instead of copying frame by frame. - if (ola_window_size_ <= faster_step && slower_step >= ola_window_size_) { - const int frames_to_copy = - std::min(audio_buffer_.frames(), requested_frames); - const int frames_read = - audio_buffer_.ReadFrames(frames_to_copy, dest_offset, dest); - DCHECK_EQ(frames_read, frames_to_copy); - return frames_read; - } - - int rendered_frames = 0; - do { - rendered_frames += - WriteCompletedFramesTo(requested_frames - rendered_frames, - dest_offset + rendered_frames, dest); - } while (rendered_frames < requested_frames && - RunOneWsolaIteration(playback_rate)); - return rendered_frames; -} - -void AudioRendererAlgorithm::FlushBuffers() { - // Clear the queue of decoded packets (releasing the buffers). - audio_buffer_.Clear(); - output_time_ = 0.0; - search_block_index_ = 0; - target_block_index_ = 0; - wsola_output_->Zero(); - num_complete_frames_ = 0; - - // Reset |capacity_| so growth triggered by underflows doesn't penalize seek - // time. - capacity_ = initial_capacity_; -} - -void AudioRendererAlgorithm::EnqueueBuffer( - const scoped_refptr<AudioBuffer>& buffer_in) { - DCHECK(!buffer_in->end_of_stream()); - audio_buffer_.Append(buffer_in); -} - -bool AudioRendererAlgorithm::IsQueueFull() { - return audio_buffer_.frames() >= capacity_; -} - -void AudioRendererAlgorithm::IncreaseQueueCapacity() { - DCHECK_LE(capacity_, max_capacity_); - capacity_ = std::min(2 * capacity_, max_capacity_); -} - -int64_t AudioRendererAlgorithm::GetMemoryUsage() const { - return audio_buffer_.frames() * channels_ * sizeof(float); -} - -bool AudioRendererAlgorithm::CanPerformWsola() const { - const int search_block_size = num_candidate_blocks_ + (ola_window_size_ - 1); - const int frames = audio_buffer_.frames(); - return target_block_index_ + ola_window_size_ <= frames && - search_block_index_ + search_block_size <= frames; -} - -int AudioRendererAlgorithm::ConvertMillisecondsToFrames(int ms) const { - return ms * (samples_per_second_ / - static_cast<double>(base::Time::kMillisecondsPerSecond)); -} - -bool AudioRendererAlgorithm::RunOneWsolaIteration(double playback_rate) { - if (!CanPerformWsola()) return false; - - GetOptimalBlock(); - - // Overlap-and-add. - for (int k = 0; k < channels_; ++k) { - const float* const ch_opt_frame = optimal_block_->channel(k); - float* ch_output = wsola_output_->channel(k) + num_complete_frames_; - for (int n = 0; n < ola_hop_size_; ++n) { - ch_output[n] = ch_output[n] * ola_window_[ola_hop_size_ + n] + - ch_opt_frame[n] * ola_window_[n]; - } - - // Copy the second half to the output. - SbMemoryCopy(&ch_output[ola_hop_size_], &ch_opt_frame[ola_hop_size_], - sizeof(*ch_opt_frame) * ola_hop_size_); - } - - num_complete_frames_ += ola_hop_size_; - UpdateOutputTime(playback_rate, ola_hop_size_); - RemoveOldInputFrames(playback_rate); - return true; -} - -void AudioRendererAlgorithm::UpdateOutputTime(double playback_rate, - double time_change) { - output_time_ += time_change; - // Center of the search region, in frames. - const int search_block_center_index = - static_cast<int>(output_time_ * playback_rate + 0.5); - search_block_index_ = search_block_center_index - search_block_center_offset_; -} - -void AudioRendererAlgorithm::RemoveOldInputFrames(double playback_rate) { - const int earliest_used_index = - std::min(target_block_index_, search_block_index_); - if (earliest_used_index <= 0) return; // Nothing to remove. - - // Remove frames from input and adjust indices accordingly. - audio_buffer_.SeekFrames(earliest_used_index); - target_block_index_ -= earliest_used_index; - - // Adjust output index. - double output_time_change = - static_cast<double>(earliest_used_index) / playback_rate; - CHECK_GE(output_time_, output_time_change); - UpdateOutputTime(playback_rate, -output_time_change); -} - -int AudioRendererAlgorithm::WriteCompletedFramesTo(int requested_frames, - int dest_offset, - AudioBus* dest) { - int rendered_frames = std::min(num_complete_frames_, requested_frames); - - if (rendered_frames == 0) - return 0; // There is nothing to read from |wsola_output_|, return. - - wsola_output_->CopyPartialFramesTo(0, rendered_frames, dest_offset, dest); - - // Remove the frames which are read. - int frames_to_move = wsola_output_->frames() - rendered_frames; - for (int k = 0; k < channels_; ++k) { - float* ch = wsola_output_->channel(k); - SbMemoryMove(ch, &ch[rendered_frames], sizeof(*ch) * frames_to_move); - } - num_complete_frames_ -= rendered_frames; - return rendered_frames; -} - -bool AudioRendererAlgorithm::TargetIsWithinSearchRegion() const { - const int search_block_size = num_candidate_blocks_ + (ola_window_size_ - 1); - - return target_block_index_ >= search_block_index_ && - target_block_index_ + ola_window_size_ <= - search_block_index_ + search_block_size; -} - -void AudioRendererAlgorithm::GetOptimalBlock() { - int optimal_index = 0; - - // An interval around last optimal block which is excluded from the search. - // This is to reduce the buzzy sound. The number 160 is rather arbitrary and - // derived heuristically. - const int kExcludeIntervalLengthFrames = 160; - if (TargetIsWithinSearchRegion()) { - optimal_index = target_block_index_; - PeekAudioWithZeroPrepend(optimal_index, optimal_block_.get()); - } else { - PeekAudioWithZeroPrepend(target_block_index_, target_block_.get()); - PeekAudioWithZeroPrepend(search_block_index_, search_block_.get()); - int last_optimal = - target_block_index_ - ola_hop_size_ - search_block_index_; - internal::Interval exclude_iterval = - std::make_pair(last_optimal - kExcludeIntervalLengthFrames / 2, - last_optimal + kExcludeIntervalLengthFrames / 2); - - // |optimal_index| is in frames and it is relative to the beginning of the - // |search_block_|. - optimal_index = internal::OptimalIndex( - search_block_.get(), target_block_.get(), exclude_iterval); - - // Translate |index| w.r.t. the beginning of |audio_buffer_| and extract the - // optimal block. - optimal_index += search_block_index_; - PeekAudioWithZeroPrepend(optimal_index, optimal_block_.get()); - - // Make a transition from target block to the optimal block if different. - // Target block has the best continuation to the current output. - // Optimal block is the most similar block to the target, however, it might - // introduce some discontinuity when over-lap-added. Therefore, we combine - // them for a smoother transition. The length of transition window is twice - // as that of the optimal-block which makes it like a weighting function - // where target-block has higher weight close to zero (weight of 1 at index - // 0) and lower weight close the end. - for (int k = 0; k < channels_; ++k) { - float* ch_opt = optimal_block_->channel(k); - const float* const ch_target = target_block_->channel(k); - for (int n = 0; n < ola_window_size_; ++n) { - ch_opt[n] = ch_opt[n] * transition_window_[n] + - ch_target[n] * transition_window_[ola_window_size_ + n]; - } - } - } - - // Next target is one hop ahead of the current optimal. - target_block_index_ = optimal_index + ola_hop_size_; -} - -void AudioRendererAlgorithm::PeekAudioWithZeroPrepend(int read_offset_frames, - AudioBus* dest) { - CHECK_LE(read_offset_frames + dest->frames(), audio_buffer_.frames()); - - int write_offset = 0; - int num_frames_to_read = dest->frames(); - if (read_offset_frames < 0) { - int num_zero_frames_appended = - std::min(-read_offset_frames, num_frames_to_read); - read_offset_frames = 0; - num_frames_to_read -= num_zero_frames_appended; - write_offset = num_zero_frames_appended; - dest->ZeroFrames(num_zero_frames_appended); - } - audio_buffer_.PeekFrames(num_frames_to_read, read_offset_frames, write_offset, - dest); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/audio_renderer_algorithm.h b/src/cobalt/media/filters/audio_renderer_algorithm.h deleted file mode 100644 index e2becc9..0000000 --- a/src/cobalt/media/filters/audio_renderer_algorithm.h +++ /dev/null
@@ -1,218 +0,0 @@ -// Copyright (c) 2012 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. - -// AudioRendererAlgorithm buffers and transforms audio data. The owner of -// this object provides audio data to the object through EnqueueBuffer() and -// requests data from the buffer via FillBuffer(). -// -// This class is *not* thread-safe. Calls to enqueue and retrieve data must be -// locked if called from multiple threads. -// -// AudioRendererAlgorithm uses the Waveform Similarity Overlap and Add (WSOLA) -// algorithm to stretch or compress audio data to meet playback speeds less than -// or greater than the natural playback of the audio stream. The algorithm -// preserves local properties of the audio, therefore, pitch and harmonics are -// are preserved. See audio_renderer_algorith.cc for a more elaborate -// description of the algorithm. -// -// Audio at very low or very high playback rates are muted to preserve quality. - -#ifndef COBALT_MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_H_ -#define COBALT_MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_H_ - -#include <memory> - -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "cobalt/media/base/audio_buffer.h" -#include "cobalt/media/base/audio_buffer_queue.h" -#include "cobalt/media/base/audio_parameters.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -class AudioBus; - -class MEDIA_EXPORT AudioRendererAlgorithm { - public: - AudioRendererAlgorithm(); - ~AudioRendererAlgorithm(); - - // Initializes this object with information about the audio stream. - void Initialize(const AudioParameters& params); - - // Tries to fill |requested_frames| frames into |dest| with possibly scaled - // data from our |audio_buffer_|. Data is scaled based on |playback_rate|, - // using a variation of the Overlap-Add method to combine sample windows. - // - // Data from |audio_buffer_| is consumed in proportion to the playback rate. - // - // |dest_offset| is the offset in frames for writing into |dest|. - // - // Returns the number of frames copied into |dest|. - int FillBuffer(AudioBus* dest, int dest_offset, int requested_frames, - double playback_rate); - - // Clears |audio_buffer_|. - void FlushBuffers(); - - // Enqueues a buffer. It is called from the owner of the algorithm after a - // read completes. - void EnqueueBuffer(const scoped_refptr<AudioBuffer>& buffer_in); - - // Returns true if |audio_buffer_| is at or exceeds capacity. - bool IsQueueFull(); - - // Returns the capacity of |audio_buffer_| in frames. - int QueueCapacity() const { return capacity_; } - - // Increase the capacity of |audio_buffer_| if possible. - void IncreaseQueueCapacity(); - - // Returns an estimate of the amount of memory (in bytes) used for frames. - int64_t GetMemoryUsage() const; - - // Returns the number of frames left in |audio_buffer_|, which may be larger - // than QueueCapacity() in the event that EnqueueBuffer() delivered more data - // than |audio_buffer_| was intending to hold. - int frames_buffered() { return audio_buffer_.frames(); } - - // Returns the samples per second for this audio stream. - int samples_per_second() { return samples_per_second_; } - - private: - // Within |search_block_|, find the block of data that is most similar to - // |target_block_|, and write it in |optimal_block_|. This method assumes that - // there is enough data to perform a search, i.e. |search_block_| and - // |target_block_| can be extracted from the available frames. - void GetOptimalBlock(); - - // Read a maximum of |requested_frames| frames from |wsola_output_|. Returns - // number of frames actually read. - int WriteCompletedFramesTo(int requested_frames, int output_offset, - AudioBus* dest); - - // Fill |dest| with frames from |audio_buffer_| starting from frame - // |read_offset_frames|. |dest| is expected to have the same number of - // channels as |audio_buffer_|. A negative offset, i.e. - // |read_offset_frames| < 0, is accepted assuming that |audio_buffer| is zero - // for negative indices. This might happen for few first frames. This method - // assumes there is enough frames to fill |dest|, i.e. |read_offset_frames| + - // |dest->frames()| does not extend to future. - void PeekAudioWithZeroPrepend(int read_offset_frames, AudioBus* dest); - - // Run one iteration of WSOLA, if there are sufficient frames. This will - // overlap-and-add one block to |wsola_output_|, hence, |num_complete_frames_| - // is incremented by |ola_hop_size_|. - bool RunOneWsolaIteration(double playback_rate); - - // Seek |audio_buffer_| forward to remove frames from input that are not used - // any more. State of the WSOLA will be updated accordingly. - void RemoveOldInputFrames(double playback_rate); - - // Update |output_time_| by |time_change|. In turn |search_block_index_| is - // updated. - void UpdateOutputTime(double playback_rate, double time_change); - - // Is |target_block_| fully within |search_block_|? If so, we don't need to - // perform the search. - bool TargetIsWithinSearchRegion() const; - - // Do we have enough data to perform one round of WSOLA? - bool CanPerformWsola() const; - - // Converts a time in milliseconds to frames using |samples_per_second_|. - int ConvertMillisecondsToFrames(int ms) const; - - // Number of channels in audio stream. - int channels_; - - // Sample rate of audio stream. - int samples_per_second_; - - // Buffered audio data. - AudioBufferQueue audio_buffer_; - - // If muted, keep track of partial frames that should have been skipped over. - double muted_partial_frame_; - - // How many frames to have in the queue before we report the queue is full. - int capacity_; - - // Book keeping of the current time of generated audio, in frames. This - // should be appropriately updated when out samples are generated, regardless - // of whether we push samples out when FillBuffer() is called or we store - // audio in |wsola_output_| for the subsequent calls to FillBuffer(). - // Furthermore, if samples from |audio_buffer_| are evicted then this - // member variable should be updated based on |playback_rate_|. - // Note that this member should be updated ONLY by calling UpdateOutputTime(), - // so that |search_block_index_| is update accordingly. - double output_time_; - - // The offset of the center frame of |search_block_| w.r.t. its first frame. - int search_block_center_offset_; - - // Index of the beginning of the |search_block_|, in frames. - int search_block_index_; - - // Number of Blocks to search to find the most similar one to the target - // frame. - int num_candidate_blocks_; - - // Index of the beginning of the target block, counted in frames. - int target_block_index_; - - // Overlap-and-add window size in frames. - int ola_window_size_; - - // The hop size of overlap-and-add in frames. This implementation assumes 50% - // overlap-and-add. - int ola_hop_size_; - - // Number of frames in |wsola_output_| that overlap-and-add is completed for - // them and can be copied to output if FillBuffer() is called. It also - // specifies the index where the next WSOLA window has to overlap-and-add. - int num_complete_frames_; - - // This stores a part of the output that is created but couldn't be rendered. - // Output is generated frame-by-frame which at some point might exceed the - // number of requested samples. Furthermore, due to overlap-and-add, - // the last half-window of the output is incomplete, which is stored in this - // buffer. - std::unique_ptr<AudioBus> wsola_output_; - - // Overlap-and-add window. - std::unique_ptr<float[]> ola_window_; - - // Transition window, used to update |optimal_block_| by a weighted sum of - // |optimal_block_| and |target_block_|. - std::unique_ptr<float[]> transition_window_; - - // Auxiliary variables to avoid allocation in every iteration. - - // Stores the optimal block in every iteration. This is the most - // similar block to |target_block_| within |search_block_| and it is - // overlap-and-added to |wsola_output_|. - std::unique_ptr<AudioBus> optimal_block_; - - // A block of data that search is performed over to find the |optimal_block_|. - std::unique_ptr<AudioBus> search_block_; - - // Stores the target block, denoted as |target| above. |search_block_| is - // searched for a block (|optimal_block_|) that is most similar to - // |target_block_|. - std::unique_ptr<AudioBus> target_block_; - - // The initial and maximum capacity calculated by Initialize(). - int initial_capacity_; - int max_capacity_; - - DISALLOW_COPY_AND_ASSIGN(AudioRendererAlgorithm); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_AUDIO_RENDERER_ALGORITHM_H_
diff --git a/src/cobalt/media/filters/audio_renderer_algorithm_unittest.cc b/src/cobalt/media/filters/audio_renderer_algorithm_unittest.cc deleted file mode 100644 index 9fce6c7..0000000 --- a/src/cobalt/media/filters/audio_renderer_algorithm_unittest.cc +++ /dev/null
@@ -1,681 +0,0 @@ -// Copyright (c) 2012 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. -// -// The format of these tests are to enqueue a known amount of data and then -// request the exact amount we expect in order to dequeue the known amount of -// data. This ensures that for any rate we are consuming input data at the -// correct rate. We always pass in a very large destination buffer with the -// expectation that FillBuffer() will fill as much as it can but no more. - -#include "cobalt/media/filters/audio_renderer_algorithm.h" - -#include <algorithm> // For std::min(). -#include <cmath> -#include <memory> -#include <vector> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/callback.h" -#include "cobalt/media/base/audio_buffer.h" -#include "cobalt/media/base/audio_bus.h" -#include "cobalt/media/base/channel_layout.h" -#include "cobalt/media/base/test_helpers.h" -#include "cobalt/media/base/timestamp_constants.h" -#include "cobalt/media/filters/wsola_internals.h" -#include "starboard/memory.h" -#include "starboard/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cobalt { -namespace media { - -const int kFrameSize = 250; -const int kSamplesPerSecond = 3000; -const int kOutputDurationInSec = 10; - -static void FillWithSquarePulseTrain(int half_pulse_width, int offset, - int num_samples, float* data) { - ASSERT_GE(offset, 0); - ASSERT_LE(offset, num_samples); - - // Fill backward from |offset| - 1 toward zero, starting with -1, alternating - // between -1 and 1 every |pulse_width| samples. - float pulse = -1.0f; - for (int n = offset - 1, k = 0; n >= 0; --n, ++k) { - if (k >= half_pulse_width) { - pulse = -pulse; - k = 0; - } - data[n] = pulse; - } - - // Fill forward from |offset| towards the end, starting with 1, alternating - // between 1 and -1 every |pulse_width| samples. - pulse = 1.0f; - for (int n = offset, k = 0; n < num_samples; ++n, ++k) { - if (k >= half_pulse_width) { - pulse = -pulse; - k = 0; - } - data[n] = pulse; - } -} - -static void FillWithSquarePulseTrain(int half_pulse_width, int offset, - int channel, AudioBus* audio_bus) { - FillWithSquarePulseTrain(half_pulse_width, offset, audio_bus->frames(), - audio_bus->channel(channel)); -} - -class AudioRendererAlgorithmTest : public testing::Test { - public: - AudioRendererAlgorithmTest() - : frames_enqueued_(0), - channels_(0), - channel_layout_(CHANNEL_LAYOUT_NONE), - sample_format_(kUnknownSampleFormat), - samples_per_second_(0), - bytes_per_sample_(0) {} - - ~AudioRendererAlgorithmTest() override {} - - void Initialize() { - Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS16, 3000, 300); - } - - void Initialize(ChannelLayout channel_layout, SampleFormat sample_format, - int samples_per_second, int frames_per_buffer) { - channels_ = ChannelLayoutToChannelCount(channel_layout); - samples_per_second_ = samples_per_second; - channel_layout_ = channel_layout; - sample_format_ = sample_format; - bytes_per_sample_ = SampleFormatToBytesPerChannel(sample_format); - AudioParameters params(media::AudioParameters::AUDIO_PCM_LINEAR, - channel_layout, samples_per_second, - bytes_per_sample_ * 8, frames_per_buffer); - algorithm_.Initialize(params); - FillAlgorithmQueue(); - } - - void FillAlgorithmQueue() { - // The value of the data is meaningless; we just want non-zero data to - // differentiate it from muted data. - scoped_refptr<AudioBuffer> buffer; - while (!algorithm_.IsQueueFull()) { - switch (sample_format_) { - case kSampleFormatU8: - buffer = MakeAudioBuffer<uint8_t>( - sample_format_, channel_layout_, - ChannelLayoutToChannelCount(channel_layout_), samples_per_second_, - 1, 1, kFrameSize, kNoTimestamp); - break; - case kSampleFormatS16: - buffer = MakeAudioBuffer<int16_t>( - sample_format_, channel_layout_, - ChannelLayoutToChannelCount(channel_layout_), samples_per_second_, - 1, 1, kFrameSize, kNoTimestamp); - break; - case kSampleFormatS32: - buffer = MakeAudioBuffer<int32_t>( - sample_format_, channel_layout_, - ChannelLayoutToChannelCount(channel_layout_), samples_per_second_, - 1, 1, kFrameSize, kNoTimestamp); - break; - default: - NOTREACHED() << "Unrecognized format " << sample_format_; - } - algorithm_.EnqueueBuffer(buffer); - frames_enqueued_ += kFrameSize; - } - } - - bool VerifyAudioData(AudioBus* bus, int offset, int frames, float value) { - for (int ch = 0; ch < bus->channels(); ++ch) { - for (int i = offset; i < offset + frames; ++i) { - if (bus->channel(ch)[i] != value) return false; - } - } - return true; - } - - bool AudioDataIsMuted(AudioBus* audio_data, int frames_written) { - return VerifyAudioData(audio_data, 0, frames_written, 0); - } - - int ComputeConsumedFrames(int initial_frames_enqueued, - int initial_frames_buffered) { - int frame_delta = frames_enqueued_ - initial_frames_enqueued; - int buffered_delta = algorithm_.frames_buffered() - initial_frames_buffered; - int consumed = frame_delta - buffered_delta; - CHECK_GE(consumed, 0); - return consumed; - } - - void TestPlaybackRate(double playback_rate) { - const int kDefaultBufferSize = algorithm_.samples_per_second() / 100; - const int kDefaultFramesRequested = - kOutputDurationInSec * algorithm_.samples_per_second(); - - TestPlaybackRate(playback_rate, kDefaultBufferSize, - kDefaultFramesRequested); - } - - void TestPlaybackRate(double playback_rate, int buffer_size_in_frames, - int total_frames_requested) { - int initial_frames_enqueued = frames_enqueued_; - int initial_frames_buffered = algorithm_.frames_buffered(); - - std::unique_ptr<AudioBus> bus = - AudioBus::Create(channels_, buffer_size_in_frames); - if (playback_rate == 0.0) { - int frames_written = algorithm_.FillBuffer( - bus.get(), 0, buffer_size_in_frames, playback_rate); - EXPECT_EQ(0, frames_written); - return; - } - - bool expect_muted = (playback_rate < 0.5 || playback_rate > 4); - - int frames_remaining = total_frames_requested; - bool first_fill_buffer = true; - while (frames_remaining > 0) { - int frames_requested = std::min(buffer_size_in_frames, frames_remaining); - int frames_written = - algorithm_.FillBuffer(bus.get(), 0, frames_requested, playback_rate); - ASSERT_GT(frames_written, 0) << "Requested: " << frames_requested - << ", playing at " << playback_rate; - - // Do not check data if it is first pull out and only one frame written. - // The very first frame out of WSOLA is always zero because of - // overlap-and-add window, which is zero for the first sample. Therefore, - // if at very first buffer-fill only one frame is written, that is zero - // which might cause exception in CheckFakeData(). - if (!first_fill_buffer || frames_written > 1) - ASSERT_EQ(expect_muted, AudioDataIsMuted(bus.get(), frames_written)); - first_fill_buffer = false; - frames_remaining -= frames_written; - - FillAlgorithmQueue(); - } - - EXPECT_EQ(algorithm_.frames_buffered() * channels_ * sizeof(float), - static_cast<size_t>(algorithm_.GetMemoryUsage())); - - int frames_consumed = - ComputeConsumedFrames(initial_frames_enqueued, initial_frames_buffered); - - // If playing back at normal speed, we should always get back the same - // number of bytes requested. - if (playback_rate == 1.0) { - EXPECT_EQ(total_frames_requested, frames_consumed); - return; - } - - // Otherwise, allow |kMaxAcceptableDelta| difference between the target and - // actual playback rate. - // When |kSamplesPerSecond| and |total_frames_requested| are reasonably - // large, one can expect less than a 1% difference in most cases. In our - // current implementation, sped up playback is less accurate than slowed - // down playback, and for playback_rate > 1, playback rate generally gets - // less and less accurate the farther it drifts from 1 (though this is - // nonlinear). - double actual_playback_rate = - 1.0 * frames_consumed / total_frames_requested; - EXPECT_NEAR(playback_rate, actual_playback_rate, playback_rate / 100.0); - } - - void WsolaTest(double playback_rate) { - const int kSampleRateHz = 48000; - const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; - const int kBytesPerSample = 2; - const int kNumFrames = kSampleRateHz / 100; // 10 milliseconds. - - channels_ = ChannelLayoutToChannelCount(kChannelLayout); - AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, - kSampleRateHz, kBytesPerSample * 8, kNumFrames); - algorithm_.Initialize(params); - - // A pulse is 6 milliseconds (even number of samples). - const int kPulseWidthSamples = 6 * kSampleRateHz / 1000; - const int kHalfPulseWidthSamples = kPulseWidthSamples / 2; - - // For the ease of implementation get 1 frame every call to FillBuffer(). - std::unique_ptr<AudioBus> output = AudioBus::Create(channels_, 1); - - // Input buffer to inject pulses. - scoped_refptr<AudioBuffer> input = - AudioBuffer::CreateBuffer(kSampleFormatPlanarF32, kChannelLayout, - channels_, kSampleRateHz, kPulseWidthSamples); - - const std::vector<uint8_t*>& channel_data = input->channel_data(); - - // Fill |input| channels. - FillWithSquarePulseTrain(kHalfPulseWidthSamples, 0, kPulseWidthSamples, - reinterpret_cast<float*>(channel_data[0])); - FillWithSquarePulseTrain(kHalfPulseWidthSamples, kHalfPulseWidthSamples, - kPulseWidthSamples, - reinterpret_cast<float*>(channel_data[1])); - - // A buffer for the output until a complete pulse is created. Then - // reference pulse is compared with this buffer. - std::unique_ptr<AudioBus> pulse_buffer = - AudioBus::Create(channels_, kPulseWidthSamples); - - const float kTolerance = 0.000001f; - // Equivalent of 4 seconds. - const int kNumRequestedPulses = kSampleRateHz * 4 / kPulseWidthSamples; - for (int n = 0; n < kNumRequestedPulses; ++n) { - int num_buffered_frames = 0; - while (num_buffered_frames < kPulseWidthSamples) { - int num_samples = - algorithm_.FillBuffer(output.get(), 0, 1, playback_rate); - ASSERT_LE(num_samples, 1); - if (num_samples > 0) { - output->CopyPartialFramesTo(0, num_samples, num_buffered_frames, - pulse_buffer.get()); - num_buffered_frames++; - } else { - algorithm_.EnqueueBuffer(input); - } - } - - // Pulses in the first half of WSOLA AOL frame are not constructed - // perfectly. Do not check them. - if (n > 3) { - for (int m = 0; m < channels_; ++m) { - const float* pulse_ch = pulse_buffer->channel(m); - - // Because of overlap-and-add we might have round off error. - for (int k = 0; k < kPulseWidthSamples; ++k) { - ASSERT_NEAR(reinterpret_cast<float*>(channel_data[m])[k], - pulse_ch[k], kTolerance) - << " loop " << n << " channel/sample " << m << "/" << k; - } - } - } - - // Zero out the buffer to be sure the next comparison is relevant. - pulse_buffer->Zero(); - } - } - - protected: - AudioRendererAlgorithm algorithm_; - int frames_enqueued_; - int channels_; - ChannelLayout channel_layout_; - SampleFormat sample_format_; - int samples_per_second_; - int bytes_per_sample_; -}; - -TEST_F(AudioRendererAlgorithmTest, InitializeWithLargeParameters) { - const int kBufferSize = 0.5 * kSamplesPerSecond; - Initialize(CHANNEL_LAYOUT_MONO, kSampleFormatU8, kSamplesPerSecond, - kBufferSize); - EXPECT_LT(kBufferSize, algorithm_.QueueCapacity()); - algorithm_.FlushBuffers(); - EXPECT_LT(kBufferSize, algorithm_.QueueCapacity()); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_NormalRate) { - Initialize(); - TestPlaybackRate(1.0); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_NearlyNormalFasterRate) { - Initialize(); - TestPlaybackRate(1.0001); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_NearlyNormalSlowerRate) { - Initialize(); - TestPlaybackRate(0.9999); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_OneAndAQuarterRate) { - Initialize(); - TestPlaybackRate(1.25); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_OneAndAHalfRate) { - Initialize(); - TestPlaybackRate(1.5); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_DoubleRate) { - Initialize(); - TestPlaybackRate(2.0); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_EightTimesRate) { - Initialize(); - TestPlaybackRate(8.0); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_ThreeQuartersRate) { - Initialize(); - TestPlaybackRate(0.75); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_HalfRate) { - Initialize(); - TestPlaybackRate(0.5); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_QuarterRate) { - Initialize(); - TestPlaybackRate(0.25); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_Pause) { - Initialize(); - TestPlaybackRate(0.0); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_SlowDown) { - Initialize(); - TestPlaybackRate(4.5); - TestPlaybackRate(3.0); - TestPlaybackRate(2.0); - TestPlaybackRate(1.0); - TestPlaybackRate(0.5); - TestPlaybackRate(0.25); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_SpeedUp) { - Initialize(); - TestPlaybackRate(0.25); - TestPlaybackRate(0.5); - TestPlaybackRate(1.0); - TestPlaybackRate(2.0); - TestPlaybackRate(3.0); - TestPlaybackRate(4.5); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_JumpAroundSpeeds) { - Initialize(); - TestPlaybackRate(2.1); - TestPlaybackRate(0.9); - TestPlaybackRate(0.6); - TestPlaybackRate(1.4); - TestPlaybackRate(0.3); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_SmallBufferSize) { - Initialize(); - static const int kBufferSizeInFrames = 1; - static const int kFramesRequested = kOutputDurationInSec * kSamplesPerSecond; - TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested); - TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested); - TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_LargeBufferSize) { - Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS16, 44100, 441); - TestPlaybackRate(1.0); - TestPlaybackRate(0.5); - TestPlaybackRate(1.5); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_LowerQualityAudio) { - Initialize(CHANNEL_LAYOUT_MONO, kSampleFormatU8, kSamplesPerSecond, - kSamplesPerSecond / 100); - TestPlaybackRate(1.0); - TestPlaybackRate(0.5); - TestPlaybackRate(1.5); -} - -TEST_F(AudioRendererAlgorithmTest, FillBuffer_HigherQualityAudio) { - Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS32, kSamplesPerSecond, - kSamplesPerSecond / 100); - TestPlaybackRate(1.0); - TestPlaybackRate(0.5); - TestPlaybackRate(1.5); -} - -TEST_F(AudioRendererAlgorithmTest, DotProduct) { - const int kChannels = 3; - const int kFrames = 20; - const int kHalfPulseWidth = 2; - - std::unique_ptr<AudioBus> a = AudioBus::Create(kChannels, kFrames); - std::unique_ptr<AudioBus> b = AudioBus::Create(kChannels, kFrames); - - std::unique_ptr<float[]> dot_prod(new float[kChannels]); - - FillWithSquarePulseTrain(kHalfPulseWidth, 0, 0, a.get()); - FillWithSquarePulseTrain(kHalfPulseWidth, 1, 1, a.get()); - FillWithSquarePulseTrain(kHalfPulseWidth, 2, 2, a.get()); - - FillWithSquarePulseTrain(kHalfPulseWidth, 0, 0, b.get()); - FillWithSquarePulseTrain(kHalfPulseWidth, 0, 1, b.get()); - FillWithSquarePulseTrain(kHalfPulseWidth, 0, 2, b.get()); - - internal::MultiChannelDotProduct(a.get(), 0, b.get(), 0, kFrames, - dot_prod.get()); - - EXPECT_FLOAT_EQ(kFrames, dot_prod[0]); - EXPECT_FLOAT_EQ(0, dot_prod[1]); - EXPECT_FLOAT_EQ(-kFrames, dot_prod[2]); - - internal::MultiChannelDotProduct(a.get(), 4, b.get(), 8, kFrames / 2, - dot_prod.get()); - - EXPECT_FLOAT_EQ(kFrames / 2, dot_prod[0]); - EXPECT_FLOAT_EQ(0, dot_prod[1]); - EXPECT_FLOAT_EQ(-kFrames / 2, dot_prod[2]); -} - -TEST_F(AudioRendererAlgorithmTest, MovingBlockEnergy) { - const int kChannels = 2; - const int kFrames = 20; - const int kFramesPerBlock = 3; - const int kNumBlocks = kFrames - (kFramesPerBlock - 1); - std::unique_ptr<AudioBus> a = AudioBus::Create(kChannels, kFrames); - std::unique_ptr<float[]> energies(new float[kChannels * kNumBlocks]); - float* ch_left = a->channel(0); - float* ch_right = a->channel(1); - - // Fill up both channels. - for (int n = 0; n < kFrames; ++n) { - ch_left[n] = n; - ch_right[n] = kFrames - 1 - n; - } - - internal::MultiChannelMovingBlockEnergies(a.get(), kFramesPerBlock, - energies.get()); - - // Check if the energy of candidate blocks of each channel computed correctly. - for (int n = 0; n < kNumBlocks; ++n) { - float expected_energy = 0; - for (int k = 0; k < kFramesPerBlock; ++k) - expected_energy += ch_left[n + k] * ch_left[n + k]; - - // Left (first) channel. - EXPECT_FLOAT_EQ(expected_energy, energies[2 * n]); - - expected_energy = 0; - for (int k = 0; k < kFramesPerBlock; ++k) - expected_energy += ch_right[n + k] * ch_right[n + k]; - - // Second (right) channel. - EXPECT_FLOAT_EQ(expected_energy, energies[2 * n + 1]); - } -} - -TEST_F(AudioRendererAlgorithmTest, FullAndDecimatedSearch) { - const int kFramesInSearchRegion = 12; - const int kChannels = 2; - float ch_0[] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}; - float ch_1[] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.1f, 1.0f, 0.1f, 0.0f, 0.0f}; - ASSERT_EQ(sizeof(ch_0), sizeof(ch_1)); - ASSERT_EQ(static_cast<size_t>(kFramesInSearchRegion), - sizeof(ch_0) / sizeof(*ch_0)); - std::unique_ptr<AudioBus> search_region = - AudioBus::Create(kChannels, kFramesInSearchRegion); - float* ch = search_region->channel(0); - SbMemoryCopy(ch, ch_0, sizeof(float) * kFramesInSearchRegion); - ch = search_region->channel(1); - SbMemoryCopy(ch, ch_1, sizeof(float) * kFramesInSearchRegion); - - const int kFramePerBlock = 4; - float target_0[] = {1.0f, 1.0f, 1.0f, 0.0f}; - float target_1[] = {0.0f, 1.0f, 0.1f, 1.0f}; - ASSERT_EQ(sizeof(target_0), sizeof(target_1)); - ASSERT_EQ(static_cast<size_t>(kFramePerBlock), - sizeof(target_0) / sizeof(*target_0)); - - std::unique_ptr<AudioBus> target = - AudioBus::Create(kChannels, kFramePerBlock); - ch = target->channel(0); - SbMemoryCopy(ch, target_0, sizeof(float) * kFramePerBlock); - ch = target->channel(1); - SbMemoryCopy(ch, target_1, sizeof(float) * kFramePerBlock); - - std::unique_ptr<float[]> energy_target(new float[kChannels]); - - internal::MultiChannelDotProduct(target.get(), 0, target.get(), 0, - kFramePerBlock, energy_target.get()); - - ASSERT_EQ(3.f, energy_target[0]); - ASSERT_EQ(2.01f, energy_target[1]); - - const int kNumCandidBlocks = kFramesInSearchRegion - (kFramePerBlock - 1); - std::unique_ptr<float[]> energy_candid_blocks( - new float[kNumCandidBlocks * kChannels]); - - internal::MultiChannelMovingBlockEnergies(search_region.get(), kFramePerBlock, - energy_candid_blocks.get()); - - // Check the energy of the candidate blocks of the first channel. - ASSERT_FLOAT_EQ(0, energy_candid_blocks[0]); - ASSERT_FLOAT_EQ(0, energy_candid_blocks[2]); - ASSERT_FLOAT_EQ(1, energy_candid_blocks[4]); - ASSERT_FLOAT_EQ(2, energy_candid_blocks[6]); - ASSERT_FLOAT_EQ(3, energy_candid_blocks[8]); - ASSERT_FLOAT_EQ(3, energy_candid_blocks[10]); - ASSERT_FLOAT_EQ(2, energy_candid_blocks[12]); - ASSERT_FLOAT_EQ(1, energy_candid_blocks[14]); - ASSERT_FLOAT_EQ(0, energy_candid_blocks[16]); - - // Check the energy of the candidate blocks of the second channel. - ASSERT_FLOAT_EQ(0, energy_candid_blocks[1]); - ASSERT_FLOAT_EQ(0, energy_candid_blocks[3]); - ASSERT_FLOAT_EQ(0, energy_candid_blocks[5]); - ASSERT_FLOAT_EQ(0, energy_candid_blocks[7]); - ASSERT_FLOAT_EQ(0.01f, energy_candid_blocks[9]); - ASSERT_FLOAT_EQ(1.01f, energy_candid_blocks[11]); - ASSERT_FLOAT_EQ(1.02f, energy_candid_blocks[13]); - ASSERT_FLOAT_EQ(1.02f, energy_candid_blocks[15]); - ASSERT_FLOAT_EQ(1.01f, energy_candid_blocks[17]); - - // An interval which is of no effect. - internal::Interval exclude_interval = std::make_pair(-100, -10); - EXPECT_EQ( - 5, internal::FullSearch(0, kNumCandidBlocks - 1, exclude_interval, - target.get(), search_region.get(), - energy_target.get(), energy_candid_blocks.get())); - - // Exclude the the best match. - exclude_interval = std::make_pair(2, 5); - EXPECT_EQ( - 7, internal::FullSearch(0, kNumCandidBlocks - 1, exclude_interval, - target.get(), search_region.get(), - energy_target.get(), energy_candid_blocks.get())); - - // An interval which is of no effect. - exclude_interval = std::make_pair(-100, -10); - EXPECT_EQ(4, internal::DecimatedSearch( - 4, exclude_interval, target.get(), search_region.get(), - energy_target.get(), energy_candid_blocks.get())); - - EXPECT_EQ(5, internal::OptimalIndex(search_region.get(), target.get(), - exclude_interval)); -} - -TEST_F(AudioRendererAlgorithmTest, QuadraticInterpolation) { - // Arbitrary coefficients. - const float kA = 0.7f; - const float kB = 1.2f; - const float kC = 0.8f; - - float y_values[3]; - y_values[0] = kA - kB + kC; - y_values[1] = kC; - y_values[2] = kA + kB + kC; - - float extremum; - float extremum_value; - - internal::QuadraticInterpolation(y_values, &extremum, &extremum_value); - - float x_star = -kB / (2.f * kA); - float y_star = kA * x_star * x_star + kB * x_star + kC; - - EXPECT_FLOAT_EQ(x_star, extremum); - EXPECT_FLOAT_EQ(y_star, extremum_value); -} - -TEST_F(AudioRendererAlgorithmTest, QuadraticInterpolation_Colinear) { - float y_values[3]; - y_values[0] = 1.0; - y_values[1] = 1.0; - y_values[2] = 1.0; - - float extremum; - float extremum_value; - - internal::QuadraticInterpolation(y_values, &extremum, &extremum_value); - - EXPECT_FLOAT_EQ(extremum, 0.0); - EXPECT_FLOAT_EQ(extremum_value, 1.0); -} - -TEST_F(AudioRendererAlgorithmTest, WsolaSlowdown) { WsolaTest(0.6); } - -TEST_F(AudioRendererAlgorithmTest, WsolaSpeedup) { WsolaTest(1.6); } - -TEST_F(AudioRendererAlgorithmTest, FillBufferOffset) { - Initialize(); - - std::unique_ptr<AudioBus> bus = AudioBus::Create(channels_, kFrameSize); - - // Verify that the first half of |bus| remains zero and the last half is - // filled appropriately at normal, above normal, below normal, and muted - // rates. - const int kHalfSize = kFrameSize / 2; - const float kAudibleRates[] = {1.0f, 2.0f, 0.5f}; - for (size_t i = 0; i < arraysize(kAudibleRates); ++i) { - SCOPED_TRACE(kAudibleRates[i]); - bus->Zero(); - - const int frames_filled = algorithm_.FillBuffer( - bus.get(), kHalfSize, kHalfSize, kAudibleRates[i]); - ASSERT_EQ(kHalfSize, frames_filled); - ASSERT_TRUE(VerifyAudioData(bus.get(), 0, kHalfSize, 0)); - ASSERT_FALSE(VerifyAudioData(bus.get(), kHalfSize, kHalfSize, 0)); - FillAlgorithmQueue(); - } - - const float kMutedRates[] = {5.0f, 0.25f}; - for (size_t i = 0; i < arraysize(kMutedRates); ++i) { - SCOPED_TRACE(kMutedRates[i]); - for (int ch = 0; ch < bus->channels(); ++ch) - std::fill(bus->channel(ch), bus->channel(ch) + bus->frames(), 1.0f); - - const int frames_filled = - algorithm_.FillBuffer(bus.get(), kHalfSize, kHalfSize, kMutedRates[i]); - ASSERT_EQ(kHalfSize, frames_filled); - ASSERT_FALSE(VerifyAudioData(bus.get(), 0, kHalfSize, 0)); - ASSERT_TRUE(VerifyAudioData(bus.get(), kHalfSize, kHalfSize, 0)); - FillAlgorithmQueue(); - } -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/audio_timestamp_validator.cc b/src/cobalt/media/filters/audio_timestamp_validator.cc deleted file mode 100644 index d8b4102..0000000 --- a/src/cobalt/media/filters/audio_timestamp_validator.cc +++ /dev/null
@@ -1,144 +0,0 @@ -// 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 "cobalt/media/filters/audio_timestamp_validator.h" - -namespace cobalt { -namespace media { - -// Defines how many milliseconds of DecoderBuffer timestamp gap will be allowed -// before warning the user. See CheckForTimestampGap(). Value of 50 chosen, as -// this is low enough to catch issues early, but high enough to avoid noise for -// containers like WebM that default to low granularity timestamp precision. -const int kGapWarningThresholdMsec = 50; - -// Limits the number of adjustments to |audio_ts_offset_| in order to reach a -// stable state where gaps between encoded timestamps match decoded output -// intervals. See CheckForTimestampGap(). -const int kLimitTriesForStableTiming = 5; - -// Limits the milliseconds of difference between expected and actual timestamps -// gaps to consider timestamp expectations "stable". 1 chosen because some -// containers (WebM) default to millisecond timestamp precision. See -// CheckForTimestampGap(). -const int kStableTimeGapThrsholdMsec = 1; - -AudioTimestampValidator::AudioTimestampValidator( - const AudioDecoderConfig& decoder_config, - const scoped_refptr<MediaLog>& media_log) - : has_codec_delay_(decoder_config.codec_delay() > 0), - media_log_(media_log), - audio_base_ts_(kNoTimestamp), - reached_stable_state_(false), - num_unstable_audio_tries_(0), - limit_unstable_audio_tries_(kLimitTriesForStableTiming), - drift_warning_threshold_msec_(kGapWarningThresholdMsec) { - DCHECK(decoder_config.IsValidConfig()); -} - -AudioTimestampValidator::~AudioTimestampValidator() {} - -void AudioTimestampValidator::CheckForTimestampGap( - const scoped_refptr<DecoderBuffer>& buffer) { - if (buffer->end_of_stream()) return; - DCHECK_NE(kNoTimestamp, buffer->timestamp()); - - // If audio_base_ts_ == kNoTimestamp, we are processing our first buffer. - // If stream has neither codec delay nor discard padding, we should expect - // timestamps and output durations to line up from the start (i.e. be stable). - if (audio_base_ts_ == kNoTimestamp && !has_codec_delay_ && - buffer->discard_padding().first == base::TimeDelta() && - buffer->discard_padding().second == base::TimeDelta()) { - DVLOG(3) << __func__ << " Expecting stable timestamps - stream has neither " - << "codec delay nor discard padding."; - limit_unstable_audio_tries_ = 0; - } - - // Don't continue checking timestamps if we've exhausted tries to reach stable - // state. This suggests the media's encoded timestamps are way off. - if (num_unstable_audio_tries_ > limit_unstable_audio_tries_) return; - - // Keep resetting encode base ts until we start getting decode output. Some - // codecs/containers (e.g. chained Ogg) will take several encoded buffers - // before producing the first decoded output. - if (!audio_output_ts_helper_) { - audio_base_ts_ = buffer->timestamp(); - DVLOG(3) << __func__ - << " setting audio_base:" << audio_base_ts_.InMicroseconds(); - return; - } - - base::TimeDelta expected_ts = audio_output_ts_helper_->GetTimestamp(); - base::TimeDelta ts_delta = buffer->timestamp() - expected_ts; - - // Reconciling encoded buffer timestamps with decoded output often requires - // adjusting expectations by some offset. This accounts for varied (and at - // this point unknown) handling of front trimming and codec delay. Codec delay - // and skip trimming may or may not be accounted for in the encoded timestamps - // depending on the codec (e.g. MP3 vs Opus) and demuxers used (e.g. FFmpeg - // vs MSE stream parsers). - if (!reached_stable_state_) { - if (std::abs(ts_delta.InMilliseconds()) < kStableTimeGapThrsholdMsec) { - reached_stable_state_ = true; - DVLOG(3) << __func__ << " stabilized! tries:" << num_unstable_audio_tries_ - << " offset:" - << audio_output_ts_helper_->base_timestamp().InMicroseconds(); - } else { - base::TimeDelta orig_offset = audio_output_ts_helper_->base_timestamp(); - - // Save since this gets reset when we set new base time. - int64_t decoded_frame_count = audio_output_ts_helper_->frame_count(); - audio_output_ts_helper_->SetBaseTimestamp(orig_offset + ts_delta); - audio_output_ts_helper_->AddFrames(decoded_frame_count); - - DVLOG(3) << __func__ - << " NOT stabilized. tries:" << num_unstable_audio_tries_ - << " offset was:" << orig_offset.InMicroseconds() << " now:" - << audio_output_ts_helper_->base_timestamp().InMicroseconds(); - num_unstable_audio_tries_++; - - // Let developers know if their files timestamps are way off from - if (num_unstable_audio_tries_ > limit_unstable_audio_tries_) { - MEDIA_LOG(ERROR, media_log_) - << "Failed to reconcile encoded audio times with decoded output."; - } - } - - // Don't bother with further checking until we reach stable state. - return; - } - - if (std::abs(ts_delta.InMilliseconds()) > drift_warning_threshold_msec_) { - MEDIA_LOG(ERROR, media_log_) - << " Large timestamp gap detected; may cause AV sync to drift." - << " time:" << buffer->timestamp().InMicroseconds() << "us" - << " expected:" << expected_ts.InMicroseconds() << "us" - << " delta:" << ts_delta.InMicroseconds() << "us"; - // Increase threshold to avoid log spam but, let us know if gap widens. - drift_warning_threshold_msec_ = std::abs(ts_delta.InMilliseconds()); - } - DVLOG(3) << __func__ << " delta:" << ts_delta.InMicroseconds() - << " expected_ts:" << expected_ts.InMicroseconds() - << " actual_ts:" << buffer->timestamp().InMicroseconds() - << " audio_ts_offset:" - << audio_output_ts_helper_->base_timestamp().InMicroseconds(); -} - -void AudioTimestampValidator::RecordOutputDuration( - const scoped_refptr<AudioBuffer>& audio_buffer) { - if (!audio_output_ts_helper_) { - DCHECK_NE(audio_base_ts_, kNoTimestamp); - // SUBTLE: deliberately creating this with output buffer sample rate because - // demuxer stream config is potentially stale for implicit AAC. - audio_output_ts_helper_.reset( - new AudioTimestampHelper(audio_buffer->sample_rate())); - audio_output_ts_helper_->SetBaseTimestamp(audio_base_ts_); - } - - DVLOG(3) << __func__ << " " << audio_buffer->frame_count() << " frames"; - audio_output_ts_helper_->AddFrames(audio_buffer->frame_count()); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/audio_timestamp_validator.h b/src/cobalt/media/filters/audio_timestamp_validator.h deleted file mode 100644 index 33609d5..0000000 --- a/src/cobalt/media/filters/audio_timestamp_validator.h +++ /dev/null
@@ -1,69 +0,0 @@ -// 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. - -#ifndef COBALT_MEDIA_FILTERS_AUDIO_TIMESTAMP_VALIDATOR_H_ -#define COBALT_MEDIA_FILTERS_AUDIO_TIMESTAMP_VALIDATOR_H_ - -#include "base/memory/ref_counted.h" -#include "base/time.h" -#include "cobalt/media/base/audio_buffer.h" -#include "cobalt/media/base/audio_decoder_config.h" -#include "cobalt/media/base/audio_timestamp_helper.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/media_log.h" -#include "cobalt/media/base/timestamp_constants.h" - -namespace cobalt { -namespace media { - -class MEDIA_EXPORT AudioTimestampValidator { - public: - AudioTimestampValidator(const AudioDecoderConfig& decoder_config, - const scoped_refptr<MediaLog>& media_log); - ~AudioTimestampValidator(); - - // These methods monitor DecoderBuffer timestamps for gaps for the purpose of - // warning developers when gaps may cause AV sync drift. A DecoderBuffer's - // timestamp should roughly equal the timestamp of the previous buffer offset - // by the previous buffer's duration. - void CheckForTimestampGap(const scoped_refptr<DecoderBuffer>& buffer); - void RecordOutputDuration(const scoped_refptr<AudioBuffer>& buffer); - - private: - bool has_codec_delay_; - scoped_refptr<MediaLog> media_log_; - - // Accumulates time from decoded audio frames. We adjust the base timestamp as - // needed for the first few buffers (stabilization period) of decoded output - // to account for pre-skip and codec delay. See CheckForTimestampGap(). - std::unique_ptr<AudioTimestampHelper> audio_output_ts_helper_; - - base::TimeDelta audio_base_ts_; - - // Initially false, set to true when we observe gap between encoded timestamps - // match gap between output decoder buffers. - bool reached_stable_state_; - - // Counts attempts to adjust |audio_output_ts_helper_| base offset in effort - // to form expectation for encoded timestamps based on decoded output. Give up - // making adjustments when count exceeds |limit_unstable_audio_tries_|. - int num_unstable_audio_tries_; - - // Limits the number of attempts to stabilize audio timestamp expectations. - int limit_unstable_audio_tries_; - - // How many milliseconds can DecoderBuffer timestamps differ from expectations - // before we MEDIA_LOG warn developers. Threshold initially set from - // kGapWarningThresholdMsec. Once hit, the threshold is increased by - // the detected gap amount. This avoids log spam while still emitting - // logs if things get worse. See CheckTimestampForGap(). - uint32_t drift_warning_threshold_msec_; - - DISALLOW_COPY_AND_ASSIGN(AudioTimestampValidator); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_AUDIO_TIMESTAMP_VALIDATOR_H_
diff --git a/src/cobalt/media/filters/audio_timestamp_validator_unittest.cc b/src/cobalt/media/filters/audio_timestamp_validator_unittest.cc deleted file mode 100644 index 836187d..0000000 --- a/src/cobalt/media/filters/audio_timestamp_validator_unittest.cc +++ /dev/null
@@ -1,251 +0,0 @@ -// 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 "cobalt/media/filters/audio_timestamp_validator.h" - -#include <tuple> - -#include "base/time.h" -#include "cobalt/media/base/audio_decoder_config.h" -#include "cobalt/media/base/media_util.h" -#include "cobalt/media/base/mock_media_log.h" -#include "cobalt/media/base/test_helpers.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::HasSubstr; - -namespace cobalt { -namespace media { - -// Constants to specify the type of audio data used. -static const AudioCodec kCodec = kCodecVorbis; -static const SampleFormat kSampleFormat = kSampleFormatPlanarF32; -static const base::TimeDelta kSeekPreroll; -static const int kSamplesPerSecond = 10000; -static const base::TimeDelta kBufferDuration = - base::TimeDelta::FromMilliseconds(20); -static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; -static const int kChannelCount = 2; -static const int kChannels = ChannelLayoutToChannelCount(kChannelLayout); -static const int kFramesPerBuffer = kBufferDuration.InMicroseconds() * - kSamplesPerSecond / - base::Time::kMicrosecondsPerSecond; - -// Params are: -// 1. Output delay: number of encoded buffers before first decoded output -// 2. Codec delay: number of frames of codec delay in decoder config -// 3. Front discard: front discard for the first buffer -using ValidatorTestParams = testing::tuple<int, int, base::TimeDelta>; - -class AudioTimestampValidatorTest - : public testing::Test, - public ::testing::WithParamInterface<ValidatorTestParams> { - public: - AudioTimestampValidatorTest() - : media_log_(new testing::StrictMock<MockMediaLog>()) {} - - protected: - void SetUp() override { - output_delay_ = testing::get<0>(GetParam()); - codec_delay_ = testing::get<1>(GetParam()); - front_discard_ = testing::get<2>(GetParam()); - } - - int output_delay_; - - int codec_delay_; - - base::TimeDelta front_discard_; - - scoped_refptr<testing::StrictMock<MockMediaLog>> media_log_; -}; - -TEST_P(AudioTimestampValidatorTest, WarnForEraticTimes) { - AudioDecoderConfig decoder_config; - decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout, - kSamplesPerSecond, EmptyExtraData(), Unencrypted(), - kSeekPreroll, codec_delay_); - - // Validator should fail to stabilize pattern for timestamp expectations. - EXPECT_MEDIA_LOG( - HasSubstr("Failed to reconcile encoded audio times " - "with decoded output.")); - - // No gap warnings should be emitted because the timestamps expectations never - // stabilized. - EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected")).Times(0); - - AudioTimestampValidator validator(decoder_config, media_log_); - - const base::TimeDelta kRandomOffsets[] = { - base::TimeDelta::FromMilliseconds(100), - base::TimeDelta::FromMilliseconds(350)}; - - for (int i = 0; i < 100; ++i) { - // Each buffer's timestamp is kBufferDuration from the previous buffer. - scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0); - - // Ping-pong between two random offsets to prevent validator from - // stabilizing timestamp pattern. - base::TimeDelta randomOffset = - kRandomOffsets[i % arraysize(kRandomOffsets)]; - encoded_buffer->set_timestamp(i * kBufferDuration + randomOffset); - - if (i == 0) { - encoded_buffer->set_discard_padding( - std::make_pair(front_discard_, base::TimeDelta())); - } - - validator.CheckForTimestampGap(encoded_buffer); - - if (i >= output_delay_) { - // kFramesPerBuffer is derived to perfectly match kBufferDuration, so - // no gaps exists as long as timestamps are exactly kBufferDuration apart. - scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>( - kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f, - 0.0f, kFramesPerBuffer, i * kBufferDuration); - validator.RecordOutputDuration(decoded_buffer.get()); - } - } -} - -TEST_P(AudioTimestampValidatorTest, NoWarningForValidTimes) { - AudioDecoderConfig decoder_config; - decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout, - kSamplesPerSecond, EmptyExtraData(), Unencrypted(), - kSeekPreroll, codec_delay_); - - // Validator should quickly stabilize pattern for timestamp expectations. - EXPECT_MEDIA_LOG(HasSubstr("Failed to reconcile encoded audio times " - "with decoded output.")) - .Times(0); - - // Expect no gap warnings for series of buffers with valid timestamps. - EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected")).Times(0); - - AudioTimestampValidator validator(decoder_config, media_log_); - - for (int i = 0; i < 100; ++i) { - // Each buffer's timestamp is kBufferDuration from the previous buffer. - scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0); - encoded_buffer->set_timestamp(i * kBufferDuration); - - if (i == 0) { - encoded_buffer->set_discard_padding( - std::make_pair(front_discard_, base::TimeDelta())); - } - - validator.CheckForTimestampGap(encoded_buffer); - - if (i >= output_delay_) { - // kFramesPerBuffer is derived to perfectly match kBufferDuration, so - // no gaps exists as long as timestamps are exactly kBufferDuration apart. - scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>( - kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f, - 0.0f, kFramesPerBuffer, i * kBufferDuration); - validator.RecordOutputDuration(decoded_buffer.get()); - } - } -} - -TEST_P(AudioTimestampValidatorTest, SingleWarnForSingleLargeGap) { - AudioDecoderConfig decoder_config; - decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout, - kSamplesPerSecond, EmptyExtraData(), Unencrypted(), - kSeekPreroll, codec_delay_); - - AudioTimestampValidator validator(decoder_config, media_log_); - - // Validator should quickly stabilize pattern for timestamp expectations. - EXPECT_MEDIA_LOG(HasSubstr("Failed to reconcile encoded audio times " - "with decoded output.")) - .Times(0); - - for (int i = 0; i < 100; ++i) { - // Halfway through the stream, introduce sudden gap of 50 milliseconds. - base::TimeDelta offset; - if (i >= 50) offset = base::TimeDelta::FromMilliseconds(100); - - // This gap never widens, so expect only a single warning when its first - // introduced. - if (i == 50) EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected")); - - scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0); - encoded_buffer->set_timestamp(i * kBufferDuration + offset); - - if (i == 0) { - encoded_buffer->set_discard_padding( - std::make_pair(front_discard_, base::TimeDelta())); - } - - validator.CheckForTimestampGap(encoded_buffer); - - if (i >= output_delay_) { - // kFramesPerBuffer is derived to perfectly match kBufferDuration, so - // no gaps exists as long as timestamps are exactly kBufferDuration apart. - scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>( - kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f, - 0.0f, kFramesPerBuffer, i * kBufferDuration); - validator.RecordOutputDuration(decoded_buffer.get()); - } - } -} - -TEST_P(AudioTimestampValidatorTest, RepeatedWarnForSlowAccumulatingDrift) { - AudioDecoderConfig decoder_config; - decoder_config.Initialize(kCodec, kSampleFormat, kChannelLayout, - kSamplesPerSecond, EmptyExtraData(), Unencrypted(), - kSeekPreroll, codec_delay_); - - AudioTimestampValidator validator(decoder_config, media_log_); - - EXPECT_MEDIA_LOG(HasSubstr("Failed to reconcile encoded audio times " - "with decoded output.")) - .Times(0); - - for (int i = 0; i < 100; ++i) { - // Wait for delayed output to begin plus an additional two iterations to - // start using drift offset. The the two iterations without offset will - // allow the validator to stabilize the pattern of timestamps and begin - // checking for gaps. Once stable, increase offset by 1 millisecond for each - // iteration. - base::TimeDelta offset; - if (i >= output_delay_ + 2) - offset = i * base::TimeDelta::FromMilliseconds(1); - - scoped_refptr<DecoderBuffer> encoded_buffer = new DecoderBuffer(0); - encoded_buffer->set_timestamp((i * kBufferDuration) + offset); - - // Expect gap warnings to start when drift hits 50 milliseconds. Warnings - // should continue as the gap widens. - if (offset > base::TimeDelta::FromMilliseconds(50)) { - EXPECT_MEDIA_LOG(HasSubstr("timestamp gap detected")); - } - - validator.CheckForTimestampGap(encoded_buffer); - - if (i >= output_delay_) { - // kFramesPerBuffer is derived to perfectly match kBufferDuration, so - // no gaps exists as long as timestamps are exactly kBufferDuration apart. - scoped_refptr<AudioBuffer> decoded_buffer = MakeAudioBuffer<float>( - kSampleFormat, kChannelLayout, kChannelCount, kSamplesPerSecond, 1.0f, - 0.0f, kFramesPerBuffer, i * kBufferDuration); - validator.RecordOutputDuration(decoded_buffer.get()); - } - } -} - -// Test with cartesian product of various output delay, codec delay, and front -// discard values. These simulate configurations for different containers/codecs -// which present different challenges when building timestamp expectations. -INSTANTIATE_TEST_CASE_P( - , AudioTimestampValidatorTest, - ::testing::Combine( - ::testing::Values(0, 10), // output delay - ::testing::Values(0, 512), // codec delay - ::testing::Values(base::TimeDelta(), // front discard - base::TimeDelta::FromMilliseconds(65)))); - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/blocking_url_protocol.cc b/src/cobalt/media/filters/blocking_url_protocol.cc deleted file mode 100644 index f17eeef..0000000 --- a/src/cobalt/media/filters/blocking_url_protocol.cc +++ /dev/null
@@ -1,96 +0,0 @@ -// Copyright (c) 2012 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/filters/blocking_url_protocol.h" - -#include "base/basictypes.h" -#include "base/bind.h" -#include "cobalt/media/base/data_source.h" -#include "cobalt/media/ffmpeg/ffmpeg_common.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -BlockingUrlProtocol::BlockingUrlProtocol(DataSource* data_source, - const base::Closure& error_cb) - : data_source_(data_source), - error_cb_(error_cb), - aborted_(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED), // We never - // want to - // reset - // |aborted_|. - read_complete_(base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED), - last_read_bytes_(0), - read_position_(0) {} - -BlockingUrlProtocol::~BlockingUrlProtocol() {} - -void BlockingUrlProtocol::Abort() { aborted_.Signal(); } - -int BlockingUrlProtocol::Read(int size, uint8_t* data) { - // Read errors are unrecoverable. - if (aborted_.IsSignaled()) return AVERROR(EIO); - - // Even though FFmpeg defines AVERROR_EOF, it's not to be used with I/O - // routines. Instead return 0 for any read at or past EOF. - int64_t file_size; - if (data_source_->GetSize(&file_size) && read_position_ >= file_size) - return 0; - - // Blocking read from data source until either: - // 1) |last_read_bytes_| is set and |read_complete_| is signalled - // 2) |aborted_| is signalled - data_source_->Read(read_position_, size, data, - base::Bind(&BlockingUrlProtocol::SignalReadCompleted, - base::Unretained(this))); - - base::WaitableEvent* events[] = {&aborted_, &read_complete_}; - size_t index = base::WaitableEvent::WaitMany(events, arraysize(events)); - - if (events[index] == &aborted_) return AVERROR(EIO); - - if (last_read_bytes_ == DataSource::kReadError) { - aborted_.Signal(); - error_cb_.Run(); - return AVERROR(EIO); - } - - if (last_read_bytes_ == DataSource::kAborted) return AVERROR(EIO); - - read_position_ += last_read_bytes_; - return last_read_bytes_; -} - -bool BlockingUrlProtocol::GetPosition(int64_t* position_out) { - *position_out = read_position_; - return true; -} - -bool BlockingUrlProtocol::SetPosition(int64_t position) { - int64_t file_size; - if ((data_source_->GetSize(&file_size) && position > file_size) || - position < 0) { - return false; - } - - read_position_ = position; - return true; -} - -bool BlockingUrlProtocol::GetSize(int64_t* size_out) { - return data_source_->GetSize(size_out); -} - -bool BlockingUrlProtocol::IsStreaming() { return data_source_->IsStreaming(); } - -void BlockingUrlProtocol::SignalReadCompleted(int size) { - last_read_bytes_ = size; - read_complete_.Signal(); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/blocking_url_protocol.h b/src/cobalt/media/filters/blocking_url_protocol.h deleted file mode 100644 index acb2bdb..0000000 --- a/src/cobalt/media/filters/blocking_url_protocol.h +++ /dev/null
@@ -1,66 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef COBALT_MEDIA_FILTERS_BLOCKING_URL_PROTOCOL_H_ -#define COBALT_MEDIA_FILTERS_BLOCKING_URL_PROTOCOL_H_ - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/synchronization/waitable_event.h" -#include "cobalt/media/filters/ffmpeg_glue.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -class DataSource; - -// An implementation of FFmpegURLProtocol that blocks until the underlying -// asynchronous DataSource::Read() operation completes. -class MEDIA_EXPORT BlockingUrlProtocol : public FFmpegURLProtocol { - public: - // Implements FFmpegURLProtocol using the given |data_source|. |error_cb| is - // fired any time DataSource::Read() returns an error. - // - // TODO(scherkus): After all blocking operations are isolated on a separate - // thread we should be able to eliminate |error_cb|. - BlockingUrlProtocol(DataSource* data_source, const base::Closure& error_cb); - virtual ~BlockingUrlProtocol(); - - // Aborts any pending reads by returning a read error. After this method - // returns all subsequent calls to Read() will immediately fail. - void Abort(); - - // FFmpegURLProtocol implementation. - int Read(int size, uint8_t* data) OVERRIDE; - bool GetPosition(int64_t* position_out) OVERRIDE; - bool SetPosition(int64_t position) OVERRIDE; - bool GetSize(int64_t* size_out) OVERRIDE; - bool IsStreaming() OVERRIDE; - - private: - // Sets |last_read_bytes_| and signals the blocked thread that the read - // has completed. - void SignalReadCompleted(int size); - - DataSource* data_source_; - base::Closure error_cb_; - - // Used to unblock the thread during shutdown and when reads complete. - base::WaitableEvent aborted_; - base::WaitableEvent read_complete_; - - // Cached number of bytes last read from the data source. - int last_read_bytes_; - - // Cached position within the data source. - int64_t read_position_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(BlockingUrlProtocol); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_BLOCKING_URL_PROTOCOL_H_
diff --git a/src/cobalt/media/filters/blocking_url_protocol_unittest.cc b/src/cobalt/media/filters/blocking_url_protocol_unittest.cc deleted file mode 100644 index 884a877..0000000 --- a/src/cobalt/media/filters/blocking_url_protocol_unittest.cc +++ /dev/null
@@ -1,119 +0,0 @@ -// Copyright (c) 2012 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/basictypes.h" -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/synchronization/waitable_event.h" -#include "cobalt/media/base/test_data_util.h" -#include "cobalt/media/ffmpeg/ffmpeg_common.h" -#include "cobalt/media/filters/blocking_url_protocol.h" -#include "cobalt/media/filters/file_data_source.h" -#include "starboard/types.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cobalt { -namespace media { - -class BlockingUrlProtocolTest : public testing::Test { - public: - BlockingUrlProtocolTest() - : url_protocol_(&data_source_, - base::Bind(&BlockingUrlProtocolTest::OnDataSourceError, - base::Unretained(this))) { - CHECK(data_source_.Initialize(GetTestDataFilePath("bear-320x240.webm"))); - } - - virtual ~BlockingUrlProtocolTest() { data_source_.Stop(); } - - MOCK_METHOD0(OnDataSourceError, void()); - - FileDataSource data_source_; - BlockingUrlProtocol url_protocol_; - - private: - DISALLOW_COPY_AND_ASSIGN(BlockingUrlProtocolTest); -}; - -TEST_F(BlockingUrlProtocolTest, Read) { - // Set read head to zero as Initialize() will have parsed a bit of the file. - int64_t position = 0; - EXPECT_TRUE(url_protocol_.SetPosition(0)); - EXPECT_TRUE(url_protocol_.GetPosition(&position)); - EXPECT_EQ(0, position); - - // Read 32 bytes from offset zero and verify position. - uint8_t buffer[32]; - EXPECT_EQ(32, url_protocol_.Read(32, buffer)); - EXPECT_TRUE(url_protocol_.GetPosition(&position)); - EXPECT_EQ(32, position); - - // Read an additional 32 bytes and verify position. - EXPECT_EQ(32, url_protocol_.Read(32, buffer)); - EXPECT_TRUE(url_protocol_.GetPosition(&position)); - EXPECT_EQ(64, position); - - // Seek to end and read until EOF. - int64_t size = 0; - EXPECT_TRUE(url_protocol_.GetSize(&size)); - EXPECT_TRUE(url_protocol_.SetPosition(size - 48)); - EXPECT_EQ(32, url_protocol_.Read(32, buffer)); - EXPECT_TRUE(url_protocol_.GetPosition(&position)); - EXPECT_EQ(size - 16, position); - - EXPECT_EQ(16, url_protocol_.Read(32, buffer)); - EXPECT_TRUE(url_protocol_.GetPosition(&position)); - EXPECT_EQ(size, position); - - EXPECT_EQ(0, url_protocol_.Read(32, buffer)); - EXPECT_TRUE(url_protocol_.GetPosition(&position)); - EXPECT_EQ(size, position); -} - -TEST_F(BlockingUrlProtocolTest, ReadError) { - data_source_.force_read_errors_for_testing(); - - uint8_t buffer[32]; - EXPECT_CALL(*this, OnDataSourceError()); - EXPECT_EQ(AVERROR(EIO), url_protocol_.Read(32, buffer)); -} - -TEST_F(BlockingUrlProtocolTest, GetSetPosition) { - int64_t size; - int64_t position; - EXPECT_TRUE(url_protocol_.GetSize(&size)); - EXPECT_TRUE(url_protocol_.GetPosition(&position)); - - EXPECT_TRUE(url_protocol_.SetPosition(512)); - EXPECT_FALSE(url_protocol_.SetPosition(size + 1)); - EXPECT_FALSE(url_protocol_.SetPosition(-1)); - EXPECT_TRUE(url_protocol_.GetPosition(&position)); - EXPECT_EQ(512, position); - - EXPECT_TRUE(url_protocol_.SetPosition(size)); - EXPECT_TRUE(url_protocol_.GetPosition(&position)); - EXPECT_EQ(size, position); -} - -TEST_F(BlockingUrlProtocolTest, GetSize) { - int64_t data_source_size = 0; - int64_t url_protocol_size = 0; - EXPECT_TRUE(data_source_.GetSize(&data_source_size)); - EXPECT_TRUE(url_protocol_.GetSize(&url_protocol_size)); - EXPECT_NE(0, data_source_size); - EXPECT_EQ(data_source_size, url_protocol_size); -} - -TEST_F(BlockingUrlProtocolTest, IsStreaming) { - EXPECT_FALSE(data_source_.IsStreaming()); - EXPECT_FALSE(url_protocol_.IsStreaming()); - - data_source_.force_streaming_for_testing(); - EXPECT_TRUE(data_source_.IsStreaming()); - EXPECT_TRUE(url_protocol_.IsStreaming()); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/context_3d.h b/src/cobalt/media/filters/context_3d.h deleted file mode 100644 index a654b30..0000000 --- a/src/cobalt/media/filters/context_3d.h +++ /dev/null
@@ -1,38 +0,0 @@ -// Copyright (c) 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. - -#ifndef COBALT_MEDIA_FILTERS_CONTEXT_3D_H_ -#define COBALT_MEDIA_FILTERS_CONTEXT_3D_H_ - -class GrContext; - -namespace gpu { -namespace gles2 { -class GLES2Interface; -} -} - -namespace cobalt { -namespace media { - -// This struct can be used to make media use gpu::gles2::GLES2Interface and -// GrContext. -// Usage: -// gpu::gles2::GLES2Interface* gl = ...; -// GrContext* gr_context = ...; -// Context3D context_3d(gl, gr_context); - -struct Context3D { - Context3D() : gl(NULL), gr_context(NULL) {} - Context3D(gpu::gles2::GLES2Interface* gl_, class GrContext* gr_context_) - : gl(gl_), gr_context(gr_context_) {} - - gpu::gles2::GLES2Interface* gl; - class GrContext* gr_context; -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_CONTEXT_3D_H_
diff --git a/src/cobalt/media/filters/decoder_selector.cc b/src/cobalt/media/filters/decoder_selector.cc deleted file mode 100644 index 5750d47..0000000 --- a/src/cobalt/media/filters/decoder_selector.cc +++ /dev/null
@@ -1,247 +0,0 @@ -// 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/filters/decoder_selector.h" - -#include <utility> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/logging.h" -#include "base/single_thread_task_runner.h" -#include "build/build_config.h" -#include "cobalt/media/base/audio_decoder.h" -#include "cobalt/media/base/bind_to_current_loop.h" -#include "cobalt/media/base/cdm_context.h" -#include "cobalt/media/base/demuxer_stream.h" -#include "cobalt/media/base/media_log.h" -#include "cobalt/media/base/video_decoder.h" -#include "cobalt/media/filters/decoder_stream_traits.h" -#include "cobalt/media/filters/decrypting_demuxer_stream.h" - -#if !defined(OS_ANDROID) -#include "cobalt/media/filters/decrypting_audio_decoder.h" -#include "cobalt/media/filters/decrypting_video_decoder.h" -#endif - -namespace cobalt { -namespace media { - -static bool HasValidStreamConfig(DemuxerStream* stream) { - switch (stream->type()) { - case DemuxerStream::AUDIO: - return stream->audio_decoder_config().IsValidConfig(); - case DemuxerStream::VIDEO: - return stream->video_decoder_config().IsValidConfig(); - case DemuxerStream::UNKNOWN: - case DemuxerStream::TEXT: - case DemuxerStream::NUM_TYPES: - NOTREACHED(); - } - return false; -} - -static bool IsStreamEncrypted(DemuxerStream* stream) { - switch (stream->type()) { - case DemuxerStream::AUDIO: - return stream->audio_decoder_config().is_encrypted(); - case DemuxerStream::VIDEO: - return stream->video_decoder_config().is_encrypted(); - case DemuxerStream::UNKNOWN: - case DemuxerStream::TEXT: - case DemuxerStream::NUM_TYPES: - NOTREACHED(); - } - return false; -} - -template <DemuxerStream::Type StreamType> -DecoderSelector<StreamType>::DecoderSelector( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - ScopedVector<Decoder> decoders, const scoped_refptr<MediaLog>& media_log) - : task_runner_(task_runner), - decoders_(std::move(decoders)), - media_log_(media_log), - input_stream_(NULL), - weak_ptr_factory_(this) {} - -template <DemuxerStream::Type StreamType> -DecoderSelector<StreamType>::~DecoderSelector() { - DVLOG(2) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (!select_decoder_cb_.is_null()) ReturnNullDecoder(); - - decoder_.reset(); - decrypted_stream_.reset(); -} - -template <DemuxerStream::Type StreamType> -void DecoderSelector<StreamType>::SelectDecoder( - StreamTraits* traits, DemuxerStream* stream, CdmContext* cdm_context, - const SelectDecoderCB& select_decoder_cb, - const typename Decoder::OutputCB& output_cb, - const base::Closure& waiting_for_decryption_key_cb) { - DVLOG(2) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(traits); - DCHECK(stream); - DCHECK(select_decoder_cb_.is_null()); - - cdm_context_ = cdm_context; - waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb; - - // Make sure |select_decoder_cb| runs on a different execution stack. - select_decoder_cb_ = BindToCurrentLoop(select_decoder_cb); - - if (!HasValidStreamConfig(stream)) { - DLOG(ERROR) << "Invalid stream config."; - ReturnNullDecoder(); - return; - } - - traits_ = traits; - input_stream_ = stream; - output_cb_ = output_cb; - - if (!IsStreamEncrypted(input_stream_)) { - InitializeDecoder(); - return; - } - - // This could be null during fallback after decoder reinitialization failure. - // See DecoderStream<StreamType>::OnDecoderReinitialized(). - if (!cdm_context_) { - ReturnNullDecoder(); - return; - } - -#if !defined(OS_ANDROID) - InitializeDecryptingDecoder(); -#else - InitializeDecryptingDemuxerStream(); -#endif -} - -#if !defined(OS_ANDROID) -template <DemuxerStream::Type StreamType> -void DecoderSelector<StreamType>::InitializeDecryptingDecoder() { - DVLOG(2) << __func__; - decoder_.reset(new typename StreamTraits::DecryptingDecoderType( - task_runner_, media_log_, waiting_for_decryption_key_cb_)); - - traits_->InitializeDecoder( - decoder_.get(), input_stream_, cdm_context_, - base::Bind(&DecoderSelector<StreamType>::DecryptingDecoderInitDone, - weak_ptr_factory_.GetWeakPtr()), - output_cb_); -} - -template <DemuxerStream::Type StreamType> -void DecoderSelector<StreamType>::DecryptingDecoderInitDone(bool success) { - DVLOG(2) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (success) { - base::ResetAndReturn(&select_decoder_cb_) - .Run(std::move(decoder_), std::unique_ptr<DecryptingDemuxerStream>()); - return; - } - - decoder_.reset(); - - // When we get here decrypt-and-decode is not supported. Try to use - // DecryptingDemuxerStream to do decrypt-only. - InitializeDecryptingDemuxerStream(); -} -#endif // !defined(OS_ANDROID) - -template <DemuxerStream::Type StreamType> -void DecoderSelector<StreamType>::InitializeDecryptingDemuxerStream() { - decrypted_stream_.reset(new DecryptingDemuxerStream( - task_runner_, media_log_, waiting_for_decryption_key_cb_)); - - decrypted_stream_->Initialize( - input_stream_, cdm_context_, - base::Bind(&DecoderSelector<StreamType>::DecryptingDemuxerStreamInitDone, - weak_ptr_factory_.GetWeakPtr())); -} - -template <DemuxerStream::Type StreamType> -void DecoderSelector<StreamType>::DecryptingDemuxerStreamInitDone( - PipelineStatus status) { - DVLOG(2) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - - // If DecryptingDemuxerStream initialization succeeded, we'll use it to do - // decryption and use a decoder to decode the clear stream. Otherwise, we'll - // try to see whether any decoder can decrypt-and-decode the encrypted stream - // directly. So in both cases, we'll initialize the decoders. - - if (status == PIPELINE_OK) { - input_stream_ = decrypted_stream_.get(); - DCHECK(!IsStreamEncrypted(input_stream_)); - } else { - decrypted_stream_.reset(); - DCHECK(IsStreamEncrypted(input_stream_)); - } - - InitializeDecoder(); -} - -template <DemuxerStream::Type StreamType> -void DecoderSelector<StreamType>::InitializeDecoder() { - DVLOG(2) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(!decoder_); - - if (decoders_.empty()) { - ReturnNullDecoder(); - return; - } - - decoder_.reset(decoders_.front()); - decoders_.weak_erase(decoders_.begin()); - - traits_->InitializeDecoder( - decoder_.get(), input_stream_, cdm_context_, - base::Bind(&DecoderSelector<StreamType>::DecoderInitDone, - weak_ptr_factory_.GetWeakPtr()), - output_cb_); -} - -template <DemuxerStream::Type StreamType> -void DecoderSelector<StreamType>::DecoderInitDone(bool success) { - DVLOG(2) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (!success) { - decoder_.reset(); - InitializeDecoder(); - return; - } - - base::ResetAndReturn(&select_decoder_cb_) - .Run(std::move(decoder_), std::move(decrypted_stream_)); -} - -template <DemuxerStream::Type StreamType> -void DecoderSelector<StreamType>::ReturnNullDecoder() { - DVLOG(2) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - base::ResetAndReturn(&select_decoder_cb_) - .Run(std::unique_ptr<Decoder>(), - std::unique_ptr<DecryptingDemuxerStream>()); -} - -// These forward declarations tell the compiler that we will use -// DecoderSelector with these arguments, allowing us to keep these definitions -// in our .cc without causing linker errors. This also means if anyone tries to -// instantiate a DecoderSelector with anything but these two specializations -// they'll most likely get linker errors. -template class DecoderSelector<DemuxerStream::AUDIO>; -template class DecoderSelector<DemuxerStream::VIDEO>; - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/decoder_selector.h b/src/cobalt/media/filters/decoder_selector.h deleted file mode 100644 index 8916654..0000000 --- a/src/cobalt/media/filters/decoder_selector.h +++ /dev/null
@@ -1,117 +0,0 @@ -// 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. - -#ifndef COBALT_MEDIA_FILTERS_DECODER_SELECTOR_H_ -#define COBALT_MEDIA_FILTERS_DECODER_SELECTOR_H_ - -#include <memory> - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_vector.h" -#include "base/memory/weak_ptr.h" -#include "build/build_config.h" -#include "cobalt/media/base/demuxer_stream.h" -#include "cobalt/media/base/pipeline_status.h" -#include "cobalt/media/filters/decoder_stream_traits.h" - -namespace base { -class SingleThreadTaskRunner; -} - -namespace cobalt { -namespace media { - -class CdmContext; -class DecoderBuffer; -class DecryptingDemuxerStream; -class MediaLog; - -// DecoderSelector (creates if necessary and) initializes the proper -// Decoder for a given DemuxerStream. If the given DemuxerStream is -// encrypted, a DecryptingDemuxerStream may also be created. -// The template parameter |StreamType| is the type of stream we will be -// selecting a decoder for. -template <DemuxerStream::Type StreamType> -class MEDIA_EXPORT DecoderSelector { - public: - typedef DecoderStreamTraits<StreamType> StreamTraits; - typedef typename StreamTraits::DecoderType Decoder; - - // Indicates completion of Decoder selection. - // - First parameter: The initialized Decoder. If it's set to NULL, then - // Decoder initialization failed. - // - Second parameter: The initialized DecryptingDemuxerStream. If it's not - // NULL, then a DecryptingDemuxerStream is created and initialized to do - // decryption for the initialized Decoder. - // Note: The caller owns selected Decoder and DecryptingDemuxerStream. - // The caller should call DecryptingDemuxerStream::Reset() before - // calling Decoder::Reset() to release any pending decryption or read. - typedef base::Callback<void(std::unique_ptr<Decoder>, - std::unique_ptr<DecryptingDemuxerStream>)> - SelectDecoderCB; - - // |decoders| contains the Decoders to use when initializing. - DecoderSelector( - const scoped_refptr<base::SingleThreadTaskRunner>& message_loop, - ScopedVector<Decoder> decoders, const scoped_refptr<MediaLog>& media_log); - - // Aborts pending Decoder selection and fires |select_decoder_cb| with - // NULL and NULL immediately if it's pending. - ~DecoderSelector(); - - // Initializes and selects the first Decoder that can decode the |stream|. - // The selected Decoder (and DecryptingDemuxerStream) is returned via - // the |select_decoder_cb|. - // Notes: - // 1. This must not be called again before |select_decoder_cb| is run. - // 2. Decoders that fail to initialize will be deleted. Future calls will - // select from the decoders following the decoder that was last returned. - // 3. |cdm_context| is optional. If |cdm_context| is - // null, no CDM will be available to perform decryption. - void SelectDecoder(StreamTraits* traits, DemuxerStream* stream, - CdmContext* cdm_context, - const SelectDecoderCB& select_decoder_cb, - const typename Decoder::OutputCB& output_cb, - const base::Closure& waiting_for_decryption_key_cb); - - private: -#if !defined(OS_ANDROID) - void InitializeDecryptingDecoder(); - void DecryptingDecoderInitDone(bool success); -#endif - void InitializeDecryptingDemuxerStream(); - void DecryptingDemuxerStreamInitDone(PipelineStatus status); - void InitializeDecoder(); - void DecoderInitDone(bool success); - void ReturnNullDecoder(); - - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - ScopedVector<Decoder> decoders_; - scoped_refptr<MediaLog> media_log_; - - StreamTraits* traits_; - DemuxerStream* input_stream_; - CdmContext* cdm_context_; - SelectDecoderCB select_decoder_cb_; - typename Decoder::OutputCB output_cb_; - base::Closure waiting_for_decryption_key_cb_; - - std::unique_ptr<Decoder> decoder_; - std::unique_ptr<DecryptingDemuxerStream> decrypted_stream_; - - // NOTE: Weak pointers must be invalidated before all other member variables. - base::WeakPtrFactory<DecoderSelector> weak_ptr_factory_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(DecoderSelector); -}; - -typedef DecoderSelector<DemuxerStream::VIDEO> VideoDecoderSelector; -typedef DecoderSelector<DemuxerStream::AUDIO> AudioDecoderSelector; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_DECODER_SELECTOR_H_
diff --git a/src/cobalt/media/filters/decrypting_audio_decoder.cc b/src/cobalt/media/filters/decrypting_audio_decoder.cc deleted file mode 100644 index 311412f..0000000 --- a/src/cobalt/media/filters/decrypting_audio_decoder.cc +++ /dev/null
@@ -1,358 +0,0 @@ -// Copyright (c) 2012 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/filters/decrypting_audio_decoder.h" - -#include <cstdlib> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/single_thread_task_runner.h" -#include "base/string_number_conversions.h" -#include "cobalt/media/base/audio_buffer.h" -#include "cobalt/media/base/audio_decoder_config.h" -#include "cobalt/media/base/audio_timestamp_helper.h" -#include "cobalt/media/base/bind_to_current_loop.h" -#include "cobalt/media/base/cdm_context.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/media_log.h" -#include "cobalt/media/base/timestamp_constants.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -static inline bool IsOutOfSync(const base::TimeDelta& timestamp_1, - const base::TimeDelta& timestamp_2) { - // Out of sync of 100ms would be pretty noticeable and we should keep any - // drift below that. - const int64_t kOutOfSyncThresholdInMilliseconds = 100; - return std::abs(timestamp_1.InMilliseconds() - timestamp_2.InMilliseconds()) > - kOutOfSyncThresholdInMilliseconds; -} - -DecryptingAudioDecoder::DecryptingAudioDecoder( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - const scoped_refptr<MediaLog>& media_log, - const base::Closure& waiting_for_decryption_key_cb) - : task_runner_(task_runner), - media_log_(media_log), - state_(kUninitialized), - waiting_for_decryption_key_cb_(waiting_for_decryption_key_cb), - decryptor_(NULL), - key_added_while_decode_pending_(false), - weak_factory_(this) {} - -std::string DecryptingAudioDecoder::GetDisplayName() const { - return "DecryptingAudioDecoder"; -} - -void DecryptingAudioDecoder::Initialize(const AudioDecoderConfig& config, - CdmContext* cdm_context, - const InitCB& init_cb, - const OutputCB& output_cb) { - DVLOG(2) << "Initialize()"; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(decode_cb_.is_null()); - DCHECK(reset_cb_.is_null()); - - weak_this_ = weak_factory_.GetWeakPtr(); - init_cb_ = BindToCurrentLoop(init_cb); - output_cb_ = BindToCurrentLoop(output_cb); - - // TODO(xhwang): We should be able to DCHECK config.IsValidConfig() and - // config.is_encrypted(). - if (!config.IsValidConfig()) { - DLOG(ERROR) << "Invalid audio stream config."; - base::ResetAndReturn(&init_cb_).Run(false); - return; - } - - // DecryptingAudioDecoder only accepts potentially encrypted stream. - if (!config.is_encrypted()) { - base::ResetAndReturn(&init_cb_).Run(false); - return; - } - - config_ = config; - - if (state_ == kUninitialized) { - DCHECK(cdm_context); - if (!cdm_context->GetDecryptor()) { - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() << ": no decryptor"; - base::ResetAndReturn(&init_cb_).Run(false); - return; - } - - decryptor_ = cdm_context->GetDecryptor(); - } else { - // Reinitialization (i.e. upon a config change) - decryptor_->DeinitializeDecoder(Decryptor::kAudio); - } - - InitializeDecoder(); -} - -void DecryptingAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, - const DecodeCB& decode_cb) { - DVLOG(3) << "Decode()"; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kIdle || state_ == kDecodeFinished) << state_; - DCHECK(!decode_cb.is_null()); - CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported."; - - decode_cb_ = BindToCurrentLoop(decode_cb); - - // Return empty (end-of-stream) frames if decoding has finished. - if (state_ == kDecodeFinished) { - output_cb_.Run(AudioBuffer::CreateEOSBuffer()); - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::OK); - return; - } - - // Initialize the |next_output_timestamp_| to be the timestamp of the first - // non-EOS buffer. - if (timestamp_helper_->base_timestamp() == kNoTimestamp && - !buffer->end_of_stream()) { - timestamp_helper_->SetBaseTimestamp(buffer->timestamp()); - } - - pending_buffer_to_decode_ = buffer; - state_ = kPendingDecode; - DecodePendingBuffer(); -} - -void DecryptingAudioDecoder::Reset(const base::Closure& closure) { - DVLOG(2) << "Reset() - state: " << state_; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kIdle || state_ == kPendingDecode || - state_ == kWaitingForKey || state_ == kDecodeFinished) - << state_; - DCHECK(init_cb_.is_null()); // No Reset() during pending initialization. - DCHECK(reset_cb_.is_null()); - - reset_cb_ = BindToCurrentLoop(closure); - - decryptor_->ResetDecoder(Decryptor::kAudio); - - // Reset() cannot complete if the read callback is still pending. - // Defer the resetting process in this case. The |reset_cb_| will be fired - // after the read callback is fired - see DecryptAndDecodeBuffer() and - // DeliverFrame(). - if (state_ == kPendingDecode) { - DCHECK(!decode_cb_.is_null()); - return; - } - - if (state_ == kWaitingForKey) { - DCHECK(!decode_cb_.is_null()); - pending_buffer_to_decode_ = NULL; - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::ABORTED); - } - - DCHECK(decode_cb_.is_null()); - DoReset(); -} - -DecryptingAudioDecoder::~DecryptingAudioDecoder() { - DVLOG(2) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (state_ == kUninitialized) return; - - if (decryptor_) { - decryptor_->DeinitializeDecoder(Decryptor::kAudio); - decryptor_ = NULL; - } - pending_buffer_to_decode_ = NULL; - if (!init_cb_.is_null()) base::ResetAndReturn(&init_cb_).Run(false); - if (!decode_cb_.is_null()) - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::ABORTED); - if (!reset_cb_.is_null()) base::ResetAndReturn(&reset_cb_).Run(); -} - -void DecryptingAudioDecoder::InitializeDecoder() { - state_ = kPendingDecoderInit; - decryptor_->InitializeAudioDecoder( - config_, BindToCurrentLoop(base::Bind( - &DecryptingAudioDecoder::FinishInitialization, weak_this_))); -} - -void DecryptingAudioDecoder::FinishInitialization(bool success) { - DVLOG(2) << "FinishInitialization()"; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kPendingDecoderInit) << state_; - DCHECK(!init_cb_.is_null()); - DCHECK(reset_cb_.is_null()); // No Reset() before initialization finished. - DCHECK(decode_cb_.is_null()); // No Decode() before initialization finished. - - if (!success) { - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() - << ": failed to init decoder on decryptor"; - base::ResetAndReturn(&init_cb_).Run(false); - decryptor_ = NULL; - state_ = kError; - return; - } - - // Success! - timestamp_helper_.reset( - new AudioTimestampHelper(config_.samples_per_second())); - - decryptor_->RegisterNewKeyCB( - Decryptor::kAudio, BindToCurrentLoop(base::Bind( - &DecryptingAudioDecoder::OnKeyAdded, weak_this_))); - - state_ = kIdle; - base::ResetAndReturn(&init_cb_).Run(true); -} - -void DecryptingAudioDecoder::DecodePendingBuffer() { - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kPendingDecode) << state_; - - int buffer_size = 0; - if (!pending_buffer_to_decode_->end_of_stream()) { - buffer_size = pending_buffer_to_decode_->data_size(); - } - - decryptor_->DecryptAndDecodeAudio( - pending_buffer_to_decode_, - BindToCurrentLoop(base::Bind(&DecryptingAudioDecoder::DeliverFrame, - weak_this_, buffer_size))); -} - -void DecryptingAudioDecoder::DeliverFrame( - int buffer_size, Decryptor::Status status, - const Decryptor::AudioFrames& frames) { - DVLOG(3) << "DeliverFrame() - status: " << status; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kPendingDecode) << state_; - DCHECK(!decode_cb_.is_null()); - DCHECK(pending_buffer_to_decode_.get()); - - bool need_to_try_again_if_nokey_is_returned = key_added_while_decode_pending_; - key_added_while_decode_pending_ = false; - - scoped_refptr<DecoderBuffer> scoped_pending_buffer_to_decode = - pending_buffer_to_decode_; - pending_buffer_to_decode_ = NULL; - - if (!reset_cb_.is_null()) { - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::ABORTED); - DoReset(); - return; - } - - DCHECK_EQ(status == Decryptor::kSuccess, !frames.empty()); - - if (status == Decryptor::kError) { - DVLOG(2) << "DeliverFrame() - kError"; - MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": decode error"; - state_ = kDecodeFinished; // TODO add kError state - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::DECODE_ERROR); - return; - } - - if (status == Decryptor::kNoKey) { - std::string key_id = - scoped_pending_buffer_to_decode->decrypt_config()->key_id(); - std::string missing_key_id = base::HexEncode(key_id.data(), key_id.size()); - DVLOG(1) << "DeliverFrame() - no key for key ID " << missing_key_id; - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() << ": no key for key ID " - << missing_key_id; - - // Set |pending_buffer_to_decode_| back as we need to try decoding the - // pending buffer again when new key is added to the decryptor. - pending_buffer_to_decode_ = scoped_pending_buffer_to_decode; - - if (need_to_try_again_if_nokey_is_returned) { - // The |state_| is still kPendingDecode. - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key was added, resuming decode"; - DecodePendingBuffer(); - return; - } - - state_ = kWaitingForKey; - waiting_for_decryption_key_cb_.Run(); - return; - } - - if (status == Decryptor::kNeedMoreData) { - DVLOG(2) << "DeliverFrame() - kNeedMoreData"; - state_ = scoped_pending_buffer_to_decode->end_of_stream() ? kDecodeFinished - : kIdle; - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::OK); - return; - } - - DCHECK_EQ(status, Decryptor::kSuccess); - DCHECK(!frames.empty()); - ProcessDecodedFrames(frames); - - if (scoped_pending_buffer_to_decode->end_of_stream()) { - // Set |pending_buffer_to_decode_| back as we need to keep flushing the - // decryptor until kNeedMoreData is returned. - pending_buffer_to_decode_ = scoped_pending_buffer_to_decode; - DecodePendingBuffer(); - return; - } - - state_ = kIdle; - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::OK); -} - -void DecryptingAudioDecoder::OnKeyAdded() { - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (state_ == kPendingDecode) { - key_added_while_decode_pending_ = true; - return; - } - - if (state_ == kWaitingForKey) { - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key added, resuming decode"; - state_ = kPendingDecode; - DecodePendingBuffer(); - } -} - -void DecryptingAudioDecoder::DoReset() { - DCHECK(init_cb_.is_null()); - DCHECK(decode_cb_.is_null()); - timestamp_helper_->SetBaseTimestamp(kNoTimestamp); - state_ = kIdle; - base::ResetAndReturn(&reset_cb_).Run(); -} - -void DecryptingAudioDecoder::ProcessDecodedFrames( - const Decryptor::AudioFrames& frames) { - for (Decryptor::AudioFrames::const_iterator iter = frames.begin(); - iter != frames.end(); ++iter) { - scoped_refptr<AudioBuffer> frame = *iter; - - DCHECK(!frame->end_of_stream()) << "EOS frame returned."; - DCHECK_GT(frame->frame_count(), 0) << "Empty frame returned."; - - base::TimeDelta current_time = timestamp_helper_->GetTimestamp(); - if (IsOutOfSync(current_time, frame->timestamp())) { - DVLOG(1) << "Timestamp returned by the decoder (" - << frame->timestamp().InMilliseconds() << " ms)" - << " does not match the input timestamp and number of samples" - << " decoded (" << current_time.InMilliseconds() << " ms)."; - } - - frame->set_timestamp(current_time); - timestamp_helper_->AddFrames(frame->frame_count()); - - output_cb_.Run(frame); - } -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/decrypting_audio_decoder.h b/src/cobalt/media/filters/decrypting_audio_decoder.h deleted file mode 100644 index 9ee686e..0000000 --- a/src/cobalt/media/filters/decrypting_audio_decoder.h +++ /dev/null
@@ -1,128 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef COBALT_MEDIA_FILTERS_DECRYPTING_AUDIO_DECODER_H_ -#define COBALT_MEDIA_FILTERS_DECRYPTING_AUDIO_DECODER_H_ - -#include <memory> -#include <string> - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/time.h" -#include "cobalt/media/base/audio_decoder.h" -#include "cobalt/media/base/cdm_context.h" -#include "cobalt/media/base/decryptor.h" -#include "cobalt/media/base/demuxer_stream.h" - -namespace base { -class SingleThreadTaskRunner; -} - -namespace cobalt { -namespace media { - -class AudioTimestampHelper; -class DecoderBuffer; -class Decryptor; -class MediaLog; - -// Decryptor-based AudioDecoder implementation that can decrypt and decode -// encrypted audio buffers and return decrypted and decompressed audio frames. -// All public APIs and callbacks are trampolined to the |task_runner_| so -// that no locks are required for thread safety. -class MEDIA_EXPORT DecryptingAudioDecoder : public AudioDecoder { - public: - DecryptingAudioDecoder( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - const scoped_refptr<MediaLog>& media_log, - const base::Closure& waiting_for_decryption_key_cb); - ~DecryptingAudioDecoder() OVERRIDE; - - // AudioDecoder implementation. - std::string GetDisplayName() const OVERRIDE; - void Initialize(const AudioDecoderConfig& config, CdmContext* cdm_context, - const InitCB& init_cb, const OutputCB& output_cb) OVERRIDE; - void Decode(const scoped_refptr<DecoderBuffer>& buffer, - const DecodeCB& decode_cb) OVERRIDE; - void Reset(const base::Closure& closure) OVERRIDE; - - private: - // For a detailed state diagram please see this link: http://goo.gl/8jAok - // TODO(xhwang): Add a ASCII state diagram in this file after this class - // stabilizes. - // TODO(xhwang): Update this diagram for DecryptingAudioDecoder. - enum State { - kUninitialized = 0, - kPendingDecoderInit, - kIdle, - kPendingDecode, - kWaitingForKey, - kDecodeFinished, - kError - }; - - // Initializes the audio decoder on the |decryptor_| with |config_|. - void InitializeDecoder(); - - // Callback for Decryptor::InitializeAudioDecoder() during initialization. - void FinishInitialization(bool success); - - void DecodePendingBuffer(); - - // Callback for Decryptor::DecryptAndDecodeAudio(). - void DeliverFrame(int buffer_size, Decryptor::Status status, - const Decryptor::AudioFrames& frames); - - // Callback for the |decryptor_| to notify this object that a new key has been - // added. - void OnKeyAdded(); - - // Resets decoder and calls |reset_cb_|. - void DoReset(); - - // Sets timestamps for |frames| and then passes them to |output_cb_|. - void ProcessDecodedFrames(const Decryptor::AudioFrames& frames); - - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - - scoped_refptr<MediaLog> media_log_; - - State state_; - - InitCB init_cb_; - OutputCB output_cb_; - DecodeCB decode_cb_; - base::Closure reset_cb_; - base::Closure waiting_for_decryption_key_cb_; - - // The current decoder configuration. - AudioDecoderConfig config_; - - Decryptor* decryptor_; - - // The buffer that needs decrypting/decoding. - scoped_refptr<media::DecoderBuffer> pending_buffer_to_decode_; - - // Indicates the situation where new key is added during pending decode - // (in other words, this variable can only be set in state kPendingDecode). - // If this variable is true and kNoKey is returned then we need to try - // decrypting/decoding again in case the newly added key is the correct - // decryption key. - bool key_added_while_decode_pending_; - - std::unique_ptr<AudioTimestampHelper> timestamp_helper_; - - base::WeakPtr<DecryptingAudioDecoder> weak_this_; - base::WeakPtrFactory<DecryptingAudioDecoder> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(DecryptingAudioDecoder); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_DECRYPTING_AUDIO_DECODER_H_
diff --git a/src/cobalt/media/filters/decrypting_audio_decoder_unittest.cc b/src/cobalt/media/filters/decrypting_audio_decoder_unittest.cc deleted file mode 100644 index 3c90504..0000000 --- a/src/cobalt/media/filters/decrypting_audio_decoder_unittest.cc +++ /dev/null
@@ -1,465 +0,0 @@ -// Copyright (c) 2012 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 <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/message_loop.h" -#include "base/run_loop.h" -#include "cobalt/media/base/audio_buffer.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/decrypt_config.h" -#include "cobalt/media/base/gmock_callback_support.h" -#include "cobalt/media/base/media_util.h" -#include "cobalt/media/base/mock_filters.h" -#include "cobalt/media/base/test_helpers.h" -#include "cobalt/media/base/timestamp_constants.h" -#include "cobalt/media/filters/decrypting_audio_decoder.h" -#include "starboard/types.h" -#include "testing/gmock/include/gmock/gmock.h" - -using ::testing::_; -using ::testing::AtMost; -using ::testing::Return; -using ::testing::SaveArg; -using ::testing::StrictMock; - -namespace cobalt { -namespace media { - -const int kSampleRate = 44100; - -// Make sure the kFakeAudioFrameSize is a valid frame size for all audio decoder -// configs used in this test. -const int kFakeAudioFrameSize = 48; -const uint8_t kFakeKeyId[] = {0x4b, 0x65, 0x79, 0x20, 0x49, 0x44}; -const uint8_t kFakeIv[DecryptConfig::kDecryptionKeySize] = {0}; -const int kDecodingDelay = 3; - -// Create a fake non-empty encrypted buffer. -static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() { - const int buffer_size = 16; // Need a non-empty buffer; - scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(buffer_size)); - buffer->set_decrypt_config(std::unique_ptr<DecryptConfig>(new DecryptConfig( - std::string(reinterpret_cast<const char*>(kFakeKeyId), - arraysize(kFakeKeyId)), - std::string(reinterpret_cast<const char*>(kFakeIv), arraysize(kFakeIv)), - std::vector<SubsampleEntry>()))); - return buffer; -} - -// Use anonymous namespace here to prevent the actions to be defined multiple -// times across multiple test files. Sadly we can't use static for them. -namespace { - -ACTION_P(ReturnBuffer, buffer) { return buffer; } - -} // namespace - -class DecryptingAudioDecoderTest : public testing::Test { - public: - DecryptingAudioDecoderTest() - : decoder_(new DecryptingAudioDecoder( - message_loop_.task_runner(), new MediaLog(), - base::Bind(&DecryptingAudioDecoderTest::OnWaitingForDecryptionKey, - base::Unretained(this)))), - cdm_context_(new StrictMock<MockCdmContext>()), - decryptor_(new StrictMock<MockDecryptor>()), - num_decrypt_and_decode_calls_(0), - num_frames_in_decryptor_(0), - encrypted_buffer_(CreateFakeEncryptedBuffer()), - decoded_frame_(NULL), - decoded_frame_list_() {} - - virtual ~DecryptingAudioDecoderTest() { Destroy(); } - - void InitializeAndExpectResult(const AudioDecoderConfig& config, - bool success) { - // Initialize data now that the config is known. Since the code uses - // invalid values (that CreateEmptyBuffer() doesn't support), tweak them - // just for CreateEmptyBuffer(). - int channels = ChannelLayoutToChannelCount(config.channel_layout()); - if (channels < 0) channels = 0; - decoded_frame_ = AudioBuffer::CreateEmptyBuffer( - config.channel_layout(), channels, kSampleRate, kFakeAudioFrameSize, - kNoTimestamp); - decoded_frame_list_.push_back(decoded_frame_); - - decoder_->Initialize(config, cdm_context_.get(), NewExpectedBoolCB(success), - base::Bind(&DecryptingAudioDecoderTest::FrameReady, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - enum CdmType { CDM_WITHOUT_DECRYPTOR, CDM_WITH_DECRYPTOR }; - - void SetCdmType(CdmType cdm_type) { - const bool has_decryptor = cdm_type == CDM_WITH_DECRYPTOR; - EXPECT_CALL(*cdm_context_, GetDecryptor()) - .WillRepeatedly(Return(has_decryptor ? decryptor_.get() : NULL)); - } - - void Initialize() { - SetCdmType(CDM_WITH_DECRYPTOR); - EXPECT_CALL(*decryptor_, InitializeAudioDecoder(_, _)) - .Times(AtMost(1)) - .WillOnce(RunCallback<1>(true)); - EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kAudio, _)) - .WillOnce(SaveArg<1>(&key_added_cb_)); - - config_.Initialize(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, kSampleRate, EmptyExtraData(), - AesCtrEncryptionScheme(), base::TimeDelta(), 0); - InitializeAndExpectResult(config_, true); - } - - void Reinitialize() { ReinitializeConfigChange(config_); } - - void ReinitializeConfigChange(const AudioDecoderConfig& new_config) { - EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kAudio)); - EXPECT_CALL(*decryptor_, InitializeAudioDecoder(_, _)) - .WillOnce(RunCallback<1>(true)); - EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kAudio, _)) - .WillOnce(SaveArg<1>(&key_added_cb_)); - decoder_->Initialize(new_config, NULL, NewExpectedBoolCB(true), - base::Bind(&DecryptingAudioDecoderTest::FrameReady, - base::Unretained(this))); - } - - // Decode |buffer| and expect DecodeDone to get called with |status|. - void DecodeAndExpect(const scoped_refptr<DecoderBuffer>& buffer, - DecodeStatus status) { - EXPECT_CALL(*this, DecodeDone(status)); - decoder_->Decode(buffer, base::Bind(&DecryptingAudioDecoderTest::DecodeDone, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - // Helper function to simulate the decrypting and decoding process in the - // |decryptor_| with a decoding delay of kDecodingDelay buffers. - void DecryptAndDecodeAudio(const scoped_refptr<DecoderBuffer>& encrypted, - const Decryptor::AudioDecodeCB& audio_decode_cb) { - num_decrypt_and_decode_calls_++; - if (!encrypted->end_of_stream()) num_frames_in_decryptor_++; - - if (num_decrypt_and_decode_calls_ <= kDecodingDelay || - num_frames_in_decryptor_ == 0) { - audio_decode_cb.Run(Decryptor::kNeedMoreData, Decryptor::AudioFrames()); - return; - } - - num_frames_in_decryptor_--; - audio_decode_cb.Run(Decryptor::kSuccess, - Decryptor::AudioFrames(1, decoded_frame_)); - } - - // Sets up expectations and actions to put DecryptingAudioDecoder in an - // active normal decoding state. - void EnterNormalDecodingState() { - EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) - .WillRepeatedly( - Invoke(this, &DecryptingAudioDecoderTest::DecryptAndDecodeAudio)); - EXPECT_CALL(*this, FrameReady(decoded_frame_)); - for (int i = 0; i < kDecodingDelay + 1; ++i) - DecodeAndExpect(encrypted_buffer_, DecodeStatus::OK); - } - - // Sets up expectations and actions to put DecryptingAudioDecoder in an end - // of stream state. This function must be called after - // EnterNormalDecodingState() to work. - void EnterEndOfStreamState() { - // The codec in the |decryptor_| will be flushed. - EXPECT_CALL(*this, FrameReady(decoded_frame_)).Times(kDecodingDelay); - DecodeAndExpect(DecoderBuffer::CreateEOSBuffer(), DecodeStatus::OK); - EXPECT_EQ(0, num_frames_in_decryptor_); - } - - // Make the audio decode callback pending by saving and not firing it. - void EnterPendingDecodeState() { - EXPECT_TRUE(pending_audio_decode_cb_.is_null()); - EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(encrypted_buffer_, _)) - .WillOnce(SaveArg<1>(&pending_audio_decode_cb_)); - - decoder_->Decode(encrypted_buffer_, - base::Bind(&DecryptingAudioDecoderTest::DecodeDone, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - // Make sure the Decode() on the decoder triggers a DecryptAndDecode() on - // the decryptor. - EXPECT_FALSE(pending_audio_decode_cb_.is_null()); - } - - void EnterWaitingForKeyState() { - EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(encrypted_buffer_, _)) - .WillRepeatedly( - RunCallback<1>(Decryptor::kNoKey, Decryptor::AudioFrames())); - EXPECT_CALL(*this, OnWaitingForDecryptionKey()); - decoder_->Decode(encrypted_buffer_, - base::Bind(&DecryptingAudioDecoderTest::DecodeDone, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - void AbortPendingAudioDecodeCB() { - if (!pending_audio_decode_cb_.is_null()) { - base::ResetAndReturn(&pending_audio_decode_cb_) - .Run(Decryptor::kSuccess, Decryptor::AudioFrames()); - } - } - - void AbortAllPendingCBs() { - if (!pending_init_cb_.is_null()) { - ASSERT_TRUE(pending_audio_decode_cb_.is_null()); - base::ResetAndReturn(&pending_init_cb_).Run(false); - return; - } - - AbortPendingAudioDecodeCB(); - } - - void Reset() { - EXPECT_CALL(*decryptor_, ResetDecoder(Decryptor::kAudio)) - .WillRepeatedly(InvokeWithoutArgs( - this, &DecryptingAudioDecoderTest::AbortPendingAudioDecodeCB)); - - decoder_->Reset(NewExpectedClosure()); - base::RunLoop().RunUntilIdle(); - } - - void Destroy() { - EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kAudio)) - .WillRepeatedly(InvokeWithoutArgs( - this, &DecryptingAudioDecoderTest::AbortAllPendingCBs)); - - decoder_.reset(); - base::RunLoop().RunUntilIdle(); - } - - MOCK_METHOD1(FrameReady, void(const scoped_refptr<AudioBuffer>&)); - MOCK_METHOD1(DecodeDone, void(DecodeStatus)); - - MOCK_METHOD0(OnWaitingForDecryptionKey, void(void)); - - base::MessageLoop message_loop_; - std::unique_ptr<DecryptingAudioDecoder> decoder_; - std::unique_ptr<StrictMock<MockCdmContext>> cdm_context_; - std::unique_ptr<StrictMock<MockDecryptor>> decryptor_; - AudioDecoderConfig config_; - - // Variables to help the |decryptor_| to simulate decoding delay and flushing. - int num_decrypt_and_decode_calls_; - int num_frames_in_decryptor_; - - Decryptor::DecoderInitCB pending_init_cb_; - Decryptor::NewKeyCB key_added_cb_; - Decryptor::AudioDecodeCB pending_audio_decode_cb_; - - // Constant buffer/frames, to be used/returned by |decoder_| and |decryptor_|. - scoped_refptr<DecoderBuffer> encrypted_buffer_; - scoped_refptr<AudioBuffer> decoded_frame_; - Decryptor::AudioFrames decoded_frame_list_; - - private: - DISALLOW_COPY_AND_ASSIGN(DecryptingAudioDecoderTest); -}; - -TEST_F(DecryptingAudioDecoderTest, Initialize_Normal) { Initialize(); } - -// Ensure that DecryptingAudioDecoder only accepts encrypted audio. -TEST_F(DecryptingAudioDecoderTest, Initialize_UnencryptedAudioConfig) { - AudioDecoderConfig config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, kSampleRate, - EmptyExtraData(), Unencrypted()); - - InitializeAndExpectResult(config, false); -} - -// Ensure decoder handles invalid audio configs without crashing. -TEST_F(DecryptingAudioDecoderTest, Initialize_InvalidAudioConfig) { - AudioDecoderConfig config(kUnknownAudioCodec, kUnknownSampleFormat, - CHANNEL_LAYOUT_STEREO, 0, EmptyExtraData(), - AesCtrEncryptionScheme()); - - InitializeAndExpectResult(config, false); -} - -// Ensure decoder handles unsupported audio configs without crashing. -TEST_F(DecryptingAudioDecoderTest, Initialize_UnsupportedAudioConfig) { - SetCdmType(CDM_WITH_DECRYPTOR); - EXPECT_CALL(*decryptor_, InitializeAudioDecoder(_, _)) - .WillOnce(RunCallback<1>(false)); - - AudioDecoderConfig config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, kSampleRate, - EmptyExtraData(), AesCtrEncryptionScheme()); - InitializeAndExpectResult(config, false); -} - -TEST_F(DecryptingAudioDecoderTest, Initialize_CdmWithoutDecryptor) { - SetCdmType(CDM_WITHOUT_DECRYPTOR); - AudioDecoderConfig config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, kSampleRate, - EmptyExtraData(), AesCtrEncryptionScheme()); - InitializeAndExpectResult(config, false); -} - -// Test normal decrypt and decode case. -TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_Normal) { - Initialize(); - EnterNormalDecodingState(); -} - -// Test the case where the decryptor returns error when doing decrypt and -// decode. -TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_DecodeError) { - Initialize(); - - EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) - .WillRepeatedly( - RunCallback<1>(Decryptor::kError, Decryptor::AudioFrames())); - - DecodeAndExpect(encrypted_buffer_, DecodeStatus::DECODE_ERROR); -} - -// Test the case where the decryptor returns multiple decoded frames. -TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_MultipleFrames) { - Initialize(); - - scoped_refptr<AudioBuffer> frame_a = AudioBuffer::CreateEmptyBuffer( - config_.channel_layout(), - ChannelLayoutToChannelCount(config_.channel_layout()), kSampleRate, - kFakeAudioFrameSize, kNoTimestamp); - scoped_refptr<AudioBuffer> frame_b = AudioBuffer::CreateEmptyBuffer( - config_.channel_layout(), - ChannelLayoutToChannelCount(config_.channel_layout()), kSampleRate, - kFakeAudioFrameSize, kNoTimestamp); - decoded_frame_list_.push_back(frame_a); - decoded_frame_list_.push_back(frame_b); - - EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) - .WillOnce(RunCallback<1>(Decryptor::kSuccess, decoded_frame_list_)); - - EXPECT_CALL(*this, FrameReady(decoded_frame_)); - EXPECT_CALL(*this, FrameReady(frame_a)); - EXPECT_CALL(*this, FrameReady(frame_b)); - DecodeAndExpect(encrypted_buffer_, DecodeStatus::OK); -} - -// Test the case where the decryptor receives end-of-stream buffer. -TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_EndOfStream) { - Initialize(); - EnterNormalDecodingState(); - EnterEndOfStreamState(); -} - -// Test reinitializing decode with a new config -TEST_F(DecryptingAudioDecoderTest, Reinitialize_ConfigChange) { - Initialize(); - - EXPECT_CALL(*decryptor_, InitializeAudioDecoder(_, _)) - .Times(AtMost(1)) - .WillOnce(RunCallback<1>(true)); - - // The new config is different from the initial config in bits-per-channel, - // channel layout and samples_per_second. - AudioDecoderConfig new_config(kCodecVorbis, kSampleFormatPlanarS16, - CHANNEL_LAYOUT_5_1, 88200, EmptyExtraData(), - AesCtrEncryptionScheme()); - EXPECT_NE(new_config.bits_per_channel(), config_.bits_per_channel()); - EXPECT_NE(new_config.channel_layout(), config_.channel_layout()); - EXPECT_NE(new_config.samples_per_second(), config_.samples_per_second()); - - ReinitializeConfigChange(new_config); - base::RunLoop().RunUntilIdle(); -} - -// Test the case where the a key is added when the decryptor is in -// kWaitingForKey state. -TEST_F(DecryptingAudioDecoderTest, KeyAdded_DuringWaitingForKey) { - Initialize(); - EnterWaitingForKeyState(); - - EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kSuccess, decoded_frame_list_)); - EXPECT_CALL(*this, FrameReady(decoded_frame_)); - EXPECT_CALL(*this, DecodeDone(DecodeStatus::OK)); - key_added_cb_.Run(); - base::RunLoop().RunUntilIdle(); -} - -// Test the case where the a key is added when the decryptor is in -// kPendingDecode state. -TEST_F(DecryptingAudioDecoderTest, KeyAdded_DruingPendingDecode) { - Initialize(); - EnterPendingDecodeState(); - - EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kSuccess, decoded_frame_list_)); - EXPECT_CALL(*this, FrameReady(decoded_frame_)); - EXPECT_CALL(*this, DecodeDone(DecodeStatus::OK)); - // The audio decode callback is returned after the correct decryption key is - // added. - key_added_cb_.Run(); - base::ResetAndReturn(&pending_audio_decode_cb_) - .Run(Decryptor::kNoKey, Decryptor::AudioFrames()); - base::RunLoop().RunUntilIdle(); -} - -// Test resetting when the decoder is in kIdle state but has not decoded any -// frame. -TEST_F(DecryptingAudioDecoderTest, Reset_DuringIdleAfterInitialization) { - Initialize(); - Reset(); -} - -// Test resetting when the decoder is in kIdle state after it has decoded one -// frame. -TEST_F(DecryptingAudioDecoderTest, Reset_DuringIdleAfterDecodedOneFrame) { - Initialize(); - EnterNormalDecodingState(); - Reset(); -} - -// Test resetting when the decoder is in kPendingDecode state. -TEST_F(DecryptingAudioDecoderTest, Reset_DuringPendingDecode) { - Initialize(); - EnterPendingDecodeState(); - - EXPECT_CALL(*this, DecodeDone(DecodeStatus::ABORTED)); - - Reset(); -} - -// Test resetting when the decoder is in kWaitingForKey state. -TEST_F(DecryptingAudioDecoderTest, Reset_DuringWaitingForKey) { - Initialize(); - EnterWaitingForKeyState(); - - EXPECT_CALL(*this, DecodeDone(DecodeStatus::ABORTED)); - - Reset(); -} - -// Test resetting when the decoder has hit end of stream and is in -// kDecodeFinished state. -TEST_F(DecryptingAudioDecoderTest, Reset_AfterDecodeFinished) { - Initialize(); - EnterNormalDecodingState(); - EnterEndOfStreamState(); - Reset(); -} - -// Test resetting after the decoder has been reset. -TEST_F(DecryptingAudioDecoderTest, Reset_AfterReset) { - Initialize(); - EnterNormalDecodingState(); - Reset(); - Reset(); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/decrypting_demuxer_stream.cc b/src/cobalt/media/filters/decrypting_demuxer_stream.cc deleted file mode 100644 index 31d1295..0000000 --- a/src/cobalt/media/filters/decrypting_demuxer_stream.cc +++ /dev/null
@@ -1,397 +0,0 @@ -// Copyright (c) 2012 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/filters/decrypting_demuxer_stream.h" - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/single_thread_task_runner.h" -#include "base/string_number_conversions.h" -#include "cobalt/media/base/bind_to_current_loop.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/media_log.h" -#include "cobalt/media/base/media_util.h" - -namespace cobalt { -namespace media { - -static bool IsStreamValidAndEncrypted(DemuxerStream* stream) { - return ((stream->type() == DemuxerStream::AUDIO && - stream->audio_decoder_config().IsValidConfig() && - stream->audio_decoder_config().is_encrypted()) || - (stream->type() == DemuxerStream::VIDEO && - stream->video_decoder_config().IsValidConfig() && - stream->video_decoder_config().is_encrypted())); -} - -DecryptingDemuxerStream::DecryptingDemuxerStream( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - const scoped_refptr<MediaLog>& media_log, - const base::Closure& waiting_for_decryption_key_cb) - : task_runner_(task_runner), - media_log_(media_log), - state_(kUninitialized), - waiting_for_decryption_key_cb_(waiting_for_decryption_key_cb), - demuxer_stream_(NULL), - decryptor_(NULL), - key_added_while_decrypt_pending_(false), - weak_factory_(this) {} - -std::string DecryptingDemuxerStream::GetDisplayName() const { - return "DecryptingDemuxerStream"; -} - -void DecryptingDemuxerStream::Initialize(DemuxerStream* stream, - CdmContext* cdm_context, - const PipelineStatusCB& status_cb) { - DVLOG(2) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kUninitialized) << state_; - DCHECK(stream); - DCHECK(cdm_context); - DCHECK(!demuxer_stream_); - - weak_this_ = weak_factory_.GetWeakPtr(); - demuxer_stream_ = stream; - init_cb_ = BindToCurrentLoop(status_cb); - - InitializeDecoderConfig(); - - if (!cdm_context->GetDecryptor()) { - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() << ": no decryptor"; - state_ = kUninitialized; - base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); - return; - } - - decryptor_ = cdm_context->GetDecryptor(); - - decryptor_->RegisterNewKeyCB( - GetDecryptorStreamType(), - BindToCurrentLoop( - base::Bind(&DecryptingDemuxerStream::OnKeyAdded, weak_this_))); - - state_ = kIdle; - base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); -} - -void DecryptingDemuxerStream::Read(const ReadCB& read_cb) { - DVLOG(3) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kIdle) << state_; - DCHECK(!read_cb.is_null()); - CHECK(read_cb_.is_null()) << "Overlapping reads are not supported."; - - read_cb_ = BindToCurrentLoop(read_cb); - state_ = kPendingDemuxerRead; - demuxer_stream_->Read( - base::Bind(&DecryptingDemuxerStream::DecryptBuffer, weak_this_)); -} - -void DecryptingDemuxerStream::Reset(const base::Closure& closure) { - DVLOG(2) << __func__ << " - state: " << state_; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ != kUninitialized) << state_; - DCHECK(reset_cb_.is_null()); - - reset_cb_ = BindToCurrentLoop(closure); - - decryptor_->CancelDecrypt(GetDecryptorStreamType()); - - // Reset() cannot complete if the read callback is still pending. - // Defer the resetting process in this case. The |reset_cb_| will be fired - // after the read callback is fired - see DoDecryptBuffer() and - // DoDeliverBuffer(). - if (state_ == kPendingDemuxerRead || state_ == kPendingDecrypt) { - DCHECK(!read_cb_.is_null()); - return; - } - - if (state_ == kWaitingForKey) { - DCHECK(!read_cb_.is_null()); - pending_buffer_to_decrypt_ = NULL; - base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); - } - - DCHECK(read_cb_.is_null()); - DoReset(); -} - -AudioDecoderConfig DecryptingDemuxerStream::audio_decoder_config() { - DCHECK(state_ != kUninitialized) << state_; - CHECK_EQ(demuxer_stream_->type(), AUDIO); - return audio_config_; -} - -VideoDecoderConfig DecryptingDemuxerStream::video_decoder_config() { - DCHECK(state_ != kUninitialized) << state_; - CHECK_EQ(demuxer_stream_->type(), VIDEO); - return video_config_; -} - -DemuxerStream::Type DecryptingDemuxerStream::type() const { - DCHECK(state_ != kUninitialized) << state_; - return demuxer_stream_->type(); -} - -DemuxerStream::Liveness DecryptingDemuxerStream::liveness() const { - DCHECK(state_ != kUninitialized) << state_; - return demuxer_stream_->liveness(); -} - -void DecryptingDemuxerStream::EnableBitstreamConverter() { - demuxer_stream_->EnableBitstreamConverter(); -} - -bool DecryptingDemuxerStream::SupportsConfigChanges() { - return demuxer_stream_->SupportsConfigChanges(); -} - -VideoRotation DecryptingDemuxerStream::video_rotation() { - return demuxer_stream_->video_rotation(); -} - -bool DecryptingDemuxerStream::enabled() const { - return demuxer_stream_->enabled(); -} - -void DecryptingDemuxerStream::set_enabled(bool enabled, - base::TimeDelta timestamp) { - demuxer_stream_->set_enabled(enabled, timestamp); -} - -void DecryptingDemuxerStream::SetStreamStatusChangeCB( - const StreamStatusChangeCB& cb) { - demuxer_stream_->SetStreamStatusChangeCB(cb); -} - -DecryptingDemuxerStream::~DecryptingDemuxerStream() { - DVLOG(2) << __func__ << " : state_ = " << state_; - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (state_ == kUninitialized) return; - - if (decryptor_) { - decryptor_->CancelDecrypt(GetDecryptorStreamType()); - decryptor_ = NULL; - } - if (!init_cb_.is_null()) - base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_ABORT); - if (!read_cb_.is_null()) base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); - if (!reset_cb_.is_null()) base::ResetAndReturn(&reset_cb_).Run(); - pending_buffer_to_decrypt_ = NULL; -} - -void DecryptingDemuxerStream::DecryptBuffer( - DemuxerStream::Status status, const scoped_refptr<DecoderBuffer>& buffer) { - DVLOG(3) << __func__; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kPendingDemuxerRead) << state_; - DCHECK(!read_cb_.is_null()); - DCHECK_EQ(buffer.get() != NULL, status == kOk) << status; - - // Even when |!reset_cb_.is_null()|, we need to pass |kConfigChanged| back to - // the caller so that the downstream decoder can be properly reinitialized. - if (status == kConfigChanged) { - DVLOG(2) << "DoDecryptBuffer() - kConfigChanged."; - DCHECK_EQ(demuxer_stream_->type() == AUDIO, audio_config_.IsValidConfig()); - DCHECK_EQ(demuxer_stream_->type() == VIDEO, video_config_.IsValidConfig()); - - // Update the decoder config, which the decoder will use when it is notified - // of kConfigChanged. - InitializeDecoderConfig(); - - state_ = kIdle; - base::ResetAndReturn(&read_cb_).Run(kConfigChanged, NULL); - if (!reset_cb_.is_null()) DoReset(); - return; - } - - if (!reset_cb_.is_null()) { - base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); - DoReset(); - return; - } - - if (status == kAborted) { - DVLOG(2) << "DoDecryptBuffer() - kAborted."; - state_ = kIdle; - base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); - return; - } - - if (buffer->end_of_stream()) { - DVLOG(2) << "DoDecryptBuffer() - EOS buffer."; - state_ = kIdle; - base::ResetAndReturn(&read_cb_).Run(status, buffer); - return; - } - - DCHECK(buffer->decrypt_config()); - // An empty iv string signals that the frame is unencrypted. - if (buffer->decrypt_config()->iv().empty()) { - DVLOG(2) << "DoDecryptBuffer() - clear buffer."; - scoped_refptr<DecoderBuffer> decrypted = - DecoderBuffer::CopyFrom(buffer->data(), buffer->data_size()); - decrypted->set_timestamp(buffer->timestamp()); - decrypted->set_duration(buffer->duration()); - if (buffer->is_key_frame()) decrypted->set_is_key_frame(true); - - state_ = kIdle; - base::ResetAndReturn(&read_cb_).Run(kOk, decrypted); - return; - } - - pending_buffer_to_decrypt_ = buffer; - state_ = kPendingDecrypt; - DecryptPendingBuffer(); -} - -void DecryptingDemuxerStream::DecryptPendingBuffer() { - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kPendingDecrypt) << state_; - decryptor_->Decrypt( - GetDecryptorStreamType(), pending_buffer_to_decrypt_, - BindToCurrentLoop( - base::Bind(&DecryptingDemuxerStream::DeliverBuffer, weak_this_))); -} - -void DecryptingDemuxerStream::DeliverBuffer( - Decryptor::Status status, - const scoped_refptr<DecoderBuffer>& decrypted_buffer) { - DVLOG(3) << __func__ << " - status: " << status; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kPendingDecrypt) << state_; - DCHECK_NE(status, Decryptor::kNeedMoreData); - DCHECK(!read_cb_.is_null()); - DCHECK(pending_buffer_to_decrypt_.get()); - - bool need_to_try_again_if_nokey = key_added_while_decrypt_pending_; - key_added_while_decrypt_pending_ = false; - - if (!reset_cb_.is_null()) { - pending_buffer_to_decrypt_ = NULL; - base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); - DoReset(); - return; - } - - DCHECK_EQ(status == Decryptor::kSuccess, decrypted_buffer.get() != NULL); - - if (status == Decryptor::kError) { - DVLOG(2) << "DoDeliverBuffer() - kError"; - MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": decrypt error"; - pending_buffer_to_decrypt_ = NULL; - state_ = kIdle; - base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); - return; - } - - if (status == Decryptor::kNoKey) { - std::string key_id = pending_buffer_to_decrypt_->decrypt_config()->key_id(); - std::string missing_key_id = base::HexEncode(key_id.data(), key_id.size()); - DVLOG(1) << "DeliverBuffer() - no key for key ID " << missing_key_id; - MEDIA_LOG(INFO, media_log_) << GetDisplayName() << ": no key for key ID " - << missing_key_id; - - if (need_to_try_again_if_nokey) { - // The |state_| is still kPendingDecrypt. - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key was added, resuming decrypt"; - DecryptPendingBuffer(); - return; - } - - state_ = kWaitingForKey; - waiting_for_decryption_key_cb_.Run(); - return; - } - - DCHECK_EQ(status, Decryptor::kSuccess); - - // Copy the key frame flag from the encrypted to decrypted buffer, assuming - // that the decryptor initialized the flag to false. - if (pending_buffer_to_decrypt_->is_key_frame()) - decrypted_buffer->set_is_key_frame(true); - - pending_buffer_to_decrypt_ = NULL; - state_ = kIdle; - base::ResetAndReturn(&read_cb_).Run(kOk, decrypted_buffer); -} - -void DecryptingDemuxerStream::OnKeyAdded() { - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (state_ == kPendingDecrypt) { - key_added_while_decrypt_pending_ = true; - return; - } - - if (state_ == kWaitingForKey) { - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key added, resuming decrypt"; - state_ = kPendingDecrypt; - DecryptPendingBuffer(); - } -} - -void DecryptingDemuxerStream::DoReset() { - DCHECK(state_ != kUninitialized); - DCHECK(init_cb_.is_null()); - DCHECK(read_cb_.is_null()); - - state_ = kIdle; - - base::ResetAndReturn(&reset_cb_).Run(); -} - -Decryptor::StreamType DecryptingDemuxerStream::GetDecryptorStreamType() const { - if (demuxer_stream_->type() == AUDIO) return Decryptor::kAudio; - - DCHECK_EQ(demuxer_stream_->type(), VIDEO); - return Decryptor::kVideo; -} - -void DecryptingDemuxerStream::InitializeDecoderConfig() { - // The decoder selector or upstream demuxer make sure the stream is valid and - // potentially encrypted. - DCHECK(IsStreamValidAndEncrypted(demuxer_stream_)); - - switch (demuxer_stream_->type()) { - case AUDIO: { - AudioDecoderConfig input_audio_config = - demuxer_stream_->audio_decoder_config(); - audio_config_.Initialize( - input_audio_config.codec(), input_audio_config.sample_format(), - input_audio_config.channel_layout(), - input_audio_config.samples_per_second(), - input_audio_config.extra_data(), Unencrypted(), - input_audio_config.seek_preroll(), input_audio_config.codec_delay()); - break; - } - - case VIDEO: { - VideoDecoderConfig input_video_config = - demuxer_stream_->video_decoder_config(); - video_config_.Initialize( - input_video_config.codec(), input_video_config.profile(), - input_video_config.format(), input_video_config.color_space(), - input_video_config.coded_size(), input_video_config.visible_rect(), - input_video_config.natural_size(), input_video_config.extra_data(), - Unencrypted()); - video_config_->set_webm_color_metadata( - input_video_config->webm_color_metadata()); - break; - } - - default: - NOTREACHED(); - return; - } -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/decrypting_demuxer_stream.h b/src/cobalt/media/filters/decrypting_demuxer_stream.h deleted file mode 100644 index c573886..0000000 --- a/src/cobalt/media/filters/decrypting_demuxer_stream.h +++ /dev/null
@@ -1,145 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef COBALT_MEDIA_FILTERS_DECRYPTING_DEMUXER_STREAM_H_ -#define COBALT_MEDIA_FILTERS_DECRYPTING_DEMUXER_STREAM_H_ - -#include <string> - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "cobalt/media/base/audio_decoder_config.h" -#include "cobalt/media/base/cdm_context.h" -#include "cobalt/media/base/decryptor.h" -#include "cobalt/media/base/demuxer_stream.h" -#include "cobalt/media/base/pipeline_status.h" -#include "cobalt/media/base/video_decoder_config.h" - -namespace base { -class SingleThreadTaskRunner; -} - -namespace cobalt { -namespace media { - -class DecoderBuffer; -class MediaLog; - -// Decryptor-based DemuxerStream implementation that converts a potentially -// encrypted demuxer stream to a clear demuxer stream. -// All public APIs and callbacks are trampolined to the |task_runner_| so -// that no locks are required for thread safety. -class MEDIA_EXPORT DecryptingDemuxerStream : public DemuxerStream { - public: - DecryptingDemuxerStream( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - const scoped_refptr<MediaLog>& media_log, - const base::Closure& waiting_for_decryption_key_cb); - - // Cancels all pending operations immediately and fires all pending callbacks. - ~DecryptingDemuxerStream() OVERRIDE; - - // |steram| must be encrypted and |cdm_context| must be non-null. - void Initialize(DemuxerStream* stream, CdmContext* cdm_context, - const PipelineStatusCB& status_cb); - - // Cancels all pending operations and fires all pending callbacks. If in - // kPendingDemuxerRead or kPendingDecrypt state, waits for the pending - // operation to finish before satisfying |closure|. Sets the state to - // kUninitialized if |this| hasn't been initialized, or to kIdle otherwise. - void Reset(const base::Closure& closure); - - // Returns the name of this class for logging purpose. - std::string GetDisplayName() const; - - // DemuxerStream implementation. - void Read(const ReadCB& read_cb) OVERRIDE; - AudioDecoderConfig audio_decoder_config() OVERRIDE; - VideoDecoderConfig video_decoder_config() OVERRIDE; - Type type() const OVERRIDE; - Liveness liveness() const OVERRIDE; - void EnableBitstreamConverter() OVERRIDE; - bool SupportsConfigChanges() OVERRIDE; - VideoRotation video_rotation() OVERRIDE; - bool enabled() const OVERRIDE; - void set_enabled(bool enabled, base::TimeDelta timestamp) OVERRIDE; - void SetStreamStatusChangeCB(const StreamStatusChangeCB& cb) OVERRIDE; - - private: - // For a detailed state diagram please see this link: http://goo.gl/8jAok - // TODO(xhwang): Add a ASCII state diagram in this file after this class - // stabilizes. - // TODO(xhwang): Update this diagram for DecryptingDemuxerStream. - enum State { - kUninitialized = 0, - kIdle, - kPendingDemuxerRead, - kPendingDecrypt, - kWaitingForKey - }; - - // Callback for DemuxerStream::Read(). - void DecryptBuffer(DemuxerStream::Status status, - const scoped_refptr<DecoderBuffer>& buffer); - - void DecryptPendingBuffer(); - - // Callback for Decryptor::Decrypt(). - void DeliverBuffer(Decryptor::Status status, - const scoped_refptr<DecoderBuffer>& decrypted_buffer); - - // Callback for the |decryptor_| to notify this object that a new key has been - // added. - void OnKeyAdded(); - - // Resets decoder and calls |reset_cb_|. - void DoReset(); - - // Returns Decryptor::StreamType converted from |stream_type_|. - Decryptor::StreamType GetDecryptorStreamType() const; - - // Creates and initializes either |audio_config_| or |video_config_| based on - // |demuxer_stream_|. - void InitializeDecoderConfig(); - - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - - scoped_refptr<MediaLog> media_log_; - - State state_; - - PipelineStatusCB init_cb_; - ReadCB read_cb_; - base::Closure reset_cb_; - base::Closure waiting_for_decryption_key_cb_; - - // Pointer to the input demuxer stream that will feed us encrypted buffers. - DemuxerStream* demuxer_stream_; - - AudioDecoderConfig audio_config_; - VideoDecoderConfig video_config_; - - Decryptor* decryptor_; - - // The buffer returned by the demuxer that needs to be decrypted. - scoped_refptr<media::DecoderBuffer> pending_buffer_to_decrypt_; - - // Indicates the situation where new key is added during pending decryption - // (in other words, this variable can only be set in state kPendingDecrypt). - // If this variable is true and kNoKey is returned then we need to try - // decrypting again in case the newly added key is the correct decryption key. - bool key_added_while_decrypt_pending_; - - base::WeakPtr<DecryptingDemuxerStream> weak_this_; - base::WeakPtrFactory<DecryptingDemuxerStream> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(DecryptingDemuxerStream); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_DECRYPTING_DEMUXER_STREAM_H_
diff --git a/src/cobalt/media/filters/decrypting_demuxer_stream_unittest.cc b/src/cobalt/media/filters/decrypting_demuxer_stream_unittest.cc deleted file mode 100644 index 0adf1f4..0000000 --- a/src/cobalt/media/filters/decrypting_demuxer_stream_unittest.cc +++ /dev/null
@@ -1,524 +0,0 @@ -// Copyright (c) 2012 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 <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/message_loop.h" -#include "base/run_loop.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/decrypt_config.h" -#include "cobalt/media/base/gmock_callback_support.h" -#include "cobalt/media/base/media_util.h" -#include "cobalt/media/base/mock_filters.h" -#include "cobalt/media/base/test_helpers.h" -#include "cobalt/media/filters/decrypting_demuxer_stream.h" -#include "starboard/types.h" -#include "testing/gmock/include/gmock/gmock.h" - -using ::testing::_; -using ::testing::IsNull; -using ::testing::Return; -using ::testing::SaveArg; -using ::testing::StrictMock; - -namespace cobalt { -namespace media { - -static const int kFakeBufferSize = 16; -static const uint8_t kFakeKeyId[] = {0x4b, 0x65, 0x79, 0x20, 0x49, 0x44}; -static const uint8_t kFakeIv[DecryptConfig::kDecryptionKeySize] = {0}; - -// Create a fake non-empty buffer in an encrypted stream. When |is_clear| is -// true, the buffer is not encrypted (signaled by an empty IV). -static scoped_refptr<DecoderBuffer> CreateFakeEncryptedStreamBuffer( - bool is_clear) { - scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(kFakeBufferSize)); - std::string iv = is_clear - ? std::string() - : std::string(reinterpret_cast<const char*>(kFakeIv), - arraysize(kFakeIv)); - buffer->set_decrypt_config(std::unique_ptr<DecryptConfig>( - new DecryptConfig(std::string(reinterpret_cast<const char*>(kFakeKeyId), - arraysize(kFakeKeyId)), - iv, std::vector<SubsampleEntry>()))); - return buffer; -} - -// Use anonymous namespace here to prevent the actions to be defined multiple -// times across multiple test files. Sadly we can't use static for them. -namespace { - -ACTION_P(ReturnBuffer, buffer) { - arg0.Run(buffer.get() ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer); -} - -} // namespace - -class DecryptingDemuxerStreamTest : public testing::Test { - public: - DecryptingDemuxerStreamTest() - : demuxer_stream_(new DecryptingDemuxerStream( - message_loop_.task_runner(), new MediaLog(), - base::Bind(&DecryptingDemuxerStreamTest::OnWaitingForDecryptionKey, - base::Unretained(this)))), - cdm_context_(new StrictMock<MockCdmContext>()), - decryptor_(new StrictMock<MockDecryptor>()), - is_initialized_(false), - input_audio_stream_( - new StrictMock<MockDemuxerStream>(DemuxerStream::AUDIO)), - input_video_stream_( - new StrictMock<MockDemuxerStream>(DemuxerStream::VIDEO)), - clear_buffer_(CreateFakeEncryptedStreamBuffer(true)), - encrypted_buffer_(CreateFakeEncryptedStreamBuffer(false)), - decrypted_buffer_(new DecoderBuffer(kFakeBufferSize)) {} - - virtual ~DecryptingDemuxerStreamTest() { - if (is_initialized_) EXPECT_CALL(*decryptor_, CancelDecrypt(_)); - demuxer_stream_.reset(); - base::RunLoop().RunUntilIdle(); - } - - void OnInitialized(PipelineStatus expected_status, PipelineStatus status) { - EXPECT_EQ(expected_status, status); - is_initialized_ = status == PIPELINE_OK; - } - - void InitializeAudioAndExpectStatus(const AudioDecoderConfig& config, - PipelineStatus expected_status) { - input_audio_stream_->set_audio_decoder_config(config); - demuxer_stream_->Initialize( - input_audio_stream_.get(), cdm_context_.get(), - base::Bind(&DecryptingDemuxerStreamTest::OnInitialized, - base::Unretained(this), expected_status)); - base::RunLoop().RunUntilIdle(); - } - - void InitializeVideoAndExpectStatus(const VideoDecoderConfig& config, - PipelineStatus expected_status) { - input_video_stream_->set_video_decoder_config(config); - demuxer_stream_->Initialize( - input_video_stream_.get(), cdm_context_.get(), - base::Bind(&DecryptingDemuxerStreamTest::OnInitialized, - base::Unretained(this), expected_status)); - base::RunLoop().RunUntilIdle(); - } - - enum CdmType { CDM_WITHOUT_DECRYPTOR, CDM_WITH_DECRYPTOR }; - - void SetCdmType(CdmType cdm_type) { - const bool has_decryptor = cdm_type == CDM_WITH_DECRYPTOR; - EXPECT_CALL(*cdm_context_, GetDecryptor()) - .WillRepeatedly(Return(has_decryptor ? decryptor_.get() : NULL)); - } - - // The following functions are used to test stream-type-neutral logic in - // DecryptingDemuxerStream. Therefore, we don't specify audio or video in the - // function names. But for testing purpose, they all use an audio input - // demuxer stream. - - void Initialize() { - SetCdmType(CDM_WITH_DECRYPTOR); - EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kAudio, _)) - .WillOnce(SaveArg<1>(&key_added_cb_)); - - AudioDecoderConfig input_config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, 44100, - EmptyExtraData(), AesCtrEncryptionScheme()); - InitializeAudioAndExpectStatus(input_config, PIPELINE_OK); - - const AudioDecoderConfig& output_config = - demuxer_stream_->audio_decoder_config(); - EXPECT_EQ(DemuxerStream::AUDIO, demuxer_stream_->type()); - EXPECT_FALSE(output_config.is_encrypted()); - EXPECT_EQ(input_config.bits_per_channel(), - output_config.bits_per_channel()); - EXPECT_EQ(input_config.channel_layout(), output_config.channel_layout()); - EXPECT_EQ(input_config.sample_format(), output_config.sample_format()); - EXPECT_EQ(input_config.samples_per_second(), - output_config.samples_per_second()); - } - - void ReadAndExpectBufferReadyWith( - DemuxerStream::Status status, - const scoped_refptr<DecoderBuffer>& decrypted_buffer) { - if (status != DemuxerStream::kOk) - EXPECT_CALL(*this, BufferReady(status, IsNull())); - else if (decrypted_buffer->end_of_stream()) - EXPECT_CALL(*this, BufferReady(status, IsEndOfStream())); - else - EXPECT_CALL(*this, BufferReady(status, decrypted_buffer)); - - demuxer_stream_->Read(base::Bind(&DecryptingDemuxerStreamTest::BufferReady, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - void EnterClearReadingState() { - EXPECT_TRUE(clear_buffer_->decrypt_config()); - EXPECT_CALL(*input_audio_stream_, Read(_)) - .WillOnce(ReturnBuffer(clear_buffer_)); - - // For clearbuffer, decryptor->Decrypt() will not be called. - - scoped_refptr<DecoderBuffer> decrypted_buffer; - EXPECT_CALL(*this, BufferReady(DemuxerStream::kOk, _)) - .WillOnce(SaveArg<1>(&decrypted_buffer)); - demuxer_stream_->Read(base::Bind(&DecryptingDemuxerStreamTest::BufferReady, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - - EXPECT_FALSE(decrypted_buffer->decrypt_config()); - } - - // Sets up expectations and actions to put DecryptingDemuxerStream in an - // active normal reading state. - void EnterNormalReadingState() { - EXPECT_CALL(*input_audio_stream_, Read(_)) - .WillOnce(ReturnBuffer(encrypted_buffer_)); - EXPECT_CALL(*decryptor_, Decrypt(_, _, _)) - .WillOnce(RunCallback<2>(Decryptor::kSuccess, decrypted_buffer_)); - - ReadAndExpectBufferReadyWith(DemuxerStream::kOk, decrypted_buffer_); - } - - // Make the read callback pending by saving and not firing it. - void EnterPendingReadState() { - EXPECT_TRUE(pending_demuxer_read_cb_.is_null()); - EXPECT_CALL(*input_audio_stream_, Read(_)) - .WillOnce(SaveArg<0>(&pending_demuxer_read_cb_)); - demuxer_stream_->Read(base::Bind(&DecryptingDemuxerStreamTest::BufferReady, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - // Make sure the Read() triggers a Read() on the input demuxer stream. - EXPECT_FALSE(pending_demuxer_read_cb_.is_null()); - } - - // Make the decrypt callback pending by saving and not firing it. - void EnterPendingDecryptState() { - EXPECT_TRUE(pending_decrypt_cb_.is_null()); - EXPECT_CALL(*input_audio_stream_, Read(_)) - .WillRepeatedly(ReturnBuffer(encrypted_buffer_)); - EXPECT_CALL(*decryptor_, Decrypt(_, encrypted_buffer_, _)) - .WillOnce(SaveArg<2>(&pending_decrypt_cb_)); - - demuxer_stream_->Read(base::Bind(&DecryptingDemuxerStreamTest::BufferReady, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - // Make sure Read() triggers a Decrypt() on the decryptor. - EXPECT_FALSE(pending_decrypt_cb_.is_null()); - } - - void EnterWaitingForKeyState() { - EXPECT_CALL(*input_audio_stream_, Read(_)) - .WillRepeatedly(ReturnBuffer(encrypted_buffer_)); - EXPECT_CALL(*decryptor_, Decrypt(_, encrypted_buffer_, _)) - .WillRepeatedly( - RunCallback<2>(Decryptor::kNoKey, scoped_refptr<DecoderBuffer>())); - EXPECT_CALL(*this, OnWaitingForDecryptionKey()); - demuxer_stream_->Read(base::Bind(&DecryptingDemuxerStreamTest::BufferReady, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - void AbortPendingDecryptCB() { - if (!pending_decrypt_cb_.is_null()) { - base::ResetAndReturn(&pending_decrypt_cb_).Run(Decryptor::kSuccess, NULL); - } - } - - void SatisfyPendingDemuxerReadCB(DemuxerStream::Status status) { - scoped_refptr<DecoderBuffer> buffer = - (status == DemuxerStream::kOk) ? encrypted_buffer_ : NULL; - base::ResetAndReturn(&pending_demuxer_read_cb_).Run(status, buffer); - } - - void Reset() { - EXPECT_CALL(*decryptor_, CancelDecrypt(Decryptor::kAudio)) - .WillRepeatedly(InvokeWithoutArgs( - this, &DecryptingDemuxerStreamTest::AbortPendingDecryptCB)); - - demuxer_stream_->Reset(NewExpectedClosure()); - base::RunLoop().RunUntilIdle(); - } - - MOCK_METHOD2(BufferReady, void(DemuxerStream::Status, - const scoped_refptr<DecoderBuffer>&)); - MOCK_METHOD0(OnWaitingForDecryptionKey, void(void)); - - base::MessageLoop message_loop_; - std::unique_ptr<DecryptingDemuxerStream> demuxer_stream_; - std::unique_ptr<StrictMock<MockCdmContext>> cdm_context_; - std::unique_ptr<StrictMock<MockDecryptor>> decryptor_; - // Whether the |demuxer_stream_| is successfully initialized. - bool is_initialized_; - std::unique_ptr<StrictMock<MockDemuxerStream>> input_audio_stream_; - std::unique_ptr<StrictMock<MockDemuxerStream>> input_video_stream_; - - DemuxerStream::ReadCB pending_demuxer_read_cb_; - Decryptor::NewKeyCB key_added_cb_; - Decryptor::DecryptCB pending_decrypt_cb_; - - // Constant buffers to be returned by the input demuxer streams and the - // |decryptor_|. - scoped_refptr<DecoderBuffer> clear_buffer_; - scoped_refptr<DecoderBuffer> encrypted_buffer_; - scoped_refptr<DecoderBuffer> decrypted_buffer_; - - private: - DISALLOW_COPY_AND_ASSIGN(DecryptingDemuxerStreamTest); -}; - -TEST_F(DecryptingDemuxerStreamTest, Initialize_NormalAudio) { Initialize(); } - -TEST_F(DecryptingDemuxerStreamTest, Initialize_NormalVideo) { - SetCdmType(CDM_WITH_DECRYPTOR); - EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kVideo, _)) - .WillOnce(SaveArg<1>(&key_added_cb_)); - - VideoDecoderConfig input_config = TestVideoConfig::NormalEncrypted(); - InitializeVideoAndExpectStatus(input_config, PIPELINE_OK); - - const VideoDecoderConfig& output_config = - demuxer_stream_->video_decoder_config(); - EXPECT_EQ(DemuxerStream::VIDEO, demuxer_stream_->type()); - EXPECT_FALSE(output_config.is_encrypted()); - EXPECT_EQ(input_config.codec(), output_config.codec()); - EXPECT_EQ(input_config.format(), output_config.format()); - EXPECT_EQ(input_config.profile(), output_config.profile()); - EXPECT_EQ(input_config.coded_size(), output_config.coded_size()); - EXPECT_EQ(input_config.visible_rect(), output_config.visible_rect()); - EXPECT_EQ(input_config.natural_size(), output_config.natural_size()); - ASSERT_EQ(input_config.extra_data(), output_config.extra_data()); -} - -TEST_F(DecryptingDemuxerStreamTest, Initialize_CdmWithoutDecryptor) { - SetCdmType(CDM_WITHOUT_DECRYPTOR); - AudioDecoderConfig input_config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, 44100, - EmptyExtraData(), AesCtrEncryptionScheme()); - InitializeAudioAndExpectStatus(input_config, DECODER_ERROR_NOT_SUPPORTED); -} - -// Test normal read case where the buffer is encrypted. -TEST_F(DecryptingDemuxerStreamTest, Read_Normal) { - Initialize(); - EnterNormalReadingState(); -} - -// Test normal read case where the buffer is clear. -TEST_F(DecryptingDemuxerStreamTest, Read_Clear) { - Initialize(); - EnterClearReadingState(); -} - -// Test the case where the decryptor returns error during read. -TEST_F(DecryptingDemuxerStreamTest, Read_DecryptError) { - Initialize(); - - EXPECT_CALL(*input_audio_stream_, Read(_)) - .WillRepeatedly(ReturnBuffer(encrypted_buffer_)); - EXPECT_CALL(*decryptor_, Decrypt(_, encrypted_buffer_, _)) - .WillRepeatedly( - RunCallback<2>(Decryptor::kError, scoped_refptr<DecoderBuffer>())); - ReadAndExpectBufferReadyWith(DemuxerStream::kAborted, NULL); -} - -// Test the case where the input is an end-of-stream buffer. -TEST_F(DecryptingDemuxerStreamTest, Read_EndOfStream) { - Initialize(); - EnterNormalReadingState(); - - // No Decryptor::Decrypt() call is expected for EOS buffer. - EXPECT_CALL(*input_audio_stream_, Read(_)) - .WillOnce(ReturnBuffer(DecoderBuffer::CreateEOSBuffer())); - - ReadAndExpectBufferReadyWith(DemuxerStream::kOk, - DecoderBuffer::CreateEOSBuffer()); -} - -// Test the case where the a key is added when the decryptor is in -// kWaitingForKey state. -TEST_F(DecryptingDemuxerStreamTest, KeyAdded_DuringWaitingForKey) { - Initialize(); - EnterWaitingForKeyState(); - - EXPECT_CALL(*decryptor_, Decrypt(_, encrypted_buffer_, _)) - .WillRepeatedly(RunCallback<2>(Decryptor::kSuccess, decrypted_buffer_)); - EXPECT_CALL(*this, BufferReady(DemuxerStream::kOk, decrypted_buffer_)); - key_added_cb_.Run(); - base::RunLoop().RunUntilIdle(); -} - -// Test the case where the a key is added when the decryptor is in -// kPendingDecrypt state. -TEST_F(DecryptingDemuxerStreamTest, KeyAdded_DuringPendingDecrypt) { - Initialize(); - EnterPendingDecryptState(); - - EXPECT_CALL(*decryptor_, Decrypt(_, encrypted_buffer_, _)) - .WillRepeatedly(RunCallback<2>(Decryptor::kSuccess, decrypted_buffer_)); - EXPECT_CALL(*this, BufferReady(DemuxerStream::kOk, decrypted_buffer_)); - // The decrypt callback is returned after the correct decryption key is added. - key_added_cb_.Run(); - base::ResetAndReturn(&pending_decrypt_cb_).Run(Decryptor::kNoKey, NULL); - base::RunLoop().RunUntilIdle(); -} - -// Test resetting in kIdle state but has not returned any buffer. -TEST_F(DecryptingDemuxerStreamTest, Reset_DuringIdleAfterInitialization) { - Initialize(); - Reset(); -} - -// Test resetting in kIdle state after having returned one buffer. -TEST_F(DecryptingDemuxerStreamTest, Reset_DuringIdleAfterReadOneBuffer) { - Initialize(); - EnterNormalReadingState(); - Reset(); -} - -// Test resetting in kPendingDemuxerRead state. -TEST_F(DecryptingDemuxerStreamTest, Reset_DuringPendingDemuxerRead) { - Initialize(); - EnterPendingReadState(); - - EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); - - Reset(); - SatisfyPendingDemuxerReadCB(DemuxerStream::kOk); - base::RunLoop().RunUntilIdle(); -} - -// Test resetting in kPendingDecrypt state. -TEST_F(DecryptingDemuxerStreamTest, Reset_DuringPendingDecrypt) { - Initialize(); - EnterPendingDecryptState(); - - EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); - - Reset(); -} - -// Test resetting in kWaitingForKey state. -TEST_F(DecryptingDemuxerStreamTest, Reset_DuringWaitingForKey) { - Initialize(); - EnterWaitingForKeyState(); - - EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); - - Reset(); -} - -// Test resetting after reset. -TEST_F(DecryptingDemuxerStreamTest, Reset_AfterReset) { - Initialize(); - EnterNormalReadingState(); - Reset(); - Reset(); -} - -// Test aborted read on the demuxer stream. -TEST_F(DecryptingDemuxerStreamTest, DemuxerRead_Aborted) { - Initialize(); - - // ReturnBuffer() with NULL triggers aborted demuxer read. - EXPECT_CALL(*input_audio_stream_, Read(_)) - .WillOnce(ReturnBuffer(scoped_refptr<DecoderBuffer>())); - - ReadAndExpectBufferReadyWith(DemuxerStream::kAborted, NULL); -} - -// Test resetting when waiting for an aborted read. -TEST_F(DecryptingDemuxerStreamTest, Reset_DuringAbortedDemuxerRead) { - Initialize(); - EnterPendingReadState(); - - // Make sure we get a NULL audio frame returned. - EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); - - Reset(); - SatisfyPendingDemuxerReadCB(DemuxerStream::kAborted); - base::RunLoop().RunUntilIdle(); -} - -// Test config change on the input demuxer stream. -TEST_F(DecryptingDemuxerStreamTest, DemuxerRead_ConfigChanged) { - Initialize(); - - AudioDecoderConfig new_config(kCodecVorbis, kSampleFormatPlanarF32, - CHANNEL_LAYOUT_STEREO, 88200, EmptyExtraData(), - AesCtrEncryptionScheme()); - input_audio_stream_->set_audio_decoder_config(new_config); - - EXPECT_CALL(*input_audio_stream_, Read(_)) - .WillOnce(RunCallback<0>(DemuxerStream::kConfigChanged, - scoped_refptr<DecoderBuffer>())); - - ReadAndExpectBufferReadyWith(DemuxerStream::kConfigChanged, NULL); -} - -// Test resetting when waiting for a config changed read. -TEST_F(DecryptingDemuxerStreamTest, Reset_DuringConfigChangedDemuxerRead) { - Initialize(); - EnterPendingReadState(); - - // Make sure we get a |kConfigChanged| instead of a |kAborted|. - EXPECT_CALL(*this, BufferReady(DemuxerStream::kConfigChanged, IsNull())); - - Reset(); - SatisfyPendingDemuxerReadCB(DemuxerStream::kConfigChanged); - base::RunLoop().RunUntilIdle(); -} - -// The following tests test destruction in various scenarios. The destruction -// happens in DecryptingDemuxerStreamTest's dtor. - -// Test destruction in kIdle state but has not returned any buffer. -TEST_F(DecryptingDemuxerStreamTest, Destroy_DuringIdleAfterInitialization) { - Initialize(); -} - -// Test destruction in kIdle state after having returned one buffer. -TEST_F(DecryptingDemuxerStreamTest, Destroy_DuringIdleAfterReadOneBuffer) { - Initialize(); - EnterNormalReadingState(); -} - -// Test destruction in kPendingDemuxerRead state. -TEST_F(DecryptingDemuxerStreamTest, Destroy_DuringPendingDemuxerRead) { - Initialize(); - EnterPendingReadState(); - - EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); -} - -// Test destruction in kPendingDecrypt state. -TEST_F(DecryptingDemuxerStreamTest, Destroy_DuringPendingDecrypt) { - Initialize(); - EnterPendingDecryptState(); - - EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); -} - -// Test destruction in kWaitingForKey state. -TEST_F(DecryptingDemuxerStreamTest, Destroy_DuringWaitingForKey) { - Initialize(); - EnterWaitingForKeyState(); - - EXPECT_CALL(*this, BufferReady(DemuxerStream::kAborted, IsNull())); -} - -// Test destruction after reset. -TEST_F(DecryptingDemuxerStreamTest, Destroy_AfterReset) { - Initialize(); - EnterNormalReadingState(); - Reset(); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/decrypting_video_decoder.cc b/src/cobalt/media/filters/decrypting_video_decoder.cc deleted file mode 100644 index f395b38..0000000 --- a/src/cobalt/media/filters/decrypting_video_decoder.cc +++ /dev/null
@@ -1,313 +0,0 @@ -// Copyright (c) 2012 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/filters/decrypting_video_decoder.h" - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/single_thread_task_runner.h" -#include "base/string_number_conversions.h" -#include "base/trace_event/trace_event.h" -#include "cobalt/media/base/bind_to_current_loop.h" -#include "cobalt/media/base/cdm_context.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/media_log.h" -#include "cobalt/media/base/video_frame.h" - -namespace cobalt { -namespace media { - -const char DecryptingVideoDecoder::kDecoderName[] = "DecryptingVideoDecoder"; - -DecryptingVideoDecoder::DecryptingVideoDecoder( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - const scoped_refptr<MediaLog>& media_log, - const base::Closure& waiting_for_decryption_key_cb) - : task_runner_(task_runner), - media_log_(media_log), - state_(kUninitialized), - waiting_for_decryption_key_cb_(waiting_for_decryption_key_cb), - decryptor_(NULL), - key_added_while_decode_pending_(false), - trace_id_(0), - weak_factory_(this) {} - -std::string DecryptingVideoDecoder::GetDisplayName() const { - return kDecoderName; -} - -void DecryptingVideoDecoder::Initialize(const VideoDecoderConfig& config, - bool /* low_delay */, - CdmContext* cdm_context, - const InitCB& init_cb, - const OutputCB& output_cb) { - DVLOG(2) << __func__ << ": " << config.AsHumanReadableString(); - - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kUninitialized || state_ == kIdle || - state_ == kDecodeFinished) - << state_; - DCHECK(decode_cb_.is_null()); - DCHECK(reset_cb_.is_null()); - DCHECK(config.IsValidConfig()); - DCHECK(config.is_encrypted()); - - init_cb_ = BindToCurrentLoop(init_cb); - output_cb_ = BindToCurrentLoop(output_cb); - weak_this_ = weak_factory_.GetWeakPtr(); - config_ = config; - - if (state_ == kUninitialized) { - DCHECK(cdm_context); - if (!cdm_context->GetDecryptor()) { - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() << ": no decryptor"; - base::ResetAndReturn(&init_cb_).Run(false); - return; - } - - decryptor_ = cdm_context->GetDecryptor(); - } else { - // Reinitialization. - decryptor_->DeinitializeDecoder(Decryptor::kVideo); - } - - state_ = kPendingDecoderInit; - decryptor_->InitializeVideoDecoder( - config_, BindToCurrentLoop(base::Bind( - &DecryptingVideoDecoder::FinishInitialization, weak_this_))); -} - -void DecryptingVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, - const DecodeCB& decode_cb) { - DVLOG(3) << "Decode()"; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kIdle || state_ == kDecodeFinished || state_ == kError) - << state_; - DCHECK(!decode_cb.is_null()); - CHECK(decode_cb_.is_null()) << "Overlapping decodes are not supported."; - - decode_cb_ = BindToCurrentLoop(decode_cb); - - if (state_ == kError) { - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::DECODE_ERROR); - return; - } - - // Return empty frames if decoding has finished. - if (state_ == kDecodeFinished) { - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::OK); - return; - } - - pending_buffer_to_decode_ = buffer; - state_ = kPendingDecode; - DecodePendingBuffer(); -} - -void DecryptingVideoDecoder::Reset(const base::Closure& closure) { - DVLOG(2) << "Reset() - state: " << state_; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK(state_ == kIdle || state_ == kPendingDecode || - state_ == kWaitingForKey || state_ == kDecodeFinished || - state_ == kError) - << state_; - DCHECK(init_cb_.is_null()); // No Reset() during pending initialization. - DCHECK(reset_cb_.is_null()); - - reset_cb_ = BindToCurrentLoop(closure); - - decryptor_->ResetDecoder(Decryptor::kVideo); - - // Reset() cannot complete if the decode callback is still pending. - // Defer the resetting process in this case. The |reset_cb_| will be fired - // after the decode callback is fired - see DecryptAndDecodeBuffer() and - // DeliverFrame(). - if (state_ == kPendingDecode) { - DCHECK(!decode_cb_.is_null()); - return; - } - - if (state_ == kWaitingForKey) { - DCHECK(!decode_cb_.is_null()); - pending_buffer_to_decode_ = NULL; - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::ABORTED); - } - - DCHECK(decode_cb_.is_null()); - DoReset(); -} - -DecryptingVideoDecoder::~DecryptingVideoDecoder() { - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (state_ == kUninitialized) return; - - if (decryptor_) { - decryptor_->DeinitializeDecoder(Decryptor::kVideo); - decryptor_ = NULL; - } - pending_buffer_to_decode_ = NULL; - if (!init_cb_.is_null()) base::ResetAndReturn(&init_cb_).Run(false); - if (!decode_cb_.is_null()) - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::ABORTED); - if (!reset_cb_.is_null()) base::ResetAndReturn(&reset_cb_).Run(); -} - -void DecryptingVideoDecoder::FinishInitialization(bool success) { - DVLOG(2) << "FinishInitialization()"; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kPendingDecoderInit) << state_; - DCHECK(!init_cb_.is_null()); - DCHECK(reset_cb_.is_null()); // No Reset() before initialization finished. - DCHECK(decode_cb_.is_null()); // No Decode() before initialization finished. - - if (!success) { - MEDIA_LOG(DEBUG, media_log_) << GetDisplayName() - << ": failed to init decoder on decryptor"; - base::ResetAndReturn(&init_cb_).Run(false); - decryptor_ = NULL; - state_ = kError; - return; - } - - decryptor_->RegisterNewKeyCB( - Decryptor::kVideo, BindToCurrentLoop(base::Bind( - &DecryptingVideoDecoder::OnKeyAdded, weak_this_))); - - // Success! - state_ = kIdle; - base::ResetAndReturn(&init_cb_).Run(true); -} - -void DecryptingVideoDecoder::DecodePendingBuffer() { - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kPendingDecode) << state_; - TRACE_EVENT_ASYNC_BEGIN0( - "media", "DecryptingVideoDecoder::DecodePendingBuffer", ++trace_id_); - - int buffer_size = 0; - if (!pending_buffer_to_decode_->end_of_stream()) { - buffer_size = pending_buffer_to_decode_->data_size(); - } - - decryptor_->DecryptAndDecodeVideo( - pending_buffer_to_decode_, - BindToCurrentLoop(base::Bind(&DecryptingVideoDecoder::DeliverFrame, - weak_this_, buffer_size))); -} - -void DecryptingVideoDecoder::DeliverFrame( - int buffer_size, Decryptor::Status status, - const scoped_refptr<VideoFrame>& frame) { - DVLOG(3) << "DeliverFrame() - status: " << status; - DCHECK(task_runner_->BelongsToCurrentThread()); - DCHECK_EQ(state_, kPendingDecode) << state_; - DCHECK(!decode_cb_.is_null()); - DCHECK(pending_buffer_to_decode_.get()); - - TRACE_EVENT_ASYNC_END2("media", "DecryptingVideoDecoder::DecodePendingBuffer", - trace_id_, "buffer_size", buffer_size, "status", - status); - - bool need_to_try_again_if_nokey_is_returned = key_added_while_decode_pending_; - key_added_while_decode_pending_ = false; - - scoped_refptr<DecoderBuffer> scoped_pending_buffer_to_decode = - pending_buffer_to_decode_; - pending_buffer_to_decode_ = NULL; - - if (!reset_cb_.is_null()) { - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::ABORTED); - DoReset(); - return; - } - - DCHECK_EQ(status == Decryptor::kSuccess, frame.get() != NULL); - - if (status == Decryptor::kError) { - DVLOG(2) << "DeliverFrame() - kError"; - MEDIA_LOG(ERROR, media_log_) << GetDisplayName() << ": decode error"; - state_ = kError; - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::DECODE_ERROR); - return; - } - - if (status == Decryptor::kNoKey) { - std::string key_id = - scoped_pending_buffer_to_decode->decrypt_config()->key_id(); - std::string missing_key_id = base::HexEncode(key_id.data(), key_id.size()); - DVLOG(1) << "DeliverFrame() - no key for key ID " << missing_key_id; - MEDIA_LOG(INFO, media_log_) << GetDisplayName() << ": no key for key ID " - << missing_key_id; - - // Set |pending_buffer_to_decode_| back as we need to try decoding the - // pending buffer again when new key is added to the decryptor. - pending_buffer_to_decode_ = scoped_pending_buffer_to_decode; - - if (need_to_try_again_if_nokey_is_returned) { - // The |state_| is still kPendingDecode. - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key was added, resuming decode"; - DecodePendingBuffer(); - return; - } - - state_ = kWaitingForKey; - waiting_for_decryption_key_cb_.Run(); - return; - } - - if (status == Decryptor::kNeedMoreData) { - DVLOG(2) << "DeliverFrame() - kNeedMoreData"; - state_ = scoped_pending_buffer_to_decode->end_of_stream() ? kDecodeFinished - : kIdle; - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::OK); - return; - } - - DCHECK_EQ(status, Decryptor::kSuccess); - // No frame returned with kSuccess should be end-of-stream frame. - DCHECK(!frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)); - output_cb_.Run(frame); - - if (scoped_pending_buffer_to_decode->end_of_stream()) { - // Set |pending_buffer_to_decode_| back as we need to keep flushing the - // decryptor. - pending_buffer_to_decode_ = scoped_pending_buffer_to_decode; - DecodePendingBuffer(); - return; - } - - state_ = kIdle; - base::ResetAndReturn(&decode_cb_).Run(DecodeStatus::OK); -} - -void DecryptingVideoDecoder::OnKeyAdded() { - DVLOG(2) << "OnKeyAdded()"; - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (state_ == kPendingDecode) { - key_added_while_decode_pending_ = true; - return; - } - - if (state_ == kWaitingForKey) { - MEDIA_LOG(INFO, media_log_) << GetDisplayName() - << ": key added, resuming decode"; - state_ = kPendingDecode; - DecodePendingBuffer(); - } -} - -void DecryptingVideoDecoder::DoReset() { - DCHECK(init_cb_.is_null()); - DCHECK(decode_cb_.is_null()); - state_ = kIdle; - base::ResetAndReturn(&reset_cb_).Run(); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/decrypting_video_decoder.h b/src/cobalt/media/filters/decrypting_video_decoder.h deleted file mode 100644 index 3eb6552..0000000 --- a/src/cobalt/media/filters/decrypting_video_decoder.h +++ /dev/null
@@ -1,122 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef COBALT_MEDIA_FILTERS_DECRYPTING_VIDEO_DECODER_H_ -#define COBALT_MEDIA_FILTERS_DECRYPTING_VIDEO_DECODER_H_ - -#include <string> - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/memory/weak_ptr.h" -#include "cobalt/media/base/cdm_context.h" -#include "cobalt/media/base/decryptor.h" -#include "cobalt/media/base/video_decoder.h" -#include "cobalt/media/base/video_decoder_config.h" -#include "starboard/types.h" - -namespace base { -class SingleThreadTaskRunner; -} - -namespace cobalt { -namespace media { - -class DecoderBuffer; -class Decryptor; -class MediaLog; - -// Decryptor-based VideoDecoder implementation that can decrypt and decode -// encrypted video buffers and return decrypted and decompressed video frames. -// All public APIs and callbacks are trampolined to the |task_runner_| so -// that no locks are required for thread safety. -class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder { - public: - DecryptingVideoDecoder( - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, - const scoped_refptr<MediaLog>& media_log, - const base::Closure& waiting_for_decryption_key_cb); - ~DecryptingVideoDecoder() OVERRIDE; - - // VideoDecoder implementation. - std::string GetDisplayName() const OVERRIDE; - void Initialize(const VideoDecoderConfig& config, bool low_delay, - CdmContext* cdm_context, const InitCB& init_cb, - const OutputCB& output_cb) OVERRIDE; - void Decode(const scoped_refptr<DecoderBuffer>& buffer, - const DecodeCB& decode_cb) OVERRIDE; - void Reset(const base::Closure& closure) OVERRIDE; - - static const char kDecoderName[]; - - private: - // For a detailed state diagram please see this link: http://goo.gl/8jAok - // TODO(xhwang): Add a ASCII state diagram in this file after this class - // stabilizes. - enum State { - kUninitialized = 0, - kPendingDecoderInit, - kIdle, - kPendingDecode, - kWaitingForKey, - kDecodeFinished, - kError - }; - - // Callback for Decryptor::InitializeVideoDecoder() during initialization. - void FinishInitialization(bool success); - - void DecodePendingBuffer(); - - // Callback for Decryptor::DecryptAndDecodeVideo(). - void DeliverFrame(int buffer_size, Decryptor::Status status, - const scoped_refptr<VideoFrame>& frame); - - // Callback for the |decryptor_| to notify this object that a new key has been - // added. - void OnKeyAdded(); - - // Reset decoder and call |reset_cb_|. - void DoReset(); - - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - - scoped_refptr<MediaLog> media_log_; - - State state_; - - InitCB init_cb_; - OutputCB output_cb_; - DecodeCB decode_cb_; - base::Closure reset_cb_; - base::Closure waiting_for_decryption_key_cb_; - - VideoDecoderConfig config_; - - Decryptor* decryptor_; - - // The buffer that needs decrypting/decoding. - scoped_refptr<media::DecoderBuffer> pending_buffer_to_decode_; - - // Indicates the situation where new key is added during pending decode - // (in other words, this variable can only be set in state kPendingDecode). - // If this variable is true and kNoKey is returned then we need to try - // decrypting/decoding again in case the newly added key is the correct - // decryption key. - bool key_added_while_decode_pending_; - - // A unique ID to trace Decryptor::DecryptAndDecodeVideo() call and the - // matching DecryptCB call (in DoDeliverFrame()). - uint32_t trace_id_; - - base::WeakPtr<DecryptingVideoDecoder> weak_this_; - base::WeakPtrFactory<DecryptingVideoDecoder> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(DecryptingVideoDecoder); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_DECRYPTING_VIDEO_DECODER_H_
diff --git a/src/cobalt/media/filters/decrypting_video_decoder_unittest.cc b/src/cobalt/media/filters/decrypting_video_decoder_unittest.cc deleted file mode 100644 index 683b2ba..0000000 --- a/src/cobalt/media/filters/decrypting_video_decoder_unittest.cc +++ /dev/null
@@ -1,481 +0,0 @@ -// Copyright (c) 2012 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 <string> -#include <vector> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/message_loop.h" -#include "base/run_loop.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/decrypt_config.h" -#include "cobalt/media/base/gmock_callback_support.h" -#include "cobalt/media/base/mock_filters.h" -#include "cobalt/media/base/test_helpers.h" -#include "cobalt/media/base/video_frame.h" -#include "cobalt/media/filters/decrypting_video_decoder.h" -#include "starboard/types.h" -#include "testing/gmock/include/gmock/gmock.h" - -using ::testing::_; -using ::testing::Invoke; -using ::testing::Return; -using ::testing::SaveArg; -using ::testing::StrictMock; - -namespace cobalt { -namespace media { - -const uint8_t kFakeKeyId[] = {0x4b, 0x65, 0x79, 0x20, 0x49, 0x44}; -const uint8_t kFakeIv[DecryptConfig::kDecryptionKeySize] = {0}; -const int kDecodingDelay = 3; - -// Create a fake non-empty encrypted buffer. -static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() { - const int buffer_size = 16; // Need a non-empty buffer; - scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(buffer_size)); - buffer->set_decrypt_config(std::unique_ptr<DecryptConfig>(new DecryptConfig( - std::string(reinterpret_cast<const char*>(kFakeKeyId), - arraysize(kFakeKeyId)), - std::string(reinterpret_cast<const char*>(kFakeIv), arraysize(kFakeIv)), - std::vector<SubsampleEntry>()))); - return buffer; -} - -// Use anonymous namespace here to prevent the actions to be defined multiple -// times across multiple test files. Sadly we can't use static for them. -namespace { - -ACTION_P3(ResetAndRunCallback, callback, p1, p2) { - base::ResetAndReturn(callback).Run(p1, p2); -} - -} // namespace - -class DecryptingVideoDecoderTest : public testing::Test { - public: - DecryptingVideoDecoderTest() - : decoder_(new DecryptingVideoDecoder( - message_loop_.task_runner(), new MediaLog(), - base::Bind(&DecryptingVideoDecoderTest::OnWaitingForDecryptionKey, - base::Unretained(this)))), - cdm_context_(new StrictMock<MockCdmContext>()), - decryptor_(new StrictMock<MockDecryptor>()), - num_decrypt_and_decode_calls_(0), - num_frames_in_decryptor_(0), - encrypted_buffer_(CreateFakeEncryptedBuffer()), - decoded_video_frame_( - VideoFrame::CreateBlackFrame(TestVideoConfig::NormalCodedSize())), - null_video_frame_(scoped_refptr<VideoFrame>()) {} - - virtual ~DecryptingVideoDecoderTest() { Destroy(); } - - enum CdmType { CDM_WITHOUT_DECRYPTOR, CDM_WITH_DECRYPTOR }; - - void SetCdmType(CdmType cdm_type) { - const bool has_decryptor = cdm_type == CDM_WITH_DECRYPTOR; - EXPECT_CALL(*cdm_context_, GetDecryptor()) - .WillRepeatedly(Return(has_decryptor ? decryptor_.get() : NULL)); - } - - // Initializes the |decoder_| and expects |success|. Note the initialization - // can succeed or fail. - void InitializeAndExpectResult(const VideoDecoderConfig& config, - bool success) { - decoder_->Initialize(config, false, cdm_context_.get(), - NewExpectedBoolCB(success), - base::Bind(&DecryptingVideoDecoderTest::FrameReady, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - // Initialize the |decoder_| and expects it to succeed. - void Initialize() { - SetCdmType(CDM_WITH_DECRYPTOR); - EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) - .WillOnce(RunCallback<1>(true)); - EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kVideo, _)) - .WillOnce(SaveArg<1>(&key_added_cb_)); - - InitializeAndExpectResult(TestVideoConfig::NormalEncrypted(), true); - } - - // Reinitialize the |decoder_| and expects it to succeed. - void Reinitialize() { - EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kVideo)); - EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) - .WillOnce(RunCallback<1>(true)); - EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kVideo, _)) - .WillOnce(SaveArg<1>(&key_added_cb_)); - - InitializeAndExpectResult(TestVideoConfig::LargeEncrypted(), true); - } - - // Decode |buffer| and expect DecodeDone to get called with |status|. - void DecodeAndExpect(const scoped_refptr<DecoderBuffer>& buffer, - DecodeStatus status) { - EXPECT_CALL(*this, DecodeDone(status)); - decoder_->Decode(buffer, base::Bind(&DecryptingVideoDecoderTest::DecodeDone, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - // Helper function to simulate the decrypting and decoding process in the - // |decryptor_| with a decoding delay of kDecodingDelay buffers. - void DecryptAndDecodeVideo(const scoped_refptr<DecoderBuffer>& encrypted, - const Decryptor::VideoDecodeCB& video_decode_cb) { - num_decrypt_and_decode_calls_++; - if (!encrypted->end_of_stream()) num_frames_in_decryptor_++; - - if (num_decrypt_and_decode_calls_ <= kDecodingDelay || - num_frames_in_decryptor_ == 0) { - video_decode_cb.Run(Decryptor::kNeedMoreData, - scoped_refptr<VideoFrame>()); - return; - } - - num_frames_in_decryptor_--; - video_decode_cb.Run(Decryptor::kSuccess, decoded_video_frame_); - } - - // Sets up expectations and actions to put DecryptingVideoDecoder in an - // active normal decoding state. - void EnterNormalDecodingState() { - EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly( - Invoke(this, &DecryptingVideoDecoderTest::DecryptAndDecodeVideo)); - EXPECT_CALL(*this, FrameReady(decoded_video_frame_)); - for (int i = 0; i < kDecodingDelay + 1; ++i) - DecodeAndExpect(encrypted_buffer_, DecodeStatus::OK); - } - - // Sets up expectations and actions to put DecryptingVideoDecoder in an end - // of stream state. This function must be called after - // EnterNormalDecodingState() to work. - void EnterEndOfStreamState() { - // The codec in the |decryptor_| will be flushed. - EXPECT_CALL(*this, FrameReady(decoded_video_frame_)).Times(kDecodingDelay); - DecodeAndExpect(DecoderBuffer::CreateEOSBuffer(), DecodeStatus::OK); - EXPECT_EQ(0, num_frames_in_decryptor_); - } - - // Make the video decode callback pending by saving and not firing it. - void EnterPendingDecodeState() { - EXPECT_TRUE(pending_video_decode_cb_.is_null()); - EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(encrypted_buffer_, _)) - .WillOnce(SaveArg<1>(&pending_video_decode_cb_)); - - decoder_->Decode(encrypted_buffer_, - base::Bind(&DecryptingVideoDecoderTest::DecodeDone, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - // Make sure the Decode() on the decoder triggers a DecryptAndDecode() on - // the decryptor. - EXPECT_FALSE(pending_video_decode_cb_.is_null()); - } - - void EnterWaitingForKeyState() { - EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly(RunCallback<1>(Decryptor::kNoKey, null_video_frame_)); - EXPECT_CALL(*this, OnWaitingForDecryptionKey()); - decoder_->Decode(encrypted_buffer_, - base::Bind(&DecryptingVideoDecoderTest::DecodeDone, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - void AbortPendingVideoDecodeCB() { - if (!pending_video_decode_cb_.is_null()) { - base::ResetAndReturn(&pending_video_decode_cb_) - .Run(Decryptor::kSuccess, scoped_refptr<VideoFrame>(NULL)); - } - } - - void AbortAllPendingCBs() { - if (!pending_init_cb_.is_null()) { - ASSERT_TRUE(pending_video_decode_cb_.is_null()); - base::ResetAndReturn(&pending_init_cb_).Run(false); - return; - } - - AbortPendingVideoDecodeCB(); - } - - void Reset() { - EXPECT_CALL(*decryptor_, ResetDecoder(Decryptor::kVideo)) - .WillRepeatedly(InvokeWithoutArgs( - this, &DecryptingVideoDecoderTest::AbortPendingVideoDecodeCB)); - - decoder_->Reset(NewExpectedClosure()); - base::RunLoop().RunUntilIdle(); - } - - void Destroy() { - EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kVideo)) - .WillRepeatedly(InvokeWithoutArgs( - this, &DecryptingVideoDecoderTest::AbortAllPendingCBs)); - - decoder_.reset(); - base::RunLoop().RunUntilIdle(); - } - - MOCK_METHOD1(FrameReady, void(const scoped_refptr<VideoFrame>&)); - MOCK_METHOD1(DecodeDone, void(DecodeStatus)); - - MOCK_METHOD0(OnWaitingForDecryptionKey, void(void)); - - base::MessageLoop message_loop_; - std::unique_ptr<DecryptingVideoDecoder> decoder_; - std::unique_ptr<StrictMock<MockCdmContext>> cdm_context_; - std::unique_ptr<StrictMock<MockDecryptor>> decryptor_; - - // Variables to help the |decryptor_| to simulate decoding delay and flushing. - int num_decrypt_and_decode_calls_; - int num_frames_in_decryptor_; - - Decryptor::DecoderInitCB pending_init_cb_; - Decryptor::NewKeyCB key_added_cb_; - Decryptor::VideoDecodeCB pending_video_decode_cb_; - - // Constant buffer/frames. - scoped_refptr<DecoderBuffer> encrypted_buffer_; - scoped_refptr<VideoFrame> decoded_video_frame_; - scoped_refptr<VideoFrame> null_video_frame_; - - private: - DISALLOW_COPY_AND_ASSIGN(DecryptingVideoDecoderTest); -}; - -TEST_F(DecryptingVideoDecoderTest, Initialize_Normal) { Initialize(); } - -TEST_F(DecryptingVideoDecoderTest, Initialize_CdmWithoutDecryptor) { - SetCdmType(CDM_WITHOUT_DECRYPTOR); - InitializeAndExpectResult(TestVideoConfig::NormalEncrypted(), false); -} - -TEST_F(DecryptingVideoDecoderTest, Initialize_Failure) { - SetCdmType(CDM_WITH_DECRYPTOR); - EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) - .WillRepeatedly(RunCallback<1>(false)); - EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kVideo, _)) - .WillRepeatedly(SaveArg<1>(&key_added_cb_)); - - InitializeAndExpectResult(TestVideoConfig::NormalEncrypted(), false); -} - -TEST_F(DecryptingVideoDecoderTest, Reinitialize_Normal) { - Initialize(); - EnterNormalDecodingState(); - Reinitialize(); -} - -TEST_F(DecryptingVideoDecoderTest, Reinitialize_Failure) { - Initialize(); - EnterNormalDecodingState(); - - EXPECT_CALL(*decryptor_, DeinitializeDecoder(Decryptor::kVideo)); - EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) - .WillOnce(RunCallback<1>(false)); - - // Reinitialize() expects the reinitialization to succeed. Call - // InitializeAndExpectResult() directly to test the reinitialization failure. - InitializeAndExpectResult(TestVideoConfig::NormalEncrypted(), false); -} - -// Test normal decrypt and decode case. -TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_Normal) { - Initialize(); - EnterNormalDecodingState(); -} - -// Test the case where the decryptor returns error when doing decrypt and -// decode. -TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_DecodeError) { - Initialize(); - - EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly( - RunCallback<1>(Decryptor::kError, scoped_refptr<VideoFrame>(NULL))); - - DecodeAndExpect(encrypted_buffer_, DecodeStatus::DECODE_ERROR); - - // After a decode error occurred, all following decodes return DECODE_ERROR. - DecodeAndExpect(encrypted_buffer_, DecodeStatus::DECODE_ERROR); -} - -// Test the case where the decryptor receives end-of-stream buffer. -TEST_F(DecryptingVideoDecoderTest, DecryptAndDecode_EndOfStream) { - Initialize(); - EnterNormalDecodingState(); - EnterEndOfStreamState(); -} - -// Test the case where the a key is added when the decryptor is in -// kWaitingForKey state. -TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringWaitingForKey) { - Initialize(); - EnterWaitingForKeyState(); - - EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly( - RunCallback<1>(Decryptor::kSuccess, decoded_video_frame_)); - EXPECT_CALL(*this, FrameReady(decoded_video_frame_)); - EXPECT_CALL(*this, DecodeDone(DecodeStatus::OK)); - key_added_cb_.Run(); - base::RunLoop().RunUntilIdle(); -} - -// Test the case where the a key is added when the decryptor is in -// kPendingDecode state. -TEST_F(DecryptingVideoDecoderTest, KeyAdded_DuringPendingDecode) { - Initialize(); - EnterPendingDecodeState(); - - EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) - .WillRepeatedly( - RunCallback<1>(Decryptor::kSuccess, decoded_video_frame_)); - EXPECT_CALL(*this, FrameReady(decoded_video_frame_)); - EXPECT_CALL(*this, DecodeDone(DecodeStatus::OK)); - // The video decode callback is returned after the correct decryption key is - // added. - key_added_cb_.Run(); - base::ResetAndReturn(&pending_video_decode_cb_) - .Run(Decryptor::kNoKey, null_video_frame_); - base::RunLoop().RunUntilIdle(); -} - -// Test resetting when the decoder is in kIdle state but has not decoded any -// frame. -TEST_F(DecryptingVideoDecoderTest, Reset_DuringIdleAfterInitialization) { - Initialize(); - Reset(); -} - -// Test resetting when the decoder is in kIdle state after it has decoded one -// frame. -TEST_F(DecryptingVideoDecoderTest, Reset_DuringIdleAfterDecodedOneFrame) { - Initialize(); - EnterNormalDecodingState(); - Reset(); -} - -// Test resetting when the decoder is in kPendingDecode state. -TEST_F(DecryptingVideoDecoderTest, Reset_DuringPendingDecode) { - Initialize(); - EnterPendingDecodeState(); - - EXPECT_CALL(*this, DecodeDone(DecodeStatus::ABORTED)); - - Reset(); -} - -// Test resetting when the decoder is in kWaitingForKey state. -TEST_F(DecryptingVideoDecoderTest, Reset_DuringWaitingForKey) { - Initialize(); - EnterWaitingForKeyState(); - - EXPECT_CALL(*this, DecodeDone(DecodeStatus::ABORTED)); - - Reset(); -} - -// Test resetting when the decoder has hit end of stream and is in -// kDecodeFinished state. -TEST_F(DecryptingVideoDecoderTest, Reset_AfterDecodeFinished) { - Initialize(); - EnterNormalDecodingState(); - EnterEndOfStreamState(); - Reset(); -} - -// Test resetting after the decoder has been reset. -TEST_F(DecryptingVideoDecoderTest, Reset_AfterReset) { - Initialize(); - EnterNormalDecodingState(); - Reset(); - Reset(); -} - -// Test destruction when the decoder is in kPendingDecoderInit state. -TEST_F(DecryptingVideoDecoderTest, Destroy_DuringPendingDecoderInit) { - SetCdmType(CDM_WITH_DECRYPTOR); - EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) - .WillOnce(SaveArg<1>(&pending_init_cb_)); - - InitializeAndExpectResult(TestVideoConfig::NormalEncrypted(), false); - EXPECT_FALSE(pending_init_cb_.is_null()); - - Destroy(); -} - -// Test destruction when the decoder is in kIdle state but has not decoded any -// frame. -TEST_F(DecryptingVideoDecoderTest, Destroy_DuringIdleAfterInitialization) { - Initialize(); - Destroy(); -} - -// Test destruction when the decoder is in kIdle state after it has decoded one -// frame. -TEST_F(DecryptingVideoDecoderTest, Destroy_DuringIdleAfterDecodedOneFrame) { - Initialize(); - EnterNormalDecodingState(); - Destroy(); -} - -// Test destruction when the decoder is in kPendingDecode state. -TEST_F(DecryptingVideoDecoderTest, Destroy_DuringPendingDecode) { - Initialize(); - EnterPendingDecodeState(); - - EXPECT_CALL(*this, DecodeDone(DecodeStatus::ABORTED)); - - Destroy(); -} - -// Test destruction when the decoder is in kWaitingForKey state. -TEST_F(DecryptingVideoDecoderTest, Destroy_DuringWaitingForKey) { - Initialize(); - EnterWaitingForKeyState(); - - EXPECT_CALL(*this, DecodeDone(DecodeStatus::ABORTED)); - - Destroy(); -} - -// Test destruction when the decoder has hit end of stream and is in -// kDecodeFinished state. -TEST_F(DecryptingVideoDecoderTest, Destroy_AfterDecodeFinished) { - Initialize(); - EnterNormalDecodingState(); - EnterEndOfStreamState(); - Destroy(); -} - -// Test destruction when there is a pending reset on the decoder. -// Reset is pending because it cannot complete when the video decode callback -// is pending. -TEST_F(DecryptingVideoDecoderTest, Destroy_DuringPendingReset) { - Initialize(); - EnterPendingDecodeState(); - - EXPECT_CALL(*decryptor_, ResetDecoder(Decryptor::kVideo)); - EXPECT_CALL(*this, DecodeDone(DecodeStatus::ABORTED)); - - decoder_->Reset(NewExpectedClosure()); - Destroy(); -} - -// Test destruction after the decoder has been reset. -TEST_F(DecryptingVideoDecoderTest, Destroy_AfterReset) { - Initialize(); - EnterNormalDecodingState(); - Reset(); - Destroy(); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/fake_video_decoder.cc b/src/cobalt/media/filters/fake_video_decoder.cc deleted file mode 100644 index 25295ff..0000000 --- a/src/cobalt/media/filters/fake_video_decoder.cc +++ /dev/null
@@ -1,253 +0,0 @@ -// Copyright 2013 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/filters/fake_video_decoder.h" - -#include "base/location.h" -#include "cobalt/media/base/bind_to_current_loop.h" -#include "cobalt/media/base/test_helpers.h" - -namespace cobalt { -namespace media { - -FakeVideoDecoder::FakeVideoDecoder(int decoding_delay, - int max_parallel_decoding_requests, - const BytesDecodedCB& bytes_decoded_cb) - : decoding_delay_(decoding_delay), - max_parallel_decoding_requests_(max_parallel_decoding_requests), - bytes_decoded_cb_(bytes_decoded_cb), - state_(STATE_UNINITIALIZED), - hold_decode_(false), - total_bytes_decoded_(0), - fail_to_initialize_(false), - weak_factory_(this) { - DCHECK_GE(decoding_delay, 0); -} - -FakeVideoDecoder::~FakeVideoDecoder() { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (state_ == STATE_UNINITIALIZED) return; - - if (!init_cb_.IsNull()) SatisfyInit(); - if (!held_decode_callbacks_.empty()) SatisfyDecode(); - if (!reset_cb_.IsNull()) SatisfyReset(); - - decoded_frames_.clear(); -} - -std::string FakeVideoDecoder::GetDisplayName() const { - return "FakeVideoDecoder"; -} - -void FakeVideoDecoder::Initialize(const VideoDecoderConfig& config, - bool low_delay, CdmContext* /* cdm_context */, - const InitCB& init_cb, - const OutputCB& output_cb) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(config.IsValidConfig()); - DCHECK(held_decode_callbacks_.empty()) - << "No reinitialization during pending decode."; - DCHECK(reset_cb_.IsNull()) << "No reinitialization during pending reset."; - - current_config_ = config; - init_cb_.SetCallback(BindToCurrentLoop(init_cb)); - - // Don't need BindToCurrentLoop() because |output_cb_| is only called from - // RunDecodeCallback() which is posted from Decode(). - output_cb_ = output_cb; - - if (!decoded_frames_.empty()) { - DVLOG(1) << "Decoded frames dropped during reinitialization."; - decoded_frames_.clear(); - } - - if (fail_to_initialize_) { - state_ = STATE_ERROR; - init_cb_.RunOrHold(false); - } else { - state_ = STATE_NORMAL; - init_cb_.RunOrHold(true); - } -} - -void FakeVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, - const DecodeCB& decode_cb) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(reset_cb_.IsNull()); - DCHECK_LE(decoded_frames_.size(), - decoding_delay_ + held_decode_callbacks_.size()); - DCHECK_LT(static_cast<int>(held_decode_callbacks_.size()), - max_parallel_decoding_requests_); - DCHECK_NE(state_, STATE_END_OF_STREAM); - - int buffer_size = buffer->end_of_stream() ? 0 : buffer->data_size(); - DecodeCB wrapped_decode_cb = - base::Bind(&FakeVideoDecoder::OnFrameDecoded, weak_factory_.GetWeakPtr(), - buffer_size, BindToCurrentLoop(decode_cb)); - - if (state_ == STATE_ERROR) { - wrapped_decode_cb.Run(DecodeStatus::DECODE_ERROR); - return; - } - - if (buffer->end_of_stream()) { - state_ = STATE_END_OF_STREAM; - } else { - DCHECK(VerifyFakeVideoBufferForTest(buffer, current_config_)); - scoped_refptr<VideoFrame> video_frame = VideoFrame::CreateColorFrame( - current_config_.coded_size(), 0, 0, 0, buffer->timestamp()); - decoded_frames_.push_back(video_frame); - } - - RunOrHoldDecode(wrapped_decode_cb); -} - -void FakeVideoDecoder::Reset(const base::Closure& closure) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(reset_cb_.IsNull()); - - reset_cb_.SetCallback(BindToCurrentLoop(closure)); - decoded_frames_.clear(); - - // Defer the reset if a decode is pending. - if (!held_decode_callbacks_.empty()) return; - - DoReset(); -} - -void FakeVideoDecoder::HoldNextInit() { - DCHECK(thread_checker_.CalledOnValidThread()); - init_cb_.HoldCallback(); -} - -void FakeVideoDecoder::HoldDecode() { - DCHECK(thread_checker_.CalledOnValidThread()); - hold_decode_ = true; -} - -void FakeVideoDecoder::HoldNextReset() { - DCHECK(thread_checker_.CalledOnValidThread()); - reset_cb_.HoldCallback(); -} - -void FakeVideoDecoder::SatisfyInit() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(held_decode_callbacks_.empty()); - DCHECK(reset_cb_.IsNull()); - - init_cb_.RunHeldCallback(); -} - -void FakeVideoDecoder::SatisfyDecode() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(hold_decode_); - - hold_decode_ = false; - - while (!held_decode_callbacks_.empty()) { - SatisfySingleDecode(); - } -} - -void FakeVideoDecoder::SatisfySingleDecode() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(!held_decode_callbacks_.empty()); - - DecodeCB decode_cb = held_decode_callbacks_.front(); - held_decode_callbacks_.pop_front(); - RunDecodeCallback(decode_cb); - - if (!reset_cb_.IsNull() && held_decode_callbacks_.empty()) DoReset(); -} - -void FakeVideoDecoder::SatisfyReset() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(held_decode_callbacks_.empty()); - reset_cb_.RunHeldCallback(); -} - -void FakeVideoDecoder::SimulateError() { - DCHECK(thread_checker_.CalledOnValidThread()); - - state_ = STATE_ERROR; - while (!held_decode_callbacks_.empty()) { - held_decode_callbacks_.front().Run(DecodeStatus::DECODE_ERROR); - held_decode_callbacks_.pop_front(); - } - decoded_frames_.clear(); -} - -void FakeVideoDecoder::SimulateFailureToInit() { fail_to_initialize_ = true; } - -int FakeVideoDecoder::GetMaxDecodeRequests() const { - return max_parallel_decoding_requests_; -} - -void FakeVideoDecoder::OnFrameDecoded(int buffer_size, - const DecodeCB& decode_cb, - DecodeStatus status) { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (status == DecodeStatus::OK) { - total_bytes_decoded_ += buffer_size; - bytes_decoded_cb_.Run(buffer_size); - } - decode_cb.Run(status); -} - -void FakeVideoDecoder::RunOrHoldDecode(const DecodeCB& decode_cb) { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (hold_decode_) { - held_decode_callbacks_.push_back(decode_cb); - } else { - DCHECK(held_decode_callbacks_.empty()); - RunDecodeCallback(decode_cb); - } -} - -void FakeVideoDecoder::RunDecodeCallback(const DecodeCB& decode_cb) { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (!reset_cb_.IsNull()) { - DCHECK(decoded_frames_.empty()); - decode_cb.Run(DecodeStatus::ABORTED); - return; - } - - // Make sure we leave decoding_delay_ frames in the queue and also frames for - // all pending decode callbacks, except the current one. - if (decoded_frames_.size() > - decoding_delay_ + held_decode_callbacks_.size()) { - output_cb_.Run(decoded_frames_.front()); - decoded_frames_.pop_front(); - } else if (state_ == STATE_END_OF_STREAM) { - // Drain the queue if this was the last request in the stream, otherwise - // just pop the last frame from the queue. - if (held_decode_callbacks_.empty()) { - while (!decoded_frames_.empty()) { - output_cb_.Run(decoded_frames_.front()); - decoded_frames_.pop_front(); - } - state_ = STATE_NORMAL; - } else if (!decoded_frames_.empty()) { - output_cb_.Run(decoded_frames_.front()); - decoded_frames_.pop_front(); - } - } - - decode_cb.Run(DecodeStatus::OK); -} - -void FakeVideoDecoder::DoReset() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(held_decode_callbacks_.empty()); - DCHECK(!reset_cb_.IsNull()); - - reset_cb_.RunOrHold(); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/fake_video_decoder.h b/src/cobalt/media/filters/fake_video_decoder.h deleted file mode 100644 index 4f1af47..0000000 --- a/src/cobalt/media/filters/fake_video_decoder.h +++ /dev/null
@@ -1,130 +0,0 @@ -// Copyright 2013 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. - -#ifndef COBALT_MEDIA_FILTERS_FAKE_VIDEO_DECODER_H_ -#define COBALT_MEDIA_FILTERS_FAKE_VIDEO_DECODER_H_ - -#include <list> -#include <string> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/callback_helpers.h" -#include "base/memory/weak_ptr.h" -#include "base/threading/thread_checker.h" -#include "cobalt/media/base/callback_holder.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/pipeline_status.h" -#include "cobalt/media/base/video_decoder.h" -#include "cobalt/media/base/video_decoder_config.h" -#include "cobalt/media/base/video_frame.h" -#include "starboard/types.h" -#include "ui/gfx/size.h" - -using base::ResetAndReturn; - -namespace base { -class SingleThreadTaskRunner; -} - -namespace cobalt { -namespace media { - -typedef base::Callback<void(int)> BytesDecodedCB; - -class FakeVideoDecoder : public VideoDecoder { - public: - // Constructs an object with a decoding delay of |decoding_delay| frames. - // |bytes_decoded_cb| is called after each decode. The sum of the byte - // count over all calls will be equal to total_bytes_decoded(). - FakeVideoDecoder(int decoding_delay, int max_parallel_decoding_requests, - const BytesDecodedCB& bytes_decoded_cb); - ~FakeVideoDecoder() OVERRIDE; - - // VideoDecoder implementation. - std::string GetDisplayName() const OVERRIDE; - void Initialize(const VideoDecoderConfig& config, bool low_delay, - CdmContext* cdm_context, const InitCB& init_cb, - const OutputCB& output_cb) OVERRIDE; - void Decode(const scoped_refptr<DecoderBuffer>& buffer, - const DecodeCB& decode_cb) OVERRIDE; - void Reset(const base::Closure& closure) OVERRIDE; - int GetMaxDecodeRequests() const OVERRIDE; - - // Holds the next init/decode/reset callback from firing. - void HoldNextInit(); - void HoldDecode(); - void HoldNextReset(); - - // Satisfies the pending init/decode/reset callback, which must be ready to - // fire when these methods are called. - void SatisfyInit(); - void SatisfyDecode(); - void SatisfyReset(); - - // Satisfies single decode request. - void SatisfySingleDecode(); - - void SimulateError(); - // Fail with status DECODER_ERROR_NOT_SUPPORTED when Initialize() is called. - void SimulateFailureToInit(); - - int total_bytes_decoded() const { return total_bytes_decoded_; } - - private: - enum State { - STATE_UNINITIALIZED, - STATE_NORMAL, - STATE_END_OF_STREAM, - STATE_ERROR, - }; - - // Callback for updating |total_bytes_decoded_|. - void OnFrameDecoded(int buffer_size, const DecodeCB& decode_cb, - DecodeStatus status); - - // Runs |decode_cb| or puts it to |held_decode_callbacks_| depending on - // current value of |hold_decode_|. - void RunOrHoldDecode(const DecodeCB& decode_cb); - - // Runs |decode_cb| with a frame from |decoded_frames_|. - void RunDecodeCallback(const DecodeCB& decode_cb); - - void DoReset(); - - base::ThreadChecker thread_checker_; - - const size_t decoding_delay_; - const int max_parallel_decoding_requests_; - BytesDecodedCB bytes_decoded_cb_; - - State state_; - - CallbackHolder<InitCB> init_cb_; - CallbackHolder<base::Closure> reset_cb_; - - OutputCB output_cb_; - - bool hold_decode_; - std::list<DecodeCB> held_decode_callbacks_; - - VideoDecoderConfig current_config_; - - std::list<scoped_refptr<VideoFrame> > decoded_frames_; - - int total_bytes_decoded_; - - bool fail_to_initialize_; - - // NOTE: Weak pointers must be invalidated before all other member variables. - base::WeakPtrFactory<FakeVideoDecoder> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(FakeVideoDecoder); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_FAKE_VIDEO_DECODER_H_
diff --git a/src/cobalt/media/filters/fake_video_decoder_unittest.cc b/src/cobalt/media/filters/fake_video_decoder_unittest.cc deleted file mode 100644 index bb5a229..0000000 --- a/src/cobalt/media/filters/fake_video_decoder_unittest.cc +++ /dev/null
@@ -1,414 +0,0 @@ -// Copyright 2013 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/basictypes.h" -#include "base/bind.h" -#include "base/message_loop.h" -#include "base/run_loop.h" -#include "cobalt/media/base/decoder_buffer.h" -#include "cobalt/media/base/mock_filters.h" -#include "cobalt/media/base/test_helpers.h" -#include "cobalt/media/base/video_frame.h" -#include "cobalt/media/filters/fake_video_decoder.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cobalt { -namespace media { - -static const int kTotalBuffers = 12; -static const int kDurationMs = 30; - -struct FakeVideoDecoderTestParams { - FakeVideoDecoderTestParams(int decoding_delay, int max_decode_requests) - : decoding_delay(decoding_delay), - max_decode_requests(max_decode_requests) {} - int decoding_delay; - int max_decode_requests; -}; - -class FakeVideoDecoderTest - : public testing::Test, - public testing::WithParamInterface<FakeVideoDecoderTestParams> { - public: - FakeVideoDecoderTest() - : decoder_(new FakeVideoDecoder( - GetParam().decoding_delay, GetParam().max_decode_requests, - base::Bind(&FakeVideoDecoderTest::OnBytesDecoded, - base::Unretained(this)))), - num_input_buffers_(0), - num_decoded_frames_(0), - num_bytes_decoded_(0), - total_bytes_in_buffers_(0), - last_decode_status_(DecodeStatus::OK), - pending_decode_requests_(0), - is_reset_pending_(false) {} - - virtual ~FakeVideoDecoderTest() { Destroy(); } - - void InitializeWithConfigAndExpectResult(const VideoDecoderConfig& config, - bool success) { - decoder_->Initialize( - config, false, NULL, NewExpectedBoolCB(success), - base::Bind(&FakeVideoDecoderTest::FrameReady, base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - current_config_ = config; - } - - void Initialize() { - InitializeWithConfigAndExpectResult(TestVideoConfig::Normal(), true); - } - - void EnterPendingInitState() { - decoder_->HoldNextInit(); - Initialize(); - } - - void SatisfyInit() { - decoder_->SatisfyInit(); - base::RunLoop().RunUntilIdle(); - } - - // Callback for VideoDecoder::Decode(). - void DecodeDone(DecodeStatus status) { - DCHECK_GT(pending_decode_requests_, 0); - --pending_decode_requests_; - last_decode_status_ = status; - } - - void FrameReady(const scoped_refptr<VideoFrame>& frame) { - DCHECK(!frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)); - last_decoded_frame_ = frame; - num_decoded_frames_++; - } - - void OnBytesDecoded(int count) { num_bytes_decoded_ += count; } - - enum CallbackResult { PENDING, OK, NOT_ENOUGH_DATA, ABORTED }; - - void ExpectReadResult(CallbackResult result) { - switch (result) { - case PENDING: - EXPECT_GT(pending_decode_requests_, 0); - break; - case OK: - EXPECT_EQ(0, pending_decode_requests_); - ASSERT_EQ(DecodeStatus::OK, last_decode_status_); - ASSERT_TRUE(last_decoded_frame_.get()); - break; - case NOT_ENOUGH_DATA: - EXPECT_EQ(0, pending_decode_requests_); - ASSERT_EQ(DecodeStatus::OK, last_decode_status_); - ASSERT_FALSE(last_decoded_frame_.get()); - break; - case ABORTED: - EXPECT_EQ(0, pending_decode_requests_); - ASSERT_EQ(DecodeStatus::ABORTED, last_decode_status_); - EXPECT_FALSE(last_decoded_frame_.get()); - break; - } - } - - void Decode() { - scoped_refptr<DecoderBuffer> buffer; - - if (num_input_buffers_ < kTotalBuffers) { - buffer = CreateFakeVideoBufferForTest( - current_config_, - base::TimeDelta::FromMilliseconds(kDurationMs * num_input_buffers_), - base::TimeDelta::FromMilliseconds(kDurationMs)); - total_bytes_in_buffers_ += buffer->data_size(); - } else { - buffer = DecoderBuffer::CreateEOSBuffer(); - } - - ++num_input_buffers_; - ++pending_decode_requests_; - - decoder_->Decode(buffer, base::Bind(&FakeVideoDecoderTest::DecodeDone, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - void ReadOneFrame() { - last_decoded_frame_ = NULL; - do { - Decode(); - } while (!last_decoded_frame_.get() && pending_decode_requests_ == 0); - } - - void ReadAllFrames() { - do { - Decode(); - } while (num_input_buffers_ <= kTotalBuffers); // All input buffers + EOS. - } - - void EnterPendingReadState() { - // Pass the initial NOT_ENOUGH_DATA stage. - ReadOneFrame(); - decoder_->HoldDecode(); - ReadOneFrame(); - ExpectReadResult(PENDING); - } - - void SatisfyDecodeAndExpect(CallbackResult result) { - decoder_->SatisfyDecode(); - base::RunLoop().RunUntilIdle(); - ExpectReadResult(result); - } - - void SatisfyRead() { SatisfyDecodeAndExpect(OK); } - - // Callback for VideoDecoder::Reset(). - void OnDecoderReset() { - DCHECK(is_reset_pending_); - is_reset_pending_ = false; - } - - void ExpectResetResult(CallbackResult result) { - switch (result) { - case PENDING: - EXPECT_TRUE(is_reset_pending_); - break; - case OK: - EXPECT_FALSE(is_reset_pending_); - break; - default: - NOTREACHED(); - } - } - - void ResetAndExpect(CallbackResult result) { - is_reset_pending_ = true; - decoder_->Reset(base::Bind(&FakeVideoDecoderTest::OnDecoderReset, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - ExpectResetResult(result); - } - - void EnterPendingResetState() { - decoder_->HoldNextReset(); - ResetAndExpect(PENDING); - } - - void SatisfyReset() { - decoder_->SatisfyReset(); - base::RunLoop().RunUntilIdle(); - ExpectResetResult(OK); - } - - void Destroy() { - decoder_.reset(); - base::RunLoop().RunUntilIdle(); - - // All pending callbacks must have been fired. - DCHECK_EQ(pending_decode_requests_, 0); - DCHECK(!is_reset_pending_); - } - - base::MessageLoop message_loop_; - VideoDecoderConfig current_config_; - - std::unique_ptr<FakeVideoDecoder> decoder_; - - int num_input_buffers_; - int num_decoded_frames_; - int num_bytes_decoded_; - int total_bytes_in_buffers_; - - // Callback result/status. - DecodeStatus last_decode_status_; - scoped_refptr<VideoFrame> last_decoded_frame_; - int pending_decode_requests_; - bool is_reset_pending_; - - private: - DISALLOW_COPY_AND_ASSIGN(FakeVideoDecoderTest); -}; - -INSTANTIATE_TEST_CASE_P(NoParallelDecode, FakeVideoDecoderTest, - ::testing::Values(FakeVideoDecoderTestParams(9, 1), - FakeVideoDecoderTestParams(0, 1))); -INSTANTIATE_TEST_CASE_P(ParallelDecode, FakeVideoDecoderTest, - ::testing::Values(FakeVideoDecoderTestParams(9, 3), - FakeVideoDecoderTestParams(0, 3))); - -TEST_P(FakeVideoDecoderTest, Initialize) { Initialize(); } - -TEST_P(FakeVideoDecoderTest, SimulateFailureToInitialize) { - decoder_->SimulateFailureToInit(); - InitializeWithConfigAndExpectResult(TestVideoConfig::Normal(), false); - Decode(); - EXPECT_EQ(last_decode_status_, DecodeStatus::DECODE_ERROR); -} - -TEST_P(FakeVideoDecoderTest, Read_AllFrames) { - Initialize(); - ReadAllFrames(); - EXPECT_EQ(kTotalBuffers, num_decoded_frames_); - EXPECT_EQ(total_bytes_in_buffers_, num_bytes_decoded_); -} - -TEST_P(FakeVideoDecoderTest, Read_DecodingDelay) { - Initialize(); - - while (num_input_buffers_ < kTotalBuffers) { - ReadOneFrame(); - EXPECT_EQ(num_input_buffers_, - num_decoded_frames_ + GetParam().decoding_delay); - } -} - -TEST_P(FakeVideoDecoderTest, Read_ZeroDelay) { - decoder_.reset(new FakeVideoDecoder( - 0, 1, base::Bind(&FakeVideoDecoderTest::OnBytesDecoded, - base::Unretained(this)))); - Initialize(); - - while (num_input_buffers_ < kTotalBuffers) { - ReadOneFrame(); - EXPECT_EQ(num_input_buffers_, num_decoded_frames_); - } -} - -TEST_P(FakeVideoDecoderTest, Read_Pending_NotEnoughData) { - if (GetParam().decoding_delay < 1) return; - - Initialize(); - decoder_->HoldDecode(); - ReadOneFrame(); - ExpectReadResult(PENDING); - SatisfyDecodeAndExpect(NOT_ENOUGH_DATA); - - // Verify that FrameReady() hasn't been called. - EXPECT_FALSE(last_decoded_frame_.get()); -} - -TEST_P(FakeVideoDecoderTest, Read_Pending_OK) { - Initialize(); - EnterPendingReadState(); - SatisfyDecodeAndExpect(OK); -} - -TEST_P(FakeVideoDecoderTest, Read_Parallel) { - if (GetParam().max_decode_requests < 2) return; - - Initialize(); - decoder_->HoldDecode(); - for (int i = 0; i < GetParam().max_decode_requests; ++i) { - ReadOneFrame(); - ExpectReadResult(PENDING); - } - EXPECT_EQ(GetParam().max_decode_requests, pending_decode_requests_); - SatisfyDecodeAndExpect(GetParam().max_decode_requests > - GetParam().decoding_delay - ? OK - : NOT_ENOUGH_DATA); -} - -TEST_P(FakeVideoDecoderTest, ReadWithHold_DecodingDelay) { - Initialize(); - - // Hold all decodes and satisfy one decode at a time. - decoder_->HoldDecode(); - int num_decodes_satisfied = 0; - while (num_decoded_frames_ == 0) { - while (pending_decode_requests_ < decoder_->GetMaxDecodeRequests()) - Decode(); - decoder_->SatisfySingleDecode(); - ++num_decodes_satisfied; - base::RunLoop().RunUntilIdle(); - } - - DCHECK_EQ(num_decoded_frames_, 1); - DCHECK_EQ(num_decodes_satisfied, GetParam().decoding_delay + 1); -} - -TEST_P(FakeVideoDecoderTest, Reinitialize) { - Initialize(); - ReadOneFrame(); - InitializeWithConfigAndExpectResult(TestVideoConfig::Large(), true); - ReadOneFrame(); -} - -TEST_P(FakeVideoDecoderTest, SimulateFailureToReinitialize) { - Initialize(); - ReadOneFrame(); - decoder_->SimulateFailureToInit(); - InitializeWithConfigAndExpectResult(TestVideoConfig::Normal(), false); - Decode(); - EXPECT_EQ(last_decode_status_, DecodeStatus::DECODE_ERROR); -} - -// Reinitializing the decoder during the middle of the decoding process can -// cause dropped frames. -TEST_P(FakeVideoDecoderTest, Reinitialize_FrameDropped) { - if (GetParam().decoding_delay < 1) return; - - Initialize(); - ReadOneFrame(); - Initialize(); - ReadAllFrames(); - EXPECT_LT(num_decoded_frames_, kTotalBuffers); -} - -TEST_P(FakeVideoDecoderTest, Reset) { - Initialize(); - ReadOneFrame(); - ResetAndExpect(OK); -} - -TEST_P(FakeVideoDecoderTest, Reset_DuringPendingRead) { - Initialize(); - EnterPendingReadState(); - ResetAndExpect(PENDING); - SatisfyDecodeAndExpect(ABORTED); -} - -TEST_P(FakeVideoDecoderTest, Reset_Pending) { - Initialize(); - EnterPendingResetState(); - SatisfyReset(); -} - -TEST_P(FakeVideoDecoderTest, Reset_PendingDuringPendingRead) { - Initialize(); - EnterPendingReadState(); - EnterPendingResetState(); - SatisfyDecodeAndExpect(ABORTED); - SatisfyReset(); -} - -TEST_P(FakeVideoDecoderTest, Destroy) { - Initialize(); - ReadOneFrame(); - ExpectReadResult(OK); - Destroy(); -} - -TEST_P(FakeVideoDecoderTest, Destroy_DuringPendingInitialization) { - EnterPendingInitState(); - Destroy(); -} - -TEST_P(FakeVideoDecoderTest, Destroy_DuringPendingRead) { - Initialize(); - EnterPendingReadState(); - Destroy(); -} - -TEST_P(FakeVideoDecoderTest, Destroy_DuringPendingReset) { - Initialize(); - EnterPendingResetState(); - Destroy(); -} - -TEST_P(FakeVideoDecoderTest, Destroy_DuringPendingReadAndPendingReset) { - Initialize(); - EnterPendingReadState(); - EnterPendingResetState(); - Destroy(); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/file_data_source.cc b/src/cobalt/media/filters/file_data_source.cc deleted file mode 100644 index 5725e2f..0000000 --- a/src/cobalt/media/filters/file_data_source.cc +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright (c) 2012 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/filters/file_data_source.h" - -#include <algorithm> -#include <utility> - -#include "base/logging.h" -#include "starboard/memory.h" - -namespace cobalt { -namespace media { - -FileDataSource::FileDataSource() - : force_read_errors_(false), force_streaming_(false), bytes_read_(0) {} - -FileDataSource::FileDataSource(base::File file) - : force_read_errors_(false), force_streaming_(false), bytes_read_(0) { - file_.Initialize(std::move(file)); -} - -bool FileDataSource::Initialize(const base::FilePath& file_path) { - DCHECK(!file_.IsValid()); - return file_.Initialize(file_path); -} - -void FileDataSource::Stop() {} - -void FileDataSource::Abort() {} - -void FileDataSource::Read(int64_t position, int size, uint8_t* data, - const DataSource::ReadCB& read_cb) { - if (force_read_errors_ || !file_.IsValid()) { - read_cb.Run(kReadError); - return; - } - - int64_t file_size = file_.length(); - - CHECK_GE(file_size, 0); - CHECK_GE(position, 0); - CHECK_GE(size, 0); - - // Cap position and size within bounds. - position = std::min(position, file_size); - int64_t clamped_size = - std::min(static_cast<int64_t>(size), file_size - position); - - SbMemoryCopy(data, file_.data() + position, clamped_size); - bytes_read_ += clamped_size; - read_cb.Run(clamped_size); -} - -bool FileDataSource::GetSize(int64_t* size_out) { - *size_out = file_.length(); - return true; -} - -bool FileDataSource::IsStreaming() { return force_streaming_; } - -void FileDataSource::SetBitrate(int bitrate) {} - -FileDataSource::~FileDataSource() {} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/file_data_source.h b/src/cobalt/media/filters/file_data_source.h deleted file mode 100644 index f66f0f3..0000000 --- a/src/cobalt/media/filters/file_data_source.h +++ /dev/null
@@ -1,58 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef COBALT_MEDIA_FILTERS_FILE_DATA_SOURCE_H_ -#define COBALT_MEDIA_FILTERS_FILE_DATA_SOURCE_H_ - -#include <string> - -#include "base/basictypes.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/files/memory_mapped_file.h" -#include "cobalt/media/base/data_source.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -// Basic data source that treats the URL as a file path, and uses the file -// system to read data for a media pipeline. -class MEDIA_EXPORT FileDataSource : public DataSource { - public: - FileDataSource(); - explicit FileDataSource(base::File file); - ~FileDataSource() OVERRIDE; - - bool Initialize(const base::FilePath& file_path); - - // Implementation of DataSource. - void Stop() OVERRIDE; - void Abort() OVERRIDE; - void Read(int64_t position, int size, uint8_t* data, - const DataSource::ReadCB& read_cb) OVERRIDE; - bool GetSize(int64_t* size_out) OVERRIDE; - bool IsStreaming() OVERRIDE; - void SetBitrate(int bitrate) OVERRIDE; - - // Unit test helpers. Recreate the object if you want the default behaviour. - void force_read_errors_for_testing() { force_read_errors_ = true; } - void force_streaming_for_testing() { force_streaming_ = true; } - uint64_t bytes_read_for_testing() { return bytes_read_; } - void reset_bytes_read_for_testing() { bytes_read_ = 0; } - - private: - base::MemoryMappedFile file_; - - bool force_read_errors_; - bool force_streaming_; - uint64_t bytes_read_; - - DISALLOW_COPY_AND_ASSIGN(FileDataSource); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_FILE_DATA_SOURCE_H_
diff --git a/src/cobalt/media/filters/file_data_source_unittest.cc b/src/cobalt/media/filters/file_data_source_unittest.cc deleted file mode 100644 index 03ed9f7..0000000 --- a/src/cobalt/media/filters/file_data_source_unittest.cc +++ /dev/null
@@ -1,87 +0,0 @@ -// Copyright (c) 2012 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 <string> - -#include "base/base_paths.h" -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/path_service.h" -#include "base/utf_string_conversions.h" -#include "cobalt/media/base/test_helpers.h" -#include "cobalt/media/filters/file_data_source.h" -#include "starboard/types.h" - -using ::testing::NiceMock; -using ::testing::StrictMock; - -namespace cobalt { -namespace media { - -class ReadCBHandler { - public: - ReadCBHandler() {} - - MOCK_METHOD1(ReadCB, void(int size)); - - private: - DISALLOW_COPY_AND_ASSIGN(ReadCBHandler); -}; - -// Returns a path to the test file which contains the string "0123456789" -// without the quotes or any trailing space or null termination. The file lives -// under the media/test/data directory. Under Windows, strings for the -// FilePath class are unicode, and the pipeline wants char strings. Convert -// the string to UTF8 under Windows. For Mac and Linux, file paths are already -// chars so just return the string from the base::FilePath. -base::FilePath TestFileURL() { - base::FilePath data_dir; - EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); - data_dir = data_dir.Append(FILE_PATH_LITERAL("media")) - .Append(FILE_PATH_LITERAL("test")) - .Append(FILE_PATH_LITERAL("data")) - .Append(FILE_PATH_LITERAL("ten_byte_file")); - return data_dir; -} - -// Use the mock filter host to directly call the Read and GetPosition methods. -TEST(FileDataSourceTest, ReadData) { - int64_t size; - uint8_t ten_bytes[10]; - - // Create our mock filter host and initialize the data source. - FileDataSource data_source; - - EXPECT_TRUE(data_source.Initialize(TestFileURL())); - EXPECT_TRUE(data_source.GetSize(&size)); - EXPECT_EQ(10, size); - - ReadCBHandler handler; - EXPECT_CALL(handler, ReadCB(10)); - data_source.Read(0, 10, ten_bytes, base::Bind(&ReadCBHandler::ReadCB, - base::Unretained(&handler))); - EXPECT_EQ('0', ten_bytes[0]); - EXPECT_EQ('5', ten_bytes[5]); - EXPECT_EQ('9', ten_bytes[9]); - - EXPECT_CALL(handler, ReadCB(1)); - data_source.Read(9, 1, ten_bytes, base::Bind(&ReadCBHandler::ReadCB, - base::Unretained(&handler))); - EXPECT_EQ('9', ten_bytes[0]); - - EXPECT_CALL(handler, ReadCB(0)); - data_source.Read(10, 10, ten_bytes, base::Bind(&ReadCBHandler::ReadCB, - base::Unretained(&handler))); - - EXPECT_CALL(handler, ReadCB(5)); - data_source.Read(5, 10, ten_bytes, base::Bind(&ReadCBHandler::ReadCB, - base::Unretained(&handler))); - EXPECT_EQ('5', ten_bytes[0]); - - data_source.Stop(); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/in_memory_url_protocol.cc b/src/cobalt/media/filters/in_memory_url_protocol.cc deleted file mode 100644 index ad63b69..0000000 --- a/src/cobalt/media/filters/in_memory_url_protocol.cc +++ /dev/null
@@ -1,59 +0,0 @@ -// Copyright (c) 2011 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/filters/in_memory_url_protocol.h" - -#include "cobalt/media/ffmpeg/ffmpeg_common.h" -#include "starboard/memory.h" - -namespace cobalt { -namespace media { - -InMemoryUrlProtocol::InMemoryUrlProtocol(const uint8_t* data, int64_t size, - bool streaming) - : data_(data), - size_(size >= 0 ? size : 0), - position_(0), - streaming_(streaming) {} - -InMemoryUrlProtocol::~InMemoryUrlProtocol() {} - -int InMemoryUrlProtocol::Read(int size, uint8_t* data) { - if (size < 0) return AVERROR(EIO); - - int64_t available_bytes = size_ - position_; - if (size > available_bytes) size = available_bytes; - - if (size > 0) { - SbMemoryCopy(data, data_ + position_, size); - position_ += size; - } - - return size; -} - -bool InMemoryUrlProtocol::GetPosition(int64_t* position_out) { - if (!position_out) return false; - - *position_out = position_; - return true; -} - -bool InMemoryUrlProtocol::SetPosition(int64_t position) { - if (position < 0 || position > size_) return false; - position_ = position; - return true; -} - -bool InMemoryUrlProtocol::GetSize(int64_t* size_out) { - if (!size_out) return false; - - *size_out = size_; - return true; -} - -bool InMemoryUrlProtocol::IsStreaming() { return streaming_; } - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/in_memory_url_protocol.h b/src/cobalt/media/filters/in_memory_url_protocol.h deleted file mode 100644 index a8de6b6..0000000 --- a/src/cobalt/media/filters/in_memory_url_protocol.h +++ /dev/null
@@ -1,45 +0,0 @@ -// Copyright (c) 2011 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. - -#ifndef COBALT_MEDIA_FILTERS_IN_MEMORY_URL_PROTOCOL_H_ -#define COBALT_MEDIA_FILTERS_IN_MEMORY_URL_PROTOCOL_H_ - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "cobalt/media/filters/ffmpeg_glue.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -// Simple FFmpegURLProtocol that reads from a buffer. -// NOTE: This object does not copy the buffer so the -// buffer pointer passed into the constructor -// needs to remain valid for the entire lifetime of -// this object. -class MEDIA_EXPORT InMemoryUrlProtocol : public FFmpegURLProtocol { - public: - InMemoryUrlProtocol(const uint8_t* buf, int64_t size, bool streaming); - virtual ~InMemoryUrlProtocol(); - - // FFmpegURLProtocol methods. - int Read(int size, uint8_t* data) OVERRIDE; - bool GetPosition(int64_t* position_out) OVERRIDE; - bool SetPosition(int64_t position) OVERRIDE; - bool GetSize(int64_t* size_out) OVERRIDE; - bool IsStreaming() OVERRIDE; - - private: - const uint8_t* data_; - int64_t size_; - int64_t position_; - bool streaming_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(InMemoryUrlProtocol); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_IN_MEMORY_URL_PROTOCOL_H_
diff --git a/src/cobalt/media/filters/in_memory_url_protocol_unittest.cc b/src/cobalt/media/filters/in_memory_url_protocol_unittest.cc deleted file mode 100644 index 6ef461d..0000000 --- a/src/cobalt/media/filters/in_memory_url_protocol_unittest.cc +++ /dev/null
@@ -1,58 +0,0 @@ -// 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 <limits> - -#include "cobalt/media/ffmpeg/ffmpeg_common.h" -#include "cobalt/media/filters/in_memory_url_protocol.h" -#include "starboard/memory.h" -#include "starboard/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cobalt { -namespace media { - -static const uint8_t kData[] = {0x01, 0x02, 0x03, 0x04}; - -TEST(InMemoryUrlProtocolTest, ReadFromLargeBuffer) { - InMemoryUrlProtocol protocol(kData, std::numeric_limits<int64_t>::max(), - false); - - uint8_t out[sizeof(kData)]; - EXPECT_EQ(4, protocol.Read(sizeof(out), out)); - EXPECT_EQ(0, SbMemoryCompare(out, kData, sizeof(out))); -} - -TEST(InMemoryUrlProtocolTest, ReadWithNegativeSize) { - InMemoryUrlProtocol protocol(kData, sizeof(kData), false); - - uint8_t out[sizeof(kData)]; - EXPECT_EQ(AVERROR(EIO), protocol.Read(-2, out)); -} - -TEST(InMemoryUrlProtocolTest, ReadWithZeroSize) { - InMemoryUrlProtocol protocol(kData, sizeof(kData), false); - - uint8_t out; - EXPECT_EQ(0, protocol.Read(0, &out)); -} - -TEST(InMemoryUrlProtocolTest, SetPosition) { - InMemoryUrlProtocol protocol(kData, sizeof(kData), false); - - EXPECT_FALSE(protocol.SetPosition(-1)); - EXPECT_FALSE(protocol.SetPosition(sizeof(kData) + 1)); - - uint8_t out; - EXPECT_TRUE(protocol.SetPosition(sizeof(kData))); - EXPECT_EQ(0, protocol.Read(1, &out)); - - int i = sizeof(kData) / 2; - EXPECT_TRUE(protocol.SetPosition(i)); - EXPECT_EQ(1, protocol.Read(1, &out)); - EXPECT_EQ(kData[i], out); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/jpeg_parser.cc b/src/cobalt/media/filters/jpeg_parser.cc deleted file mode 100644 index 2c69ac2..0000000 --- a/src/cobalt/media/filters/jpeg_parser.cc +++ /dev/null
@@ -1,472 +0,0 @@ -// Copyright 2015 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/filters/jpeg_parser.h" - -#include "base/basictypes.h" -#include "base/big_endian.h" -#include "base/logging.h" -#include "starboard/memory.h" - -using base::BigEndianReader; - -#define READ_U8_OR_RETURN_FALSE(out) \ - do { \ - uint8_t _out; \ - if (!reader.ReadU8(&_out)) { \ - DVLOG(1) \ - << "Error in stream: unexpected EOS while trying to read " #out; \ - return false; \ - } \ - *(out) = _out; \ - } while (0) - -#define READ_U16_OR_RETURN_FALSE(out) \ - do { \ - uint16_t _out; \ - if (!reader.ReadU16(&_out)) { \ - DVLOG(1) \ - << "Error in stream: unexpected EOS while trying to read " #out; \ - return false; \ - } \ - *(out) = _out; \ - } while (0) - -namespace cobalt { -namespace media { - -static bool InRange(int value, int a, int b) { - return a <= value && value <= b; -} - -// Round up |value| to multiple of |mul|. |value| must be non-negative. -// |mul| must be positive. -static int RoundUp(int value, int mul) { - DCHECK_GE(value, 0); - DCHECK_GE(mul, 1); - return (value + mul - 1) / mul * mul; -} - -// |frame_header| is already initialized to 0 in ParseJpegPicture. -static bool ParseSOF(const char* buffer, size_t length, - JpegFrameHeader* frame_header) { - // Spec B.2.2 Frame header syntax - DCHECK(buffer); - DCHECK(frame_header); - BigEndianReader reader(buffer, length); - - uint8_t precision; - READ_U8_OR_RETURN_FALSE(&precision); - READ_U16_OR_RETURN_FALSE(&frame_header->visible_height); - READ_U16_OR_RETURN_FALSE(&frame_header->visible_width); - READ_U8_OR_RETURN_FALSE(&frame_header->num_components); - - if (precision != 8) { - DLOG(ERROR) << "Only support 8-bit precision, not " - << static_cast<int>(precision) << " bit for baseline"; - return false; - } - if (!InRange(frame_header->num_components, 1, - arraysize(frame_header->components))) { - DLOG(ERROR) << "num_components=" - << static_cast<int>(frame_header->num_components) - << " is not supported"; - return false; - } - - int max_h_factor = 0; - int max_v_factor = 0; - for (size_t i = 0; i < frame_header->num_components; i++) { - JpegComponent& component = frame_header->components[i]; - READ_U8_OR_RETURN_FALSE(&component.id); - if (component.id > frame_header->num_components) { - DLOG(ERROR) << "component id (" << static_cast<int>(component.id) - << ") should be <= num_components (" - << static_cast<int>(frame_header->num_components) << ")"; - return false; - } - uint8_t hv; - READ_U8_OR_RETURN_FALSE(&hv); - component.horizontal_sampling_factor = hv / 16; - component.vertical_sampling_factor = hv % 16; - if (component.horizontal_sampling_factor > max_h_factor) - max_h_factor = component.horizontal_sampling_factor; - if (component.vertical_sampling_factor > max_v_factor) - max_v_factor = component.vertical_sampling_factor; - if (!InRange(component.horizontal_sampling_factor, 1, 4)) { - DVLOG(1) << "Invalid horizontal sampling factor " - << static_cast<int>(component.horizontal_sampling_factor); - return false; - } - if (!InRange(component.vertical_sampling_factor, 1, 4)) { - DVLOG(1) << "Invalid vertical sampling factor " - << static_cast<int>(component.horizontal_sampling_factor); - return false; - } - READ_U8_OR_RETURN_FALSE(&component.quantization_table_selector); - } - - // The size of data unit is 8*8 and the coded size should be extended - // to complete minimum coded unit, MCU. See Spec A.2. - frame_header->coded_width = - RoundUp(frame_header->visible_width, max_h_factor * 8); - frame_header->coded_height = - RoundUp(frame_header->visible_height, max_v_factor * 8); - - return true; -} - -// |q_table| is already initialized to 0 in ParseJpegPicture. -static bool ParseDQT(const char* buffer, size_t length, - JpegQuantizationTable* q_table) { - // Spec B.2.4.1 Quantization table-specification syntax - DCHECK(buffer); - DCHECK(q_table); - BigEndianReader reader(buffer, length); - while (reader.remaining() > 0) { - uint8_t precision_and_table_id; - READ_U8_OR_RETURN_FALSE(&precision_and_table_id); - uint8_t precision = precision_and_table_id / 16; - uint8_t table_id = precision_and_table_id % 16; - if (!InRange(precision, 0, 1)) { - DVLOG(1) << "Invalid precision " << static_cast<int>(precision); - return false; - } - if (precision == 1) { // 1 means 16-bit precision - DLOG(ERROR) << "An 8-bit DCT-based process shall not use a 16-bit " - << "precision quantization table"; - return false; - } - if (table_id >= kJpegMaxQuantizationTableNum) { - DLOG(ERROR) << "Quantization table id (" << static_cast<int>(table_id) - << ") exceeded " << kJpegMaxQuantizationTableNum; - return false; - } - - if (!reader.ReadBytes(&q_table[table_id].value, - sizeof(q_table[table_id].value))) - return false; - q_table[table_id].valid = true; - } - return true; -} - -// |dc_table| and |ac_table| are already initialized to 0 in ParseJpegPicture. -static bool ParseDHT(const char* buffer, size_t length, - JpegHuffmanTable* dc_table, JpegHuffmanTable* ac_table) { - // Spec B.2.4.2 Huffman table-specification syntax - DCHECK(buffer); - DCHECK(dc_table); - DCHECK(ac_table); - BigEndianReader reader(buffer, length); - while (reader.remaining() > 0) { - uint8_t table_class_and_id; - READ_U8_OR_RETURN_FALSE(&table_class_and_id); - int table_class = table_class_and_id / 16; - int table_id = table_class_and_id % 16; - if (!InRange(table_class, 0, 1)) { - DVLOG(1) << "Invalid table class " << table_class; - return false; - } - if (table_id >= 2) { - DLOG(ERROR) << "Table id(" << table_id - << ") >= 2 is invalid for baseline profile"; - return false; - } - - JpegHuffmanTable* table; - if (table_class == 1) - table = &ac_table[table_id]; - else - table = &dc_table[table_id]; - - size_t count = 0; - if (!reader.ReadBytes(&table->code_length, sizeof(table->code_length))) - return false; - for (size_t i = 0; i < arraysize(table->code_length); i++) - count += table->code_length[i]; - - if (!InRange(count, 0, sizeof(table->code_value))) { - DVLOG(1) << "Invalid code count " << count; - return false; - } - if (!reader.ReadBytes(&table->code_value, count)) return false; - table->valid = true; - } - return true; -} - -static bool ParseDRI(const char* buffer, size_t length, - uint16_t* restart_interval) { - // Spec B.2.4.4 Restart interval definition syntax - DCHECK(buffer); - DCHECK(restart_interval); - BigEndianReader reader(buffer, length); - return reader.ReadU16(restart_interval) && reader.remaining() == 0; -} - -// |scan| is already initialized to 0 in ParseJpegPicture. -static bool ParseSOS(const char* buffer, size_t length, - const JpegFrameHeader& frame_header, - JpegScanHeader* scan) { - // Spec B.2.3 Scan header syntax - DCHECK(buffer); - DCHECK(scan); - BigEndianReader reader(buffer, length); - READ_U8_OR_RETURN_FALSE(&scan->num_components); - if (scan->num_components != frame_header.num_components) { - DLOG(ERROR) << "The number of scan components (" - << static_cast<int>(scan->num_components) - << ") mismatches the number of image components (" - << static_cast<int>(frame_header.num_components) << ")"; - return false; - } - - for (int i = 0; i < scan->num_components; i++) { - JpegScanHeader::Component* component = &scan->components[i]; - READ_U8_OR_RETURN_FALSE(&component->component_selector); - uint8_t dc_and_ac_selector; - READ_U8_OR_RETURN_FALSE(&dc_and_ac_selector); - component->dc_selector = dc_and_ac_selector / 16; - component->ac_selector = dc_and_ac_selector % 16; - if (component->component_selector != frame_header.components[i].id) { - DLOG(ERROR) << "component selector mismatches image component id"; - return false; - } - if (component->dc_selector >= kJpegMaxHuffmanTableNumBaseline) { - DLOG(ERROR) << "DC selector (" << static_cast<int>(component->dc_selector) - << ") should be 0 or 1 for baseline mode"; - return false; - } - if (component->ac_selector >= kJpegMaxHuffmanTableNumBaseline) { - DLOG(ERROR) << "AC selector (" << static_cast<int>(component->ac_selector) - << ") should be 0 or 1 for baseline mode"; - return false; - } - } - - // Unused fields, only for value checking. - uint8_t spectral_selection_start; - uint8_t spectral_selection_end; - uint8_t point_transform; - READ_U8_OR_RETURN_FALSE(&spectral_selection_start); - READ_U8_OR_RETURN_FALSE(&spectral_selection_end); - READ_U8_OR_RETURN_FALSE(&point_transform); - if (spectral_selection_start != 0 || spectral_selection_end != 63) { - DLOG(ERROR) << "Spectral selection should be 0,63 for baseline mode"; - return false; - } - if (point_transform != 0) { - DLOG(ERROR) << "Point transform should be 0 for baseline mode"; - return false; - } - - return true; -} - -// |eoi_ptr| will point to the end of image (after EOI marker) after search -// succeeds. Returns true on EOI marker found, or false. -static bool SearchEOI(const char* buffer, size_t length, const char** eoi_ptr) { - DCHECK(buffer); - DCHECK(eoi_ptr); - BigEndianReader reader(buffer, length); - uint8_t marker2; - - while (reader.remaining() > 0) { - const char* marker1_ptr = static_cast<const char*>( - SbMemoryFindByte(reader.ptr(), JPEG_MARKER_PREFIX, reader.remaining())); - if (!marker1_ptr) return false; - reader.Skip(marker1_ptr - reader.ptr() + 1); - - do { - READ_U8_OR_RETURN_FALSE(&marker2); - } while (marker2 == JPEG_MARKER_PREFIX); // skip fill bytes - - switch (marker2) { - // Compressed data escape. - case 0x00: - break; - // Restart - case JPEG_RST0: - case JPEG_RST1: - case JPEG_RST2: - case JPEG_RST3: - case JPEG_RST4: - case JPEG_RST5: - case JPEG_RST6: - case JPEG_RST7: - break; - case JPEG_EOI: - *eoi_ptr = reader.ptr(); - return true; - default: - // Skip for other markers. - uint16_t size; - READ_U16_OR_RETURN_FALSE(&size); - if (size < sizeof(size)) { - DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size - << ") is smaller than size field (" << sizeof(size) - << ")"; - return false; - } - size -= sizeof(size); - - if (!reader.Skip(size)) { - DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" - << reader.remaining() - << ") is smaller than header specified (" << size << ")"; - return false; - } - break; - } - } - return false; -} - -// |result| is already initialized to 0 in ParseJpegPicture. -static bool ParseSOI(const char* buffer, size_t length, - JpegParseResult* result) { - // Spec B.2.1 High-level syntax - DCHECK(buffer); - DCHECK(result); - BigEndianReader reader(buffer, length); - uint8_t marker1; - uint8_t marker2; - bool has_marker_dqt = false; - bool has_marker_sos = false; - - // Once reached SOS, all neccesary data are parsed. - while (!has_marker_sos) { - READ_U8_OR_RETURN_FALSE(&marker1); - if (marker1 != JPEG_MARKER_PREFIX) return false; - - do { - READ_U8_OR_RETURN_FALSE(&marker2); - } while (marker2 == JPEG_MARKER_PREFIX); // skip fill bytes - - uint16_t size; - READ_U16_OR_RETURN_FALSE(&size); - // The size includes the size field itself. - if (size < sizeof(size)) { - DLOG(ERROR) << "Ill-formed JPEG. Segment size (" << size - << ") is smaller than size field (" << sizeof(size) << ")"; - return false; - } - size -= sizeof(size); - - if (reader.remaining() < size) { - DLOG(ERROR) << "Ill-formed JPEG. Remaining size (" << reader.remaining() - << ") is smaller than header specified (" << size << ")"; - return false; - } - - switch (marker2) { - case JPEG_SOF0: - if (!ParseSOF(reader.ptr(), size, &result->frame_header)) { - DLOG(ERROR) << "ParseSOF failed"; - return false; - } - break; - case JPEG_SOF1: - case JPEG_SOF2: - case JPEG_SOF3: - case JPEG_SOF5: - case JPEG_SOF6: - case JPEG_SOF7: - case JPEG_SOF9: - case JPEG_SOF10: - case JPEG_SOF11: - case JPEG_SOF13: - case JPEG_SOF14: - case JPEG_SOF15: - DLOG(ERROR) << "Only SOF0 (baseline) is supported, but got SOF" - << (marker2 - JPEG_SOF0); - return false; - case JPEG_DQT: - if (!ParseDQT(reader.ptr(), size, result->q_table)) { - DLOG(ERROR) << "ParseDQT failed"; - return false; - } - has_marker_dqt = true; - break; - case JPEG_DHT: - if (!ParseDHT(reader.ptr(), size, result->dc_table, result->ac_table)) { - DLOG(ERROR) << "ParseDHT failed"; - return false; - } - break; - case JPEG_DRI: - if (!ParseDRI(reader.ptr(), size, &result->restart_interval)) { - DLOG(ERROR) << "ParseDRI failed"; - return false; - } - break; - case JPEG_SOS: - if (!ParseSOS(reader.ptr(), size, result->frame_header, - &result->scan)) { - DLOG(ERROR) << "ParseSOS failed"; - return false; - } - has_marker_sos = true; - break; - default: - DVLOG(4) << "unknown marker " << static_cast<int>(marker2); - break; - } - reader.Skip(size); - } - - if (!has_marker_dqt) { - DLOG(ERROR) << "No DQT marker found"; - return false; - } - - // Scan data follows scan header immediately. - result->data = reader.ptr(); - result->data_size = reader.remaining(); - const size_t kSoiSize = 2; - result->image_size = length + kSoiSize; - - return true; -} - -bool ParseJpegPicture(const uint8_t* buffer, size_t length, - JpegParseResult* result) { - DCHECK(buffer); - DCHECK(result); - BigEndianReader reader(reinterpret_cast<const char*>(buffer), length); - SbMemorySet(result, 0, sizeof(JpegParseResult)); - - uint8_t marker1, marker2; - READ_U8_OR_RETURN_FALSE(&marker1); - READ_U8_OR_RETURN_FALSE(&marker2); - if (marker1 != JPEG_MARKER_PREFIX || marker2 != JPEG_SOI) { - DLOG(ERROR) << "Not a JPEG"; - return false; - } - - return ParseSOI(reader.ptr(), reader.remaining(), result); -} - -bool ParseJpegStream(const uint8_t* buffer, size_t length, - JpegParseResult* result) { - DCHECK(buffer); - DCHECK(result); - if (!ParseJpegPicture(buffer, length, result)) return false; - - BigEndianReader reader(reinterpret_cast<const char*>(result->data), - result->data_size); - const char* eoi_ptr = NULL; - if (!SearchEOI(reader.ptr(), reader.remaining(), &eoi_ptr)) { - DLOG(ERROR) << "SearchEOI failed"; - return false; - } - DCHECK(eoi_ptr); - result->data_size = eoi_ptr - result->data; - result->image_size = eoi_ptr - reinterpret_cast<const char*>(buffer); - return true; -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/jpeg_parser.h b/src/cobalt/media/filters/jpeg_parser.h deleted file mode 100644 index ecf8e93..0000000 --- a/src/cobalt/media/filters/jpeg_parser.h +++ /dev/null
@@ -1,124 +0,0 @@ -// Copyright 2015 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. -// -#ifndef COBALT_MEDIA_FILTERS_JPEG_PARSER_H_ -#define COBALT_MEDIA_FILTERS_JPEG_PARSER_H_ - -#include "cobalt/media/base/media_export.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -// It's not a full featured JPEG parser implememtation. It only parses JPEG -// baseline sequential process. For explanations of each struct and its -// members, see JPEG specification at -// http://www.w3.org/Graphics/JPEG/itu-t81.pdf. - -enum JpegMarker { - JPEG_SOF0 = 0xC0, // start of frame (baseline) - JPEG_SOF1 = 0xC1, // start of frame (extended sequential) - JPEG_SOF2 = 0xC2, // start of frame (progressive) - JPEG_SOF3 = 0xC3, // start of frame (lossless)) - JPEG_DHT = 0xC4, // define huffman table - JPEG_SOF5 = 0xC5, // start of frame (differential, sequential) - JPEG_SOF6 = 0xC6, // start of frame (differential, progressive) - JPEG_SOF7 = 0xC7, // start of frame (differential, lossless) - JPEG_SOF9 = 0xC9, // start of frame (arithmetic coding, extended) - JPEG_SOF10 = 0xCA, // start of frame (arithmetic coding, progressive) - JPEG_SOF11 = 0xCB, // start of frame (arithmetic coding, lossless) - JPEG_SOF13 = 0xCD, // start of frame (differential, arithmetic, sequential) - JPEG_SOF14 = 0xCE, // start of frame (differential, arithmetic, progressive) - JPEG_SOF15 = 0xCF, // start of frame (differential, arithmetic, lossless) - JPEG_RST0 = 0xD0, // restart - JPEG_RST1 = 0xD1, // restart - JPEG_RST2 = 0xD2, // restart - JPEG_RST3 = 0xD3, // restart - JPEG_RST4 = 0xD4, // restart - JPEG_RST5 = 0xD5, // restart - JPEG_RST6 = 0xD6, // restart - JPEG_RST7 = 0xD7, // restart - JPEG_SOI = 0xD8, // start of image - JPEG_EOI = 0xD9, // end of image - JPEG_SOS = 0xDA, // start of scan - JPEG_DQT = 0xDB, // define quantization table - JPEG_DRI = 0xDD, // define restart internal - JPEG_MARKER_PREFIX = 0xFF, // jpeg marker prefix -}; - -const size_t kJpegMaxHuffmanTableNumBaseline = 2; -const size_t kJpegMaxComponents = 4; -const size_t kJpegMaxQuantizationTableNum = 4; - -// Parsing result of JPEG DHT marker. -struct JpegHuffmanTable { - bool valid; - uint8_t code_length[16]; - uint8_t code_value[256]; -}; - -// Parsing result of JPEG DQT marker. -struct JpegQuantizationTable { - bool valid; - uint8_t value[64]; // baseline only supports 8 bits quantization table -}; - -// Parsing result of a JPEG component. -struct JpegComponent { - uint8_t id; - uint8_t horizontal_sampling_factor; - uint8_t vertical_sampling_factor; - uint8_t quantization_table_selector; -}; - -// Parsing result of a JPEG SOF marker. -struct JpegFrameHeader { - uint16_t visible_width; - uint16_t visible_height; - uint16_t coded_width; - uint16_t coded_height; - uint8_t num_components; - JpegComponent components[kJpegMaxComponents]; -}; - -// Parsing result of JPEG SOS marker. -struct JpegScanHeader { - uint8_t num_components; - struct Component { - uint8_t component_selector; - uint8_t dc_selector; - uint8_t ac_selector; - } components[kJpegMaxComponents]; -}; - -struct JpegParseResult { - JpegFrameHeader frame_header; - JpegHuffmanTable dc_table[kJpegMaxHuffmanTableNumBaseline]; - JpegHuffmanTable ac_table[kJpegMaxHuffmanTableNumBaseline]; - JpegQuantizationTable q_table[kJpegMaxQuantizationTableNum]; - uint16_t restart_interval; - JpegScanHeader scan; - const char* data; - // The size of compressed data of the first image. - size_t data_size; - // The size of the first entire image including header. - size_t image_size; -}; - -// Parses JPEG picture in |buffer| with |length|. Returns true iff header is -// valid and JPEG baseline sequential process is present. If parsed -// successfully, |result| is the parsed result. -MEDIA_EXPORT bool ParseJpegPicture(const uint8_t* buffer, size_t length, - JpegParseResult* result); - -// Parses the first image of JPEG stream in |buffer| with |length|. Returns -// true iff header is valid and JPEG baseline sequential process is present. -// If parsed successfully, |result| is the parsed result. -MEDIA_EXPORT bool ParseJpegStream(const uint8_t* buffer, size_t length, - JpegParseResult* result); - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_JPEG_PARSER_H_
diff --git a/src/cobalt/media/filters/jpeg_parser_unittest.cc b/src/cobalt/media/filters/jpeg_parser_unittest.cc deleted file mode 100644 index 7b6d076..0000000 --- a/src/cobalt/media/filters/jpeg_parser_unittest.cc +++ /dev/null
@@ -1,116 +0,0 @@ -// Copyright 2015 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/at_exit.h" -#include "base/files/memory_mapped_file.h" -#include "base/path_service.h" -#include "cobalt/media/base/test_data_util.h" -#include "cobalt/media/filters/jpeg_parser.h" -#include "starboard/types.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cobalt { -namespace media { - -TEST(JpegParserTest, Parsing) { - base::FilePath data_dir; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); - - // This sample frame is captured from Chromebook Pixel - base::FilePath file_path = data_dir.AppendASCII("media") - .AppendASCII("test") - .AppendASCII("data") - .AppendASCII("pixel-1280x720.jpg"); - - base::MemoryMappedFile stream; - ASSERT_TRUE(stream.Initialize(file_path)) << "Couldn't open stream file: " - << file_path.MaybeAsASCII(); - - JpegParseResult result; - ASSERT_TRUE(ParseJpegPicture(stream.data(), stream.length(), &result)); - - // Verify selected fields - - // SOF fields - EXPECT_EQ(1280, result.frame_header.visible_width); - EXPECT_EQ(720, result.frame_header.visible_height); - EXPECT_EQ(1280, result.frame_header.coded_width); - EXPECT_EQ(720, result.frame_header.coded_height); - EXPECT_EQ(3, result.frame_header.num_components); - EXPECT_EQ(1, result.frame_header.components[0].id); - EXPECT_EQ(2, result.frame_header.components[0].horizontal_sampling_factor); - EXPECT_EQ(1, result.frame_header.components[0].vertical_sampling_factor); - EXPECT_EQ(0, result.frame_header.components[0].quantization_table_selector); - EXPECT_EQ(2, result.frame_header.components[1].id); - EXPECT_EQ(1, result.frame_header.components[1].horizontal_sampling_factor); - EXPECT_EQ(1, result.frame_header.components[1].vertical_sampling_factor); - EXPECT_EQ(1, result.frame_header.components[1].quantization_table_selector); - EXPECT_EQ(3, result.frame_header.components[2].id); - EXPECT_EQ(1, result.frame_header.components[2].horizontal_sampling_factor); - EXPECT_EQ(1, result.frame_header.components[2].vertical_sampling_factor); - EXPECT_EQ(1, result.frame_header.components[2].quantization_table_selector); - - // DRI fields - EXPECT_EQ(0, result.restart_interval); - - // DQT fields - EXPECT_TRUE(result.q_table[0].valid); - EXPECT_TRUE(result.q_table[1].valid); - EXPECT_FALSE(result.q_table[2].valid); - EXPECT_FALSE(result.q_table[3].valid); - - // DHT fields (no DHT marker) - EXPECT_FALSE(result.dc_table[0].valid); - EXPECT_FALSE(result.ac_table[0].valid); - EXPECT_FALSE(result.dc_table[1].valid); - EXPECT_FALSE(result.ac_table[1].valid); - - // SOS fields - EXPECT_EQ(3, result.scan.num_components); - EXPECT_EQ(1, result.scan.components[0].component_selector); - EXPECT_EQ(0, result.scan.components[0].dc_selector); - EXPECT_EQ(0, result.scan.components[0].ac_selector); - EXPECT_EQ(2, result.scan.components[1].component_selector); - EXPECT_EQ(1, result.scan.components[1].dc_selector); - EXPECT_EQ(1, result.scan.components[1].ac_selector); - EXPECT_EQ(3, result.scan.components[2].component_selector); - EXPECT_EQ(1, result.scan.components[2].dc_selector); - EXPECT_EQ(1, result.scan.components[2].ac_selector); - EXPECT_EQ(121150u, result.data_size); - EXPECT_EQ(121358u, result.image_size); -} - -TEST(JpegParserTest, CodedSizeNotEqualVisibleSize) { - base::FilePath data_dir; - ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); - - base::FilePath file_path = data_dir.AppendASCII("media") - .AppendASCII("test") - .AppendASCII("data") - .AppendASCII("blank-1x1.jpg"); - - base::MemoryMappedFile stream; - ASSERT_TRUE(stream.Initialize(file_path)) << "Couldn't open stream file: " - << file_path.MaybeAsASCII(); - - JpegParseResult result; - ASSERT_TRUE(ParseJpegPicture(stream.data(), stream.length(), &result)); - - EXPECT_EQ(1, result.frame_header.visible_width); - EXPECT_EQ(1, result.frame_header.visible_height); - // The sampling factor of the given image is 2:2, so coded size is 16x16 - EXPECT_EQ(16, result.frame_header.coded_width); - EXPECT_EQ(16, result.frame_header.coded_height); - EXPECT_EQ(2, result.frame_header.components[0].horizontal_sampling_factor); - EXPECT_EQ(2, result.frame_header.components[0].vertical_sampling_factor); -} - -TEST(JpegParserTest, ParsingFail) { - const uint8_t data[] = {0, 1, 2, 3}; // not jpeg - JpegParseResult result; - ASSERT_FALSE(ParseJpegPicture(data, sizeof(data), &result)); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/memory_data_source.cc b/src/cobalt/media/filters/memory_data_source.cc deleted file mode 100644 index 08a90fe..0000000 --- a/src/cobalt/media/filters/memory_data_source.cc +++ /dev/null
@@ -1,56 +0,0 @@ -// 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 "cobalt/media/filters/memory_data_source.h" - -#include <algorithm> - -#include "base/logging.h" -#include "starboard/memory.h" - -namespace cobalt { -namespace media { - -MemoryDataSource::MemoryDataSource(const uint8_t* data, size_t size) - : data_(data), size_(size) {} - -MemoryDataSource::~MemoryDataSource() {} - -void MemoryDataSource::Read(int64_t position, int size, uint8_t* data, - const DataSource::ReadCB& read_cb) { - DCHECK(!read_cb.is_null()); - - if (is_stopped_ || size < 0 || position < 0 || - static_cast<size_t>(position) > size_) { - read_cb.Run(kReadError); - return; - } - - // Cap size within bounds. - size_t clamped_size = std::min(static_cast<size_t>(size), - size_ - static_cast<size_t>(position)); - - if (clamped_size > 0) { - DCHECK(data); - SbMemoryCopy(data, data_ + position, clamped_size); - } - - read_cb.Run(clamped_size); -} - -void MemoryDataSource::Stop() { is_stopped_ = true; } - -void MemoryDataSource::Abort() {} - -bool MemoryDataSource::GetSize(int64_t* size_out) { - *size_out = size_; - return true; -} - -bool MemoryDataSource::IsStreaming() { return false; } - -void MemoryDataSource::SetBitrate(int bitrate) {} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/memory_data_source.h b/src/cobalt/media/filters/memory_data_source.h deleted file mode 100644 index 73bd5d2..0000000 --- a/src/cobalt/media/filters/memory_data_source.h +++ /dev/null
@@ -1,45 +0,0 @@ -// 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. - -#ifndef COBALT_MEDIA_FILTERS_MEMORY_DATA_SOURCE_H_ -#define COBALT_MEDIA_FILTERS_MEMORY_DATA_SOURCE_H_ - -#include "base/basictypes.h" -#include "cobalt/media/base/data_source.h" -#include "starboard/types.h" - -namespace cobalt { -namespace media { - -// Basic data source that treats the URL as a file path, and uses the file -// system to read data for a media pipeline. -class MEDIA_EXPORT MemoryDataSource : public DataSource { - public: - // Construct MemoryDataSource with |data| and |size|. The data is guaranteed - // to be valid during the lifetime of MemoryDataSource. - MemoryDataSource(const uint8_t* data, size_t size); - ~MemoryDataSource() final; - - // Implementation of DataSource. - void Read(int64_t position, int size, uint8_t* data, - const DataSource::ReadCB& read_cb) final; - void Stop() final; - void Abort() final; - bool GetSize(int64_t* size_out) final; - bool IsStreaming() final; - void SetBitrate(int bitrate) final; - - private: - const uint8_t* data_ = NULL; - size_t size_ = 0; - - bool is_stopped_ = false; - - DISALLOW_COPY_AND_ASSIGN(MemoryDataSource); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_MEMORY_DATA_SOURCE_H_
diff --git a/src/cobalt/media/filters/memory_data_source_unittest.cc b/src/cobalt/media/filters/memory_data_source_unittest.cc deleted file mode 100644 index f8bfeb4..0000000 --- a/src/cobalt/media/filters/memory_data_source_unittest.cc +++ /dev/null
@@ -1,115 +0,0 @@ -// 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 "cobalt/media/filters/memory_data_source.h" - -#include <memory> -#include <vector> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/rand_util.h" -#include "starboard/memory.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cobalt { -namespace media { - -class MemoryDataSourceTest : public ::testing::Test { - public: - MemoryDataSourceTest() {} - - protected: - void Initialize(size_t size) { - data_.assign(size, 0); - base::RandBytes(data_.data(), size); - memory_data_source_.reset(new MemoryDataSource(data_.data(), size)); - EXPECT_EQ(size, GetSize()); - } - - // Reads |size| bytes starting from |position|, expects |expected_read_size| - // bytes to be read and checks the read data. Expects error when - // |expected_read_size| is DataSource::kReadError. - void ReadAndExpect(int64_t position, int size, int expected_read_size) { - std::vector<uint8_t> data(size < 0 ? 0 : size, 0); - - EXPECT_CALL(*this, ReadCB(expected_read_size)); - memory_data_source_->Read( - position, size, data.data(), - base::Bind(&MemoryDataSourceTest::ReadCB, base::Unretained(this))); - - if (expected_read_size != DataSource::kReadError) { - EXPECT_EQ(0, SbMemoryCompare(data_.data() + position, data.data(), - expected_read_size)); - } - } - - size_t GetSize() { - int64_t size = 0; - EXPECT_TRUE(memory_data_source_->GetSize(&size)); - EXPECT_GE(size, 0); - return size; - } - - void Stop() { memory_data_source_->Stop(); } - - MOCK_METHOD1(ReadCB, void(int size)); - - private: - std::vector<uint8_t> data_; - std::unique_ptr<MemoryDataSource> memory_data_source_; - - DISALLOW_COPY_AND_ASSIGN(MemoryDataSourceTest); -}; - -TEST_F(MemoryDataSourceTest, EmptySource) { - Initialize(0); - ReadAndExpect(0, 0, 0); -} - -TEST_F(MemoryDataSourceTest, ReadData) { - Initialize(128); - ReadAndExpect(0, 0, 0); - ReadAndExpect(0, 128, 128); - ReadAndExpect(12, 64, 64); - ReadAndExpect(128, 0, 0); -} - -TEST_F(MemoryDataSourceTest, ReadData_InvalidPosition) { - Initialize(128); - ReadAndExpect(-7, 64, DataSource::kReadError); - ReadAndExpect(129, 64, DataSource::kReadError); -} - -TEST_F(MemoryDataSourceTest, ReadData_InvalidSize) { - Initialize(128); - ReadAndExpect(0, -12, DataSource::kReadError); -} - -TEST_F(MemoryDataSourceTest, ReadData_PartialRead) { - Initialize(128); - ReadAndExpect(0, 129, 128); - ReadAndExpect(96, 100, 32); -} - -// All ReadData() will fail after Stop(). -TEST_F(MemoryDataSourceTest, Stop) { - Initialize(128); - ReadAndExpect(12, 64, 64); - Stop(); - ReadAndExpect(12, 64, DataSource::kReadError); -} - -// ReadData() doesn't affect GetSize(). -TEST_F(MemoryDataSourceTest, GetSize) { - Initialize(128); - ReadAndExpect(12, 64, 64); - EXPECT_EQ(128u, GetSize()); - ReadAndExpect(-7, 64, DataSource::kReadError); - EXPECT_EQ(128u, GetSize()); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/pipeline_controller.cc b/src/cobalt/media/filters/pipeline_controller.cc deleted file mode 100644 index 1a47ed6..0000000 --- a/src/cobalt/media/filters/pipeline_controller.cc +++ /dev/null
@@ -1,233 +0,0 @@ -// 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 "cobalt/media/filters/pipeline_controller.h" - -#include "base/bind.h" -#include "cobalt/media/base/demuxer.h" - -namespace cobalt { -namespace media { - -PipelineController::PipelineController( - Pipeline* pipeline, const RendererFactoryCB& renderer_factory_cb, - const SeekedCB& seeked_cb, const SuspendedCB& suspended_cb, - const PipelineStatusCB& error_cb) - : pipeline_(pipeline), - renderer_factory_cb_(renderer_factory_cb), - seeked_cb_(seeked_cb), - suspended_cb_(suspended_cb), - error_cb_(error_cb), - weak_factory_(this) { - DCHECK(pipeline_); - DCHECK(!renderer_factory_cb_.is_null()); - DCHECK(!seeked_cb_.is_null()); - DCHECK(!suspended_cb_.is_null()); - DCHECK(!error_cb_.is_null()); -} - -PipelineController::~PipelineController() { - DCHECK(thread_checker_.CalledOnValidThread()); -} - -// TODO(sandersd): If there is a pending suspend, don't call pipeline_.Start() -// until Resume(). -void PipelineController::Start(Demuxer* demuxer, Pipeline::Client* client, - bool is_streaming, bool is_static) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(state_ == State::CREATED); - DCHECK(demuxer); - - // Once the pipeline is started, we want to call the seeked callback but - // without a time update. - pending_seeked_cb_ = true; - state_ = State::STARTING; - - demuxer_ = demuxer; - is_streaming_ = is_streaming; - is_static_ = is_static; - pipeline_->Start(demuxer, renderer_factory_cb_.Run(), client, - base::Bind(&PipelineController::OnPipelineStatus, - weak_factory_.GetWeakPtr(), State::PLAYING)); -} - -void PipelineController::Seek(base::TimeDelta time, bool time_updated) { - DCHECK(thread_checker_.CalledOnValidThread()); - - // It would be slightly more clear to set this in Dispatch(), but we want to - // be sure it gets updated even if the seek is elided. - if (time_updated) pending_time_updated_ = true; - pending_seeked_cb_ = true; - - // If we are already seeking to |time|, and the media is static, elide the - // seek. - if ((state_ == State::SEEKING || state_ == State::RESUMING) && - seek_time_ == time && is_static_) { - pending_seek_ = false; - return; - } - - pending_seek_time_ = time; - pending_seek_ = true; - Dispatch(); -} - -// TODO(sandersd): It may be easier to use this interface if |suspended_cb_| is -// executed when Suspend() is called while already suspended. -void PipelineController::Suspend() { - DCHECK(thread_checker_.CalledOnValidThread()); - pending_resume_ = false; - if (state_ != State::SUSPENDING && state_ != State::SUSPENDED) { - pending_suspend_ = true; - Dispatch(); - } -} - -void PipelineController::Resume() { - DCHECK(thread_checker_.CalledOnValidThread()); - pending_suspend_ = false; - if (state_ == State::SUSPENDING || state_ == State::SUSPENDED) { - pending_resume_ = true; - Dispatch(); - } -} - -bool PipelineController::IsStable() { - DCHECK(thread_checker_.CalledOnValidThread()); - return state_ == State::PLAYING; -} - -bool PipelineController::IsSuspended() { - DCHECK(thread_checker_.CalledOnValidThread()); - return (pending_suspend_ || state_ == State::SUSPENDING || - state_ == State::SUSPENDED) && - !pending_resume_; -} - -bool PipelineController::IsPipelineSuspended() { - DCHECK(thread_checker_.CalledOnValidThread()); - return state_ == State::SUSPENDED; -} - -void PipelineController::OnPipelineStatus(State state, - PipelineStatus pipeline_status) { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (pipeline_status != PIPELINE_OK) { - error_cb_.Run(pipeline_status); - return; - } - - state_ = state; - - if (state == State::PLAYING) { - // Start(), Seek(), or Resume() completed; we can be sure that - // |demuxer_| got the seek it was waiting for. - waiting_for_seek_ = false; - } else if (state == State::SUSPENDED) { - // Warning: possibly reentrant. The state may change inside this callback. - // It must be safe to call Dispatch() twice in a row here. - suspended_cb_.Run(); - } - - Dispatch(); -} - -// Note: Dispatch() may be called re-entrantly (by callbacks internally) or -// twice in a row (by OnPipelineStatus()). -void PipelineController::Dispatch() { - DCHECK(thread_checker_.CalledOnValidThread()); - - // Suspend/resume transitions take priority because seeks before a suspend - // are wasted, and seeks after can be merged into the resume operation. - if (pending_suspend_ && state_ == State::PLAYING) { - pending_suspend_ = false; - state_ = State::SUSPENDING; - pipeline_->Suspend(base::Bind(&PipelineController::OnPipelineStatus, - weak_factory_.GetWeakPtr(), - State::SUSPENDED)); - return; - } - - if (pending_resume_ && state_ == State::SUSPENDED) { - // If there is a pending seek, resume to that time instead... - if (pending_seek_) { - seek_time_ = pending_seek_time_; - pending_seek_ = false; - } else { - seek_time_ = pipeline_->GetMediaTime(); - } - - // ...unless the media is streaming, in which case we resume at the start - // because seeking doesn't work well. - if (is_streaming_ && !seek_time_.is_zero()) { - seek_time_ = base::TimeDelta(); - - // In this case we want to make sure that the controls get updated - // immediately, so we don't try to hide the seek. - pending_time_updated_ = true; - } - - // Tell |demuxer_| to expect our resume. - DCHECK(!waiting_for_seek_); - waiting_for_seek_ = true; - demuxer_->StartWaitingForSeek(seek_time_); - - pending_resume_ = false; - state_ = State::RESUMING; - pipeline_->Resume(renderer_factory_cb_.Run(), seek_time_, - base::Bind(&PipelineController::OnPipelineStatus, - weak_factory_.GetWeakPtr(), State::PLAYING)); - return; - } - - // If we have pending operations, and a seek is ongoing, abort it. - if ((pending_seek_ || pending_suspend_) && waiting_for_seek_) { - // If there is no pending seek, return the current seek to pending status. - if (!pending_seek_) { - pending_seek_time_ = seek_time_; - pending_seek_ = true; - } - - // CancelPendingSeek() may be reentrant, so update state first and return - // immediately. - waiting_for_seek_ = false; - demuxer_->CancelPendingSeek(pending_seek_time_); - return; - } - - // Ordinary seeking. - if (pending_seek_ && state_ == State::PLAYING) { - seek_time_ = pending_seek_time_; - - // Tell |demuxer_| to expect our seek. - DCHECK(!waiting_for_seek_); - waiting_for_seek_ = true; - demuxer_->StartWaitingForSeek(seek_time_); - - pending_seek_ = false; - state_ = State::SEEKING; - pipeline_->Seek(seek_time_, - base::Bind(&PipelineController::OnPipelineStatus, - weak_factory_.GetWeakPtr(), State::PLAYING)); - return; - } - - // If |state_| is PLAYING and we didn't trigger an operation above then we - // are in a stable state. If there is a seeked callback pending, emit it. - if (state_ == State::PLAYING) { - if (pending_seeked_cb_) { - // |seeked_cb_| may be reentrant, so update state first and return - // immediately. - pending_seeked_cb_ = false; - bool was_pending_time_updated = pending_time_updated_; - pending_time_updated_ = false; - seeked_cb_.Run(was_pending_time_updated); - return; - } - } -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/pipeline_controller.h b/src/cobalt/media/filters/pipeline_controller.h deleted file mode 100644 index 4c6cc43..0000000 --- a/src/cobalt/media/filters/pipeline_controller.h +++ /dev/null
@@ -1,168 +0,0 @@ -// 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. - -#ifndef COBALT_MEDIA_FILTERS_PIPELINE_CONTROLLER_H_ -#define COBALT_MEDIA_FILTERS_PIPELINE_CONTROLLER_H_ - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/memory/weak_ptr.h" -#include "base/threading/thread_checker.h" -#include "base/time.h" -#include "cobalt/media/base/media_export.h" -#include "cobalt/media/base/pipeline.h" -#include "cobalt/media/base/renderer.h" - -namespace cobalt { -namespace media { - -class Demuxer; - -// PipelineController wraps a Pipeline to expose the one-at-a-time operations -// (Seek(), Suspend(), and Resume()) with a simpler API. Internally it tracks -// pending operations and dispatches them when possible. Duplicate requests -// (such as seeking twice to the same time) may be elided. -// -// TODO(sandersd): -// - Expose an operation that restarts via suspend+resume. -// - Block invalid calls after an error occurs. -class MEDIA_EXPORT PipelineController { - public: - enum class State { - CREATED, - STARTING, - PLAYING, - SEEKING, - SUSPENDING, - SUSPENDED, - RESUMING, - }; - - using RendererFactoryCB = base::Callback<std::unique_ptr<Renderer>(void)>; - using SeekedCB = base::Callback<void(bool time_updated)>; - using SuspendedCB = base::Callback<void()>; - - // Construct a PipelineController wrapping |pipeline_|. |pipeline_| must - // outlive the resulting PipelineController. The callbacks are: - // - |renderer_factory_cb| is called by PipelineController to create new - // renderers when starting and resuming. - // - |seeked_cb| is called upon reaching a stable state if a seek occured. - // - |suspended_cb| is called immediately after suspendeding. - // - |error_cb| is called if any operation on |pipeline_| does not result - // in PIPELINE_OK or its error callback is called. - PipelineController(Pipeline* pipeline, - const RendererFactoryCB& renderer_factory_cb, - const SeekedCB& seeked_cb, const SuspendedCB& suspended_cb, - const PipelineStatusCB& error_cb); - ~PipelineController(); - - // Start |pipeline_|. |demuxer| will be retained and StartWaitingForSeek()/ - // CancelPendingSeek() will be issued to it as necessary. - // - // When |is_streaming| is true, Resume() will always start at the - // beginning of the stream, rather than attempting to seek to the current - // time. - // - // When |is_static| is true, seeks to the current time may be elided. - // Otherwise it is assumed that the media data may have changed. - // - // The remaining parameters are just passed directly to pipeline_.Start(). - void Start(Demuxer* demuxer, Pipeline::Client* client, bool is_streaming, - bool is_static); - - // Request a seek to |time|. If |time_updated| is true, then the eventual - // |seeked_cb| callback will also have |time_updated| set to true; it - // indicates that the seek was requested by Blink and a time update is - // expected so that Blink can fire the seeked event. - void Seek(base::TimeDelta time, bool time_updated); - - // Request that |pipeline_| be suspended. This is a no-op if |pipeline_| has - // been suspended. - void Suspend(); - - // Request that |pipeline_| be resumed. This is a no-op if |pipeline_| has not - // been suspended. - void Resume(); - - // Returns true if the current state is stable. This means that |state_| is - // PLAYING and there are no pending operations. Requests are processed - // immediately when the state is stable, otherwise they are queued. - // - // Exceptions to the above: - // - Start() is processed immediately while in the CREATED state. - // - Resume() is processed immediately while in the SUSPENDED state. - bool IsStable(); - - // Returns true if the current target state is suspended. - bool IsSuspended(); - - // Returns true if |pipeline_| is suspended. - bool IsPipelineSuspended(); - - private: - // Attempts to make progress from the current state to the target state. - void Dispatch(); - - // PipelineStaus callback that also carries the target state. - void OnPipelineStatus(State state, PipelineStatus pipeline_status); - - // The Pipeline we are managing state for. - Pipeline* pipeline_ = NULL; - - // Factory for Renderers, used for Start() and Resume(). - RendererFactoryCB renderer_factory_cb_; - - // Called after seeks (which includes Start()) upon reaching a stable state. - // Multiple seeks result in only one callback if no stable state occurs - // between them. - SeekedCB seeked_cb_; - - // Called immediately when |pipeline_| completes a suspend operation. - SuspendedCB suspended_cb_; - - // Called immediately when any operation on |pipeline_| results in an error. - PipelineStatusCB error_cb_; - - // State for handling StartWaitingForSeek()/CancelPendingSeek(). - Demuxer* demuxer_ = NULL; - bool waiting_for_seek_ = false; - - // When true, Resume() will start at time zero instead of seeking to the - // current time. - bool is_streaming_ = false; - - // When true, seeking to the current time may be elided. - bool is_static_ = true; - - // Tracks the current state of |pipeline_|. - State state_ = State::CREATED; - - // Indicates that a seek has occurred. When set, a seeked callback will be - // issued at the next stable state. - bool pending_seeked_cb_ = false; - - // Indicates that time has been changed by a seek, which will be reported at - // the next seeked callback. - bool pending_time_updated_ = false; - - // The target time of the active seek; valid while SEEKING or RESUMING. - base::TimeDelta seek_time_; - - // Target state which we will work to achieve. |pending_seek_time_| is only - // valid when |pending_seek_| is true. - bool pending_seek_ = false; - base::TimeDelta pending_seek_time_; - bool pending_suspend_ = false; - bool pending_resume_ = false; - - base::ThreadChecker thread_checker_; - base::WeakPtrFactory<PipelineController> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(PipelineController); -}; - -} // namespace media -} // namespace cobalt - -#endif // COBALT_MEDIA_FILTERS_PIPELINE_CONTROLLER_H_
diff --git a/src/cobalt/media/filters/pipeline_controller_unittest.cc b/src/cobalt/media/filters/pipeline_controller_unittest.cc deleted file mode 100644 index 94e6638..0000000 --- a/src/cobalt/media/filters/pipeline_controller_unittest.cc +++ /dev/null
@@ -1,330 +0,0 @@ -// 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 "cobalt/media/filters/pipeline_controller.h" - -#include <memory> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop.h" -#include "base/run_loop.h" -#include "base/time.h" -#include "cobalt/media/base/mock_filters.h" -#include "cobalt/media/base/pipeline.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::DoAll; -using ::testing::Mock; -using ::testing::NiceMock; -using ::testing::Return; -using ::testing::SaveArg; -using ::testing::StrictMock; - -namespace cobalt { -namespace media { - -class PipelineControllerTest : public ::testing::Test, public Pipeline::Client { - public: - PipelineControllerTest() - : pipeline_controller_(&pipeline_, - base::Bind(&PipelineControllerTest::CreateRenderer, - base::Unretained(this)), - base::Bind(&PipelineControllerTest::OnSeeked, - base::Unretained(this)), - base::Bind(&PipelineControllerTest::OnSuspended, - base::Unretained(this)), - base::Bind(&PipelineControllerTest::OnError, - base::Unretained(this))) {} - - ~PipelineControllerTest() override {} - - PipelineStatusCB StartPipeline(bool is_streaming, bool is_static) { - EXPECT_FALSE(pipeline_controller_.IsStable()); - PipelineStatusCB start_cb; - EXPECT_CALL(pipeline_, Start(_, _, _, _)).WillOnce(SaveArg<3>(&start_cb)); - pipeline_controller_.Start(&demuxer_, this, is_streaming, is_static); - Mock::VerifyAndClear(&pipeline_); - EXPECT_FALSE(pipeline_controller_.IsStable()); - return start_cb; - } - - PipelineStatusCB StartPipeline() { return StartPipeline(false, true); } - - PipelineStatusCB StartPipeline_WithDynamicData() { - return StartPipeline(false, false); - } - - PipelineStatusCB StartPipeline_WithStreamingData() { - return StartPipeline(true, false); - } - - PipelineStatusCB SeekPipeline(base::TimeDelta time) { - EXPECT_TRUE(pipeline_controller_.IsStable()); - PipelineStatusCB seek_cb; - EXPECT_CALL(pipeline_, Seek(time, _)).WillOnce(SaveArg<1>(&seek_cb)); - pipeline_controller_.Seek(time, true); - Mock::VerifyAndClear(&pipeline_); - EXPECT_FALSE(pipeline_controller_.IsStable()); - return seek_cb; - } - - PipelineStatusCB SuspendPipeline() { - EXPECT_TRUE(pipeline_controller_.IsStable()); - PipelineStatusCB suspend_cb; - EXPECT_CALL(pipeline_, Suspend(_)).WillOnce(SaveArg<0>(&suspend_cb)); - pipeline_controller_.Suspend(); - Mock::VerifyAndClear(&pipeline_); - EXPECT_TRUE(pipeline_controller_.IsSuspended()); - EXPECT_FALSE(pipeline_controller_.IsStable()); - EXPECT_FALSE(pipeline_controller_.IsPipelineSuspended()); - return suspend_cb; - } - - PipelineStatusCB ResumePipeline() { - EXPECT_TRUE(pipeline_controller_.IsPipelineSuspended()); - PipelineStatusCB resume_cb; - EXPECT_CALL(pipeline_, Resume(_, _, _)) - .WillOnce( - DoAll(SaveArg<1>(&last_resume_time_), SaveArg<2>(&resume_cb))); - EXPECT_CALL(pipeline_, GetMediaTime()) - .WillRepeatedly(Return(base::TimeDelta())); - pipeline_controller_.Resume(); - Mock::VerifyAndClear(&pipeline_); - EXPECT_FALSE(pipeline_controller_.IsSuspended()); - EXPECT_FALSE(pipeline_controller_.IsStable()); - EXPECT_FALSE(pipeline_controller_.IsPipelineSuspended()); - return resume_cb; - } - - void Complete(const PipelineStatusCB& cb) { - cb.Run(PIPELINE_OK); - base::RunLoop().RunUntilIdle(); - } - - protected: - std::unique_ptr<Renderer> CreateRenderer() { - return std::unique_ptr<Renderer>(); - } - - void OnSeeked(bool time_updated) { - was_seeked_ = true; - last_seeked_time_updated_ = time_updated; - } - - void OnSuspended() { was_suspended_ = true; } - - // Pipeline::Client overrides - void OnError(PipelineStatus status) override { NOTREACHED(); } - void OnEnded() override {} - void OnMetadata(PipelineMetadata metadata) override {} - void OnBufferingStateChange(BufferingState state) override {} - void OnDurationChange() override {} - void OnAddTextTrack(const TextTrackConfig& config, - const AddTextTrackDoneCB& done_cb) override {} - void OnWaitingForDecryptionKey() override {} - void OnVideoNaturalSizeChange(const gfx::Size& size) override {} - void OnVideoOpacityChange(bool opaque) override {} - - base::MessageLoop message_loop_; - - NiceMock<MockDemuxer> demuxer_; - StrictMock<MockPipeline> pipeline_; - PipelineController pipeline_controller_; - - bool was_seeked_ = false; - bool last_seeked_time_updated_ = false; - bool was_suspended_ = false; - base::TimeDelta last_resume_time_; - - DISALLOW_COPY_AND_ASSIGN(PipelineControllerTest); -}; - -TEST_F(PipelineControllerTest, Startup) { - PipelineStatusCB start_cb = StartPipeline(); - EXPECT_FALSE(was_seeked_); - - Complete(start_cb); - EXPECT_TRUE(was_seeked_); - EXPECT_FALSE(last_seeked_time_updated_); - EXPECT_FALSE(was_suspended_); - EXPECT_TRUE(pipeline_controller_.IsStable()); -} - -TEST_F(PipelineControllerTest, SuspendResume) { - Complete(StartPipeline()); - EXPECT_TRUE(was_seeked_); - was_seeked_ = false; - - Complete(SuspendPipeline()); - EXPECT_TRUE(was_suspended_); - EXPECT_FALSE(pipeline_controller_.IsStable()); - - Complete(ResumePipeline()); - EXPECT_TRUE(pipeline_controller_.IsStable()); - - // |was_seeked_| should not be affected by Suspend()/Resume() at all. - EXPECT_FALSE(was_seeked_); -} - -TEST_F(PipelineControllerTest, Seek) { - // Normal seeking should not result in a cancel. - EXPECT_CALL(demuxer_, CancelPendingSeek(_)).Times(0); - - Complete(StartPipeline()); - was_seeked_ = false; - - base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); - EXPECT_CALL(demuxer_, StartWaitingForSeek(seek_time)); - PipelineStatusCB seek_cb = SeekPipeline(seek_time); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(was_seeked_); - - Complete(seek_cb); - EXPECT_TRUE(was_seeked_); - EXPECT_TRUE(pipeline_controller_.IsStable()); -} - -TEST_F(PipelineControllerTest, SuspendResumeTime) { - Complete(StartPipeline()); - Complete(SuspendPipeline()); - - base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); - pipeline_controller_.Seek(seek_time, true); - base::RunLoop().RunUntilIdle(); - - Complete(ResumePipeline()); - EXPECT_EQ(seek_time, last_resume_time_); -} - -TEST_F(PipelineControllerTest, SuspendResumeTime_WithStreamingData) { - Complete(StartPipeline_WithStreamingData()); - Complete(SuspendPipeline()); - - base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); - pipeline_controller_.Seek(seek_time, true); - base::RunLoop().RunUntilIdle(); - - Complete(ResumePipeline()); - EXPECT_EQ(base::TimeDelta(), last_resume_time_); -} - -TEST_F(PipelineControllerTest, SeekAborted) { - Complete(StartPipeline()); - - // Create a first pending seek. - base::TimeDelta seek_time_1 = base::TimeDelta::FromSeconds(5); - EXPECT_CALL(demuxer_, StartWaitingForSeek(seek_time_1)); - PipelineStatusCB seek_cb_1 = SeekPipeline(seek_time_1); - base::RunLoop().RunUntilIdle(); - Mock::VerifyAndClear(&demuxer_); - - // Create a second seek; the first should be aborted. - base::TimeDelta seek_time_2 = base::TimeDelta::FromSeconds(10); - EXPECT_CALL(demuxer_, CancelPendingSeek(seek_time_2)); - pipeline_controller_.Seek(seek_time_2, true); - base::RunLoop().RunUntilIdle(); - Mock::VerifyAndClear(&demuxer_); - - // When the first seek is completed (or aborted) the second should be issued. - EXPECT_CALL(demuxer_, StartWaitingForSeek(seek_time_2)); - EXPECT_CALL(pipeline_, Seek(seek_time_2, _)); - Complete(seek_cb_1); -} - -TEST_F(PipelineControllerTest, PendingSuspend) { - Complete(StartPipeline()); - - base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); - PipelineStatusCB seek_cb = SeekPipeline(seek_time); - base::RunLoop().RunUntilIdle(); - - // While the seek is ongoing, request a suspend. - // It will be a mock failure if pipeline_.Suspend() is called. - pipeline_controller_.Suspend(); - base::RunLoop().RunUntilIdle(); - - // Expect the suspend to trigger when the seek is completed. - EXPECT_CALL(pipeline_, Suspend(_)); - Complete(seek_cb); -} - -TEST_F(PipelineControllerTest, SeekMergesWithResume) { - Complete(StartPipeline()); - Complete(SuspendPipeline()); - - // Request a seek while suspended. - // It will be a mock failure if pipeline_.Seek() is called. - base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); - pipeline_controller_.Seek(seek_time, true); - base::RunLoop().RunUntilIdle(); - - // Resume and verify the resume time includes the seek. - Complete(ResumePipeline()); - EXPECT_EQ(seek_time, last_resume_time_); - EXPECT_TRUE(last_seeked_time_updated_); -} - -TEST_F(PipelineControllerTest, SeekMergesWithSeek) { - Complete(StartPipeline()); - - base::TimeDelta seek_time_1 = base::TimeDelta::FromSeconds(5); - PipelineStatusCB seek_cb_1 = SeekPipeline(seek_time_1); - base::RunLoop().RunUntilIdle(); - - // Request another seek while the first is ongoing. - base::TimeDelta seek_time_2 = base::TimeDelta::FromSeconds(10); - pipeline_controller_.Seek(seek_time_2, true); - base::RunLoop().RunUntilIdle(); - - // Request a third seek. (It should replace the second.) - base::TimeDelta seek_time_3 = base::TimeDelta::FromSeconds(15); - pipeline_controller_.Seek(seek_time_3, true); - base::RunLoop().RunUntilIdle(); - - // Expect the third seek to trigger when the first seek completes. - EXPECT_CALL(pipeline_, Seek(seek_time_3, _)); - Complete(seek_cb_1); -} - -TEST_F(PipelineControllerTest, SeekToSeekTimeElided) { - Complete(StartPipeline()); - - base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); - PipelineStatusCB seek_cb_1 = SeekPipeline(seek_time); - base::RunLoop().RunUntilIdle(); - - // Request a seek to the same time again. - pipeline_controller_.Seek(seek_time, true); - base::RunLoop().RunUntilIdle(); - - // Complete the first seek. - // It would be a mock error if the second seek was dispatched here. - Complete(seek_cb_1); - EXPECT_TRUE(pipeline_controller_.IsStable()); -} - -TEST_F(PipelineControllerTest, SeekToSeekTimeNotElided) { - Complete(StartPipeline_WithDynamicData()); - - base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5); - PipelineStatusCB seek_cb_1 = SeekPipeline(seek_time); - base::RunLoop().RunUntilIdle(); - - // Request a seek to the same time again. - pipeline_controller_.Seek(seek_time, true); - base::RunLoop().RunUntilIdle(); - - // Expect the second seek to trigger when the first seek completes. - EXPECT_CALL(pipeline_, Seek(seek_time, _)); - Complete(seek_cb_1); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/media/filters/video_frame_stream_unittest.cc b/src/cobalt/media/filters/video_frame_stream_unittest.cc deleted file mode 100644 index 89ad107..0000000 --- a/src/cobalt/media/filters/video_frame_stream_unittest.cc +++ /dev/null
@@ -1,1112 +0,0 @@ -// Copyright (c) 2012 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 <utility> - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/message_loop.h" -#include "base/run_loop.h" -#include "cobalt/media/base/fake_demuxer_stream.h" -#include "cobalt/media/base/gmock_callback_support.h" -#include "cobalt/media/base/mock_filters.h" -#include "cobalt/media/base/test_helpers.h" -#include "cobalt/media/base/timestamp_constants.h" -#include "cobalt/media/filters/decoder_stream.h" -#include "cobalt/media/filters/fake_video_decoder.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::AnyNumber; -using ::testing::Assign; -using ::testing::Invoke; -using ::testing::InvokeWithoutArgs; -using ::testing::NiceMock; -using ::testing::Return; -using ::testing::SaveArg; -using ::testing::StrictMock; - -static const int kNumConfigs = 4; -static const int kNumBuffersInOneConfig = 5; - -namespace cobalt { -namespace media { - -struct VideoFrameStreamTestParams { - VideoFrameStreamTestParams(bool is_encrypted, int decoding_delay, - int parallel_decoding) - : is_encrypted(is_encrypted), - decoding_delay(decoding_delay), - parallel_decoding(parallel_decoding) {} - - bool is_encrypted; - int decoding_delay; - int parallel_decoding; -}; - -class VideoFrameStreamTest - : public testing::Test, - public testing::WithParamInterface<VideoFrameStreamTestParams> { - public: - VideoFrameStreamTest() - : demuxer_stream_(new FakeDemuxerStream( - kNumConfigs, kNumBuffersInOneConfig, GetParam().is_encrypted)), - cdm_context_(new StrictMock<MockCdmContext>()), - decryptor_(new NiceMock<MockDecryptor>()), - decoder1_(new FakeVideoDecoder( - GetParam().decoding_delay, GetParam().parallel_decoding, - base::Bind(&VideoFrameStreamTest::OnBytesDecoded, - base::Unretained(this)))), - decoder2_(new FakeVideoDecoder( - GetParam().decoding_delay, GetParam().parallel_decoding, - base::Bind(&VideoFrameStreamTest::OnBytesDecoded, - base::Unretained(this)))), - decoder3_(new FakeVideoDecoder( - GetParam().decoding_delay, GetParam().parallel_decoding, - base::Bind(&VideoFrameStreamTest::OnBytesDecoded, - base::Unretained(this)))), - is_initialized_(false), - num_decoded_frames_(0), - pending_initialize_(false), - pending_read_(false), - pending_reset_(false), - pending_stop_(false), - num_decoded_bytes_unreported_(0), - has_no_key_(false) { - ScopedVector<VideoDecoder> decoders; - decoders.push_back(decoder1_); - decoders.push_back(decoder2_); - decoders.push_back(decoder3_); - - video_frame_stream_.reset(new VideoFrameStream( - message_loop_.task_runner(), std::move(decoders), new MediaLog())); - - EXPECT_CALL(*cdm_context_, GetDecryptor()) - .WillRepeatedly(Return(decryptor_.get())); - - // Decryptor can only decrypt (not decrypt-and-decode) so that - // DecryptingDemuxerStream will be used. - EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) - .WillRepeatedly(RunCallback<1>(false)); - EXPECT_CALL(*decryptor_, Decrypt(_, _, _)) - .WillRepeatedly(Invoke(this, &VideoFrameStreamTest::Decrypt)); - } - - ~VideoFrameStreamTest() { - // Check that the pipeline statistics callback was fired correctly. - EXPECT_EQ(num_decoded_bytes_unreported_, 0); - - is_initialized_ = false; - decoder1_ = NULL; - decoder2_ = NULL; - decoder3_ = NULL; - video_frame_stream_.reset(); - base::RunLoop().RunUntilIdle(); - - DCHECK(!pending_initialize_); - DCHECK(!pending_read_); - DCHECK(!pending_reset_); - DCHECK(!pending_stop_); - } - - MOCK_METHOD1(OnNewSpliceBuffer, void(base::TimeDelta)); - MOCK_METHOD0(OnWaitingForDecryptionKey, void(void)); - - void OnStatistics(const PipelineStatistics& statistics) { - num_decoded_bytes_unreported_ -= statistics.video_bytes_decoded; - } - - void OnBytesDecoded(int count) { num_decoded_bytes_unreported_ += count; } - - void OnInitialized(bool success) { - DCHECK(!pending_read_); - DCHECK(!pending_reset_); - DCHECK(pending_initialize_); - pending_initialize_ = false; - - is_initialized_ = success; - if (!success) { - decoder1_ = NULL; - decoder2_ = NULL; - decoder3_ = NULL; - } - } - - void InitializeVideoFrameStream() { - pending_initialize_ = true; - video_frame_stream_->Initialize( - demuxer_stream_.get(), base::Bind(&VideoFrameStreamTest::OnInitialized, - base::Unretained(this)), - cdm_context_.get(), - base::Bind(&VideoFrameStreamTest::OnStatistics, base::Unretained(this)), - base::Bind(&VideoFrameStreamTest::OnWaitingForDecryptionKey, - base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - // Fake Decrypt() function used by DecryptingDemuxerStream. It does nothing - // but removes the DecryptConfig to make the buffer unencrypted. - void Decrypt(Decryptor::StreamType stream_type, - const scoped_refptr<DecoderBuffer>& encrypted, - const Decryptor::DecryptCB& decrypt_cb) { - DCHECK(encrypted->decrypt_config()); - if (has_no_key_) { - decrypt_cb.Run(Decryptor::kNoKey, NULL); - return; - } - - DCHECK_EQ(stream_type, Decryptor::kVideo); - scoped_refptr<DecoderBuffer> decrypted = - DecoderBuffer::CopyFrom(encrypted->data(), encrypted->data_size()); - if (encrypted->is_key_frame()) decrypted->set_is_key_frame(true); - decrypted->set_timestamp(encrypted->timestamp()); - decrypted->set_duration(encrypted->duration()); - decrypt_cb.Run(Decryptor::kSuccess, decrypted); - } - - // Callback for VideoFrameStream::Read(). - void FrameReady(VideoFrameStream::Status status, - const scoped_refptr<VideoFrame>& frame) { - DCHECK(pending_read_); - frame_read_ = frame; - last_read_status_ = status; - if (frame.get() && - !frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) { - num_decoded_frames_++; - } - pending_read_ = false; - } - - void OnReset() { - DCHECK(!pending_read_); - DCHECK(pending_reset_); - pending_reset_ = false; - } - - void ReadOneFrame() { - frame_read_ = NULL; - pending_read_ = true; - video_frame_stream_->Read( - base::Bind(&VideoFrameStreamTest::FrameReady, base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - } - - void ReadUntilPending() { - do { - ReadOneFrame(); - } while (!pending_read_); - } - - void ReadAllFrames(int expected_decoded_frames) { - do { - ReadOneFrame(); - } while ( - frame_read_.get() && - !frame_read_->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)); - - DCHECK_EQ(expected_decoded_frames, num_decoded_frames_); - } - - void ReadAllFrames() { - // No frames should have been dropped. - ReadAllFrames(kNumConfigs * kNumBuffersInOneConfig); - } - - enum PendingState { - NOT_PENDING, - DEMUXER_READ_NORMAL, - DEMUXER_READ_CONFIG_CHANGE, - DECRYPTOR_NO_KEY, - DECODER_INIT, - DECODER_REINIT, - DECODER_DECODE, - DECODER_RESET - }; - - void EnterPendingState(PendingState state) { - EnterPendingState(state, decoder1_); - } - - void EnterPendingState(PendingState state, FakeVideoDecoder* decoder) { - DCHECK_NE(state, NOT_PENDING); - switch (state) { - case DEMUXER_READ_NORMAL: - demuxer_stream_->HoldNextRead(); - ReadUntilPending(); - break; - - case DEMUXER_READ_CONFIG_CHANGE: - demuxer_stream_->HoldNextConfigChangeRead(); - ReadUntilPending(); - break; - - case DECRYPTOR_NO_KEY: - if (GetParam().is_encrypted) - EXPECT_CALL(*this, OnWaitingForDecryptionKey()); - has_no_key_ = true; - ReadOneFrame(); - break; - - case DECODER_INIT: - decoder->HoldNextInit(); - InitializeVideoFrameStream(); - break; - - case DECODER_REINIT: - decoder->HoldNextInit(); - ReadUntilPending(); - break; - - case DECODER_DECODE: - decoder->HoldDecode(); - ReadUntilPending(); - break; - - case DECODER_RESET: - decoder->HoldNextReset(); - pending_reset_ = true; - video_frame_stream_->Reset( - base::Bind(&VideoFrameStreamTest::OnReset, base::Unretained(this))); - base::RunLoop().RunUntilIdle(); - break; - - case NOT_PENDING: - NOTREACHED(); - break; - } - } - - void SatisfyPendingCallback(PendingState state) { - SatisfyPendingCallback(state, decoder1_); - } - - void SatisfyPendingCallback(PendingState state, FakeVideoDecoder* decoder) { - DCHECK_NE(state, NOT_PENDING); - switch (state) { - case DEMUXER_READ_NORMAL: - case DEMUXER_READ_CONFIG_CHANGE: - demuxer_stream_->SatisfyRead(); - break; - - // This is only interesting to test during VideoFrameStream destruction. - // There's no need to satisfy a callback. - case DECRYPTOR_NO_KEY: - NOTREACHED(); - break; - - case DECODER_INIT: - decoder->SatisfyInit(); - break; - - case DECODER_REINIT: - decoder->SatisfyInit(); - break; - - case DECODER_DECODE: - decoder->SatisfyDecode(); - break; - - case DECODER_RESET: - decoder->SatisfyReset(); - break; - - case NOT_PENDING: - NOTREACHED(); - break; - } - - base::RunLoop().RunUntilIdle(); - } - - void Initialize() { - EnterPendingState(DECODER_INIT); - SatisfyPendingCallback(DECODER_INIT); - } - - void Read() { - EnterPendingState(DECODER_DECODE); - SatisfyPendingCallback(DECODER_DECODE); - } - - void Reset() { - EnterPendingState(DECODER_RESET); - SatisfyPendingCallback(DECODER_RESET); - } - - void ReadUntilDecoderReinitialized(FakeVideoDecoder* decoder) { - EnterPendingState(DECODER_REINIT, decoder); - SatisfyPendingCallback(DECODER_REINIT, decoder); - } - - base::MessageLoop message_loop_; - - std::unique_ptr<VideoFrameStream> video_frame_stream_; - std::unique_ptr<FakeDemuxerStream> demuxer_stream_; - std::unique_ptr<StrictMock<MockCdmContext>> cdm_context_; - - // Use NiceMock since we don't care about most of calls on the decryptor, - // e.g. RegisterNewKeyCB(). - std::unique_ptr<NiceMock<MockDecryptor>> decryptor_; - - // Three decoders are needed to test that decoder fallback can occur more than - // once on a config change. They are owned by |video_frame_stream_|. - FakeVideoDecoder* decoder1_; - FakeVideoDecoder* decoder2_; - FakeVideoDecoder* decoder3_; - - bool is_initialized_; - int num_decoded_frames_; - bool pending_initialize_; - bool pending_read_; - bool pending_reset_; - bool pending_stop_; - int num_decoded_bytes_unreported_; - scoped_refptr<VideoFrame> frame_read_; - VideoFrameStream::Status last_read_status_; - - // Decryptor has no key to decrypt a frame. - bool has_no_key_; - - private: - DISALLOW_COPY_AND_ASSIGN(VideoFrameStreamTest); -}; - -INSTANTIATE_TEST_CASE_P( - Clear, VideoFrameStreamTest, - ::testing::Values(VideoFrameStreamTestParams(false, 0, 1), - VideoFrameStreamTestParams(false, 3, 1), - VideoFrameStreamTestParams(false, 7, 1))); - -INSTANTIATE_TEST_CASE_P(Encrypted, VideoFrameStreamTest, - ::testing::Values(VideoFrameStreamTestParams(true, 7, - 1))); - -INSTANTIATE_TEST_CASE_P( - Clear_Parallel, VideoFrameStreamTest, - ::testing::Values(VideoFrameStreamTestParams(false, 0, 3), - VideoFrameStreamTestParams(false, 2, 3))); - -TEST_P(VideoFrameStreamTest, Initialization) { Initialize(); } - -TEST_P(VideoFrameStreamTest, AllDecoderInitializationFails) { - decoder1_->SimulateFailureToInit(); - decoder2_->SimulateFailureToInit(); - decoder3_->SimulateFailureToInit(); - Initialize(); - EXPECT_FALSE(is_initialized_); -} - -TEST_P(VideoFrameStreamTest, PartialDecoderInitializationFails) { - decoder1_->SimulateFailureToInit(); - decoder2_->SimulateFailureToInit(); - Initialize(); - EXPECT_TRUE(is_initialized_); -} - -TEST_P(VideoFrameStreamTest, ReadOneFrame) { - Initialize(); - Read(); -} - -TEST_P(VideoFrameStreamTest, ReadAllFrames) { - Initialize(); - ReadAllFrames(); -} - -TEST_P(VideoFrameStreamTest, Read_AfterReset) { - Initialize(); - Reset(); - Read(); - Reset(); - Read(); -} - -TEST_P(VideoFrameStreamTest, Read_BlockedDemuxer) { - Initialize(); - demuxer_stream_->HoldNextRead(); - ReadOneFrame(); - EXPECT_TRUE(pending_read_); - - int demuxed_buffers = 0; - - // Pass frames from the demuxer to the VideoFrameStream until the first read - // request is satisfied. - while (pending_read_) { - ++demuxed_buffers; - demuxer_stream_->SatisfyReadAndHoldNext(); - base::RunLoop().RunUntilIdle(); - } - - EXPECT_EQ(std::min(GetParam().decoding_delay + 1, kNumBuffersInOneConfig + 1), - demuxed_buffers); - - // At this point the stream is waiting on read from the demuxer, but there is - // no pending read from the stream. The stream should be blocked if we try - // reading from it again. - ReadUntilPending(); - - demuxer_stream_->SatisfyRead(); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(pending_read_); -} - -TEST_P(VideoFrameStreamTest, Read_BlockedDemuxerAndDecoder) { - // Test applies only when the decoder allows multiple parallel requests. - if (GetParam().parallel_decoding == 1) return; - - Initialize(); - demuxer_stream_->HoldNextRead(); - decoder1_->HoldDecode(); - ReadOneFrame(); - EXPECT_TRUE(pending_read_); - - int demuxed_buffers = 0; - - // Pass frames from the demuxer to the VideoFrameStream until the first read - // request is satisfied, while always keeping one decode request pending. - while (pending_read_) { - ++demuxed_buffers; - demuxer_stream_->SatisfyReadAndHoldNext(); - base::RunLoop().RunUntilIdle(); - - // Always keep one decode request pending. - if (demuxed_buffers > 1) { - decoder1_->SatisfySingleDecode(); - base::RunLoop().RunUntilIdle(); - } - } - - ReadUntilPending(); - EXPECT_TRUE(pending_read_); - - // Unblocking one decode request should unblock read even when demuxer is - // still blocked. - decoder1_->SatisfySingleDecode(); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(pending_read_); - - // Stream should still be blocked on the demuxer after unblocking the decoder. - decoder1_->SatisfyDecode(); - ReadUntilPending(); - EXPECT_TRUE(pending_read_); - - // Verify that the stream has returned all frames that have been demuxed, - // accounting for the decoder delay. - EXPECT_EQ(demuxed_buffers - GetParam().decoding_delay, num_decoded_frames_); - - // Unblocking the demuxer will unblock the stream. - demuxer_stream_->SatisfyRead(); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(pending_read_); -} - -TEST_P(VideoFrameStreamTest, Read_DuringEndOfStreamDecode) { - // Test applies only when the decoder allows multiple parallel requests, and - // they are not satisfied in a single batch. - if (GetParam().parallel_decoding == 1 || GetParam().decoding_delay != 0) - return; - - Initialize(); - decoder1_->HoldDecode(); - - // Read all of the frames up to end of stream. Since parallel decoding is - // enabled, the end of stream buffer will be sent to the decoder immediately, - // but we don't satisfy it yet. - for (int configuration = 0; configuration < kNumConfigs; configuration++) { - for (int frame = 0; frame < kNumBuffersInOneConfig; frame++) { - ReadOneFrame(); - while (pending_read_) { - decoder1_->SatisfySingleDecode(); - base::RunLoop().RunUntilIdle(); - } - } - } - - // Read() again. The callback must be delayed until the decode completes. - ReadOneFrame(); - ASSERT_TRUE(pending_read_); - - // Satisfy decoding of the end of stream buffer. The read should complete. - decoder1_->SatisfySingleDecode(); - base::RunLoop().RunUntilIdle(); - ASSERT_FALSE(pending_read_); - EXPECT_EQ(last_read_status_, VideoFrameStream::OK); - - // The read output should indicate end of stream. - ASSERT_TRUE(frame_read_.get()); - EXPECT_TRUE( - frame_read_->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)); -} - -// No Reset() before initialization is successfully completed. -TEST_P(VideoFrameStreamTest, Reset_AfterInitialization) { - Initialize(); - Reset(); - Read(); -} - -TEST_P(VideoFrameStreamTest, Reset_DuringReinitialization) { - Initialize(); - EnterPendingState(DECODER_REINIT); - // VideoDecoder::Reset() is not called when we reset during reinitialization. - pending_reset_ = true; - video_frame_stream_->Reset( - base::Bind(&VideoFrameStreamTest::OnReset, base::Unretained(this))); - SatisfyPendingCallback(DECODER_REINIT); - Read(); -} - -TEST_P(VideoFrameStreamTest, Reset_AfterReinitialization) { - Initialize(); - EnterPendingState(DECODER_REINIT); - SatisfyPendingCallback(DECODER_REINIT); - Reset(); - Read(); -} - -TEST_P(VideoFrameStreamTest, Reset_DuringDemuxerRead_Normal) { - Initialize(); - EnterPendingState(DEMUXER_READ_NORMAL); - EnterPendingState(DECODER_RESET); - SatisfyPendingCallback(DEMUXER_READ_NORMAL); - SatisfyPendingCallback(DECODER_RESET); - Read(); -} - -TEST_P(VideoFrameStreamTest, Reset_DuringDemuxerRead_ConfigChange) { - Initialize(); - EnterPendingState(DEMUXER_READ_CONFIG_CHANGE); - EnterPendingState(DECODER_RESET); - SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE); - SatisfyPendingCallback(DECODER_RESET); - Read(); -} - -TEST_P(VideoFrameStreamTest, Reset_DuringNormalDecoderDecode) { - Initialize(); - EnterPendingState(DECODER_DECODE); - EnterPendingState(DECODER_RESET); - SatisfyPendingCallback(DECODER_DECODE); - SatisfyPendingCallback(DECODER_RESET); - Read(); -} - -TEST_P(VideoFrameStreamTest, Reset_AfterNormalRead) { - Initialize(); - Read(); - Reset(); - Read(); -} - -TEST_P(VideoFrameStreamTest, Reset_AfterNormalReadWithActiveSplice) { - video_frame_stream_->set_splice_observer(base::Bind( - &VideoFrameStreamTest::OnNewSpliceBuffer, base::Unretained(this))); - Initialize(); - - // Send buffers with a splice timestamp, which sets the active splice flag. - const base::TimeDelta splice_timestamp = base::TimeDelta(); - demuxer_stream_->set_splice_timestamp(splice_timestamp); - EXPECT_CALL(*this, OnNewSpliceBuffer(splice_timestamp)).Times(AnyNumber()); - Read(); - - // Issue an explicit Reset() and clear the splice timestamp. - Reset(); - demuxer_stream_->set_splice_timestamp(kNoTimestamp); - - // Ensure none of the upcoming calls indicate they have a splice timestamp. - EXPECT_CALL(*this, OnNewSpliceBuffer(_)).Times(0); - Read(); -} - -TEST_P(VideoFrameStreamTest, Reset_AfterDemuxerRead_ConfigChange) { - Initialize(); - EnterPendingState(DEMUXER_READ_CONFIG_CHANGE); - SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE); - Reset(); - Read(); -} - -TEST_P(VideoFrameStreamTest, Reset_AfterEndOfStream) { - Initialize(); - ReadAllFrames(); - Reset(); - num_decoded_frames_ = 0; - demuxer_stream_->SeekToStart(); - ReadAllFrames(); -} - -TEST_P(VideoFrameStreamTest, Reset_DuringNoKeyRead) { - Initialize(); - EnterPendingState(DECRYPTOR_NO_KEY); - Reset(); -} - -// In the following Destroy_* tests, |video_frame_stream_| is destroyed in -// VideoFrameStreamTest dtor. - -TEST_P(VideoFrameStreamTest, Destroy_BeforeInitialization) {} - -TEST_P(VideoFrameStreamTest, Destroy_DuringInitialization) { - EnterPendingState(DECODER_INIT); -} - -TEST_P(VideoFrameStreamTest, Destroy_AfterInitialization) { Initialize(); } - -TEST_P(VideoFrameStreamTest, Destroy_DuringReinitialization) { - Initialize(); - EnterPendingState(DECODER_REINIT); -} - -TEST_P(VideoFrameStreamTest, Destroy_AfterReinitialization) { - Initialize(); - EnterPendingState(DECODER_REINIT); - SatisfyPendingCallback(DECODER_REINIT); -} - -TEST_P(VideoFrameStreamTest, Destroy_DuringDemuxerRead_Normal) { - Initialize(); - EnterPendingState(DEMUXER_READ_NORMAL); -} - -TEST_P(VideoFrameStreamTest, Destroy_DuringDemuxerRead_ConfigChange) { - Initialize(); - EnterPendingState(DEMUXER_READ_CONFIG_CHANGE); -} - -TEST_P(VideoFrameStreamTest, Destroy_DuringNormalDecoderDecode) { - Initialize(); - EnterPendingState(DECODER_DECODE); -} - -TEST_P(VideoFrameStreamTest, Destroy_AfterNormalRead) { - Initialize(); - Read(); -} - -TEST_P(VideoFrameStreamTest, Destroy_AfterConfigChangeRead) { - Initialize(); - EnterPendingState(DEMUXER_READ_CONFIG_CHANGE); - SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE); -} - -TEST_P(VideoFrameStreamTest, Destroy_DuringDecoderReinitialization) { - Initialize(); - EnterPendingState(DECODER_REINIT); -} - -TEST_P(VideoFrameStreamTest, Destroy_DuringNoKeyRead) { - Initialize(); - EnterPendingState(DECRYPTOR_NO_KEY); -} - -TEST_P(VideoFrameStreamTest, Destroy_DuringReset) { - Initialize(); - EnterPendingState(DECODER_RESET); -} - -TEST_P(VideoFrameStreamTest, Destroy_AfterReset) { - Initialize(); - Reset(); -} - -TEST_P(VideoFrameStreamTest, Destroy_DuringRead_DuringReset) { - Initialize(); - EnterPendingState(DECODER_DECODE); - EnterPendingState(DECODER_RESET); -} - -TEST_P(VideoFrameStreamTest, Destroy_AfterRead_DuringReset) { - Initialize(); - EnterPendingState(DECODER_DECODE); - EnterPendingState(DECODER_RESET); - SatisfyPendingCallback(DECODER_DECODE); -} - -TEST_P(VideoFrameStreamTest, Destroy_AfterRead_AfterReset) { - Initialize(); - Read(); - Reset(); -} - -TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnInitialDecodeError) { - Initialize(); - decoder1_->SimulateError(); - ReadOneFrame(); - - // |video_frame_stream_| should have fallen back to |decoder2_|. - ASSERT_FALSE(pending_read_); - ASSERT_EQ(VideoFrameStream::OK, last_read_status_); - - // Can't check |decoder1_| right now, it might have been destroyed already. - ASSERT_GT(decoder2_->total_bytes_decoded(), 0); - - // Verify no frame was dropped. - ReadAllFrames(); -} - -TEST_P(VideoFrameStreamTest, FallbackDecoder_EndOfStreamReachedBeforeFallback) { - // Only consider cases where there is a decoder delay. For test simplicity, - // omit the parallel case. - if (GetParam().decoding_delay == 0 || GetParam().parallel_decoding > 1) - return; - - Initialize(); - decoder1_->HoldDecode(); - ReadOneFrame(); - - // One buffer should have already pulled from the demuxer stream. Set the next - // one to be an EOS. - demuxer_stream_->SeekToEndOfStream(); - - decoder1_->SatisfySingleDecode(); - base::RunLoop().RunUntilIdle(); - - // |video_frame_stream_| should not have emited a frame. - EXPECT_TRUE(pending_read_); - - // Pending buffers should contain a regular buffer and an EOS buffer. - EXPECT_EQ(video_frame_stream_->get_pending_buffers_size_for_testing(), 2); - - decoder1_->SimulateError(); - base::RunLoop().RunUntilIdle(); - - // A frame should have been emited - EXPECT_FALSE(pending_read_); - EXPECT_EQ(last_read_status_, VideoFrameStream::OK); - EXPECT_FALSE( - frame_read_->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)); - EXPECT_GT(decoder2_->total_bytes_decoded(), 0); - - ReadOneFrame(); - - EXPECT_FALSE(pending_read_); - EXPECT_EQ(0, video_frame_stream_->get_fallback_buffers_size_for_testing()); - EXPECT_TRUE( - frame_read_->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)); -} - -TEST_P(VideoFrameStreamTest, FallbackDecoder_DoesReinitializeStompPendingRead) { - // Test only the case where there is no decoding delay and parallel decoding. - if (GetParam().decoding_delay != 0 || GetParam().parallel_decoding <= 1) - return; - - Initialize(); - decoder1_->HoldDecode(); - - // Queue one read, defer the second. - frame_read_ = NULL; - pending_read_ = true; - video_frame_stream_->Read( - base::Bind(&VideoFrameStreamTest::FrameReady, base::Unretained(this))); - demuxer_stream_->HoldNextRead(); - - // Force an error to occur on the first decode, but ensure it isn't propagated - // until after the next read has been started. - decoder1_->SimulateError(); - decoder2_->HoldDecode(); - - // Complete the fallback to the second decoder with the read still pending. - base::RunLoop().RunUntilIdle(); - - // Can't check |decoder1_| right now, it might have been destroyed already. - // Verify that there was nothing decoded until we kicked the decoder. - EXPECT_EQ(decoder2_->total_bytes_decoded(), 0); - decoder2_->SatisfyDecode(); - const int first_decoded_bytes = decoder2_->total_bytes_decoded(); - ASSERT_GT(first_decoded_bytes, 0); - - // Satisfy the previously pending read and ensure it is decoded. - demuxer_stream_->SatisfyRead(); - base::RunLoop().RunUntilIdle(); - ASSERT_GT(decoder2_->total_bytes_decoded(), first_decoded_bytes); -} - -TEST_P(VideoFrameStreamTest, - FallbackDecoder_SelectedOnInitialDecodeError_Twice) { - Initialize(); - decoder1_->SimulateError(); - decoder2_->HoldNextInit(); - ReadOneFrame(); - - decoder2_->SatisfyInit(); - decoder2_->SimulateError(); - base::RunLoop().RunUntilIdle(); - - // |video_frame_stream_| should have fallen back to |decoder3_|. - ASSERT_FALSE(pending_read_); - ASSERT_EQ(VideoFrameStream::OK, last_read_status_); - - // Can't check |decoder1_| or |decoder2_| right now, they might have been - // destroyed already. - ASSERT_GT(decoder3_->total_bytes_decoded(), 0); - - // Verify no frame was dropped. - ReadAllFrames(); -} - -TEST_P(VideoFrameStreamTest, FallbackDecoder_ConfigChangeClearsPendingBuffers) { - // Test case is only interesting if the decoder can receive a config change - // before returning its first frame. - if (GetParam().decoding_delay < kNumBuffersInOneConfig) return; - - Initialize(); - EnterPendingState(DEMUXER_READ_CONFIG_CHANGE); - ASSERT_GT(video_frame_stream_->get_pending_buffers_size_for_testing(), 0); - - SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE); - ASSERT_EQ(video_frame_stream_->get_pending_buffers_size_for_testing(), 0); - EXPECT_FALSE(pending_read_); - - ReadAllFrames(); -} - -TEST_P(VideoFrameStreamTest, FallbackDecoder_ErrorDuringConfigChangeFlushing) { - // Test case is only interesting if the decoder can receive a config change - // before returning its first frame. - if (GetParam().decoding_delay < kNumBuffersInOneConfig) return; - - Initialize(); - EnterPendingState(DEMUXER_READ_CONFIG_CHANGE); - EXPECT_GT(video_frame_stream_->get_pending_buffers_size_for_testing(), 0); - - decoder1_->HoldDecode(); - SatisfyPendingCallback(DEMUXER_READ_CONFIG_CHANGE); - - // The flush request should have been sent and held. - EXPECT_EQ(video_frame_stream_->get_pending_buffers_size_for_testing(), 0); - EXPECT_TRUE(pending_read_); - - // Triggering an error here will cause the frames in |decoder1_| to be lost. - // There are no pending buffers buffers to give to give to |decoder2_| due to - // crbug.com/603713. - decoder1_->SimulateError(); - base::RunLoop().RunUntilIdle(); - - // We want to make sure that |decoder2_| can decode the rest of the frames - // in the demuxer stream. - ReadAllFrames(kNumBuffersInOneConfig * (kNumConfigs - 1)); -} - -TEST_P(VideoFrameStreamTest, FallbackDecoder_PendingBuffersIsFilledAndCleared) { - // Test applies only when there is a decoder delay, and the decoder will not - // receive a config change before outputing its first frame. Parallel decoding - // is also disabled in this test case, for readability and simplicity of the - // unit test. - if (GetParam().decoding_delay == 0 || - GetParam().decoding_delay > kNumBuffersInOneConfig || - GetParam().parallel_decoding > 1) { - return; - } - Initialize(); - - // Block on demuxer read and decoder decode so we can step through. - demuxer_stream_->HoldNextRead(); - decoder1_->HoldDecode(); - ReadOneFrame(); - - int demuxer_reads_satisfied = 0; - // Send back and requests buffers until the next one would fill the decoder - // delay. - while (demuxer_reads_satisfied < GetParam().decoding_delay - 1) { - // Send a buffer back. - demuxer_stream_->SatisfyReadAndHoldNext(); - base::RunLoop().RunUntilIdle(); - ++demuxer_reads_satisfied; - - // Decode one buffer. - decoder1_->SatisfySingleDecode(); - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(pending_read_); - EXPECT_EQ(demuxer_reads_satisfied, - video_frame_stream_->get_pending_buffers_size_for_testing()); - // No fallback buffers should be queued up yet. - EXPECT_EQ(0, video_frame_stream_->get_fallback_buffers_size_for_testing()); - } - - // Hold the init before triggering the error, to verify internal state. - demuxer_stream_->SatisfyReadAndHoldNext(); - ++demuxer_reads_satisfied; - decoder2_->HoldNextInit(); - decoder1_->SimulateError(); - base::RunLoop().RunUntilIdle(); - - EXPECT_TRUE(pending_read_); - EXPECT_EQ(demuxer_reads_satisfied, - video_frame_stream_->get_pending_buffers_size_for_testing()); - - decoder2_->SatisfyInit(); - decoder2_->HoldDecode(); - base::RunLoop().RunUntilIdle(); - - // Make sure the pending buffers have been transfered to fallback buffers. - // One call to Decode() during the initialization process, so we expect one - // buffer to already have been consumed from the fallback buffers. - // Pending buffers should never go down (unless we encounter a config change) - EXPECT_EQ(demuxer_reads_satisfied - 1, - video_frame_stream_->get_fallback_buffers_size_for_testing()); - EXPECT_EQ(demuxer_reads_satisfied, - video_frame_stream_->get_pending_buffers_size_for_testing()); - - decoder2_->SatisfyDecode(); - base::RunLoop().RunUntilIdle(); - - // Make sure all buffers consumed by |decoder2_| have come from the fallback. - // Pending buffers should not have been cleared yet. - EXPECT_EQ(0, video_frame_stream_->get_fallback_buffers_size_for_testing()); - EXPECT_EQ(demuxer_reads_satisfied, - video_frame_stream_->get_pending_buffers_size_for_testing()); - EXPECT_EQ(video_frame_stream_->get_previous_decoder_for_testing(), decoder1_); - EXPECT_TRUE(pending_read_); - - // Give the decoder one more buffer, enough to release a frame. - demuxer_stream_->SatisfyReadAndHoldNext(); - base::RunLoop().RunUntilIdle(); - - // New buffers should not have been added after the frame was released. - EXPECT_EQ(video_frame_stream_->get_pending_buffers_size_for_testing(), 0); - EXPECT_FALSE(pending_read_); - - demuxer_stream_->SatisfyRead(); - - // Confirm no frames were dropped. - ReadAllFrames(); -} - -TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnDecodeThenInitErrors) { - Initialize(); - decoder1_->SimulateError(); - decoder2_->SimulateFailureToInit(); - ReadOneFrame(); - - // |video_frame_stream_| should have fallen back to |decoder3_| - ASSERT_FALSE(pending_read_); - ASSERT_EQ(VideoFrameStream::OK, last_read_status_); - - // Can't check |decoder1_| or |decoder2_| right now, they might have been - // destroyed already. - ASSERT_GT(decoder3_->total_bytes_decoded(), 0); - - // Verify no frame was dropped. - ReadAllFrames(); -} - -TEST_P(VideoFrameStreamTest, FallbackDecoder_SelectedOnInitThenDecodeErrors) { - decoder1_->SimulateFailureToInit(); - decoder2_->HoldDecode(); - Initialize(); - ReadOneFrame(); - decoder2_->SimulateError(); - base::RunLoop().RunUntilIdle(); - - // |video_frame_stream_| should have fallen back to |decoder3_| - ASSERT_FALSE(pending_read_); - ASSERT_EQ(VideoFrameStream::OK, last_read_status_); - - // Can't check |decoder1_| or |decoder2_| right now, they might have been - // destroyed already. - ASSERT_GT(decoder3_->total_bytes_decoded(), 0); - - // Verify no frame was dropped. - ReadAllFrames(); -} - -TEST_P(VideoFrameStreamTest, - FallbackDecoder_NotSelectedOnMidstreamDecodeError) { - Initialize(); - ReadOneFrame(); - - // Successfully received a frame. - EXPECT_FALSE(pending_read_); - ASSERT_GT(decoder1_->total_bytes_decoded(), 0); - - decoder1_->SimulateError(); - - // The error must surface from Read() as DECODE_ERROR. - while (last_read_status_ == VideoFrameStream::OK) { - ReadOneFrame(); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(pending_read_); - } - - // Verify the error was surfaced, rather than falling back to |decoder2_|. - EXPECT_FALSE(pending_read_); - ASSERT_EQ(decoder2_->total_bytes_decoded(), 0); - ASSERT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_); -} - -TEST_P(VideoFrameStreamTest, DecoderErrorWhenNotReading) { - Initialize(); - decoder1_->HoldDecode(); - ReadOneFrame(); - EXPECT_TRUE(pending_read_); - - // Satisfy decode requests until we get the first frame out. - while (pending_read_) { - decoder1_->SatisfySingleDecode(); - base::RunLoop().RunUntilIdle(); - } - - // Trigger an error in the decoding. - decoder1_->SimulateError(); - - // The error must surface from Read() as DECODE_ERROR. - while (last_read_status_ == VideoFrameStream::OK) { - ReadOneFrame(); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(pending_read_); - } - EXPECT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_); -} - -TEST_P(VideoFrameStreamTest, FallbackDecoderSelectedOnFailureToReinitialize) { - Initialize(); - decoder1_->SimulateFailureToInit(); - // Holding decode, because large decoder delays might cause us to get rid of - // |previous_decoder_| before we are in a pending state again. - decoder2_->HoldDecode(); - ReadUntilDecoderReinitialized(decoder1_); - ASSERT_TRUE(video_frame_stream_->get_previous_decoder_for_testing()); - decoder2_->SatisfyDecode(); - base::RunLoop().RunUntilIdle(); - ReadAllFrames(); - ASSERT_FALSE(video_frame_stream_->get_previous_decoder_for_testing()); -} - -TEST_P(VideoFrameStreamTest, - FallbackDecoderSelectedOnFailureToReinitialize_Twice) { - Initialize(); - decoder1_->SimulateFailureToInit(); - ReadUntilDecoderReinitialized(decoder1_); - ReadOneFrame(); - decoder2_->SimulateFailureToInit(); - ReadUntilDecoderReinitialized(decoder2_); - ReadAllFrames(); -} - -TEST_P(VideoFrameStreamTest, DecodeErrorAfterFallbackDecoderSelectionFails) { - Initialize(); - decoder1_->SimulateFailureToInit(); - decoder2_->SimulateFailureToInit(); - decoder3_->SimulateFailureToInit(); - ReadUntilDecoderReinitialized(decoder1_); - // The error will surface from Read() as DECODE_ERROR. - while (last_read_status_ == VideoFrameStream::OK) { - ReadOneFrame(); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(pending_read_); - } - EXPECT_EQ(VideoFrameStream::DECODE_ERROR, last_read_status_); -} - -TEST_P(VideoFrameStreamTest, Destroy_DuringFallbackDecoderSelection) { - Initialize(); - decoder1_->SimulateFailureToInit(); - EnterPendingState(DECODER_REINIT); - decoder2_->HoldNextInit(); - SatisfyPendingCallback(DECODER_REINIT); -} - -} // namespace media -} // namespace cobalt
diff --git a/src/cobalt/network/persistent_cookie_store.cc b/src/cobalt/network/persistent_cookie_store.cc index 19aa978..5c2ae8c 100644 --- a/src/cobalt/network/persistent_cookie_store.cc +++ b/src/cobalt/network/persistent_cookie_store.cc
@@ -32,8 +32,6 @@ const base::TimeDelta kMaxCookieLifetime = base::TimeDelta::FromDays(365 * 2); std::vector<net::CanonicalCookie*> GetAllCookies(sql::Connection* conn) { - base::Time maximum_expiry = base::Time::Now() + kMaxCookieLifetime; - std::vector<net::CanonicalCookie*> actual_cookies; sql::Statement get_all(conn->GetCachedStatement( SQL_FROM_HERE, @@ -41,25 +39,20 @@ "creation, expiration, last_access, secure, http_only " "FROM CookieTable")); while (get_all.Step()) { - base::Time expiry = base::Time::FromInternalValue(get_all.ColumnInt64(8)); - if (expiry > maximum_expiry) { - expiry = maximum_expiry; - } - - net::CanonicalCookie* cookie = net::CanonicalCookie::Create( + // We create a CanonicalCookie directly through its constructor instead of + // through CanonicalCookie::Create() and its sanitization because these + // values are just serialized from a former instance of a CanonicalCookie + // object that *was* created through CanonicalCookie::Create(). + net::CanonicalCookie* cookie = new net::CanonicalCookie( GURL(get_all.ColumnString(0)), get_all.ColumnString(1), get_all.ColumnString(2), get_all.ColumnString(3), get_all.ColumnString(4), get_all.ColumnString(5), get_all.ColumnString(6), - base::Time::FromInternalValue(get_all.ColumnInt64(7)), expiry, + base::Time::FromInternalValue(get_all.ColumnInt64(7)), + base::Time::FromInternalValue(get_all.ColumnInt64(8)), + base::Time::FromInternalValue(get_all.ColumnInt64(9)), get_all.ColumnBool(10), get_all.ColumnBool(11)); - if (cookie) { - cookie->SetLastAccessDate( - base::Time::FromInternalValue(get_all.ColumnInt64(9))); - actual_cookies.push_back(cookie); - } else { - DLOG(ERROR) << "Failed to create cookie."; - } + actual_cookies.push_back(cookie); } return actual_cookies;
diff --git a/src/cobalt/script/fake_global_environment.h b/src/cobalt/script/fake_global_environment.h index 391c5e7..68349e7 100644 --- a/src/cobalt/script/fake_global_environment.h +++ b/src/cobalt/script/fake_global_environment.h
@@ -25,15 +25,17 @@ class FakeGlobalEnvironment : public GlobalEnvironment { public: - void CreateGlobalObject() OVERRIDE {}; + void CreateGlobalObject() OVERRIDE {} bool EvaluateScript(const scoped_refptr<SourceCode>& /*script_utf8*/, - std::string* /*out_result*/) OVERRIDE { + std::string* /*out_result*/, + bool /*mute_script_erros_report*/) OVERRIDE { return false; } - bool EvaluateScript(const scoped_refptr<SourceCode>& /*script_utf8*/, - const scoped_refptr<Wrappable>& /*owning_object*/, - base::optional<OpaqueHandleHolder::Reference>* - out_opaque_handle) OVERRIDE { + bool EvaluateScript( + const scoped_refptr<SourceCode>& /*script_utf8*/, + const scoped_refptr<Wrappable>& /*owning_object*/, + base::optional<OpaqueHandleHolder::Reference>* out_opaque_handle, + bool /*mute_errors*/) OVERRIDE { return false; } // False positive lint error.
diff --git a/src/cobalt/script/fake_script_runner.h b/src/cobalt/script/fake_script_runner.h index 1a6bf9e..cb304ed 100644 --- a/src/cobalt/script/fake_script_runner.h +++ b/src/cobalt/script/fake_script_runner.h
@@ -27,10 +27,10 @@ class FakeScriptRunner : public ScriptRunner { public: FakeScriptRunner() : fake_global_environment_(new FakeGlobalEnvironment()) {} - std::string Execute( - const std::string& /*script_utf8*/, - const base::SourceLocation& /*script_location*/, - bool* out_succeeded) OVERRIDE { + std::string Execute(const std::string& /*script_utf8*/, + const base::SourceLocation& /*script_location*/, + bool* out_succeeded, + bool /*mute_script_erros_report*/) OVERRIDE { if (out_succeeded) { *out_succeeded = true; }
diff --git a/src/cobalt/script/global_environment.h b/src/cobalt/script/global_environment.h index 9676f73..daf1e38 100644 --- a/src/cobalt/script/global_environment.h +++ b/src/cobalt/script/global_environment.h
@@ -58,7 +58,8 @@ // If out_result is non-NULL, it will be set to hold the result of the script // evaluation if the script succeeds, or an exception message if it fails. virtual bool EvaluateScript(const scoped_refptr<SourceCode>& script_utf8, - std::string* out_result_utf8) = 0; + std::string* out_result_utf8, + bool mute_errors) = 0; // Evaluate the JavaScript source code. Returns true on success, // false if there is an exception. @@ -67,7 +68,8 @@ virtual bool EvaluateScript( const scoped_refptr<SourceCode>& script_utf8, const scoped_refptr<Wrappable>& owning_object, - base::optional<OpaqueHandleHolder::Reference>* out_opaque_handle) = 0; + base::optional<OpaqueHandleHolder::Reference>* out_opaque_handle, + bool mute_errors) = 0; // Returns the stack trace as a vector of individual frames. // Set |max_frames| to 0 to retrieve all available frames. Otherwise
diff --git a/src/cobalt/script/mozjs-45/mozjs-45.gyp b/src/cobalt/script/mozjs-45/mozjs-45.gyp index fb0b0da..2ce1ff4 100644 --- a/src/cobalt/script/mozjs-45/mozjs-45.gyp +++ b/src/cobalt/script/mozjs-45/mozjs-45.gyp
@@ -20,7 +20,6 @@ 'sources': [ 'conversion_helpers.cc', 'mozjs_callback_interface.cc', - 'mozjs_debugger.cc', 'mozjs_engine.cc', 'mozjs_exception_state.cc', 'mozjs_global_environment.cc', @@ -40,6 +39,7 @@ 'weak_heap_object_manager.h', 'wrapper_factory.cc', 'wrapper_private.cc', + '<(DEPTH)/cobalt/script/shared/stub_script_debugger.cc', ], 'dependencies': [ '<(DEPTH)/cobalt/script/script.gyp:script',
diff --git a/src/cobalt/script/mozjs-45/mozjs.cc b/src/cobalt/script/mozjs-45/mozjs.cc index b17937f..dc8e290 100644 --- a/src/cobalt/script/mozjs-45/mozjs.cc +++ b/src/cobalt/script/mozjs-45/mozjs.cc
@@ -60,7 +60,7 @@ } int MozjsMain(int argc, char** argv) { - cobalt::script::StandaloneJavascriptRunner standalone_runner(); + cobalt::script::StandaloneJavascriptRunner standalone_runner; MozjsGlobalEnvironment* global_environment = static_cast<MozjsGlobalEnvironment*>( standalone_runner.global_environment().get()); @@ -88,7 +88,8 @@ // Execute the script and get the results of execution. std::string result; - bool success = global_environment->EvaluateScript(source, &result); + bool success = global_environment->EvaluateScript( + source, &result, false /*mute_errors*/); // Echo the results to stdout. if (!success) { std::cout << "Exception: ";
diff --git a/src/cobalt/script/mozjs-45/mozjs_debugger.cc b/src/cobalt/script/mozjs-45/mozjs_debugger.cc deleted file mode 100644 index 90a1d76..0000000 --- a/src/cobalt/script/mozjs-45/mozjs_debugger.cc +++ /dev/null
@@ -1,65 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "cobalt/script/mozjs-45/mozjs_debugger.h" - -#include "base/logging.h" - -namespace cobalt { -namespace script { - -// Static factory method declared in public interface. -scoped_ptr<ScriptDebugger> ScriptDebugger::CreateDebugger( - GlobalEnvironment* global_environment, Delegate* delegate) { - return scoped_ptr<ScriptDebugger>( - new mozjs::MozjsDebugger(global_environment, delegate)); -} - -namespace mozjs { - -MozjsDebugger::MozjsDebugger(GlobalEnvironment* global_environment, - Delegate* delegate) { - NOTIMPLEMENTED(); -} - -MozjsDebugger::~MozjsDebugger() { NOTIMPLEMENTED(); } - -void MozjsDebugger::Attach() { NOTIMPLEMENTED(); } - -void MozjsDebugger::Detach() { NOTIMPLEMENTED(); } - -void MozjsDebugger::Pause() { NOTIMPLEMENTED(); } - -void MozjsDebugger::Resume() { NOTIMPLEMENTED(); } - -void MozjsDebugger::SetBreakpoint(const std::string& script_id, int line_number, - int column_number) { - NOTIMPLEMENTED(); -} - -script::ScriptDebugger::PauseOnExceptionsState -MozjsDebugger::SetPauseOnExceptions(PauseOnExceptionsState state) { - NOTIMPLEMENTED(); - return kNone; -} - -void MozjsDebugger::StepInto() { NOTIMPLEMENTED(); } - -void MozjsDebugger::StepOut() { NOTIMPLEMENTED(); } - -void MozjsDebugger::StepOver() { NOTIMPLEMENTED(); } - -} // namespace mozjs -} // namespace script -} // namespace cobalt
diff --git a/src/cobalt/script/mozjs-45/mozjs_debugger.h b/src/cobalt/script/mozjs-45/mozjs_debugger.h deleted file mode 100644 index 3bb37ac..0000000 --- a/src/cobalt/script/mozjs-45/mozjs_debugger.h +++ /dev/null
@@ -1,48 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef COBALT_SCRIPT_MOZJS_45_MOZJS_DEBUGGER_H_ -#define COBALT_SCRIPT_MOZJS_45_MOZJS_DEBUGGER_H_ - -#include <string> - -#include "cobalt/script/script_debugger.h" - -namespace cobalt { -namespace script { -namespace mozjs { - -class MozjsDebugger : public ScriptDebugger { - public: - MozjsDebugger(GlobalEnvironment* global_environment, Delegate* delegate); - ~MozjsDebugger() OVERRIDE; - - // Implementation of ScriptDebugger. - void Attach() OVERRIDE; - void Detach() OVERRIDE; - void Pause() OVERRIDE; - void Resume() OVERRIDE; - void SetBreakpoint(const std::string& script_id, int line_number, - int column_number) OVERRIDE; - PauseOnExceptionsState SetPauseOnExceptions( - PauseOnExceptionsState state) OVERRIDE; - void StepInto() OVERRIDE; - void StepOut() OVERRIDE; - void StepOver() OVERRIDE; -}; - -} // namespace mozjs -} // namespace script -} // namespace cobalt - -#endif // COBALT_SCRIPT_MOZJS_45_MOZJS_DEBUGGER_H_
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc index ee6dcd4..bf451ff 100644 --- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc +++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -188,8 +188,8 @@ } bool MozjsGlobalEnvironment::EvaluateScript( - const scoped_refptr<SourceCode>& source_code, - std::string* out_result_utf8) { + const scoped_refptr<SourceCode>& source_code, std::string* out_result_utf8, + bool mute_errors) { TRACK_MEMORY_SCOPE("Javascript"); DCHECK(thread_checker_.CalledOnValidThread()); @@ -201,7 +201,8 @@ std::string error_message; last_error_message_ = &error_message; - bool success = EvaluateScriptInternal(source_code, &result_value); + bool success = + EvaluateScriptInternal(source_code, &result_value, mute_errors); if (out_result_utf8) { if (success) { MozjsExceptionState exception_state(context_); @@ -220,14 +221,16 @@ bool MozjsGlobalEnvironment::EvaluateScript( const scoped_refptr<SourceCode>& source_code, const scoped_refptr<Wrappable>& owning_object, - base::optional<OpaqueHandleHolder::Reference>* out_opaque_handle) { + base::optional<OpaqueHandleHolder::Reference>* out_opaque_handle, + bool mute_errors) { TRACK_MEMORY_SCOPE("Javascript"); DCHECK(thread_checker_.CalledOnValidThread()); JSAutoRequest auto_request(context_); JSAutoCompartment auto_compartment(context_, global_object_proxy_); JS::AutoSaveExceptionState auto_save_exception_state(context_); JS::RootedValue result_value(context_); - bool success = EvaluateScriptInternal(source_code, &result_value); + bool success = + EvaluateScriptInternal(source_code, &result_value, mute_errors); if (success && out_opaque_handle) { JS::RootedObject js_object(context_); JS_ValueToObject(context_, result_value, &js_object); @@ -240,7 +243,7 @@ bool MozjsGlobalEnvironment::EvaluateScriptInternal( const scoped_refptr<SourceCode>& source_code, - JS::MutableHandleValue out_result) { + JS::MutableHandleValue out_result, bool mute_errors) { TRACK_MEMORY_SCOPE("Javascript"); DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(global_object_proxy_); @@ -266,6 +269,7 @@ JS::CompileOptions options(context_); options.setFileAndLine(location.file_path.c_str(), location.line_number); + options.setMutedErrors(mute_errors); bool success = JS::Evaluate(context_, options, inflated_buffer, length, out_result); if (!success && context_->isExceptionPending()) { @@ -283,7 +287,7 @@ scoped_refptr<SourceCode> source_code = new MozjsSourceCode(source, base::SourceLocation(filename, 1, 1)); std::string result; - bool success = EvaluateScript(source_code, &result); + bool success = EvaluateScript(source_code, &result, false /*mute_errors*/); if (!success) { DLOG(FATAL) << result; }
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h index ad99d14..5a11a63 100644 --- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h +++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -51,12 +51,13 @@ void CreateGlobalObject() OVERRIDE; bool EvaluateScript(const scoped_refptr<SourceCode>& script, - std::string* out_result_utf8) OVERRIDE; + std::string* out_result_utf8, bool mute_errors) OVERRIDE; - bool EvaluateScript(const scoped_refptr<SourceCode>& script_utf8, - const scoped_refptr<Wrappable>& owning_object, - base::optional<OpaqueHandleHolder::Reference>* - out_opaque_handle) OVERRIDE; + bool EvaluateScript( + const scoped_refptr<SourceCode>& script_utf8, + const scoped_refptr<Wrappable>& owning_object, + base::optional<OpaqueHandleHolder::Reference>* out_opaque_handle, + bool mute_errors) OVERRIDE; std::vector<StackFrame> GetStackTrace(int max_frames = 0) OVERRIDE; @@ -143,7 +144,8 @@ private: bool EvaluateScriptInternal(const scoped_refptr<SourceCode>& source_code, - JS::MutableHandleValue out_result); + JS::MutableHandleValue out_result, + bool mute_errors); void EvaluateEmbeddedScript(const unsigned char* data, size_t size, const char* filename);
diff --git a/src/cobalt/script/script_runner.cc b/src/cobalt/script/script_runner.cc index 0768705..1da3b56 100644 --- a/src/cobalt/script/script_runner.cc +++ b/src/cobalt/script/script_runner.cc
@@ -70,7 +70,7 @@ std::string Execute(const std::string& script_utf8, const base::SourceLocation& script_location, - bool* out_succeeded) OVERRIDE; + bool* out_succeeded, bool mute_errors) OVERRIDE; GlobalEnvironment* GetGlobalEnvironment() const OVERRIDE { return global_environment_; } @@ -80,9 +80,8 @@ }; std::string ScriptRunnerImpl::Execute( - const std::string& script_utf8, - const base::SourceLocation& script_location, - bool* out_succeeded) { + const std::string& script_utf8, const base::SourceLocation& script_location, + bool* out_succeeded, bool mute_errors) { scoped_refptr<SourceCode> source_code = SourceCode::CreateSourceCode(script_utf8, script_location); if (out_succeeded) { @@ -93,7 +92,7 @@ return ""; } std::string result; - if (!global_environment_->EvaluateScript(source_code, &result)) { + if (!global_environment_->EvaluateScript(source_code, &result, mute_errors)) { DLOG(WARNING) << "Failed to execute JavaScript: " << result; #if defined(HANDLE_CORE_DUMP) script_runner_log.Get().IncrementFailCount();
diff --git a/src/cobalt/script/script_runner.h b/src/cobalt/script/script_runner.h index 142c136..5f399d4 100644 --- a/src/cobalt/script/script_runner.h +++ b/src/cobalt/script/script_runner.h
@@ -36,7 +36,7 @@ // script executed without errors. virtual std::string Execute(const std::string& script_utf8, const base::SourceLocation& script_location, - bool* out_succeeded) = 0; + bool* out_succeeded, bool mute_errors) = 0; virtual GlobalEnvironment* GetGlobalEnvironment() const { return NULL; } virtual ~ScriptRunner() {} };
diff --git a/src/cobalt/script/shared/stub_script_debugger.cc b/src/cobalt/script/shared/stub_script_debugger.cc new file mode 100644 index 0000000..a839e83 --- /dev/null +++ b/src/cobalt/script/shared/stub_script_debugger.cc
@@ -0,0 +1,56 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cobalt/script/script_debugger.h" + +#include "base/logging.h" + +namespace cobalt { +namespace script { + +class StubScriptDebugger : public ScriptDebugger { + public: + StubScriptDebugger(GlobalEnvironment* global_environment, + Delegate* delegate) { + NOTIMPLEMENTED(); + } + ~StubScriptDebugger() override { NOTIMPLEMENTED(); } + + void Attach() override { NOTIMPLEMENTED(); } + void Detach() override { NOTIMPLEMENTED(); } + void Pause() override { NOTIMPLEMENTED(); } + void Resume() override { NOTIMPLEMENTED(); } + void SetBreakpoint(const std::string& script_id, int line_number, + int column_number) override { + NOTIMPLEMENTED(); + } + PauseOnExceptionsState SetPauseOnExceptions( + PauseOnExceptionsState state) override { + NOTIMPLEMENTED(); + return kNone; + } + void StepInto() override { NOTIMPLEMENTED(); } + void StepOut() override { NOTIMPLEMENTED(); } + void StepOver() override { NOTIMPLEMENTED(); } +}; + +// Static factory method declared in public interface. +scoped_ptr<ScriptDebugger> ScriptDebugger::CreateDebugger( + GlobalEnvironment* global_environment, Delegate* delegate) { + return scoped_ptr<ScriptDebugger>( + new StubScriptDebugger(global_environment, delegate)); +} + +} // namespace script +} // namespace cobalt
diff --git a/src/cobalt/script/standalone_javascript_runner.cc b/src/cobalt/script/standalone_javascript_runner.cc index a71d2d1..aca3084 100644 --- a/src/cobalt/script/standalone_javascript_runner.cc +++ b/src/cobalt/script/standalone_javascript_runner.cc
@@ -69,7 +69,8 @@ // Execute the script and get the results of execution. std::string result; - bool success = global_environment_->EvaluateScript(source, &result); + bool success = global_environment_->EvaluateScript(source, &result, + false /*mute_errors*/); // Echo the results to stdout. if (!success) { std::cout << "Exception: ";
diff --git a/src/cobalt/script/v8c/v8c.gyp b/src/cobalt/script/v8c/v8c.gyp index dc9b560..d1deb54 100644 --- a/src/cobalt/script/v8c/v8c.gyp +++ b/src/cobalt/script/v8c/v8c.gyp
@@ -24,6 +24,7 @@ 'v8c_global_environment.h', 'v8c_source_code.cc', 'v8c_source_code.h', + '<(DEPTH)/cobalt/script/shared/stub_script_debugger.cc', ], 'dependencies': [ '<(DEPTH)/v8/src/v8.gyp:v8',
diff --git a/src/cobalt/webdriver/script_executor.cc b/src/cobalt/webdriver/script_executor.cc index 508448a..1bbcaf3 100644 --- a/src/cobalt/webdriver/script_executor.cc +++ b/src/cobalt/webdriver/script_executor.cc
@@ -81,7 +81,8 @@ // Evaluate the harness initialization script. std::string result; - if (!global_environment->EvaluateScript(source, &result)) { + if (!global_environment->EvaluateScript(source, &result, + false /*mute_errors*/)) { return NULL; }
diff --git a/src/cobalt/webdriver/script_executor_params.cc b/src/cobalt/webdriver/script_executor_params.cc index 1bafe8f..262ecc7 100644 --- a/src/cobalt/webdriver/script_executor_params.cc +++ b/src/cobalt/webdriver/script_executor_params.cc
@@ -40,7 +40,8 @@ function.c_str(), base::SourceLocation("[webdriver]", 1, 1)); if (!global_environment->EvaluateScript(function_source, params.get(), - ¶ms->function_object_)) { + ¶ms->function_object_, + false /*mute_errors*/)) { DLOG(ERROR) << "Failed to create Function object"; } return {params, global_environment.get()};
diff --git a/src/cobalt/webdriver/window_driver.cc b/src/cobalt/webdriver/window_driver.cc index fac224c..f750fb3 100644 --- a/src/cobalt/webdriver/window_driver.cc +++ b/src/cobalt/webdriver/window_driver.cc
@@ -24,6 +24,7 @@ #include "cobalt/dom/pointer_event_init.h" #include "cobalt/math/clamp.h" #include "cobalt/script/global_environment.h" +#include "cobalt/script/logging_exception_state.h" #include "cobalt/webdriver/keyboard.h" #include "cobalt/webdriver/search.h" #include "cobalt/webdriver/util/call_on_message_loop.h" @@ -147,8 +148,9 @@ DCHECK(thread_checker_.CalledOnValidThread()); ElementDriver* result; bool success = util::TryCallOnMessageLoop( - window_message_loop_, base::Bind(&WindowDriver::GetElementDriver, - base::Unretained(this), element_id), + window_message_loop_, + base::Bind(&WindowDriver::GetElementDriver, base::Unretained(this), + element_id), &result); return success ? result : NULL; } @@ -292,8 +294,9 @@ util::CommandResult<protocol::ElementId> WindowDriver::GetActiveElement() { return util::CallOnMessageLoop( - window_message_loop_, base::Bind(&WindowDriver::GetActiveElementInternal, - base::Unretained(this)), + window_message_loop_, + base::Bind(&WindowDriver::GetActiveElementInternal, + base::Unretained(this)), protocol::Response::kNoSuchWindow); }
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc index 3bdc99b..7b7d15e 100644 --- a/src/cobalt/xhr/xml_http_request.cc +++ b/src/cobalt/xhr/xml_http_request.cc
@@ -831,7 +831,7 @@ // 8. If CORS flag is set and actualResponse’s location URL includes // credentials, then return a network error. if (new_url.has_username() || new_url.has_password()) { - if (dom::URLUtils::Origin(new_url) != dom::URLUtils::Origin(request_url_)) { + if (loader::Origin(new_url) != loader::Origin(request_url_)) { DLOG(INFO) << "XHR is redirected to cross-origin url with credentials, " "aborting request for security reasons."; HandleRequestError(kNetworkError); @@ -865,11 +865,11 @@ // If CORS flag is set and actualResponse’s location URL’s origin is not // same origin with request’s current url’s origin, then set request’s // origin to a unique opaque origin. - if (dom::URLUtils::Origin(new_url) != dom::URLUtils::Origin(request_url_)) { + if (loader::Origin(new_url) != loader::Origin(request_url_)) { if (is_cross_origin_) { - origin_ = dom::URLUtils::Origin(); + origin_ = loader::Origin(); } else { - origin_ = dom::URLUtils::Origin(request_url_); + origin_ = loader::Origin(request_url_); is_cross_origin_ = true; } } @@ -1087,9 +1087,8 @@ // We let data url fetch resources freely but with no response headers. is_data_url_ = is_data_url_ || request_url_.SchemeIs("data"); - is_cross_origin_ = - (is_redirect_ && is_cross_origin_) || - (origin_ != dom::URLUtils::Origin(request_url_) && !is_data_url_); + is_cross_origin_ = (is_redirect_ && is_cross_origin_) || + (origin_ != loader::Origin(request_url_) && !is_data_url_); is_redirect_ = false; // If the CORS flag is set, httpRequest’s method is neither `GET` nor `HEAD` // or httpRequest’s mode is "websocket", then append `Origin`/httpRequest’s @@ -1106,7 +1105,8 @@ base::Unretained(this)), origin_.SerializedOrigin(), base::Bind(&XMLHttpRequest::CORSPreflightErrorCallback, - base::Unretained(this)))); + base::Unretained(this)), + settings_->window()->get_preflight_cache())); corspreflight_->set_headers(request_headers_); // For cross-origin requests, don't send or save auth data / cookies unless // withCredentials was set. @@ -1136,8 +1136,12 @@ } void XMLHttpRequest::CORSPreflightSuccessCallback() { - DCHECK(url_fetcher_); - url_fetcher_->Start(); + if (!url_fetcher_) { + HandleRequestError(XMLHttpRequest::kNetworkError); + } else { + DCHECK(url_fetcher_); + url_fetcher_->Start(); + } } std::ostream& operator<<(std::ostream& out, const XMLHttpRequest& xhr) {
diff --git a/src/cobalt/xhr/xml_http_request.h b/src/cobalt/xhr/xml_http_request.h index b8f4c41..abf96a1 100644 --- a/src/cobalt/xhr/xml_http_request.h +++ b/src/cobalt/xhr/xml_http_request.h
@@ -304,7 +304,7 @@ bool is_cross_origin_; // net::URLReuqest does not have origin variable so we can only store it here. // https://fetch.spec.whatwg.org/#concept-request-origin - dom::URLUtils::Origin origin_; + loader::Origin origin_; bool is_redirect_; std::string response_mime_type_; std::string request_body_text_;
diff --git a/src/starboard/shared/uwp/application_uwp.cc b/src/starboard/shared/uwp/application_uwp.cc index 3609033..8087ee5 100644 --- a/src/starboard/shared/uwp/application_uwp.cc +++ b/src/starboard/shared/uwp/application_uwp.cc
@@ -33,13 +33,14 @@ #include "starboard/shared/starboard/player/video_frame_internal.h" #include "starboard/shared/uwp/analog_thumbstick_input_thread.h" #include "starboard/shared/uwp/async_utils.h" +#include "starboard/shared/uwp/log_file_impl.h" #include "starboard/shared/uwp/window_internal.h" -#include "starboard/shared/win32/log_file_impl.h" #include "starboard/shared/win32/thread_private.h" #include "starboard/shared/win32/wchar_utils.h" #include "starboard/string.h" #include "starboard/system.h" +namespace sbuwp = starboard::shared::uwp; namespace sbwin32 = starboard::shared::win32; using Microsoft::WRL::ComPtr; @@ -60,11 +61,13 @@ using Windows::ApplicationModel::Core::IFrameworkView; using Windows::ApplicationModel::Core::IFrameworkViewSource; using Windows::ApplicationModel::SuspendingEventArgs; +using Windows::Foundation::Collections::IVectorView; using Windows::Foundation::EventHandler; using Windows::Foundation::IAsyncOperation; using Windows::Foundation::TimeSpan; using Windows::Foundation::TypedEventHandler; using Windows::Foundation::Uri; +using Windows::Globalization::Calendar; using Windows::Media::Protection::HdcpProtection; using Windows::Media::Protection::HdcpSession; using Windows::Media::Protection::HdcpSetProtectionResult; @@ -72,6 +75,8 @@ using Windows::Networking::Connectivity::NetworkConnectivityLevel; using Windows::Networking::Connectivity::NetworkInformation; using Windows::Networking::Connectivity::NetworkStatusChangedEventHandler; +using Windows::Storage::KnownFolders; +using Windows::Storage::StorageFolder; using Windows::System::Threading::ThreadPoolTimer; using Windows::System::Threading::TimerElapsedHandler; using Windows::System::UserAuthenticationStatus; @@ -410,8 +415,35 @@ ::starboard::shared::uwp::GetCommandLinePointer(application_uwp); if (command_line->HasSwitch(kLogPathSwitch)) { std::string switch_val = command_line->GetSwitchValue(kLogPathSwitch); - sbwin32::OpenLogInCacheDirectory(switch_val.c_str(), - kSbFileCreateAlways); + sbuwp::OpenLogFile( + Windows::Storage::ApplicationData::Current->LocalCacheFolder, + switch_val.c_str()); + } else { +#if !defined(COBALT_BUILD_TYPE_GOLD) + // Log to a file on the last removable device available (probably the + // most recently added removable device). + try { + if (KnownFolders::RemovableDevices != nullptr) { + concurrency::create_task( + KnownFolders::RemovableDevices->GetFoldersAsync()).then( + [](IVectorView<StorageFolder^>^ results) { + if (results->Size > 0) { + StorageFolder^ folder = results->GetAt(results->Size - 1); + Calendar^ now = ref new Calendar(); + char filename[128]; + SbStringFormatF(filename, sizeof(filename), + "cobalt_log_%04d%02d%02d_%02d%02d%02d.txt", + now->Year, now->Month, now->Day, + now->Hour + now->FirstHourInThisPeriod, + now->Minute, now->Second); + sbuwp::OpenLogFile(folder, filename); + } + }); + } + } catch(Platform::Exception^) { + SB_LOG(ERROR) << "Unable to open log file in RemovableDevices"; + } +#endif } SB_LOG(INFO) << "Starting " << GetBinaryName();
diff --git a/src/starboard/shared/uwp/log_file_impl.cc b/src/starboard/shared/uwp/log_file_impl.cc new file mode 100644 index 0000000..fbcebc9 --- /dev/null +++ b/src/starboard/shared/uwp/log_file_impl.cc
@@ -0,0 +1,122 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/shared/uwp/log_file_impl.h" + +#include <string> + +#include "starboard/log.h" +#include "starboard/mutex.h" +#include "starboard/once.h" +#include "starboard/shared/win32/wchar_utils.h" +#include "starboard/string.h" + +using Windows::Foundation::AsyncOperationCompletedHandler; +using Windows::Foundation::AsyncStatus; +using Windows::Foundation::IAsyncOperation; +using Windows::Storage::FileAccessMode; +using Windows::Storage::StorageFile; +using Windows::Storage::StorageFolder; +using Windows::Storage::Streams::DataWriter; +using Windows::Storage::Streams::IRandomAccessStream; +using Windows::Storage::Streams::IOutputStream; + +namespace { + +SbMutex log_mutex_ = SB_MUTEX_INITIALIZER; + +// The Windows Storage API must be used in order to access files in priviledged +// areas (e.g. KnownFolders::RemovableDevices). The win32 file API used by +// SbFile returns access denied errors in these situations. +DataWriter^ log_writer_ = nullptr; + +// SbMutex is not reentrant, so factor out close log file functionality for use +// by other functions. +void CloseLogFileWithoutLock() { + log_writer_ = nullptr; +} + +} // namespace + +namespace starboard { +namespace shared { +namespace uwp { + +void CloseLogFile() { + SbMutexAcquire(&log_mutex_); + CloseLogFileWithoutLock(); + SbMutexRelease(&log_mutex_); +} + +void OpenLogFile(StorageFolder^ folder, const char* filename) { + std::wstring wfilename = win32::CStringToWString(filename); + + SbMutexAcquire(&log_mutex_); + CloseLogFileWithoutLock(); + + // Manually set the completion callback function instead of using + // concurrency::create_task() since those tasks may not execute before the + // UI thread wants the log_mutex_ to output another log. + auto task = folder->CreateFileAsync( + ref new Platform::String(wfilename.c_str()), + Windows::Storage::CreationCollisionOption::ReplaceExisting); + task->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>( + [folder](IAsyncOperation<StorageFile^>^ op, AsyncStatus) { + try { + auto task = op->GetResults()->OpenAsync(FileAccessMode::ReadWrite); + task->Completed = ref new + AsyncOperationCompletedHandler<IRandomAccessStream^>( + [](IAsyncOperation<IRandomAccessStream^>^ op, AsyncStatus) { + log_writer_ = ref new DataWriter( + op->GetResults()->GetOutputStreamAt(0)); + SbMutexRelease(&log_mutex_); + }); + } catch(Platform::Exception^) { + SbMutexRelease(&log_mutex_); + SB_LOG(ERROR) << "Unable to open log file in folder " + << win32::platformStringToString(folder->Name); + } + }); +} + +void WriteToLogFile(const char* text, int text_length) { + if (text_length <= 0) { + return; + } + + SbMutexAcquire(&log_mutex_); + if (log_writer_) { + log_writer_->WriteBytes(ref new Platform::Array<unsigned char>( + (unsigned char*) text, text_length)); + + // Manually set the completion callback function instead of using + // concurrency::create_task() since those tasks may not execute before the + // UI thread wants the log_mutex_ to output another log. + auto task = log_writer_->StoreAsync(); + task->Completed = ref new AsyncOperationCompletedHandler<unsigned int>( + [](IAsyncOperation<unsigned int>^, AsyncStatus) { + auto task = log_writer_->FlushAsync(); + task->Completed = ref new AsyncOperationCompletedHandler<bool>( + [](IAsyncOperation<bool>^, AsyncStatus) { + SbMutexRelease(&log_mutex_); + }); + }); + } else { + SbMutexRelease(&log_mutex_); + } +} + +} // namespace uwp +} // namespace shared +} // namespace starboard
diff --git a/src/starboard/shared/uwp/log_file_impl.h b/src/starboard/shared/uwp/log_file_impl.h new file mode 100644 index 0000000..6f24f53 --- /dev/null +++ b/src/starboard/shared/uwp/log_file_impl.h
@@ -0,0 +1,36 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This header provides a mechanism for multiple Android logging +// formats to share a single log file handle. + +#ifndef STARBOARD_SHARED_UWP_LOG_FILE_IMPL_H_ +#define STARBOARD_SHARED_UWP_LOG_FILE_IMPL_H_ + +namespace starboard { +namespace shared { +namespace uwp { + +void CloseLogFile(); + +void OpenLogFile(Windows::Storage::StorageFolder^ folder, + const char* filename); + +void WriteToLogFile(const char* text, int text_length); + +} // namespace uwp +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_UWP_LOG_FILE_IMPL_H_
diff --git a/src/starboard/shared/uwp/log_raw.cc b/src/starboard/shared/uwp/log_raw.cc new file mode 100644 index 0000000..da8ca37 --- /dev/null +++ b/src/starboard/shared/uwp/log_raw.cc
@@ -0,0 +1,30 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/log.h" + +#include <stdio.h> +#include <windows.h> + +#include "starboard/shared/uwp/log_file_impl.h" +#include "starboard/string.h" + +namespace sbuwp = starboard::shared::uwp; + +void SbLogRaw(const char* message) { + fprintf(stderr, "%s", message); + OutputDebugStringA(message); + sbuwp::WriteToLogFile( + message, static_cast<int>(SbStringGetLength(message))); +}
diff --git a/src/starboard/shared/uwp/log_raw_format.cc b/src/starboard/shared/uwp/log_raw_format.cc new file mode 100644 index 0000000..40354bf --- /dev/null +++ b/src/starboard/shared/uwp/log_raw_format.cc
@@ -0,0 +1,36 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "starboard/log.h" + +#include <stdio.h> +#include <windows.h> + +#include "starboard/shared/uwp/log_file_impl.h" + +static const int kMaxLogLineChars = 16 * 1024; + +namespace sbuwp = starboard::shared::uwp; + +void SbLogRawFormat(const char* format, va_list arguments) { + vfprintf(stderr, format, arguments); + char log_buffer[kMaxLogLineChars]; + int result = vsprintf_s(log_buffer, kMaxLogLineChars, format, arguments); + if (result > 0) { + OutputDebugStringA(log_buffer); + sbuwp::WriteToLogFile(log_buffer, result); + } else { + OutputDebugStringA("[log line too long]"); + } +}
diff --git a/src/starboard/shared/uwp/starboard_platform.gypi b/src/starboard/shared/uwp/starboard_platform.gypi index bb3a5d5..7b0c645 100644 --- a/src/starboard/shared/uwp/starboard_platform.gypi +++ b/src/starboard/shared/uwp/starboard_platform.gypi
@@ -24,6 +24,10 @@ 'application_uwp.h', 'async_utils.h', 'get_home_directory.cc', + 'log_file_impl.cc', + 'log_file_impl.h', + 'log_raw.cc', + 'log_raw_format.cc', 'media_is_output_protected.cc', 'media_set_output_protection.cc', 'system_clear_platform_error.cc',
diff --git a/src/starboard/shared/win32/log_file_impl.cc b/src/starboard/shared/win32/log_file_impl.cc index 1d258ae..89371cd 100644 --- a/src/starboard/shared/win32/log_file_impl.cc +++ b/src/starboard/shared/win32/log_file_impl.cc
@@ -28,6 +28,16 @@ SbMutex log_mutex = SB_MUTEX_INITIALIZER; SbFile log_file = kSbFileInvalid; +// SbMutex is not reentrant, so factor out close log file functionality for use +// by other functions. +void CloseLogFileWithoutLock() { + if (SbFileIsValid(log_file)) { + SbFileFlush(log_file); + SbFileClose(log_file); + log_file = kSbFileInvalid; + } +} + } // namespace namespace starboard { @@ -36,13 +46,7 @@ void CloseLogFile() { SbMutexAcquire(&log_mutex); - - if (SbFileIsValid(log_file)) { - SbFileFlush(log_file); - SbFileClose(log_file); - log_file = kSbFileInvalid; - } - + CloseLogFileWithoutLock(); SbMutexRelease(&log_mutex); } @@ -73,12 +77,12 @@ void OpenLogFile(const char* path, const int creation_flags) { SB_DCHECK((creation_flags & kSbFileOpenAlways) || (creation_flags & kSbFileCreateAlways)); - CloseLogFile(); SB_DLOG(INFO) << "Logging to [" << path << "]"; int flags = creation_flags | kSbFileWrite; SbMutexAcquire(&log_mutex); + CloseLogFileWithoutLock(); if ((path != nullptr) && (path != '\0')) { log_file = SbFileOpen(path, flags, nullptr, nullptr); SB_DCHECK(SbFileIsValid(log_file));
diff --git a/src/starboard/win/shared/starboard_platform.gypi b/src/starboard/win/shared/starboard_platform.gypi index 9302e9c..260f790 100644 --- a/src/starboard/win/shared/starboard_platform.gypi +++ b/src/starboard/win/shared/starboard_platform.gypi
@@ -22,6 +22,10 @@ '<(DEPTH)/starboard/shared/win32/application_win32.cc', '<(DEPTH)/starboard/shared/win32/dialog.cc', '<(DEPTH)/starboard/shared/win32/get_home_directory.cc', + '<(DEPTH)/starboard/shared/win32/log_file_impl.cc', + '<(DEPTH)/starboard/shared/win32/log_file_impl.h', + '<(DEPTH)/starboard/shared/win32/log_raw.cc', + '<(DEPTH)/starboard/shared/win32/log_raw_format.cc', '<(DEPTH)/starboard/shared/win32/media_is_video_supported.cc', '<(DEPTH)/starboard/shared/win32/playready_license.cc', '<(DEPTH)/starboard/shared/win32/starboard_main.cc', @@ -302,14 +306,10 @@ '<(DEPTH)/starboard/shared/win32/file_truncate.cc', '<(DEPTH)/starboard/shared/win32/file_write.cc', '<(DEPTH)/starboard/shared/win32/log.cc', - '<(DEPTH)/starboard/shared/win32/log_file_impl.cc', - '<(DEPTH)/starboard/shared/win32/log_file_impl.h', '<(DEPTH)/starboard/shared/win32/log_flush.cc', '<(DEPTH)/starboard/shared/win32/log_format.cc', '<(DEPTH)/starboard/shared/win32/log_is_tty.cc', - '<(DEPTH)/starboard/shared/win32/log_raw.cc', '<(DEPTH)/starboard/shared/win32/log_raw_dump_stack.cc', - '<(DEPTH)/starboard/shared/win32/log_raw_format.cc', '<(DEPTH)/starboard/shared/win32/memory_allocate_aligned_unchecked.cc', '<(DEPTH)/starboard/shared/win32/memory_allocate_unchecked.cc', '<(DEPTH)/starboard/shared/win32/memory_free.cc',
diff --git a/src/v8/src/d8.gyp b/src/v8/src/d8.gyp index e6a4021..780a833 100644 --- a/src/v8/src/d8.gyp +++ b/src/v8/src/d8.gyp
@@ -31,7 +31,22 @@ # Enable support for Intel VTune. Supported on ia32/x64 only 'v8_enable_vtunejit%': 0, 'v8_enable_i18n_support%': 1, + + 'test_isolation_mode': 'noop', + 'v8_use_snapshot': 0, + 'v8_optimized_debug': 0, + 'v8_use_external_startup_data': 0, + # TODO: Enable i18n support. + 'v8_enable_i18n_support': 0, }, + # TODO: Remove after starboardization. + 'target_defaults': { + 'defines': [ + 'V8_OS_POSIX=1', + 'V8_OS_LINUX=1', + 'V8_TRAP_HANDLER_SUPPORTED=1', + ], + }, 'includes': ['../gypfiles/toolchain.gypi', '../gypfiles/features.gypi'], 'targets': [ { @@ -65,9 +80,11 @@ 'd8_js2c', ], }], + # TODO: Remove OS=="starboard" after starboardization. ['(OS=="linux" or OS=="mac" or OS=="freebsd" or OS=="netbsd" \ or OS=="openbsd" or OS=="solaris" or OS=="android" \ - or OS=="qnx" or OS=="aix")', { + or OS=="qnx" or OS=="aix" \ + or OS=="starboard")', { 'sources': [ 'd8-posix.cc', ] }], [ 'OS=="win"', {
diff --git a/src/v8/src/libplatform/tracing/trace-object.cc b/src/v8/src/libplatform/tracing/trace-object.cc index bb4bf71..54a785f 100644 --- a/src/v8/src/libplatform/tracing/trace-object.cc +++ b/src/v8/src/libplatform/tracing/trace-object.cc
@@ -4,7 +4,7 @@ #include "include/libplatform/v8-tracing.h" -#include "base/trace_event/common/trace_event_common.h" +#include "v8/src/tracing/trace_event_common.h" #include "include/v8-platform.h" #include "src/base/platform/platform.h" #include "src/base/platform/time.h"
diff --git a/src/v8/src/libplatform/tracing/trace-writer.cc b/src/v8/src/libplatform/tracing/trace-writer.cc index 7445087..86e69ef 100644 --- a/src/v8/src/libplatform/tracing/trace-writer.cc +++ b/src/v8/src/libplatform/tracing/trace-writer.cc
@@ -6,7 +6,7 @@ #include <cmath> -#include "base/trace_event/common/trace_event_common.h" +#include "v8/src/tracing/trace_event_common.h" #include "include/v8-platform.h" #include "src/base/platform/platform.h"
diff --git a/src/v8/src/tracing/trace-event.h b/src/v8/src/tracing/trace-event.h index 6ca3950..4cb4275 100644 --- a/src/v8/src/tracing/trace-event.h +++ b/src/v8/src/tracing/trace-event.h
@@ -8,7 +8,7 @@ #include <stddef.h> #include <memory> -#include "base/trace_event/common/trace_event_common.h" +#include "v8/src/tracing/trace_event_common.h" #include "include/v8-platform.h" #include "src/base/atomicops.h" #include "src/base/macros.h"
diff --git a/src/v8/src/tracing/trace_event_common.h b/src/v8/src/tracing/trace_event_common.h new file mode 100644 index 0000000..5866d41 --- /dev/null +++ b/src/v8/src/tracing/trace_event_common.h
@@ -0,0 +1,1085 @@ +// Copyright 2015 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. + +// This header file defines the set of trace_event macros without specifying +// how the events actually get collected and stored. If you need to expose trace +// events to some other universe, you can copy-and-paste this file as well as +// trace_event.h, modifying the macros contained there as necessary for the +// target platform. The end result is that multiple libraries can funnel events +// through to a shared trace event collector. + +// IMPORTANT: To avoid conflicts, if you need to modify this file for a library, +// land your change in base/ first, and then copy-and-paste it. + +// Trace events are for tracking application performance and resource usage. +// Macros are provided to track: +// Begin and end of function calls +// Counters +// +// Events are issued against categories. Whereas LOG's +// categories are statically defined, TRACE categories are created +// implicitly with a string. For example: +// TRACE_EVENT_INSTANT0("MY_SUBSYSTEM", "SomeImportantEvent", +// TRACE_EVENT_SCOPE_THREAD) +// +// It is often the case that one trace may belong in multiple categories at the +// same time. The first argument to the trace can be a comma-separated list of +// categories, forming a category group, like: +// +// TRACE_EVENT_INSTANT0("input,views", "OnMouseOver", TRACE_EVENT_SCOPE_THREAD) +// +// We can enable/disable tracing of OnMouseOver by enabling/disabling either +// category. +// +// Events can be INSTANT, or can be pairs of BEGIN and END in the same scope: +// TRACE_EVENT_BEGIN0("MY_SUBSYSTEM", "SomethingCostly") +// doSomethingCostly() +// TRACE_EVENT_END0("MY_SUBSYSTEM", "SomethingCostly") +// Note: our tools can't always determine the correct BEGIN/END pairs unless +// these are used in the same scope. Use ASYNC_BEGIN/ASYNC_END macros if you +// need them to be in separate scopes. +// +// A common use case is to trace entire function scopes. This +// issues a trace BEGIN and END automatically: +// void doSomethingCostly() { +// TRACE_EVENT0("MY_SUBSYSTEM", "doSomethingCostly"); +// ... +// } +// +// Additional parameters can be associated with an event: +// void doSomethingCostly2(int howMuch) { +// TRACE_EVENT1("MY_SUBSYSTEM", "doSomethingCostly", +// "howMuch", howMuch); +// ... +// } +// +// The trace system will automatically add to this information the +// current process id, thread id, and a timestamp in microseconds. +// +// To trace an asynchronous procedure such as an IPC send/receive, use +// ASYNC_BEGIN and ASYNC_END: +// [single threaded sender code] +// static int send_count = 0; +// ++send_count; +// TRACE_EVENT_ASYNC_BEGIN0("ipc", "message", send_count); +// Send(new MyMessage(send_count)); +// [receive code] +// void OnMyMessage(send_count) { +// TRACE_EVENT_ASYNC_END0("ipc", "message", send_count); +// } +// The third parameter is a unique ID to match ASYNC_BEGIN/ASYNC_END pairs. +// ASYNC_BEGIN and ASYNC_END can occur on any thread of any traced process. +// Pointers can be used for the ID parameter, and they will be mangled +// internally so that the same pointer on two different processes will not +// match. For example: +// class MyTracedClass { +// public: +// MyTracedClass() { +// TRACE_EVENT_ASYNC_BEGIN0("category", "MyTracedClass", this); +// } +// ~MyTracedClass() { +// TRACE_EVENT_ASYNC_END0("category", "MyTracedClass", this); +// } +// } +// +// Trace event also supports counters, which is a way to track a quantity +// as it varies over time. Counters are created with the following macro: +// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter", g_myCounterValue); +// +// Counters are process-specific. The macro itself can be issued from any +// thread, however. +// +// Sometimes, you want to track two counters at once. You can do this with two +// counter macros: +// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter0", g_myCounterValue[0]); +// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter1", g_myCounterValue[1]); +// Or you can do it with a combined macro: +// TRACE_COUNTER2("MY_SUBSYSTEM", "myCounter", +// "bytesPinned", g_myCounterValue[0], +// "bytesAllocated", g_myCounterValue[1]); +// This indicates to the tracing UI that these counters should be displayed +// in a single graph, as a summed area chart. +// +// Since counters are in a global namespace, you may want to disambiguate with a +// unique ID, by using the TRACE_COUNTER_ID* variations. +// +// By default, trace collection is compiled in, but turned off at runtime. +// Collecting trace data is the responsibility of the embedding +// application. In Chrome's case, navigating to about:tracing will turn on +// tracing and display data collected across all active processes. +// +// +// Memory scoping note: +// Tracing copies the pointers, not the string content, of the strings passed +// in for category_group, name, and arg_names. Thus, the following code will +// cause problems: +// char* str = strdup("importantName"); +// TRACE_EVENT_INSTANT0("SUBSYSTEM", str); // BAD! +// free(str); // Trace system now has dangling pointer +// +// To avoid this issue with the |name| and |arg_name| parameters, use the +// TRACE_EVENT_COPY_XXX overloads of the macros at additional runtime overhead. +// Notes: The category must always be in a long-lived char* (i.e. static const). +// The |arg_values|, when used, are always deep copied with the _COPY +// macros. +// +// When are string argument values copied: +// const char* arg_values are only referenced by default: +// TRACE_EVENT1("category", "name", +// "arg1", "literal string is only referenced"); +// Use TRACE_STR_COPY to force copying of a const char*: +// TRACE_EVENT1("category", "name", +// "arg1", TRACE_STR_COPY("string will be copied")); +// std::string arg_values are always copied: +// TRACE_EVENT1("category", "name", +// "arg1", std::string("string will be copied")); +// +// +// Convertable notes: +// Converting a large data type to a string can be costly. To help with this, +// the trace framework provides an interface ConvertableToTraceFormat. If you +// inherit from it and implement the AppendAsTraceFormat method the trace +// framework will call back to your object to convert a trace output time. This +// means, if the category for the event is disabled, the conversion will not +// happen. +// +// class MyData : public base::trace_event::ConvertableToTraceFormat { +// public: +// MyData() {} +// void AppendAsTraceFormat(std::string* out) const override { +// out->append("{\"foo\":1}"); +// } +// private: +// ~MyData() override {} +// DISALLOW_COPY_AND_ASSIGN(MyData); +// }; +// +// TRACE_EVENT1("foo", "bar", "data", +// std::unique_ptr<ConvertableToTraceFormat>(new MyData())); +// +// The trace framework will take ownership if the passed pointer and it will +// be free'd when the trace buffer is flushed. +// +// Note, we only do the conversion when the buffer is flushed, so the provided +// data object should not be modified after it's passed to the trace framework. +// +// +// Thread Safety: +// A thread safe singleton and mutex are used for thread safety. Category +// enabled flags are used to limit the performance impact when the system +// is not enabled. +// +// TRACE_EVENT macros first cache a pointer to a category. The categories are +// statically allocated and safe at all times, even after exit. Fetching a +// category is protected by the TraceLog::lock_. Multiple threads initializing +// the static variable is safe, as they will be serialized by the lock and +// multiple calls will return the same pointer to the category. +// +// Then the category_group_enabled flag is checked. This is a unsigned char, and +// not intended to be multithread safe. It optimizes access to AddTraceEvent +// which is threadsafe internally via TraceLog::lock_. The enabled flag may +// cause some threads to incorrectly call or skip calling AddTraceEvent near +// the time of the system being enabled or disabled. This is acceptable as +// we tolerate some data loss while the system is being enabled/disabled and +// because AddTraceEvent is threadsafe internally and checks the enabled state +// again under lock. +// +// Without the use of these static category pointers and enabled flags all +// trace points would carry a significant performance cost of acquiring a lock +// and resolving the category. + +#if defined(TRACE_EVENT0) +#error "Another copy of this file has already been included." +#endif + +// This will mark the trace event as disabled by default. The user will need +// to explicitly enable the event. +#define TRACE_DISABLED_BY_DEFAULT(name) "disabled-by-default-" name + +// Records a pair of begin and end events called "name" for the current +// scope, with 0, 1 or 2 associated arguments. If the category is not +// enabled, then this does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT0(category_group, name) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name) +#define TRACE_EVENT_WITH_FLOW0(category_group, name, bind_id, flow_flags) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, bind_id, \ + flow_flags) +#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, arg1_name, arg1_val) +#define TRACE_EVENT_WITH_FLOW1(category_group, name, bind_id, flow_flags, \ + arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, bind_id, \ + flow_flags, arg1_name, arg1_val) +#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, \ + arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) +#define TRACE_EVENT_WITH_FLOW2(category_group, name, bind_id, flow_flags, \ + arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, bind_id, \ + flow_flags, arg1_name, arg1_val, \ + arg2_name, arg2_val) + +// Records a single event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT_INSTANT0(category_group, name, scope) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \ + TRACE_EVENT_FLAG_NONE | scope) +#define TRACE_EVENT_INSTANT1(category_group, name, scope, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \ + TRACE_EVENT_FLAG_NONE | scope, arg1_name, arg1_val) +#define TRACE_EVENT_INSTANT2(category_group, name, scope, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \ + TRACE_EVENT_FLAG_NONE | scope, arg1_name, arg1_val, \ + arg2_name, arg2_val) +#define TRACE_EVENT_COPY_INSTANT0(category_group, name, scope) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \ + TRACE_EVENT_FLAG_COPY | scope) +#define TRACE_EVENT_COPY_INSTANT1(category_group, name, scope, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \ + TRACE_EVENT_FLAG_COPY | scope, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_INSTANT2(category_group, name, scope, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name, \ + TRACE_EVENT_FLAG_COPY | scope, arg1_name, arg1_val, \ + arg2_name, arg2_val) + +#define TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(category_group, name, scope, \ + timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \ + TRACE_EVENT_PHASE_INSTANT, category_group, name, timestamp, \ + TRACE_EVENT_FLAG_NONE | scope) + +#define TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(category_group, name, scope, \ + timestamp, arg_name, arg_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \ + TRACE_EVENT_PHASE_INSTANT, category_group, name, timestamp, \ + TRACE_EVENT_FLAG_NONE | scope, arg_name, arg_val) + +// Records a single BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT_BEGIN0(category_group, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, \ + TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_BEGIN1(category_group, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_BEGIN2(category_group, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ + arg2_name, arg2_val) +#define TRACE_EVENT_COPY_BEGIN0(category_group, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, \ + TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_BEGIN1(category_group, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_BEGIN2(category_group, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group, name, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ + arg2_name, arg2_val) + +// Similar to TRACE_EVENT_BEGINx but with a custom |at| timestamp provided. +// - |id| is used to match the _BEGIN event with the _END event. +// Events are considered to match if their category_group, name and id values +// all match. |id| must either be a pointer or an integer value up to 64 bits. +// If it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +#define TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(category_group, name, id, \ + thread_id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP0( \ + category_group, name, id, thread_id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP1( \ + category_group, name, id, thread_id, timestamp, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP2( \ + category_group, name, id, thread_id, timestamp, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, \ + arg2_val) + +// Records a single END event for "name" immediately. If the category +// is not enabled, then this does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT_END0(category_group, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, \ + TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_END1(category_group, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_END2(category_group, name, arg1_name, arg1_val, arg2_name, \ + arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, \ + arg2_name, arg2_val) +#define TRACE_EVENT_COPY_END0(category_group, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, \ + TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_END1(category_group, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_END2(category_group, name, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_END, category_group, name, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ + arg2_name, arg2_val) + +#define TRACE_EVENT_MARK_WITH_TIMESTAMP0(category_group, name, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \ + TRACE_EVENT_PHASE_MARK, category_group, name, timestamp, \ + TRACE_EVENT_FLAG_NONE) + +#define TRACE_EVENT_MARK_WITH_TIMESTAMP1(category_group, name, timestamp, \ + arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \ + TRACE_EVENT_PHASE_MARK, category_group, name, timestamp, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) + +#define TRACE_EVENT_MARK_WITH_TIMESTAMP2( \ + category_group, name, timestamp, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \ + TRACE_EVENT_PHASE_MARK, category_group, name, timestamp, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val) + +#define TRACE_EVENT_COPY_MARK(category_group, name) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_MARK, category_group, name, \ + TRACE_EVENT_FLAG_COPY) + +#define TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP(category_group, name, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \ + TRACE_EVENT_PHASE_MARK, category_group, name, timestamp, \ + TRACE_EVENT_FLAG_COPY) + +// Similar to TRACE_EVENT_ENDx but with a custom |at| timestamp provided. +// - |id| is used to match the _BEGIN event with the _END event. +// Events are considered to match if their category_group, name and id values +// all match. |id| must either be a pointer or an integer value up to 64 bits. +// If it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +#define TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0(category_group, name, id, \ + thread_id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP0( \ + category_group, name, id, thread_id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP1( \ + category_group, name, id, thread_id, timestamp, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP2( \ + category_group, name, id, thread_id, timestamp, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, thread_id, \ + timestamp, TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, \ + arg2_val) + +// Records the value of a counter called "name" immediately. Value +// must be representable as a 32 bit integer. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_COUNTER1(category_group, name, value) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category_group, name, \ + TRACE_EVENT_FLAG_NONE, "value", \ + static_cast<int>(value)) +#define TRACE_COPY_COUNTER1(category_group, name, value) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category_group, name, \ + TRACE_EVENT_FLAG_COPY, "value", \ + static_cast<int>(value)) + +// Records the values of a multi-parted counter called "name" immediately. +// The UI will treat value1 and value2 as parts of a whole, displaying their +// values as a stacked-bar chart. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_COUNTER2(category_group, name, value1_name, value1_val, \ + value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category_group, name, \ + TRACE_EVENT_FLAG_NONE, value1_name, \ + static_cast<int>(value1_val), value2_name, \ + static_cast<int>(value2_val)) +#define TRACE_COPY_COUNTER2(category_group, name, value1_name, value1_val, \ + value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_COUNTER, category_group, name, \ + TRACE_EVENT_FLAG_COPY, value1_name, \ + static_cast<int>(value1_val), value2_name, \ + static_cast<int>(value2_val)) + +// Similar to TRACE_COUNTERx, but with a custom |timestamp| provided. +#define TRACE_COUNTER_WITH_TIMESTAMP1(category_group, name, timestamp, value) \ + INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \ + TRACE_EVENT_PHASE_COUNTER, category_group, name, timestamp, \ + TRACE_EVENT_FLAG_NONE, "value", static_cast<int>(value)) + +#define TRACE_COUNTER_WITH_TIMESTAMP2(category_group, name, timestamp, \ + value1_name, value1_val, value2_name, \ + value2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \ + TRACE_EVENT_PHASE_COUNTER, category_group, name, timestamp, \ + TRACE_EVENT_FLAG_NONE, value1_name, static_cast<int>(value1_val), \ + value2_name, static_cast<int>(value2_val)) + +// Records the value of a counter called "name" immediately. Value +// must be representable as a 32 bit integer. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to disambiguate counters with the same name. It must either +// be a pointer or an integer value up to 64 bits. If it's a pointer, the bits +// will be xored with a hash of the process ID so that the same pointer on +// two different processes will not collide. +#define TRACE_COUNTER_ID1(category_group, name, id, value) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category_group, \ + name, id, TRACE_EVENT_FLAG_NONE, "value", \ + static_cast<int>(value)) +#define TRACE_COPY_COUNTER_ID1(category_group, name, id, value) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category_group, \ + name, id, TRACE_EVENT_FLAG_COPY, "value", \ + static_cast<int>(value)) + +// Records the values of a multi-parted counter called "name" immediately. +// The UI will treat value1 and value2 as parts of a whole, displaying their +// values as a stacked-bar chart. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to disambiguate counters with the same name. It must either +// be a pointer or an integer value up to 64 bits. If it's a pointer, the bits +// will be xored with a hash of the process ID so that the same pointer on +// two different processes will not collide. +#define TRACE_COUNTER_ID2(category_group, name, id, value1_name, value1_val, \ + value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category_group, \ + name, id, TRACE_EVENT_FLAG_NONE, \ + value1_name, static_cast<int>(value1_val), \ + value2_name, static_cast<int>(value2_val)) +#define TRACE_COPY_COUNTER_ID2(category_group, name, id, value1_name, \ + value1_val, value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_COUNTER, category_group, \ + name, id, TRACE_EVENT_FLAG_COPY, \ + value1_name, static_cast<int>(value1_val), \ + value2_name, static_cast<int>(value2_val)) + +// TRACE_EVENT_SAMPLE_* events are injected by the sampling profiler. +#define TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP0(category_group, name, \ + thread_id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_SAMPLE, category_group, name, 0, thread_id, timestamp, \ + TRACE_EVENT_FLAG_NONE) + +#define TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP1( \ + category_group, name, thread_id, timestamp, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_SAMPLE, category_group, name, 0, thread_id, timestamp, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) + +#define TRACE_EVENT_SAMPLE_WITH_TID_AND_TIMESTAMP2(category_group, name, \ + thread_id, timestamp, \ + arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_SAMPLE, category_group, name, 0, thread_id, timestamp, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val) + +#define TRACE_EVENT_SAMPLE_WITH_ID1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_SAMPLE, category_group, \ + name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ + arg1_val) + +// ASYNC_STEP_* APIs should be only used by legacy code. New code should +// consider using NESTABLE_ASYNC_* APIs to describe substeps within an async +// event. +// Records a single ASYNC_BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to match the ASYNC_BEGIN event with the ASYNC_END event. ASYNC +// events are considered to match if their category_group, name and id values +// all match. |id| must either be a pointer or an integer value up to 64 bits. +// If it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +// +// An asynchronous operation can consist of multiple phases. The first phase is +// defined by the ASYNC_BEGIN calls. Additional phases can be defined using the +// ASYNC_STEP_INTO or ASYNC_STEP_PAST macros. The ASYNC_STEP_INTO macro will +// annotate the block following the call. The ASYNC_STEP_PAST macro will +// annotate the block prior to the call. Note that any particular event must use +// only STEP_INTO or STEP_PAST macros; they can not mix and match. When the +// operation completes, call ASYNC_END. +// +// An ASYNC trace typically occurs on a single thread (if not, they will only be +// drawn on the thread defined in the ASYNC_BEGIN event), but all events in that +// operation must use the same |name| and |id|. Each step can have its own +// args. +#define TRACE_EVENT_ASYNC_BEGIN0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_ASYNC_BEGIN1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_ASYNC_BEGIN2(category_group, name, id, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_ASYNC_BEGIN0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_ASYNC_BEGIN1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category_group, name, id, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, arg2_val) + +// Similar to TRACE_EVENT_ASYNC_BEGINx but with a custom |at| timestamp +// provided. +#define TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, name, id, \ + timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1( \ + category_group, name, id, timestamp, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val) +#define TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP2(category_group, name, id, \ + timestamp, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, name, id, \ + timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_COPY) + +// Records a single ASYNC_STEP_INTO event for |step| immediately. If the +// category is not enabled, then this does nothing. The |name| and |id| must +// match the ASYNC_BEGIN event above. The |step| param identifies this step +// within the async event. This should be called at the beginning of the next +// phase of an asynchronous operation. The ASYNC_BEGIN event must not have any +// ASYNC_STEP_PAST events. +#define TRACE_EVENT_ASYNC_STEP_INTO0(category_group, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, "step", step) +#define TRACE_EVENT_ASYNC_STEP_INTO1(category_group, name, id, step, \ + arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_ASYNC_STEP_INTO, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, "step", step, arg1_name, arg1_val) + +// Similar to TRACE_EVENT_ASYNC_STEP_INTOx but with a custom |at| timestamp +// provided. +#define TRACE_EVENT_ASYNC_STEP_INTO_WITH_TIMESTAMP0(category_group, name, id, \ + step, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_STEP_INTO, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \ + "step", step) + +// Records a single ASYNC_STEP_PAST event for |step| immediately. If the +// category is not enabled, then this does nothing. The |name| and |id| must +// match the ASYNC_BEGIN event above. The |step| param identifies this step +// within the async event. This should be called at the beginning of the next +// phase of an asynchronous operation. The ASYNC_BEGIN event must not have any +// ASYNC_STEP_INTO events. +#define TRACE_EVENT_ASYNC_STEP_PAST0(category_group, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_PAST, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, "step", step) +#define TRACE_EVENT_ASYNC_STEP_PAST1(category_group, name, id, step, \ + arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_ASYNC_STEP_PAST, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, "step", step, arg1_name, arg1_val) + +// Records a single ASYNC_END event for "name" immediately. If the category +// is not enabled, then this does nothing. +#define TRACE_EVENT_ASYNC_END0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_ASYNC_END1(category_group, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_ASYNC_END2(category_group, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_ASYNC_END0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_ASYNC_END1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_ASYNC_END2(category_group, name, id, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, arg2_val) + +// Similar to TRACE_EVENT_ASYNC_ENDx but with a custom |at| timestamp provided. +#define TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP0(category_group, name, id, \ + timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP1(category_group, name, id, \ + timestamp, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val) +#define TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP2(category_group, name, id, \ + timestamp, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \ + arg1_name, arg1_val, arg2_name, arg2_val) + +// NESTABLE_ASYNC_* APIs are used to describe an async operation, which can +// be nested within a NESTABLE_ASYNC event and/or have inner NESTABLE_ASYNC +// events. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - A pair of NESTABLE_ASYNC_BEGIN event and NESTABLE_ASYNC_END event is +// considered as a match if their category_group, name and id all match. +// - |id| must either be a pointer or an integer value up to 64 bits. +// If it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +// - |id| is used to match a child NESTABLE_ASYNC event with its parent +// NESTABLE_ASYNC event. Therefore, events in the same nested event tree must +// be logged using the same id and category_group. +// +// Unmatched NESTABLE_ASYNC_END event will be parsed as an event that starts +// at the first NESTABLE_ASYNC event of that id, and unmatched +// NESTABLE_ASYNC_BEGIN event will be parsed as an event that ends at the last +// NESTABLE_ASYNC event of that id. Corresponding warning messages for +// unmatched events will be shown in the analysis view. + +// Records a single NESTABLE_ASYNC_BEGIN event called "name" immediately, with +// 0, 1 or 2 associated arguments. If the category is not enabled, then this +// does nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(category_group, name, id, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val) +// Records a single NESTABLE_ASYNC_END event called "name" immediately, with 0 +// or 2 associated arguments. If the category is not enabled, then this does +// nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_END0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE) +// Records a single NESTABLE_ASYNC_END event called "name" immediately, with 1 +// associated argument. If the category is not enabled, then this does nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_END1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_NESTABLE_ASYNC_END2(category_group, name, id, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val) + +// Records a single NESTABLE_ASYNC_INSTANT event called "name" immediately, +// with none, one or two associated argument. If the category is not enabled, +// then this does nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE) + +#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT1(category_group, name, id, \ + arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) + +#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT2( \ + category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val) + +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TTS2( \ + category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_FLAG_ASYNC_TTS | TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ + arg2_name, arg2_val) +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TTS2( \ + category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_FLAG_ASYNC_TTS | TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, \ + arg2_name, arg2_val) + +// Similar to TRACE_EVENT_NESTABLE_ASYNC_{BEGIN,END}x but with a custom +// |timestamp| provided. +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, name, \ + id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE) + +#define TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(category_group, name, \ + id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE) + +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( \ + category_group, name, id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0( \ + category_group, name, id, timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_COPY) + +// Records a single FLOW_BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to match the FLOW_BEGIN event with the FLOW_END event. FLOW +// events are considered to match if their category_group, name and id values +// all match. |id| must either be a pointer or an integer value up to 64 bits. +// If it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +// FLOW events are different from ASYNC events in how they are drawn by the +// tracing UI. A FLOW defines asynchronous data flow, such as posting a task +// (FLOW_BEGIN) and later executing that task (FLOW_END). Expect FLOWs to be +// drawn as lines or arrows from FLOW_BEGIN scopes to FLOW_END scopes. Similar +// to ASYNC, a FLOW can consist of multiple phases. The first phase is defined +// by the FLOW_BEGIN calls. Additional phases can be defined using the FLOW_STEP +// macros. When the operation completes, call FLOW_END. An async operation can +// span threads and processes, but all events in that operation must use the +// same |name| and |id|. Each event can have its own args. +#define TRACE_EVENT_FLOW_BEGIN0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_FLOW_BEGIN1(category_group, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val) +#define TRACE_EVENT_FLOW_BEGIN2(category_group, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_FLOW_BEGIN0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_FLOW_BEGIN1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_FLOW_BEGIN2(category_group, name, id, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, id, \ + TRACE_EVENT_FLAG_COPY, arg1_name, arg1_val, arg2_name, arg2_val) + +// Records a single FLOW_STEP event for |step| immediately. If the category +// is not enabled, then this does nothing. The |name| and |id| must match the +// FLOW_BEGIN event above. The |step| param identifies this step within the +// async event. This should be called at the beginning of the next phase of an +// asynchronous operation. +#define TRACE_EVENT_FLOW_STEP0(category_group, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, "step", step) +#define TRACE_EVENT_FLOW_STEP1(category_group, name, id, step, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE, "step", step, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_FLOW_STEP0(category_group, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_COPY, "step", step) +#define TRACE_EVENT_COPY_FLOW_STEP1(category_group, name, id, step, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, id, \ + TRACE_EVENT_FLAG_COPY, "step", step, arg1_name, arg1_val) + +// Records a single FLOW_END event for "name" immediately. If the category +// is not enabled, then this does nothing. +#define TRACE_EVENT_FLOW_END0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \ + name, id, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \ + name, id, \ + TRACE_EVENT_FLAG_BIND_TO_ENCLOSING) +#define TRACE_EVENT_FLOW_END1(category_group, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \ + name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ + arg1_val) +#define TRACE_EVENT_FLOW_END2(category_group, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \ + name, id, TRACE_EVENT_FLAG_NONE, arg1_name, \ + arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_FLOW_END0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \ + name, id, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_FLOW_END1(category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \ + name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ + arg1_val) +#define TRACE_EVENT_COPY_FLOW_END2(category_group, name, id, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, category_group, \ + name, id, TRACE_EVENT_FLAG_COPY, arg1_name, \ + arg1_val, arg2_name, arg2_val) + +// Special trace event macro to trace task execution with the location where it +// was posted from. +#define TRACE_TASK_EXECUTION(run_function, task) \ + INTERNAL_TRACE_TASK_EXECUTION(run_function, task) + +// TRACE_EVENT_METADATA* events are information related to other +// injected events, not events in their own right. +#define TRACE_EVENT_METADATA1(category_group, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_METADATA_ADD(category_group, name, arg1_name, arg1_val) + +// Records a clock sync event. +#define TRACE_EVENT_CLOCK_SYNC_RECEIVER(sync_id) \ + INTERNAL_TRACE_EVENT_ADD( \ + TRACE_EVENT_PHASE_CLOCK_SYNC, "__metadata", "clock_sync", \ + TRACE_EVENT_FLAG_NONE, "sync_id", sync_id) +#define TRACE_EVENT_CLOCK_SYNC_ISSUER(sync_id, issue_ts, issue_end_ts) \ + INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP( \ + TRACE_EVENT_PHASE_CLOCK_SYNC, "__metadata", "clock_sync", \ + issue_end_ts, TRACE_EVENT_FLAG_NONE, \ + "sync_id", sync_id, "issue_ts", issue_ts) + +// Macros to track the life time and value of arbitrary client objects. +// See also TraceTrackableObject. +#define TRACE_EVENT_OBJECT_CREATED_WITH_ID(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_CREATE_OBJECT, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE) + +#define TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(category_group, name, id, \ + snapshot) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_SNAPSHOT_OBJECT, category_group, name, \ + id, TRACE_EVENT_FLAG_NONE, "snapshot", snapshot) + +#define TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP( \ + category_group, name, id, timestamp, snapshot) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + TRACE_EVENT_PHASE_SNAPSHOT_OBJECT, category_group, name, \ + id, TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \ + "snapshot", snapshot) + +#define TRACE_EVENT_OBJECT_DELETED_WITH_ID(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_DELETE_OBJECT, category_group, name, id, \ + TRACE_EVENT_FLAG_NONE) + +// Records entering and leaving trace event contexts. |category_group| and +// |name| specify the context category and type. |context| is a +// snapshotted context object id. +#define TRACE_EVENT_ENTER_CONTEXT(category_group, name, context) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_ENTER_CONTEXT, category_group, name, context, \ + TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_LEAVE_CONTEXT(category_group, name, context) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + TRACE_EVENT_PHASE_LEAVE_CONTEXT, category_group, name, context, \ + TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_SCOPED_CONTEXT(category_group, name, context) \ + INTERNAL_TRACE_EVENT_SCOPED_CONTEXT(category_group, name, context) + +// Macro to specify that two trace IDs are identical. For example, +// TRACE_LINK_IDS( +// "category", "name", +// TRACE_ID_WITH_SCOPE("net::URLRequest", 0x1000), +// TRACE_ID_WITH_SCOPE("blink::ResourceFetcher::FetchRequest", 0x2000)) +// tells the trace consumer that events with ID ("net::URLRequest", 0x1000) from +// the current process have the same ID as events with ID +// ("blink::ResourceFetcher::FetchRequest", 0x2000). +#define TRACE_LINK_IDS(category_group, name, id, linked_id) \ + INTERNAL_TRACE_EVENT_ADD_LINK_IDS(category_group, name, id, linked_id); + +// Macro to efficiently determine if a given category group is enabled. +#define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ + if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ + *ret = true; \ + } else { \ + *ret = false; \ + } \ + } while (0) + +// Macro to explicitly warm up a given category group. This could be useful in +// cases where we want to initialize a category group before any trace events +// for that category group is reported. For example, to have a category group +// always show up in the "record categories" list for manually selecting +// settings in about://tracing. +#define TRACE_EVENT_WARMUP_CATEGORY(category_group) \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group) + +// Macro to efficiently determine, through polling, if a new trace has begun. +#define TRACE_EVENT_IS_NEW_TRACE(ret) \ + do { \ + static int INTERNAL_TRACE_EVENT_UID(lastRecordingNumber) = 0; \ + int num_traces_recorded = TRACE_EVENT_API_GET_NUM_TRACES_RECORDED(); \ + if (num_traces_recorded != -1 && \ + num_traces_recorded != \ + INTERNAL_TRACE_EVENT_UID(lastRecordingNumber)) { \ + INTERNAL_TRACE_EVENT_UID(lastRecordingNumber) = num_traces_recorded; \ + *ret = true; \ + } else { \ + *ret = false; \ + } \ + } while (0) + +// Notes regarding the following definitions: +// New values can be added and propagated to third party libraries, but existing +// definitions must never be changed, because third party libraries may use old +// definitions. + +// Phase indicates the nature of an event entry. E.g. part of a begin/end pair. +#define TRACE_EVENT_PHASE_BEGIN ('B') +#define TRACE_EVENT_PHASE_END ('E') +#define TRACE_EVENT_PHASE_COMPLETE ('X') +#define TRACE_EVENT_PHASE_INSTANT ('I') +#define TRACE_EVENT_PHASE_ASYNC_BEGIN ('S') +#define TRACE_EVENT_PHASE_ASYNC_STEP_INTO ('T') +#define TRACE_EVENT_PHASE_ASYNC_STEP_PAST ('p') +#define TRACE_EVENT_PHASE_ASYNC_END ('F') +#define TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN ('b') +#define TRACE_EVENT_PHASE_NESTABLE_ASYNC_END ('e') +#define TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT ('n') +#define TRACE_EVENT_PHASE_FLOW_BEGIN ('s') +#define TRACE_EVENT_PHASE_FLOW_STEP ('t') +#define TRACE_EVENT_PHASE_FLOW_END ('f') +#define TRACE_EVENT_PHASE_METADATA ('M') +#define TRACE_EVENT_PHASE_COUNTER ('C') +#define TRACE_EVENT_PHASE_SAMPLE ('P') +#define TRACE_EVENT_PHASE_CREATE_OBJECT ('N') +#define TRACE_EVENT_PHASE_SNAPSHOT_OBJECT ('O') +#define TRACE_EVENT_PHASE_DELETE_OBJECT ('D') +#define TRACE_EVENT_PHASE_MEMORY_DUMP ('v') +#define TRACE_EVENT_PHASE_MARK ('R') +#define TRACE_EVENT_PHASE_CLOCK_SYNC ('c') +#define TRACE_EVENT_PHASE_ENTER_CONTEXT ('(') +#define TRACE_EVENT_PHASE_LEAVE_CONTEXT (')') +#define TRACE_EVENT_PHASE_LINK_IDS ('=') + +// Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT. +#define TRACE_EVENT_FLAG_NONE (static_cast<unsigned int>(0)) +#define TRACE_EVENT_FLAG_COPY (static_cast<unsigned int>(1 << 0)) +#define TRACE_EVENT_FLAG_HAS_ID (static_cast<unsigned int>(1 << 1)) +// TODO(crbug.com/639003): Free this bit after ID mangling is deprecated. +#define TRACE_EVENT_FLAG_MANGLE_ID (static_cast<unsigned int>(1 << 2)) +#define TRACE_EVENT_FLAG_SCOPE_OFFSET (static_cast<unsigned int>(1 << 3)) +#define TRACE_EVENT_FLAG_SCOPE_EXTRA (static_cast<unsigned int>(1 << 4)) +#define TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP (static_cast<unsigned int>(1 << 5)) +#define TRACE_EVENT_FLAG_ASYNC_TTS (static_cast<unsigned int>(1 << 6)) +#define TRACE_EVENT_FLAG_BIND_TO_ENCLOSING (static_cast<unsigned int>(1 << 7)) +#define TRACE_EVENT_FLAG_FLOW_IN (static_cast<unsigned int>(1 << 8)) +#define TRACE_EVENT_FLAG_FLOW_OUT (static_cast<unsigned int>(1 << 9)) +#define TRACE_EVENT_FLAG_HAS_CONTEXT_ID (static_cast<unsigned int>(1 << 10)) +#define TRACE_EVENT_FLAG_HAS_PROCESS_ID (static_cast<unsigned int>(1 << 11)) +#define TRACE_EVENT_FLAG_HAS_LOCAL_ID (static_cast<unsigned int>(1 << 12)) +#define TRACE_EVENT_FLAG_HAS_GLOBAL_ID (static_cast<unsigned int>(1 << 13)) + +#define TRACE_EVENT_FLAG_SCOPE_MASK \ + (static_cast<unsigned int>(TRACE_EVENT_FLAG_SCOPE_OFFSET | \ + TRACE_EVENT_FLAG_SCOPE_EXTRA)) + +// Type values for identifying types in the TraceValue union. +#define TRACE_VALUE_TYPE_BOOL (static_cast<unsigned char>(1)) +#define TRACE_VALUE_TYPE_UINT (static_cast<unsigned char>(2)) +#define TRACE_VALUE_TYPE_INT (static_cast<unsigned char>(3)) +#define TRACE_VALUE_TYPE_DOUBLE (static_cast<unsigned char>(4)) +#define TRACE_VALUE_TYPE_POINTER (static_cast<unsigned char>(5)) +#define TRACE_VALUE_TYPE_STRING (static_cast<unsigned char>(6)) +#define TRACE_VALUE_TYPE_COPY_STRING (static_cast<unsigned char>(7)) +#define TRACE_VALUE_TYPE_CONVERTABLE (static_cast<unsigned char>(8)) + +// Enum reflecting the scope of an INSTANT event. Must fit within +// TRACE_EVENT_FLAG_SCOPE_MASK. +#define TRACE_EVENT_SCOPE_GLOBAL (static_cast<unsigned char>(0 << 3)) +#define TRACE_EVENT_SCOPE_PROCESS (static_cast<unsigned char>(1 << 3)) +#define TRACE_EVENT_SCOPE_THREAD (static_cast<unsigned char>(2 << 3)) + +#define TRACE_EVENT_SCOPE_NAME_GLOBAL ('g') +#define TRACE_EVENT_SCOPE_NAME_PROCESS ('p') +#define TRACE_EVENT_SCOPE_NAME_THREAD ('t') \ No newline at end of file
diff --git a/src/v8/src/v8.gyp b/src/v8/src/v8.gyp index ef55e7c..6f9b36f 100644 --- a/src/v8/src/v8.gyp +++ b/src/v8/src/v8.gyp
@@ -36,7 +36,21 @@ 'v8_experimental_extra_library_files%': [], 'mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)mksnapshot<(EXECUTABLE_SUFFIX)', 'v8_os_page_size%': 0, + + 'v8_use_snapshot': 0, + 'v8_optimized_debug': 0, + 'v8_use_external_startup_data': 0, + # TODO: Enable i18n support. + 'v8_enable_i18n_support': 0, }, + # TODO: Remove after starboardization. + 'target_defaults': { + 'defines': [ + 'V8_OS_POSIX=1', + 'V8_OS_LINUX=1', + 'V8_TRAP_HANDLER_SUPPORTED=1', + ], + }, 'includes': ['../gypfiles/toolchain.gypi', '../gypfiles/features.gypi', 'inspector/inspector.gypi'], 'targets': [ { @@ -1708,7 +1722,8 @@ 'third_party/valgrind/valgrind.h', ], }], - ['v8_target_arch=="x64" and OS=="linux"', { + # TODO: Remove after starboardization. + ['v8_target_arch=="x64" and (OS=="linux" or OS=="starboard")', { 'sources': ['trap-handler/handler-inside.cc'] }], ['v8_target_arch=="ppc" or v8_target_arch=="ppc64"', { @@ -1935,7 +1950,8 @@ ], }, }], - ['OS=="linux"', { + # TODO: Remove after starboardization. + ['OS=="linux" or OS=="starboard"', { 'link_settings': { 'libraries': [ '-ldl',
diff --git a/src/v8/third_party/inspector_protocol/templates/TypeBuilder_h.template b/src/v8/third_party/inspector_protocol/templates/TypeBuilder_h.template index 81dd7f2..f6264b8 100644 --- a/src/v8/third_party/inspector_protocol/templates/TypeBuilder_h.template +++ b/src/v8/third_party/inspector_protocol/templates/TypeBuilder_h.template
@@ -113,7 +113,8 @@ {% set count = 0 %} {% for property in type.properties %} {% if not(property.optional) %} - {% set count = count + 1 %} + {# "count = count + 1" does not work with our version of jinja, use the loop index instead. #} + {% set count = loop.index + 1 %} {{property.name | to_title_case}}Set = 1 << {{count}}, {% endif %} {% endfor %}