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();
-