Import Cobalt 8.21796
diff --git a/src/base/bind_helpers.h b/src/base/bind_helpers.h
index b60a56e..17b2f55 100644
--- a/src/base/bind_helpers.h
+++ b/src/base/bind_helpers.h
@@ -255,7 +255,7 @@
static Yes& Check(...);
public:
- static const bool value = sizeof(Check<Base>(0)) == sizeof(Yes);
+ enum { value = sizeof(Check<Base>(0)) == sizeof(Yes) };
};
// Helpers to assert that arguments of a recounted type are bound with a
diff --git a/src/base/file_util_unittest.cc b/src/base/file_util_unittest.cc
index 81fdcc7..66595fc 100644
--- a/src/base/file_util_unittest.cc
+++ b/src/base/file_util_unittest.cc
@@ -33,6 +33,9 @@
#if defined(OS_WIN)
#include "base/win/scoped_handle.h"
#endif
+#if defined(OS_STARBOARD)
+#include "starboard/string.h"
+#endif
// This macro helps avoid wrapped lines in the test structs.
#define FPL(x) FILE_PATH_LITERAL(x)
diff --git a/src/base/string_number_conversions.cc b/src/base/string_number_conversions.cc
index 1aafc4e..18afd5e 100644
--- a/src/base/string_number_conversions.cc
+++ b/src/base/string_number_conversions.cc
@@ -300,6 +300,13 @@
typedef BaseHexIteratorRangeToIntTraits<StringPiece::const_iterator>
HexIteratorRangeToIntTraits;
+template <typename ITERATOR>
+class BaseHexIteratorRangeToUIntTraits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, unsigned int, 16> {};
+
+typedef BaseHexIteratorRangeToUIntTraits<StringPiece::const_iterator>
+ HexIteratorRangeToUIntTraits;
+
template<typename STR>
bool HexStringToBytesT(const STR& input, std::vector<uint8>* output) {
DCHECK_EQ(output->size(), 0u);
@@ -419,6 +426,11 @@
input.begin(), input.end(), output);
}
+bool HexStringToUInt(const StringPiece& input, unsigned int* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToUIntTraits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
bool HexStringToBytes(const std::string& input, std::vector<uint8>* output) {
return HexStringToBytesT(input, output);
}
diff --git a/src/base/string_number_conversions.h b/src/base/string_number_conversions.h
index 7b7cfe1..e1cd1e6 100644
--- a/src/base/string_number_conversions.h
+++ b/src/base/string_number_conversions.h
@@ -96,6 +96,8 @@
// Best effort conversion, see StringToInt above for restrictions.
BASE_EXPORT bool HexStringToInt(const StringPiece& input, int* output);
+BASE_EXPORT bool HexStringToUInt(const StringPiece& input,
+ unsigned int* output);
// Similar to the previous functions, except that output is a vector of bytes.
// |*output| will contain as many bytes as were successfully parsed prior to the
diff --git a/src/base/string_util.cc b/src/base/string_util.cc
index 1acf1be..0f7f151 100644
--- a/src/base/string_util.cc
+++ b/src/base/string_util.cc
@@ -754,13 +754,26 @@
if (parts.empty())
return STR();
- STR result(parts[0]);
+ std::size_t expected_result_length = 0;
+
typename std::vector<STR>::const_iterator iter = parts.begin();
- ++iter;
for (; iter != parts.end(); ++iter) {
+ expected_result_length += iter->size();
+ }
+ expected_result_length += sep.size() * (parts.size() - 1);
+
+ STR result;
+ result.reserve(expected_result_length);
+
+ iter = parts.begin();
+ result += *iter;
+ ++iter;
+
+ while(iter != parts.end()) {
result += sep;
result += *iter;
+ ++iter;
}
return result;
diff --git a/src/base/time.h b/src/base/time.h
index 9fde0e4..5640a78 100644
--- a/src/base/time.h
+++ b/src/base/time.h
@@ -25,6 +25,8 @@
#include <time.h>
+#include <ostream>
+
#include "base/atomicops.h"
#include "base/base_export.h"
#include "base/basictypes.h"
@@ -98,6 +100,18 @@
return delta_;
}
+ // Returns the magnitude (absolute value) of this TimeDelta.
+ TimeDelta magnitude() const {
+ // Some toolchains provide an incomplete C++11 implementation and lack an
+ // int64_t overload for std::abs(). The following is a simple branchless
+ // implementation:
+ const int64_t mask = delta_ >> (sizeof(delta_) * 8 - 1);
+ return TimeDelta((delta_ + mask) ^ mask);
+ }
+
+ // Returns true if the time delta is zero.
+ bool is_zero() const { return delta_ == 0; }
+
// Returns true if the time delta is the maximum time delta.
bool is_max() const {
return delta_ == std::numeric_limits<int64>::max();
@@ -214,6 +228,10 @@
return TimeDelta(a * td.delta_);
}
+inline std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
+ return os << time_delta.InSecondsF() << " s";
+}
+
// Time -----------------------------------------------------------------------
// Represents a wall clock time.
diff --git a/src/cobalt/accessibility/accessibility.gyp b/src/cobalt/accessibility/accessibility.gyp
index c73c56e..149d26f 100644
--- a/src/cobalt/accessibility/accessibility.gyp
+++ b/src/cobalt/accessibility/accessibility.gyp
@@ -17,6 +17,8 @@
'target_name': 'accessibility',
'type': 'static_library',
'sources': [
+ 'internal/live_region.cc',
+ 'internal/live_region.h',
'internal/text_alternative_helper.cc',
'internal/text_alternative_helper.h',
'focus_observer.cc',
diff --git a/src/cobalt/accessibility/accessibility_test.gyp b/src/cobalt/accessibility/accessibility_test.gyp
index 2c4c7b8..e28a8f1 100644
--- a/src/cobalt/accessibility/accessibility_test.gyp
+++ b/src/cobalt/accessibility/accessibility_test.gyp
@@ -18,6 +18,7 @@
'type': '<(gtest_target_type)',
'sources': [
'text_alternative_tests.cc',
+ 'internal/live_region_test.cc',
'internal/text_alternative_helper_test.cc',
],
'dependencies': [
diff --git a/src/cobalt/accessibility/internal/live_region.cc b/src/cobalt/accessibility/internal/live_region.cc
new file mode 100644
index 0000000..fb9cb68
--- /dev/null
+++ b/src/cobalt/accessibility/internal/live_region.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/accessibility/internal/live_region.h"
+
+#include <bitset>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/string_split.h"
+#include "cobalt/base/tokens.h"
+
+namespace cobalt {
+namespace accessibility {
+namespace internal {
+namespace {
+typedef std::bitset<8> RelevantMutationsBitset;
+
+// Default value for the aria-relevant property per the spec.
+// https://www.w3.org/TR/wai-aria/states_and_properties#aria-relevant
+const char kDefaultAriaRelevantValue[] = "additions text";
+
+bool HasValidLiveRegionProperty(const scoped_refptr<dom::Element>& element) {
+ std::string aria_live_attribute =
+ element->GetAttribute(base::Tokens::aria_live().c_str()).value_or("");
+
+ return aria_live_attribute == base::Tokens::assertive() ||
+ aria_live_attribute == base::Tokens::polite();
+}
+
+RelevantMutationsBitset GetRelevantMutations(
+ const scoped_refptr<dom::Element>& element) {
+ RelevantMutationsBitset bitset;
+
+ std::string aria_relevant_attribute =
+ element->GetAttribute(base::Tokens::aria_relevant().c_str())
+ .value_or(kDefaultAriaRelevantValue);
+ std::vector<std::string> tokens;
+ base::SplitStringAlongWhitespace(aria_relevant_attribute, &tokens);
+ for (size_t i = 0; i < tokens.size(); ++i) {
+ if (tokens[i] == base::Tokens::additions()) {
+ bitset.set(LiveRegion::kMutationTypeAddition);
+ } else if (tokens[i] == base::Tokens::removals()) {
+ bitset.set(LiveRegion::kMutationTypeRemoval);
+ } else if (tokens[i] == base::Tokens::text()) {
+ bitset.set(LiveRegion::kMutationTypeText);
+ } else if (tokens[i] == base::Tokens::all()) {
+ bitset.set();
+ } else {
+ DLOG(WARNING) << "Unexpected value for aria-relevant attribute: "
+ << tokens[i];
+ }
+ }
+ return bitset;
+}
+} // namespace
+
+// static
+scoped_ptr<LiveRegion> LiveRegion::GetLiveRegionForElement(
+ const scoped_refptr<dom::Element>& element) {
+ if (!element) {
+ return make_scoped_ptr<LiveRegion>(NULL);
+ }
+ if (HasValidLiveRegionProperty(element)) {
+ return make_scoped_ptr(new LiveRegion(element));
+ }
+ return GetLiveRegionForElement(element->parent_element());
+}
+
+bool LiveRegion::IsAssertive() const {
+ base::optional<std::string> aria_live_attribute =
+ root_->GetAttribute(base::Tokens::aria_live().c_str());
+ if (!aria_live_attribute) {
+ NOTREACHED();
+ return false;
+ }
+ return *aria_live_attribute == base::Tokens::assertive();
+}
+
+bool LiveRegion::IsMutationRelevant(MutationType mutation_type) const {
+ RelevantMutationsBitset bitset = GetRelevantMutations(root_);
+ return bitset.test(mutation_type);
+}
+
+bool LiveRegion::IsAtomic(const scoped_refptr<dom::Element>& element) const {
+ // Stop searching if we go past the live region's root node. The default is
+ // non-atomic.
+ if (!element || element == root_->parent_element()) {
+ return false;
+ }
+
+ // Search ancestors of the changed element to determine if this change is
+ // atomic or not, per the algorithm described in the spec.
+ // https://www.w3.org/TR/wai-aria/states_and_properties#aria-atomic
+ base::optional<std::string> aria_atomic_attribute =
+ element->GetAttribute(base::Tokens::aria_atomic().c_str());
+ if (aria_atomic_attribute) {
+ if (*aria_atomic_attribute == base::Tokens::true_token()) {
+ return true;
+ } else if (*aria_atomic_attribute == base::Tokens::false_token()) {
+ return false;
+ } else {
+ DLOG(WARNING) << "Unexpected token for aria-atomic: "
+ << *aria_atomic_attribute;
+ }
+ }
+ return IsAtomic(element->parent_element());
+}
+
+} // namespace internal
+} // namespace accessibility
+} // namespace cobalt
diff --git a/src/cobalt/accessibility/internal/live_region.h b/src/cobalt/accessibility/internal/live_region.h
new file mode 100644
index 0000000..d39ca2b
--- /dev/null
+++ b/src/cobalt/accessibility/internal/live_region.h
@@ -0,0 +1,84 @@
+/*
+ * 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_ACCESSIBILITY_INTERNAL_LIVE_REGION_H_
+#define COBALT_ACCESSIBILITY_INTERNAL_LIVE_REGION_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/dom/element.h"
+
+namespace cobalt {
+namespace accessibility {
+namespace internal {
+// Collection of functions to support the implementation of wai-aria Live
+// Regions:
+// https://www.w3.org/TR/2010/WD-wai-aria-20100916/terms#def_liveregion
+class LiveRegion {
+ public:
+ // Types of mutation to the DOM subtree that might cause a live region to be
+ // updated, corresponding to the possible values of aria-relevant as
+ // described in:
+ // https://www.w3.org/TR/2011/CR-wai-aria-20110118/states_and_properties#aria-relevant
+ enum MutationType {
+ kMutationTypeAddition = 1, // corresponds to "additions"
+ kMutationTypeRemoval, // corresponds to "removals"
+ kMutationTypeText, // corresponds to "text"
+ };
+
+ // Returns a LiveRegion instance describing the live region that this element
+ // is a member of, or NULL if this element is not part of a live region.
+ //
+ // Searches the element's ancestors looking for the first element with a
+ // valid aria-live attribute.
+ static scoped_ptr<LiveRegion> GetLiveRegionForElement(
+ const scoped_refptr<dom::Element>& element);
+
+ // Returns true if the value of aria-live is "assertive".
+ // https://www.w3.org/TR/2011/CR-wai-aria-20110118/states_and_properties#aria-live
+ bool IsAssertive() const;
+
+ // Returns true if this live region should be updated in response to the
+ // specified type of change, based on the value of this element's
+ // aria-relevant property.
+ // https://www.w3.org/TR/2011/CR-wai-aria-20110118/states_and_properties#aria-relevant
+ bool IsMutationRelevant(MutationType mutation_type) const;
+
+ // Returns true if changes to this element should result in an atomic update
+ // to its live region, according to the value of the aria-atomic property set
+ // on this element or its ancestors, up to and including the live region root.
+ //
+ // https://www.w3.org/TR/2011/CR-wai-aria-20110118/states_and_properties#aria-atomic
+ bool IsAtomic(const scoped_refptr<dom::Element>& element) const;
+
+ // Returns the root of the live region (i.e.) the element that has the
+ // aria-live property set on it.
+ const scoped_refptr<dom::Element>& root() { return root_; }
+
+ private:
+ explicit LiveRegion(const scoped_refptr<dom::Element>& root) : root_(root) {}
+
+ // The root element of the live region. That is, the element with a valid
+ // aria-live attribute.
+ scoped_refptr<dom::Element> root_;
+};
+
+} // namespace internal
+} // namespace accessibility
+} // namespace cobalt
+
+#endif // COBALT_ACCESSIBILITY_INTERNAL_LIVE_REGION_H_
diff --git a/src/cobalt/accessibility/internal/live_region_test.cc b/src/cobalt/accessibility/internal/live_region_test.cc
new file mode 100644
index 0000000..cfb9230
--- /dev/null
+++ b/src/cobalt/accessibility/internal/live_region_test.cc
@@ -0,0 +1,233 @@
+/*
+ * 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 "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/accessibility/internal/live_region.h"
+#include "cobalt/test/empty_document.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace accessibility {
+namespace internal {
+class LiveRegionTest : public ::testing::Test {
+ protected:
+ dom::Document* document() { return empty_document_.document(); }
+
+ scoped_refptr<dom::Element> CreateLiveRegion(const char* type) {
+ scoped_refptr<dom::Element> live_region = document()->CreateElement("div");
+ live_region->SetAttribute(base::Tokens::aria_live().c_str(), type);
+ return live_region;
+ }
+
+ private:
+ test::EmptyDocument empty_document_;
+};
+
+TEST_F(LiveRegionTest, GetLiveRegionRoot) {
+ scoped_ptr<LiveRegion> live_region;
+
+ scoped_refptr<dom::Element> live_region_element =
+ CreateLiveRegion(base::Tokens::polite().c_str());
+ ASSERT_TRUE(live_region_element);
+
+ // GetLiveRegionForElement with the live region root.
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_EQ(live_region->root(), live_region_element);
+
+ // GetLiveRegionForElement with a descendent node of a live region.
+ scoped_refptr<dom::Element> child = document()->CreateElement("div");
+ live_region_element->AppendChild(child);
+ live_region = LiveRegion::GetLiveRegionForElement(child);
+ ASSERT_TRUE(live_region);
+ EXPECT_EQ(live_region->root(), live_region_element);
+
+ // Element that is not in a live region.
+ scoped_refptr<dom::Element> not_live = document()->CreateElement("div");
+ live_region = LiveRegion::GetLiveRegionForElement(not_live);
+ EXPECT_FALSE(live_region);
+}
+
+TEST_F(LiveRegionTest, NestedLiveRegion) {
+ scoped_refptr<dom::Element> live_region_element =
+ CreateLiveRegion(base::Tokens::polite().c_str());
+ ASSERT_TRUE(live_region_element);
+
+ scoped_refptr<dom::Element> nested_region_element =
+ CreateLiveRegion(base::Tokens::polite().c_str());
+ ASSERT_TRUE(nested_region_element);
+ live_region_element->AppendChild(nested_region_element);
+
+ // The closest ancestor live region is this element's live region.
+ scoped_refptr<dom::Element> child = document()->CreateElement("div");
+ nested_region_element->AppendChild(child);
+ scoped_ptr<LiveRegion> live_region =
+ LiveRegion::GetLiveRegionForElement(child);
+ ASSERT_TRUE(live_region);
+ EXPECT_EQ(live_region->root(), nested_region_element);
+
+ // Ignore live regions that are not active.
+ nested_region_element->SetAttribute(base::Tokens::aria_live().c_str(),
+ base::Tokens::off().c_str());
+ live_region = LiveRegion::GetLiveRegionForElement(child);
+ ASSERT_TRUE(live_region);
+ EXPECT_EQ(live_region->root(), live_region_element);
+}
+
+TEST_F(LiveRegionTest, LiveRegionType) {
+ scoped_ptr<LiveRegion> live_region;
+
+ // aria-live="polite"
+ scoped_refptr<dom::Element> polite_element =
+ CreateLiveRegion(base::Tokens::polite().c_str());
+ ASSERT_TRUE(polite_element);
+ live_region = LiveRegion::GetLiveRegionForElement(polite_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_FALSE(live_region->IsAssertive());
+
+ // aria-live="assertive"
+ scoped_refptr<dom::Element> assertive_element =
+ CreateLiveRegion(base::Tokens::assertive().c_str());
+ ASSERT_TRUE(assertive_element);
+ live_region = LiveRegion::GetLiveRegionForElement(assertive_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_TRUE(live_region->IsAssertive());
+
+ // aria-live="off"
+ scoped_refptr<dom::Element> off_element =
+ CreateLiveRegion(base::Tokens::off().c_str());
+ ASSERT_TRUE(off_element);
+ live_region = LiveRegion::GetLiveRegionForElement(off_element);
+ EXPECT_FALSE(live_region);
+
+ // aria-live=<invalid token>
+ scoped_refptr<dom::Element> invalid_element = CreateLiveRegion("banana");
+ ASSERT_TRUE(invalid_element);
+ live_region = LiveRegion::GetLiveRegionForElement(invalid_element);
+ EXPECT_FALSE(live_region);
+}
+
+TEST_F(LiveRegionTest, IsMutationRelevant) {
+ scoped_refptr<dom::Element> live_region_element =
+ CreateLiveRegion(base::Tokens::polite().c_str());
+ ASSERT_TRUE(live_region_element);
+
+ // GetLiveRegionForElement with the live region root.
+ scoped_ptr<LiveRegion> live_region =
+ LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ // Default is that additions and text are relevant.
+ EXPECT_TRUE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+ EXPECT_TRUE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+ EXPECT_FALSE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+
+ // Only removals are relevant.
+ live_region_element->SetAttribute(base::Tokens::aria_relevant().c_str(),
+ "removals");
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_FALSE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+ EXPECT_FALSE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+ EXPECT_TRUE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+
+ // Removals and additions are relevant.
+ live_region_element->SetAttribute(base::Tokens::aria_relevant().c_str(),
+ "removals additions");
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_TRUE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+ EXPECT_FALSE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+ EXPECT_TRUE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+
+ // An invalid token.
+ live_region_element->SetAttribute(base::Tokens::aria_relevant().c_str(),
+ "text dog additions");
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_TRUE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+ EXPECT_TRUE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+ EXPECT_FALSE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+
+ // "all" token.
+ live_region_element->SetAttribute(base::Tokens::aria_relevant().c_str(),
+ "all");
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_TRUE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeAddition));
+ EXPECT_TRUE(live_region->IsMutationRelevant(LiveRegion::kMutationTypeText));
+ EXPECT_TRUE(
+ live_region->IsMutationRelevant(LiveRegion::kMutationTypeRemoval));
+}
+
+TEST_F(LiveRegionTest, IsAtomic) {
+ scoped_ptr<LiveRegion> live_region;
+
+ scoped_refptr<dom::Element> live_region_element =
+ CreateLiveRegion(base::Tokens::polite().c_str());
+ ASSERT_TRUE(live_region_element);
+
+ // Default is non-atomic.
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_FALSE(live_region->IsAtomic(live_region_element));
+
+ // aria-atomic=true
+ live_region_element->SetAttribute(base::Tokens::aria_atomic().c_str(),
+ "true");
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_TRUE(live_region->IsAtomic(live_region_element));
+
+ // aria-atomic=false
+ live_region_element->SetAttribute(base::Tokens::aria_atomic().c_str(),
+ "false");
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_FALSE(live_region->IsAtomic(live_region_element));
+
+ // Get the value of aria-atomic from ancestor elements.
+ live_region_element->SetAttribute(base::Tokens::aria_atomic().c_str(),
+ "true");
+ scoped_refptr<dom::Element> child = document()->CreateElement("div");
+ live_region_element->AppendChild(child);
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_TRUE(live_region->IsAtomic(child));
+ EXPECT_TRUE(live_region->IsAtomic(live_region_element));
+
+ // Stop checking ancestors on the first aria-atomic attribute.
+ live_region_element->SetAttribute(base::Tokens::aria_atomic().c_str(),
+ "true");
+ child->SetAttribute(base::Tokens::aria_atomic().c_str(), "false");
+ live_region = LiveRegion::GetLiveRegionForElement(live_region_element);
+ ASSERT_TRUE(live_region);
+ EXPECT_FALSE(live_region->IsAtomic(child));
+ EXPECT_TRUE(live_region->IsAtomic(live_region_element));
+}
+} // namespace internal
+} // namespace accessibility
+} // namespace cobalt
diff --git a/src/cobalt/accessibility/internal/text_alternative_helper_test.cc b/src/cobalt/accessibility/internal/text_alternative_helper_test.cc
index 528efa5..1686df8 100644
--- a/src/cobalt/accessibility/internal/text_alternative_helper_test.cc
+++ b/src/cobalt/accessibility/internal/text_alternative_helper_test.cc
@@ -17,11 +17,9 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "cobalt/accessibility/internal/text_alternative_helper.h"
-#include "cobalt/css_parser/parser.h"
-#include "cobalt/dom/document.h"
-#include "cobalt/dom/dom_stat_tracker.h"
#include "cobalt/dom/html_image_element.h"
#include "cobalt/dom/text.h"
+#include "cobalt/test/empty_document.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -30,20 +28,10 @@
namespace internal {
class TextAlternativeHelperTest : public ::testing::Test {
protected:
- TextAlternativeHelperTest()
- : css_parser_(css_parser::Parser::Create()),
- dom_stat_tracker_(new dom::DomStatTracker("TextAlternativeHelperTest")),
- html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- dom_stat_tracker_.get(), ""),
- document_(new dom::Document(&html_element_context_)) {}
+ dom::Document* document() { return empty_document_.document(); }
- scoped_ptr<css_parser::Parser> css_parser_;
- scoped_ptr<dom::DomStatTracker> dom_stat_tracker_;
- dom::HTMLElementContext html_element_context_;
- scoped_refptr<dom::Document> document_;
- scoped_refptr<dom::Element> element_;
TextAlternativeHelper text_alternative_helper_;
+ test::EmptyDocument empty_document_;
};
TEST_F(TextAlternativeHelperTest, ConcatenatesAlternatives) {
@@ -70,7 +58,7 @@
}
TEST_F(TextAlternativeHelperTest, HiddenOnlyIfAriaHiddenSet) {
- scoped_refptr<dom::Element> element = document_->CreateElement("div");
+ scoped_refptr<dom::Element> element = document()->CreateElement("div");
// No aria-hidden attribute set.
EXPECT_FALSE(TextAlternativeHelper::IsAriaHidden(element));
@@ -90,8 +78,8 @@
}
TEST_F(TextAlternativeHelperTest, GetTextAlternativeFromTextNode) {
- scoped_refptr<dom::Element> element = document_->CreateElement("div");
- element->AppendChild(document_->CreateTextNode("text node contents"));
+ scoped_refptr<dom::Element> element = document()->CreateElement("div");
+ element->AppendChild(document()->CreateTextNode("text node contents"));
text_alternative_helper_.AppendTextAlternative(element);
EXPECT_STREQ("text node contents",
text_alternative_helper_.GetTextAlternative().c_str());
@@ -99,7 +87,7 @@
TEST_F(TextAlternativeHelperTest, GetAltAttributeFromHTMLImage) {
scoped_refptr<dom::Element> element =
- document_->CreateElement(dom::HTMLImageElement::kTagName);
+ document()->CreateElement(dom::HTMLImageElement::kTagName);
element->SetAttribute(base::Tokens::alt().c_str(), "image alt text");
EXPECT_TRUE(text_alternative_helper_.TryAppendFromAltProperty(element));
EXPECT_STREQ("image alt text",
@@ -108,7 +96,7 @@
TEST_F(TextAlternativeHelperTest, EmptyAltAttribute) {
scoped_refptr<dom::Element> element =
- document_->CreateElement(dom::HTMLImageElement::kTagName);
+ document()->CreateElement(dom::HTMLImageElement::kTagName);
// No alt attribute.
EXPECT_FALSE(text_alternative_helper_.TryAppendFromAltProperty(element));
EXPECT_TRUE(text_alternative_helper_.GetTextAlternative().empty());
@@ -120,14 +108,14 @@
}
TEST_F(TextAlternativeHelperTest, AltAttributeOnArbitraryElement) {
- scoped_refptr<dom::Element> element = document_->CreateElement("div");
+ scoped_refptr<dom::Element> element = document()->CreateElement("div");
element->SetAttribute(base::Tokens::alt().c_str(), "alt text");
EXPECT_FALSE(text_alternative_helper_.TryAppendFromAltProperty(element));
EXPECT_TRUE(text_alternative_helper_.GetTextAlternative().empty());
}
TEST_F(TextAlternativeHelperTest, GetTextFromAriaLabel) {
- scoped_refptr<dom::Element> element = document_->CreateElement("div");
+ scoped_refptr<dom::Element> element = document()->CreateElement("div");
element->SetAttribute(base::Tokens::aria_label().c_str(), "label text");
EXPECT_TRUE(text_alternative_helper_.TryAppendFromLabel(element));
EXPECT_STREQ("label text",
@@ -135,23 +123,23 @@
}
TEST_F(TextAlternativeHelperTest, IgnoreEmptyAriaLabel) {
- scoped_refptr<dom::Element> element = document_->CreateElement("test");
+ scoped_refptr<dom::Element> element = document()->CreateElement("test");
element->SetAttribute(base::Tokens::aria_label().c_str(), " ");
EXPECT_FALSE(text_alternative_helper_.TryAppendFromLabel(element));
EXPECT_TRUE(text_alternative_helper_.GetTextAlternative().empty());
}
TEST_F(TextAlternativeHelperTest, GetTextFromAriaLabelledBy) {
- scoped_refptr<dom::Element> target_element = document_->CreateElement("div");
- target_element->AppendChild(document_->CreateTextNode("labelledby target"));
+ scoped_refptr<dom::Element> target_element = document()->CreateElement("div");
+ target_element->AppendChild(document()->CreateTextNode("labelledby target"));
target_element->set_id("target_element");
- document_->AppendChild(target_element);
+ document()->AppendChild(target_element);
scoped_refptr<dom::Element> labelledby_element =
- document_->CreateElement("div");
+ document()->CreateElement("div");
labelledby_element->SetAttribute(base::Tokens::aria_labelledby().c_str(),
"target_element");
- document_->AppendChild(labelledby_element);
+ document()->AppendChild(labelledby_element);
EXPECT_TRUE(
text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
@@ -160,16 +148,16 @@
}
TEST_F(TextAlternativeHelperTest, InvalidLabelledByReference) {
- scoped_refptr<dom::Element> target_element = document_->CreateElement("div");
- target_element->AppendChild(document_->CreateTextNode("labelledby target"));
+ scoped_refptr<dom::Element> target_element = document()->CreateElement("div");
+ target_element->AppendChild(document()->CreateTextNode("labelledby target"));
target_element->set_id("target_element");
- document_->AppendChild(target_element);
+ document()->AppendChild(target_element);
scoped_refptr<dom::Element> labelledby_element =
- document_->CreateElement("div");
+ document()->CreateElement("div");
labelledby_element->SetAttribute(base::Tokens::aria_labelledby().c_str(),
"bad_reference");
- document_->AppendChild(labelledby_element);
+ document()->AppendChild(labelledby_element);
EXPECT_FALSE(
text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
@@ -177,25 +165,25 @@
}
TEST_F(TextAlternativeHelperTest, MultipleLabelledByReferences) {
- scoped_refptr<dom::Element> target_element = document_->CreateElement("div");
- target_element->AppendChild(document_->CreateTextNode("target1"));
+ scoped_refptr<dom::Element> target_element = document()->CreateElement("div");
+ target_element->AppendChild(document()->CreateTextNode("target1"));
target_element->set_id("target_element1");
- document_->AppendChild(target_element);
- target_element = document_->CreateElement("div");
- target_element->AppendChild(document_->CreateTextNode("target2"));
+ document()->AppendChild(target_element);
+ target_element = document()->CreateElement("div");
+ target_element->AppendChild(document()->CreateTextNode("target2"));
target_element->set_id("target_element2");
- document_->AppendChild(target_element);
- target_element = document_->CreateElement("div");
- target_element->AppendChild(document_->CreateTextNode("target3"));
+ document()->AppendChild(target_element);
+ target_element = document()->CreateElement("div");
+ target_element->AppendChild(document()->CreateTextNode("target3"));
target_element->set_id("target_element3");
- document_->AppendChild(target_element);
+ document()->AppendChild(target_element);
scoped_refptr<dom::Element> labelledby_element =
- document_->CreateElement("div");
+ document()->CreateElement("div");
labelledby_element->SetAttribute(
base::Tokens::aria_labelledby().c_str(),
"target_element1 target_element2 bad_reference target_element3");
- document_->AppendChild(labelledby_element);
+ document()->AppendChild(labelledby_element);
EXPECT_TRUE(
text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
@@ -204,19 +192,19 @@
}
TEST_F(TextAlternativeHelperTest, LabelledByReferencesSelf) {
- scoped_refptr<dom::Element> target_element = document_->CreateElement("div");
- target_element->AppendChild(document_->CreateTextNode("other-text"));
+ scoped_refptr<dom::Element> target_element = document()->CreateElement("div");
+ target_element->AppendChild(document()->CreateTextNode("other-text"));
target_element->set_id("other_id");
- document_->AppendChild(target_element);
+ document()->AppendChild(target_element);
scoped_refptr<dom::Element> labelledby_element =
- document_->CreateElement("div");
+ document()->CreateElement("div");
labelledby_element->set_id("self_id");
labelledby_element->SetAttribute(base::Tokens::aria_label().c_str(),
"self-label");
labelledby_element->SetAttribute(base::Tokens::aria_labelledby().c_str(),
"other_id self_id");
- document_->AppendChild(labelledby_element);
+ document()->AppendChild(labelledby_element);
EXPECT_TRUE(
text_alternative_helper_.TryAppendFromLabelledBy(labelledby_element));
@@ -225,26 +213,26 @@
}
TEST_F(TextAlternativeHelperTest, NoRecursiveLabelledBy) {
- scoped_refptr<dom::Element> element = document_->CreateElement("div");
+ scoped_refptr<dom::Element> element = document()->CreateElement("div");
element->SetAttribute(base::Tokens::aria_labelledby().c_str(),
"first_labelled_by_id");
- document_->AppendChild(element);
+ document()->AppendChild(element);
scoped_refptr<dom::Element> first_labelledby_element =
- document_->CreateElement("div");
+ document()->CreateElement("div");
first_labelledby_element->AppendChild(
- document_->CreateTextNode("first-labelledby-text-contents"));
+ document()->CreateTextNode("first-labelledby-text-contents"));
first_labelledby_element->set_id("first_labelled_by_id");
first_labelledby_element->SetAttribute(
base::Tokens::aria_labelledby().c_str(), "second_labelled_by_id");
- document_->AppendChild(first_labelledby_element);
+ document()->AppendChild(first_labelledby_element);
scoped_refptr<dom::Element> second_labelledby_element =
- document_->CreateElement("div");
+ document()->CreateElement("div");
second_labelledby_element->AppendChild(
- document_->CreateTextNode("second-labelledby-text-contents"));
+ document()->CreateTextNode("second-labelledby-text-contents"));
second_labelledby_element->set_id("second_labelled_by_id");
- document_->AppendChild(second_labelledby_element);
+ document()->AppendChild(second_labelledby_element);
// In the non-recursive case, we should follow the labelledby attribute.
text_alternative_helper_.AppendTextAlternative(first_labelledby_element);
@@ -260,22 +248,22 @@
}
TEST_F(TextAlternativeHelperTest, GetTextFromSubtree) {
- scoped_refptr<dom::Element> element = document_->CreateElement("div");
- document_->AppendChild(element);
+ scoped_refptr<dom::Element> element = document()->CreateElement("div");
+ document()->AppendChild(element);
- scoped_refptr<dom::Element> child = document_->CreateElement("div");
- child->AppendChild(document_->CreateTextNode("child1-text"));
+ scoped_refptr<dom::Element> child = document()->CreateElement("div");
+ child->AppendChild(document()->CreateTextNode("child1-text"));
element->AppendChild(child);
- scoped_refptr<dom::Element> child_element = document_->CreateElement("div");
+ scoped_refptr<dom::Element> child_element = document()->CreateElement("div");
element->AppendChild(child_element);
- child = document_->CreateElement("div");
- child->AppendChild(document_->CreateTextNode("child2-text"));
+ child = document()->CreateElement("div");
+ child->AppendChild(document()->CreateTextNode("child2-text"));
child_element->AppendChild(child);
- child = document_->CreateElement("div");
- child->AppendChild(document_->CreateTextNode("child3-text"));
+ child = document()->CreateElement("div");
+ child->AppendChild(document()->CreateTextNode("child3-text"));
child_element->AppendChild(child);
text_alternative_helper_.AppendTextAlternative(element);
@@ -284,19 +272,19 @@
}
TEST_F(TextAlternativeHelperTest, DontFollowReferenceLoops) {
- scoped_refptr<dom::Element> element = document_->CreateElement("div");
+ scoped_refptr<dom::Element> element = document()->CreateElement("div");
element->set_id("root_element_id");
- document_->AppendChild(element);
+ document()->AppendChild(element);
- scoped_refptr<dom::Element> child_element1 = document_->CreateElement("div");
+ scoped_refptr<dom::Element> child_element1 = document()->CreateElement("div");
child_element1->SetAttribute(base::Tokens::aria_labelledby().c_str(),
"root_element_id");
element->AppendChild(child_element1);
- scoped_refptr<dom::Element> child_element2 = document_->CreateElement("div");
+ scoped_refptr<dom::Element> child_element2 = document()->CreateElement("div");
child_element1->AppendChild(child_element2);
- child_element2->AppendChild(document_->CreateTextNode("child2-text"));
+ child_element2->AppendChild(document()->CreateTextNode("child2-text"));
text_alternative_helper_.AppendTextAlternative(element);
EXPECT_STREQ("child2-text",
diff --git a/src/cobalt/base/c_val.h b/src/cobalt/base/c_val.h
index 4f7529c..e07b543 100644
--- a/src/cobalt/base/c_val.h
+++ b/src/cobalt/base/c_val.h
@@ -211,6 +211,11 @@
}
template <>
+inline std::string ValToString<bool>(const bool& value) {
+ return value ? "true" : "false";
+}
+
+template <>
inline std::string ValToString<std::string>(const std::string& value) {
return value;
}
diff --git a/src/cobalt/base/c_val_collection_entry_stats.h b/src/cobalt/base/c_val_collection_entry_stats.h
index ad4831e..c1dd807 100644
--- a/src/cobalt/base/c_val_collection_entry_stats.h
+++ b/src/cobalt/base/c_val_collection_entry_stats.h
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cmath>
#include <numeric>
+#include <sstream>
#include <string>
#include <vector>
@@ -43,13 +44,17 @@
// entries is extremely large, |CValTimeIntervalEntryStats| is more
// appropriate, as it does its tracking without keeping entries in memory
// (at the cost of not being able to provide percentiles);
+// NOTE3: This class provides the ability to record all entries within a single
+// string CVal when |enable_entry_list_c_val| is set to true in the
+// constructor. By default, this CVal is not used.
template <typename EntryType, typename Visibility = CValDebug>
class CValCollectionEntryStatsImpl {
public:
static const size_t kNoMaxSize = 0;
CValCollectionEntryStatsImpl(const std::string& name,
- size_t max_size = kNoMaxSize);
+ size_t max_size = kNoMaxSize,
+ bool enable_entry_list_c_val = false);
// Add an entry to the collection. This may trigger a Flush() if adding the
// entry causes the max size to be reached.
@@ -66,6 +71,11 @@
static double CalculateStandardDeviation(const CollectionType& collection,
double mean);
+ // Constructs a string representing the entries within the collection and
+ // populates |entry_list_| with it if the entry list CVal was enabled during
+ // construction.
+ void PopulateEntryList();
+
// The maximum size of the collection before Flush() is automatically called.
const size_t max_size_;
// The current collection of entries. These will be used to generate the cval
@@ -82,12 +92,15 @@
base::CVal<EntryType, Visibility> percentile_75th_;
base::CVal<EntryType, Visibility> percentile_95th_;
base::CVal<EntryType, Visibility> standard_deviation_;
+ // |entry_list_| is only non-NULL when it is enabled.
+ scoped_ptr<base::CVal<std::string, Visibility> > entry_list_;
};
template <typename EntryType, typename Visibility>
CValCollectionEntryStatsImpl<EntryType, Visibility>::
CValCollectionEntryStatsImpl(const std::string& name,
- size_t max_size /*=kNoMaxSize*/)
+ size_t max_size /*=kNoMaxSize*/,
+ bool enable_entry_list_c_val /*=false*/)
: max_size_(max_size),
count_(StringPrintf("%s.Cnt", name.c_str()), 0, "Total entries."),
average_(StringPrintf("%s.Avg", name.c_str()), EntryType(),
@@ -105,7 +118,13 @@
percentile_95th_(StringPrintf("%s.Pct.95th", name.c_str()), EntryType(),
"95th percentile value."),
standard_deviation_(StringPrintf("%s.Std", name.c_str()), EntryType(),
- "Standard deviation of values.") {}
+ "Standard deviation of values.") {
+ if (enable_entry_list_c_val) {
+ entry_list_.reset(new base::CVal<std::string, Visibility>(
+ StringPrintf("%s.EntryList", name.c_str()), "[]",
+ "The list of entries in the collection."));
+ }
+}
template <typename EntryType, typename Visibility>
void CValCollectionEntryStatsImpl<EntryType, Visibility>::AddEntry(
@@ -122,6 +141,9 @@
return;
}
+ // Populate the entry list cval before the collection is sorted.
+ PopulateEntryList();
+
// Sort the collection. This allows min, max, and percentiles to be easily
// determined.
std::sort(collection_.begin(), collection_.end());
@@ -204,6 +226,28 @@
return std::sqrt(variance);
}
+template <typename EntryType, typename Visibility>
+void CValCollectionEntryStatsImpl<EntryType, Visibility>::PopulateEntryList() {
+ // If the entry list was not enabled, then |entry_list_| will be NULL and
+ // there is nothing to do. Simply return.
+ if (!entry_list_) {
+ return;
+ }
+
+ // Construct a string containing a list representation of the collection
+ // entries and set the entry list CVal to it.
+ std::ostringstream oss;
+ oss << "[";
+ for (size_t i = 0; i < collection_.size(); ++i) {
+ if (i > 0) {
+ oss << ", ";
+ }
+ oss << CValDetail::ValToString<EntryType>(collection_[i]);
+ }
+ oss << "]";
+ *entry_list_ = oss.str();
+}
+
#if !defined(ENABLE_DEBUG_C_VAL)
// This is a stub class that disables CValCollectionEntryStats when
// ENABLE_DEBUG_C_VAL is not defined.
@@ -213,9 +257,11 @@
explicit CValCollectionEntryStatsStub(const std::string& name) {
UNREFERENCED_PARAMETER(name);
}
- CValCollectionEntryStatsStub(const std::string& name, size_t max_size) {
+ CValCollectionEntryStatsStub(const std::string& name, size_t max_size,
+ bool enable_entry_list_c_val) {
UNREFERENCED_PARAMETER(name);
UNREFERENCED_PARAMETER(max_size);
+ UNREFERENCED_PARAMETER(enable_entry_list_c_val);
}
void AddEntry(const EntryType& value) { UNREFERENCED_PARAMETER(value); }
@@ -248,8 +294,9 @@
public:
explicit CValCollectionEntryStats(const std::string& name)
: CValParent(name) {}
- CValCollectionEntryStats(const std::string& name, size_t max_size)
- : CValParent(name, max_size) {}
+ CValCollectionEntryStats(const std::string& name, size_t max_size,
+ bool enable_entry_list_c_val)
+ : CValParent(name, max_size, enable_entry_list_c_val) {}
};
// CVals with visibility set to CValPublic are always tracked though the CVal
@@ -263,8 +310,9 @@
public:
explicit CValCollectionEntryStats(const std::string& name)
: CValParent(name) {}
- CValCollectionEntryStats(const std::string& name, size_t max_size)
- : CValParent(name, max_size) {}
+ CValCollectionEntryStats(const std::string& name, size_t max_size,
+ bool enable_entry_list_c_val)
+ : CValParent(name, max_size, enable_entry_list_c_val) {}
};
} // namespace base
diff --git a/src/cobalt/base/c_val_collection_entry_stats_test.cc b/src/cobalt/base/c_val_collection_entry_stats_test.cc
index 794e27c..3a17189 100644
--- a/src/cobalt/base/c_val_collection_entry_stats_test.cc
+++ b/src/cobalt/base/c_val_collection_entry_stats_test.cc
@@ -30,8 +30,10 @@
TEST(CValCollectionEntryStatsTest, DefaultValues) {
const std::string name = "CollectionEntry";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = false;
- base::CValCollectionEntryStats<int> cval(name, max_size);
+ base::CValCollectionEntryStats<int> cval(name, max_size,
+ enable_entry_list_c_val);
base::CValManager* cvm = base::CValManager::GetInstance();
base::optional<std::string> count =
@@ -52,6 +54,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "0");
@@ -71,13 +75,16 @@
EXPECT_EQ(*pct95, "0");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "0");
+ EXPECT_FALSE(entry_list);
}
TEST(CValCollectionEntryStatsTest, NoFlush) {
const std::string name = "CollectionEntryStats";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = true;
- base::CValCollectionEntryStats<int> cval(name, max_size);
+ base::CValCollectionEntryStats<int> cval(name, max_size,
+ enable_entry_list_c_val);
cval.AddEntry(3);
cval.AddEntry(9);
cval.AddEntry(1);
@@ -102,6 +109,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "0");
@@ -121,13 +130,17 @@
EXPECT_EQ(*pct95, "0");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "0");
+ EXPECT_TRUE(entry_list);
+ EXPECT_EQ(*entry_list, "[]");
}
TEST(CValCollectionEntryStatsTest, MaxSizeFlush) {
const std::string name = "CollectionEntryStats";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = true;
- base::CValCollectionEntryStats<int> cval(name, max_size);
+ base::CValCollectionEntryStats<int> cval(name, max_size,
+ enable_entry_list_c_val);
cval.AddEntry(3);
cval.AddEntry(9);
cval.AddEntry(1);
@@ -153,6 +166,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "5");
@@ -172,13 +187,17 @@
EXPECT_EQ(*pct95, "8");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "3");
+ EXPECT_TRUE(entry_list);
+ EXPECT_EQ(*entry_list, "[3, 9, 1, 7, 5]");
}
TEST(CValCollectionEntryStatsTest, ManualFlush) {
const std::string name = "CollectionEntryStats";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = true;
- base::CValCollectionEntryStats<int, CValPublic> cval(name, max_size);
+ base::CValCollectionEntryStats<int> cval(name, max_size,
+ enable_entry_list_c_val);
cval.AddEntry(3);
cval.AddEntry(9);
cval.AddEntry(1);
@@ -205,6 +224,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "4");
@@ -224,13 +245,17 @@
EXPECT_EQ(*pct95, "8");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "3");
+ EXPECT_TRUE(entry_list);
+ EXPECT_EQ(*entry_list, "[3, 9, 1, 7]");
}
TEST(CValCollectionEntryStatsTest, TwoManualFlushes) {
const std::string name = "CollectionEntryStats";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = true;
- base::CValCollectionEntryStats<int, CValDebug> cval(name, max_size);
+ base::CValCollectionEntryStats<int> cval(name, max_size,
+ enable_entry_list_c_val);
cval.AddEntry(3);
cval.AddEntry(9);
cval.AddEntry(1);
@@ -258,6 +283,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "1");
@@ -277,6 +304,8 @@
EXPECT_EQ(*pct95, "5");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "0");
+ EXPECT_TRUE(entry_list);
+ EXPECT_EQ(*entry_list, "[5]");
}
} // namespace base
diff --git a/src/cobalt/base/c_val_collection_timer_stats.h b/src/cobalt/base/c_val_collection_timer_stats.h
index 0454cd1..aff2c02 100644
--- a/src/cobalt/base/c_val_collection_timer_stats.h
+++ b/src/cobalt/base/c_val_collection_timer_stats.h
@@ -35,8 +35,9 @@
public:
explicit CValCollectionTimerStats(const std::string& name)
: entry_stats_(name) {}
- CValCollectionTimerStats(const std::string& name, size_t max_size)
- : entry_stats_(name, max_size) {}
+ CValCollectionTimerStats(const std::string& name, size_t max_size,
+ bool enable_entry_list_c_val)
+ : entry_stats_(name, max_size, enable_entry_list_c_val) {}
// Start the timer. If the timer is currently running, it is stopped and
// re-started. If no time is provided, then |base::TimeTicks::Now()| is used.
diff --git a/src/cobalt/base/c_val_collection_timer_stats_test.cc b/src/cobalt/base/c_val_collection_timer_stats_test.cc
index 596d9cc..8b0f6f3 100644
--- a/src/cobalt/base/c_val_collection_timer_stats_test.cc
+++ b/src/cobalt/base/c_val_collection_timer_stats_test.cc
@@ -31,8 +31,10 @@
TEST(CValCollectionTimerStatsTest, DefaultValues) {
const std::string name = "CollectionTimerStats";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = false;
- base::CValCollectionTimerStats<> cval(name, max_size);
+ base::CValCollectionTimerStats<> cval(name, max_size,
+ enable_entry_list_c_val);
base::CValManager* cvm = base::CValManager::GetInstance();
base::optional<std::string> count =
@@ -53,6 +55,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "0");
@@ -72,13 +76,16 @@
EXPECT_EQ(*pct95, "0us");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "0us");
+ EXPECT_FALSE(entry_list);
}
TEST(CValCollectionTimerStatsTest, NoFlush) {
const std::string name = "CollectionTimerStats";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = true;
- base::CValCollectionTimerStats<> cval(name, max_size);
+ base::CValCollectionTimerStats<> cval(name, max_size,
+ enable_entry_list_c_val);
cval.Start(base::TimeTicks::FromInternalValue(1000));
cval.Stop(base::TimeTicks::FromInternalValue(4000));
cval.Start(base::TimeTicks::FromInternalValue(4000));
@@ -107,6 +114,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "0");
@@ -126,13 +135,17 @@
EXPECT_EQ(*pct95, "0us");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "0us");
+ EXPECT_TRUE(entry_list);
+ EXPECT_EQ(*entry_list, "[]");
}
TEST(CValCollectionTimerStatsTest, MaxSizeFlush) {
const std::string name = "CollectionTimerStats";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = true;
- base::CValCollectionTimerStats<> cval(name, max_size);
+ base::CValCollectionTimerStats<> cval(name, max_size,
+ enable_entry_list_c_val);
cval.Start(base::TimeTicks::FromInternalValue(1000));
cval.Stop(base::TimeTicks::FromInternalValue(4000));
cval.Start(base::TimeTicks::FromInternalValue(4000));
@@ -164,6 +177,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "5");
@@ -183,13 +198,17 @@
EXPECT_EQ(*pct95, "8.6ms");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "3.2ms");
+ EXPECT_TRUE(entry_list);
+ EXPECT_EQ(*entry_list, "[3000, 9000, 1000, 7000, 5000]");
}
TEST(CValCollectionTimerStatsTest, ManualFlush) {
const std::string name = "CollectionTimerStats";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = true;
- base::CValCollectionTimerStats<CValPublic> cval(name, max_size);
+ base::CValCollectionTimerStats<CValPublic> cval(name, max_size,
+ enable_entry_list_c_val);
cval.Start(base::TimeTicks::FromInternalValue(1000));
cval.Stop(base::TimeTicks::FromInternalValue(4000));
cval.Start(base::TimeTicks::FromInternalValue(4000));
@@ -221,6 +240,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "4");
@@ -240,13 +261,17 @@
EXPECT_EQ(*pct95, "8.7ms");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "3.7ms");
+ EXPECT_TRUE(entry_list);
+ EXPECT_EQ(*entry_list, "[3000, 9000, 1000, 7000]");
}
TEST(CValCollectionTimerStatsTest, TwoManualFlushes) {
const std::string name = "CollectionTimer";
const size_t max_size = 5;
+ bool enable_entry_list_c_val = true;
- base::CValCollectionTimerStats<CValDebug> cval(name, max_size);
+ base::CValCollectionTimerStats<CValDebug> cval(name, max_size,
+ enable_entry_list_c_val);
cval.Start(base::TimeTicks::FromInternalValue(1000));
cval.Stop(base::TimeTicks::FromInternalValue(4000));
cval.Start(base::TimeTicks::FromInternalValue(4000));
@@ -279,6 +304,8 @@
cvm->GetValueAsPrettyString(StringPrintf("%s.Pct.95th", name.c_str()));
base::optional<std::string> std =
cvm->GetValueAsPrettyString(StringPrintf("%s.Std", name.c_str()));
+ base::optional<std::string> entry_list =
+ cvm->GetValueAsPrettyString(StringPrintf("%s.EntryList", name.c_str()));
EXPECT_TRUE(count);
EXPECT_EQ(*count, "1");
@@ -298,6 +325,8 @@
EXPECT_EQ(*pct95, "5.0ms");
EXPECT_TRUE(std);
EXPECT_EQ(*std, "0us");
+ EXPECT_TRUE(entry_list);
+ EXPECT_EQ(*entry_list, "[5000]");
}
} // namespace base
diff --git a/src/cobalt/base/c_val_test.cc b/src/cobalt/base/c_val_test.cc
index ad60807..adfbb61 100644
--- a/src/cobalt/base/c_val_test.cc
+++ b/src/cobalt/base/c_val_test.cc
@@ -65,7 +65,7 @@
base::CValManager* cvm = base::CValManager::GetInstance();
base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
EXPECT_TRUE(result);
- EXPECT_EQ(*result, "1");
+ EXPECT_EQ(*result, "true");
}
TEST(CValTest, RegisterAndPrintBoolFalse) {
@@ -75,7 +75,7 @@
base::CValManager* cvm = base::CValManager::GetInstance();
base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
EXPECT_TRUE(result);
- EXPECT_EQ(*result, "0");
+ EXPECT_EQ(*result, "false");
}
TEST(CValTest, RegisterAndPrintU32) {
diff --git a/src/cobalt/base/tokens.h b/src/cobalt/base/tokens.h
index 6885dfc..5c42945 100644
--- a/src/cobalt/base/tokens.h
+++ b/src/cobalt/base/tokens.h
@@ -28,6 +28,7 @@
MacroOpWithNameOnly(abort) \
MacroOpWithNameOnly(additions) \
MacroOpWithNameOnly(alt) \
+ MacroOpWithNameOnly(all) \
MacroOpWithNameOnly(animationend) \
MacroOpWithNameOnly(assertive) \
MacroOpWithNameOnly(blur) \
diff --git a/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp b/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp
new file mode 100644
index 0000000..bb7227e
--- /dev/null
+++ b/src/cobalt/bindings/testing/global_constructors_idls_idl_files_list.tmp
@@ -0,0 +1,46 @@
+AnonymousIndexedGetterInterface.idl
+AnonymousNamedGetterInterface.idl
+AnonymousNamedIndexedGetterInterface.idl
+ArbitraryInterface.idl
+BaseInterface.idl
+BooleanTypeTestInterface.idl
+CallbackFunctionInterface.idl
+CallbackInterfaceInterface.idl
+ConditionalInterface.idl
+ConstantsInterface.idl
+ConstructorInterface.idl
+ConstructorWithArgumentsInterface.idl
+DerivedGetterSetterInterface.idl
+DerivedInterface.idl
+DisabledInterface.idl
+DOMStringTestInterface.idl
+EnumerationInterface.idl
+ExceptionObjectInterface.idl
+ExceptionsInterface.idl
+ExtendedIDLAttributesInterface.idl
+GarbageCollectionTestInterface.idl
+GetOpaqueRootInterface.idl
+GlobalInterfaceParent.idl
+IndexedGetterInterface.idl
+InterfaceWithUnsupportedProperties.idl
+NamedConstructorInterface.idl
+NamedGetterInterface.idl
+NamedIndexedGetterInterface.idl
+NestedPutForwardsInterface.idl
+NoConstructorInterface.idl
+NoInterfaceObjectInterface.idl
+NullableTypesTestInterface.idl
+NumericTypesTestInterface.idl
+ObjectTypeBindingsInterface.idl
+OperationsTestInterface.idl
+PutForwardsInterface.idl
+SequenceUser.idl
+SingleOperationInterface.idl
+StaticPropertiesInterface.idl
+StringifierAnonymousOperationInterface.idl
+StringifierAttributeInterface.idl
+StringifierOperationInterface.idl
+TargetInterface.idl
+UnionTypesInterface.idl
+Window.idl
+UnsupportedInterface.idl
diff --git a/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp b/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp
new file mode 100644
index 0000000..3b0f3da
--- /dev/null
+++ b/src/cobalt/bindings/testing/global_objects_idl_files_list.tmp
@@ -0,0 +1,45 @@
+AnonymousIndexedGetterInterface.idl
+AnonymousNamedGetterInterface.idl
+AnonymousNamedIndexedGetterInterface.idl
+ArbitraryInterface.idl
+BaseInterface.idl
+BooleanTypeTestInterface.idl
+CallbackFunctionInterface.idl
+CallbackInterfaceInterface.idl
+ConditionalInterface.idl
+ConstantsInterface.idl
+ConstructorInterface.idl
+ConstructorWithArgumentsInterface.idl
+DerivedGetterSetterInterface.idl
+DerivedInterface.idl
+DisabledInterface.idl
+DOMStringTestInterface.idl
+EnumerationInterface.idl
+ExceptionObjectInterface.idl
+ExceptionsInterface.idl
+ExtendedIDLAttributesInterface.idl
+GarbageCollectionTestInterface.idl
+GetOpaqueRootInterface.idl
+GlobalInterfaceParent.idl
+IndexedGetterInterface.idl
+InterfaceWithUnsupportedProperties.idl
+NamedConstructorInterface.idl
+NamedGetterInterface.idl
+NamedIndexedGetterInterface.idl
+NestedPutForwardsInterface.idl
+NoConstructorInterface.idl
+NoInterfaceObjectInterface.idl
+NullableTypesTestInterface.idl
+NumericTypesTestInterface.idl
+ObjectTypeBindingsInterface.idl
+OperationsTestInterface.idl
+PutForwardsInterface.idl
+SequenceUser.idl
+SingleOperationInterface.idl
+StaticPropertiesInterface.idl
+StringifierAnonymousOperationInterface.idl
+StringifierAttributeInterface.idl
+StringifierOperationInterface.idl
+TargetInterface.idl
+UnionTypesInterface.idl
+Window.idl
diff --git a/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp b/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp
new file mode 100644
index 0000000..ea6cdb9
--- /dev/null
+++ b/src/cobalt/bindings/testing/interfaces_info_individual_static_idl_files_list.tmp
@@ -0,0 +1,49 @@
+AnonymousIndexedGetterInterface.idl
+AnonymousNamedGetterInterface.idl
+AnonymousNamedIndexedGetterInterface.idl
+ArbitraryInterface.idl
+BaseInterface.idl
+BooleanTypeTestInterface.idl
+CallbackFunctionInterface.idl
+CallbackInterfaceInterface.idl
+ConditionalInterface.idl
+ConstantsInterface.idl
+ConstructorInterface.idl
+ConstructorWithArgumentsInterface.idl
+DerivedGetterSetterInterface.idl
+DerivedInterface.idl
+DisabledInterface.idl
+DOMStringTestInterface.idl
+EnumerationInterface.idl
+ExceptionObjectInterface.idl
+ExceptionsInterface.idl
+ExtendedIDLAttributesInterface.idl
+GarbageCollectionTestInterface.idl
+GetOpaqueRootInterface.idl
+GlobalInterfaceParent.idl
+IndexedGetterInterface.idl
+InterfaceWithUnsupportedProperties.idl
+NamedConstructorInterface.idl
+NamedGetterInterface.idl
+NamedIndexedGetterInterface.idl
+NestedPutForwardsInterface.idl
+NoConstructorInterface.idl
+NoInterfaceObjectInterface.idl
+NullableTypesTestInterface.idl
+NumericTypesTestInterface.idl
+ObjectTypeBindingsInterface.idl
+OperationsTestInterface.idl
+PutForwardsInterface.idl
+SequenceUser.idl
+SingleOperationInterface.idl
+StaticPropertiesInterface.idl
+StringifierAnonymousOperationInterface.idl
+StringifierAttributeInterface.idl
+StringifierOperationInterface.idl
+TargetInterface.idl
+UnionTypesInterface.idl
+Window.idl
+ImplementedInterface.idl
+PartialInterface.idl
+InterfaceWithUnsupportedProperties_partial.idl
+UnsupportedInterface.idl
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 671baa7..765ee14 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -101,7 +101,11 @@
int GetWebDriverPort() {
// The default port on which the webdriver server should listen for incoming
// connections.
+#if defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
+ const int kDefaultWebDriverPort = SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT;
+#else
const int kDefaultWebDriverPort = 9515;
+#endif // defined(SB_OVERRIDE_DEFAULT_WEBDRIVER_PORT)
int webdriver_port = kDefaultWebDriverPort;
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kWebDriverPort)) {
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 3bdd680..c544756 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -91,6 +91,9 @@
['enable_about_scheme == 1', {
'defines': [ 'ENABLE_ABOUT_SCHEME' ],
}],
+ ['enable_mtm == 1', {
+ 'defines' : ['ENABLE_MTM'],
+ }],
],
},
diff --git a/src/cobalt/browser/global_constructors_idls_idl_files_list.tmp b/src/cobalt/browser/global_constructors_idls_idl_files_list.tmp
new file mode 100644
index 0000000..b50619f
--- /dev/null
+++ b/src/cobalt/browser/global_constructors_idls_idl_files_list.tmp
@@ -0,0 +1,149 @@
+../audio/AudioBuffer.idl
+../audio/AudioBufferSourceNode.idl
+../audio/AudioContext.idl
+../audio/AudioDestinationNode.idl
+../audio/AudioNode.idl
+../cssom/CSS.idl
+../cssom/CSSConditionRule.idl
+../cssom/CSSGroupingRule.idl
+../cssom/CSSFontFaceRule.idl
+../cssom/CSSMediaRule.idl
+../cssom/CSSKeyframeRule.idl
+../cssom/CSSKeyframesRule.idl
+../cssom/CSSRule.idl
+../cssom/CSSRuleList.idl
+../cssom/CSSStyleDeclaration.idl
+../cssom/CSSStyleRule.idl
+../cssom/CSSStyleSheet.idl
+../cssom/MediaList.idl
+../cssom/StyleSheet.idl
+../cssom/StyleSheetList.idl
+../debug/DebugHub.idl
+../debug/Debugger.idl
+../debug/DebuggerEventTarget.idl
+../debug/DebugScriptRunner.idl
+../dom/AnimationEvent.idl
+../dom/ArrayBuffer.idl
+../dom/ArrayBufferView.idl
+../dom/Attr.idl
+../dom/Blob.idl
+../dom/CDATASection.idl
+../dom/CharacterData.idl
+../dom/Comment.idl
+../dom/Console.idl
+../dom/Crypto.idl
+../dom/DataView.idl
+../dom/Document.idl
+../dom/DocumentTimeline.idl
+../dom/DocumentType.idl
+../dom/DOMException.idl
+../dom/DOMImplementation.idl
+../dom/DOMParser.idl
+../dom/DOMRect.idl
+../dom/DOMRectList.idl
+../dom/DOMRectReadOnly.idl
+../dom/DOMStringMap.idl
+../dom/DOMTokenList.idl
+../dom/Element.idl
+../dom/Event.idl
+../dom/EventListener.idl
+../dom/EventTarget.idl
+../dom/Float32Array.idl
+../dom/Float64Array.idl
+../dom/FocusEvent.idl
+../dom/History.idl
+../dom/HTMLAnchorElement.idl
+../dom/HTMLBodyElement.idl
+../dom/HTMLBRElement.idl
+../dom/HTMLCollection.idl
+../dom/HTMLDivElement.idl
+../dom/HTMLElement.idl
+../dom/HTMLHeadElement.idl
+../dom/HTMLHeadingElement.idl
+../dom/HTMLHtmlElement.idl
+../dom/HTMLImageElement.idl
+../dom/HTMLLinkElement.idl
+../dom/HTMLMediaElement.idl
+../dom/HTMLMetaElement.idl
+../dom/HTMLParagraphElement.idl
+../dom/HTMLScriptElement.idl
+../dom/HTMLSpanElement.idl
+../dom/HTMLStyleElement.idl
+../dom/HTMLTitleElement.idl
+../dom/HTMLUnknownElement.idl
+../dom/HTMLVideoElement.idl
+../dom/KeyboardEvent.idl
+../dom/Location.idl
+../dom/MediaError.idl
+../dom/MediaKeyCompleteEvent.idl
+../dom/MediaKeyError.idl
+../dom/MediaKeyErrorEvent.idl
+../dom/MediaKeyMessageEvent.idl
+../dom/MediaKeyNeededEvent.idl
+../dom/MediaQueryList.idl
+../dom/MediaSource.idl
+../dom/MemoryInfo.idl
+../dom/MimeTypeArray.idl
+../dom/NamedNodeMap.idl
+../dom/Navigator.idl
+../dom/Node.idl
+../dom/NodeList.idl
+../dom/Performance.idl
+../dom/PerformanceTiming.idl
+../dom/PluginArray.idl
+../dom/ProgressEvent.idl
+../dom/Screen.idl
+../dom/SecurityPolicyViolationEvent.idl
+../dom/SourceBuffer.idl
+../dom/SourceBufferList.idl
+../dom/Storage.idl
+../dom/StorageEvent.idl
+../dom/TestRunner.idl
+../dom/Text.idl
+../dom/TimeRanges.idl
+../dom/TransitionEvent.idl
+../dom/UIEvent.idl
+../dom/Uint16Array.idl
+../dom/Uint32Array.idl
+../dom/Uint8Array.idl
+../dom/URL.idl
+../dom/VideoPlaybackQuality.idl
+../dom/Window.idl
+../dom/XMLDocument.idl
+../dom/XMLSerializer.idl
+../h5vcc/dial/DialHttpRequest.idl
+../h5vcc/dial/DialHttpResponse.idl
+../h5vcc/dial/DialServer.idl
+../h5vcc/H5vcc.idl
+../h5vcc/H5vccAccountInfo.idl
+../h5vcc/H5vccAccountManager.idl
+../h5vcc/H5vccAudioConfig.idl
+../h5vcc/H5vccAudioConfigArray.idl
+../h5vcc/H5vccCVal.idl
+../h5vcc/H5vccCValKeyList.idl
+../h5vcc/H5vccDeepLinkEventTarget.idl
+../h5vcc/H5vccRuntime.idl
+../h5vcc/H5vccRuntimeEventTarget.idl
+../h5vcc/H5vccSettings.idl
+../h5vcc/H5vccStorage.idl
+../h5vcc/H5vccSystem.idl
+../speech/SpeechRecognition.idl
+../speech/SpeechRecognitionAlternative.idl
+../speech/SpeechRecognitionError.idl
+../speech/SpeechRecognitionEvent.idl
+../speech/SpeechRecognitionResult.idl
+../speech/SpeechRecognitionResultList.idl
+../web_animations/Animatable.idl
+../web_animations/Animation.idl
+../web_animations/AnimationEffectReadOnly.idl
+../web_animations/AnimationEffectTimingReadOnly.idl
+../web_animations/AnimationTimeline.idl
+../web_animations/Keyframe.idl
+../web_animations/KeyframeEffectReadOnly.idl
+../webdriver/ScriptExecutor.idl
+../webdriver/ScriptExecutorParams.idl
+../webdriver/ScriptExecutorResult.idl
+../websocket/WebSocket.idl
+../xhr/XMLHttpRequest.idl
+../xhr/XMLHttpRequestEventTarget.idl
+../xhr/XMLHttpRequestUpload.idl
diff --git a/src/cobalt/browser/global_objects_idl_files_list.tmp b/src/cobalt/browser/global_objects_idl_files_list.tmp
new file mode 100644
index 0000000..b50619f
--- /dev/null
+++ b/src/cobalt/browser/global_objects_idl_files_list.tmp
@@ -0,0 +1,149 @@
+../audio/AudioBuffer.idl
+../audio/AudioBufferSourceNode.idl
+../audio/AudioContext.idl
+../audio/AudioDestinationNode.idl
+../audio/AudioNode.idl
+../cssom/CSS.idl
+../cssom/CSSConditionRule.idl
+../cssom/CSSGroupingRule.idl
+../cssom/CSSFontFaceRule.idl
+../cssom/CSSMediaRule.idl
+../cssom/CSSKeyframeRule.idl
+../cssom/CSSKeyframesRule.idl
+../cssom/CSSRule.idl
+../cssom/CSSRuleList.idl
+../cssom/CSSStyleDeclaration.idl
+../cssom/CSSStyleRule.idl
+../cssom/CSSStyleSheet.idl
+../cssom/MediaList.idl
+../cssom/StyleSheet.idl
+../cssom/StyleSheetList.idl
+../debug/DebugHub.idl
+../debug/Debugger.idl
+../debug/DebuggerEventTarget.idl
+../debug/DebugScriptRunner.idl
+../dom/AnimationEvent.idl
+../dom/ArrayBuffer.idl
+../dom/ArrayBufferView.idl
+../dom/Attr.idl
+../dom/Blob.idl
+../dom/CDATASection.idl
+../dom/CharacterData.idl
+../dom/Comment.idl
+../dom/Console.idl
+../dom/Crypto.idl
+../dom/DataView.idl
+../dom/Document.idl
+../dom/DocumentTimeline.idl
+../dom/DocumentType.idl
+../dom/DOMException.idl
+../dom/DOMImplementation.idl
+../dom/DOMParser.idl
+../dom/DOMRect.idl
+../dom/DOMRectList.idl
+../dom/DOMRectReadOnly.idl
+../dom/DOMStringMap.idl
+../dom/DOMTokenList.idl
+../dom/Element.idl
+../dom/Event.idl
+../dom/EventListener.idl
+../dom/EventTarget.idl
+../dom/Float32Array.idl
+../dom/Float64Array.idl
+../dom/FocusEvent.idl
+../dom/History.idl
+../dom/HTMLAnchorElement.idl
+../dom/HTMLBodyElement.idl
+../dom/HTMLBRElement.idl
+../dom/HTMLCollection.idl
+../dom/HTMLDivElement.idl
+../dom/HTMLElement.idl
+../dom/HTMLHeadElement.idl
+../dom/HTMLHeadingElement.idl
+../dom/HTMLHtmlElement.idl
+../dom/HTMLImageElement.idl
+../dom/HTMLLinkElement.idl
+../dom/HTMLMediaElement.idl
+../dom/HTMLMetaElement.idl
+../dom/HTMLParagraphElement.idl
+../dom/HTMLScriptElement.idl
+../dom/HTMLSpanElement.idl
+../dom/HTMLStyleElement.idl
+../dom/HTMLTitleElement.idl
+../dom/HTMLUnknownElement.idl
+../dom/HTMLVideoElement.idl
+../dom/KeyboardEvent.idl
+../dom/Location.idl
+../dom/MediaError.idl
+../dom/MediaKeyCompleteEvent.idl
+../dom/MediaKeyError.idl
+../dom/MediaKeyErrorEvent.idl
+../dom/MediaKeyMessageEvent.idl
+../dom/MediaKeyNeededEvent.idl
+../dom/MediaQueryList.idl
+../dom/MediaSource.idl
+../dom/MemoryInfo.idl
+../dom/MimeTypeArray.idl
+../dom/NamedNodeMap.idl
+../dom/Navigator.idl
+../dom/Node.idl
+../dom/NodeList.idl
+../dom/Performance.idl
+../dom/PerformanceTiming.idl
+../dom/PluginArray.idl
+../dom/ProgressEvent.idl
+../dom/Screen.idl
+../dom/SecurityPolicyViolationEvent.idl
+../dom/SourceBuffer.idl
+../dom/SourceBufferList.idl
+../dom/Storage.idl
+../dom/StorageEvent.idl
+../dom/TestRunner.idl
+../dom/Text.idl
+../dom/TimeRanges.idl
+../dom/TransitionEvent.idl
+../dom/UIEvent.idl
+../dom/Uint16Array.idl
+../dom/Uint32Array.idl
+../dom/Uint8Array.idl
+../dom/URL.idl
+../dom/VideoPlaybackQuality.idl
+../dom/Window.idl
+../dom/XMLDocument.idl
+../dom/XMLSerializer.idl
+../h5vcc/dial/DialHttpRequest.idl
+../h5vcc/dial/DialHttpResponse.idl
+../h5vcc/dial/DialServer.idl
+../h5vcc/H5vcc.idl
+../h5vcc/H5vccAccountInfo.idl
+../h5vcc/H5vccAccountManager.idl
+../h5vcc/H5vccAudioConfig.idl
+../h5vcc/H5vccAudioConfigArray.idl
+../h5vcc/H5vccCVal.idl
+../h5vcc/H5vccCValKeyList.idl
+../h5vcc/H5vccDeepLinkEventTarget.idl
+../h5vcc/H5vccRuntime.idl
+../h5vcc/H5vccRuntimeEventTarget.idl
+../h5vcc/H5vccSettings.idl
+../h5vcc/H5vccStorage.idl
+../h5vcc/H5vccSystem.idl
+../speech/SpeechRecognition.idl
+../speech/SpeechRecognitionAlternative.idl
+../speech/SpeechRecognitionError.idl
+../speech/SpeechRecognitionEvent.idl
+../speech/SpeechRecognitionResult.idl
+../speech/SpeechRecognitionResultList.idl
+../web_animations/Animatable.idl
+../web_animations/Animation.idl
+../web_animations/AnimationEffectReadOnly.idl
+../web_animations/AnimationEffectTimingReadOnly.idl
+../web_animations/AnimationTimeline.idl
+../web_animations/Keyframe.idl
+../web_animations/KeyframeEffectReadOnly.idl
+../webdriver/ScriptExecutor.idl
+../webdriver/ScriptExecutorParams.idl
+../webdriver/ScriptExecutorResult.idl
+../websocket/WebSocket.idl
+../xhr/XMLHttpRequest.idl
+../xhr/XMLHttpRequestEventTarget.idl
+../xhr/XMLHttpRequestUpload.idl
diff --git a/src/cobalt/browser/interfaces_info_individual_static_idl_files_list.tmp b/src/cobalt/browser/interfaces_info_individual_static_idl_files_list.tmp
new file mode 100644
index 0000000..a9c6e86
--- /dev/null
+++ b/src/cobalt/browser/interfaces_info_individual_static_idl_files_list.tmp
@@ -0,0 +1,176 @@
+../audio/AudioBuffer.idl
+../audio/AudioBufferSourceNode.idl
+../audio/AudioContext.idl
+../audio/AudioDestinationNode.idl
+../audio/AudioNode.idl
+../cssom/CSS.idl
+../cssom/CSSConditionRule.idl
+../cssom/CSSGroupingRule.idl
+../cssom/CSSFontFaceRule.idl
+../cssom/CSSMediaRule.idl
+../cssom/CSSKeyframeRule.idl
+../cssom/CSSKeyframesRule.idl
+../cssom/CSSRule.idl
+../cssom/CSSRuleList.idl
+../cssom/CSSStyleDeclaration.idl
+../cssom/CSSStyleRule.idl
+../cssom/CSSStyleSheet.idl
+../cssom/MediaList.idl
+../cssom/StyleSheet.idl
+../cssom/StyleSheetList.idl
+../debug/DebugHub.idl
+../debug/Debugger.idl
+../debug/DebuggerEventTarget.idl
+../debug/DebugScriptRunner.idl
+../dom/AnimationEvent.idl
+../dom/ArrayBuffer.idl
+../dom/ArrayBufferView.idl
+../dom/Attr.idl
+../dom/Blob.idl
+../dom/CDATASection.idl
+../dom/CharacterData.idl
+../dom/Comment.idl
+../dom/Console.idl
+../dom/Crypto.idl
+../dom/DataView.idl
+../dom/Document.idl
+../dom/DocumentTimeline.idl
+../dom/DocumentType.idl
+../dom/DOMException.idl
+../dom/DOMImplementation.idl
+../dom/DOMParser.idl
+../dom/DOMRect.idl
+../dom/DOMRectList.idl
+../dom/DOMRectReadOnly.idl
+../dom/DOMStringMap.idl
+../dom/DOMTokenList.idl
+../dom/Element.idl
+../dom/Event.idl
+../dom/EventListener.idl
+../dom/EventTarget.idl
+../dom/Float32Array.idl
+../dom/Float64Array.idl
+../dom/FocusEvent.idl
+../dom/History.idl
+../dom/HTMLAnchorElement.idl
+../dom/HTMLBodyElement.idl
+../dom/HTMLBRElement.idl
+../dom/HTMLCollection.idl
+../dom/HTMLDivElement.idl
+../dom/HTMLElement.idl
+../dom/HTMLHeadElement.idl
+../dom/HTMLHeadingElement.idl
+../dom/HTMLHtmlElement.idl
+../dom/HTMLImageElement.idl
+../dom/HTMLLinkElement.idl
+../dom/HTMLMediaElement.idl
+../dom/HTMLMetaElement.idl
+../dom/HTMLParagraphElement.idl
+../dom/HTMLScriptElement.idl
+../dom/HTMLSpanElement.idl
+../dom/HTMLStyleElement.idl
+../dom/HTMLTitleElement.idl
+../dom/HTMLUnknownElement.idl
+../dom/HTMLVideoElement.idl
+../dom/KeyboardEvent.idl
+../dom/Location.idl
+../dom/MediaError.idl
+../dom/MediaKeyCompleteEvent.idl
+../dom/MediaKeyError.idl
+../dom/MediaKeyErrorEvent.idl
+../dom/MediaKeyMessageEvent.idl
+../dom/MediaKeyNeededEvent.idl
+../dom/MediaQueryList.idl
+../dom/MediaSource.idl
+../dom/MemoryInfo.idl
+../dom/MimeTypeArray.idl
+../dom/NamedNodeMap.idl
+../dom/Navigator.idl
+../dom/Node.idl
+../dom/NodeList.idl
+../dom/Performance.idl
+../dom/PerformanceTiming.idl
+../dom/PluginArray.idl
+../dom/ProgressEvent.idl
+../dom/Screen.idl
+../dom/SecurityPolicyViolationEvent.idl
+../dom/SourceBuffer.idl
+../dom/SourceBufferList.idl
+../dom/Storage.idl
+../dom/StorageEvent.idl
+../dom/TestRunner.idl
+../dom/Text.idl
+../dom/TimeRanges.idl
+../dom/TransitionEvent.idl
+../dom/UIEvent.idl
+../dom/Uint16Array.idl
+../dom/Uint32Array.idl
+../dom/Uint8Array.idl
+../dom/URL.idl
+../dom/VideoPlaybackQuality.idl
+../dom/Window.idl
+../dom/XMLDocument.idl
+../dom/XMLSerializer.idl
+../h5vcc/dial/DialHttpRequest.idl
+../h5vcc/dial/DialHttpResponse.idl
+../h5vcc/dial/DialServer.idl
+../h5vcc/H5vcc.idl
+../h5vcc/H5vccAccountInfo.idl
+../h5vcc/H5vccAccountManager.idl
+../h5vcc/H5vccAudioConfig.idl
+../h5vcc/H5vccAudioConfigArray.idl
+../h5vcc/H5vccCVal.idl
+../h5vcc/H5vccCValKeyList.idl
+../h5vcc/H5vccDeepLinkEventTarget.idl
+../h5vcc/H5vccRuntime.idl
+../h5vcc/H5vccRuntimeEventTarget.idl
+../h5vcc/H5vccSettings.idl
+../h5vcc/H5vccStorage.idl
+../h5vcc/H5vccSystem.idl
+../speech/SpeechRecognition.idl
+../speech/SpeechRecognitionAlternative.idl
+../speech/SpeechRecognitionError.idl
+../speech/SpeechRecognitionEvent.idl
+../speech/SpeechRecognitionResult.idl
+../speech/SpeechRecognitionResultList.idl
+../web_animations/Animatable.idl
+../web_animations/Animation.idl
+../web_animations/AnimationEffectReadOnly.idl
+../web_animations/AnimationEffectTimingReadOnly.idl
+../web_animations/AnimationTimeline.idl
+../web_animations/Keyframe.idl
+../web_animations/KeyframeEffectReadOnly.idl
+../webdriver/ScriptExecutor.idl
+../webdriver/ScriptExecutorParams.idl
+../webdriver/ScriptExecutorResult.idl
+../websocket/WebSocket.idl
+../xhr/XMLHttpRequest.idl
+../xhr/XMLHttpRequestEventTarget.idl
+../xhr/XMLHttpRequestUpload.idl
+../cssom/LinkStyle.idl
+../dom/Document_CSSOM.idl
+../dom/Document_HTML5.idl
+../dom/Document_WebAnimationsAPI.idl
+../dom/Element_CSSOMView.idl
+../dom/Element_DOMParsingAndSerialization.idl
+../dom/ElementCSSInlineStyle.idl
+../dom/GlobalCrypto.idl
+../dom/GlobalEventHandlers.idl
+../dom/HTMLElement_CSSOMView.idl
+../dom/NavigatorID.idl
+../dom/NavigatorLanguage.idl
+../dom/NavigatorPlugins.idl
+../dom/NavigatorStorageUtils.idl
+../dom/NonDocumentTypeChildNode.idl
+../dom/NonElementParentNode.idl
+../dom/ParentNode.idl
+../dom/Performance_HighResolutionTime.idl
+../dom/URLUtils.idl
+../dom/Window_AnimationTiming.idl
+../dom/Window_CSSOM.idl
+../dom/Window_CSSOMView.idl
+../dom/Window_Performance.idl
+../dom/WindowEventHandlers.idl
+../dom/WindowLocalStorage.idl
+../dom/WindowSessionStorage.idl
+../dom/WindowTimers.idl
diff --git a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc b/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
index 5b82542..cc317ea 100644
--- a/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
+++ b/src/cobalt/browser/memory_tracker/memory_tracker_tool_impl.cc
@@ -655,12 +655,14 @@
// Begin output to CSV.
// Preamble
- ss << kNewLine << "//////////////////////////////////////////////" << kNewLine
- << "// CSV of bytes allocated per region (MB's)." << kNewLine
+ ss << kNewLine
+ << "//////////////////////////////////////////////" << kNewLine
+ << "// CSV of BYTES allocated per region (MB's)." << kNewLine
<< "// Units are in Megabytes. This is designed" << kNewLine
<< "// to be used in a stacked graph." << kNewLine;
// HEADER.
+ ss << "Time(mins),";
for (MapIt it = samples.begin(); it != samples.end(); ++it) {
const std::string& name = it->first;
ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
@@ -669,6 +671,9 @@
// Print out the values of each of the samples.
for (size_t i = 0; i < smallest_sample_size; ++i) {
+ // Output time first so that it can be used as an x-axis.
+ const double time_mins = timeseries.time_stamps_[i].InSeconds()/60.f;
+ ss << time_mins << ",";
for (MapIt it = samples.begin(); it != samples.end(); ++it) {
const int64 alloc_bytes = it->second.allocated_bytes_[i];
// Convert to float megabytes with decimals of precision.
@@ -678,26 +683,34 @@
}
ss << kNewLine;
}
+ ss << "// END CSV of BYTES allocated per region." << kNewLine;
+ ss << "//////////////////////////////////////////////";
ss << kNewLine;
// Preamble
ss << kNewLine << "//////////////////////////////////////////////";
- ss << kNewLine << "// CSV of number of allocations per region." << kNewLine;
+ ss << kNewLine << "// CSV of COUNT of allocations per region." << kNewLine;
// HEADER
+ ss << "Time(mins),";
for (MapIt it = samples.begin(); it != samples.end(); ++it) {
const std::string& name = it->first;
ss << kQuote << SanitizeCSVKey(name) << kQuote << kDelimiter;
}
ss << kNewLine;
for (size_t i = 0; i < smallest_sample_size; ++i) {
+ // Output time first so that it can be used as an x-axis.
+ const double time_mins = timeseries.time_stamps_[i].InSeconds() / 60.f;
+ ss << time_mins << ",";
for (MapIt it = samples.begin(); it != samples.end(); ++it) {
const int64 n_allocs = it->second.number_allocations_[i];
ss << n_allocs << kDelimiter;
}
ss << kNewLine;
}
- ss << kNewLine;
+ ss << "// END CSV of COUNT of allocations per region." << kNewLine;
+ ss << "//////////////////////////////////////////////";
+
std::string output = ss.str();
return output;
}
@@ -736,6 +749,19 @@
new_entry.allocated_bytes_.push_back(allocation_bytes);
new_entry.number_allocations_.push_back(num_allocs);
}
+
+ // Sample the in use memory bytes reported by malloc.
+ MemoryStats memory_stats = GetProcessMemoryStats();
+ AllocationSamples& process_mem_in_use = map_samples["ProcessMemoryInUse"];
+ process_mem_in_use.allocated_bytes_.push_back(memory_stats.used_cpu_memory);
+ process_mem_in_use.number_allocations_.push_back(0);
+
+ // Sample the reserved memory bytes reported by malloc.
+ AllocationSamples& process_mem_reserved
+ = map_samples["ProcessMemoryReserved"];
+ process_mem_reserved.allocated_bytes_
+ .push_back(memory_stats.used_cpu_memory);
+ process_mem_reserved.number_allocations_.push_back(0);
}
bool MemoryTrackerCompressedTimeSeries::IsFull(const TimeSeries& timeseries,
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index dd689c9..572200d 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -326,7 +326,27 @@
: name_(data.options.name), is_running_(false) {
resource_provider_ = data.resource_provider;
- css_parser_ = css_parser::Parser::Create();
+ // Currently we rely on a platform to explicitly specify that it supports
+ // the map-to-mesh filter via the ENABLE_MTM define (and the 'enable_mtm' gyp
+ // variable). When we have better support for checking for decode to texture
+ // support, it would be nice to switch this logic to something like:
+ //
+ // supports_map_to_mesh =
+ // (resource_provider_->Supports3D() && SbPlayerSupportsDecodeToTexture()
+ // ? css_parser::Parser::kSupportsMapToMesh
+ // : css_parser::Parser::kDoesNotSupportMapToMesh);
+ //
+ // Note that it is important that we do not parse map-to-mesh filters if we
+ // cannot render them, since web apps may check for map-to-mesh support by
+ // testing whether it parses or not via the CSS.supports() Web API.
+ css_parser::Parser::SupportsMapToMeshFlag supports_map_to_mesh =
+#if defined(ENABLE_MTM)
+ css_parser::Parser::kSupportsMapToMesh;
+#else
+ css_parser::Parser::kDoesNotSupportMapToMesh;
+#endif
+
+ css_parser_ = css_parser::Parser::Create(supports_map_to_mesh);
DCHECK(css_parser_);
dom_parser_.reset(new dom_parser::Parser(
diff --git a/src/cobalt/browser/web_module_stat_tracker.cc b/src/cobalt/browser/web_module_stat_tracker.cc
index 61b0d92..06323fa 100644
--- a/src/cobalt/browser/web_module_stat_tracker.cc
+++ b/src/cobalt/browser/web_module_stat_tracker.cc
@@ -16,9 +16,16 @@
#include "cobalt/browser/web_module_stat_tracker.h"
+#if defined(ENABLE_WEBDRIVER)
+#include <sstream>
+#endif // ENABLE_WEBDRIVER
+
#include "base/stringprintf.h"
#include "cobalt/base/tokens.h"
#include "cobalt/dom/event.h"
+#if defined(ENABLE_WEBDRIVER)
+#include "cobalt/dom/global_stats.h"
+#endif // ENABLE_WEBDRIVER
namespace cobalt {
namespace browser {
@@ -162,7 +169,15 @@
StringPrintf("Event.Duration.%s.Layout.RenderAndAnimate",
name.c_str()),
base::TimeDelta(),
- "RenderAndAnimate duration for event (in microseconds).") {}
+ "RenderAndAnimate duration for event (in microseconds).")
+#if defined(ENABLE_WEBDRIVER)
+ ,
+ value_dictionary(
+ StringPrintf("Event.%s.ValueDictionary", name.c_str()),
+ "All event values represented as a dictionary in a string.")
+#endif // ENABLE_WEBDRIVER
+{
+}
bool WebModuleStatTracker::IsStopWatchEnabled(int /*id*/) const { return true; }
@@ -217,6 +232,70 @@
layout_stat_tracker_->GetStopWatchTypeDuration(
layout::LayoutStatTracker::kStopWatchTypeRenderAndAnimate);
+#if defined(ENABLE_WEBDRIVER)
+ // When the Webdriver is enabled, all of the event's values are stored within
+ // a single string representing a dictionary of key-value pairs. This allows
+ // the Webdriver to query a single CVal to retrieve all of the event's values.
+ std::ostringstream oss;
+ oss << "{"
+ << "\"ProducedRenderTree\":" << was_render_tree_produced << ", "
+ << "\"CntDomEventListeners\":"
+ << dom::GlobalStats::GetInstance()->GetNumEventListeners() << ", "
+ << "\"CntDomNodes\":" << dom::GlobalStats::GetInstance()->GetNumNodes()
+ << ", "
+ << "\"CntDomHtmlElements\":" << dom_stat_tracker_->total_html_elements()
+ << ", "
+ << "\"CntDomHtmlElementsCreated\":"
+ << dom_stat_tracker_->html_elements_created_count() << ", "
+ << "\"CntDomHtmlElementsDestroyed\":"
+ << dom_stat_tracker_->html_elements_destroyed_count() << ", "
+ << "\"CntDomUpdateMatchingRuleCalls\":"
+ << dom_stat_tracker_->update_matching_rules_count() << ", "
+ << "\"CntDomUpdateComputedStyleCalls\":"
+ << dom_stat_tracker_->update_computed_style_count() << ", "
+ << "\"CntLayoutBoxes\":" << layout_stat_tracker_->total_boxes() << ", "
+ << "\"CntLayoutBoxesCreated\":"
+ << layout_stat_tracker_->boxes_created_count() << ", "
+ << "\"CntLayoutBoxesDestroyed\":"
+ << layout_stat_tracker_->boxes_destroyed_count() << ", "
+ << "\"DurTotalUs\":"
+ << stop_watch_durations_[kStopWatchTypeEvent].InMicroseconds() << ", "
+ << "\"DurDomInjectEventUs\":"
+ << stop_watch_durations_[kStopWatchTypeInjectEvent].InMicroseconds()
+ << ", "
+ << "\"DurDomUpdateComputedStyleUs\":"
+ << dom_stat_tracker_
+ ->GetStopWatchTypeDuration(
+ dom::DomStatTracker::kStopWatchTypeUpdateComputedStyle)
+ .InMicroseconds()
+ << ", "
+ << "\"DurLayoutBoxTreeUs\":"
+ << layout_stat_tracker_
+ ->GetStopWatchTypeDuration(
+ layout::LayoutStatTracker::kStopWatchTypeLayoutBoxTree)
+ .InMicroseconds()
+ << ", "
+ << "\"DurLayoutBoxTreeBoxGenerationUs\":"
+ << layout_stat_tracker_
+ ->GetStopWatchTypeDuration(
+ layout::LayoutStatTracker::kStopWatchTypeBoxGeneration)
+ .InMicroseconds()
+ << ", "
+ << "\"DurLayoutBoxTreeUpdateUsedSizesUs\":"
+ << layout_stat_tracker_
+ ->GetStopWatchTypeDuration(
+ layout::LayoutStatTracker::kStopWatchTypeUpdateUsedSizes)
+ .InMicroseconds()
+ << ", "
+ << "\"DurLayoutRenderAndAnimateUs\":"
+ << layout_stat_tracker_
+ ->GetStopWatchTypeDuration(
+ layout::LayoutStatTracker::kStopWatchTypeRenderAndAnimate)
+ .InMicroseconds()
+ << "}";
+ event_stats->value_dictionary = oss.str();
+#endif // ENABLE_WEBDRIVER
+
current_event_type_ = kEventTypeInvalid;
dom_stat_tracker_->OnEndEvent();
diff --git a/src/cobalt/browser/web_module_stat_tracker.h b/src/cobalt/browser/web_module_stat_tracker.h
index ef5d751..95efb4a 100644
--- a/src/cobalt/browser/web_module_stat_tracker.h
+++ b/src/cobalt/browser/web_module_stat_tracker.h
@@ -97,6 +97,13 @@
duration_layout_update_used_sizes;
base::CVal<base::TimeDelta, base::CValPublic>
duration_layout_render_and_animate;
+
+#if defined(ENABLE_WEBDRIVER)
+ // A string containing all of the event's values as a dictionary of
+ // key-value pairs. This is used by the Webdriver and is only enabled with
+ // it.
+ base::CVal<std::string> value_dictionary;
+#endif // ENABLE_WEBDRIVER
};
// From base::StopWatchOwner
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 615df59..b679c25 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-20698
\ No newline at end of file
+21796
\ No newline at end of file
diff --git a/src/cobalt/build/gyp_utils.py b/src/cobalt/build/gyp_utils.py
index e1f3791..c39ed35 100644
--- a/src/cobalt/build/gyp_utils.py
+++ b/src/cobalt/build/gyp_utils.py
@@ -94,7 +94,10 @@
if os.path.isfile(BUILD_ID_PATH):
with open(BUILD_ID_PATH, 'r') as build_id_file:
- return build_id_file.read().replace('\n', '')
+ build_number = int(build_id_file.read().replace('\n', ''))
+ logging.info('Retrieving build number from %s', BUILD_ID_PATH)
+ logging.info('Build Number: %d', build_number)
+ return build_number
revinfo = GetRevinfo()
json_deps = json.dumps(revinfo)
diff --git a/src/cobalt/content/ssl/certs/578d5c04.0 b/src/cobalt/content/ssl/certs/578d5c04.0
deleted file mode 100644
index 676db97..0000000
--- a/src/cobalt/content/ssl/certs/578d5c04.0
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
-UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
-dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
-MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
-dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
-AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
-BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
-cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
-AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
-MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
-aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
-ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
-IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
-MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
-A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
-7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
-1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
------END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/c01cdfa2.0 b/src/cobalt/content/ssl/certs/c01cdfa2.0
index b5f1875..27778ab 100644
--- a/src/cobalt/content/ssl/certs/c01cdfa2.0
+++ b/src/cobalt/content/ssl/certs/c01cdfa2.0
@@ -25,4 +25,4 @@
BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
7M2CYfE45k+XmCpajQ==
------END CERTIFICATE-----
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/src/cobalt/css_parser/grammar.y b/src/cobalt/css_parser/grammar.y
index 50f29c5..3342a9e 100644
--- a/src/cobalt/css_parser/grammar.y
+++ b/src/cobalt/css_parser/grammar.y
@@ -198,6 +198,7 @@
%token kEllipseToken // ellipse
%token kEllipsisToken // ellipsis
%token kEndToken // end
+%token kEquirectangularToken // equirectangular
%token kFantasyToken // fantasy
%token kFarthestCornerToken // farthest-corner
%token kFarthestSideToken // farthest-side
@@ -793,6 +794,11 @@
%destructor { delete $$; } <cobalt_mtm_filter_functions>
%union {
+ cssom::MTMFunction::MeshSpec* cobalt_map_to_mesh_spec; }
+%type <cobalt_map_to_mesh_spec> cobalt_map_to_mesh_spec
+%destructor { delete $$; } <cobalt_map_to_mesh_spec>
+
+%union {
cssom::MTMFunction::ResolutionMatchedMeshListBuilder* cobalt_mtm_resolution_matched_meshes; }
%type <cobalt_mtm_resolution_matched_meshes> cobalt_mtm_resolution_matched_mesh_list
%destructor { delete $$; } <cobalt_mtm_resolution_matched_meshes>
@@ -6542,21 +6548,42 @@
cobalt_mtm_filter_function:
// Encodes an mtm filter. Currently the only type of filter function supported.
- cobalt_mtm_function_name maybe_whitespace url
- cobalt_mtm_resolution_matched_mesh_list comma angle angle comma
- cobalt_mtm_transform_function maybe_cobalt_mtm_stereo_mode
+ cobalt_mtm_function_name maybe_whitespace cobalt_map_to_mesh_spec comma angle
+ angle comma cobalt_mtm_transform_function maybe_cobalt_mtm_stereo_mode
')' maybe_whitespace {
- scoped_ptr<cssom::MTMFunction::ResolutionMatchedMeshListBuilder>
- resolution_matched_mesh_urls($4);
- scoped_ptr<glm::mat4> transform($9);
+ scoped_ptr<cssom::MTMFunction::MeshSpec>
+ mesh_spec($3);
+ scoped_ptr<glm::mat4> transform($8);
+ scoped_refptr<cssom::KeywordValue> stereo_mode =
+ MakeScopedRefPtrAndRelease($9);
- $$ = new cssom::MTMFunction(
- MakeScopedRefPtrAndRelease($3),
- resolution_matched_mesh_urls->Pass(),
- $6,
- $7,
- *transform,
- MakeScopedRefPtrAndRelease($10));
+ if (!parser_impl->supports_map_to_mesh()) {
+ YYERROR;
+ } else {
+ $$ = new cssom::MTMFunction(
+ mesh_spec.Pass(),
+ $5,
+ $6,
+ *transform,
+ stereo_mode);
+ }
+ }
+ ;
+
+cobalt_map_to_mesh_spec:
+ kEquirectangularToken {
+ $$ = new cssom::MTMFunction::MeshSpec(
+ cssom::MTMFunction::kEquirectangular);
+ }
+ | url cobalt_mtm_resolution_matched_mesh_list {
+ scoped_refptr<cssom::PropertyValue> url = MakeScopedRefPtrAndRelease($1);
+ scoped_ptr<cssom::MTMFunction::ResolutionMatchedMeshListBuilder>
+ resolution_matched_mesh_urls($2);
+
+ $$ = new cssom::MTMFunction::MeshSpec(
+ cssom::MTMFunction::kUrls,
+ url,
+ resolution_matched_mesh_urls->Pass());
}
;
diff --git a/src/cobalt/css_parser/parser.cc b/src/cobalt/css_parser/parser.cc
index ca538a5..c632354 100644
--- a/src/cobalt/css_parser/parser.cc
+++ b/src/cobalt/css_parser/parser.cc
@@ -119,7 +119,8 @@
cssom::CSSParser* css_parser,
const Parser::OnMessageCallback& on_warning_callback,
const Parser::OnMessageCallback& on_error_callback,
- Parser::MessageVerbosity message_verbosity);
+ Parser::MessageVerbosity message_verbosity,
+ Parser::SupportsMapToMeshFlag supports_map_to_mesh);
cssom::CSSParser* css_parser() { return css_parser_; }
@@ -176,6 +177,8 @@
return into_declaration_data_;
}
+ bool supports_map_to_mesh() const { return supports_map_to_mesh_; }
+
private:
bool Parse();
@@ -215,6 +218,9 @@
// ParsePropertyIntoDeclarationData() is called.
cssom::CSSDeclarationData* into_declaration_data_;
+ // Whether or not we support parsing "filter: map-to-mesh(...)".
+ bool supports_map_to_mesh_;
+
static void IncludeInputWithMessage(const std::string& input,
const Parser::OnMessageCallback& callback,
const std::string& message) {
@@ -245,7 +251,8 @@
cssom::CSSParser* css_parser,
const Parser::OnMessageCallback& on_warning_callback,
const Parser::OnMessageCallback& on_error_callback,
- Parser::MessageVerbosity message_verbosity)
+ Parser::MessageVerbosity message_verbosity,
+ Parser::SupportsMapToMeshFlag supports_map_to_mesh)
: input_(input),
input_location_(input_location),
on_warning_callback_(on_warning_callback),
@@ -253,7 +260,9 @@
message_verbosity_(message_verbosity),
css_parser_(css_parser),
scanner_(input_.c_str(), &string_pool_),
- into_declaration_data_(NULL) {}
+ into_declaration_data_(NULL),
+ supports_map_to_mesh_(supports_map_to_mesh ==
+ Parser::kSupportsMapToMesh) {}
scoped_refptr<cssom::CSSStyleSheet> ParserImpl::ParseStyleSheet() {
TRACK_MEMORY_SCOPE("CSS");
@@ -531,39 +540,44 @@
} // namespace
-scoped_ptr<Parser> Parser::Create() {
+scoped_ptr<Parser> Parser::Create(SupportsMapToMeshFlag supports_map_to_mesh) {
return make_scoped_ptr(new Parser(base::Bind(&LogWarningCallback),
base::Bind(&LogErrorCallback),
- Parser::kVerbose));
+ Parser::kVerbose, supports_map_to_mesh));
}
Parser::Parser(const OnMessageCallback& on_warning_callback,
const OnMessageCallback& on_error_callback,
- MessageVerbosity message_verbosity)
+ MessageVerbosity message_verbosity,
+ SupportsMapToMeshFlag supports_map_to_mesh)
: on_warning_callback_(on_warning_callback),
on_error_callback_(on_error_callback),
- message_verbosity_(message_verbosity) {}
+ message_verbosity_(message_verbosity),
+ supports_map_to_mesh_(supports_map_to_mesh) {}
Parser::~Parser() {}
scoped_refptr<cssom::CSSStyleSheet> Parser::ParseStyleSheet(
const std::string& input, const base::SourceLocation& input_location) {
ParserImpl parser_impl(input, input_location, this, on_warning_callback_,
- on_error_callback_, message_verbosity_);
+ on_error_callback_, message_verbosity_,
+ supports_map_to_mesh_);
return parser_impl.ParseStyleSheet();
}
scoped_refptr<cssom::CSSRule> Parser::ParseRule(
const std::string& input, const base::SourceLocation& input_location) {
ParserImpl parser_impl(input, input_location, this, on_warning_callback_,
- on_error_callback_, message_verbosity_);
+ on_error_callback_, message_verbosity_,
+ supports_map_to_mesh_);
return parser_impl.ParseRule();
}
scoped_refptr<cssom::CSSDeclaredStyleData> Parser::ParseStyleDeclarationList(
const std::string& input, const base::SourceLocation& input_location) {
ParserImpl parser_impl(input, input_location, this, on_warning_callback_,
- on_error_callback_, message_verbosity_);
+ on_error_callback_, message_verbosity_,
+ supports_map_to_mesh_);
return parser_impl.ParseStyleDeclarationList();
}
@@ -571,7 +585,8 @@
Parser::ParseFontFaceDeclarationList(
const std::string& input, const base::SourceLocation& input_location) {
ParserImpl parser_impl(input, input_location, this, on_warning_callback_,
- on_error_callback_, message_verbosity_);
+ on_error_callback_, message_verbosity_,
+ supports_map_to_mesh_);
return parser_impl.ParseFontFaceDeclarationList();
}
@@ -580,7 +595,7 @@
const base::SourceLocation& property_location) {
ParserImpl parser_impl(property_value, property_location, this,
on_warning_callback_, on_error_callback_,
- message_verbosity_);
+ message_verbosity_, supports_map_to_mesh_);
return parser_impl.ParsePropertyValue(property_name);
}
@@ -590,7 +605,7 @@
cssom::CSSDeclarationData* declaration_data) {
ParserImpl parser_impl(property_value, property_location, this,
on_warning_callback_, on_error_callback_,
- message_verbosity_);
+ message_verbosity_, supports_map_to_mesh_);
return parser_impl.ParsePropertyIntoDeclarationData(property_name,
declaration_data);
}
@@ -598,7 +613,8 @@
scoped_refptr<cssom::MediaList> Parser::ParseMediaList(
const std::string& media_list, const base::SourceLocation& input_location) {
ParserImpl parser_impl(media_list, input_location, this, on_warning_callback_,
- on_error_callback_, message_verbosity_);
+ on_error_callback_, message_verbosity_,
+ supports_map_to_mesh_);
return parser_impl.ParseMediaList();
}
@@ -607,7 +623,7 @@
const base::SourceLocation& input_location) {
ParserImpl parser_impl(media_query, input_location, this,
on_warning_callback_, on_error_callback_,
- message_verbosity_);
+ message_verbosity_, supports_map_to_mesh_);
return parser_impl.ParseMediaQuery();
}
diff --git a/src/cobalt/css_parser/parser.h b/src/cobalt/css_parser/parser.h
index 116f1e2..e7ecb23 100644
--- a/src/cobalt/css_parser/parser.h
+++ b/src/cobalt/css_parser/parser.h
@@ -27,7 +27,16 @@
class Parser : public cssom::CSSParser {
public:
- static scoped_ptr<Parser> Create();
+ enum SupportsMapToMeshFlag {
+ kSupportsMapToMesh,
+ kDoesNotSupportMapToMesh,
+ };
+
+ // The parameter |supports_map_to_mesh| can be used to toggle parser support
+ // for the map-to-mesh CSS filter. If disabled, "filter: map-to-mesh(...)"
+ // will result in a parse error.
+ static scoped_ptr<Parser> Create(
+ SupportsMapToMeshFlag supports_map_to_mesh = kSupportsMapToMesh);
~Parser();
scoped_refptr<cssom::CSSStyleSheet> ParseStyleSheet(
@@ -79,14 +88,16 @@
Parser(const OnMessageCallback& on_warning_callback,
const OnMessageCallback& on_error_callback,
- MessageVerbosity message_verbosity);
+ MessageVerbosity message_verbosity,
+ SupportsMapToMeshFlag supports_map_to_mesh);
const OnMessageCallback on_warning_callback_;
const OnMessageCallback on_error_callback_;
MessageVerbosity message_verbosity_;
+ SupportsMapToMeshFlag supports_map_to_mesh_;
friend class ParserImpl;
- friend class ParserTest;
+ friend class ParserTestBase;
DISALLOW_COPY_AND_ASSIGN(Parser);
};
diff --git a/src/cobalt/css_parser/parser_test.cc b/src/cobalt/css_parser/parser_test.cc
index e786724..a428d6b 100644
--- a/src/cobalt/css_parser/parser_test.cc
+++ b/src/cobalt/css_parser/parser_test.cc
@@ -95,10 +95,10 @@
MOCK_METHOD1(OnError, void(const std::string& message));
};
-class ParserTest : public ::testing::Test {
+class ParserTestBase : public ::testing::Test {
public:
- ParserTest();
- ~ParserTest() OVERRIDE {}
+ explicit ParserTestBase(Parser::SupportsMapToMeshFlag supports_map_to_mesh);
+ ~ParserTestBase() OVERRIDE {}
protected:
::testing::StrictMock<MockParserObserver> parser_observer_;
@@ -106,14 +106,25 @@
const base::SourceLocation source_location_;
};
-ParserTest::ParserTest()
+ParserTestBase::ParserTestBase(
+ Parser::SupportsMapToMeshFlag supports_map_to_mesh)
: parser_(base::Bind(&MockParserObserver::OnWarning,
base::Unretained(&parser_observer_)),
base::Bind(&MockParserObserver::OnError,
base::Unretained(&parser_observer_)),
- Parser::kShort),
+ Parser::kShort, supports_map_to_mesh),
source_location_("[object ParserTest]", 1, 1) {}
+class ParserTest : public ParserTestBase {
+ public:
+ ParserTest() : ParserTestBase(Parser::kSupportsMapToMesh) {}
+};
+
+class ParserNoMapToMeshTest : public ParserTestBase {
+ public:
+ ParserNoMapToMeshTest() : ParserTestBase(Parser::kDoesNotSupportMapToMesh) {}
+};
+
// TODO: Test every reduction that has semantic action.
TEST_F(ParserTest, ParsesEmptyInput) {
@@ -8373,11 +8384,11 @@
scoped_refptr<cssom::CSSDeclaredStyleData> style =
parser_.ParseStyleDeclarationList(
"filter: map-to-mesh(url(projection.msh),"
- " 180deg 1.5rad,"
- " matrix3d(1, 0, 0, 0,"
- " 0, 1, 0, 0,"
- " 0, 0, 1, 0,"
- " 0, 0, 0, 1));",
+ " 180deg 1.5rad,"
+ " matrix3d(1, 0, 0, 0,"
+ " 0, 1, 0, 0,"
+ " 0, 0, 1, 0,"
+ " 0, 0, 0, 1));",
source_location_);
scoped_refptr<cssom::FilterFunctionListValue> filter_list =
dynamic_cast<cssom::FilterFunctionListValue*>(
@@ -8390,6 +8401,40 @@
dynamic_cast<const cssom::MTMFunction*>(filter_list->value()[0]);
ASSERT_TRUE(mtm_function);
+ EXPECT_EQ(cssom::MTMFunction::kUrls, mtm_function->mesh_spec().mesh_type());
+
+ EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
+ EXPECT_EQ(1.5f, mtm_function->vertical_fov());
+
+ EXPECT_EQ(mtm_function->stereo_mode()->value(),
+ cssom::KeywordValue::kMonoscopic);
+}
+
+TEST_F(ParserTest, ParsesMtmEquirectangularFilter) {
+ scoped_refptr<cssom::CSSDeclaredStyleData> style =
+ parser_.ParseStyleDeclarationList(
+ "filter: map-to-mesh(equirectangular,"
+ " 180deg 1.5rad,"
+ " matrix3d(1, 0, 0, 0,"
+ " 0, 1, 0, 0,"
+ " 0, 0, 1, 0,"
+ " 0, 0, 0, 1),"
+ " monoscopic);",
+ source_location_);
+ scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+ dynamic_cast<cssom::FilterFunctionListValue*>(
+ style->GetPropertyValue(cssom::kFilterProperty).get());
+
+ ASSERT_TRUE(filter_list);
+ ASSERT_EQ(1, filter_list->value().size());
+
+ const cssom::MTMFunction* mtm_function =
+ dynamic_cast<const cssom::MTMFunction*>(filter_list->value()[0]);
+ ASSERT_TRUE(mtm_function);
+
+ EXPECT_EQ(cssom::MTMFunction::kEquirectangular,
+ mtm_function->mesh_spec().mesh_type());
+
EXPECT_EQ(static_cast<float>(M_PI), mtm_function->horizontal_fov());
EXPECT_EQ(1.5f, mtm_function->vertical_fov());
@@ -8425,6 +8470,46 @@
cssom::KeywordValue::kMonoscopic);
}
+TEST_F(ParserNoMapToMeshTest, DoesNotParseMapToMeshFilter) {
+ EXPECT_CALL(parser_observer_,
+ OnWarning("[object ParserTest]:1:9: warning: unsupported value"));
+
+ scoped_refptr<cssom::CSSDeclaredStyleData> style =
+ parser_.ParseStyleDeclarationList(
+ "filter: map-to-mesh(url(projection.msh),"
+ " 180deg 1.5rad,"
+ " matrix3d(1, 0, 0, 0,"
+ " 0, 1, 0, 0,"
+ " 0, 0, 1, 0,"
+ " 0, 0, 0, 1));",
+ source_location_);
+ scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+ dynamic_cast<cssom::FilterFunctionListValue*>(
+ style->GetPropertyValue(cssom::kFilterProperty).get());
+
+ EXPECT_FALSE(filter_list);
+}
+
+TEST_F(ParserNoMapToMeshTest, DoesNotParseMtmFilter) {
+ EXPECT_CALL(parser_observer_,
+ OnWarning("[object ParserTest]:1:9: warning: unsupported value"));
+
+ scoped_refptr<cssom::CSSDeclaredStyleData> style =
+ parser_.ParseStyleDeclarationList(
+ "filter: -cobalt-mtm(url(projection.msh),"
+ " 180deg 1.5rad,"
+ " matrix3d(1, 0, 0, 0,"
+ " 0, 1, 0, 0,"
+ " 0, 0, 1, 0,"
+ " 0, 0, 0, 1));",
+ source_location_);
+ scoped_refptr<cssom::FilterFunctionListValue> filter_list =
+ dynamic_cast<cssom::FilterFunctionListValue*>(
+ style->GetPropertyValue(cssom::kFilterProperty).get());
+
+ EXPECT_FALSE(filter_list);
+}
+
TEST_F(ParserTest, ParsesMtmResolutionMatchedUrlsFilter) {
scoped_refptr<cssom::CSSDeclaredStyleData> style =
parser_.ParseStyleDeclarationList(
@@ -8448,7 +8533,7 @@
dynamic_cast<const cssom::MTMFunction*>(filter_list->value()[0]);
const cssom::MTMFunction::ResolutionMatchedMeshListBuilder& meshes =
- mtm_function->resolution_matched_meshes();
+ mtm_function->mesh_spec().resolution_matched_meshes();
ASSERT_EQ(3, meshes.size());
EXPECT_EQ(640, meshes[0]->width_match());
diff --git a/src/cobalt/css_parser/scanner.cc b/src/cobalt/css_parser/scanner.cc
index 3be1ffb..3d3ce5e 100644
--- a/src/cobalt/css_parser/scanner.cc
+++ b/src/cobalt/css_parser/scanner.cc
@@ -2257,6 +2257,11 @@
*property_value_token = kFarthestCornerToken;
return true;
}
+ if (IsEqualToCssIdentifier(name.begin,
+ cssom::kEquirectangularKeywordName)) {
+ *property_value_token = kEquirectangularToken;
+ return true;
+ }
return false;
case 17:
diff --git a/src/cobalt/cssom/computed_style.cc b/src/cobalt/cssom/computed_style.cc
index 5fa9201..3e13d7d 100644
--- a/src/cobalt/cssom/computed_style.cc
+++ b/src/cobalt/cssom/computed_style.cc
@@ -351,6 +351,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kForwards:
case KeywordValue::kFixed:
@@ -449,6 +450,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
case KeywordValue::kForwards:
@@ -555,6 +557,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
case KeywordValue::kForwards:
@@ -676,6 +679,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
case KeywordValue::kForwards:
@@ -798,6 +802,7 @@
case KeywordValue::kCurrentColor:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
case KeywordValue::kForwards:
@@ -918,6 +923,7 @@
case KeywordValue::kCurrentColor:
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kEnd:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
@@ -1035,6 +1041,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
case KeywordValue::kForwards:
@@ -1146,6 +1153,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
case KeywordValue::kForwards:
@@ -1563,6 +1571,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
case KeywordValue::kForwards:
@@ -1839,6 +1848,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
case KeywordValue::kForwards:
@@ -2089,6 +2099,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kForwards:
case KeywordValue::kFixed:
@@ -2476,6 +2487,7 @@
case KeywordValue::kCursive:
case KeywordValue::kEllipsis:
case KeywordValue::kEnd:
+ case KeywordValue::kEquirectangular:
case KeywordValue::kFantasy:
case KeywordValue::kFixed:
case KeywordValue::kForwards:
diff --git a/src/cobalt/cssom/keyword_names.cc b/src/cobalt/cssom/keyword_names.cc
index 34fde90..dce8cd0 100644
--- a/src/cobalt/cssom/keyword_names.cc
+++ b/src/cobalt/cssom/keyword_names.cc
@@ -50,6 +50,7 @@
const char kEllipseKeywordName[] = "ellipse";
const char kEllipsisKeywordName[] = "ellipsis";
const char kEndKeywordName[] = "end";
+const char kEquirectangularKeywordName[] = "equirectangular";
const char kFantasyKeywordName[] = "fantasy";
const char kFarthestCornerKeywordName[] = "farthest-corner";
const char kFarthestSideKeywordName[] = "farthest-side";
diff --git a/src/cobalt/cssom/keyword_names.h b/src/cobalt/cssom/keyword_names.h
index 86f0eda..7cd5852 100644
--- a/src/cobalt/cssom/keyword_names.h
+++ b/src/cobalt/cssom/keyword_names.h
@@ -52,6 +52,7 @@
extern const char kEllipseKeywordName[];
extern const char kEllipsisKeywordName[];
extern const char kEndKeywordName[];
+extern const char kEquirectangularKeywordName[];
extern const char kFantasyKeywordName[];
extern const char kFarthestCornerKeywordName[];
extern const char kFarthestSideKeywordName[];
diff --git a/src/cobalt/cssom/keyword_value.cc b/src/cobalt/cssom/keyword_value.cc
index 0d90535..ac2c21e 100644
--- a/src/cobalt/cssom/keyword_value.cc
+++ b/src/cobalt/cssom/keyword_value.cc
@@ -44,6 +44,7 @@
cursive_value(new KeywordValue(KeywordValue::kCursive)),
ellipsis_value(new KeywordValue(KeywordValue::kEllipsis)),
end_value(new KeywordValue(KeywordValue::kEnd)),
+ equirectangular_value(new KeywordValue(KeywordValue::kEquirectangular)),
fantasy_value(new KeywordValue(KeywordValue::kFantasy)),
forwards_value(new KeywordValue(KeywordValue::kForwards)),
fixed_value(new KeywordValue(KeywordValue::kFixed)),
@@ -100,6 +101,7 @@
const scoped_refptr<KeywordValue> cursive_value;
const scoped_refptr<KeywordValue> ellipsis_value;
const scoped_refptr<KeywordValue> end_value;
+ const scoped_refptr<KeywordValue> equirectangular_value;
const scoped_refptr<KeywordValue> fantasy_value;
const scoped_refptr<KeywordValue> forwards_value;
const scoped_refptr<KeywordValue> fixed_value;
@@ -219,6 +221,10 @@
return non_trivial_static_fields.Get().end_value;
}
+const scoped_refptr<KeywordValue>& KeywordValue::GetEquirectangular() {
+ return non_trivial_static_fields.Get().equirectangular_value;
+}
+
const scoped_refptr<KeywordValue>& KeywordValue::GetFantasy() {
return non_trivial_static_fields.Get().fantasy_value;
}
@@ -401,6 +407,8 @@
return kEllipsisKeywordName;
case kEnd:
return kEndKeywordName;
+ case kEquirectangular:
+ return kEquirectangularKeywordName;
case kFantasy:
return kFantasyKeywordName;
case kForwards:
diff --git a/src/cobalt/cssom/keyword_value.h b/src/cobalt/cssom/keyword_value.h
index bed4ba0..35659b6 100644
--- a/src/cobalt/cssom/keyword_value.h
+++ b/src/cobalt/cssom/keyword_value.h
@@ -131,6 +131,11 @@
// https://www.w3.org/TR/css-text-3/#text-align
kEnd,
+ // "equirectangular" is a value of a parameter of the "map-to-mesh"
+ // filter function which indicates that the built-in equirectangular mesh
+ // should be used.
+ kEquirectangular,
+
// "fantasy" is a value of "font_family" property which indicates a generic
// font family using decorative or expressive representations of characters.
// https://www.w3.org/TR/css3-fonts/#generic-font-families
@@ -346,6 +351,7 @@
static const scoped_refptr<KeywordValue>& GetCursive();
static const scoped_refptr<KeywordValue>& GetEllipsis();
static const scoped_refptr<KeywordValue>& GetEnd();
+ static const scoped_refptr<KeywordValue>& GetEquirectangular();
static const scoped_refptr<KeywordValue>& GetFantasy();
static const scoped_refptr<KeywordValue>& GetForwards();
static const scoped_refptr<KeywordValue>& GetFixed();
diff --git a/src/cobalt/cssom/mtm_function.cc b/src/cobalt/cssom/mtm_function.cc
index 661060b..f973bf6 100644
--- a/src/cobalt/cssom/mtm_function.cc
+++ b/src/cobalt/cssom/mtm_function.cc
@@ -26,34 +26,24 @@
namespace cobalt {
namespace cssom {
-MTMFunction::MTMFunction(
- const scoped_refptr<PropertyValue>& mesh_url,
- ResolutionMatchedMeshListBuilder resolution_matched_meshes,
- float horizontal_fov, float vertical_fov, const glm::mat4& transform,
- const scoped_refptr<KeywordValue>& stereo_mode)
- : mesh_url_(mesh_url),
- resolution_matched_meshes_(resolution_matched_meshes.Pass()),
- horizontal_fov_(horizontal_fov),
- vertical_fov_(vertical_fov),
- transform_(transform),
- stereo_mode_(stereo_mode) {
- DCHECK(mesh_url_);
- DCHECK(stereo_mode_);
-}
-
std::string MTMFunction::ToString() const {
- std::string result = "-cobalt-mtm(";
+ std::string result = "map-to-mesh(";
- result.append(mesh_url()->ToString());
+ if (mesh_spec().mesh_type() == kEquirectangular) {
+ result.append("equirectangular");
+ } else if (mesh_spec().mesh_type() == kUrls) {
+ result.append(mesh_spec().mesh_url()->ToString());
- const ResolutionMatchedMeshListBuilder& meshes = resolution_matched_meshes();
- for (size_t mesh_index = 0; mesh_index < meshes.size(); ++mesh_index) {
- result.push_back(' ');
- result.append(base::IntToString(meshes[mesh_index]->width_match()));
- result.push_back(' ');
- result.append(base::IntToString(meshes[mesh_index]->height_match()));
- result.push_back(' ');
- result.append(meshes[mesh_index]->mesh_url()->ToString());
+ const ResolutionMatchedMeshListBuilder& meshes =
+ mesh_spec().resolution_matched_meshes();
+ for (size_t mesh_index = 0; mesh_index < meshes.size(); ++mesh_index) {
+ result.push_back(' ');
+ result.append(base::IntToString(meshes[mesh_index]->width_match()));
+ result.push_back(' ');
+ result.append(base::IntToString(meshes[mesh_index]->height_match()));
+ result.push_back(' ');
+ result.append(meshes[mesh_index]->mesh_url()->ToString());
+ }
}
result.append(base::StringPrintf(", %.7grad", horizontal_fov()));
@@ -118,31 +108,38 @@
}
bool MTMFunction::operator==(const MTMFunction& rhs) const {
- if (!mesh_url()->Equals(*rhs.mesh_url()) ||
+ if (mesh_spec().mesh_type() != rhs.mesh_spec().mesh_type() ||
horizontal_fov() != rhs.horizontal_fov() ||
horizontal_fov() != rhs.horizontal_fov() ||
!stereo_mode()->Equals(*rhs.stereo_mode())) {
return false;
}
- const ResolutionMatchedMeshListBuilder& lhs_meshes =
- resolution_matched_meshes();
- const ResolutionMatchedMeshListBuilder& rhs_meshes =
- rhs.resolution_matched_meshes();
- if (lhs_meshes.size() != rhs_meshes.size()) {
- return false;
- }
-
- for (size_t i = 0; i < lhs_meshes.size(); ++i) {
- if (*lhs_meshes[i] != *rhs_meshes[i]) {
+ if (mesh_spec().mesh_type() == kUrls) {
+ if (!mesh_spec().mesh_url()->Equals(*rhs.mesh_spec().mesh_url())) {
return false;
}
- }
- for (int col = 0; col <= 3; ++col) {
- if (!glm::all(glm::equal(transform()[col], rhs.transform()[col]))) {
+ const ResolutionMatchedMeshListBuilder& lhs_meshes =
+ mesh_spec().resolution_matched_meshes();
+ const ResolutionMatchedMeshListBuilder& rhs_meshes =
+ rhs.mesh_spec().resolution_matched_meshes();
+
+ if (lhs_meshes.size() != rhs_meshes.size()) {
return false;
}
+
+ for (size_t i = 0; i < lhs_meshes.size(); ++i) {
+ if (*lhs_meshes[i] != *rhs_meshes[i]) {
+ return false;
+ }
+ }
+
+ for (int col = 0; col <= 3; ++col) {
+ if (!glm::all(glm::equal(transform()[col], rhs.transform()[col]))) {
+ return false;
+ }
+ }
}
return true;
diff --git a/src/cobalt/cssom/mtm_function.h b/src/cobalt/cssom/mtm_function.h
index 764c0cb..00a1a17 100644
--- a/src/cobalt/cssom/mtm_function.h
+++ b/src/cobalt/cssom/mtm_function.h
@@ -22,6 +22,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "cobalt/base/polymorphic_equatable.h"
#include "cobalt/cssom/filter_function.h"
@@ -33,7 +34,7 @@
namespace cobalt {
namespace cssom {
-// Represent an MTM filter.
+// Represent a map-to-mesh filter.
class MTMFunction : public FilterFunction {
public:
// A resolution-matched mesh specifier.
@@ -52,21 +53,95 @@
const int height_match_;
const scoped_refptr<PropertyValue> mesh_url_;
};
+
+ // Type of the source of the mesh: either a built-in mesh type or a custom
+ // mesh.
+ enum MeshSpecType {
+ // Built-in equirectangular mesh.
+ kEquirectangular,
+ // List of custom binary mesh URLs.
+ kUrls
+ };
typedef ScopedVector<ResolutionMatchedMesh> ResolutionMatchedMeshListBuilder;
+ // Contains the specification of the mesh source.
+ class MeshSpec {
+ public:
+ explicit MeshSpec(MeshSpecType mesh_type) : mesh_type_(mesh_type) {
+ // Check that this is a built-in mesh type.
+ DCHECK_EQ(mesh_type, kEquirectangular);
+ }
+
+ MeshSpec(MeshSpecType mesh_type,
+ const scoped_refptr<PropertyValue>& mesh_url,
+ ResolutionMatchedMeshListBuilder resolution_matched_meshes)
+ : mesh_type_(mesh_type),
+ mesh_url_(mesh_url),
+ resolution_matched_meshes_(resolution_matched_meshes.Pass()) {
+ DCHECK_EQ(mesh_type_, kUrls);
+ DCHECK(mesh_url);
+ }
+
+ const scoped_refptr<PropertyValue>& mesh_url() const {
+ DCHECK_EQ(mesh_type_, kUrls);
+ return mesh_url_;
+ }
+ const ResolutionMatchedMeshListBuilder& resolution_matched_meshes() const {
+ DCHECK_EQ(mesh_type_, kUrls);
+ return resolution_matched_meshes_;
+ }
+ MeshSpecType mesh_type() const { return mesh_type_; }
+
+ private:
+ MeshSpecType mesh_type_;
+ scoped_refptr<PropertyValue> mesh_url_;
+ ResolutionMatchedMeshListBuilder resolution_matched_meshes_;
+
+ DISALLOW_COPY_AND_ASSIGN(MeshSpec);
+ };
+
+ MTMFunction(scoped_ptr<MeshSpec> mesh_spec, float horizontal_fov,
+ float vertical_fov, const glm::mat4& transform,
+ const scoped_refptr<KeywordValue>& stereo_mode)
+ : mesh_spec_(mesh_spec.Pass()),
+ horizontal_fov_(horizontal_fov),
+ vertical_fov_(vertical_fov),
+ transform_(transform),
+ stereo_mode_(stereo_mode) {
+ DCHECK(mesh_spec_);
+ DCHECK(stereo_mode_);
+ }
+
+ // Alternate constructor for mesh URL lists.
MTMFunction(const scoped_refptr<PropertyValue>& mesh_url,
ResolutionMatchedMeshListBuilder resolution_matched_meshes,
float horizontal_fov, float vertical_fov,
const glm::mat4& transform,
- const scoped_refptr<KeywordValue>& stereo_mode);
+ const scoped_refptr<KeywordValue>& stereo_mode)
+ : mesh_spec_(
+ new MeshSpec(kUrls, mesh_url, resolution_matched_meshes.Pass())),
+ horizontal_fov_(horizontal_fov),
+ vertical_fov_(vertical_fov),
+ transform_(transform),
+ stereo_mode_(stereo_mode) {
+ DCHECK(stereo_mode_);
+ }
+
+ // Alternate constructor for built-in meshes.
+ MTMFunction(MeshSpecType spec_type, float horizontal_fov, float vertical_fov,
+ const glm::mat4& transform,
+ const scoped_refptr<KeywordValue>& stereo_mode)
+ : mesh_spec_(new MeshSpec(spec_type)),
+ horizontal_fov_(horizontal_fov),
+ vertical_fov_(vertical_fov),
+ transform_(transform),
+ stereo_mode_(stereo_mode) {
+ DCHECK(stereo_mode_);
+ }
~MTMFunction() OVERRIDE {}
- const scoped_refptr<PropertyValue>& mesh_url() const { return mesh_url_; }
- const ResolutionMatchedMeshListBuilder& resolution_matched_meshes() const {
- return resolution_matched_meshes_;
- }
-
+ const MeshSpec& mesh_spec() const { return *mesh_spec_; }
float horizontal_fov() const { return horizontal_fov_; }
float vertical_fov() const { return vertical_fov_; }
const glm::mat4& transform() const { return transform_; }
@@ -83,8 +158,7 @@
DEFINE_POLYMORPHIC_EQUATABLE_TYPE(MTMFunction);
private:
- const scoped_refptr<PropertyValue> mesh_url_;
- const ResolutionMatchedMeshListBuilder resolution_matched_meshes_;
+ const scoped_ptr<MeshSpec> mesh_spec_;
const float horizontal_fov_;
const float vertical_fov_;
const glm::mat4 transform_;
diff --git a/src/cobalt/cssom/property_value_is_equal_test.cc b/src/cobalt/cssom/property_value_is_equal_test.cc
index 263634b..d619871 100644
--- a/src/cobalt/cssom/property_value_is_equal_test.cc
+++ b/src/cobalt/cssom/property_value_is_equal_test.cc
@@ -612,6 +612,11 @@
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
KeywordValue::GetStereoscopicLeftRight()));
+ filter_list_a.push_back(
+ new MTMFunction(MTMFunction::kEquirectangular, 0.676f, 6.28f,
+ glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetStereoscopicLeftRight()));
scoped_refptr<FilterFunctionListValue> value_a(
new FilterFunctionListValue(filter_list_a.Pass()));
@@ -628,6 +633,11 @@
glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
KeywordValue::GetStereoscopicLeftRight()));
+ filter_list_b.push_back(
+ new MTMFunction(MTMFunction::kEquirectangular, 0.676f, 6.28f,
+ glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetStereoscopicLeftRight()));
scoped_refptr<FilterFunctionListValue> value_b(
new FilterFunctionListValue(filter_list_b.Pass()));
@@ -656,6 +666,27 @@
new FilterFunctionListValue(filter_list_b.Pass()));
EXPECT_FALSE(value_a->Equals(*value_b));
+
+ FilterFunctionListValue::Builder filter_list_c;
+ filter_list_c.push_back(
+ new MTMFunction(MTMFunction::kEquirectangular, 8.5f, 3.14f,
+ glm::mat4(1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
+ scoped_refptr<FilterFunctionListValue> value_c(
+ new FilterFunctionListValue(filter_list_c.Pass()));
+
+ FilterFunctionListValue::Builder filter_list_d;
+ filter_list_d.push_back(new MTMFunction(
+ new URLValue("format.msh"),
+ MTMFunction::ResolutionMatchedMeshListBuilder().Pass(), 8.5f, 3.14f,
+ glm::mat4(1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
+ scoped_refptr<FilterFunctionListValue> value_d(
+ new FilterFunctionListValue(filter_list_d.Pass()));
+
+ EXPECT_FALSE(value_c->Equals(*value_d));
}
} // namespace cssom
diff --git a/src/cobalt/cssom/property_value_to_string_test.cc b/src/cobalt/cssom/property_value_to_string_test.cc
index 7d18941..ea7cc8e 100644
--- a/src/cobalt/cssom/property_value_to_string_test.cc
+++ b/src/cobalt/cssom/property_value_to_string_test.cc
@@ -356,7 +356,21 @@
1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
KeywordValue::GetMonoscopic()));
EXPECT_EQ(
- "-cobalt-mtm(url(-.msh), "
+ "map-to-mesh(url(-.msh), "
+ "2.5rad 3.14rad, "
+ "matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
+ "monoscopic)",
+ function->ToString());
+}
+
+TEST(PropertyValueToStringTest, MTMFunctionEquirectangular) {
+ scoped_ptr<MTMFunction> function(
+ new MTMFunction(MTMFunction::kEquirectangular, 2.5f, 3.14f,
+ glm::mat4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
+ KeywordValue::GetMonoscopic()));
+ EXPECT_EQ(
+ "map-to-mesh(equirectangular, "
"2.5rad 3.14rad, "
"matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
"monoscopic)",
@@ -375,7 +389,7 @@
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
KeywordValue::GetStereoscopicLeftRight()));
EXPECT_EQ(
- "-cobalt-mtm(url(-.msh) 1920 2000000 url(a.msh) 640 5 url(b.msh), "
+ "map-to-mesh(url(-.msh) 1920 2000000 url(a.msh) 640 5 url(b.msh), "
"28.5rad 3.14rad, "
"matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
"stereoscopic-left-right)",
@@ -413,19 +427,19 @@
new FilterFunctionListValue(filter_list.Pass()));
EXPECT_EQ(
- "-cobalt-mtm(url(-.msh), "
+ "map-to-mesh(url(-.msh), "
"8.5rad 3.14rad, "
"matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
"monoscopic) "
- "-cobalt-mtm(url(world.msh), "
+ "map-to-mesh(url(world.msh), "
"8.5rad 39rad, "
"matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
"monoscopic) "
- "-cobalt-mtm(url(stereoscopic-world.msh), "
+ "map-to-mesh(url(stereoscopic-world.msh), "
"8.5rad 39rad, "
"matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
"stereoscopic-left-right) "
- "-cobalt-mtm(url(stereoscopic-top-bottom-world.msh), "
+ "map-to-mesh(url(stereoscopic-top-bottom-world.msh), "
"8.5rad 39rad, "
"matrix3d(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), "
"stereoscopic-top-bottom)",
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 71ab518..02775e4 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -179,6 +179,10 @@
'memory_info.h',
'mime_type_array.cc',
'mime_type_array.h',
+ 'mutation_observer.cc',
+ 'mutation_observer.h',
+ 'mutation_observer_init.h',
+ 'mutation_record.h',
'named_node_map.cc',
'named_node_map.h',
'navigator.cc',
diff --git a/src/cobalt/dom/dom_stat_tracker.h b/src/cobalt/dom/dom_stat_tracker.h
index 03ad867..80e1208 100644
--- a/src/cobalt/dom/dom_stat_tracker.h
+++ b/src/cobalt/dom/dom_stat_tracker.h
@@ -50,6 +50,8 @@
void OnUpdateMatchingRules();
void OnUpdateComputedStyle();
+ int total_html_elements() const { return total_html_elements_; }
+
int html_elements_created_count() const {
return html_elements_created_count_;
}
diff --git a/src/cobalt/dom/global_stats.h b/src/cobalt/dom/global_stats.h
index 67ff3d3..0fc2b08 100644
--- a/src/cobalt/dom/global_stats.h
+++ b/src/cobalt/dom/global_stats.h
@@ -61,6 +61,9 @@
void Remove(NodeList* object);
void RemoveEventListener();
+ int GetNumEventListeners() const { return num_event_listeners_; }
+ int GetNumNodes() const { return num_nodes_; }
+
void StartDispatchEvent();
void StopDispatchEvent();
diff --git a/src/cobalt/dom/mutation_observer.cc b/src/cobalt/dom/mutation_observer.cc
new file mode 100644
index 0000000..3cfa7a0
--- /dev/null
+++ b/src/cobalt/dom/mutation_observer.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/dom/mutation_observer.h"
+
+namespace cobalt {
+namespace dom {
+// Abstract base class for a MutationCallback.
+class MutationObserver::CallbackInternal {
+ public:
+ virtual void RunCallback(const MutationRecordSequence& mutations,
+ const scoped_refptr<MutationObserver>& observer) = 0;
+ virtual ~CallbackInternal() {}
+};
+
+namespace {
+// Implement the CallbackInternal interface for a JavaScript callback.
+class ScriptCallback : public MutationObserver::CallbackInternal {
+ public:
+ ScriptCallback(const MutationObserver::MutationCallbackArg& callback,
+ MutationObserver* owner)
+ : callback_(owner, callback) {}
+ void RunCallback(const MutationObserver::MutationRecordSequence& mutations,
+ const scoped_refptr<MutationObserver>& observer) OVERRIDE {
+ callback_.value().Run(mutations, observer);
+ }
+
+ private:
+ MutationObserver::MutationCallbackArg::Reference callback_;
+};
+
+// Implement the CallbackInternal interface for a native callback.
+class NativeCallback : public MutationObserver::CallbackInternal {
+ public:
+ explicit NativeCallback(
+ const MutationObserver::NativeMutationCallback& callback)
+ : callback_(callback) {}
+ void RunCallback(const MutationObserver::MutationRecordSequence& mutations,
+ const scoped_refptr<MutationObserver>& observer) OVERRIDE {
+ callback_.Run(mutations, observer);
+ }
+
+ private:
+ MutationObserver::NativeMutationCallback callback_;
+};
+} // namespace
+
+MutationObserver::MutationObserver(
+ const NativeMutationCallback& native_callback) {
+ callback_.reset(new NativeCallback(native_callback));
+}
+
+MutationObserver::MutationObserver(const MutationCallbackArg& callback) {
+ callback_.reset(new ScriptCallback(callback, this));
+}
+
+void MutationObserver::Observe(const scoped_refptr<Node>& target,
+ const MutationObserverInit& options) {
+ UNREFERENCED_PARAMETER(target);
+ UNREFERENCED_PARAMETER(options);
+ NOTIMPLEMENTED();
+}
+
+void MutationObserver::Disconnect() { NOTIMPLEMENTED(); }
+
+MutationObserver::MutationRecordSequence MutationObserver::TakeRecords() {
+ NOTIMPLEMENTED();
+ return MutationRecordSequence();
+}
+
+} // namespace dom
+} // namespace cobalt
diff --git a/src/cobalt/dom/mutation_observer.h b/src/cobalt/dom/mutation_observer.h
new file mode 100644
index 0000000..a78030a
--- /dev/null
+++ b/src/cobalt/dom/mutation_observer.h
@@ -0,0 +1,73 @@
+/*
+ * 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_DOM_MUTATION_OBSERVER_H_
+#define COBALT_DOM_MUTATION_OBSERVER_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "cobalt/dom/mutation_observer_init.h"
+#include "cobalt/dom/mutation_record.h"
+#include "cobalt/dom/node.h"
+#include "cobalt/script/callback_function.h"
+#include "cobalt/script/sequence.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+// A MutationObserver object can be used to observe mutations to the tree of
+// nodes.
+// https://www.w3.org/TR/dom/#mutationobserver
+class MutationObserver : public script::Wrappable {
+ public:
+ typedef script::Sequence<scoped_refptr<MutationRecord> >
+ MutationRecordSequence;
+
+ typedef script::CallbackFunction<void(const MutationRecordSequence&,
+ const scoped_refptr<MutationObserver>&)>
+ MutationCallback;
+ typedef script::ScriptObject<MutationCallback> MutationCallbackArg;
+
+ typedef base::Callback<void(const MutationRecordSequence&,
+ const scoped_refptr<MutationObserver>&)>
+ NativeMutationCallback;
+
+ // Not part of the spec. Support creating MutationObservers from native Cobalt
+ // code.
+ explicit MutationObserver(const NativeMutationCallback& native_callback);
+
+ // Web Api: MutationObserver
+ explicit MutationObserver(const MutationCallbackArg& callback);
+ void Observe(const scoped_refptr<Node>& target,
+ const MutationObserverInit& options);
+ void Disconnect();
+ MutationRecordSequence TakeRecords();
+
+ // Internal helper class to allow creation of a MutationObserver with either a
+ // native or script callback. Must be public so it can be inherited from in
+ // the .cc file.
+ class CallbackInternal;
+
+ private:
+ scoped_ptr<CallbackInternal> callback_;
+};
+} // namespace dom
+} // namespace cobalt
+#endif // COBALT_DOM_MUTATION_OBSERVER_H_
diff --git a/src/cobalt/dom/mutation_observer_init.h b/src/cobalt/dom/mutation_observer_init.h
new file mode 100644
index 0000000..3e3c0ea
--- /dev/null
+++ b/src/cobalt/dom/mutation_observer_init.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_DOM_MUTATION_OBSERVER_INIT_H_
+#define COBALT_DOM_MUTATION_OBSERVER_INIT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/optional.h"
+
+namespace cobalt {
+namespace dom {
+
+// Represents the MutationObserverInit dictionary type that is used to
+// initialize the MutationObserver interface.
+// https://www.w3.org/TR/dom/#mutationobserver
+struct MutationObserverInit {
+ typedef std::vector<std::string> StringSequence;
+
+ // Default values as specified in the IDL.
+ MutationObserverInit() : child_list(false), subtree(false) {}
+
+ // Dictionary members:
+ bool child_list;
+ base::optional<bool> attributes;
+ base::optional<bool> character_data;
+ bool subtree;
+ base::optional<bool> attribute_old_value;
+ base::optional<bool> character_data_old_value;
+ base::optional<StringSequence> attribute_filter;
+};
+} // namespace dom
+} // namespace cobalt
+
+#endif // COBALT_DOM_MUTATION_OBSERVER_INIT_H_
diff --git a/src/cobalt/dom/mutation_record.h b/src/cobalt/dom/mutation_record.h
new file mode 100644
index 0000000..cd43589
--- /dev/null
+++ b/src/cobalt/dom/mutation_record.h
@@ -0,0 +1,50 @@
+/*
+ * 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_DOM_MUTATION_RECORD_H_
+#define COBALT_DOM_MUTATION_RECORD_H_
+
+#include <string>
+
+#include "base/optional.h"
+#include "cobalt/dom/node.h"
+#include "cobalt/dom/node_list.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace dom {
+
+// MutationRecords are used with the MutationObserver interface to describe a
+// mutation on the documnet.
+// https://www.w3.org/TR/dom/#mutationrecord
+class MutationRecord : public script::Wrappable {
+ public:
+ // Web API: MutationRecord
+ std::string type() { return ""; }
+ scoped_refptr<dom::Node> target() { return NULL; }
+ scoped_refptr<dom::NodeList> added_nodes() { return NULL; }
+ scoped_refptr<dom::NodeList> removed_nodes() { return NULL; }
+ scoped_refptr<dom::Node> previous_sibling() { return NULL; }
+ scoped_refptr<dom::Node> next_sibling() { return NULL; }
+ base::optional<std::string> attribute_name() { return base::nullopt; }
+ base::optional<std::string> attribute_namespace() { return base::nullopt; }
+ base::optional<std::string> old_value() { return base::nullopt; }
+
+ DEFINE_WRAPPABLE_TYPE(MutationRecord);
+};
+} // namespace dom
+} // namespace cobalt
+#endif // COBALT_DOM_MUTATION_RECORD_H_
diff --git a/src/cobalt/layout/layout_stat_tracker.h b/src/cobalt/layout/layout_stat_tracker.h
index 247aadd..195cce9 100644
--- a/src/cobalt/layout/layout_stat_tracker.h
+++ b/src/cobalt/layout/layout_stat_tracker.h
@@ -49,6 +49,8 @@
void OnBoxCreated();
void OnBoxDestroyed();
+ int total_boxes() const { return total_boxes_; }
+
int boxes_created_count() const { return boxes_created_count_; }
int boxes_destroyed_count() const { return boxes_destroyed_count_; }
diff --git a/src/cobalt/media/fetcher_buffered_data_source.cc b/src/cobalt/media/fetcher_buffered_data_source.cc
index fd8511b..3306431 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.cc
+++ b/src/cobalt/media/fetcher_buffered_data_source.cc
@@ -59,7 +59,9 @@
DCHECK(network_module);
}
-FetcherBufferedDataSource::~FetcherBufferedDataSource() { DCHECK(!fetcher_); }
+FetcherBufferedDataSource::~FetcherBufferedDataSource() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+}
void FetcherBufferedDataSource::Read(int64 position, int size, uint8* data,
const ReadCB& read_cb) {
@@ -84,8 +86,15 @@
base::ResetAndReturn(&pending_read_cb_).Run(0);
}
// From this moment on, any call to Read() should be treated as an error.
+ // Note that we cannot reset |fetcher_| here because of:
+ // 1. Fetcher has to be destroyed on the thread that it is created, however
+ // Stop() is usually called from the pipeline thread where |fetcher_| is
+ // created on the web thread.
+ // 2. We cannot post a task to the web thread to destroy |fetcher_| as the
+ // web thread is blocked by WMPI::Destroy().
+ // Once error_occured_ is set to true, the fetcher callbacks return
+ // immediately so it is safe to destroy |fetcher_| inside the dtor.
error_occured_ = true;
- DestroyFetcher(fetcher_.Pass());
}
// We cannot post the callback using the MessageLoop as we share the
@@ -112,12 +121,11 @@
DCHECK(message_loop_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
- if (fetcher_.get() != source) {
+
+ if (fetcher_.get() != source || error_occured_) {
return;
}
- if (error_occured_) {
- return;
- }
+
if (!source->GetStatus().is_success()) {
// The error will be handled on OnURLFetchComplete()
error_occured_ = true;
@@ -181,10 +189,8 @@
}
const uint8* data = reinterpret_cast<const uint8*>(download_data->data());
base::AutoLock auto_lock(lock_);
- if (fetcher_.get() != source) {
- return;
- }
- if (error_occured_) {
+
+ if (fetcher_.get() != source || error_occured_) {
return;
}
@@ -193,7 +199,7 @@
if (size == 0) {
// The server side doesn't support range request. Delete the fetcher to
// stop the current request.
- DestroyFetcher(fetcher_.Pass());
+ fetcher_.reset();
ProcessPendingRead_Locked();
return;
}
@@ -245,9 +251,11 @@
DCHECK(message_loop_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
- if (fetcher_.get() != source) {
+
+ if (fetcher_.get() != source || error_occured_) {
return;
}
+
const net::URLRequestStatus& status = source->GetStatus();
if (status.is_success()) {
if (total_size_of_resource_ && last_request_size_ != 0) {
@@ -266,21 +274,13 @@
ProcessPendingRead_Locked();
}
-void FetcherBufferedDataSource::DestroyFetcher(
- scoped_ptr<net::URLFetcher> fetcher) {
- if (fetcher && !message_loop_->BelongsToCurrentThread()) {
- message_loop_->PostTask(
- FROM_HERE, base::Bind(&FetcherBufferedDataSource::DestroyFetcher, this,
- base::Passed(&fetcher)));
- return;
- }
- // Do nothing as |fetcher| will destroy itself.
-}
-
void FetcherBufferedDataSource::CreateNewFetcher() {
DCHECK(message_loop_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
+
+ fetcher_.reset();
+
DCHECK_GE(static_cast<int64>(last_request_offset_), 0);
DCHECK_GE(static_cast<int64>(last_request_size_), 0);
@@ -405,7 +405,6 @@
last_request_offset_ = buffer_offset_ + buffer_.GetLength();
}
- DestroyFetcher(fetcher_.Pass());
message_loop_->PostTask(
FROM_HERE,
base::Bind(&FetcherBufferedDataSource::CreateNewFetcher, this));
diff --git a/src/cobalt/media/fetcher_buffered_data_source.h b/src/cobalt/media/fetcher_buffered_data_source.h
index 9c64a7f..f8e56ae 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.h
+++ b/src/cobalt/media/fetcher_buffered_data_source.h
@@ -81,8 +81,6 @@
scoped_ptr<std::string> download_data) OVERRIDE;
void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
- // This function can be posted to |message_loop_| to destroy a fetcher on it.
- void DestroyFetcher(scoped_ptr<net::URLFetcher> fetcher);
void CreateNewFetcher();
void Read_Locked(uint64 position, size_t size, uint8* data,
const ReadCB& read_cb);
diff --git a/src/cobalt/media/media2.gyp b/src/cobalt/media/media2.gyp
index 7c53993..74ad424 100644
--- a/src/cobalt/media/media2.gyp
+++ b/src/cobalt/media/media2.gyp
@@ -181,7 +181,9 @@
],
},
{
- 'target_name': 'media_unittests',
+ # Rename 'media2_unittests' to 'media_unittests' once the original media
+ # is removed.
+ 'target_name': 'media2_unittests',
'type': '<(gtest_target_type)',
'dependencies': [
'media2',
@@ -291,14 +293,18 @@
],
},
{
- 'target_name': 'qt_faststart',
+ # Rename 'media2_qt_faststart' to 'qt_faststart' once the original media
+ # is removed.
+ 'target_name': 'media2_qt_faststart',
'type': 'executable',
'sources': [
'tools/qt_faststart/qt_faststart.c'
],
},
{
- 'target_name': 'seek_tester',
+ # Rename 'media2_seek_tester' to 'seek_tester' once the original media is
+ # removed.
+ 'target_name': 'media2_seek_tester',
'type': 'executable',
'dependencies': [
'media2',
@@ -309,7 +315,9 @@
],
},
{
- 'target_name': 'demuxer_bench',
+ # Rename 'media2_demuxer_bench' to 'demuxer_bench' once the original media
+ # is removed.
+ 'target_name': 'media2_demuxer_bench',
'type': 'executable',
'dependencies': [
'media2',
diff --git a/src/cobalt/media/sandbox/media2_sandbox.cc b/src/cobalt/media/sandbox/media2_sandbox.cc
new file mode 100644
index 0000000..585ba73
--- /dev/null
+++ b/src/cobalt/media/sandbox/media2_sandbox.cc
@@ -0,0 +1,159 @@
+/*
+ * 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 "base/bind.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "cobalt/base/wrap_main.h"
+#include "cobalt/media/base/media_log.h"
+#include "cobalt/media/filters/chunk_demuxer.h"
+#include "starboard/event.h"
+
+namespace cobalt {
+namespace media {
+namespace sandbox {
+
+using ::media::ChunkDemuxer;
+using ::media::DecoderBuffer;
+using ::media::DemuxerStream;
+using ::media::MediaTracks;
+
+namespace {
+
+class DemuxerHostStub : public ::media::DemuxerHost {
+ void OnBufferedTimeRangesChanged(
+ const ::media::Ranges<base::TimeDelta>& ranges) OVERRIDE {}
+
+ void SetDuration(base::TimeDelta duration) OVERRIDE {}
+
+ void OnDemuxerError(::media::PipelineStatus error) OVERRIDE {}
+
+ void AddTextStream(::media::DemuxerStream* text_stream,
+ const ::media::TextTrackConfig& config) OVERRIDE {}
+
+ void RemoveTextStream(::media::DemuxerStream* text_stream) OVERRIDE {}
+};
+
+void OnDemuxerOpen() {}
+
+void OnEncryptedMediaInitData(::media::EmeInitDataType type,
+ const std::vector<uint8_t>& init_data) {}
+
+void OnInitSegmentReceived(scoped_ptr<MediaTracks> tracks) {}
+
+void OnDemuxerStatus(::media::PipelineStatus status) {
+ status = ::media::PIPELINE_OK;
+}
+
+std::string LoadFile(const std::string& file_name) {
+ FilePath file_path(file_name);
+ if (!file_path.IsAbsolute()) {
+ FilePath content_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &content_path);
+ DCHECK(content_path.IsAbsolute());
+ file_path = content_path.Append(file_path);
+ }
+
+ std::string content;
+ if (!file_util::ReadFileToString(file_path, &content)) {
+ LOG(ERROR) << "Failed to load file " << file_path.value();
+ return "";
+ }
+ return content;
+}
+
+const char* GetDemuxerStreamType(DemuxerStream* demuxer_stream) {
+ return demuxer_stream->type() == DemuxerStream::AUDIO ? "audio" : "video";
+}
+
+void ReadDemuxerStream(DemuxerStream* demuxer_stream);
+
+void OnDemuxerStreamRead(DemuxerStream* demuxer_stream,
+ DemuxerStream::Status status,
+ const scoped_refptr<DecoderBuffer>& decoder_buffer) {
+ if (!decoder_buffer->end_of_stream()) {
+ LOG(INFO) << "Reading " << GetDemuxerStreamType(demuxer_stream)
+ << " buffer at " << decoder_buffer->timestamp();
+ ReadDemuxerStream(demuxer_stream);
+ } else {
+ LOG(INFO) << "Received " << GetDemuxerStreamType(demuxer_stream) << " EOS";
+ }
+}
+
+void ReadDemuxerStream(DemuxerStream* demuxer_stream) {
+ DCHECK(demuxer_stream);
+ demuxer_stream->Read(
+ base::Bind(OnDemuxerStreamRead, base::Unretained(demuxer_stream)));
+}
+
+} // namespace
+
+int SandboxMain(int argc, char** argv) {
+ if (argc != 3) {
+ // Path should be in the form of
+ // "cobalt/browser/testdata/media-element-demo/dash-video-240p.mp4".
+ LOG(ERROR) << "Usage: " << argv[0] << " <audio_path> <video_path>";
+ return 1;
+ }
+
+ MessageLoop message_loop;
+
+ DemuxerHostStub demuxer_host;
+ scoped_ptr<ChunkDemuxer> demuxer(new ChunkDemuxer(
+ base::Bind(OnDemuxerOpen), base::Bind(OnEncryptedMediaInitData),
+ new ::media::MediaLog, false));
+ demuxer->Initialize(&demuxer_host, base::Bind(OnDemuxerStatus), false);
+
+ ChunkDemuxer::Status status =
+ demuxer->AddId("audio", "audio/mp4", "mp4a.40.2");
+ DCHECK_EQ(status, ChunkDemuxer::kOk);
+ status = demuxer->AddId("video", "video/mp4", "avc1.640028");
+ DCHECK_EQ(status, ChunkDemuxer::kOk);
+ base::TimeDelta timestamp_offset;
+
+ std::string audio_content = LoadFile(argv[1]);
+ std::string video_content = LoadFile(argv[2]);
+ DCHECK(!audio_content.empty());
+ DCHECK(!video_content.empty());
+
+ demuxer->SetTracksWatcher("audio", base::Bind(OnInitSegmentReceived));
+ demuxer->SetTracksWatcher("video", base::Bind(OnInitSegmentReceived));
+ demuxer->AppendData("audio", reinterpret_cast<uint8*>(&audio_content[0]),
+ audio_content.size(), base::TimeDelta(),
+ base::TimeDelta::Max(), ×tamp_offset);
+ demuxer->AppendData("video", reinterpret_cast<uint8*>(&video_content[0]),
+ video_content.size(), base::TimeDelta(),
+ base::TimeDelta::Max(), ×tamp_offset);
+ demuxer->MarkEndOfStream(::media::PIPELINE_OK);
+
+ DemuxerStream* audio_stream = demuxer->GetStream(DemuxerStream::AUDIO);
+ DemuxerStream* video_stream = demuxer->GetStream(DemuxerStream::VIDEO);
+
+ ReadDemuxerStream(audio_stream);
+ ReadDemuxerStream(video_stream);
+
+ message_loop.RunUntilIdle();
+
+ demuxer->Stop();
+
+ return 0;
+}
+
+} // namespace sandbox
+} // namespace media
+} // namespace cobalt
+
+COBALT_WRAP_SIMPLE_MAIN(cobalt::media::sandbox::SandboxMain);
diff --git a/src/cobalt/media/sandbox/sandbox.gyp b/src/cobalt/media/sandbox/sandbox.gyp
index 3290d35..1e1783e 100644
--- a/src/cobalt/media/sandbox/sandbox.gyp
+++ b/src/cobalt/media/sandbox/sandbox.gyp
@@ -99,6 +99,22 @@
},
],
'conditions': [
+ ['OS=="starboard" and sb_media_platform == "starboard"', {
+ 'targets': [
+ {
+ 'target_name': 'media2_sandbox',
+ 'type': '<(final_executable_type)',
+ 'sources': [
+ 'media2_sandbox.cc',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/cobalt/base/base.gyp:base',
+ '<(DEPTH)/cobalt/math/math.gyp:math',
+ '<(DEPTH)/cobalt/media/media2.gyp:media2',
+ ],
+ },
+ ],
+ }],
['OS == "starboard" and has_zzuf == "True"', {
'targets': [
# This target will build a sandbox application that allows for fuzzing
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index e19c59c..568b2b2 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -80,9 +80,11 @@
submit_even_if_render_tree_is_unchanged),
last_render_animations_active_(false),
rasterize_periodic_timer_("Renderer.Rasterize.Duration",
- kRasterizePeriodicTimerEntriesPerUpdate),
+ kRasterizePeriodicTimerEntriesPerUpdate,
+ false /*enable_entry_list_c_val*/),
rasterize_animations_timer_("Renderer.Rasterize.Animations",
- kRasterizeAnimationsTimerMaxEntries),
+ kRasterizeAnimationsTimerMaxEntries,
+ true /*enable_entry_list_c_val*/),
has_active_animations_c_val_(
"Renderer.HasActiveAnimations", false,
"Is non-zero if the current render tree has active animations.")
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
index 5e64ba7..b76eee8 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.cc
@@ -306,6 +306,15 @@
eval_enabled_ = true;
}
+void MozjsGlobalEnvironment::DisableJit() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ JS::RuntimeOptionsRef(context_)
+ .setBaseline(false)
+ .setIon(false)
+ .setAsmJS(false)
+ .setNativeRegExp(false);
+}
+
void MozjsGlobalEnvironment::SetReportEvalCallback(
const base::Closure& report_eval) {
DCHECK(thread_checker_.CalledOnValidThread());
diff --git a/src/cobalt/script/mozjs-45/mozjs_global_environment.h b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
index dcee058..ce17451 100644
--- a/src/cobalt/script/mozjs-45/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs-45/mozjs_global_environment.h
@@ -71,6 +71,8 @@
void EnableEval() OVERRIDE;
+ void DisableJit() OVERRIDE;
+
void SetReportEvalCallback(const base::Closure& report_eval) OVERRIDE;
void Bind(const std::string& identifier,
diff --git a/src/cobalt/speech/microphone_fake.cc b/src/cobalt/speech/microphone_fake.cc
index 1ac01bd..31d372b 100644
--- a/src/cobalt/speech/microphone_fake.cc
+++ b/src/cobalt/speech/microphone_fake.cc
@@ -25,6 +25,7 @@
#include "base/rand_util.h"
#include "cobalt/audio/audio_file_reader.h"
#include "starboard/file.h"
+#include "starboard/log.h"
#include "starboard/memory.h"
#include "starboard/time.h"
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc
index 4eab7de..5e40ee2 100644
--- a/src/cobalt/speech/speech_recognition_manager.cc
+++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -111,7 +111,7 @@
microphone_creator));
}
-SpeechRecognitionManager::~SpeechRecognitionManager() { Stop(); }
+SpeechRecognitionManager::~SpeechRecognitionManager() { Stop(false); }
void SpeechRecognitionManager::Start(const SpeechRecognitionConfig& config,
script::ExceptionState* exception_state) {
@@ -131,7 +131,7 @@
state_ = kStarted;
}
-void SpeechRecognitionManager::Stop() {
+void SpeechRecognitionManager::Stop(bool run_callback) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
// If the stop method is called on an object which is already stopped or being
@@ -144,7 +144,9 @@
microphone_manager_->Close();
recognizer_->Stop();
state_ = kStopped;
- event_callback_.Run(new dom::Event(base::Tokens::soundend()));
+ if (run_callback) {
+ event_callback_.Run(new dom::Event(base::Tokens::soundend()));
+ }
}
void SpeechRecognitionManager::Abort() {
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h
index 2d622dd..323c389 100644
--- a/src/cobalt/speech/speech_recognition_manager.h
+++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -54,7 +54,7 @@
// managed by their own class.
void Start(const SpeechRecognitionConfig& config,
script::ExceptionState* exception_state);
- void Stop();
+ void Stop(bool run_callback = true);
void Abort();
private:
diff --git a/src/cobalt/test/empty_document.h b/src/cobalt/test/empty_document.h
new file mode 100644
index 0000000..9fae1b9
--- /dev/null
+++ b/src/cobalt/test/empty_document.h
@@ -0,0 +1,50 @@
+/*
+ * 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_TEST_EMPTY_DOCUMENT_H_
+#define COBALT_TEST_EMPTY_DOCUMENT_H_
+
+#include "cobalt/css_parser/parser.h"
+#include "cobalt/dom/document.h"
+#include "cobalt/dom/dom_stat_tracker.h"
+#include "cobalt/dom/html_element_context.h"
+
+namespace cobalt {
+namespace test {
+
+// This is a helper class for tests that want an empty dom::Document, which can
+// be used to create other dom::Element instances.
+class EmptyDocument {
+ public:
+ EmptyDocument()
+ : css_parser_(css_parser::Parser::Create()),
+ dom_stat_tracker_(new dom::DomStatTracker("EmptyDocument")),
+ html_element_context_(NULL, css_parser_.get(), NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ dom_stat_tracker_.get(), ""),
+ document_(new dom::Document(&html_element_context_)) {}
+
+ dom::Document* document() { return document_.get(); }
+
+ private:
+ scoped_ptr<css_parser::Parser> css_parser_;
+ scoped_ptr<dom::DomStatTracker> dom_stat_tracker_;
+ dom::HTMLElementContext html_element_context_;
+ scoped_refptr<dom::Document> document_;
+};
+} // namespace test
+} // namespace cobalt
+#endif // COBALT_TEST_EMPTY_DOCUMENT_H_
diff --git a/src/cobalt/tools/binary_to_callgraph.sh b/src/cobalt/tools/binary_to_callgraph.sh
new file mode 100755
index 0000000..375dc7e
--- /dev/null
+++ b/src/cobalt/tools/binary_to_callgraph.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# 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.
+#
+# Create a callgraph from an x86 or x86-64 binary.
+#
+# Example Usage:
+# cobalt/tools/binary_to_callgraph.sh out/linux-x64x11_debug/cobalt > callgraph.txt
+#
+# TODO: It'd be cool to include "...->PostTask(...)" stuff in here too.
+# TODO: It'd be cool to output to a sqlite database instead of text, and then
+# have a command line program to search through it.
+# TODO: It'd be cool to support ARM and MIPS too.
+
+set -e
+
+objdump -d "$1" |
+python3 -c '
+import collections
+import re
+import sys
+
+caller = None
+callee = None
+graph = collections.defaultdict(set)
+for line in sys.stdin:
+ m = re.match(r"\w+ <(.*?)>:", line)
+ if m:
+ caller = m.group(1)
+ continue
+
+ m = re.match(".*callq?.*?<(.*?)>", line)
+ if m:
+ callee = m.group(1)
+ graph[caller].add(callee)
+
+for key, value in graph.items():
+ for item in value:
+ print(key)
+ print(item)
+' | c++filt |
+python3 -c '
+import sys
+
+buffer = []
+for line in sys.stdin:
+ buffer.append(line.rstrip())
+ if len(buffer) == 2:
+ print("{} @@ {}".format(*buffer))
+ buffer = []
+'
+
+
diff --git a/src/cobalt/webdriver_benchmarks/c_val_names.py b/src/cobalt/webdriver_benchmarks/c_val_names.py
index d1fdfa0..9e2a1aa 100644
--- a/src/cobalt/webdriver_benchmarks/c_val_names.py
+++ b/src/cobalt/webdriver_benchmarks/c_val_names.py
@@ -4,140 +4,34 @@
from __future__ import division
from __future__ import print_function
-EVENT_COUNT_STRING = "Event.Count.MainWebModule"
-EVENT_DURATION_STRING = "Event.Duration.MainWebModule"
-
def count_dom_active_dispatch_events():
return "Count.DOM.ActiveDispatchEvents"
-def count_dom_event_listeners():
- return "Count.DOM.EventListeners"
-
-
-def count_dom_nodes():
- return "Count.DOM.Nodes"
-
-
-def count_dom_html_elements():
- return "Count.MainWebModule.DOM.HtmlElement"
-
-
def count_image_cache_loading_resources():
return "Count.MainWebModule.ImageCache.LoadingResources"
-def count_image_cache_pending_callbacks():
- return "Count.MainWebModule.ImageCache.PendingCallbacks"
-
-
-def count_layout_boxes():
- return "Count.MainWebModule.Layout.Box"
-
-
def count_font_files_loaded():
return "Count.Font.FilesLoaded"
-def count_remote_typeface_cache_loading_resources():
- return "Count.MainWebModule.RemoteTypefaceCache.LoadingResources"
-
-
-def count_remote_typeface_cache_pending_callbacks():
- return "Count.MainWebModule.RemoteTypefaceCache.PendingCallbacks"
-
-
-def event_produced_render_tree(event_type):
- return "Event.MainWebModule.{}.ProducedRenderTree".format(event_type)
-
-
-def event_count_dom_html_elements_created(event_type):
- return "{}.{}.DOM.HtmlElement.Created".format(EVENT_COUNT_STRING, event_type)
-
-
-def event_count_dom_html_elements_destroyed(event_type):
- return "{}.{}.DOM.HtmlElement.Destroyed".format(EVENT_COUNT_STRING,
- event_type)
-
-
-def event_count_dom_update_matching_rules(event_type):
- return "{}.{}.DOM.HtmlElement.UpdateMatchingRules".format(EVENT_COUNT_STRING,
- event_type)
-
-
-def event_count_dom_update_computed_style(event_type):
- return "{}.{}.DOM.HtmlElement.UpdateComputedStyle".format(EVENT_COUNT_STRING,
- event_type)
-
-
-def event_count_layout_boxes_created(event_type):
- return "{}.{}.Layout.Box.Created".format(EVENT_COUNT_STRING, event_type)
-
-
-def event_count_layout_boxes_destroyed(event_type):
- return "{}.{}.Layout.Box.Destroyed".format(EVENT_COUNT_STRING, event_type)
-
-
-def event_duration_total(event_type):
- return "{}.{}".format(EVENT_DURATION_STRING, event_type)
-
-
-def event_duration_dom_inject_event(event_type):
- return "{}.{}.DOM.InjectEvent".format(EVENT_DURATION_STRING, event_type)
-
-
-def event_duration_dom_update_computed_style(event_type):
- return "{}.{}.DOM.UpdateComputedStyle".format(EVENT_DURATION_STRING,
- event_type)
-
-
def event_duration_dom_video_start_delay():
- return "{}.DOM.VideoStartDelay".format(EVENT_DURATION_STRING)
+ return "Event.Duration.MainWebModule.DOM.VideoStartDelay"
-def event_duration_layout_box_tree(event_type):
- return "{}.{}.Layout.BoxTree".format(EVENT_DURATION_STRING, event_type)
-
-
-def event_duration_layout_box_tree_box_generation(event_type):
- return "{}.{}.Layout.BoxTree.BoxGeneration".format(EVENT_DURATION_STRING,
- event_type)
-
-
-def event_duration_layout_box_tree_update_used_sizes(event_type):
- return "{}.{}.Layout.BoxTree.UpdateUsedSizes".format(EVENT_DURATION_STRING,
- event_type)
-
-
-def event_duration_layout_render_and_animate(event_type):
- return "{}.{}.Layout.RenderAndAnimate".format(EVENT_DURATION_STRING,
- event_type)
+def event_value_dictionary(event_type):
+ return "Event.MainWebModule.{}.ValueDictionary".format(event_type)
def layout_is_dirty():
return "MainWebModule.Layout.IsDirty"
+def rasterize_animations_entry_list():
+ return "Renderer.Rasterize.Animations.EntryList"
+
+
def renderer_has_active_animations():
return "Renderer.HasActiveAnimations"
-
-
-def rasterize_animations_average():
- return "Renderer.Rasterize.Animations.Avg"
-
-
-def rasterize_animations_percentile_25():
- return "Renderer.Rasterize.Animations.Pct.25th"
-
-
-def rasterize_animations_percentile_50():
- return "Renderer.Rasterize.Animations.Pct.50th"
-
-
-def rasterize_animations_percentile_75():
- return "Renderer.Rasterize.Animations.Pct.75th"
-
-
-def rasterize_animations_percentile_95():
- return "Renderer.Rasterize.Animations.Pct.95th"
diff --git a/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
index e829a80..9551d9d 100755
--- a/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
+++ b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
@@ -19,6 +19,7 @@
from __future__ import division
from __future__ import print_function
+import datetime
import os
import sys
@@ -36,20 +37,23 @@
"""This test tries to measure the startup time for the YouTube TV page.
Specifically, this test uses the Cobalt CVal Cobalt.Lifetime, which gets
- updated ~60Hz on a best effort basis. This value is in microseconds.
+ updated ~60Hz on a best effort basis and is in microseconds, to determine
+ "timeToShelfBlankStartupTimeUs" and uses Python's datetime module to
+ determine "timeToShelfTestTimeShelfDisplayMedianUs".
Note: t0 is defined after Cobalt starts up, but has not navigated to a page.
If that true startup time metric is desired, perhaps a separate should be
used.
"""
metrics_array = []
- blank_startup_time_microseconds = self.get_int_cval('Cobalt.Lifetime')
+ blank_startup_time_microseconds = self.get_cval('Cobalt.Lifetime')
for _ in range(10):
- t0 = self.get_int_cval('Cobalt.Lifetime')
+ t0 = datetime.datetime.now()
self.load_tv()
self.wait_for_processing_complete_after_focused_shelf()
- t1 = self.get_int_cval('Cobalt.Lifetime')
- startup_time_microseconds = t1 - t0
+ t1 = datetime.datetime.now()
+ delta = t1 - t0
+ startup_time_microseconds = delta.seconds * 1000000 + delta.microseconds
metrics_array.append(startup_time_microseconds)
tv_testcase_util.record_test_result_median(
@@ -57,5 +61,6 @@
tv_testcase_util.record_test_result('timeToShelfBlankStartupTimeUs',
blank_startup_time_microseconds)
+
if __name__ == '__main__':
tv_testcase.main()
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase.py b/src/cobalt/webdriver_benchmarks/tv_testcase.py
index 19c70ab..796c860 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase.py
@@ -4,6 +4,7 @@
from __future__ import division
from __future__ import print_function
+import json
import logging
import os
import sys
@@ -75,33 +76,19 @@
return tv_testcase_runner.GetWebDriver()
def get_cval(self, cval_name):
- """Returns value of a cval.
+ """Returns the Python object represented by a JSON cval string.
Args:
cval_name: Name of the cval.
Returns:
- Value of the cval.
+ Python object represented by the JSON cval string
"""
javascript_code = "return h5vcc.cVal.getValue('{}')".format(cval_name)
- return self.get_webdriver().execute_script(javascript_code)
-
- def get_int_cval(self, cval_name):
- """Returns int value of a cval.
-
- The cval value must be an integer, if it is a float, then this function will
- throw a ValueError.
-
- Args:
- cval_name: Name of the cval.
- Returns:
- Value of the cval.
- Raises:
- ValueError if the cval is cannot be converted to int.
- """
- answer = self.get_cval(cval_name)
- if answer is None:
- return answer
- return int(answer)
+ json_result = self.get_webdriver().execute_script(javascript_code)
+ if json_result is None:
+ return None
+ else:
+ return json.loads(json_result)
def goto(self, path, query_params=None):
"""Goes to a path off of BASE_URL.
@@ -267,17 +254,11 @@
def is_processing(self, check_animations):
"""Checks to see if Cobalt is currently processing."""
- return (
- self.get_int_cval(c_val_names.count_dom_active_dispatch_events()) or
- self.get_int_cval(c_val_names.layout_is_dirty()) or
- (check_animations and
- self.get_int_cval(c_val_names.renderer_has_active_animations())) or
- self.get_int_cval(c_val_names.count_image_cache_loading_resources()) or
- self.get_int_cval(c_val_names.count_image_cache_pending_callbacks()) or
- self.get_int_cval(
- c_val_names.count_remote_typeface_cache_loading_resources()) or
- self.get_int_cval(
- c_val_names.count_remote_typeface_cache_pending_callbacks()))
+ return (self.get_cval(c_val_names.count_dom_active_dispatch_events()) or
+ self.get_cval(c_val_names.layout_is_dirty()) or
+ (check_animations and
+ self.get_cval(c_val_names.renderer_has_active_animations())) or
+ self.get_cval(c_val_names.count_image_cache_loading_resources()))
def wait_for_media_element_playing(self):
"""Waits for a video to begin playing.
@@ -287,8 +268,8 @@
required time.
"""
start_time = time.time()
- while self.get_int_cval(c_val_names.event_duration_dom_video_start_delay(
- )) == 0:
+ while self.get_cval(
+ c_val_names.event_duration_dom_video_start_delay()) == 0:
if time.time() - start_time > MEDIA_TIMEOUT_SECONDS:
raise TvTestCase.MediaTimeoutException()
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_c_val_recorder.py b/src/cobalt/webdriver_benchmarks/tv_testcase_c_val_recorder.py
deleted file mode 100644
index 4ab6a10..0000000
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_c_val_recorder.py
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/python2
-""""Records stats on the collected values of a CVal during a benchmark test."""
-
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import tv_testcase_util
-
-
-class CValRecorderBase(object):
- """"Records stats on the collected values of a CVal during a benchmark test.
-
- Handles collecting values for a specific CVal during a benchmark test and
- records statistics on those values when the test ends. The base class does not
- record any statistics and subclasses determine which statistics to record.
- """
-
- def __init__(self, test, c_val_name, record_name):
- """Initializes the event benchmark recorder.
-
- Args:
- test: specific benchmark test being run
- c_val_name: the lookup name of the CVal
- record_name: the name to use when recording CVal stats for the test
- """
- self.test = test
- self.c_val_name = c_val_name
- self.record_name = record_name
- self.values = []
-
- def collect_current_value(self):
- self.values.append(self.test.get_int_cval(self.c_val_name))
-
- def on_end_test(self):
- """Handles logic related to the end of the benchmark test."""
-
- # Only record the test results when values have been collected.
- if self.values:
- self._record_test_results()
-
- def _record_test_results(self):
- """Records test results for the collected CVal values.
-
- This function is expected to be overridden by subclasses, which will use
- it to record select statistics for the test results.
- """
- pass
-
- def _record_test_result_mean(self):
- tv_testcase_util.record_test_result_mean("{}Mean".format(self.record_name),
- self.values)
-
- def _record_test_result_percentile(self, percentile):
- tv_testcase_util.record_test_result_percentile("{}Pct{}".format(
- self.record_name, percentile), self.values, percentile)
-
-
-class FullCValRecorder(CValRecorderBase):
- """"Subclass that records full statistics of the collected values.
-
- This subclass records the mean, 25th, 50th, 75th, and 95th percentiles of the
- collected values when the test ends.
- """
-
- def _record_test_results(self):
- self._record_test_result_mean()
- self._record_test_result_percentile(25)
- self._record_test_result_percentile(50)
- self._record_test_result_percentile(75)
- self._record_test_result_percentile(95)
-
-
-class MeanCValRecorder(CValRecorderBase):
- """"Subclass that records the mean of the collected values.
-
- This subclass only records the mean of the collected values when the test
- ends.
- """
-
- def _record_test_results(self):
- self._record_test_result_mean()
-
-
-class PercentileCValRecorder(CValRecorderBase):
- """"Subclass that records a percentile of the collected values.
-
- This subclass records only records a single percentile of the collected values
- when the test ends.
- """
-
- def __init__(self, test, c_val_name, output_name, percentile_to_record):
- super(PercentileCValRecorder, self).__init__(test, c_val_name, output_name)
- self.percentile_to_record = percentile_to_record
-
- def _record_test_results(self):
- self._record_test_result_percentile(self.percentile_to_record)
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
index 3a33150..50acc3c 100644
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_event_recorder.py
@@ -6,7 +6,7 @@
from __future__ import print_function
import c_val_names
-import tv_testcase_c_val_recorder
+import tv_testcase_value_recorder
class EventRecorderOptions(object):
@@ -32,9 +32,9 @@
class EventRecorder(object):
"""Records stats on an event injected by a benchmark test.
- Handles collecting CVal event data over all of the instances of a specific
- event injected by a benchmark test and records statistics on the data when the
- test ends. The specific event types that can be injected are listed within
+ Handles collecting event data over all of the instances of a specific event
+ injected by a benchmark test and records statistics on the data when the test
+ ends. The specific event types that can be injected are listed within
tv_testcase_util.py.
Both rasterize animations and video start delay data can potentially be
@@ -59,181 +59,111 @@
self.event_type = options.event_type
self.skip_font_file_load_events = options.skip_font_file_load_events
+ if self.skip_font_file_load_events:
+ self.font_files_loaded_count = self.test.get_cval(
+ c_val_names.count_font_files_loaded())
+
self.render_tree_failure_count = 0
self.font_file_load_skip_count = 0
- self.c_val_recorders = []
+ # Each entry in the list contains a tuple with a key and value recorder.
+ self.value_dictionary_recorders = []
- # Count cval recorders
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.count_dom_event_listeners(),
- self.event_name + "CntDomEventListeners"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.count_dom_nodes(),
- self.event_name + "CntDomNodes"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.count_dom_html_elements(),
- self.event_name + "CntDomHtmlElements"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_count_dom_html_elements_created(self.event_type),
- self.event_name + "CntDomHtmlElementsCreated"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_count_dom_html_elements_destroyed(
- self.event_type),
- self.event_name + "CntDomHtmlElementsDestroyed"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_count_dom_update_matching_rules(self.event_type),
- self.event_name + "CntDomUpdateMatchingRuleCalls"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_count_dom_update_computed_style(self.event_type),
- self.event_name + "CntDomUpdateComputedStyleCalls"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.count_layout_boxes(),
- self.event_name + "CntLayoutBoxes"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_count_layout_boxes_created(self.event_type),
- self.event_name + "CntLayoutBoxesCreated"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_count_layout_boxes_destroyed(self.event_type),
- self.event_name + "CntLayoutBoxesDestroyed"))
+ self.animations_recorder = None
+ self.video_delay_recorder = None
- # Duration cval recorders
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_duration_total(self.event_type), self.event_name +
- "DurTotalUs"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_duration_dom_inject_event(self.event_type),
- self.event_name + "DurDomInjectEventUs"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_duration_dom_update_computed_style(
- self.event_type),
- self.event_name + "DurDomUpdateComputedStyleUs"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_duration_layout_box_tree(self.event_type),
- self.event_name + "DurLayoutBoxTreeUs"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_duration_layout_box_tree_box_generation(
- self.event_type), self.event_name +
- "DurLayoutBoxTreeBoxGenerationUs"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_duration_layout_box_tree_update_used_sizes(
- self.event_type), self.event_name +
- "DurLayoutBoxTreeUpdateUsedSizesUs"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_duration_layout_render_and_animate(
- self.event_type), self.event_name +
- "DurLayoutRenderAndAnimateUs"))
+ # Count recorders
+ self._add_value_dictionary_recorder("CntDomEventListeners")
+ self._add_value_dictionary_recorder("CntDomNodes")
+ self._add_value_dictionary_recorder("CntDomHtmlElements")
+ self._add_value_dictionary_recorder("CntDomHtmlElementsCreated")
+ self._add_value_dictionary_recorder("CntDomHtmlElementsDestroyed")
+ self._add_value_dictionary_recorder("CntDomUpdateMatchingRuleCalls")
+ self._add_value_dictionary_recorder("CntDomUpdateComputedStyleCalls")
+ self._add_value_dictionary_recorder("CntLayoutBoxes")
+ self._add_value_dictionary_recorder("CntLayoutBoxesCreated")
+ self._add_value_dictionary_recorder("CntLayoutBoxesDestroyed")
- # Optional rasterize animations cval recorders
+ # Duration recorders
+ self._add_value_dictionary_recorder("DurTotalUs")
+ self._add_value_dictionary_recorder("DurDomInjectEventUs")
+ self._add_value_dictionary_recorder("DurDomUpdateComputedStyleUs")
+ self._add_value_dictionary_recorder("DurLayoutBoxTreeUs")
+ self._add_value_dictionary_recorder("DurLayoutBoxTreeBoxGenerationUs")
+ self._add_value_dictionary_recorder("DurLayoutBoxTreeUpdateUsedSizesUs")
+ self._add_value_dictionary_recorder("DurLayoutRenderAndAnimateUs")
+
+ # Optional rasterize animations recorders
if options.record_rasterize_animations:
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.MeanCValRecorder(
- self.test,
- c_val_names.rasterize_animations_average(), self.event_name +
- "DurRasterizeAnimationsUs"))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.PercentileCValRecorder(
- self.test,
- c_val_names.rasterize_animations_percentile_25(), self.event_name
- + "DurRasterizeAnimationsUs", 25))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.PercentileCValRecorder(
- self.test,
- c_val_names.rasterize_animations_percentile_50(), self.event_name
- + "DurRasterizeAnimationsUs", 50))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.PercentileCValRecorder(
- self.test,
- c_val_names.rasterize_animations_percentile_75(), self.event_name
- + "DurRasterizeAnimationsUs", 75))
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.PercentileCValRecorder(
- self.test,
- c_val_names.rasterize_animations_percentile_95(), self.event_name
- + "DurRasterizeAnimationsUs", 95))
+ self.animations_recorder = tv_testcase_value_recorder.ValueRecorder(
+ self.event_name + "DurRasterizeAnimationsUs")
- # Optional video start delay cval recorder
+ # Optional video start delay recorder
if options.record_video_start_delay:
- self.c_val_recorders.append(
- tv_testcase_c_val_recorder.FullCValRecorder(
- self.test,
- c_val_names.event_duration_dom_video_start_delay(),
- self.event_name + "DurVideoStartDelayUs"))
+ self.video_delay_recorder = tv_testcase_value_recorder.ValueRecorder(
+ self.event_name + "DurVideoStartDelayUs")
+
+ def _add_value_dictionary_recorder(self, key):
+ recorder = tv_testcase_value_recorder.ValueRecorder(self.event_name + key)
+ self.value_dictionary_recorders.append((key, recorder))
def on_start_event(self):
- """Handles logic related to the start of a new instance of the event."""
-
- # If the recorder is set to skip events with font file loads, then the font
- # files loaded count needs to be set prior to the event. That'll allow the
- # recorder to know whether or not font files were loaded during the event.
- if self.skip_font_file_load_events:
- self.start_event_font_files_loaded_count = self.test.get_int_cval(
- c_val_names.count_font_files_loaded())
+ """Handles logic related to the start of the event instance."""
+ pass
def on_end_event(self):
"""Handles logic related to the end of the event instance."""
+ # If the event is set to skip events with font file loads and a font file
+ # loaded during the event, then its data is not collected. Log that it was
+ # skipped and return.
+ if self.skip_font_file_load_events:
+ current_font_files_loaded_count = self.test.get_cval(
+ c_val_names.count_font_files_loaded())
+ if self.font_files_loaded_count != current_font_files_loaded_count:
+ self.font_file_load_skip_count += 1
+ print("{} event skipped because a font file loaded during it! {} "
+ "events skipped.".format(self.event_name,
+ self.font_file_load_skip_count))
+ self.font_files_loaded_count = current_font_files_loaded_count
+ return
+
+ value_dictionary = self.test.get_cval(
+ c_val_names.event_value_dictionary(self.event_type))
+
# If the event failed to produce a render tree, then its data is not
# collected. Log the failure and return.
- if self.test.get_int_cval(
- c_val_names.event_produced_render_tree(self.event_type)) == 0:
+ if not value_dictionary.get("ProducedRenderTree"):
self.render_tree_failure_count += 1
print("{} event failed to produce render tree! {} events failed.".format(
self.event_name, self.render_tree_failure_count))
return
- # If the event is set to skip events with font file loads and a font file
- # loaded during the event, then its data is not collected. Log that it was
- # skipped and return.
- if (self.skip_font_file_load_events and
- self.start_event_font_files_loaded_count !=
- self.test.get_int_cval(c_val_names.count_font_files_loaded())):
- self.font_file_load_skip_count += 1
- print("{} event skipped because a font file loaded during it! {} events "
- "skipped.".format(self.event_name, self.font_file_load_skip_count))
- return
+ # Record all of the values from the event.
+ for value_dictionary_recorder in self.value_dictionary_recorders:
+ value = value_dictionary.get(value_dictionary_recorder[0])
+ if value is not None:
+ value_dictionary_recorder[1].collect_value(value)
- # Collect the current value of all of the CVal recorders.
- for c_val_recorder in self.c_val_recorders:
- c_val_recorder.collect_current_value()
+ if self.animations_recorder:
+ animation_entries = self.test.get_cval(
+ c_val_names.rasterize_animations_entry_list())
+ for value in animation_entries:
+ self.animations_recorder.collect_value(value)
+
+ if self.video_delay_recorder:
+ self.video_delay_recorder.collect_value(
+ self.test.get_cval(
+ c_val_names.event_duration_dom_video_start_delay()))
def on_end_test(self):
"""Handles logic related to the end of the test."""
- # Notify all of the CVal recorders that the test has ended.
- for c_val_recorder_entry in self.c_val_recorders:
- c_val_recorder_entry.on_end_test()
+ for value_dictionary_recorder in self.value_dictionary_recorders:
+ value_dictionary_recorder[1].on_end_test()
+
+ if self.animations_recorder:
+ self.animations_recorder.on_end_test()
+
+ if self.video_delay_recorder:
+ self.video_delay_recorder.on_end_test()
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py b/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
index 0f89a62..ab69ff6 100755
--- a/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_runner.py
@@ -141,12 +141,12 @@
self.should_exit.set()
self.launcher.SendKill()
- def _GetIPAddress(self):
- return self.launcher.GetIPAddress()
+ def _GetProcessIPAddress(self):
+ return self.launcher.GetProcessIPAddress()
def _StartWebdriver(self, port):
global _webdriver
- url = "http://{}:{}/".format(self._GetIPAddress(), port)
+ url = "http://{}:{}/".format(self._GetProcessIPAddress(), port)
self.webdriver = self.selenium_webdriver_module.Remote(
url, COBALT_WEBDRIVER_CAPABILITIES)
self.webdriver.command_executor.set_timeout(WEBDRIVER_HTTP_TIMEOUT_SECS)
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase_value_recorder.py b/src/cobalt/webdriver_benchmarks/tv_testcase_value_recorder.py
new file mode 100644
index 0000000..0c48fd0
--- /dev/null
+++ b/src/cobalt/webdriver_benchmarks/tv_testcase_value_recorder.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python2
+""""Records stats on collected values during a benchmark test."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import tv_testcase_util
+
+
+class ValueRecorder(object):
+ """"Records stats on collected values during a benchmark test.
+
+ Handles collecting values and records statistics on those values when the
+ test ends.
+ """
+
+ def __init__(self, name):
+ """Initializes the event benchmark recorder.
+
+ Args:
+ name: the name to use when recording test results
+ """
+ self.name = name
+ self.values = []
+
+ def collect_value(self, value):
+ self.values.append(value)
+
+ def on_end_test(self):
+ """Handles logic related to the end of the benchmark test."""
+
+ # Only record the test results when values have been collected.
+ if self.values:
+ self._record_test_results()
+
+ def _record_test_result_mean(self):
+ tv_testcase_util.record_test_result_mean("{}Mean".format(self.name),
+ self.values)
+
+ def _record_test_result_percentile(self, percentile):
+ tv_testcase_util.record_test_result_percentile("{}Pct{}".format(
+ self.name, percentile), self.values, percentile)
+
+ def _record_test_results(self):
+ """Records test results for the collected values.
+
+ This subclass records the mean, 25th, 50th, 75th, and 95th percentiles of
+ the collected values when the test ends.
+ """
+ self._record_test_result_mean()
+ self._record_test_result_percentile(25)
+ self._record_test_result_percentile(50)
+ self._record_test_result_percentile(75)
+ self._record_test_result_percentile(95)
diff --git a/src/media/base/starboard_player.cc b/src/media/base/starboard_player.cc
index d5c173a..32b73d4 100644
--- a/src/media/base/starboard_player.cc
+++ b/src/media/base/starboard_player.cc
@@ -117,10 +117,9 @@
video_info.frame_height = frame_height_;
#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
- SbMediaHdrMetadataColorSpace sb_media_hdr_metadata_color_space =
- MediaToSbMediaHdrMetadataColorSpace(video_config_.hdr_metadata(),
- video_config_.color_space_info());
- video_info.hdr_metadata_color_space = &sb_media_hdr_metadata_color_space;
+ SbMediaColorMetadata sb_media_color_metadata =
+ MediaToSbMediaColorMetadata(video_config_.webm_color_metadata());
+ video_info.color_metadata = &sb_media_color_metadata;
#endif
if (is_encrypted) {
FillDrmSampleInfo(buffer, &drm_info, &subsample_mapping);
diff --git a/src/media/base/starboard_utils.cc b/src/media/base/starboard_utils.cc
index f0e103f..4f60ec9 100644
--- a/src/media/base/starboard_utils.cc
+++ b/src/media/base/starboard_utils.cc
@@ -207,59 +207,74 @@
ENUM_EQ(kSbMediaRangeIdDerived, gfx::ColorSpace::kRangeIdDerived);
ENUM_EQ(kSbMediaRangeIdLast, gfx::ColorSpace::kRangeIdLast);
-SbMediaHdrMetadataColorSpace MediaToSbMediaHdrMetadataColorSpace(
- const HDRMetadata& hdr_metadata,
- const gfx::ColorSpace& color_space_info) {
+SbMediaColorMetadata MediaToSbMediaColorMetadata(
+ const WebMColorMetadata& webm_color_metadata) {
+ SbMediaColorMetadata sb_media_color_metadata;
+
+ // Copy the other color metadata below.
+ sb_media_color_metadata.bits_per_channel = webm_color_metadata.BitsPerChannel;
+ sb_media_color_metadata.chroma_subsampling_horizontal =
+ webm_color_metadata.ChromaSubsamplingHorz;
+ sb_media_color_metadata.chroma_subsampling_vertical =
+ webm_color_metadata.ChromaSubsamplingVert;
+ sb_media_color_metadata.cb_subsampling_horizontal =
+ webm_color_metadata.CbSubsamplingHorz;
+ sb_media_color_metadata.cb_subsampling_vertical =
+ webm_color_metadata.CbSubsamplingVert;
+ sb_media_color_metadata.chroma_siting_horizontal =
+ webm_color_metadata.ChromaSitingHorz;
+ sb_media_color_metadata.chroma_siting_vertical =
+ webm_color_metadata.ChromaSitingVert;
+
// Copy the HDR Metadata below.
SbMediaMasteringMetadata sb_media_mastering_metadata;
+ HDRMetadata hdr_metadata = webm_color_metadata.hdr_metadata;
+ MasteringMetadata mastering_metadata = hdr_metadata.mastering_metadata;
sb_media_mastering_metadata.primary_r_chromaticity_x =
- hdr_metadata.mastering_metadata.primary_r_chromaticity_x;
+ mastering_metadata.primary_r_chromaticity_x;
sb_media_mastering_metadata.primary_r_chromaticity_y =
- hdr_metadata.mastering_metadata.primary_r_chromaticity_y;
+ mastering_metadata.primary_r_chromaticity_y;
sb_media_mastering_metadata.primary_g_chromaticity_x =
- hdr_metadata.mastering_metadata.primary_g_chromaticity_x;
+ mastering_metadata.primary_g_chromaticity_x;
sb_media_mastering_metadata.primary_g_chromaticity_y =
- hdr_metadata.mastering_metadata.primary_g_chromaticity_y;
+ mastering_metadata.primary_g_chromaticity_y;
sb_media_mastering_metadata.primary_b_chromaticity_x =
- hdr_metadata.mastering_metadata.primary_b_chromaticity_x;
+ mastering_metadata.primary_b_chromaticity_x;
sb_media_mastering_metadata.primary_b_chromaticity_y =
- hdr_metadata.mastering_metadata.primary_b_chromaticity_y;
+ mastering_metadata.primary_b_chromaticity_y;
sb_media_mastering_metadata.white_point_chromaticity_x =
- hdr_metadata.mastering_metadata.white_point_chromaticity_x;
+ mastering_metadata.white_point_chromaticity_x;
sb_media_mastering_metadata.white_point_chromaticity_y =
- hdr_metadata.mastering_metadata.white_point_chromaticity_y;
+ mastering_metadata.white_point_chromaticity_y;
- sb_media_mastering_metadata.luminance_max =
- hdr_metadata.mastering_metadata.luminance_max;
- sb_media_mastering_metadata.luminance_min =
- hdr_metadata.mastering_metadata.luminance_min;
+ sb_media_mastering_metadata.luminance_max = mastering_metadata.luminance_max;
+ sb_media_mastering_metadata.luminance_min = mastering_metadata.luminance_min;
- SbMediaHdrMetadataColorSpace sb_media_hdr_metadata_color_space;
- sb_media_hdr_metadata_color_space.mastering_metadata =
- sb_media_mastering_metadata;
- sb_media_hdr_metadata_color_space.max_cll = hdr_metadata.max_cll;
- sb_media_hdr_metadata_color_space.max_fall = hdr_metadata.max_fall;
+ sb_media_color_metadata.mastering_metadata = sb_media_mastering_metadata;
+ sb_media_color_metadata.max_cll = hdr_metadata.max_cll;
+ sb_media_color_metadata.max_fall = hdr_metadata.max_fall;
// Copy the color space below.
- sb_media_hdr_metadata_color_space.primaries =
- static_cast<SbMediaPrimaryId>(color_space_info.primaries());
- sb_media_hdr_metadata_color_space.transfer =
- static_cast<SbMediaTransferId>(color_space_info.transfer());
- sb_media_hdr_metadata_color_space.matrix =
- static_cast<SbMediaMatrixId>(color_space_info.matrix());
- sb_media_hdr_metadata_color_space.range =
- static_cast<SbMediaRangeId>(color_space_info.range());
- if (sb_media_hdr_metadata_color_space.primaries == kSbMediaPrimaryIdCustom) {
- const float* custom_primary_matrix =
- color_space_info.custom_primary_matrix();
- SbMemoryCopy(sb_media_hdr_metadata_color_space.custom_primary_matrix,
+ gfx::ColorSpace color_space = webm_color_metadata.color_space;
+ sb_media_color_metadata.primaries =
+ static_cast<SbMediaPrimaryId>(color_space.primaries());
+ sb_media_color_metadata.transfer =
+ static_cast<SbMediaTransferId>(color_space.transfer());
+ sb_media_color_metadata.matrix =
+ static_cast<SbMediaMatrixId>(color_space.matrix());
+ sb_media_color_metadata.range =
+ static_cast<SbMediaRangeId>(color_space.range());
+ if (sb_media_color_metadata.primaries == kSbMediaPrimaryIdCustom) {
+ const float* custom_primary_matrix = color_space.custom_primary_matrix();
+ SbMemoryCopy(sb_media_color_metadata.custom_primary_matrix,
custom_primary_matrix, sizeof(custom_primary_matrix));
}
- return sb_media_hdr_metadata_color_space;
+
+ return sb_media_color_metadata;
}
#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
diff --git a/src/media/base/starboard_utils.h b/src/media/base/starboard_utils.h
index d2b3c22..ef03e27 100644
--- a/src/media/base/starboard_utils.h
+++ b/src/media/base/starboard_utils.h
@@ -38,9 +38,8 @@
SbDrmSubSampleMapping* subsample_mapping);
#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
-SbMediaHdrMetadataColorSpace MediaToSbMediaHdrMetadataColorSpace(
- const HDRMetadata& hdr_metadata,
- const gfx::ColorSpace& color_space_info);
+SbMediaColorMetadata MediaToSbMediaColorMetadata(
+ const WebMColorMetadata& webm_color_metadata);
#endif
} // namespace media
diff --git a/src/media/base/video_decoder_config.cc b/src/media/base/video_decoder_config.cc
index e7e2dc7..7d0055f 100644
--- a/src/media/base/video_decoder_config.cc
+++ b/src/media/base/video_decoder_config.cc
@@ -15,8 +15,8 @@
profile_(VIDEO_CODEC_PROFILE_UNKNOWN),
format_(VideoFrame::INVALID),
extra_data_size_(0),
- is_encrypted_(false) {
-}
+ is_encrypted_(false),
+ color_space_(COLOR_SPACE_UNSPECIFIED) {}
VideoDecoderConfig::VideoDecoderConfig(VideoCodec codec,
VideoCodecProfile profile,
@@ -102,13 +102,13 @@
switch (color_space) {
case COLOR_SPACE_JPEG:
- color_space_info_ = gfx::ColorSpace::CreateJpeg();
+ webm_color_metadata_.color_space = gfx::ColorSpace::CreateJpeg();
break;
case COLOR_SPACE_HD_REC709:
- color_space_info_ = gfx::ColorSpace::CreateREC709();
+ webm_color_metadata_.color_space = gfx::ColorSpace::CreateREC709();
break;
case COLOR_SPACE_SD_REC601:
- color_space_info_ = gfx::ColorSpace::CreateREC601();
+ webm_color_metadata_.color_space = gfx::ColorSpace::CreateREC601();
break;
case COLOR_SPACE_UNSPECIFIED:
break;
@@ -119,17 +119,13 @@
}
void VideoDecoderConfig::CopyFrom(const VideoDecoderConfig& video_config) {
- Initialize(video_config.codec(),
- video_config.profile(),
- video_config.format(),
- video_config.color_space(),
- video_config.coded_size(),
- video_config.visible_rect(),
- video_config.natural_size(),
- video_config.extra_data(),
- video_config.extra_data_size(),
- video_config.is_encrypted(),
+ Initialize(video_config.codec(), video_config.profile(),
+ video_config.format(), video_config.color_space_,
+ video_config.coded_size(), video_config.visible_rect(),
+ video_config.natural_size(), video_config.extra_data(),
+ video_config.extra_data_size(), video_config.is_encrypted(),
false);
+ webm_color_metadata_ = video_config.webm_color_metadata_;
}
bool VideoDecoderConfig::IsValidConfig() const {
@@ -141,16 +137,15 @@
}
bool VideoDecoderConfig::Matches(const VideoDecoderConfig& config) const {
- return ((codec() == config.codec()) &&
- (format() == config.format()) &&
- (color_space_info() == config.color_space_info()) &&
+ return ((codec() == config.codec()) && (format() == config.format()) &&
+ (webm_color_metadata_ == config.webm_color_metadata()) &&
(profile() == config.profile()) &&
(coded_size() == config.coded_size()) &&
(visible_rect() == config.visible_rect()) &&
(natural_size() == config.natural_size()) &&
(extra_data_size() == config.extra_data_size()) &&
- (!extra_data() || !memcmp(extra_data(), config.extra_data(),
- extra_data_size())) &&
+ (!extra_data() ||
+ !memcmp(extra_data(), config.extra_data(), extra_data_size())) &&
(is_encrypted() == config.is_encrypted()));
}
@@ -208,25 +203,4 @@
return is_encrypted_;
}
-void VideoDecoderConfig::set_color_space_info(
- const gfx::ColorSpace& color_space_info) {
- color_space_info_ = color_space_info;
-}
-
-gfx::ColorSpace VideoDecoderConfig::color_space_info() const {
- return color_space_info_;
-}
-
-ColorSpace VideoDecoderConfig::color_space() const {
- return color_space_;
-}
-
-void VideoDecoderConfig::set_hdr_metadata(const HDRMetadata& hdr_metadata) {
- hdr_metadata_ = hdr_metadata;
-}
-
-HDRMetadata VideoDecoderConfig::hdr_metadata() const {
- return hdr_metadata_;
-}
-
} // namespace media
diff --git a/src/media/base/video_decoder_config.h b/src/media/base/video_decoder_config.h
index acd8762..d828420 100644
--- a/src/media/base/video_decoder_config.h
+++ b/src/media/base/video_decoder_config.h
@@ -14,6 +14,7 @@
#include "media/base/media_export.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
+#include "media/webm/webm_colour_parser.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
@@ -83,7 +84,8 @@
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
- const uint8* extra_data, size_t extra_data_size,
+ const uint8* extra_data,
+ size_t extra_data_size,
bool is_encrypted);
~VideoDecoderConfig();
@@ -96,7 +98,8 @@
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
- const uint8* extra_data, size_t extra_data_size,
+ const uint8* extra_data,
+ size_t extra_data_size,
bool is_encrypted,
bool record_stats);
@@ -142,13 +145,13 @@
// can be encrypted or not encrypted.
bool is_encrypted() const;
- void set_color_space_info(const gfx::ColorSpace& color_space_info);
- gfx::ColorSpace color_space_info() const;
+ void set_webm_color_metadata(const WebMColorMetadata& webm_color_metadata) {
+ webm_color_metadata_ = webm_color_metadata;
+ }
- ColorSpace color_space() const;
-
- void set_hdr_metadata(const HDRMetadata& hdr_metadata);
- HDRMetadata hdr_metadata() const;
+ const WebMColorMetadata& webm_color_metadata() const {
+ return webm_color_metadata_;
+ }
private:
VideoCodec codec_;
@@ -168,9 +171,7 @@
bool is_encrypted_;
- gfx::ColorSpace color_space_info_;
- HDRMetadata hdr_metadata_;
-
+ WebMColorMetadata webm_color_metadata_;
DISALLOW_COPY_AND_ASSIGN(VideoDecoderConfig);
};
diff --git a/src/media/filters/decrypting_demuxer_stream.cc b/src/media/filters/decrypting_demuxer_stream.cc
index 2c0e799..cdf3c26 100644
--- a/src/media/filters/decrypting_demuxer_stream.cc
+++ b/src/media/filters/decrypting_demuxer_stream.cc
@@ -358,17 +358,7 @@
const VideoDecoderConfig& input_video_config =
stream->video_decoder_config();
video_config_.reset(new VideoDecoderConfig());
- 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(),
- input_video_config.extra_data_size(),
- false, // Output video is not encrypted.
- false);
+ video_config_->CopyFrom(input_video_config);
break;
}
diff --git a/src/media/webm/webm_colour_parser.cc b/src/media/webm/webm_colour_parser.cc
index 616cc6c..0b12c52 100644
--- a/src/media/webm/webm_colour_parser.cc
+++ b/src/media/webm/webm_colour_parser.cc
@@ -343,6 +343,17 @@
ChromaSitingVert = 0;
}
+bool WebMColorMetadata::operator==(const WebMColorMetadata& rhs) const {
+ return (BitsPerChannel == rhs.BitsPerChannel &&
+ ChromaSubsamplingHorz == rhs.ChromaSubsamplingHorz &&
+ ChromaSubsamplingVert == rhs.ChromaSubsamplingVert &&
+ CbSubsamplingHorz == rhs.CbSubsamplingHorz &&
+ CbSubsamplingVert == rhs.CbSubsamplingVert &&
+ ChromaSitingHorz == rhs.ChromaSitingHorz &&
+ ChromaSitingVert == rhs.ChromaSitingVert &&
+ color_space == rhs.color_space && hdr_metadata == rhs.hdr_metadata);
+}
+
WebMMasteringMetadataParser::WebMMasteringMetadataParser() {}
WebMMasteringMetadataParser::~WebMMasteringMetadataParser() {}
diff --git a/src/media/webm/webm_colour_parser.h b/src/media/webm/webm_colour_parser.h
index 76d1c85..acf9a3d 100644
--- a/src/media/webm/webm_colour_parser.h
+++ b/src/media/webm/webm_colour_parser.h
@@ -29,6 +29,7 @@
WebMColorMetadata();
WebMColorMetadata(const WebMColorMetadata& rhs);
+ bool operator==(const WebMColorMetadata& rhs) const;
};
// Parser for WebM MasteringMetadata within Colour element:
diff --git a/src/media/webm/webm_video_client.cc b/src/media/webm/webm_video_client.cc
index aa3efed..bcd3b87 100644
--- a/src/media/webm/webm_video_client.cc
+++ b/src/media/webm/webm_video_client.cc
@@ -107,8 +107,7 @@
true);
if (colour_parsed_) {
WebMColorMetadata color_metadata = colour_parser_.GetWebMColorMetadata();
- config->set_color_space_info(color_metadata.color_space);
- config->set_hdr_metadata(color_metadata.hdr_metadata);
+ config->set_webm_color_metadata(color_metadata);
}
return config->IsValidConfig();
}
diff --git a/src/nb/atomic.h b/src/nb/atomic.h
index 9bd5c61..8c3c999 100644
--- a/src/nb/atomic.h
+++ b/src/nb/atomic.h
@@ -17,6 +17,7 @@
#ifndef NB_ATOMIC_H_
#define NB_ATOMIC_H_
+#include "starboard/atomic.h"
#include "starboard/mutex.h"
#include "starboard/types.h"
@@ -64,7 +65,7 @@
// Base functionality for atomic types. Defines exchange(), load(),
// store(), compare_exhange_weak(), compare_exchange_strong()
-template <typename T>
+template <typename T>
class atomic;
// Subtype of atomic<T> for numbers likes float and integer types but not bool.
@@ -244,13 +245,80 @@
explicit atomic_bool(bool initial_val) : Super(initial_val) {}
};
-// Simple atomic int class. This could be optimized for speed using
-// compiler intrinsics for concurrent integer modification.
-class atomic_int32_t : public atomic_integral<int32_t> {
+// Lockfree atomic int class.
+class atomic_int32_t {
public:
- typedef atomic_integral<int32_t> Super;
- atomic_int32_t() : Super() {}
- explicit atomic_int32_t(int32_t initial_val) : Super(initial_val) {}
+ typedef int32_t ValueType;
+ atomic_int32_t() : value_(0) {}
+ atomic_int32_t(SbAtomic32 value) : value_(value) {}
+
+ bool is_lock_free() const { return true; }
+ bool is_lock_free() const volatile { return true; }
+
+ int32_t increment() {
+ return fetch_add(1);
+ }
+ int32_t decrement() {
+ return fetch_add(-1);
+ }
+
+ int32_t fetch_add(int32_t val) {
+ // fetch_add is a post-increment operation, while SbAtomicBarrier_Increment
+ // is a pre-increment operation. Therefore subtract the value to match
+ // the expected interface.
+ return SbAtomicBarrier_Increment(volatile_ptr(), val) - val;
+ }
+
+ int32_t fetch_sub(int32_t val) {
+ return fetch_add(-val);
+ }
+
+ // Atomically replaces the value of the atomic object
+ // and returns the value held previously.
+ // See also std::atomic<T>::exchange().
+ int32_t exchange(int32_t new_val) {
+ return SbAtomicNoBarrier_Exchange(volatile_ptr(), new_val);
+ }
+
+ // Atomically obtains the value of the atomic object.
+ // See also std::atomic<T>::load().
+ int32_t load() const {
+ return SbAtomicAcquire_Load(volatile_const_ptr());
+ }
+
+ // Stores the value. See std::atomic<T>::store(...)
+ void store(int32_t val) {
+ SbAtomicRelease_Store(volatile_ptr(), val);
+ }
+
+ bool compare_exchange_strong(int32_t* expected_value, int32_t new_value) {
+ int32_t prev_value = *expected_value;
+ SbAtomicMemoryBarrier();
+ int32_t value_written = SbAtomicRelease_CompareAndSwap(volatile_ptr(),
+ prev_value,
+ new_value);
+ const bool write_ok = (prev_value == value_written);
+ if (!write_ok) {
+ *expected_value = value_written;
+ }
+ return write_ok;
+ }
+
+ // Weak version of this function is documented to be faster, but has allows
+ // weaker memory ordering and therefore will sometimes have a false negative:
+ // The value compared will actually be equal but the return value from this
+ // function indicates otherwise.
+ // By default, the function delegates to compare_exchange_strong(...).
+ //
+ // See also std::atomic<T>::compare_exchange_weak(...).
+ bool compare_exchange_weak(int32_t* expected_value, int32_t new_value) {
+ return compare_exchange_strong(expected_value, new_value);
+ }
+
+ private:
+ volatile int32_t* volatile_ptr() { return &value_; }
+ volatile const int32_t* volatile_const_ptr() const { return &value_; }
+ int32_t value_;
};
// Simple atomic int class. This could be optimized for speed using
diff --git a/src/nb/atomic_test.cc b/src/nb/atomic_test.cc
index fc76703..222965d 100644
--- a/src/nb/atomic_test.cc
+++ b/src/nb/atomic_test.cc
@@ -17,6 +17,7 @@
#include "nb/atomic.h"
#include <algorithm>
+#include <numeric>
#include <vector>
#include "nb/test_thread.h"
@@ -24,6 +25,8 @@
#include "starboard/mutex.h"
#include "testing/gtest/include/gtest/gtest.h"
+#define NUM_THREADS 4
+
namespace nb {
namespace {
@@ -41,18 +44,25 @@
typedef ::testing::Types<atomic_int32_t, atomic_int64_t,
atomic_float, atomic_double> AtomicNumberTypes;
+// Defines a typelist for just atomic number types.
+typedef ::testing::Types<atomic_int32_t, atomic_int64_t> AtomicIntegralTypes;
+
// Defines test type that will be instantiated using each type in
// AllAtomicTypes type list.
-template <typename T>
+template <typename T>
class AtomicTest : public ::testing::Test {};
TYPED_TEST_CASE(AtomicTest, AllAtomicTypes); // Registration.
// Defines test type that will be instantiated using each type in
// AtomicNumberTypes type list.
-template <typename T>
+template <typename T>
class AtomicNumberTest : public ::testing::Test {};
TYPED_TEST_CASE(AtomicNumberTest, AtomicNumberTypes); // Registration.
+template <typename T>
+class AtomicIntegralTest : public ::testing::Test {};
+TYPED_TEST_CASE(AtomicIntegralTest, AtomicIntegralTypes); // Registration.
+
///////////////////////////////////////////////////////////////////////////////
// Singlethreaded tests.
@@ -163,6 +173,19 @@
ASSERT_EQ(neg_two, atomic.load()); // 0-2 = -2
}
+TYPED_TEST(AtomicIntegralTest, IncrementAndDecrement_SingleThread) {
+ typedef TypeParam AtomicT;
+ typedef typename AtomicT::ValueType T;
+
+ const T zero(0);
+ const T one = zero + 1; // Allows AtomicPointer<T*>.
+
+ AtomicT atomic;
+ ASSERT_EQ(atomic.load(), zero); // Default is 0.
+ ASSERT_EQ(zero, atomic.increment()); // Tests for post-increment operation.
+ ASSERT_EQ(one, atomic.decrement()); // Tests for post-decrement operation.
+}
+
///////////////////////////////////////////////////////////////////////////////
// Multithreaded tests.
///////////////////////////////////////////////////////////////////////////////
@@ -190,7 +213,7 @@
T new_value = T(i);
while (true) {
if (std::rand() % 3 == 0) {
- // 1 in 3 chance of yeilding.
+ // 1 in 3 chance of yielding.
// Attempt to cause more contention by giving other threads a chance
// to run.
SbThreadYield();
@@ -227,17 +250,17 @@
// The numbers are sequentially written to the shared Atomic type and then
// exposed to other threads:
//
-// Generates [0,1,2,...,n/2)
-// +------+ Thread A <--------+ (Write Exchanged Value)
-// | |
-// | compare_exchange() +---> exchanged? ---+
-// +----> +------------+ +----+ v
-// | AtomicType | Output vector
-// +----> +------------+ +----+ ^
-// | compare_exchange() +---> exchanged? ---+
-// | |
-// +------+ Thread B <--------+
-// Generates [n/2,n/2+1,...,n)
+// Generates [0,1,2,...,n/2)
+// +------+ Thread A <--------+ (Write Exchanged Value)
+// | |
+// | compare_exchange() +---> exchanged? ---+
+// +----> +------------+ +----+ v
+// | AtomicType | Output vector
+// +----> +------------+ +----+ ^
+// | compare_exchange() +---> exchanged? ---+
+// | |
+// +------+ Thread B <--------+
+// Generates [n/2,n/2+1,...,n)
//
// By repeatedly calling atomic<T>::compare_exchange_strong() by each of the
// threads, each will see the previous value of the shared variable when their
@@ -245,8 +268,8 @@
// values are recombined then it will form the original generated sequence from
// all threads.
//
-// TEST PHASE
-// sort( output vector ) AND TEST THAT
+// TEST PHASE
+// sort( output vector ) AND TEST THAT
// output vector Contains [0,1,2,...,n)
//
// The test passes when the output array is tested that it contains every
@@ -258,16 +281,15 @@
typedef typename AtomicT::ValueType T;
static const int kNumElements = 1000;
- static const int kNumThreads = 4;
AtomicT atomic_value(T(-1));
std::vector<TestThread*> threads;
std::vector<T> output_values;
starboard::Mutex output_mutex;
- for (int i = 0; i < kNumThreads; ++i) {
- const int start_num = (kNumElements * i) / kNumThreads;
- const int end_num = (kNumElements * (i + 1)) / kNumThreads;
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ const int start_num = (kNumElements * i) / NUM_THREADS;
+ const int end_num = (kNumElements * (i + 1)) / NUM_THREADS;
threads.push_back(
new CompareExchangeThread<AtomicT>(
start_num, // defines the number range to generate.
@@ -279,15 +301,15 @@
// These threads will generate unique numbers in their range and then
// write them to the output array.
- for (int i = 0; i < kNumThreads; ++i) {
+ for (int i = 0; i < NUM_THREADS; ++i) {
threads[i]->Start();
}
- for (int i = 0; i < kNumThreads; ++i) {
+ for (int i = 0; i < NUM_THREADS; ++i) {
threads[i]->Join();
}
// Cleanup threads.
- for (int i = 0; i < kNumThreads; ++i) {
+ for (int i = 0; i < NUM_THREADS; ++i) {
delete threads[i];
}
threads.clear();
@@ -313,5 +335,153 @@
}
}
+// A thread that will invoke increment() and decrement() and equal number
+// of times to atomic_value. The value after this is done should be equal to
+// 0.
+template <typename AtomicT>
+class IncrementAndDecrementThread : public TestThread {
+ public:
+ typedef typename AtomicT::ValueType T;
+ IncrementAndDecrementThread(size_t half_number_of_operations,
+ AtomicT* atomic_value)
+ : atomic_value_(atomic_value) {
+ for (size_t i = 0; i < half_number_of_operations; ++i) {
+ operation_sequence_.push_back(true);
+ }
+ for (size_t i = 0; i < half_number_of_operations; ++i) {
+ operation_sequence_.push_back(false);
+ }
+ std::random_shuffle(operation_sequence_.begin(),
+ operation_sequence_.end());
+ }
+
+ virtual void Run() {
+ for (size_t i = 0; i < operation_sequence_.size(); ++i) {
+ if (std::rand() % 3 == 0) {
+ // 1 in 3 chance of yielding.
+ // Attempt to cause more contention by giving other threads a chance
+ // to run.
+ SbThreadYield();
+ }
+ T prev_value = 0;
+ if (operation_sequence_[i]) {
+ prev_value = atomic_value_->increment();
+ } else {
+ prev_value = atomic_value_->decrement();
+ }
+ }
+ }
+ private:
+ // Used purely for true/false values. Note that we don't
+ // use std::vector<bool> because some platforms won't support
+ // swapping elements of std::vector<bool>, which is required for
+ // std::random_shuffle().
+ std::vector<uint8_t> operation_sequence_;
+ AtomicT*const atomic_value_;
+};
+
+TYPED_TEST(AtomicIntegralTest, Test_IncrementAndDecrement_MultiThreaded) {
+ typedef TypeParam AtomicT;
+ typedef typename AtomicT::ValueType T;
+
+ static const int kNumOperations = 10000;
+
+ AtomicT atomic_value(T(0));
+ std::vector<TestThread*> threads;
+
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ threads.push_back(
+ new IncrementAndDecrementThread<AtomicT>(
+ kNumOperations,
+ &atomic_value));
+ }
+
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ threads[i]->Start();
+ }
+
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ threads[i]->Join();
+ }
+ // Cleanup threads.
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ delete threads[i];
+ }
+ threads.clear();
+
+ // After an equal number of decrements and increments, the final value should
+ // be 0.
+ ASSERT_EQ(0, atomic_value.load());
+}
+
+template <typename AtomicT>
+class FetchAddSubThread : public TestThread {
+ public:
+ typedef typename AtomicT::ValueType T;
+ FetchAddSubThread(const int32_t start_value,
+ const int32_t end_value,
+ AtomicT* atomic_value)
+ : start_value_(start_value),
+ end_value_(end_value),
+ atomic_value_(atomic_value) {
+ }
+
+ virtual void Run() {
+ for (int32_t i = start_value_; i < end_value_; ++i) {
+ if (std::rand() % 3 == 0) {
+ // 1 in 3 chance of yielding.
+ // Attempt to cause more contention by giving other threads a chance
+ // to run.s
+ SbThreadYield();
+ }
+
+ if (std::rand() % 2 == 0) {
+ atomic_value_->fetch_add(i);
+ } else {
+ atomic_value_->fetch_sub(-i);
+ }
+ }
+ }
+ private:
+ int32_t start_value_;
+ int32_t end_value_;
+ AtomicT*const atomic_value_;
+};
+
+TYPED_TEST(AtomicIntegralTest, Test_FetchAdd_MultiThreaded) {
+ typedef TypeParam AtomicT;
+ typedef typename AtomicT::ValueType T;
+
+ static const int kNumOperations = 10000;
+
+ AtomicT atomic_value(T(0));
+ std::vector<TestThread*> threads;
+
+ // First value is inclusive, second is exclusive.
+ threads.push_back(
+ new FetchAddSubThread<AtomicT>(-kNumOperations, 0, &atomic_value));
+
+ threads.push_back(
+ new FetchAddSubThread<AtomicT>(1, kNumOperations+1, &atomic_value));
+
+ for (int i = 0; i < threads.size(); ++i) {
+ threads[i]->Start();
+ }
+
+ for (int i = 0; i < threads.size(); ++i) {
+ threads[i]->Join();
+ }
+ // Cleanup threads.
+ for (int i = 0; i < threads.size(); ++i) {
+ delete threads[i];
+ }
+ threads.clear();
+
+ // After an equal number of decrements and increments, the final value should
+ // be 0.
+ ASSERT_EQ(0, atomic_value.load());
+}
+
+
} // namespace
} // namespace nb
diff --git a/src/nb/lexical_cast.h b/src/nb/lexical_cast.h
new file mode 100644
index 0000000..0ce3a20
--- /dev/null
+++ b/src/nb/lexical_cast.h
@@ -0,0 +1,100 @@
+/*
+ * 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 NB_LEXICAL_CAST_H_
+#define NB_LEXICAL_CAST_H_
+
+#include <limits>
+#include <sstream>
+
+#include "starboard/types.h"
+
+namespace nb {
+
+// Converts a string into a value. This function should not be used in
+// performance sensitive code.
+//
+// Note:
+// * All strings are assumed to represent numbers in base 10.
+// * Casting will parse until a non-numerical character is encountered:
+// * Numbers like "128M" will drop "M" and cast to a value of 128.
+// * Numbers like "12M8" will cast to value 12.
+// * Numbers like "M128" will fail to cast.
+//
+// Returns the value of the result after the lexical cast. If the lexical
+// cast fails then the default value of the parameterized type is returned.
+// |cast_ok| is an optional parameter which will be |true| if the cast
+// succeeds, otherwise |false|.
+// Example:
+// int value = lexical_cast<int>("1234");
+// EXPECT_EQ(value, 1234);
+// bool ok = true;
+// value = lexical_cast<int>("not a number", &ok);
+// EXPECT_FALSE(ok);
+// EXPECT_EQ(0, value);
+template <typename T>
+T lexical_cast(const char* s, bool* cast_ok = NULL) {
+ if (!s) { // Handle NULL case of input string.
+ if (cast_ok) {
+ *cast_ok = false;
+ }
+ return T();
+ }
+ std::stringstream ss;
+ ss << s;
+ T value;
+ ss >> value;
+ if (cast_ok) {
+ *cast_ok = !ss.fail();
+ }
+ if (ss.fail()) {
+ value = T();
+ }
+ return value;
+}
+
+// int8_t and uint8_t will normally be interpreted as a char, which will
+// result in only the first character being parsed. This is obviously not
+// what we want. Therefore we provide specializations for lexical_cast for
+// these types.
+template <>
+int8_t lexical_cast<int8_t>(const char* s, bool* cast_ok) {
+ int16_t value_i16 = lexical_cast<int16_t>(s, cast_ok);
+ if (value_i16 < std::numeric_limits<int8_t>::min() ||
+ value_i16 > std::numeric_limits<int8_t>::max()) {
+ value_i16 = 0;
+ if (cast_ok) {
+ *cast_ok = false;
+ }
+ }
+ return static_cast<int8_t>(value_i16);
+}
+
+template <>
+uint8_t lexical_cast<uint8_t>(const char* s, bool* cast_ok) {
+ uint16_t value_i16 = lexical_cast<uint16_t>(s, cast_ok);
+ if (value_i16 > std::numeric_limits<uint8_t>::max()) {
+ value_i16 = 0;
+ if (cast_ok) {
+ *cast_ok = false;
+ }
+ }
+ return static_cast<uint8_t>(value_i16);
+}
+
+} // namespace nb
+
+#endif // NB_LEXICAL_CAST_H_
\ No newline at end of file
diff --git a/src/nb/lexical_cast_test.cc b/src/nb/lexical_cast_test.cc
new file mode 100644
index 0000000..fb7e12d
--- /dev/null
+++ b/src/nb/lexical_cast_test.cc
@@ -0,0 +1,193 @@
+/*
+ * 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 "nb/lexical_cast.h"
+#include "starboard/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace nb {
+namespace {
+
+TEST(lexical_cast, OptionalParameterOmitted) {
+ EXPECT_EQ(123, lexical_cast<int>("123"));
+ EXPECT_EQ(0, lexical_cast<int>("not a number"));
+ EXPECT_EQ(-123, lexical_cast<int8_t>("-123"));
+ EXPECT_EQ(123, lexical_cast<uint8_t>("123"));
+}
+
+TEST(lexical_cast, PositiveBasicTypes) {
+ bool cast_ok = false;
+ EXPECT_EQ(123, lexical_cast<int>("123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(123, lexical_cast<int8_t>("123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(123, lexical_cast<int16_t>("123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(123, lexical_cast<int32_t>("123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(123, lexical_cast<int64_t>("123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+
+ EXPECT_EQ(123, lexical_cast<uint8_t>("123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(123, lexical_cast<uint16_t>("123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(123, lexical_cast<uint32_t>("123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(123, lexical_cast<uint64_t>("123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+
+ EXPECT_FLOAT_EQ(1234.5f, lexical_cast<float>("1234.5", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_FLOAT_EQ(1234.5f, lexical_cast<float>("1234.5f", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+
+ EXPECT_FLOAT_EQ(1234.5f, lexical_cast<double>("1234.5", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_FLOAT_EQ(1234.5f, lexical_cast<double>("1234.5f", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+}
+
+TEST(lexical_cast, NegativeBasicTypes) {
+ bool cast_ok = false;
+ EXPECT_EQ(-123, lexical_cast<int>("-123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(-123, lexical_cast<int8_t>("-123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(-123, lexical_cast<int16_t>("-123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(-123, lexical_cast<int32_t>("-123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(-123, lexical_cast<int64_t>("-123", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+
+ EXPECT_EQ(0, lexical_cast<uint8_t>("-123", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, lexical_cast<uint16_t>("-123", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, lexical_cast<uint32_t>("-123", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, lexical_cast<uint64_t>("-123", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+
+ EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<float>("-1234.5", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<float>("-1234.5f", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+
+ EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<double>("-1234.5", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+ EXPECT_FLOAT_EQ(-1234.5f, lexical_cast<double>("-1234.5f", &cast_ok));
+ EXPECT_TRUE(cast_ok);
+}
+
+TEST(lexical_cast, StringIsNonNumerical) {
+ bool cast_ok = false;
+ EXPECT_EQ(0, lexical_cast<int>("not a number", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, lexical_cast<int8_t>("not a number", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, lexical_cast<int16_t>("not a number", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, lexical_cast<int32_t>("not a number", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, lexical_cast<int64_t>("not a number", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+
+ EXPECT_FLOAT_EQ(0.f, lexical_cast<float>("not a number", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+ EXPECT_FLOAT_EQ(0.0, lexical_cast<double>("not a number", &cast_ok));
+ EXPECT_FALSE(cast_ok);
+}
+
+TEST(lexical_cast, StringIsEmpty) {
+ bool cast_ok = false;
+ int value = lexical_cast<int>("");
+ EXPECT_EQ(value, 0);
+ EXPECT_FALSE(cast_ok);
+}
+
+TEST(lexical_cast, StringIsNull) {
+ bool cast_ok = false;
+ int value = lexical_cast<int>(NULL);
+ EXPECT_EQ(value, 0);
+ EXPECT_FALSE(cast_ok);
+}
+
+TEST(lexical_cast, IntegerOverflow_int8) {
+ bool cast_ok = false;
+ int8_t value = lexical_cast<int8_t>("128", &cast_ok);
+
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, value);
+}
+
+TEST(lexical_cast, IntegerOverflow_uint8) {
+ bool cast_ok = false;
+ uint8_t value = lexical_cast<uint8_t>("256", &cast_ok);
+
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, value);
+}
+
+TEST(lexical_cast, IntegerOverflow_int16) {
+ bool cast_ok = false;
+ int16_t value = lexical_cast<int16_t>("65535", &cast_ok);
+
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, value);
+}
+
+TEST(lexical_cast, IntegerOverflow_uint16) {
+ bool cast_ok = false;
+ int16_t value = lexical_cast<int16_t>("65536", &cast_ok);
+
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, value);
+}
+
+TEST(lexical_cast, LetterBeforeNumber) {
+ bool cast_ok = false;
+ int value = lexical_cast<int>("M128", &cast_ok);
+ EXPECT_FALSE(cast_ok);
+ EXPECT_EQ(0, value);
+}
+
+TEST(lexical_cast, LetterInNumber) {
+ bool cast_ok = false;
+ int value = lexical_cast<int>("12M8", &cast_ok);
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(12, value);
+}
+
+TEST(lexical_cast, LetterAfterNumber) {
+ bool cast_ok = false;
+ int value = lexical_cast<int>("128M", &cast_ok);
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(128, value);
+}
+
+TEST(lexical_cast, OnlyBase10) {
+ bool cast_ok = false;
+ // Expect that the "100000" part of "100000ff" will be parsed. The "ff" part
+ // is dropped.
+ uint32_t value = lexical_cast<uint32_t>("100000ff", &cast_ok);
+ EXPECT_TRUE(cast_ok);
+ EXPECT_EQ(100000, value); //
+}
+
+} // namespace
+} // namespace nb
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index 30179ea..d9ea369 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -37,6 +37,7 @@
'fixed_no_free_allocator.h',
'hash.cc',
'hash.h',
+ 'lexical_cast.h',
'memory_pool.cc',
'memory_pool.h',
'memory_scope.cc',
@@ -83,6 +84,7 @@
'analytics/memory_tracker_test.cc',
'atomic_test.cc',
'fixed_no_free_allocator_test.cc',
+ 'lexical_cast_test.cc',
'memory_scope_test.cc',
'reuse_allocator_test.cc',
'run_all_unittests.cc',
diff --git a/src/starboard/common/flat_map.h b/src/starboard/common/flat_map.h
index 02130cb..4f4d39c 100644
--- a/src/starboard/common/flat_map.h
+++ b/src/starboard/common/flat_map.h
@@ -1,431 +1,431 @@
-// 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 STARBOARD_COMMON_FLAT_MAP_H_
-#define STARBOARD_COMMON_FLAT_MAP_H_
-
-#include <algorithm>
-#include <functional>
-#include <utility>
-#include <vector>
-
-#include "starboard/log.h"
-#include "starboard/types.h"
-
-namespace starboard {
-namespace flat_map_detail {
-// IsPod<> is a white list of common types that are "plain-old-data'.
-// Types not registered with IsPod<> will default to non-pod.
-// Usage: IsPod<int>::value == true;
-// IsPod<std::string>::value == false
-// See specializations at the bottom of this file for what has been defined
-// as pod.
-template <typename T>
-struct IsPod {
- enum { value = false };
-};
-} // namespace flat_map_detail.
-
-// FlatMap<key_type, mapped_type> is a sorted vector map of key-value pairs.
-// This map has an interface that is largely compatible with std::map<> and
-// is designed to be a near drop in replacement for std::map<>.
-//
-// A main usage difference between FlatMap<> and std::map<> is that FlatMap<>
-// will invalidate it's iterators on insert() and erase().
-//
-// Reasons to use this map include low-level operations which require that
-// the map does not allocate memory during runtime (this is achievable by
-// invoking FlatMap::reserve()), or to speed up a lot of find() operations
-// where the FlatMap() is mutated to very occasionally.
-//
-// PERFORMANCE
-// where n is the number of items in flatmap
-// and m is the input size for the operation.
-//
-// bulk insert | O(n+m + m*log(m))
-// insert | O(n)
-// erase | O(n)
-// bulk erase | O(n*m) TODO: Make faster - O(n+m + log(m))
-// find | O(log(n))
-// clear | O(n)
-//
-// Performance of FlatMap::find() tends to be about +50% faster than that
-// of std::map<> for pod types in optimized builds. However this is balanced
-// by the fact that FlatMap::insert() and FlatMap::erase() both operate at
-// O(n), where std::map will operate at O(log n) for both operations.
-//
-// FlatMap<int,int>::find() Performance
-// NUMBER OF ELEMENTS | SPEED COMPARSION vs std::map
-// -------------------------------------
-// 5 | 220.37%
-// 10 | 158.602%
-// 25 | 87.7049%
-// 50 | 91.0873%
-// 100 | 96.1367%
-// 1000 | 120.588%
-// 10000 | 156.969%
-// 100000 | 179.55%
-//
-// When in doubt, use std::map. If you need to use FlatMap<>, then make sure
-// that insertion() is done in bulk, and that delete are infrequent, or that
-// the maps are small.
-//
-// Please see unit tests for additional usage.
-
-template <typename Key,
- typename Value,
- typename LessThan = std::less<Key>,
- typename VectorType = std::vector<std::pair<Key, Value> > >
-class FlatMap {
- public:
- // Most typedefs here are to make FlatMap<> compatible with std::map<>.
- typedef Key key_type;
- typedef Value mapped_type;
- typedef LessThan key_compare;
- typedef std::pair<key_type, mapped_type> value_type;
- typedef typename VectorType::size_type size_type;
- typedef typename VectorType::difference_type difference_type;
- typedef typename VectorType::iterator iterator;
- typedef typename VectorType::const_iterator const_iterator;
-
- FlatMap() {}
- FlatMap(const FlatMap& other) : vector_(other.vector_) {}
-
- template <typename ITERATOR>
- FlatMap(ITERATOR start, ITERATOR end) {
- insert(start, end);
- }
-
- void reserve(size_t size) { vector_.reserve(size); }
-
- // Takes the incoming elements and only adds the elements that don't already
- // exist in this map.
- // Returns the number of elements that were added.
- template <typename Iterator>
- size_t insert(Iterator begin_it, Iterator end_it) {
- const size_t partition_idx = vector_.size();
-
- // PART 1 - Elements are added unsorted into array as a new partition.
- //
- // Only add elements that don't exist
- for (Iterator it = begin_it; it != end_it; ++it) {
- // These have to be recomputed every loop iteration because
- // vector_.push_back() will invalidate iterators.
- const_iterator sorted_begin = vector_.begin();
- const_iterator sorted_end = sorted_begin + partition_idx;
-
- const bool already_exists_sorted_part =
- exists_in_range(sorted_begin, sorted_end, it->first);
- // Okay so it doesn't exist yet so place it in the vector.
- if (!already_exists_sorted_part) {
- vector_.push_back(*it);
- }
- }
-
- // No elements added.
- if (vector_.size() == partition_idx) {
- return 0;
- }
-
- iterator unsorted_begin = vector_.begin() + partition_idx;
- // std::sort(...) will not maintain the order of values which have
- // keys. Therefore InplaceMergeSort(...) is used instead, which has
- // the same guarantees as std::stable_sort but with the added addition
- // that no memory will be allocated during the operation of
- // InplaceMergeSort(...). This is important because when bulk inserting
- // elements, the first key-value pair (of duplicate keys) will be the one
- // that gets inserted, and the second one is ignored.
- InplaceMergeSort(unsorted_begin, vector_.end());
-
- // Corner-case: remove duplicates in the input.
- iterator new_end = std::unique(unsorted_begin, vector_.end(), EqualTo);
- std::inplace_merge(vector_.begin(), unsorted_begin, new_end, LessThanValue);
-
- vector_.erase(new_end, vector_.end());
-
- return std::distance(vector_.begin() + partition_idx, new_end);
- }
-
- std::pair<iterator, bool> insert(const value_type& entry) {
- iterator insertion_it =
- std::upper_bound(begin(), end(), entry, LessThanValue);
-
- // DUPLICATE CHECK - If the key already exists then return it.
- if (insertion_it != begin()) {
- // Check for a duplicate value, which will be the preceding value, if
- // it exits.
- iterator previous_it = (insertion_it - 1);
- const key_type& previous_key = previous_it->first;
- if (EqualKeyTo(previous_key, entry.first)) {
- // Value already exists.
- return std::pair<iterator, bool>(previous_it, false);
- }
- }
-
- iterator inserted_it = vector_.insert(insertion_it, entry);
- return std::pair<iterator, bool>(inserted_it, true);
- }
-
- iterator find(const key_type& key) {
- return find_in_range(vector_.begin(), vector_.end(), key);
- }
-
- const_iterator find(const key_type& key) const {
- return find_in_range_const(vector_.begin(), vector_.end(), key);
- }
-
- bool erase(const key_type& key) {
- iterator found_it = find(key);
- if (found_it != vector_.end()) {
- vector_.erase(found_it); // no resorting necessary.
- return true;
- } else {
- return false;
- }
- }
-
- void erase(iterator begin_it, iterator end_it) {
- vector_.erase(begin_it, end_it);
- }
-
- bool empty() const { return vector_.empty(); }
- size_t size() const { return vector_.size(); }
- iterator begin() { return vector_.begin(); }
- const_iterator begin() const { return vector_.begin(); }
- const_iterator cbegin() const { return vector_.begin(); }
- iterator end() { return vector_.end(); }
- const_iterator end() const { return vector_.end(); }
- const_iterator cend() const { return vector_.end(); }
- void clear() { vector_.clear(); }
-
- size_t count(const key_type& key) const {
- if (cend() != find(key)) {
- return 1;
- } else {
- return 0;
- }
- }
-
- size_type max_size() const { return vector_.max_size(); }
- void swap(FlatMap& other) { vector_.swap(other.vector_); }
-
- iterator lower_bound(const key_type& key) {
- mapped_type dummy;
- value_type key_data(key, dummy); // Second value is ignored.
- return std::lower_bound(begin(), end(), key_data, LessThanValue);
- }
-
- const_iterator lower_bound(const key_type& key) const {
- mapped_type dummy;
- value_type key_data(key, dummy); // Second value is ignored.
- return std::lower_bound(cbegin(), cend(), key_data, LessThanValue);
- }
-
- iterator upper_bound(const key_type& key) {
- mapped_type dummy;
- value_type key_data(key, dummy); // Second value is ignored.
- return std::upper_bound(begin(), end(), key_data, LessThanValue);
- }
-
- const_iterator upper_bound(const key_type& key) const {
- mapped_type dummy;
- value_type key_data(key, dummy); // Second value is ignored.
- return std::upper_bound(cbegin(), cend(), key_data, LessThanValue);
- }
-
- std::pair<iterator, iterator> equal_range(const key_type& key) {
- iterator found = find(key);
- if (found == end()) {
- return std::pair<iterator, iterator>(end(), end());
- }
- iterator found_end = found;
- ++found_end;
- return std::pair<iterator, iterator>(found, found_end);
- }
-
- std::pair<const_iterator, const_iterator> equal_range(
- const key_type& key) const {
- const_iterator found = find(key);
- if (found == end()) {
- return std::pair<const_iterator, const_iterator>(cend(), cend());
- }
- const_iterator found_end = found;
- ++found_end;
- return std::pair<const_iterator, const_iterator>(found, found_end);
- }
-
- key_compare key_comp() const {
- key_compare return_value;
- return return_value;
- }
-
- mapped_type& operator[](const key_type& key) {
- std::pair<key_type, mapped_type> entry;
- entry.first = key;
- std::pair<iterator, bool> result = insert(entry);
- iterator found = result.first;
- mapped_type& value = found->second;
- return value;
- }
-
- bool operator==(const FlatMap& other) const {
- return vector_ == other.vector_;
- }
-
- bool operator!=(const FlatMap& other) const {
- return vector_ != other.vector_;
- }
-
- private:
- static bool LessThanValue(const std::pair<key_type, mapped_type>& a,
- const std::pair<key_type, mapped_type>& b) {
- return LessThanKey(a.first, b.first);
- }
-
- static bool LessThanKey(const key_type& a, const key_type& b) {
- key_compare less_than;
- return less_than(a, b);
- }
-
- static bool NotEqualKeyTo(const key_type& a, const key_type& b) {
- key_compare less_than;
- return less_than(a, b) || less_than(b, a);
- }
-
- static bool EqualKeyTo(const key_type& a, const key_type& b) {
- return !NotEqualKeyTo(a, b);
- }
-
- static bool EqualTo(const std::pair<key_type, mapped_type>& a,
- const std::pair<key_type, mapped_type>& b) {
- return EqualKeyTo(a.first, b.first);
- }
-
- static iterator find_in_range(iterator begin_it,
- iterator end_it,
- const key_type& key) {
- // Delegate to find_in_range_const().
- const_iterator begin_it_const = begin_it;
- const_iterator end_it_const = end_it;
- const_iterator found_it_const =
- find_in_range_const(begin_it_const, end_it_const, key);
- const size_t diff = std::distance(begin_it_const, found_it_const);
- return begin_it + diff;
- }
-
- static inline const_iterator find_in_range_const_linear(
- const_iterator begin_it,
- const_iterator end_it,
- const value_type& key_data) {
- SB_DCHECK(end_it >= begin_it);
- for (const_iterator it = begin_it; it != end_it; ++it) {
- if (LessThanValue(key_data, *it)) {
- continue;
- }
- if (!LessThanValue(*it, key_data)) {
- return it;
- }
- }
- return end_it;
- }
-
- static inline const_iterator find_in_range_const(const_iterator begin_it,
- const_iterator end_it,
- const key_type& key) {
- // This was tested and found to have a very positive effect on
- // performance. The threshold could be a lot higher (~20 elements) but is
- // kept at 11 elements to be on the conservative side.
- static const difference_type kLinearSearchThreshold = 11;
-
- mapped_type dummy;
- value_type key_data(key, dummy); // Second value is ignored.
-
- // Speedup for small maps of pod type: just do a linear search. This was
- // tested to be significantly faster - 2x speedup. The conditions for this
- // search are very specific so that in many situations the fast path won't
- // be taken.
- if (flat_map_detail::IsPod<key_type>::value) {
- const difference_type range_distance = std::distance(begin_it, end_it);
-
- // Linear search.
- if (range_distance < kLinearSearchThreshold) {
- return find_in_range_const_linear(begin_it, end_it, key_data);
- }
- }
-
- const_iterator found_it =
- std::lower_bound(begin_it, end_it, key_data, LessThanValue);
- if (found_it == end_it) {
- return end_it;
- }
- if (NotEqualKeyTo(found_it->first, key)) { // different keys.
- return end_it;
- }
- size_t dist = std::distance(begin_it, found_it);
- return begin_it + dist;
- }
-
- static bool exists_in_range(const_iterator begin_it,
- const_iterator end_it,
- const key_type& key) {
- const_iterator result_iterator = find_in_range_const(begin_it, end_it, key);
- return result_iterator != end_it;
- }
-
- // This is needed as a stable sorting algorithm, which leaves duplicates in
- // relative order from which they appeared in the original container.
- // Unlike std::stable_sort(...) this function will not allocate memory.
- void InplaceMergeSort(iterator begin_it, iterator end_it) {
- if (end_it - begin_it > 1) {
- iterator middle_it = begin_it + (end_it - begin_it) / 2;
- InplaceMergeSort(begin_it, middle_it);
- InplaceMergeSort(middle_it, end_it);
- std::inplace_merge(begin_it, middle_it, end_it, LessThanValue);
- }
- }
-
- VectorType vector_;
-};
-
-namespace flat_map_detail {
-#define STARBOARD_FLATMAP_DEFINE_IS_POD(TYPE) \
- template <> \
- struct IsPod<TYPE> { \
- enum { value = true }; \
- }
-
-STARBOARD_FLATMAP_DEFINE_IS_POD(bool);
-STARBOARD_FLATMAP_DEFINE_IS_POD(float);
-STARBOARD_FLATMAP_DEFINE_IS_POD(double);
-STARBOARD_FLATMAP_DEFINE_IS_POD(int8_t);
-STARBOARD_FLATMAP_DEFINE_IS_POD(uint8_t);
-STARBOARD_FLATMAP_DEFINE_IS_POD(int16_t);
-STARBOARD_FLATMAP_DEFINE_IS_POD(uint16_t);
-STARBOARD_FLATMAP_DEFINE_IS_POD(int32_t);
-STARBOARD_FLATMAP_DEFINE_IS_POD(uint32_t);
-STARBOARD_FLATMAP_DEFINE_IS_POD(int64_t);
-STARBOARD_FLATMAP_DEFINE_IS_POD(uint64_t);
-
-#undef STARBOARD_FLATMAP_DEFINE_IS_POD
-
-// Specialization - all pointer types are treated as pod.
-template <typename T>
-struct IsPod<T*> {
- enum { value = true };
-};
-
-} // namespace flat_map_detail.
-} // namespace starboard.
-
-#endif // STARBOARD_COMMON_FLAT_MAP_H_
+// 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 STARBOARD_COMMON_FLAT_MAP_H_
+#define STARBOARD_COMMON_FLAT_MAP_H_
+
+#include <algorithm>
+#include <functional>
+#include <utility>
+#include <vector>
+
+#include "starboard/log.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace flat_map_detail {
+// IsPod<> is a white list of common types that are "plain-old-data'.
+// Types not registered with IsPod<> will default to non-pod.
+// Usage: IsPod<int>::value == true;
+// IsPod<std::string>::value == false
+// See specializations at the bottom of this file for what has been defined
+// as pod.
+template <typename T>
+struct IsPod {
+ enum { value = false };
+};
+} // namespace flat_map_detail.
+
+// FlatMap<key_type, mapped_type> is a sorted vector map of key-value pairs.
+// This map has an interface that is largely compatible with std::map<> and
+// is designed to be a near drop in replacement for std::map<>.
+//
+// A main usage difference between FlatMap<> and std::map<> is that FlatMap<>
+// will invalidate it's iterators on insert() and erase().
+//
+// Reasons to use this map include low-level operations which require that
+// the map does not allocate memory during runtime (this is achievable by
+// invoking FlatMap::reserve()), or to speed up a lot of find() operations
+// where the FlatMap() is mutated to very occasionally.
+//
+// PERFORMANCE
+// where n is the number of items in flatmap
+// and m is the input size for the operation.
+//
+// bulk insert | O(n+m + m*log(m))
+// insert | O(n)
+// erase | O(n)
+// bulk erase | O(n*m) TODO: Make faster - O(n+m + log(m))
+// find | O(log(n))
+// clear | O(n)
+//
+// Performance of FlatMap::find() tends to be about +50% faster than that
+// of std::map<> for pod types in optimized builds. However this is balanced
+// by the fact that FlatMap::insert() and FlatMap::erase() both operate at
+// O(n), where std::map will operate at O(log n) for both operations.
+//
+// FlatMap<int,int>::find() Performance
+// NUMBER OF ELEMENTS | SPEED COMPARSION vs std::map
+// -------------------------------------
+// 5 | 220.37%
+// 10 | 158.602%
+// 25 | 87.7049%
+// 50 | 91.0873%
+// 100 | 96.1367%
+// 1000 | 120.588%
+// 10000 | 156.969%
+// 100000 | 179.55%
+//
+// When in doubt, use std::map. If you need to use FlatMap<>, then make sure
+// that insertion() is done in bulk, and that delete are infrequent, or that
+// the maps are small.
+//
+// Please see unit tests for additional usage.
+
+template <typename Key,
+ typename Value,
+ typename LessThan = std::less<Key>,
+ typename VectorType = std::vector<std::pair<Key, Value> > >
+class FlatMap {
+ public:
+ // Most typedefs here are to make FlatMap<> compatible with std::map<>.
+ typedef Key key_type;
+ typedef Value mapped_type;
+ typedef LessThan key_compare;
+ typedef std::pair<key_type, mapped_type> value_type;
+ typedef typename VectorType::size_type size_type;
+ typedef typename VectorType::difference_type difference_type;
+ typedef typename VectorType::iterator iterator;
+ typedef typename VectorType::const_iterator const_iterator;
+
+ FlatMap() {}
+ FlatMap(const FlatMap& other) : vector_(other.vector_) {}
+
+ template <typename ITERATOR>
+ FlatMap(ITERATOR start, ITERATOR end) {
+ insert(start, end);
+ }
+
+ void reserve(size_t size) { vector_.reserve(size); }
+
+ // Takes the incoming elements and only adds the elements that don't already
+ // exist in this map.
+ // Returns the number of elements that were added.
+ template <typename Iterator>
+ size_t insert(Iterator begin_it, Iterator end_it) {
+ const size_t partition_idx = vector_.size();
+
+ // PART 1 - Elements are added unsorted into array as a new partition.
+ //
+ // Only add elements that don't exist
+ for (Iterator it = begin_it; it != end_it; ++it) {
+ // These have to be recomputed every loop iteration because
+ // vector_.push_back() will invalidate iterators.
+ const_iterator sorted_begin = vector_.begin();
+ const_iterator sorted_end = sorted_begin + partition_idx;
+
+ const bool already_exists_sorted_part =
+ exists_in_range(sorted_begin, sorted_end, it->first);
+ // Okay so it doesn't exist yet so place it in the vector.
+ if (!already_exists_sorted_part) {
+ vector_.push_back(*it);
+ }
+ }
+
+ // No elements added.
+ if (vector_.size() == partition_idx) {
+ return 0;
+ }
+
+ iterator unsorted_begin = vector_.begin() + partition_idx;
+ // std::sort(...) will not maintain the order of values which have
+ // keys. Therefore InplaceMergeSort(...) is used instead, which has
+ // the same guarantees as std::stable_sort but with the added addition
+ // that no memory will be allocated during the operation of
+ // InplaceMergeSort(...). This is important because when bulk inserting
+ // elements, the first key-value pair (of duplicate keys) will be the one
+ // that gets inserted, and the second one is ignored.
+ InplaceMergeSort(unsorted_begin, vector_.end());
+
+ // Corner-case: remove duplicates in the input.
+ iterator new_end = std::unique(unsorted_begin, vector_.end(), EqualTo);
+ std::inplace_merge(vector_.begin(), unsorted_begin, new_end, LessThanValue);
+
+ vector_.erase(new_end, vector_.end());
+
+ return std::distance(vector_.begin() + partition_idx, new_end);
+ }
+
+ std::pair<iterator, bool> insert(const value_type& entry) {
+ iterator insertion_it =
+ std::upper_bound(begin(), end(), entry, LessThanValue);
+
+ // DUPLICATE CHECK - If the key already exists then return it.
+ if (insertion_it != begin()) {
+ // Check for a duplicate value, which will be the preceding value, if
+ // it exits.
+ iterator previous_it = (insertion_it - 1);
+ const key_type& previous_key = previous_it->first;
+ if (EqualKeyTo(previous_key, entry.first)) {
+ // Value already exists.
+ return std::pair<iterator, bool>(previous_it, false);
+ }
+ }
+
+ iterator inserted_it = vector_.insert(insertion_it, entry);
+ return std::pair<iterator, bool>(inserted_it, true);
+ }
+
+ iterator find(const key_type& key) {
+ return find_in_range(vector_.begin(), vector_.end(), key);
+ }
+
+ const_iterator find(const key_type& key) const {
+ return find_in_range_const(vector_.begin(), vector_.end(), key);
+ }
+
+ bool erase(const key_type& key) {
+ iterator found_it = find(key);
+ if (found_it != vector_.end()) {
+ vector_.erase(found_it); // no resorting necessary.
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void erase(iterator begin_it, iterator end_it) {
+ vector_.erase(begin_it, end_it);
+ }
+
+ bool empty() const { return vector_.empty(); }
+ size_t size() const { return vector_.size(); }
+ iterator begin() { return vector_.begin(); }
+ const_iterator begin() const { return vector_.begin(); }
+ const_iterator cbegin() const { return vector_.begin(); }
+ iterator end() { return vector_.end(); }
+ const_iterator end() const { return vector_.end(); }
+ const_iterator cend() const { return vector_.end(); }
+ void clear() { vector_.clear(); }
+
+ size_t count(const key_type& key) const {
+ if (cend() != find(key)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ size_type max_size() const { return vector_.max_size(); }
+ void swap(FlatMap& other) { vector_.swap(other.vector_); }
+
+ iterator lower_bound(const key_type& key) {
+ mapped_type dummy;
+ value_type key_data(key, dummy); // Second value is ignored.
+ return std::lower_bound(begin(), end(), key_data, LessThanValue);
+ }
+
+ const_iterator lower_bound(const key_type& key) const {
+ mapped_type dummy;
+ value_type key_data(key, dummy); // Second value is ignored.
+ return std::lower_bound(cbegin(), cend(), key_data, LessThanValue);
+ }
+
+ iterator upper_bound(const key_type& key) {
+ mapped_type dummy;
+ value_type key_data(key, dummy); // Second value is ignored.
+ return std::upper_bound(begin(), end(), key_data, LessThanValue);
+ }
+
+ const_iterator upper_bound(const key_type& key) const {
+ mapped_type dummy;
+ value_type key_data(key, dummy); // Second value is ignored.
+ return std::upper_bound(cbegin(), cend(), key_data, LessThanValue);
+ }
+
+ std::pair<iterator, iterator> equal_range(const key_type& key) {
+ iterator found = find(key);
+ if (found == end()) {
+ return std::pair<iterator, iterator>(end(), end());
+ }
+ iterator found_end = found;
+ ++found_end;
+ return std::pair<iterator, iterator>(found, found_end);
+ }
+
+ std::pair<const_iterator, const_iterator> equal_range(
+ const key_type& key) const {
+ const_iterator found = find(key);
+ if (found == end()) {
+ return std::pair<const_iterator, const_iterator>(cend(), cend());
+ }
+ const_iterator found_end = found;
+ ++found_end;
+ return std::pair<const_iterator, const_iterator>(found, found_end);
+ }
+
+ key_compare key_comp() const {
+ key_compare return_value;
+ return return_value;
+ }
+
+ mapped_type& operator[](const key_type& key) {
+ std::pair<key_type, mapped_type> entry;
+ entry.first = key;
+ std::pair<iterator, bool> result = insert(entry);
+ iterator found = result.first;
+ mapped_type& value = found->second;
+ return value;
+ }
+
+ bool operator==(const FlatMap& other) const {
+ return vector_ == other.vector_;
+ }
+
+ bool operator!=(const FlatMap& other) const {
+ return vector_ != other.vector_;
+ }
+
+ private:
+ static bool LessThanValue(const std::pair<key_type, mapped_type>& a,
+ const std::pair<key_type, mapped_type>& b) {
+ return LessThanKey(a.first, b.first);
+ }
+
+ static bool LessThanKey(const key_type& a, const key_type& b) {
+ key_compare less_than;
+ return less_than(a, b);
+ }
+
+ static bool NotEqualKeyTo(const key_type& a, const key_type& b) {
+ key_compare less_than;
+ return less_than(a, b) || less_than(b, a);
+ }
+
+ static bool EqualKeyTo(const key_type& a, const key_type& b) {
+ return !NotEqualKeyTo(a, b);
+ }
+
+ static bool EqualTo(const std::pair<key_type, mapped_type>& a,
+ const std::pair<key_type, mapped_type>& b) {
+ return EqualKeyTo(a.first, b.first);
+ }
+
+ static iterator find_in_range(iterator begin_it,
+ iterator end_it,
+ const key_type& key) {
+ // Delegate to find_in_range_const().
+ const_iterator begin_it_const = begin_it;
+ const_iterator end_it_const = end_it;
+ const_iterator found_it_const =
+ find_in_range_const(begin_it_const, end_it_const, key);
+ const size_t diff = std::distance(begin_it_const, found_it_const);
+ return begin_it + diff;
+ }
+
+ static inline const_iterator find_in_range_const_linear(
+ const_iterator begin_it,
+ const_iterator end_it,
+ const value_type& key_data) {
+ SB_DCHECK(end_it >= begin_it);
+ for (const_iterator it = begin_it; it != end_it; ++it) {
+ if (LessThanValue(key_data, *it)) {
+ continue;
+ }
+ if (!LessThanValue(*it, key_data)) {
+ return it;
+ }
+ }
+ return end_it;
+ }
+
+ static inline const_iterator find_in_range_const(const_iterator begin_it,
+ const_iterator end_it,
+ const key_type& key) {
+ // This was tested and found to have a very positive effect on
+ // performance. The threshold could be a lot higher (~20 elements) but is
+ // kept at 11 elements to be on the conservative side.
+ static const difference_type kLinearSearchThreshold = 11;
+
+ mapped_type dummy;
+ value_type key_data(key, dummy); // Second value is ignored.
+
+ // Speedup for small maps of pod type: just do a linear search. This was
+ // tested to be significantly faster - 2x speedup. The conditions for this
+ // search are very specific so that in many situations the fast path won't
+ // be taken.
+ if (flat_map_detail::IsPod<key_type>::value) {
+ const difference_type range_distance = std::distance(begin_it, end_it);
+
+ // Linear search.
+ if (range_distance < kLinearSearchThreshold) {
+ return find_in_range_const_linear(begin_it, end_it, key_data);
+ }
+ }
+
+ const_iterator found_it =
+ std::lower_bound(begin_it, end_it, key_data, LessThanValue);
+ if (found_it == end_it) {
+ return end_it;
+ }
+ if (NotEqualKeyTo(found_it->first, key)) { // different keys.
+ return end_it;
+ }
+ size_t dist = std::distance(begin_it, found_it);
+ return begin_it + dist;
+ }
+
+ static bool exists_in_range(const_iterator begin_it,
+ const_iterator end_it,
+ const key_type& key) {
+ const_iterator result_iterator = find_in_range_const(begin_it, end_it, key);
+ return result_iterator != end_it;
+ }
+
+ // This is needed as a stable sorting algorithm, which leaves duplicates in
+ // relative order from which they appeared in the original container.
+ // Unlike std::stable_sort(...) this function will not allocate memory.
+ void InplaceMergeSort(iterator begin_it, iterator end_it) {
+ if (end_it - begin_it > 1) {
+ iterator middle_it = begin_it + (end_it - begin_it) / 2;
+ InplaceMergeSort(begin_it, middle_it);
+ InplaceMergeSort(middle_it, end_it);
+ std::inplace_merge(begin_it, middle_it, end_it, LessThanValue);
+ }
+ }
+
+ VectorType vector_;
+};
+
+namespace flat_map_detail {
+#define STARBOARD_FLATMAP_DEFINE_IS_POD(TYPE) \
+ template <> \
+ struct IsPod<TYPE> { \
+ enum { value = true }; \
+ }
+
+STARBOARD_FLATMAP_DEFINE_IS_POD(bool);
+STARBOARD_FLATMAP_DEFINE_IS_POD(float);
+STARBOARD_FLATMAP_DEFINE_IS_POD(double);
+STARBOARD_FLATMAP_DEFINE_IS_POD(int8_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(uint8_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(int16_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(uint16_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(int32_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(uint32_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(int64_t);
+STARBOARD_FLATMAP_DEFINE_IS_POD(uint64_t);
+
+#undef STARBOARD_FLATMAP_DEFINE_IS_POD
+
+// Specialization - all pointer types are treated as pod.
+template <typename T>
+struct IsPod<T*> {
+ enum { value = true };
+};
+
+} // namespace flat_map_detail.
+} // namespace starboard.
+
+#endif // STARBOARD_COMMON_FLAT_MAP_H_
diff --git a/src/starboard/creator/ci20directfb/starboard_platform.gyp b/src/starboard/creator/ci20directfb/starboard_platform.gyp
index d5566bf..d897ef9 100644
--- a/src/starboard/creator/ci20directfb/starboard_platform.gyp
+++ b/src/starboard/creator/ci20directfb/starboard_platform.gyp
@@ -241,6 +241,8 @@
'<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
'<(DEPTH)/starboard/shared/signal/suspend_signals.h',
'<(DEPTH)/starboard/shared/starboard/application.cc',
+ '<(DEPTH)/starboard/shared/starboard/command_line.cc',
+ '<(DEPTH)/starboard/shared/starboard/command_line.h',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
diff --git a/src/starboard/creator/ci20x11/starboard_platform.gyp b/src/starboard/creator/ci20x11/starboard_platform.gyp
index b694772..8105b97 100644
--- a/src/starboard/creator/ci20x11/starboard_platform.gyp
+++ b/src/starboard/creator/ci20x11/starboard_platform.gyp
@@ -206,6 +206,8 @@
'<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
'<(DEPTH)/starboard/shared/signal/suspend_signals.h',
'<(DEPTH)/starboard/shared/starboard/application.cc',
+ '<(DEPTH)/starboard/shared/starboard/command_line.cc',
+ '<(DEPTH)/starboard/shared/starboard/command_line.h',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
diff --git a/src/starboard/doc/principles.md b/src/starboard/doc/principles.md
new file mode 100644
index 0000000..a58fba7
--- /dev/null
+++ b/src/starboard/doc/principles.md
@@ -0,0 +1,153 @@
+# Starboard Design Principles
+
+An overview of the goals and design principles of Starboard with the perspective
+of hindsight.
+
+**Status:** REVIEWED\
+**Created:** 2016-11-12
+
+Starboard is a porting abstraction and a collection of implementation fragments
+used to abstract operating system facilities and platform quirks from C or C++
+applications. It occupies a similar space to SDL, DirectFB, Marmalade, and
+various others.
+
+## Background
+
+Starboard was created as a response to the significant effort it has
+historically taken to port desktop-oriented web browsers to non-traditional
+device platforms like game consoles. Chromium in particular mixes
+platform-specific code with common code throughout the technology stack, making
+it very difficult to know what has to be done for a new platform or how much
+work it is going to be.
+
+## Goals
+
+Here are the main goals of Starboard, stack-ranked from most-to-least important.
+
+ * **G1** Minimize the total time and effort required to port Starboard Client
+ Applications to new platforms.
+ * **G2** Minimize the incremental time and effort required to rebase Starboard
+ Client Applications across platforms.
+ * **G3** Enable third parties to port Starboard to their platform without
+ significant engineering support from the Starboard team.
+ * **G4** Ensure support for low-profile platforms that are not geared towards
+ broad native C/C++ development access.
+ * **G5** Provide an organization framework for platform-specific code, clearly
+ delineating what is common and what is platform-specific, consolidating
+ platform-specific code into a single location, and enumerating all the APIs
+ needed to provide full functionality.
+ * **G6** Encapsulate all platform-provided services needed to build graphical
+ media applications into a single API.
+ * **G7** Reduce the total surface area needed to port to new platforms.
+ * **G8** Improve and encourage sharing of platform-specific implementation
+ components between platforms.
+ * **G9** Maximize the amount of common (platform-independent) code, to avoid
+ variances between platforms, and to increase the leverage of testing,
+ including fuzz testing which must often be done on particular platforms.
+ * **G10** Maintain a loose binding between a Starboard Platform Implementation
+ and a Starboard Client Application, such that they can be updated
+ independently at a source level.
+ * **G11** Avoid the pitfalls of trying to emulate POSIX, including, but not
+ limited to: auto-included headers, global defines of short and common
+ symbols, wrapping misbehaving or misprototyped system APIs, using custom
+ toolchain facilities, and conflicts with platform headers.
+
+## Principles
+
+### Make APIs sufficient for their purpose, but minimally so.
+
+APIs can generally be augmented without serious backwards-compatibility
+consequences, but they can not be changed or pruned so easily, so it is better
+to **err on the side of providing less**.
+
+#### Corollary: Implementation details should be as hidden as possible.
+
+#### Corollary: Anything that could be implemented purely on top of Starboard APIs should be implemented purely on top of Starboard APIs.
+
+#### Exception: If there are good reasons why an API may need to be implemented in a platform-specific manner on one or more platforms, but can be commonly implemented on other platforms, it should be part of the API, with a shared Starboard-based implementation.
+
+#### Exception: For the select few cases where Starboard implementations also need to use it, it should be included in the Starboard API so that can happen without creating circular dependencies.
+
+### Specify public APIs concretely and narrowly.
+
+A broader specification of the behavior of an API function makes life easier for
+the implementor, but more difficult for anyone attempting to use the API. An API
+can be so weakly specified that it is not usable across platforms. It can also
+be so strictly specified that it is not implementable across platforms. **Err on
+the side of narrower specifications**, requiring generality only when
+necessitated by one or more platforms.
+
+#### Corollary: Documentation should be exhaustive and clear.
+
+#### Corollary: Avoid overly-flexible convention-based interfaces.
+
+For example, passing in a set of string-string name-value pairs. This takes the
+compiler out of any kind of validation, and can encourage mismatches of
+understanding between Clients and Platforms.
+
+### Minimize the burden on the porter.
+
+Whenever adding or changing an API, or specifying a contract, consider whether
+this places a large burden on some platform implementers. This burden could be
+because of a wide porting surface, or complex requirements that are difficult to
+implement. It could be caused by a fundamental piece of infrastructure that
+isn't provided by a particular platform.
+
+We can always make APIs that are burdensome to use easier with more common code.
+
+### Be consistent and predictable.
+
+Consistency, not just in formatting, but in semantics, leads to
+predictability. Some people just won't read the documentation, even if it's
+thorough. Perhaps especially if it's thorough. The names of API entities should
+convey the intention as completely as possible.
+
+Yet, overly-verbose naming will make the API difficult to remember, read, and
+use.
+
+### Assume the porter knows nothing.
+
+Engineers from a broad set of backgrounds and environments will end up being
+dropped into porting Starboard. They may not have knowledge of any particular
+technologies, best practices, or design patterns. They may be under an
+aggressive deadline.
+
+### Always consider threading (and document it).
+
+Each function and module should have a strategy for dealing with multiple
+threads. It should make sense for the expected use cases of the interface
+entities in question. As the interface designer, it is most clear to you how the
+API should be used with respect to threads.
+
+Some may be "thread-safe," such that any functions can be called from any thread
+without much concern. Note that this places the burden on the implementer to
+ensure that an implementation meets that specification, and they MAY not know
+how to do that. This can also be more complex than just acquiring a mutex inside
+every function implementation, as there may be inherent race conditions between
+function calls even when using a synchronization mechanism.
+
+The path of least burden to the porter is to say that an interface is not
+thread-safe at all, which means applications will have to take care how the API
+is used. But, sometimes even this requires clarification as to what modes of
+access are dangerous and how to use the API safely.
+
+### Don't expose Informational-Only result codes, but do DLOG them.
+
+"Informational-Only" is defined by a result code that doesn't change the
+behavior of the caller. Many times, why something failed does not matter when
+the product is already in the hands of the user. We often want diagnostic
+logging during development
+
+### Trust but Verify. Whenever possible, write NPLB tests for all contracts declared by the interface.
+
+#### Corollary: For hard-to-test features (like Input) add an example sandbox app for testing.
+
+### We will get it wrong the first time, so plan for some kind of change mechanism.
+
+Starboard has a [versioning mechanism](versioning.md) to manage change.
+
+## References
+ * [Joshua Bloch's presentation about API design](https://www.youtube.com/watch?v=aAb7hSCtvGw)
+ * [Joshua Bloch's bumper sticker API design rules](http://www.infoq.com/articles/API-Design-Joshua-Bloch)
+ * [digithead's collection of API design links (I didn't read them all)](http://digitheadslabnotebook.blogspot.com/2010/07/how-to-design-good-apis.html)
+
diff --git a/src/starboard/doc/style.md b/src/starboard/doc/style.md
new file mode 100644
index 0000000..7345067
--- /dev/null
+++ b/src/starboard/doc/style.md
@@ -0,0 +1,271 @@
+# Starboard C and C++ Style Guide
+
+A description of the coding conventions for Starboard code and API headers.
+
+**Status:** REVIEWED\
+**Created:** 2016-11-08
+
+Starboard generally tries to follow the coding conventions of Cobalt, which
+itself mostly follows the conventions of Chromium, which mostly follows the
+externally-published Google C++ coding conventions. But, Starboard has some
+special requirements due to its unusual constraints, so it must add a few new
+conventions and loosen some of the existing style prescriptions.
+
+## Background
+
+Before looking at this document, bear in mind that it is not intending to
+completely describe all conventions that apply to Starboard. You probably want
+to take some time to familiarize yourself with these documents first, probably
+in this order:
+
+ * [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
+ * [Chromium C++ Style Guide](https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++.md)
+ * [Cobalt Style Guide](http://cobalt.foo/broken)
+
+The main additional constraints that Starboard has to deal with are:
+
+ * The Starboard API is defined in straight-C. It must be able to interface
+ with all possible third-party components, many of which are in C and not
+ C++.
+ * Starboard is a public API. Starboard platform implementations and
+ applications written on top of Starboard will change independently. This
+ means there are intense requirements for API stability, usage
+ predictability, searchability, and documentation.
+ * Note that even though it is presented as a "style guide," the conventions
+ presented here are required to be approved for check-in unless otherwise
+ noted.
+
+
+## Definitions
+
+### snake-case
+
+Words separated with underscores.
+
+ lower_snake_case
+ ALL_CAPS_SNAKE_CASE
+
+### camel-case
+
+Words separated by letter capitalization.
+
+ camelCase
+ CapitalizedCamelCase
+
+## C++ Guidelines
+
+What follows are hereby the guidelines for Starboard C and C++ code. Heretofore
+the guidelines follow thusly as follows.
+
+### API Definitions
+
+ * Starboard API definitions must always be compatible with straight-C99 compilers.
+ * All public API declarations must be specified in headers in
+ `src/starboard/*.h`, not in any subdirectories.
+ * Non-public declarations must NOT be specified in headers in
+ `src/starboard/*.h`.
+ * C++ inline helper definitions may be included inside an `#if
+ defined(__cplusplus)` preprocessor block. They must only provide
+ convenience, and must NOT be required for any API functionality.
+ * All public API functions should be exported symbols with the SB_EXPORT
+ attribute.
+ * No non-const variables shall be exposed as part of the public API.
+ * All APIs should be implemented in C++ source files, not straight-C source files.
+
+### Modules
+
+ * Each module header must be contained with a single header file.
+
+ * The name of the module must be the singular form of the noun being
+ interfaced with by the module, without any "sb" or "starboard".
+ * `file.h`
+ * `directory.h`
+ * `window.h`
+ * Module interfaces should not have circular dependencies.
+
+### File Names
+
+ * Like in the other conventions (e.g. Google, Chromium), file names must be in
+ `lower_snake_case`.
+ * File names must not contain `sb_` or `starboard_`.
+ * The name of a module header file must be the `lower_snake_case` form of the
+ module name.
+ * `SbConditionVariable` âž¡ `starboard/condition_variable.h`
+ * A header that is intended to be an internal implementation detail of one or
+ more platform implementations should have the suffix `_internal.h`, and
+ include the header `starboard/shared/internal_only.h`.
+ * See "Implementations" for conventions about where to place implementation files.
+
+### Types
+
+ * Like in the other conventions, types should be `CapitalizedCamelCase`.
+ * Every public Starboard type must start with `Sb`. There are no namespaces in
+ C, so `Sb` is the Starboard namespace.
+ * Every public Starboard type must be declared by a module, and must have the
+ name of the module following the `Sb`.
+ * `file.h` contains `SbFile`, `SbFileInfo`, `SbFileWhence`, etc...
+ * Every seemingly-allocatable, platform-specific Starboard type should be
+ defined as an opaque handle to a publically undefined struct with the
+ `Private` suffix. Follow this pattern for all such type declarations.
+ * `struct SbFilePrivate` is declared, but not defined in the public header.
+ * `SbFilePrivate` is `typedef`'d to `struct SbFilePrivate`. This is a C
+ thing where types are defined as names with the "`struct`" keyword
+ prepended unless `typedef`'d.
+ * `SbFile` is defined as a `typedef` of `struct SbFilePrivate*`.
+ * C structs may be defined internally to have functions and visibility. It is
+ allowed for such structs to have constructors, destructors, methods,
+ members, and public members.
+ * It is also considered idiomatic to never define the private struct but to
+ just treat it like a handle into some other method of object tracking,
+ casting the handle back and forth to the pointer type.
+ * If a word in the name of a type is redundant with the module name, it is
+ omitted.
+ * A monotonic time type in the Time module is `SbTimeMonotonic`, not
+ ~~`SbMonotonicTime`, `SbTimeMonotonicTime`, or
+ `SbTimeMonotonicSbTime`~~.
+
+### Functions
+
+ * Like in the other conventions, functions should be `CapitalizedCamelCase`.
+ * Every public Starboard function must start with `Sb`. There are no namespaces
+ in C, so `Sb` is the Starboard namespace.
+ * Every public Starboard function must be declared by a module, and must have
+ the name of the module following the `Sb`.
+ * `system.h` contains `SbSystemGetPath()`
+ * `file.h` contains `SbFileOpen()`
+ * After the Starboard and Module prefix, functions should start with an
+ imperative verb indicating what the function does.
+ * The Thread module defines `SbThreadCreateLocalKey()` to create a key for
+ thread local storage.
+ * If a word in the name of a function is redundant with the module name, it is
+ omitted.
+ * The `File` module as the function `SbFileOpen`, not ~~`SbOpenFile`,
+ `SbFileOpenFile` or `SbFileOpenSbFile`~~.
+ * If this gets awkward, it may indicate a need to split into a different
+ module.
+
+### Variables, Parameters, Fields
+
+ * Like in the other conventions, variable, function parameter, and field names
+ must be in `lower_snake_case`.
+ * Private member fields end in an underscore.
+ * Public member fields do not end in an underscore.
+
+### Namespaces
+
+Most Starboard API headers are straight-C compatible, so cannot live inside a
+namespace. Implementations, since they implement straight-C interface functions,
+also cannot live inside a namespace.
+
+But, in all other cases, Starboard C++ code should follow the inherited
+conventions and use a namespace for each directory starting with a "starboard"
+namespace at the starboard repository root.
+
+### Preprocessor Macros
+
+ * Like in the other conventions, variable, function parameter, and field names
+ must be in `ALL_CAPS_SNAKE_CASE`.
+ * Macros may be used as compile-time constants because straight-C does not
+ have a proper facility for typed constants. This is as opposed to macros
+ used primarily at preprocessor-time to filter or modify what gets sent to
+ the compiler. Macros used as compile-time constants and that are not
+ configuration parameters should be explicitly-typed with a c-style cast, and
+ should follow the Constants naming conventions.
+ * Macros must start with `SB_`, and then must further be namespaced with the
+ module name, with the exception of configuration definitions.
+ * Configuration definitions should be namespaced with the module name that
+ they primarily affect, if applicable, or a scope that generally indicates
+ its domain.
+ * `SB_FILE_MAX_NAME`
+ * `SB_MEMORY_PAGE_SIZE`
+ * Always use `#if defined(MACRO)` over `#ifdef MACRO`.
+
+### Constants
+
+ * Constants (including enum entries) are named using the Google constant
+ naming convention, `CapitalizedCamelCase`d, but starting with a lower-case
+ `k`.
+ * After the `k`, all constants have `Sb`, the Starboard namespace.
+ * `kSb`
+ * After `kSb`, all constants then have the module name.
+ * `kSbTime`
+ * `kSbFile`
+ * After `kSb<module>` comes the rest of the name of the constant.
+ * `kSbTimeMillisecond`
+ * `kSbFileInvalid`
+ * Enum entries are prefixed with the full name of the enum.
+ * The enum `SbSystemDeviceType` contains entries like
+ `kSbSystemDeviceTypeBlueRayDiskPlayer`.
+
+### Comments
+
+ * All files must have a license and copyright comment.
+ * It is expected that the straight-C compiler supports C99 single-line
+ comments. Block comments should be avoided whenever possible, even in
+ license and copyright headers.
+ * Each public API module file should have a Module Overview documentation
+ comment below the license explaining what the module is for, and how to use
+ it effectively.
+ * The Module Overview must be separated by a completely blank line from the
+ license comment.
+ * The first line of the Module Overview documentation comment must say
+ "`Module Overview: Starboard <module-name> module`", followed by a blank
+ comment line (i.e. a line that contains a `//`, but nothing else).
+ * The first sentence of a documentation comment describing any entity (module,
+ type, function, variable, etc...) should assume "This module," "This type,"
+ "This function," or "This variable" at the beginning of the sentence, and
+ not include it.
+ * The first sentence of a documentation comment describing any entity should
+ be a single-sentence summary description of the entire entity.
+ * The first paragraph of a documentation comment should describe the overall
+ behavior of the entity.
+ * Paragraphs in comments should be separated by a blank comment line.
+ * All public entities must have documentation comments, including enum
+ entries.
+ * Documentation comments should be formatted with Markdown.
+ * Variables, constants, literals, and expressions should be referenced in
+ comments with pipes around them.
+ * All comments must be full grammatically-correct English sentences with
+ proper punctuation.
+
+### Implementations
+
+ * Each API implementation should attempt to minimize other platform
+ assumptions, and should therefore use Starboard APIs to accomplish
+ platform-specific work unless directly related to the platform functionality
+ being implemented.
+ * For example, `SbFile` can use POSIX file I/O, because that what it is
+ abstracting, but it should use `SbMemoryAllocate` for any memory
+ allocations, because it might be used with a variety of `SbMemory`
+ implementations.
+ * Whenever possible, each shared function implementation should be implemented
+ in an individual file so as to maximize the chances of reuse between
+ implementations.
+ * This does not apply to platform-specific functions that have no chance of
+ being reusable on other platforms.
+ * Implementation files that can conceivably be shared between one or more
+ implementations should be placed in a `starboard/shared/<dependency>/`
+ directory, where `<dependency>` is the primary platform dependency of that
+ implementation. (e.g. `libevent`, `posix`, `c++11`, etc.)
+ * Implementation files that don't have a specific platform dependency, but
+ whether to use them should be a platform decision should be placed in
+ `starboard/shared/starboard/`, and must only have dependencies on other
+ Starboard APIs.
+ * Implementation files that definitely can be common to ALL implementations
+ should be placed in `starboard/common/`.
+
+### Language Features
+
+ * In public headers, particularly in inline functions and macros, only C-Style
+ casts may be used, though they are forbidden everywhere else.
+ * It is expected that the C compiler supports inline functions. They must be
+ declared `static`, and they must use the `SB_C_INLINE` or
+ `SB_C_FORCE_INLINE` attribute. In straight-C code, there is no anonymous
+ namespace, so `static` is allowed and required for inline functions.
+ * No straight-C ISO or POSIX headers should be assumed to exist. Basic C++03
+ headers may be assumed to exist in C++ code. The ISO C standards have grown
+ up over a long period of time and have historically been implemented with
+ quirks, missing pieces, and so on. Support for the core C++ standard library
+ is much more consistent on those platforms that do support it.
+ * It is idiomatic to include thin C++ inline wrappers inside public API
+ headers, gated by an `#if defined(cplusplus__)` check.
diff --git a/src/starboard/file.h b/src/starboard/file.h
index 0e92907..4e80e92 100644
--- a/src/starboard/file.h
+++ b/src/starboard/file.h
@@ -20,7 +20,6 @@
#define STARBOARD_FILE_H_
#include "starboard/export.h"
-#include "starboard/log.h"
#include "starboard/time.h"
#include "starboard/types.h"
diff --git a/src/starboard/linux/shared/gyp_configuration.gypi b/src/starboard/linux/shared/gyp_configuration.gypi
index 1fd243d..f053843 100644
--- a/src/starboard/linux/shared/gyp_configuration.gypi
+++ b/src/starboard/linux/shared/gyp_configuration.gypi
@@ -24,11 +24,6 @@
'in_app_dial%': 1,
'gl_type%': 'system_gles3',
- # This should have a default value in cobalt/base.gypi. See the comment
- # there for acceptable values for this variable.
- 'javascript_engine': 'mozjs',
- 'cobalt_enable_jit': 1,
-
'platform_libraries': [
'-lasound',
'-lavcodec',
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 6700e41..281a28d 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -192,6 +192,8 @@
'<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
'<(DEPTH)/starboard/shared/signal/suspend_signals.h',
'<(DEPTH)/starboard/shared/starboard/application.cc',
+ '<(DEPTH)/starboard/shared/starboard/command_line.cc',
+ '<(DEPTH)/starboard/shared/starboard/command_line.h',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
@@ -218,6 +220,8 @@
'<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
'<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
'<(DEPTH)/starboard/shared/starboard/new.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
@@ -228,6 +232,8 @@
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/job_queue.h',
'<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
diff --git a/src/starboard/linux/x64directfb/README.md b/src/starboard/linux/x64directfb/README.md
index 3d1a187..b8fd196 100644
--- a/src/starboard/linux/x64directfb/README.md
+++ b/src/starboard/linux/x64directfb/README.md
@@ -50,7 +50,7 @@
which will automatically run an executable (and its parameters) within a
Xephyr window. For example, to run the nplb executable under Xephyr:
- $ starboard/linux/x64/directfb/xephyr_run.sh out/linux-x64directfb_debug/nplb
+ $ starboard/linux/x64directfb/xephyr_run.sh out/linux-x64directfb_debug/nplb
Note that the script will start an instance of Xephyr, run the application
within it, and then shutdown Xephyr when the application terminates.
diff --git a/src/starboard/linux/x64directfb/gyp_configuration.gypi b/src/starboard/linux/x64directfb/gyp_configuration.gypi
index 7005f6f..1fbc03e 100644
--- a/src/starboard/linux/x64directfb/gyp_configuration.gypi
+++ b/src/starboard/linux/x64directfb/gyp_configuration.gypi
@@ -24,6 +24,11 @@
],
'gl_type': 'none',
+
+ # This should have a default value in cobalt/base.gypi. See the comment
+ # there for acceptable values for this variable.
+ 'javascript_engine': 'mozjs',
+ 'cobalt_enable_jit': 1,
},
'target_defaults': {
diff --git a/src/starboard/linux/x64x11/clang/gyp_configuration.gypi b/src/starboard/linux/x64x11/clang/gyp_configuration.gypi
index 2ede477..0df5d5f 100644
--- a/src/starboard/linux/x64x11/clang/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/clang/gyp_configuration.gypi
@@ -13,6 +13,12 @@
# limitations under the License.
{
+ 'variables': {
+ # This should have a default value in cobalt/base.gypi. See the comment
+ # there for acceptable values for this variable.
+ 'javascript_engine': 'mozjs',
+ 'cobalt_enable_jit': 1,
+ },
'includes': [
'../libraries.gypi',
'../../shared/gyp_configuration.gypi',
diff --git a/src/starboard/linux/x64x11/cpp11/atomic_public.h b/src/starboard/linux/x64x11/cpp11/atomic_public.h
new file mode 100644
index 0000000..d74094e
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/atomic_public.h
@@ -0,0 +1,22 @@
+/*
+ * 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 STARBOARD_LINUX_X64X11_CPP11_ATOMIC_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_CPP11_ATOMIC_PUBLIC_H_
+
+#include "starboard/linux/shared/atomic_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_CPP11_ATOMIC_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi b/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi
new file mode 100644
index 0000000..1210a68
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/compiler_flags.gypi
@@ -0,0 +1,161 @@
+# 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.
+
+# Platform specific compiler flags for Linux on Starboard. Included from
+# gyp_configuration.gypi.
+#
+{
+ 'variables': {
+ 'compiler_flags_host': [
+ '-O2',
+ ],
+ 'linker_flags': [
+ ],
+ 'compiler_flags_debug': [
+ '-frtti',
+ '-O0',
+ ],
+ 'compiler_flags_devel': [
+ '-frtti',
+ '-O2',
+ ],
+ 'compiler_flags_qa': [
+ '-fno-rtti',
+ '-O2',
+ '-gline-tables-only',
+ ],
+ 'compiler_flags_gold': [
+ '-fno-rtti',
+ '-O2',
+ '-gline-tables-only',
+ ],
+ 'conditions': [
+ ['clang==1', {
+ 'common_clang_flags': [
+ '-Werror',
+ '-fcolor-diagnostics',
+ # Default visibility to hidden, to enable dead stripping.
+ '-fvisibility=hidden',
+ # Warn for implicit type conversions that may change a value.
+ '-Wconversion',
+ '-Wno-c++11-compat',
+ # This (rightfully) complains about 'override', which we use
+ # heavily.
+ '-Wno-c++11-extensions',
+ # Warns on switches on enums that cover all enum values but
+ # also contain a default: branch. Chrome is full of that.
+ '-Wno-covered-switch-default',
+ # protobuf uses hash_map.
+ '-Wno-deprecated',
+ '-fno-exceptions',
+ # Don't warn about the "struct foo f = {0};" initialization pattern.
+ '-Wno-missing-field-initializers',
+ # Do not warn for implicit sign conversions.
+ '-Wno-sign-conversion',
+ '-fno-strict-aliasing', # See http://crbug.com/32204
+ '-Wno-unnamed-type-template-args',
+ # Triggered by the COMPILE_ASSERT macro.
+ '-Wno-unused-local-typedef',
+ ],
+ }],
+ ['cobalt_fastbuild==0', {
+ 'compiler_flags_debug': [
+ '-g',
+ ],
+ 'compiler_flags_devel': [
+ '-g',
+ ],
+ 'compiler_flags_qa': [
+ '-gline-tables-only',
+ ],
+ 'compiler_flags_gold': [
+ '-gline-tables-only',
+ ],
+ }],
+ ],
+ },
+
+ 'target_defaults': {
+ 'defines': [
+ # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some
+ # nasty macros (|Status|, for example) that conflict with Chromium base.
+ 'MESA_EGL_NO_X11_HEADERS'
+ ],
+ 'cflags_c': [
+ # Limit to C99. This allows Linux to be a canary build for any
+ # C11 features that are not supported on some platforms' compilers.
+ '-std=c99',
+ ],
+ 'cflags_cc': [
+ '-std=gnu++11',
+ # Don't warn about an implicit exception spec mismatch
+ '-Wno-implicit-exception-spec-mismatch',
+ # Don't warn about inline new and delete
+ '-Wno-inline-new-delete',
+ ],
+ 'target_conditions': [
+ ['cobalt_code==1', {
+ 'cflags': [
+ '-Wall',
+ '-Wextra',
+ '-Wunreachable-code',
+ '<@(common_clang_flags)',
+ ],
+ },{
+ 'cflags': [
+ '<@(common_clang_flags)',
+ # 'this' pointer cannot be NULL...pointer may be assumed
+ # to always convert to true.
+ '-Wno-undefined-bool-conversion',
+ # Skia doesn't use overrides.
+ '-Wno-inconsistent-missing-override',
+ # Do not warn about unused function params.
+ '-Wno-unused-parameter',
+ # Do not warn for implicit type conversions that may change a value.
+ '-Wno-conversion',
+ # shifting a negative signed value is undefined
+ '-Wno-shift-negative-value',
+ # Width of bit-field exceeds width of its type- value will be truncated
+ '-Wno-bitfield-width',
+ ],
+ }],
+ ['use_asan==1', {
+ 'cflags': [
+ '-fsanitize=address',
+ '-fno-omit-frame-pointer',
+ ],
+ 'ldflags': [
+ '-fsanitize=address',
+ # Force linking of the helpers in sanitizer_options.cc
+ '-Wl,-u_sanitizer_options_link_helper',
+ ],
+ 'defines': [
+ 'ADDRESS_SANITIZER',
+ ],
+ }],
+ ['use_tsan==1', {
+ 'cflags': [
+ '-fsanitize=thread',
+ '-fno-omit-frame-pointer',
+ ],
+ 'ldflags': [
+ '-fsanitize=thread',
+ ],
+ 'defines': [
+ 'THREAD_SANITIZER',
+ ],
+ }],
+ ],
+ }, # end of target_defaults
+}
diff --git a/src/starboard/linux/x64x11/cpp11/configuration_public.h b/src/starboard/linux/x64x11/cpp11/configuration_public.h
new file mode 100644
index 0000000..1c14924
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/configuration_public.h
@@ -0,0 +1,27 @@
+// 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.
+
+// The Starboard configuration for Desktop X86 Linux. Other devices will have
+// specific Starboard implementations, even if they ultimately are running some
+// version of Linux.
+
+// Other source files should never include this header directly, but should
+// include the generic "starboard/configuration.h" instead.
+
+#ifndef STARBOARD_LINUX_X64X11_CPP11_CONFIGURATION_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_CPP11_CONFIGURATION_PUBLIC_H_
+
+#include "starboard/linux/x64x11/configuration_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_CPP11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/cpp11/gyp_configuration.gypi b/src/starboard/linux/x64x11/cpp11/gyp_configuration.gypi
new file mode 100644
index 0000000..fcc5747
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/gyp_configuration.gypi
@@ -0,0 +1,45 @@
+# 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.
+
+{
+ 'variables': {
+ # This should have a default value in cobalt/base.gypi. See the comment
+ # there for acceptable values for this variable.
+ 'javascript_engine': 'mozjs-45',
+ 'cobalt_enable_jit': 1,
+ },
+ 'target_defaults': {
+ 'default_configuration': 'linux-x64x11-cpp11_debug',
+ 'configurations': {
+ 'linux-x64x11-cpp11_debug': {
+ 'inherit_from': ['debug_base'],
+ },
+ 'linux-x64x11-cpp11_devel': {
+ 'inherit_from': ['devel_base'],
+ },
+ 'linux-x64x11-cpp11_qa': {
+ 'inherit_from': ['qa_base'],
+ },
+ 'linux-x64x11-cpp11_gold': {
+ 'inherit_from': ['gold_base'],
+ },
+ }, # end of configurations
+ },
+
+ 'includes': [
+ 'compiler_flags.gypi',
+ '../libraries.gypi',
+ '../../shared/gyp_configuration.gypi',
+ ],
+}
diff --git a/src/starboard/linux/x64x11/cpp11/gyp_configuration.py b/src/starboard/linux/x64x11/cpp11/gyp_configuration.py
new file mode 100644
index 0000000..c5dab76
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/gyp_configuration.py
@@ -0,0 +1,34 @@
+# 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.
+"""Starboard Linux X64 X11 platform configuration for gyp_cobalt."""
+
+import logging
+import os
+import sys
+
+# Import the shared Linux platform configuration.
+sys.path.append(
+ os.path.realpath(
+ os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
+ 'shared')))
+# pylint: disable=import-self,g-import-not-at-top
+import gyp_configuration
+
+
+def CreatePlatformConfig():
+ try:
+ return gyp_configuration.PlatformConfig('linux-x64x11-cpp11')
+ except RuntimeError as e:
+ logging.critical(e)
+ return None
diff --git a/src/starboard/linux/x64x11/cpp11/starboard_platform.gyp b/src/starboard/linux/x64x11/cpp11/starboard_platform.gyp
new file mode 100644
index 0000000..42b0bf5
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/starboard_platform.gyp
@@ -0,0 +1,33 @@
+# 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.
+{
+ 'includes': [
+ '../starboard_platform.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'starboard_platform',
+ 'type': 'static_library',
+ 'sources': ['<@(starboard_platform_sources)'],
+ 'defines': [
+ # This must be defined when building Starboard, and must not when
+ # building Starboard client code.
+ 'STARBOARD_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '<@(starboard_platform_dependencies)',
+ ],
+ },
+ ],
+}
diff --git a/src/starboard/linux/x64x11/cpp11/thread_types_public.h b/src/starboard/linux/x64x11/cpp11/thread_types_public.h
new file mode 100644
index 0000000..9676b99
--- /dev/null
+++ b/src/starboard/linux/x64x11/cpp11/thread_types_public.h
@@ -0,0 +1,22 @@
+/*
+ * 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 STARBOARD_LINUX_X64X11_CPP11_THREAD_TYPES_PUBLIC_H_
+#define STARBOARD_LINUX_X64X11_CPP11_THREAD_TYPES_PUBLIC_H_
+
+#include "starboard/linux/shared/thread_types_public.h"
+
+#endif // STARBOARD_LINUX_X64X11_CPP11_THREAD_TYPES_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/gcc/gyp_configuration.gypi b/src/starboard/linux/x64x11/gcc/gyp_configuration.gypi
index 2ede477..0df5d5f 100644
--- a/src/starboard/linux/x64x11/gcc/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/gcc/gyp_configuration.gypi
@@ -13,6 +13,12 @@
# limitations under the License.
{
+ 'variables': {
+ # This should have a default value in cobalt/base.gypi. See the comment
+ # there for acceptable values for this variable.
+ 'javascript_engine': 'mozjs',
+ 'cobalt_enable_jit': 1,
+ },
'includes': [
'../libraries.gypi',
'../../shared/gyp_configuration.gypi',
diff --git a/src/starboard/linux/x64x11/gyp_configuration.gypi b/src/starboard/linux/x64x11/gyp_configuration.gypi
index 78216dd..1203c9d 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.gypi
+++ b/src/starboard/linux/x64x11/gyp_configuration.gypi
@@ -13,6 +13,12 @@
# limitations under the License.
{
+ 'variables': {
+ # This should have a default value in cobalt/base.gypi. See the comment
+ # there for acceptable values for this variable.
+ 'javascript_engine': 'mozjs',
+ 'cobalt_enable_jit': 1,
+ },
'target_defaults': {
'default_configuration': 'linux-x64x11_debug',
'configurations': {
diff --git a/src/starboard/media.h b/src/starboard/media.h
index 3e82d17..1b11b8f 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -289,7 +289,43 @@
// with standard digital imaging. See the Consumer Electronics
// Association press release:
// https://www.cta.tech/News/Press-Releases/2015/August/CEA-Defines-%E2%80%98HDR-Compatible%E2%80%99-Displays.aspx
-typedef struct SbMediaHdrMetadataColorSpace {
+typedef struct SbMediaColorMetadata {
+ // Number of decoded bits per channel. A value of 0 indicates that
+ // the BitsPerChannel is unspecified.
+ unsigned int bits_per_channel;
+
+ // The amount of pixels to remove in the Cr and Cb channels for
+ // every pixel not removed horizontally. Example: For video with
+ // 4:2:0 chroma subsampling, the |chroma_subsampling_horizontal| should be set
+ // to 1.
+ unsigned int chroma_subsampling_horizontal;
+
+ // The amount of pixels to remove in the Cr and Cb channels for
+ // every pixel not removed vertically. Example: For video with
+ // 4:2:0 chroma subsampling, the |chroma_subsampling_vertical| should be set
+ // to 1.
+ unsigned int chroma_subsampling_vertical;
+
+ // The amount of pixels to remove in the Cb channel for every pixel
+ // not removed horizontally. This is additive with
+ // ChromaSubsamplingHorz. Example: For video with 4:2:1 chroma
+ // subsampling, the |chroma_subsampling_horizontal| should be set to 1 and
+ // |cb_subsampling_horizontal| should be set to 1.
+ unsigned int cb_subsampling_horizontal;
+
+ // The amount of pixels to remove in the Cb channel for every pixel
+ // not removed vertically. This is additive with
+ // |chroma_subsampling_vertical|.
+ unsigned int cb_subsampling_vertical;
+
+ // How chroma is subsampled horizontally. (0: Unspecified, 1: Left
+ // Collocated, 2: Half)
+ unsigned int chroma_siting_horizontal;
+
+ // How chroma is subsampled vertically. (0: Unspecified, 1: Top
+ // Collocated, 2: Half)
+ unsigned int chroma_siting_vertical;
+
// [HDR Metadata field] SMPTE 2086 mastering data.
SbMediaMasteringMetadata mastering_metadata;
@@ -347,7 +383,7 @@
// submatrix of the 4 x 4 transform matrix. The 4th row is
// completed as (0, 0, 0, 1).
float custom_primary_matrix[12];
-} SbMediaHdrMetadataColorSpace;
+} SbMediaColorMetadata;
#endif // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
// The set of information required by the decoder or player for each video
@@ -368,13 +404,15 @@
int frame_height;
#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
- // HDR metadata common for HDR10 and WeB/VP9-based HDR formats as
- // well as the Color Space: MatrixCoefficients, Range,
- // TansferCharacteristics, and Primaries described here:
- // https://matroska.org/technical/specs/index.html
- // This will only be specified on frames where the HDR metadata and color
- // space might have changed (e.g. keyframes).
- SbMediaHdrMetadataColorSpace* hdr_metadata_color_space;
+ // HDR metadata common for HDR10 and WebM/VP9-based HDR formats as
+ // well as the Color Space, and Color elements: MatrixCoefficients,
+ // BitsPerChannel, ChromaSubsamplingHorz, ChromaSubsamplingVert,
+ // CbSubsamplingHorz, CbSubsamplingVert, ChromaSitingHorz,
+ // ChromaSitingVert, Range, TransferCharacteristics, and Primaries
+ // described here: https://matroska.org/technical/specs/index.html .
+ // This will only be specified on frames where the HDR metadata and
+ // color / color space might have changed (e.g. keyframes).
+ SbMediaColorMetadata* color_metadata;
#endif
} SbMediaVideoSampleInfo;
diff --git a/src/starboard/nplb/audio_sink_create_test.cc b/src/starboard/nplb/audio_sink_create_test.cc
index 22a6d7e..d4702cf 100644
--- a/src/starboard/nplb/audio_sink_create_test.cc
+++ b/src/starboard/nplb/audio_sink_create_test.cc
@@ -1,229 +1,229 @@
-// 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 "starboard/audio_sink.h"
-
-#include <vector>
-
-#include "starboard/nplb/audio_sink_helpers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-
-namespace {
-
-void UpdateSourceStatusFuncStub(int* frames_in_buffer,
- int* offset_in_frames,
- bool* is_playing,
- bool* is_eos_reached,
- void* context) {}
-void ConsumeFramesFuncStub(int frames_consumed, void* context) {}
-
-} // namespace
-
-TEST(SbAudioSinkCreateTest, SunnyDay) {
- ASSERT_GE(SbAudioSinkGetMaxChannels(), 1);
-
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
-
- SbAudioSink audio_sink = SbAudioSinkCreate(
- frame_buffers.channels(),
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
- UpdateSourceStatusFuncStub, ConsumeFramesFuncStub,
- reinterpret_cast<void*>(1));
- EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));
- SbAudioSinkDestroy(audio_sink);
-}
-
-TEST(SbAudioSinkCreateTest, SunnyDayAllCombinations) {
- std::vector<SbMediaAudioSampleType> sample_types;
- if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) {
- sample_types.push_back(kSbMediaAudioSampleTypeInt16);
- }
- if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
- sample_types.push_back(kSbMediaAudioSampleTypeFloat32);
- }
-
- std::vector<SbMediaAudioFrameStorageType> storage_types;
- if (SbAudioSinkIsAudioFrameStorageTypeSupported(
- kSbMediaAudioFrameStorageTypeInterleaved)) {
- storage_types.push_back(kSbMediaAudioFrameStorageTypeInterleaved);
- }
- if (SbAudioSinkIsAudioFrameStorageTypeSupported(
- kSbMediaAudioFrameStorageTypePlanar)) {
- storage_types.push_back(kSbMediaAudioFrameStorageTypePlanar);
- }
-
- for (size_t sample_type = 0; sample_type < sample_types.size();
- ++sample_type) {
- for (size_t storage_type = 0; storage_type < storage_types.size();
- ++storage_type) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
- sample_types[sample_type],
- storage_types[storage_type]);
- // It should at least support stereo and the claimed max channels.
- SbAudioSink audio_sink = SbAudioSinkCreate(
- frame_buffers.channels(),
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
- UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
- EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));
- SbAudioSinkDestroy(audio_sink);
-
- if (SbAudioSinkGetMaxChannels() > 2) {
- AudioSinkTestFrameBuffers frame_buffers(2, sample_types[sample_type],
- storage_types[storage_type]);
- audio_sink = SbAudioSinkCreate(
- frame_buffers.channels(),
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
- UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
- EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));
- SbAudioSinkDestroy(audio_sink);
- }
- }
- }
-}
-
-TEST(SbAudioSinkCreateTest, RainyDayInvalidChannels) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
- SbAudioSink audio_sink = SbAudioSinkCreate(
- 0, // |channels| is 0
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
- UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
- EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
-}
-
-TEST(SbAudioSinkCreateTest, RainyDayInvalidFrequency) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
- SbAudioSink audio_sink = SbAudioSinkCreate(
- frame_buffers.channels(), 0, // |sampling_frequency_hz| is 0
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
- UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
- EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
-
- const int kOddFrequency = 12345;
- if (SbAudioSinkGetNearestSupportedSampleFrequency(kOddFrequency) !=
- kOddFrequency) {
- audio_sink = SbAudioSinkCreate(
- frame_buffers.channels(),
- kOddFrequency, // |sampling_frequency_hz| is unsupported
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
- UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
- EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
- }
-}
-
-TEST(SbAudioSinkCreateTest, RainyDayInvalidSampleType) {
- SbMediaAudioSampleType invalid_sample_type;
- if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) {
- if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
- return;
- }
- invalid_sample_type = kSbMediaAudioSampleTypeFloat32;
- } else {
- invalid_sample_type = kSbMediaAudioSampleTypeInt16;
- }
-
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
- invalid_sample_type);
- SbAudioSink audio_sink = SbAudioSinkCreate(
- SbAudioSinkGetMaxChannels(),
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- invalid_sample_type, // |sample_type| is invalid
- frame_buffers.storage_type(), frame_buffers.frame_buffers(),
- frame_buffers.frames_per_channel(), UpdateSourceStatusFuncStub,
- ConsumeFramesFuncStub, NULL);
- EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
-}
-
-TEST(SbAudioSinkCreateTest, RainyDayInvalidFrameStorageType) {
- SbMediaAudioFrameStorageType invalid_storage_type;
- if (SbAudioSinkIsAudioFrameStorageTypeSupported(
- kSbMediaAudioFrameStorageTypeInterleaved)) {
- if (SbAudioSinkIsAudioFrameStorageTypeSupported(
- kSbMediaAudioFrameStorageTypePlanar)) {
- return;
- }
- invalid_storage_type = kSbMediaAudioFrameStorageTypePlanar;
- } else {
- invalid_storage_type = kSbMediaAudioFrameStorageTypeInterleaved;
- }
-
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
- invalid_storage_type);
- SbAudioSink audio_sink = SbAudioSinkCreate(
- SbAudioSinkGetMaxChannels(),
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- frame_buffers.sample_type(),
- invalid_storage_type, // |storage_type| is invalid
- frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
- UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
- EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
-}
-
-TEST(SbAudioSinkCreateTest, RainyDayInvalidFrameBuffers) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
- SbAudioSink audio_sink = SbAudioSinkCreate(
- frame_buffers.channels(),
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- NULL, // |frame_buffers| is NULL
- frame_buffers.frames_per_channel(), UpdateSourceStatusFuncStub,
- ConsumeFramesFuncStub, NULL);
- EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
-
- audio_sink = SbAudioSinkCreate(
- frame_buffers.channels(),
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- frame_buffers.frame_buffers(),
- 0, // |frames_per_channel| is 0
- UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
- EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
-}
-
-TEST(SbAudioSinkCreateTest, RainyDayInvalidCallback) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
- SbAudioSink audio_sink = SbAudioSinkCreate(
- frame_buffers.channels(),
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
- NULL, // |update_source_status_func| is NULL
- ConsumeFramesFuncStub, NULL);
- EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
-
- audio_sink = SbAudioSinkCreate(
- frame_buffers.channels(),
- SbAudioSinkGetNearestSupportedSampleFrequency(44100),
- frame_buffers.sample_type(), frame_buffers.storage_type(),
- frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
- UpdateSourceStatusFuncStub,
- NULL, // |consume_frames_func| is NULL
- NULL);
- EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
-}
-
-} // namespace nplb
-} // namespace starboard
+// 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 "starboard/audio_sink.h"
+
+#include <vector>
+
+#include "starboard/nplb/audio_sink_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+namespace {
+
+void UpdateSourceStatusFuncStub(int* frames_in_buffer,
+ int* offset_in_frames,
+ bool* is_playing,
+ bool* is_eos_reached,
+ void* context) {}
+void ConsumeFramesFuncStub(int frames_consumed, void* context) {}
+
+} // namespace
+
+TEST(SbAudioSinkCreateTest, SunnyDay) {
+ ASSERT_GE(SbAudioSinkGetMaxChannels(), 1);
+
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+
+ SbAudioSink audio_sink = SbAudioSinkCreate(
+ frame_buffers.channels(),
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+ UpdateSourceStatusFuncStub, ConsumeFramesFuncStub,
+ reinterpret_cast<void*>(1));
+ EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));
+ SbAudioSinkDestroy(audio_sink);
+}
+
+TEST(SbAudioSinkCreateTest, SunnyDayAllCombinations) {
+ std::vector<SbMediaAudioSampleType> sample_types;
+ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) {
+ sample_types.push_back(kSbMediaAudioSampleTypeInt16);
+ }
+ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+ sample_types.push_back(kSbMediaAudioSampleTypeFloat32);
+ }
+
+ std::vector<SbMediaAudioFrameStorageType> storage_types;
+ if (SbAudioSinkIsAudioFrameStorageTypeSupported(
+ kSbMediaAudioFrameStorageTypeInterleaved)) {
+ storage_types.push_back(kSbMediaAudioFrameStorageTypeInterleaved);
+ }
+ if (SbAudioSinkIsAudioFrameStorageTypeSupported(
+ kSbMediaAudioFrameStorageTypePlanar)) {
+ storage_types.push_back(kSbMediaAudioFrameStorageTypePlanar);
+ }
+
+ for (size_t sample_type = 0; sample_type < sample_types.size();
+ ++sample_type) {
+ for (size_t storage_type = 0; storage_type < storage_types.size();
+ ++storage_type) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
+ sample_types[sample_type],
+ storage_types[storage_type]);
+ // It should at least support stereo and the claimed max channels.
+ SbAudioSink audio_sink = SbAudioSinkCreate(
+ frame_buffers.channels(),
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+ UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+ EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));
+ SbAudioSinkDestroy(audio_sink);
+
+ if (SbAudioSinkGetMaxChannels() > 2) {
+ AudioSinkTestFrameBuffers frame_buffers(2, sample_types[sample_type],
+ storage_types[storage_type]);
+ audio_sink = SbAudioSinkCreate(
+ frame_buffers.channels(),
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+ UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+ EXPECT_TRUE(SbAudioSinkIsValid(audio_sink));
+ SbAudioSinkDestroy(audio_sink);
+ }
+ }
+ }
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidChannels) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+ SbAudioSink audio_sink = SbAudioSinkCreate(
+ 0, // |channels| is 0
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+ UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+ EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidFrequency) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+ SbAudioSink audio_sink = SbAudioSinkCreate(
+ frame_buffers.channels(), 0, // |sampling_frequency_hz| is 0
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+ UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+ EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+
+ const int kOddFrequency = 12345;
+ if (SbAudioSinkGetNearestSupportedSampleFrequency(kOddFrequency) !=
+ kOddFrequency) {
+ audio_sink = SbAudioSinkCreate(
+ frame_buffers.channels(),
+ kOddFrequency, // |sampling_frequency_hz| is unsupported
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+ UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+ EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+ }
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidSampleType) {
+ SbMediaAudioSampleType invalid_sample_type;
+ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16)) {
+ if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
+ return;
+ }
+ invalid_sample_type = kSbMediaAudioSampleTypeFloat32;
+ } else {
+ invalid_sample_type = kSbMediaAudioSampleTypeInt16;
+ }
+
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
+ invalid_sample_type);
+ SbAudioSink audio_sink = SbAudioSinkCreate(
+ SbAudioSinkGetMaxChannels(),
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ invalid_sample_type, // |sample_type| is invalid
+ frame_buffers.storage_type(), frame_buffers.frame_buffers(),
+ frame_buffers.frames_per_channel(), UpdateSourceStatusFuncStub,
+ ConsumeFramesFuncStub, NULL);
+ EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidFrameStorageType) {
+ SbMediaAudioFrameStorageType invalid_storage_type;
+ if (SbAudioSinkIsAudioFrameStorageTypeSupported(
+ kSbMediaAudioFrameStorageTypeInterleaved)) {
+ if (SbAudioSinkIsAudioFrameStorageTypeSupported(
+ kSbMediaAudioFrameStorageTypePlanar)) {
+ return;
+ }
+ invalid_storage_type = kSbMediaAudioFrameStorageTypePlanar;
+ } else {
+ invalid_storage_type = kSbMediaAudioFrameStorageTypeInterleaved;
+ }
+
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels(),
+ invalid_storage_type);
+ SbAudioSink audio_sink = SbAudioSinkCreate(
+ SbAudioSinkGetMaxChannels(),
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ frame_buffers.sample_type(),
+ invalid_storage_type, // |storage_type| is invalid
+ frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+ UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+ EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidFrameBuffers) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+ SbAudioSink audio_sink = SbAudioSinkCreate(
+ frame_buffers.channels(),
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ NULL, // |frame_buffers| is NULL
+ frame_buffers.frames_per_channel(), UpdateSourceStatusFuncStub,
+ ConsumeFramesFuncStub, NULL);
+ EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+
+ audio_sink = SbAudioSinkCreate(
+ frame_buffers.channels(),
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ frame_buffers.frame_buffers(),
+ 0, // |frames_per_channel| is 0
+ UpdateSourceStatusFuncStub, ConsumeFramesFuncStub, NULL);
+ EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+TEST(SbAudioSinkCreateTest, RainyDayInvalidCallback) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+ SbAudioSink audio_sink = SbAudioSinkCreate(
+ frame_buffers.channels(),
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+ NULL, // |update_source_status_func| is NULL
+ ConsumeFramesFuncStub, NULL);
+ EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+
+ audio_sink = SbAudioSinkCreate(
+ frame_buffers.channels(),
+ SbAudioSinkGetNearestSupportedSampleFrequency(44100),
+ frame_buffers.sample_type(), frame_buffers.storage_type(),
+ frame_buffers.frame_buffers(), frame_buffers.frames_per_channel(),
+ UpdateSourceStatusFuncStub,
+ NULL, // |consume_frames_func| is NULL
+ NULL);
+ EXPECT_FALSE(SbAudioSinkIsValid(audio_sink));
+}
+
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_destroy_test.cc b/src/starboard/nplb/audio_sink_destroy_test.cc
index 3f93a3e..cc461ed 100644
--- a/src/starboard/nplb/audio_sink_destroy_test.cc
+++ b/src/starboard/nplb/audio_sink_destroy_test.cc
@@ -1,26 +1,26 @@
-// 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 "starboard/audio_sink.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-
-TEST(SbAudioSinkCreateTest, DestroyInvalidAudioSink) {
- SbAudioSinkDestroy(kSbAudioSinkInvalid);
-}
-
-} // namespace nplb
-} // namespace starboard
+// 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 "starboard/audio_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkCreateTest, DestroyInvalidAudioSink) {
+ SbAudioSinkDestroy(kSbAudioSinkInvalid);
+}
+
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_get_max_channels_test.cc b/src/starboard/nplb/audio_sink_get_max_channels_test.cc
index a61af9a..ea88467 100644
--- a/src/starboard/nplb/audio_sink_get_max_channels_test.cc
+++ b/src/starboard/nplb/audio_sink_get_max_channels_test.cc
@@ -1,29 +1,29 @@
-// 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 "starboard/audio_sink.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-
-TEST(SbAudioSinkGetMaxChannelsTest, SunnyDay) {
- int max_channels = SbAudioSinkGetMaxChannels();
- EXPECT_GT(max_channels, 0);
- // See if it reports anything ridiculous.
- EXPECT_LT(max_channels, 99);
-}
-
-} // namespace nplb
-} // namespace starboard
+// 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 "starboard/audio_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkGetMaxChannelsTest, SunnyDay) {
+ int max_channels = SbAudioSinkGetMaxChannels();
+ EXPECT_GT(max_channels, 0);
+ // See if it reports anything ridiculous.
+ EXPECT_LT(max_channels, 99);
+}
+
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_get_nearest_supported_sample_frequency_test.cc b/src/starboard/nplb/audio_sink_get_nearest_supported_sample_frequency_test.cc
index 0fd816f..5f8d0d8 100644
--- a/src/starboard/nplb/audio_sink_get_nearest_supported_sample_frequency_test.cc
+++ b/src/starboard/nplb/audio_sink_get_nearest_supported_sample_frequency_test.cc
@@ -1,60 +1,60 @@
-// 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 "starboard/audio_sink.h"
-
-#include <limits>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-
-namespace {
-const int kFrequencyCD = 44100;
-} // namespace
-
-TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, SunnyDay) {
- int nearest_frequency =
- SbAudioSinkGetNearestSupportedSampleFrequency(kFrequencyCD);
- EXPECT_GT(nearest_frequency, 0);
- EXPECT_EQ(nearest_frequency,
- SbAudioSinkGetNearestSupportedSampleFrequency(nearest_frequency));
-}
-
-TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, ZeroFrequency) {
- int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(0);
- EXPECT_GT(nearest_frequency, 0);
-}
-
-TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, SnapUp) {
- int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(1);
- EXPECT_GT(nearest_frequency, 0);
- if (nearest_frequency > 1) {
- EXPECT_EQ(nearest_frequency,
- SbAudioSinkGetNearestSupportedSampleFrequency(2));
- }
-}
-
-TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, Snap) {
- int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(
- std::numeric_limits<int>::max());
- if (nearest_frequency < std::numeric_limits<int>::max()) {
- EXPECT_EQ(nearest_frequency, SbAudioSinkGetNearestSupportedSampleFrequency(
- std::numeric_limits<int>::max() - 1));
- }
-}
-
-} // namespace nplb
-} // namespace starboard
+// 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 "starboard/audio_sink.h"
+
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+namespace {
+const int kFrequencyCD = 44100;
+} // namespace
+
+TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, SunnyDay) {
+ int nearest_frequency =
+ SbAudioSinkGetNearestSupportedSampleFrequency(kFrequencyCD);
+ EXPECT_GT(nearest_frequency, 0);
+ EXPECT_EQ(nearest_frequency,
+ SbAudioSinkGetNearestSupportedSampleFrequency(nearest_frequency));
+}
+
+TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, ZeroFrequency) {
+ int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(0);
+ EXPECT_GT(nearest_frequency, 0);
+}
+
+TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, SnapUp) {
+ int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(1);
+ EXPECT_GT(nearest_frequency, 0);
+ if (nearest_frequency > 1) {
+ EXPECT_EQ(nearest_frequency,
+ SbAudioSinkGetNearestSupportedSampleFrequency(2));
+ }
+}
+
+TEST(SbAudioSinkGetNearestSupportedSampleFrequencyTest, Snap) {
+ int nearest_frequency = SbAudioSinkGetNearestSupportedSampleFrequency(
+ std::numeric_limits<int>::max());
+ if (nearest_frequency < std::numeric_limits<int>::max()) {
+ EXPECT_EQ(nearest_frequency, SbAudioSinkGetNearestSupportedSampleFrequency(
+ std::numeric_limits<int>::max() - 1));
+ }
+}
+
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_is_audio_frame_storage_type_supported_test.cc b/src/starboard/nplb/audio_sink_is_audio_frame_storage_type_supported_test.cc
index 372f970..ce49962 100644
--- a/src/starboard/nplb/audio_sink_is_audio_frame_storage_type_supported_test.cc
+++ b/src/starboard/nplb/audio_sink_is_audio_frame_storage_type_supported_test.cc
@@ -1,31 +1,31 @@
-// 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 "starboard/audio_sink.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-
-TEST(SbAudioSinkIsAudioFrameStorageTypeSupportedTest, SunnyDay) {
- bool interleaved_supported = SbAudioSinkIsAudioFrameStorageTypeSupported(
- kSbMediaAudioFrameStorageTypeInterleaved);
- bool planar_supported = SbAudioSinkIsAudioFrameStorageTypeSupported(
- kSbMediaAudioFrameStorageTypePlanar);
- // A platform must support at least one of the frame storage types.
- EXPECT_TRUE(interleaved_supported || planar_supported);
-}
-
-} // namespace nplb
-} // namespace starboard
+// 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 "starboard/audio_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkIsAudioFrameStorageTypeSupportedTest, SunnyDay) {
+ bool interleaved_supported = SbAudioSinkIsAudioFrameStorageTypeSupported(
+ kSbMediaAudioFrameStorageTypeInterleaved);
+ bool planar_supported = SbAudioSinkIsAudioFrameStorageTypeSupported(
+ kSbMediaAudioFrameStorageTypePlanar);
+ // A platform must support at least one of the frame storage types.
+ EXPECT_TRUE(interleaved_supported || planar_supported);
+}
+
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc b/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc
index 2498eec..b1adfbf 100644
--- a/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc
+++ b/src/starboard/nplb/audio_sink_is_audio_sample_type_supported_test.cc
@@ -1,31 +1,31 @@
-// 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 "starboard/audio_sink.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-
-TEST(SbAudioSinkIsAudioSampleTypeSupportedTest, SunnyDay) {
- bool int16_supported =
- SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16);
- bool float32_supported =
- SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32);
- // A platform must support at least one of the sample types.
- EXPECT_TRUE(int16_supported || float32_supported);
-}
-
-} // namespace nplb
-} // namespace starboard
+// 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 "starboard/audio_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkIsAudioSampleTypeSupportedTest, SunnyDay) {
+ bool int16_supported =
+ SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeInt16);
+ bool float32_supported =
+ SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32);
+ // A platform must support at least one of the sample types.
+ EXPECT_TRUE(int16_supported || float32_supported);
+}
+
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/audio_sink_test.cc b/src/starboard/nplb/audio_sink_test.cc
index a40cf5e..a48011c 100644
--- a/src/starboard/nplb/audio_sink_test.cc
+++ b/src/starboard/nplb/audio_sink_test.cc
@@ -1,95 +1,95 @@
-// 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 "starboard/audio_sink.h"
-
-#include <algorithm>
-
-#include "starboard/nplb/audio_sink_helpers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-
-TEST(SbAudioSinkTest, UpdateStatusCalled) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
- AudioSinkTestEnvironment environment(frame_buffers);
-
- EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
- EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
-}
-
-TEST(SbAudioSinkTest, SomeFramesConsumed) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
- AudioSinkTestEnvironment environment(frame_buffers);
-
- environment.AppendFrame(1);
- EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
-}
-
-TEST(SbAudioSinkTest, AllFramesConsumed) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
- AudioSinkTestEnvironment environment(frame_buffers);
-
- environment.AppendFrame(1024);
- EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
-}
-
-TEST(SbAudioSinkTest, MultipleAppendAndConsume) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
- AudioSinkTestEnvironment environment(frame_buffers);
-
- int frames_to_append = frame_buffers.frames_per_channel();
-
- environment.AppendFrame(frames_to_append / 2);
- EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
- environment.AppendFrame(frames_to_append / 2);
- EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
-}
-
-TEST(SbAudioSinkTest, Pause) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
- AudioSinkTestEnvironment environment(frame_buffers);
- environment.SetIsPlaying(false);
-
- int frames_to_append = frame_buffers.frames_per_channel();
-
- environment.AppendFrame(frames_to_append / 2);
- int free_space = environment.GetFrameBufferFreeSpaceAmount();
- EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
- EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
- EXPECT_EQ(free_space, environment.GetFrameBufferFreeSpaceAmount());
- environment.SetIsPlaying(true);
- EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
-}
-
-TEST(SbAudioSinkTest, ContinuousAppend) {
- AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
-
- AudioSinkTestEnvironment environment(frame_buffers);
- int sample_rate = environment.sample_rate();
- // We are trying to send 1/4s worth of audio samples.
- int frames_to_append = sample_rate / 4;
-
- while (frames_to_append > 0) {
- int free_space = environment.GetFrameBufferFreeSpaceAmount();
- environment.AppendFrame(std::min(free_space, frames_to_append));
- frames_to_append -= std::min(free_space, frames_to_append);
- ASSERT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
- }
- EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
-}
-
-} // namespace nplb
-} // namespace starboard
+// 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 "starboard/audio_sink.h"
+
+#include <algorithm>
+
+#include "starboard/nplb/audio_sink_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbAudioSinkTest, UpdateStatusCalled) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+ AudioSinkTestEnvironment environment(frame_buffers);
+
+ EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
+ EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
+}
+
+TEST(SbAudioSinkTest, SomeFramesConsumed) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+ AudioSinkTestEnvironment environment(frame_buffers);
+
+ environment.AppendFrame(1);
+ EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
+}
+
+TEST(SbAudioSinkTest, AllFramesConsumed) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+ AudioSinkTestEnvironment environment(frame_buffers);
+
+ environment.AppendFrame(1024);
+ EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
+}
+
+TEST(SbAudioSinkTest, MultipleAppendAndConsume) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+ AudioSinkTestEnvironment environment(frame_buffers);
+
+ int frames_to_append = frame_buffers.frames_per_channel();
+
+ environment.AppendFrame(frames_to_append / 2);
+ EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
+ environment.AppendFrame(frames_to_append / 2);
+ EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
+}
+
+TEST(SbAudioSinkTest, Pause) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+ AudioSinkTestEnvironment environment(frame_buffers);
+ environment.SetIsPlaying(false);
+
+ int frames_to_append = frame_buffers.frames_per_channel();
+
+ environment.AppendFrame(frames_to_append / 2);
+ int free_space = environment.GetFrameBufferFreeSpaceAmount();
+ EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
+ EXPECT_TRUE(environment.WaitUntilUpdateStatusCalled());
+ EXPECT_EQ(free_space, environment.GetFrameBufferFreeSpaceAmount());
+ environment.SetIsPlaying(true);
+ EXPECT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
+}
+
+TEST(SbAudioSinkTest, ContinuousAppend) {
+ AudioSinkTestFrameBuffers frame_buffers(SbAudioSinkGetMaxChannels());
+
+ AudioSinkTestEnvironment environment(frame_buffers);
+ int sample_rate = environment.sample_rate();
+ // We are trying to send 1/4s worth of audio samples.
+ int frames_to_append = sample_rate / 4;
+
+ while (frames_to_append > 0) {
+ int free_space = environment.GetFrameBufferFreeSpaceAmount();
+ environment.AppendFrame(std::min(free_space, frames_to_append));
+ frames_to_append -= std::min(free_space, frames_to_append);
+ ASSERT_TRUE(environment.WaitUntilSomeFramesAreConsumed());
+ }
+ EXPECT_TRUE(environment.WaitUntilAllFramesAreConsumed());
+}
+
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/memory_align_to_page_size_test.cc b/src/starboard/nplb/memory_align_to_page_size_test.cc
index 78b3be8..8412399 100644
--- a/src/starboard/nplb/memory_align_to_page_size_test.cc
+++ b/src/starboard/nplb/memory_align_to_page_size_test.cc
@@ -1,34 +1,34 @@
-// 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 "starboard/memory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-namespace {
-
-TEST(SbMemoryAlignToPageSizeTest, AlignsVariousSizes) {
- EXPECT_EQ(0, SbMemoryAlignToPageSize(0));
- EXPECT_EQ(SB_MEMORY_PAGE_SIZE, SbMemoryAlignToPageSize(1));
- EXPECT_EQ(SB_MEMORY_PAGE_SIZE,
- SbMemoryAlignToPageSize(SB_MEMORY_PAGE_SIZE - 1));
- EXPECT_EQ(SB_MEMORY_PAGE_SIZE, SbMemoryAlignToPageSize(SB_MEMORY_PAGE_SIZE));
- EXPECT_EQ(100 * SB_MEMORY_PAGE_SIZE,
- SbMemoryAlignToPageSize(100 * SB_MEMORY_PAGE_SIZE));
-}
-
-} // namespace
-} // namespace nplb
-} // namespace starboard
+// 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 "starboard/memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+TEST(SbMemoryAlignToPageSizeTest, AlignsVariousSizes) {
+ EXPECT_EQ(0, SbMemoryAlignToPageSize(0));
+ EXPECT_EQ(SB_MEMORY_PAGE_SIZE, SbMemoryAlignToPageSize(1));
+ EXPECT_EQ(SB_MEMORY_PAGE_SIZE,
+ SbMemoryAlignToPageSize(SB_MEMORY_PAGE_SIZE - 1));
+ EXPECT_EQ(SB_MEMORY_PAGE_SIZE, SbMemoryAlignToPageSize(SB_MEMORY_PAGE_SIZE));
+ EXPECT_EQ(100 * SB_MEMORY_PAGE_SIZE,
+ SbMemoryAlignToPageSize(100 * SB_MEMORY_PAGE_SIZE));
+}
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/memory_map_test.cc b/src/starboard/nplb/memory_map_test.cc
index a17bbdc..05ad242 100644
--- a/src/starboard/nplb/memory_map_test.cc
+++ b/src/starboard/nplb/memory_map_test.cc
@@ -101,6 +101,9 @@
static int sum(int x, int y) {
return x + y;
}
+static int sum2(int x, int y) {
+ return x + y + x;
+}
// This test is known to run on x64, ARM, and MIPS32 with MIPS32 and MIPS16
// instructions.
@@ -109,17 +112,31 @@
kSize, kSbMemoryMapProtectReadWrite | kSbMemoryMapProtectExec, "test");
ASSERT_NE(kFailed, memory);
- // There's no reliable way to determine the size of the 'sum' function. We can
+ // There's no reliable way to determine the size of the 'sum' function. If we
+ // assume the function is at most a certain size, then we might try to read
+ // beyond mapped memory when copying it to the destination address. We can
// get a reasonable upper bound by assuming that the function's implementation
// does not cross a page boundary, and copy the memory from the beginning of
// the function to the end of the page that it is mapped to.
- // Note that this will fail if the function implementation crosses a page
- // boundary, which seems pretty unlikely, especially given the size of the
- // function.
+ //
+ // However, since it's possible the function may cross the page boundary, we
+ // define two functions and use the one closest to the start of a page. There
+ // is no guarantee that the linker will place these definitions sequentially
+ // (although it likely will), so we can't use the address of 'sum2' as the
+ // end of 'sum'.
+ //
+ // To avoid the possibility that COMDAT folding will combine these two
+ // definitions into one, make sure they are different.
// A function pointer can't be cast to void*, but uint8* seems to be okay. So
// cast to a uint* which will be implicitly casted to a void* below.
- uint8_t* sum_function_start = reinterpret_cast<uint8_t*>(&sum);
+ SumFunction original_function = ∑
+ if (reinterpret_cast<uintptr_t>(&sum2) % SB_MEMORY_PAGE_SIZE <
+ reinterpret_cast<uintptr_t>(&sum) % SB_MEMORY_PAGE_SIZE) {
+ original_function = &sum2;
+ }
+
+ uint8_t* sum_function_start = reinterpret_cast<uint8_t*>(original_function);
// MIPS16 instruction are kept odd addresses to differentiate between that and
// MIPS32 instructions. Most other instructions are aligned to at least even
@@ -148,8 +165,8 @@
SumFunction mapped_function = reinterpret_cast<SumFunction>(
reinterpret_cast<uint8_t*>(memory) + sum_function_offset);
- EXPECT_EQ(4, (*mapped_function)(1, 3));
- EXPECT_EQ(5, (*mapped_function)(10, -5));
+ EXPECT_EQ((*original_function)(1, 3), (*mapped_function)(1, 3));
+ EXPECT_EQ((*original_function)(10, -5), (*mapped_function)(10, -5));
EXPECT_TRUE(SbMemoryUnmap(memory, kSize));
}
diff --git a/src/starboard/nplb/once_test.cc b/src/starboard/nplb/once_test.cc
index a504fc2..c94993d 100644
--- a/src/starboard/nplb/once_test.cc
+++ b/src/starboard/nplb/once_test.cc
@@ -1,163 +1,163 @@
-// Copyright 2015 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.
-
-// Broadcast is Sunny Day tested in most of the other SbConditionVariable tests.
-
-#include "starboard/nplb/thread_helpers.h"
-#include "starboard/once.h"
-#include "starboard/thread.h"
-#include "starboard/thread_types.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-namespace {
-
-int s_global_value;
-
-void IncrementGlobalValue() {
- ++s_global_value;
-}
-
-TEST(SbOnceTest, SunnyDaySingleInit) {
- SbOnceControl once_control = SB_ONCE_INITIALIZER;
-
- s_global_value = 0;
- EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
-
- EXPECT_EQ(1, s_global_value);
-}
-
-TEST(SbOnceTest, SunnyDayMultipleInit) {
- SbOnceControl once_control = SB_ONCE_INITIALIZER;
-
- s_global_value = 0;
- EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
- EXPECT_EQ(1, s_global_value);
-
- s_global_value = 0;
- EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
- EXPECT_EQ(0, s_global_value);
-
- s_global_value = 0;
- EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
- EXPECT_EQ(0, s_global_value);
-}
-
-struct RunSbOnceContext {
- RunSbOnceContext() : once_control(SB_ONCE_INITIALIZER) {
- SbMutexCreate(&mutex);
- SbConditionVariableCreate(&condition, &mutex);
- }
- ~RunSbOnceContext() {
- SbConditionVariableDestroy(&condition);
- SbMutexDestroy(&mutex);
- }
-
- Semaphore semaphore;
- SbMutex mutex;
- SbConditionVariable condition;
-
- SbOnceControl once_control;
-};
-
-void* RunSbOnceEntryPoint(void* context) {
- RunSbOnceContext* run_sbonce_context =
- reinterpret_cast<RunSbOnceContext*>(context);
-
- {
- SbMutexAcquire(&run_sbonce_context->mutex);
- run_sbonce_context->semaphore.Put();
- SbConditionVariableWait(&run_sbonce_context->condition,
- &run_sbonce_context->mutex);
- SbMutexRelease(&run_sbonce_context->mutex);
- }
-
- SbThreadYield();
- static const int kIterationCount = 3;
- for (int i = 0; i < kIterationCount; ++i) {
- SbOnce(&run_sbonce_context->once_control, &IncrementGlobalValue);
- }
-
- return NULL;
-}
-
-// Here we spawn many threads each of which will call SbOnce multiple times
-// using a shared SbOnceControl object. We then test that the initialization
-// routine got called exactly one time.
-TEST(SbOnceTest, SunnyDayMultipleThreadsInit) {
- const int kMany = SB_MAX_THREADS;
- SbThread threads[kMany];
-
- const int kIterationCount = 10;
- for (int i = 0; i < kIterationCount; ++i) {
- SbOnceControl once_control = SB_ONCE_INITIALIZER;
- RunSbOnceContext context;
-
- s_global_value = 0;
- for (int i = 0; i < kMany; ++i) {
- threads[i] =
- SbThreadCreate(0, kSbThreadNoPriority, kSbThreadNoAffinity, true,
- kThreadName, RunSbOnceEntryPoint, &context);
- }
-
- // Wait for all threads to finish initializing and become ready, then
- // broadcast the signal to begin. We do this to increase the chances that
- // threads will call SbOnce() at the same time as each other.
- for (int i = 0; i < kMany; ++i) {
- context.semaphore.Take();
- }
- {
- SbMutexAcquire(&context.mutex);
- SbConditionVariableBroadcast(&context.condition);
- SbMutexRelease(&context.mutex);
- }
-
- // Signal threads to beginWait for all threads to complete.
- for (int i = 0; i < kMany; ++i) {
- void* result;
- SbThreadJoin(threads[i], &result);
- }
-
- EXPECT_EQ(1, s_global_value);
- }
-}
-
-TEST(SbOnceTest, RainyDayBadOnceControl) {
- s_global_value = 0;
- EXPECT_FALSE(SbOnce(NULL, &IncrementGlobalValue));
-
- EXPECT_EQ(0, s_global_value);
-}
-
-TEST(SbOnceTest, RainyDayBadInitRoutine) {
- SbOnceControl once_control = SB_ONCE_INITIALIZER;
-
- s_global_value = 0;
- EXPECT_FALSE(SbOnce(&once_control, NULL));
-
- EXPECT_EQ(0, s_global_value);
-}
-
-SB_ONCE_INITIALIZE_FUNCTION(int, GetIntSingleton);
-TEST(SbOnceTest, InitializeOnceMacroFunction) {
- int* int_singelton = GetIntSingleton();
- ASSERT_TRUE(int_singelton);
- EXPECT_EQ(0, *int_singelton)
- << "Singleton Macro does not default initialize.";
-}
-
-} // namespace.
-} // namespace nplb.
-} // namespace starboard.
+// Copyright 2015 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.
+
+// Broadcast is Sunny Day tested in most of the other SbConditionVariable tests.
+
+#include "starboard/nplb/thread_helpers.h"
+#include "starboard/once.h"
+#include "starboard/thread.h"
+#include "starboard/thread_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+int s_global_value;
+
+void IncrementGlobalValue() {
+ ++s_global_value;
+}
+
+TEST(SbOnceTest, SunnyDaySingleInit) {
+ SbOnceControl once_control = SB_ONCE_INITIALIZER;
+
+ s_global_value = 0;
+ EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
+
+ EXPECT_EQ(1, s_global_value);
+}
+
+TEST(SbOnceTest, SunnyDayMultipleInit) {
+ SbOnceControl once_control = SB_ONCE_INITIALIZER;
+
+ s_global_value = 0;
+ EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
+ EXPECT_EQ(1, s_global_value);
+
+ s_global_value = 0;
+ EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
+ EXPECT_EQ(0, s_global_value);
+
+ s_global_value = 0;
+ EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue));
+ EXPECT_EQ(0, s_global_value);
+}
+
+struct RunSbOnceContext {
+ RunSbOnceContext() : once_control(SB_ONCE_INITIALIZER) {
+ SbMutexCreate(&mutex);
+ SbConditionVariableCreate(&condition, &mutex);
+ }
+ ~RunSbOnceContext() {
+ SbConditionVariableDestroy(&condition);
+ SbMutexDestroy(&mutex);
+ }
+
+ Semaphore semaphore;
+ SbMutex mutex;
+ SbConditionVariable condition;
+
+ SbOnceControl once_control;
+};
+
+void* RunSbOnceEntryPoint(void* context) {
+ RunSbOnceContext* run_sbonce_context =
+ reinterpret_cast<RunSbOnceContext*>(context);
+
+ {
+ SbMutexAcquire(&run_sbonce_context->mutex);
+ run_sbonce_context->semaphore.Put();
+ SbConditionVariableWait(&run_sbonce_context->condition,
+ &run_sbonce_context->mutex);
+ SbMutexRelease(&run_sbonce_context->mutex);
+ }
+
+ SbThreadYield();
+ static const int kIterationCount = 3;
+ for (int i = 0; i < kIterationCount; ++i) {
+ SbOnce(&run_sbonce_context->once_control, &IncrementGlobalValue);
+ }
+
+ return NULL;
+}
+
+// Here we spawn many threads each of which will call SbOnce multiple times
+// using a shared SbOnceControl object. We then test that the initialization
+// routine got called exactly one time.
+TEST(SbOnceTest, SunnyDayMultipleThreadsInit) {
+ const int kMany = SB_MAX_THREADS;
+ SbThread threads[kMany];
+
+ const int kIterationCount = 10;
+ for (int i = 0; i < kIterationCount; ++i) {
+ SbOnceControl once_control = SB_ONCE_INITIALIZER;
+ RunSbOnceContext context;
+
+ s_global_value = 0;
+ for (int i = 0; i < kMany; ++i) {
+ threads[i] =
+ SbThreadCreate(0, kSbThreadNoPriority, kSbThreadNoAffinity, true,
+ kThreadName, RunSbOnceEntryPoint, &context);
+ }
+
+ // Wait for all threads to finish initializing and become ready, then
+ // broadcast the signal to begin. We do this to increase the chances that
+ // threads will call SbOnce() at the same time as each other.
+ for (int i = 0; i < kMany; ++i) {
+ context.semaphore.Take();
+ }
+ {
+ SbMutexAcquire(&context.mutex);
+ SbConditionVariableBroadcast(&context.condition);
+ SbMutexRelease(&context.mutex);
+ }
+
+ // Signal threads to beginWait for all threads to complete.
+ for (int i = 0; i < kMany; ++i) {
+ void* result;
+ SbThreadJoin(threads[i], &result);
+ }
+
+ EXPECT_EQ(1, s_global_value);
+ }
+}
+
+TEST(SbOnceTest, RainyDayBadOnceControl) {
+ s_global_value = 0;
+ EXPECT_FALSE(SbOnce(NULL, &IncrementGlobalValue));
+
+ EXPECT_EQ(0, s_global_value);
+}
+
+TEST(SbOnceTest, RainyDayBadInitRoutine) {
+ SbOnceControl once_control = SB_ONCE_INITIALIZER;
+
+ s_global_value = 0;
+ EXPECT_FALSE(SbOnce(&once_control, NULL));
+
+ EXPECT_EQ(0, s_global_value);
+}
+
+SB_ONCE_INITIALIZE_FUNCTION(int, GetIntSingleton);
+TEST(SbOnceTest, InitializeOnceMacroFunction) {
+ int* int_singelton = GetIntSingleton();
+ ASSERT_TRUE(int_singelton);
+ EXPECT_EQ(0, *int_singelton)
+ << "Singleton Macro does not default initialize.";
+}
+
+} // namespace.
+} // namespace nplb.
+} // namespace starboard.
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc
index dffa92c..e463b90 100644
--- a/src/starboard/nplb/player_create_test.cc
+++ b/src/starboard/nplb/player_create_test.cc
@@ -1,61 +1,61 @@
-// 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 "starboard/player.h"
-
-#include "starboard/window.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if SB_HAS(PLAYER)
-
-namespace starboard {
-namespace nplb {
-
-TEST(SbPlayerTest, SunnyDay) {
- SbWindowOptions window_options;
- SbWindowSetDefaultOptions(&window_options);
-
- SbWindow window = SbWindowCreate(&window_options);
- EXPECT_TRUE(SbWindowIsValid(window));
-
- SbMediaAudioHeader audio_header;
-
- audio_header.format_tag = 0xff;
- audio_header.number_of_channels = 2;
- audio_header.samples_per_second = 22050;
- audio_header.block_alignment = 4;
- audio_header.bits_per_sample = 32;
- audio_header.audio_specific_config_size = 0;
- audio_header.average_bytes_per_second = audio_header.samples_per_second *
- audio_header.number_of_channels *
- audio_header.bits_per_sample / 8;
-
- SbPlayer player =
- SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,
- SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header,
- NULL, NULL, NULL, NULL
-#if SB_VERSION(3)
- ,
- NULL
-#endif
- ); // NOLINT
- EXPECT_TRUE(SbPlayerIsValid(player));
- SbPlayerDestroy(player);
- SbWindowDestroy(window);
-}
-
-} // namespace nplb
-} // namespace starboard
-
-#endif // SB_HAS(PLAYER)
+// 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 "starboard/player.h"
+
+#include "starboard/window.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if SB_HAS(PLAYER)
+
+namespace starboard {
+namespace nplb {
+
+TEST(SbPlayerTest, SunnyDay) {
+ SbWindowOptions window_options;
+ SbWindowSetDefaultOptions(&window_options);
+
+ SbWindow window = SbWindowCreate(&window_options);
+ EXPECT_TRUE(SbWindowIsValid(window));
+
+ SbMediaAudioHeader audio_header;
+
+ audio_header.format_tag = 0xff;
+ audio_header.number_of_channels = 2;
+ audio_header.samples_per_second = 22050;
+ audio_header.block_alignment = 4;
+ audio_header.bits_per_sample = 32;
+ audio_header.audio_specific_config_size = 0;
+ audio_header.average_bytes_per_second = audio_header.samples_per_second *
+ audio_header.number_of_channels *
+ audio_header.bits_per_sample / 8;
+
+ SbPlayer player =
+ SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac,
+ SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header,
+ NULL, NULL, NULL, NULL
+#if SB_VERSION(3)
+ ,
+ NULL
+#endif
+ ); // NOLINT
+ EXPECT_TRUE(SbPlayerIsValid(player));
+ SbPlayerDestroy(player);
+ SbWindowDestroy(window);
+}
+
+} // namespace nplb
+} // namespace starboard
+
+#endif // SB_HAS(PLAYER)
diff --git a/src/starboard/nplb/speech_synthesis_basic_test.cc b/src/starboard/nplb/speech_synthesis_basic_test.cc
index 4e01d35..d2e67c7 100644
--- a/src/starboard/nplb/speech_synthesis_basic_test.cc
+++ b/src/starboard/nplb/speech_synthesis_basic_test.cc
@@ -18,11 +18,8 @@
#if SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
TEST(SbSpeechSynthesisBasicTest, Basic) {
- // An implementation must at least support US English
- EXPECT_TRUE(SbSpeechSynthesisSetLanguage("en-US"));
SbSpeechSynthesisSpeak("Hello");
SbSpeechSynthesisCancel();
}
#endif // SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3)
-
diff --git a/src/starboard/nplb/system_get_total_cpu_memory_test.cc b/src/starboard/nplb/system_get_total_cpu_memory_test.cc
index 591fd6f..1fbe72e 100644
--- a/src/starboard/nplb/system_get_total_cpu_memory_test.cc
+++ b/src/starboard/nplb/system_get_total_cpu_memory_test.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "starboard/log.h"
#include "starboard/system.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -24,6 +25,18 @@
EXPECT_LE(SB_INT64_C(80) * 1024 * 1024, SbSystemGetTotalCPUMemory());
}
+TEST(SbSystemGetTotalCPUMemoryTest, PrintValues) {
+ int64_t bytes_reserved = SbSystemGetTotalCPUMemory();
+ int64_t bytes_in_use = SbSystemGetUsedCPUMemory();
+
+ std::stringstream ss;
+ ss << "\n"
+ << "SbSystemGetTotalCPUMemory() = " << SbSystemGetTotalCPUMemory() << "\n"
+ << "SbSystemGetUsedCPUMemory() = " << SbSystemGetUsedCPUMemory()
+ << "\n\n";
+ SbLogRaw(ss.str().c_str());
+}
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/queue.h b/src/starboard/queue.h
index 53f7633..e333ef3 100644
--- a/src/starboard/queue.h
+++ b/src/starboard/queue.h
@@ -21,6 +21,7 @@
#ifndef STARBOARD_QUEUE_H_
#define STARBOARD_QUEUE_H_
+#include <algorithm>
#include <deque>
#include "starboard/condition_variable.h"
@@ -115,6 +116,20 @@
condition_.Signal();
}
+ // It is guaranteed that after this function returns there is no element in
+ // the queue equals to |value|. This is useful to remove un-processed values
+ // before destroying the owning object.
+ // Note that it is the responsibility of the caller to free any resources
+ // associated with |value| after this function returns. It is possible that
+ // another thread is still using |value|, the caller should make sure that
+ // this is properly coordinated with the free of resources. Usually this can
+ // be solved by calling Remove() on the same thread that calls Get().
+ void Remove(T value) {
+ ScopedLock lock(mutex_);
+ queue_.erase(std::remove(queue_.begin(), queue_.end(), value),
+ queue_.end());
+ }
+
private:
Mutex mutex_;
ConditionVariable condition_;
diff --git a/src/starboard/raspi/shared/open_max/video_decoder.cc b/src/starboard/raspi/shared/open_max/video_decoder.cc
index 3311a06..9d3e6b2 100644
--- a/src/starboard/raspi/shared/open_max/video_decoder.cc
+++ b/src/starboard/raspi/shared/open_max/video_decoder.cc
@@ -149,7 +149,7 @@
filled_buffers_.push(buffer);
}
- if (current_buffer.valid()) {
+ if (current_buffer.is_valid()) {
int size = static_cast<int>(current_buffer.size());
while (offset < size) {
int written = component.WriteData(
diff --git a/src/starboard/raspi/shared/starboard_platform.gypi b/src/starboard/raspi/shared/starboard_platform.gypi
index 9d44f1e..a096687 100644
--- a/src/starboard/raspi/shared/starboard_platform.gypi
+++ b/src/starboard/raspi/shared/starboard_platform.gypi
@@ -236,6 +236,8 @@
'<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
'<(DEPTH)/starboard/shared/signal/suspend_signals.h',
'<(DEPTH)/starboard/shared/starboard/application.cc',
+ '<(DEPTH)/starboard/shared/starboard/command_line.cc',
+ '<(DEPTH)/starboard/shared/starboard/command_line.h',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc',
'<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc',
@@ -262,6 +264,8 @@
'<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
'<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
'<(DEPTH)/starboard/shared/starboard/new.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/decoded_audio_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h',
@@ -272,6 +276,8 @@
'<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc',
'<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/player/job_queue.cc',
+ '<(DEPTH)/starboard/shared/starboard/player/job_queue.h',
'<(DEPTH)/starboard/shared/starboard/player/player_create.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc',
'<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc',
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
index 1444180..4b73ed7 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -16,6 +16,7 @@
#include "starboard/audio_sink.h"
#include "starboard/log.h"
+#include "starboard/memory.h"
namespace starboard {
namespace shared {
@@ -81,9 +82,7 @@
TeardownCodec();
}
-void AudioDecoder::Decode(const InputBuffer& input_buffer,
- std::vector<uint8_t>* output) {
- SB_CHECK(output != NULL);
+void AudioDecoder::Decode(const InputBuffer& input_buffer) {
SB_CHECK(codec_context_ != NULL);
if (stream_ended_) {
@@ -105,7 +104,6 @@
SB_DLOG(WARNING) << "avcodec_decode_audio4() failed with result: " << result
<< " with input buffer size: " << input_buffer.size()
<< " and frame decoded: " << frame_decoded;
- output->clear();
return;
}
@@ -115,20 +113,24 @@
audio_header_.samples_per_second = codec_context_->sample_rate;
if (decoded_audio_size > 0) {
- output->resize(codec_context_->channels * av_frame_->nb_samples *
- (sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4));
+ scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
+ input_buffer.pts(),
+ codec_context_->channels * av_frame_->nb_samples *
+ (sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4));
if (codec_context_->sample_fmt == codec_context_->request_sample_fmt) {
- memcpy(&(*output)[0], av_frame_->extended_data, output->size());
+ SbMemoryCopy(decoded_audio->buffer(), av_frame_->extended_data,
+ decoded_audio->size());
} else {
- ConvertSamples(
- codec_context_->sample_fmt, codec_context_->request_sample_fmt,
- codec_context_->channel_layout, audio_header_.samples_per_second,
- av_frame_->nb_samples, av_frame_->extended_data, &(*output)[0]);
+ ConvertSamples(codec_context_->sample_fmt,
+ codec_context_->request_sample_fmt,
+ codec_context_->channel_layout,
+ audio_header_.samples_per_second, av_frame_->nb_samples,
+ av_frame_->extended_data, decoded_audio->buffer());
}
+ decoded_audios_.push(decoded_audio);
} else {
// TODO: Consider fill it with silence.
SB_LOG(ERROR) << "Decoded audio frame is empty.";
- output->clear();
}
}
@@ -136,10 +138,24 @@
// AAC has no dependent frames so we needn't flush the decoder. Set the flag
// to ensure that Decode() is not called when the stream is ended.
stream_ended_ = true;
+ // Put EOS into the queue.
+ decoded_audios_.push(new DecodedAudio);
+}
+
+scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read() {
+ scoped_refptr<DecodedAudio> result;
+ if (!decoded_audios_.empty()) {
+ result = decoded_audios_.front();
+ decoded_audios_.pop();
+ }
+ return result;
}
void AudioDecoder::Reset() {
stream_ended_ = false;
+ while (!decoded_audios_.empty()) {
+ decoded_audios_.pop();
+ }
}
SbMediaAudioSampleType AudioDecoder::GetSampleType() const {
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
index c2a78db..4d6ba4c 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
+++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -15,11 +15,12 @@
#ifndef STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
#define STARBOARD_SHARED_FFMPEG_FFMPEG_AUDIO_DECODER_H_
-#include <vector>
+#include <queue>
#include "starboard/media.h"
#include "starboard/shared/ffmpeg/ffmpeg_common.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
namespace starboard {
@@ -28,15 +29,16 @@
class AudioDecoder : public starboard::player::filter::AudioDecoder {
public:
+ typedef starboard::player::DecodedAudio DecodedAudio;
typedef starboard::player::InputBuffer InputBuffer;
AudioDecoder(SbMediaAudioCodec audio_codec,
const SbMediaAudioHeader& audio_header);
~AudioDecoder() SB_OVERRIDE;
- void Decode(const InputBuffer& input_buffer,
- std::vector<uint8_t>* output) SB_OVERRIDE;
+ void Decode(const InputBuffer& input_buffer) SB_OVERRIDE;
void WriteEndOfStream() SB_OVERRIDE;
+ scoped_refptr<DecodedAudio> Read() SB_OVERRIDE;
void Reset() SB_OVERRIDE;
SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE;
int GetSamplesPerSecond() const SB_OVERRIDE;
@@ -52,6 +54,7 @@
AVFrame* av_frame_;
bool stream_ended_;
+ std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
SbMediaAudioHeader audio_header_;
};
diff --git a/src/starboard/shared/iso/directory_close.cc b/src/starboard/shared/iso/directory_close.cc
index a4fb9b5..93f50c2 100644
--- a/src/starboard/shared/iso/directory_close.cc
+++ b/src/starboard/shared/iso/directory_close.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// 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.
@@ -12,20 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/directory.h"
-
-#include <dirent.h>
-#include <errno.h>
-
-#include "starboard/file.h"
#include "starboard/shared/iso/directory_internal.h"
-bool SbDirectoryClose(SbDirectory directory) {
- if (!directory || !directory->directory) {
- return false;
- }
+#include "starboard/shared/iso/impl/directory_close.h"
- bool result = !closedir(directory->directory);
- delete directory;
- return result;
+bool SbDirectoryClose(SbDirectory directory) {
+ return ::starboard::shared::iso::impl::SbDirectoryClose(directory);
}
diff --git a/src/starboard/shared/iso/directory_get_next.cc b/src/starboard/shared/iso/directory_get_next.cc
index c60375f..bef30b6 100644
--- a/src/starboard/shared/iso/directory_get_next.cc
+++ b/src/starboard/shared/iso/directory_get_next.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// 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.
@@ -12,28 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/directory.h"
-
-#include <dirent.h>
-#include <errno.h>
-
-#include "starboard/file.h"
#include "starboard/shared/iso/directory_internal.h"
-#include "starboard/string.h"
+
+#include "starboard/shared/iso/impl/directory_get_next.h"
bool SbDirectoryGetNext(SbDirectory directory, SbDirectoryEntry* out_entry) {
- if (!directory || !directory->directory || !out_entry) {
- return false;
- }
-
- struct dirent dirent_buffer;
- struct dirent* dirent;
- int result = readdir_r(directory->directory, &dirent_buffer, &dirent);
- if (result || !dirent) {
- return false;
- }
-
- SbStringCopy(out_entry->name, dirent->d_name,
- SB_ARRAY_SIZE_INT(out_entry->name));
- return true;
+ return ::starboard::shared::iso::impl::SbDirectoryGetNext(directory,
+ out_entry);
}
diff --git a/src/starboard/shared/iso/directory_open.cc b/src/starboard/shared/iso/directory_open.cc
index e1fe59b..2604aea 100644
--- a/src/starboard/shared/iso/directory_open.cc
+++ b/src/starboard/shared/iso/directory_open.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
+// 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.
@@ -12,63 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/directory.h"
-
-#include <dirent.h>
-#include <errno.h>
-
-#include "starboard/file.h"
#include "starboard/shared/iso/directory_internal.h"
+#include "starboard/shared/iso/impl/directory_open.h"
+
SbDirectory SbDirectoryOpen(const char* path, SbFileError* out_error) {
- if (!path) {
- if (out_error) {
- *out_error = kSbFileErrorNotFound;
- }
- return kSbDirectoryInvalid;
- }
-
- DIR* dir = opendir(path);
- if (!dir) {
- if (out_error) {
- switch (errno) {
- case EACCES:
- *out_error = kSbFileErrorAccessDenied;
- break;
- case ELOOP:
- *out_error = kSbFileErrorFailed;
- break;
- case ENAMETOOLONG:
- *out_error = kSbFileErrorInvalidOperation;
- break;
- case ENOENT:
- *out_error = kSbFileErrorNotFound;
- break;
- case ENOTDIR:
- *out_error = kSbFileErrorNotADirectory;
- break;
- case EMFILE:
- case ENFILE:
- *out_error = kSbFileErrorTooManyOpened;
- break;
- }
- }
-
- return kSbDirectoryInvalid;
- }
-
- SbDirectory result = new SbDirectoryPrivate();
- if (!result) {
- closedir(dir);
- if (out_error) {
- *out_error = kSbFileErrorNoMemory;
- }
- return kSbDirectoryInvalid;
- }
-
- result->directory = dir;
- if (out_error) {
- *out_error = kSbFileOk;
- }
- return result;
+ return ::starboard::shared::iso::impl::SbDirectoryOpen(path, out_error);
}
diff --git a/src/starboard/shared/iso/impl/directory_close.h b/src/starboard/shared/iso/impl/directory_close.h
new file mode 100644
index 0000000..f9760a9
--- /dev/null
+++ b/src/starboard/shared/iso/impl/directory_close.h
@@ -0,0 +1,48 @@
+// 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 STARBOARD_SHARED_ISO_IMPL_DIRECTORY_CLOSE_H_
+#define STARBOARD_SHARED_ISO_IMPL_DIRECTORY_CLOSE_H_
+
+#include "starboard/directory.h"
+
+#include <dirent.h>
+#include <errno.h>
+
+#include "starboard/file.h"
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/iso/impl/directory_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace iso {
+namespace impl {
+
+bool SbDirectoryClose(SbDirectory directory) {
+ if (!directory || !directory->directory) {
+ return false;
+ }
+
+ bool result = !closedir(directory->directory);
+ delete directory;
+ return result;
+}
+
+} // namespace impl
+} // namespace iso
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_ISO_IMPL_DIRECTORY_CLOSE_H_
diff --git a/src/starboard/shared/iso/impl/directory_get_next.h b/src/starboard/shared/iso/impl/directory_get_next.h
new file mode 100644
index 0000000..be4ca1b
--- /dev/null
+++ b/src/starboard/shared/iso/impl/directory_get_next.h
@@ -0,0 +1,56 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_ISO_IMPL_DIRECTORY_GET_NEXT_H_
+#define STARBOARD_SHARED_ISO_IMPL_DIRECTORY_GET_NEXT_H_
+
+#include "starboard/directory.h"
+
+#include <dirent.h>
+#include <errno.h>
+
+#include "starboard/file.h"
+#include "starboard/string.h"
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/iso/impl/directory_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace iso {
+namespace impl {
+
+bool SbDirectoryGetNext(SbDirectory directory, SbDirectoryEntry* out_entry) {
+ if (!directory || !directory->directory || !out_entry) {
+ return false;
+ }
+
+ struct dirent dirent_buffer;
+ struct dirent* dirent;
+ int result = readdir_r(directory->directory, &dirent_buffer, &dirent);
+ if (result || !dirent) {
+ return false;
+ }
+
+ SbStringCopy(out_entry->name, dirent->d_name,
+ SB_ARRAY_SIZE_INT(out_entry->name));
+ return true;
+}
+
+} // namespace impl
+} // namespace iso
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_ISO_IMPL_DIRECTORY_GET_NEXT_H_
diff --git a/src/starboard/shared/iso/impl/directory_impl.h b/src/starboard/shared/iso/impl/directory_impl.h
new file mode 100644
index 0000000..99c9cfe
--- /dev/null
+++ b/src/starboard/shared/iso/impl/directory_impl.h
@@ -0,0 +1,28 @@
+// 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 STARBOARD_SHARED_ISO_IMPL_DIRECTORY_IMPL_H_
+#define STARBOARD_SHARED_ISO_IMPL_DIRECTORY_IMPL_H_
+
+#include "starboard/configuration.h"
+#include "starboard/directory.h"
+
+#include "starboard/shared/internal_only.h"
+
+// Ensure SbDirectory is typedef'd to a SbDirectoryPrivate* that has a directory
+// field.
+SB_COMPILE_ASSERT(sizeof(((SbDirectory)0)->directory), \
+ SbDirectoryPrivate_must_have_directory);
+
+#endif // STARBOARD_SHARED_ISO_IMPL_DIRECTORY_IMPL_H_
diff --git a/src/starboard/shared/iso/impl/directory_open.h b/src/starboard/shared/iso/impl/directory_open.h
new file mode 100644
index 0000000..9594035
--- /dev/null
+++ b/src/starboard/shared/iso/impl/directory_open.h
@@ -0,0 +1,91 @@
+// 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 STARBOARD_SHARED_ISO_IMPL_DIRECTORY_OPEN_H_
+#define STARBOARD_SHARED_ISO_IMPL_DIRECTORY_OPEN_H_
+
+#include "starboard/directory.h"
+
+#include <dirent.h>
+#include <errno.h>
+
+#include "starboard/file.h"
+
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/iso/impl/directory_impl.h"
+
+namespace starboard {
+namespace shared {
+namespace iso {
+namespace impl {
+
+SbDirectory SbDirectoryOpen(const char* path, SbFileError* out_error) {
+ if (!path) {
+ if (out_error) {
+ *out_error = kSbFileErrorNotFound;
+ }
+ return kSbDirectoryInvalid;
+ }
+
+ DIR* dir = opendir(path);
+ if (!dir) {
+ if (out_error) {
+ switch (errno) {
+ case EACCES:
+ *out_error = kSbFileErrorAccessDenied;
+ break;
+ case ELOOP:
+ *out_error = kSbFileErrorFailed;
+ break;
+ case ENAMETOOLONG:
+ *out_error = kSbFileErrorInvalidOperation;
+ break;
+ case ENOENT:
+ *out_error = kSbFileErrorNotFound;
+ break;
+ case ENOTDIR:
+ *out_error = kSbFileErrorNotADirectory;
+ break;
+ case EMFILE:
+ case ENFILE:
+ *out_error = kSbFileErrorTooManyOpened;
+ break;
+ }
+ }
+
+ return kSbDirectoryInvalid;
+ }
+
+ SbDirectory result = new SbDirectoryPrivate();
+ if (!result) {
+ closedir(dir);
+ if (out_error) {
+ *out_error = kSbFileErrorNoMemory;
+ }
+ return kSbDirectoryInvalid;
+ }
+
+ result->directory = dir;
+ if (out_error) {
+ *out_error = kSbFileOk;
+ }
+ return result;
+}
+
+} // namespace impl
+} // namespace iso
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_ISO_IMPL_DIRECTORY_OPEN_H_
diff --git a/src/starboard/shared/pthread/once.cc b/src/starboard/shared/pthread/once.cc
index 74395f8..1fe6b82 100644
--- a/src/starboard/shared/pthread/once.cc
+++ b/src/starboard/shared/pthread/once.cc
@@ -1,28 +1,28 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/once.h"
-
-#include <pthread.h>
-
-bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {
- if (once_control == NULL) {
- return false;
- }
- if (init_routine == NULL) {
- return false;
- }
-
- return pthread_once(once_control, init_routine) == 0;
-}
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/once.h"
+
+#include <pthread.h>
+
+bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {
+ if (once_control == NULL) {
+ return false;
+ }
+ if (init_routine == NULL) {
+ return false;
+ }
+
+ return pthread_once(once_control, init_routine) == 0;
+}
diff --git a/src/starboard/shared/speechd/speech_synthesis_set_language.cc b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
index 1e8080f..cbd2c81 100644
--- a/src/starboard/shared/speechd/speech_synthesis_set_language.cc
+++ b/src/starboard/shared/speechd/speech_synthesis_set_language.cc
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+// DEPRECATED IN API VERSION 4
+
#include "starboard/speech_synthesis.h"
#include "starboard/shared/speechd/speechd_internal.h"
@@ -25,3 +28,5 @@
}
return speech_dispatcher->SetLanguage(lang);
}
+
+#endif
diff --git a/src/starboard/shared/speechd/speechd_internal.cc b/src/starboard/shared/speechd/speechd_internal.cc
index 12efab4..ee30ff9 100644
--- a/src/starboard/shared/speechd/speechd_internal.cc
+++ b/src/starboard/shared/speechd/speechd_internal.cc
@@ -40,6 +40,9 @@
if (!connection_) {
SB_DLOG(ERROR) << "Failed to initialize SpeechDispatcher.";
}
+ if (!SetLanguage(SbSystemGetLocaleId())) {
+ SB_DLOG(ERROR) << "Unable to set language to current locale.";
+ }
}
SpeechDispatcher::~SpeechDispatcher() {
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc
index 61fc960..f79f5dd 100644
--- a/src/starboard/shared/starboard/application.cc
+++ b/src/starboard/shared/starboard/application.cc
@@ -20,6 +20,8 @@
#include "starboard/memory.h"
#include "starboard/string.h"
+#include "starboard/shared/starboard/command_line.h"
+
namespace starboard {
namespace shared {
namespace starboard {
@@ -49,8 +51,6 @@
Application::Application()
: error_level_(0),
thread_(SbThreadGetCurrent()),
- argument_count_(0),
- argument_values_(NULL),
start_link_(NULL),
state_(kStateUnstarted) {
Application* old_instance =
@@ -74,8 +74,7 @@
int Application::Run(int argc, char** argv) {
Initialize();
- argument_count_ = argc;
- argument_values_ = argv;
+ command_line_.reset(new CommandLine(argc, argv));
if (IsStartImmediate()) {
DispatchStart();
}
@@ -91,6 +90,10 @@
return error_level_;
}
+CommandLine* Application::GetCommandLine() {
+ return command_line_.get();
+}
+
void Application::Pause(void* context, EventHandledCallback callback) {
Inject(new Event(kSbEventTypePause, context, callback));
}
@@ -148,8 +151,9 @@
void Application::DispatchStart() {
SB_DCHECK(state_ == kStateUnstarted);
SbEventStartData start_data;
- start_data.argument_values = argument_values_;
- start_data.argument_count = argument_count_;
+ start_data.argument_values =
+ const_cast<char**>(command_line_->GetOriginalArgv());
+ start_data.argument_count = command_line_->GetOriginalArgc();
start_data.link = start_link_;
Dispatch(kSbEventTypeStart, &start_data, NULL);
state_ = kStateStarted;
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h
index 50dfe9f..99b4061 100644
--- a/src/starboard/shared/starboard/application.h
+++ b/src/starboard/shared/starboard/application.h
@@ -26,6 +26,7 @@
#include "starboard/log.h"
#include "starboard/player.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/command_line.h"
#include "starboard/shared/starboard/player/video_frame_internal.h"
#include "starboard/thread.h"
#include "starboard/time.h"
@@ -151,6 +152,10 @@
// initialization and teardown events. Returns the resulting error level.
int Run(int argc, char** argv);
+ // Retrieves the CommandLine for the application.
+ // NULL until Run() is called.
+ CommandLine* GetCommandLine();
+
// Signals that the application should transition from STARTED to PAUSED as
// soon as possible. Does nothing if already PAUSED or SUSPENDED. May be
// called from an external thread.
@@ -317,9 +322,8 @@
// main thread.
SbThread thread_;
- // The command line arguments passed to |Run|.
- int argument_count_;
- char** argument_values_;
+ // CommandLine instance initialized in |Run|.
+ scoped_ptr<CommandLine> command_line_;
// The deep link included in the Start event sent to Cobalt. Initially NULL,
// derived classes may set it during initialization using |SetStartLink|.
diff --git a/src/starboard/shared/starboard/command_line.cc b/src/starboard/shared/starboard/command_line.cc
new file mode 100644
index 0000000..f4a8459
--- /dev/null
+++ b/src/starboard/shared/starboard/command_line.cc
@@ -0,0 +1,178 @@
+// 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 "starboard/shared/starboard/command_line.h"
+
+#include <algorithm>
+#include <ostream>
+
+#include "starboard/configuration.h"
+#include "starboard/log.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+namespace {
+// From base/string_util.cc
+const char kWhitespaceASCII[] = {
+ 0x09, // <control-0009> to <control-000D>
+ 0x0A,
+ 0x0B,
+ 0x0C,
+ 0x0D,
+ 0x20, // Space
+ 0
+};
+
+const CommandLine::CharType kSwitchTerminator[] = "--";
+const CommandLine::CharType kSwitchValueSeparator[] = "=";
+// Since we use a lazy match, make sure that longer versions (like "--") are
+// listed before shorter versions (like "-") of similar prefixes.
+// Unixes don't use slash as a switch.
+const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
+
+size_t GetSwitchPrefixLength(const CommandLine::StringType& string) {
+ for (size_t i = 0; i < SB_ARRAY_SIZE_INT(kSwitchPrefixes); ++i) {
+ CommandLine::StringType prefix(kSwitchPrefixes[i]);
+ if (string.compare(0, prefix.length(), prefix) == 0)
+ return prefix.length();
+ }
+ return 0;
+}
+
+// Fills in |switch_string| and |switch_value| if |string| is a switch.
+// This will preserve the input switch prefix in the output |switch_string|.
+bool IsSwitch(const CommandLine::StringType& string,
+ CommandLine::StringType* switch_string,
+ CommandLine::StringType* switch_value) {
+ switch_string->clear();
+ switch_value->clear();
+ size_t prefix_length = GetSwitchPrefixLength(string);
+ if (prefix_length == 0 || prefix_length == string.length())
+ return false;
+
+ const size_t equals_position = string.find(kSwitchValueSeparator);
+ *switch_string = string.substr(0, equals_position);
+ if (equals_position != CommandLine::StringType::npos)
+ *switch_value = string.substr(equals_position + 1);
+ return true;
+}
+
+// Append switches and arguments, keeping switches before arguments.
+void AppendSwitchesAndArguments(CommandLine& command_line,
+ const CommandLine::StringVector& argv) {
+ bool parse_switches = true;
+ for (size_t i = 1; i < argv.size(); ++i) {
+ CommandLine::StringType arg = argv[i];
+
+ // Begin inlined TrimWhitespace TRIM_ALL from base/string_util.cc
+ CommandLine::StringType::size_type last_char = arg.length() - 1;
+ CommandLine::StringType::size_type first_good_char =
+ arg.find_first_not_of(kWhitespaceASCII);
+ CommandLine::StringType::size_type last_good_char =
+ arg.find_last_not_of(kWhitespaceASCII);
+
+ if (arg.empty() ||
+ (first_good_char == CommandLine::StringType::npos) ||
+ (last_good_char == CommandLine::StringType::npos)) {
+ arg.clear();
+ } else {
+ // Trim the whitespace.
+ arg = arg.substr(first_good_char, last_good_char - first_good_char + 1);
+ }
+ // End inlined TrimWhitespace TRIM_ALL from base/string_util.cc
+
+ CommandLine::StringType switch_string;
+ CommandLine::StringType switch_value;
+ parse_switches &= (arg != kSwitchTerminator);
+ if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
+ command_line.AppendSwitch(switch_string, switch_value);
+ } else {
+ command_line.AppendArg(arg);
+ }
+ }
+}
+
+// Lowercase switches for backwards compatiblity *on Windows*.
+std::string LowerASCIIOnWindows(const std::string& string) {
+ return string;
+}
+
+} // namespace
+
+CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
+ : argv_(1),
+ begin_args_(1),
+ original_argument_count_(argc),
+ original_argument_vector_(argv) {
+ InitFromArgv(argc, argv);
+}
+
+CommandLine::~CommandLine() {
+}
+
+void CommandLine::InitFromArgv(int argc,
+ const CommandLine::CharType* const* argv) {
+ StringVector new_argv;
+ for (int i = 0; i < argc; ++i)
+ new_argv.push_back(argv[i]);
+ InitFromArgv(new_argv);
+}
+
+void CommandLine::InitFromArgv(const StringVector& argv) {
+ argv_ = StringVector(1);
+ begin_args_ = 1;
+ AppendSwitchesAndArguments(*this, argv);
+}
+
+bool CommandLine::HasSwitch(const std::string& switch_string) const {
+ return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end();
+}
+
+CommandLine::StringType CommandLine::GetSwitchValue(
+ const std::string& switch_string) const {
+ SwitchMap::const_iterator result = switches_.end();
+ result = switches_.find(LowerASCIIOnWindows(switch_string));
+ return result == switches_.end() ? StringType() : result->second;
+}
+
+void CommandLine::AppendSwitch(const std::string& switch_string,
+ const CommandLine::StringType& value) {
+ std::string switch_key(LowerASCIIOnWindows(switch_string));
+ StringType combined_switch_string(switch_string);
+ size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
+ switches_[switch_key.substr(prefix_length)] = value;
+ // Preserve existing switch prefixes in |argv_|; only append one if necessary.
+ if (prefix_length == 0)
+ combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
+ if (!value.empty())
+ combined_switch_string += kSwitchValueSeparator + value;
+ // Append the switch and update the switches/arguments divider |begin_args_|.
+ argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
+}
+
+CommandLine::StringVector CommandLine::GetArgs() const {
+ // Gather all arguments after the last switch (may include kSwitchTerminator).
+ StringVector args(argv_.begin() + begin_args_, argv_.end());
+ // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
+ StringVector::iterator switch_terminator =
+ std::find(args.begin(), args.end(), kSwitchTerminator);
+ if (switch_terminator != args.end())
+ args.erase(switch_terminator);
+ return args;
+}
+
+void CommandLine::AppendArg(const CommandLine::StringType& value) {
+ argv_.push_back(value);
+}
+
+void CommandLine::AppendArguments(const CommandLine& other,
+ bool include_program) {
+ AppendSwitchesAndArguments(*this, other.argv());
+}
+
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/command_line.h b/src/starboard/shared/starboard/command_line.h
new file mode 100644
index 0000000..9baf916
--- /dev/null
+++ b/src/starboard/shared/starboard/command_line.h
@@ -0,0 +1,103 @@
+// 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.
+
+// This class works with command lines: building and parsing.
+// Arguments with prefixes ('--', '-') are switches.
+// Switches will precede all other arguments without switch prefixes.
+// Switches can optionally have values, delimited by '=', e.g., "-switch=value".
+// An argument of "--" will terminate switch parsing during initialization,
+// interpreting subsequent tokens as non-switch arguments, regardless of prefix.
+
+#ifndef STARBOARD_SHARED_STARBOARD_COMMAND_LINE_H_
+#define STARBOARD_SHARED_STARBOARD_COMMAND_LINE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+class CommandLine {
+ public:
+ typedef std::string StringType;
+
+ typedef StringType::value_type CharType;
+ typedef std::vector<StringType> StringVector;
+ typedef std::map<std::string, StringType> SwitchMap;
+
+ // Construct a new command line from an argument list.
+ CommandLine(int argc, const CharType* const* argv);
+
+ ~CommandLine();
+
+ // Initialize from an argv vector.
+ void InitFromArgv(int argc, const CharType* const* argv);
+
+ // Returns the original command line string as a vector of strings.
+ const StringVector& argv() const { return argv_; }
+
+ int GetOriginalArgc() const { return original_argument_count_; }
+ const CharType* const* GetOriginalArgv() const {
+ return original_argument_vector_;
+ }
+
+ // Returns true if this command line contains the given switch.
+ // (Switch names are case-insensitive).
+ bool HasSwitch(const std::string& switch_string) const;
+
+ // Append a switch [with optional value] to the command line.
+ // Note: Switches will precede arguments regardless of appending order.
+ void AppendSwitch(const std::string& switch_string,
+ const StringType& value);
+
+ // Returns the value associated with the given switch. If the switch has no
+ // value or isn't present, this method returns the empty string.
+ StringType GetSwitchValue(const std::string& switch_string) const;
+
+ // Get a copy of all switches, along with their values.
+ const SwitchMap& GetSwitches() const { return switches_; }
+
+ // Get the remaining arguments to the command.
+ StringVector GetArgs() const;
+
+ // Append an argument to the command line. Note that the argument is quoted
+ // properly such that it is interpreted as one argument to the target command.
+ // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8.
+ // Note: Switches will precede arguments regardless of appending order.
+ void AppendArg(const std::string& value);
+
+ // Append the switches and arguments from another command line to this one.
+ // If |include_program| is true, include |other|'s program as well.
+ void AppendArguments(const CommandLine& other, bool include_program);
+
+ private:
+ void InitFromArgv(const StringVector& argv);
+
+ // Disallow default constructor; a program name must be explicitly specified.
+ CommandLine();
+ // Allow the copy constructor. A common pattern is to copy of the current
+ // process's command line and then add some flags to it.
+
+ // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* }
+ StringVector argv_;
+
+ // Parsed-out switch keys and values.
+ SwitchMap switches_;
+
+ // The index after the program and switches, any arguments start here.
+ size_t begin_args_;
+
+ int original_argument_count_;
+ const CharType* const* original_argument_vector_;
+};
+
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_COMMAND_LINE_H_
diff --git a/src/starboard/shared/starboard/lazy_initialization_internal.cc b/src/starboard/shared/starboard/lazy_initialization_internal.cc
index 6d2cdaf..f108899 100644
--- a/src/starboard/shared/starboard/lazy_initialization_internal.cc
+++ b/src/starboard/shared/starboard/lazy_initialization_internal.cc
@@ -1,61 +1,61 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/shared/starboard/lazy_initialization_internal.h"
-
-#include "starboard/log.h"
-#include "starboard/thread.h"
-
-// INITIALIZED_STATE_UNINITIALIZED is defined in the header.
-#define INITIALIZED_STATE_INITIALIZING 2
-#define INITIALIZED_STATE_INITIALIZED 3
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-
-bool EnsureInitialized(InitializedState* state) {
- // Check what state we're in, and if we find that we are uninitialized,
- // simultaneously mark us as initializing and return to the caller.
- InitializedState original = SbAtomicNoBarrier_CompareAndSwap(
- state, INITIALIZED_STATE_UNINITIALIZED, INITIALIZED_STATE_INITIALIZING);
- if (original == INITIALIZED_STATE_UNINITIALIZED) {
- // If we were uninitialized, we are now marked as initializing and so
- // we relay this information to the caller, so that they may initialize.
- return false;
- } else if (original == INITIALIZED_STATE_INITIALIZING) {
- // If the current state is that we are being initialized, spin until
- // initialization is complete, then return.
- do {
- SbThreadYield();
- } while (SbAtomicAcquire_Load(state) != INITIALIZED_STATE_INITIALIZED);
- } else {
- SB_DCHECK(original == INITIALIZED_STATE_INITIALIZED);
- }
-
- return true;
-}
-
-bool IsInitialized(InitializedState* state) {
- return SbAtomicNoBarrier_Load(state) == INITIALIZED_STATE_INITIALIZED;
-}
-
-void SetInitialized(InitializedState* state) {
- // Mark that we are initialized now.
- SbAtomicRelease_Store(state, INITIALIZED_STATE_INITIALIZED);
-}
-
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+
+#include "starboard/log.h"
+#include "starboard/thread.h"
+
+// INITIALIZED_STATE_UNINITIALIZED is defined in the header.
+#define INITIALIZED_STATE_INITIALIZING 2
+#define INITIALIZED_STATE_INITIALIZED 3
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+bool EnsureInitialized(InitializedState* state) {
+ // Check what state we're in, and if we find that we are uninitialized,
+ // simultaneously mark us as initializing and return to the caller.
+ InitializedState original = SbAtomicNoBarrier_CompareAndSwap(
+ state, INITIALIZED_STATE_UNINITIALIZED, INITIALIZED_STATE_INITIALIZING);
+ if (original == INITIALIZED_STATE_UNINITIALIZED) {
+ // If we were uninitialized, we are now marked as initializing and so
+ // we relay this information to the caller, so that they may initialize.
+ return false;
+ } else if (original == INITIALIZED_STATE_INITIALIZING) {
+ // If the current state is that we are being initialized, spin until
+ // initialization is complete, then return.
+ do {
+ SbThreadYield();
+ } while (SbAtomicAcquire_Load(state) != INITIALIZED_STATE_INITIALIZED);
+ } else {
+ SB_DCHECK(original == INITIALIZED_STATE_INITIALIZED);
+ }
+
+ return true;
+}
+
+bool IsInitialized(InitializedState* state) {
+ return SbAtomicNoBarrier_Load(state) == INITIALIZED_STATE_INITIALIZED;
+}
+
+void SetInitialized(InitializedState* state) {
+ // Mark that we are initialized now.
+ SbAtomicRelease_Store(state, INITIALIZED_STATE_INITIALIZED);
+}
+
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/lazy_initialization_internal.h b/src/starboard/shared/starboard/lazy_initialization_internal.h
index eb7028a..f1cd603 100644
--- a/src/starboard/shared/starboard/lazy_initialization_internal.h
+++ b/src/starboard/shared/starboard/lazy_initialization_internal.h
@@ -1,47 +1,47 @@
-// Copyright 2015 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 STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_
-#define STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_
-
-#include "starboard/atomic.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/lazy_initialization_public.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-
-// The utility functions defined here use atomics and spin-locks to allow for
-// easy lazy initialization in a thread-safe way.
-
-// Returns false if initialization is necessary, otherwise returns true.
-// If false is returned, you must initialize the state (e.g. by eventually
-// calling SetInitialized() or else other threads waiting for initialization
-// to complete will wait forever.)
-bool EnsureInitialized(InitializedState* state);
-
-// Returns true if the state is initialized, false otherwise. Do not
-// use the outcome of this function to make a decision on whether to initialize
-// or not, use EnsureInitialized() for that.
-bool IsInitialized(InitializedState* state);
-
-// Sets the state as being initialized.
-void SetInitialized(InitializedState* state);
-
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_
+// Copyright 2015 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 STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_
+
+#include "starboard/atomic.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/lazy_initialization_public.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+
+// The utility functions defined here use atomics and spin-locks to allow for
+// easy lazy initialization in a thread-safe way.
+
+// Returns false if initialization is necessary, otherwise returns true.
+// If false is returned, you must initialize the state (e.g. by eventually
+// calling SetInitialized() or else other threads waiting for initialization
+// to complete will wait forever.)
+bool EnsureInitialized(InitializedState* state);
+
+// Returns true if the state is initialized, false otherwise. Do not
+// use the outcome of this function to make a decision on whether to initialize
+// or not, use EnsureInitialized() for that.
+bool IsInitialized(InitializedState* state);
+
+// Sets the state as being initialized.
+void SetInitialized(InitializedState* state);
+
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/lazy_initialization_public.h b/src/starboard/shared/starboard/lazy_initialization_public.h
index 73d0726..c4d7f8f 100644
--- a/src/starboard/shared/starboard/lazy_initialization_public.h
+++ b/src/starboard/shared/starboard/lazy_initialization_public.h
@@ -1,30 +1,30 @@
-// Copyright 2015 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 STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_
-#define STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_
-
-#include "starboard/atomic.h"
-
-// The utility functions defined here use atomics and spin-locks to allow for
-// easy lazy initialization in a thread-safe way.
-
-// An InitializedState should be treated as an opaque type that can be
-// initialized to kInitializedStateUninitialized and then passed into the
-// functions in this file to transition it first to a "initializing" state and
-// then an "initialized" state.
-typedef SbAtomic32 InitializedState;
-#define INITIALIZED_STATE_UNINITIALIZED 1
-
-#endif // STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_
+// Copyright 2015 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 STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_
+#define STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_
+
+#include "starboard/atomic.h"
+
+// The utility functions defined here use atomics and spin-locks to allow for
+// easy lazy initialization in a thread-safe way.
+
+// An InitializedState should be treated as an opaque type that can be
+// initialized to kInitializedStateUninitialized and then passed into the
+// functions in this file to transition it first to a "initializing" state and
+// then an "initialized" state.
+typedef SbAtomic32 InitializedState;
+#define INITIALIZED_STATE_UNINITIALIZED 1
+
+#endif // STARBOARD_SHARED_STARBOARD_LAZY_INITIALIZATION_PUBLIC_H_
diff --git a/src/starboard/shared/starboard/once.cc b/src/starboard/shared/starboard/once.cc
index e472279..ace0197 100644
--- a/src/starboard/shared/starboard/once.cc
+++ b/src/starboard/shared/starboard/once.cc
@@ -1,38 +1,38 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/once.h"
-#include "starboard/shared/starboard/lazy_initialization_internal.h"
-
-// Platform-independent SbOnce() implementation based on the functionality
-// declared by starboard/shared/lazy_initialization_internal.h which internally
-// uses atomics.
-using starboard::shared::starboard::EnsureInitialized;
-using starboard::shared::starboard::SetInitialized;
-
-bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {
- if (once_control == NULL) {
- return false;
- }
- if (init_routine == NULL) {
- return false;
- }
-
- if (!EnsureInitialized(once_control)) {
- init_routine();
- SetInitialized(once_control);
- }
-
- return true;
-}
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/once.h"
+#include "starboard/shared/starboard/lazy_initialization_internal.h"
+
+// Platform-independent SbOnce() implementation based on the functionality
+// declared by starboard/shared/lazy_initialization_internal.h which internally
+// uses atomics.
+using starboard::shared::starboard::EnsureInitialized;
+using starboard::shared::starboard::SetInitialized;
+
+bool SbOnce(SbOnceControl* once_control, SbOnceInitRoutine init_routine) {
+ if (once_control == NULL) {
+ return false;
+ }
+ if (init_routine == NULL) {
+ return false;
+ }
+
+ if (!EnsureInitialized(once_control)) {
+ init_routine();
+ SetInitialized(once_control);
+ }
+
+ return true;
+}
diff --git a/src/starboard/shared/starboard/once_types_public.h b/src/starboard/shared/starboard/once_types_public.h
index 692e944..835c030 100644
--- a/src/starboard/shared/starboard/once_types_public.h
+++ b/src/starboard/shared/starboard/once_types_public.h
@@ -1,34 +1,34 @@
-// Copyright 2015 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.
-
-// Provides definitions useful for working with SbOnce implemented using
-// SbAtomic32.
-
-#ifndef STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_
-#define STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_
-
-#include "starboard/atomic.h"
-#include "starboard/shared/starboard/lazy_initialization_public.h"
-
-// Defines once types for the platform-independent atomics-based implementation
-// of SbOnce() defined in shared/once.cc.
-
-// Transparent Once control handle.
-typedef InitializedState SbOnceControl;
-
-// Once static initializer.
-#define SB_ONCE_INITIALIZER \
- { INITIALIZED_STATE_UNINITIALIZED }
-
-#endif // STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_
+// Copyright 2015 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.
+
+// Provides definitions useful for working with SbOnce implemented using
+// SbAtomic32.
+
+#ifndef STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_
+#define STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_
+
+#include "starboard/atomic.h"
+#include "starboard/shared/starboard/lazy_initialization_public.h"
+
+// Defines once types for the platform-independent atomics-based implementation
+// of SbOnce() defined in shared/once.cc.
+
+// Transparent Once control handle.
+typedef InitializedState SbOnceControl;
+
+// Once static initializer.
+#define SB_ONCE_INITIALIZER \
+ { INITIALIZED_STATE_UNINITIALIZED }
+
+#endif // STARBOARD_SHARED_STARBOARD_ONCE_TYPES_PUBLIC_H_
diff --git a/src/starboard/shared/starboard/player/closure.h b/src/starboard/shared/starboard/player/closure.h
new file mode 100644
index 0000000..96e2b4e
--- /dev/null
+++ b/src/starboard/shared/starboard/player/closure.h
@@ -0,0 +1,149 @@
+// 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 STARBOARD_SHARED_STARBOARD_PLAYER_CLOSURE_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_CLOSURE_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/log.h"
+#include "starboard/shared/internal_only.h"
+
+#ifndef __cplusplus
+#error "Only C++ files can include this header."
+#endif
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+class Closure {
+ public:
+ class Functor : public RefCountedThreadSafe<Functor> {
+ public:
+ virtual ~Functor() {}
+ virtual void Run() = 0;
+ };
+
+ explicit Closure(const scoped_refptr<Functor> functor = NULL)
+ : functor_(functor) {}
+
+ bool operator==(const Closure& that) const {
+ return functor_ == that.functor_;
+ }
+
+ bool is_valid() const { return functor_ != NULL; }
+ void reset() { functor_ = NULL; }
+ void Run() {
+ SB_DCHECK(functor_);
+ if (functor_) {
+ functor_->Run();
+ }
+ }
+
+ private:
+ scoped_refptr<Functor> functor_;
+};
+
+inline Closure Bind(void (*closure)()) {
+ class FunctorImpl : public Closure::Functor {
+ public:
+ explicit FunctorImpl(void (*closure)()) : closure_(closure) {}
+
+ void Run() SB_OVERRIDE { closure_(); }
+
+ private:
+ void (*closure_)();
+ };
+ return Closure(new FunctorImpl(closure));
+}
+
+template <typename Param>
+inline Closure Bind(void (*func)(Param), Param param) {
+ class FunctorImpl : public Closure::Functor {
+ public:
+ FunctorImpl(void (*func)(Param), Param param)
+ : func_(func), param_(param) {}
+
+ void Run() SB_OVERRIDE { func_(param_); }
+
+ private:
+ void (*func_)(Param);
+ Param param_;
+ };
+ return Closure(new FunctorImpl(func, param));
+}
+
+template <typename C>
+inline Closure Bind(void (C::*func)(), C* obj) {
+ class FunctorImpl : public Closure::Functor {
+ public:
+ FunctorImpl(void (C::*func)(), C* obj) : func_(func), obj_(obj) {}
+
+ void Run() SB_OVERRIDE { ((*obj_).*func_)(); }
+
+ private:
+ void (C::*func_)();
+ C* obj_;
+ };
+ return Closure(new FunctorImpl(func, obj));
+}
+
+template <typename C, typename Param>
+inline Closure Bind(void (C::*func)(Param), C* obj, Param param) {
+ class FunctorImpl : public Closure::Functor {
+ public:
+ FunctorImpl(void (C::*func)(Param), C* obj, Param param)
+ : func_(func), obj_(obj), param_(param) {}
+
+ void Run() SB_OVERRIDE { ((*obj_).*func_)(param_); }
+
+ private:
+ void (C::*func_)(Param);
+ C* obj_;
+ Param param_;
+ };
+ return Closure(new FunctorImpl(func, obj, param));
+}
+
+template <typename C, typename Param1, typename Param2>
+inline Closure Bind(void (C::*func)(Param1, Param2),
+ C* obj,
+ Param1 param1,
+ Param2 param2) {
+ class FunctorImpl : public Closure::Functor {
+ public:
+ FunctorImpl(void (C::*func)(Param1, Param2),
+ C* obj,
+ Param1 param1,
+ Param2 param2)
+ : func_(func), obj_(obj), param1_(param1), param2_(param2) {}
+
+ void Run() SB_OVERRIDE { ((*obj_).*func_)(param1_, param2_); }
+
+ private:
+ void (C::*func_)(Param1, Param2);
+ C* obj_;
+ Param1 param1_;
+ Param2 param2_;
+ };
+ return Closure(new FunctorImpl(func, obj, param1, param2));
+}
+
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_CLOSURE_H_
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.cc b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
new file mode 100644
index 0000000..1d53da4
--- /dev/null
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.cc
@@ -0,0 +1,34 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+
+#include "starboard/log.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+DecodedAudio::DecodedAudio() : pts_(0), size_(0) {}
+
+DecodedAudio::DecodedAudio(SbMediaTime pts, size_t size)
+ : pts_(pts), buffer_(new uint8_t[size]), size_(size) {
+ SB_DCHECK(size > 0) << size;
+}
+
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/decoded_audio_internal.h b/src/starboard/shared/starboard/player/decoded_audio_internal.h
new file mode 100644
index 0000000..40a12fe
--- /dev/null
+++ b/src/starboard/shared/starboard/player/decoded_audio_internal.h
@@ -0,0 +1,61 @@
+// 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 STARBOARD_SHARED_STARBOARD_PLAYER_DECODED_AUDIO_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_DECODED_AUDIO_INTERNAL_H_
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/media.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+// Decoded audio frames produced by an audio decoder. It can contain multiple
+// frames with continuous timestamps.
+// It doesn't have a specific storage type and sample type. The decoder and the
+// renderer will determinate the proper storage type and sample type in their
+// own way.
+class DecodedAudio : public RefCountedThreadSafe<DecodedAudio> {
+ public:
+ DecodedAudio(); // Signal an EOS.
+ DecodedAudio(SbMediaTime pts, size_t size);
+
+ bool is_end_of_stream() const { return buffer_ == NULL; }
+ SbMediaTime pts() const { return pts_; }
+ const uint8_t* buffer() const { return buffer_.get(); }
+ size_t size() const { return size_; }
+
+ uint8_t* buffer() { return buffer_.get(); }
+
+ private:
+ // The timestamp of the first audio frame.
+ SbMediaTime pts_;
+ // Use scoped_array<uint8_t> instead of std::vector<uint8_t> to avoid wasting
+ // time on setting content to 0.
+ scoped_array<uint8_t> buffer_;
+ size_t size_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(DecodedAudio);
+};
+
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_DECODED_AUDIO_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
index cb1850c..6fb5acc 100644
--- a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
@@ -19,6 +19,7 @@
#include "starboard/media.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
#include "starboard/types.h"
@@ -33,12 +34,17 @@
public:
virtual ~AudioDecoder() {}
- // Decode the encoded audio data stored in |input_buffer| and store the
- // result in |output|.
- virtual void Decode(const InputBuffer& input_buffer,
- std::vector<uint8_t>* output) = 0;
+ // Decode the encoded audio data stored in |input_buffer|.
+ virtual void Decode(const InputBuffer& input_buffer) = 0;
+
// Note that there won't be more input data unless Reset() is called.
virtual void WriteEndOfStream() = 0;
+
+ // Try to read the next decoded audio buffer. If there is no decoded audio
+ // available currently, it returns NULL. If the audio stream reaches EOS and
+ // there is no more decoded audio available, it returns an EOS buffer.
+ virtual scoped_refptr<DecodedAudio> Read() = 0;
+
// Clear any cached buffer of the codec and reset the state of the codec.
// This function will be called during seek to ensure that the left over
// data from previous buffers are cleared.
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
index 105e23f..4c3ea65 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
@@ -17,6 +17,7 @@
#include <algorithm>
#include "starboard/memory.h"
+#include "starboard/shared/starboard/player/closure.h"
namespace starboard {
namespace shared {
@@ -24,15 +25,11 @@
namespace player {
namespace filter {
-namespace {
-// TODO: This should be retrieved from the decoder.
-// TODO: Make it not dependent on the frame size of AAC and HE-AAC.
-const int kMaxFramesPerAccessUnit = 1024 * 2;
-} // namespace
-
-AudioRenderer::AudioRenderer(scoped_ptr<AudioDecoder> decoder,
+AudioRenderer::AudioRenderer(JobQueue* job_queue,
+ scoped_ptr<AudioDecoder> decoder,
const SbMediaAudioHeader& audio_header)
- : channels_(audio_header.number_of_channels),
+ : job_queue_(job_queue),
+ channels_(audio_header.number_of_channels),
bytes_per_frame_(
(decoder->GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4) *
channels_),
@@ -43,75 +40,59 @@
frames_in_buffer_(0),
offset_in_frames_(0),
frames_consumed_(0),
- end_of_stream_reached_(false),
+ end_of_stream_written_(false),
+ end_of_stream_decoded_(false),
decoder_(decoder.Pass()),
audio_sink_(kSbAudioSinkInvalid) {
+ SB_DCHECK(job_queue != NULL);
SB_DCHECK(decoder_ != NULL);
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
frame_buffers_[0] = &frame_buffer_[0];
}
AudioRenderer::~AudioRenderer() {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
if (audio_sink_ != kSbAudioSinkInvalid) {
SbAudioSinkDestroy(audio_sink_);
}
+
+ if (read_from_decoder_closure_.is_valid()) {
+ job_queue_->Remove(read_from_decoder_closure_);
+ }
}
void AudioRenderer::WriteSample(const InputBuffer& input_buffer) {
- if (end_of_stream_reached_) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+ if (end_of_stream_written_) {
SB_LOG(ERROR) << "Appending audio sample at " << input_buffer.pts()
<< " after EOS reached.";
return;
}
- SbMediaTime input_pts = input_buffer.pts();
- std::vector<uint8_t> decoded_audio;
- decoder_->Decode(input_buffer, &decoded_audio);
- if (decoded_audio.empty()) {
- SB_DLOG(ERROR) << "decoded_audio contains no frames.";
- return;
- }
+ decoder_->Decode(input_buffer);
- {
- ScopedLock lock(mutex_);
- if (seeking_) {
- if (input_pts < seeking_to_pts_) {
- return;
- }
- }
-
- AppendFrames(&decoded_audio[0], decoded_audio.size() / bytes_per_frame_);
-
- if (seeking_ && frame_buffer_.size() > kPrerollFrames * bytes_per_frame_) {
- seeking_ = false;
- }
- }
-
- // Create the audio sink if it is the first incoming AU after seeking.
- if (audio_sink_ == kSbAudioSinkInvalid) {
- int sample_rate = decoder_->GetSamplesPerSecond();
- // TODO: Implement resampler.
- SB_DCHECK(sample_rate ==
- SbAudioSinkGetNearestSupportedSampleFrequency(sample_rate));
- // TODO: Handle sink creation failure.
- audio_sink_ = SbAudioSinkCreate(
- channels_, sample_rate, decoder_->GetSampleType(),
- kSbMediaAudioFrameStorageTypeInterleaved,
- reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
- kMaxCachedFrames, &AudioRenderer::UpdateSourceStatusFunc,
- &AudioRenderer::ConsumeFramesFunc, this);
+ ScopedLock lock(mutex_);
+ if (!read_from_decoder_closure_.is_valid()) {
+ read_from_decoder_closure_ = Bind(&AudioRenderer::ReadFromDecoder, this);
+ job_queue_->Schedule(read_from_decoder_closure_);
}
}
void AudioRenderer::WriteEndOfStream() {
- SB_LOG_IF(WARNING, end_of_stream_reached_)
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+ SB_LOG_IF(WARNING, end_of_stream_written_)
<< "Try to write EOS after EOS is reached";
- if (end_of_stream_reached_) {
+ if (end_of_stream_written_) {
return;
}
- end_of_stream_reached_ = true;
decoder_->WriteEndOfStream();
ScopedLock lock(mutex_);
+ end_of_stream_written_ = true;
// If we are seeking, we consider the seek is finished if end of stream is
// reached as there won't be any audio data in future.
if (seeking_) {
@@ -120,19 +101,26 @@
}
void AudioRenderer::Play() {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
ScopedLock lock(mutex_);
paused_ = false;
}
void AudioRenderer::Pause() {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
ScopedLock lock(mutex_);
paused_ = true;
}
void AudioRenderer::Seek(SbMediaTime seek_to_pts) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
SB_DCHECK(seek_to_pts >= 0);
SbAudioSinkDestroy(audio_sink_);
+ // Now the sink is destroyed and the callbacks will no longer be called, so
+ // the following modifications are safe without lock.
audio_sink_ = kSbAudioSinkInvalid;
seeking_to_pts_ = std::max<SbMediaTime>(seek_to_pts, 0);
@@ -140,27 +128,38 @@
frames_in_buffer_ = 0;
offset_in_frames_ = 0;
frames_consumed_ = 0;
- end_of_stream_reached_ = false;
+ end_of_stream_written_ = false;
+ end_of_stream_decoded_ = false;
+ pending_decoded_audio_ = NULL;
decoder_->Reset();
- return;
}
bool AudioRenderer::IsEndOfStreamPlayed() const {
- return end_of_stream_reached_ && frames_in_buffer_ == 0;
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+ ScopedLock lock(mutex_);
+ return end_of_stream_decoded_ && frames_in_buffer_ == 0;
}
bool AudioRenderer::CanAcceptMoreData() const {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
ScopedLock lock(mutex_);
- return frames_in_buffer_ <= kMaxCachedFrames - kMaxFramesPerAccessUnit &&
- !end_of_stream_reached_;
+ if (end_of_stream_written_) {
+ return false;
+ }
+ return pending_decoded_audio_ == NULL;
}
bool AudioRenderer::IsSeekingInProgress() const {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
return seeking_;
}
SbMediaTime AudioRenderer::GetCurrentTime() {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
if (seeking_) {
return seeking_to_pts_;
}
@@ -169,38 +168,13 @@
decoder_->GetSamplesPerSecond();
}
-// static
-void AudioRenderer::UpdateSourceStatusFunc(int* frames_in_buffer,
- int* offset_in_frames,
- bool* is_playing,
- bool* is_eos_reached,
- void* context) {
- AudioRenderer* audio_renderer = reinterpret_cast<AudioRenderer*>(context);
- SB_DCHECK(audio_renderer);
- SB_DCHECK(frames_in_buffer);
- SB_DCHECK(offset_in_frames);
- SB_DCHECK(is_playing);
- SB_DCHECK(is_eos_reached);
-
- audio_renderer->UpdateSourceStatus(frames_in_buffer, offset_in_frames,
- is_playing, is_eos_reached);
-}
-
-// static
-void AudioRenderer::ConsumeFramesFunc(int frames_consumed, void* context) {
- AudioRenderer* audio_renderer = reinterpret_cast<AudioRenderer*>(context);
- SB_DCHECK(audio_renderer);
-
- audio_renderer->ConsumeFrames(frames_consumed);
-}
-
void AudioRenderer::UpdateSourceStatus(int* frames_in_buffer,
int* offset_in_frames,
bool* is_playing,
bool* is_eos_reached) {
ScopedLock lock(mutex_);
- *is_eos_reached = end_of_stream_reached_;
+ *is_eos_reached = end_of_stream_decoded_;
if (paused_ || seeking_) {
*is_playing = false;
@@ -221,11 +195,84 @@
offset_in_frames_ %= kMaxCachedFrames;
frames_in_buffer_ -= frames_consumed;
frames_consumed_ += frames_consumed;
+
+ bool decoded_audio_available =
+ pending_decoded_audio_ ||
+ (end_of_stream_written_ && !end_of_stream_decoded_);
+ if (decoded_audio_available && !read_from_decoder_closure_.is_valid()) {
+ read_from_decoder_closure_ = Bind(&AudioRenderer::ReadFromDecoder, this);
+ job_queue_->Schedule(read_from_decoder_closure_);
+ }
}
-void AudioRenderer::AppendFrames(const uint8_t* source_buffer,
- int frames_to_append) {
- SB_DCHECK(frames_in_buffer_ + frames_to_append <= kMaxCachedFrames);
+// Try to read some audio data from the decoder. Note that this operation is
+// valid across seeking. If a seek happens immediately after a ReadFromDecoder
+// request is scheduled, the seek will reset the decoder. So the
+// ReadFromDecoder request will not read stale data.
+void AudioRenderer::ReadFromDecoder() {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+ ScopedLock lock(mutex_);
+ SB_DCHECK(read_from_decoder_closure_.is_valid());
+ read_from_decoder_closure_.reset();
+
+ scoped_refptr<DecodedAudio> decoded_audio =
+ pending_decoded_audio_ ? pending_decoded_audio_ : decoder_->Read();
+ pending_decoded_audio_ = NULL;
+ if (!decoded_audio) {
+ return;
+ }
+
+ if (decoded_audio->is_end_of_stream()) {
+ SB_DCHECK(end_of_stream_written_);
+ end_of_stream_decoded_ = true;
+ return;
+ }
+
+ if (seeking_) {
+ if (decoded_audio->pts() < seeking_to_pts_) {
+ // Discard any audio data before the seeking target.
+ return;
+ }
+ }
+
+ if (!AppendDecodedAudio_Locked(decoded_audio)) {
+ pending_decoded_audio_ = decoded_audio;
+ return;
+ }
+
+ if (seeking_ && frame_buffer_.size() > kPrerollFrames * bytes_per_frame_) {
+ seeking_ = false;
+ }
+
+ // Create the audio sink if it is the first incoming AU after seeking.
+ if (audio_sink_ == kSbAudioSinkInvalid) {
+ int sample_rate = decoder_->GetSamplesPerSecond();
+ // TODO: Implement resampler.
+ SB_DCHECK(sample_rate ==
+ SbAudioSinkGetNearestSupportedSampleFrequency(sample_rate));
+ // TODO: Handle sink creation failure.
+ audio_sink_ = SbAudioSinkCreate(
+ channels_, sample_rate, decoder_->GetSampleType(),
+ kSbMediaAudioFrameStorageTypeInterleaved,
+ reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_),
+ kMaxCachedFrames, &AudioRenderer::UpdateSourceStatusFunc,
+ &AudioRenderer::ConsumeFramesFunc, this);
+ }
+}
+
+// TODO: This function should be executed when lock is not acquired as it copies
+// relatively large amount of data.
+bool AudioRenderer::AppendDecodedAudio_Locked(
+ const scoped_refptr<DecodedAudio>& decoded_audio) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+ const uint8_t* source_buffer = decoded_audio->buffer();
+ int frames_to_append = decoded_audio->size() / bytes_per_frame_;
+
+ if (frames_in_buffer_ + frames_to_append > kMaxCachedFrames) {
+ return false;
+ }
int offset_to_append =
(offset_in_frames_ + frames_in_buffer_) % kMaxCachedFrames;
@@ -241,6 +288,33 @@
SbMemoryCopy(&frame_buffer_[offset_to_append * bytes_per_frame_],
source_buffer, frames_to_append * bytes_per_frame_);
frames_in_buffer_ += frames_to_append;
+
+ return true;
+}
+
+// static
+void AudioRenderer::UpdateSourceStatusFunc(int* frames_in_buffer,
+ int* offset_in_frames,
+ bool* is_playing,
+ bool* is_eos_reached,
+ void* context) {
+ AudioRenderer* audio_renderer = static_cast<AudioRenderer*>(context);
+ SB_DCHECK(audio_renderer);
+ SB_DCHECK(frames_in_buffer);
+ SB_DCHECK(offset_in_frames);
+ SB_DCHECK(is_playing);
+ SB_DCHECK(is_eos_reached);
+
+ audio_renderer->UpdateSourceStatus(frames_in_buffer, offset_in_frames,
+ is_playing, is_eos_reached);
+}
+
+// static
+void AudioRenderer::ConsumeFramesFunc(int frames_consumed, void* context) {
+ AudioRenderer* audio_renderer = static_cast<AudioRenderer*>(context);
+ SB_DCHECK(audio_renderer);
+
+ audio_renderer->ConsumeFrames(frames_consumed);
}
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
index e14457c..745c4f0 100644
--- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -23,8 +23,11 @@
#include "starboard/media.h"
#include "starboard/mutex.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/closure.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
#include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
#include "starboard/types.h"
namespace starboard {
@@ -35,7 +38,8 @@
class AudioRenderer {
public:
- AudioRenderer(scoped_ptr<AudioDecoder> decoder,
+ AudioRenderer(JobQueue* job_queue,
+ scoped_ptr<AudioDecoder> decoder,
const SbMediaAudioHeader& audio_header);
~AudioRenderer();
@@ -48,7 +52,7 @@
void Pause();
void Seek(SbMediaTime seek_to_pts);
- bool IsEndOfStreamWritten() const { return end_of_stream_reached_; }
+ bool IsEndOfStreamWritten() const { return end_of_stream_written_; }
bool IsEndOfStreamPlayed() const;
bool CanAcceptMoreData() const;
bool IsSeekingInProgress() const;
@@ -64,6 +68,16 @@
// no longer accept more data.
static const size_t kMaxCachedFrames = 256 * 1024;
+ void UpdateSourceStatus(int* frames_in_buffer,
+ int* offset_in_frames,
+ bool* is_playing,
+ bool* is_eos_reached);
+ void ConsumeFrames(int frames_consumed);
+
+ void ReadFromDecoder();
+ bool AppendDecodedAudio_Locked(
+ const scoped_refptr<DecodedAudio>& decoded_audio);
+
// SbAudioSink callbacks
static void UpdateSourceStatusFunc(int* frames_in_buffer,
int* offset_in_frames,
@@ -71,14 +85,8 @@
bool* is_eos_reached,
void* context);
static void ConsumeFramesFunc(int frames_consumed, void* context);
- void UpdateSourceStatus(int* frames_in_buffer,
- int* offset_in_frames,
- bool* is_playing,
- bool* is_eos_reached);
- void ConsumeFrames(int frames_consumed);
- void AppendFrames(const uint8_t* source_buffer, int frames_to_append);
-
+ JobQueue* job_queue_;
const int channels_;
const int bytes_per_frame_;
@@ -93,10 +101,13 @@
int offset_in_frames_;
int frames_consumed_;
- bool end_of_stream_reached_;
+ bool end_of_stream_written_;
+ bool end_of_stream_decoded_;
scoped_ptr<AudioDecoder> decoder_;
SbAudioSink audio_sink_;
+ scoped_refptr<DecodedAudio> pending_decoded_audio_;
+ Closure read_from_decoder_closure_;
};
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index 973ed57..447b416 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -29,12 +29,20 @@
namespace player {
namespace filter {
+namespace {
+
+// TODO: Make this configurable inside SbPlayerCreate().
+const SbTimeMonotonic kUpdateInterval = 5 * kSbTimeMillisecond;
+
+} // namespace
+
FilterBasedPlayerWorkerHandler::FilterBasedPlayerWorkerHandler(
SbMediaVideoCodec video_codec,
SbMediaAudioCodec audio_codec,
SbDrmSystem drm_system,
const SbMediaAudioHeader& audio_header)
: player_worker_(NULL),
+ job_queue_(NULL),
player_(kSbPlayerInvalid),
update_media_time_cb_(NULL),
get_player_state_cb_(NULL),
@@ -43,10 +51,15 @@
audio_codec_(audio_codec),
drm_system_(drm_system),
audio_header_(audio_header),
- paused_(false) {}
+ paused_(false) {
+#if SB_IS(PLAYER_PUNCHED_OUT)
+ bounds_ = PlayerWorker::Bounds();
+#endif // SB_IS(PLAYER_PUNCHED_OUT)
+}
-void FilterBasedPlayerWorkerHandler::Setup(
+bool FilterBasedPlayerWorkerHandler::Init(
PlayerWorker* player_worker,
+ JobQueue* job_queue,
SbPlayer player,
UpdateMediaTimeCB update_media_time_cb,
GetPlayerStateCB get_player_state_cb,
@@ -56,19 +69,20 @@
// All parameters has to be valid.
SB_DCHECK(player_worker);
+ SB_DCHECK(job_queue);
+ SB_DCHECK(job_queue->BelongsToCurrentThread());
SB_DCHECK(SbPlayerIsValid(player));
SB_DCHECK(update_media_time_cb);
SB_DCHECK(get_player_state_cb);
SB_DCHECK(update_player_state_cb);
player_worker_ = player_worker;
+ job_queue_ = job_queue;
player_ = player;
update_media_time_cb_ = update_media_time_cb;
get_player_state_cb_ = get_player_state_cb;
update_player_state_cb_ = update_player_state_cb;
-}
-bool FilterBasedPlayerWorkerHandler::ProcessInitEvent() {
scoped_ptr<AudioDecoder> audio_decoder(
AudioDecoder::Create(audio_codec_, audio_header_));
scoped_ptr<VideoDecoder> video_decoder(VideoDecoder::Create(video_codec_));
@@ -77,9 +91,12 @@
return false;
}
- audio_renderer_.reset(new AudioRenderer(audio_decoder.Pass(), audio_header_));
+ audio_renderer_.reset(
+ new AudioRenderer(job_queue, audio_decoder.Pass(), audio_header_));
video_renderer_.reset(new VideoRenderer(video_decoder.Pass()));
if (audio_renderer_->is_valid() && video_renderer_->is_valid()) {
+ update_closure_ = Bind(&FilterBasedPlayerWorkerHandler::Update, this);
+ job_queue_->Schedule(update_closure_, kUpdateInterval);
return true;
}
@@ -88,9 +105,9 @@
return false;
}
-bool FilterBasedPlayerWorkerHandler::ProcessSeekEvent(
- const SeekEventData& data) {
- SbMediaTime seek_to_pts = data.seek_to_pts;
+bool FilterBasedPlayerWorkerHandler::Seek(SbMediaTime seek_to_pts, int ticket) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
if (seek_to_pts < 0) {
SB_DLOG(ERROR) << "Try to seek to negative timestamp " << seek_to_pts;
seek_to_pts = 0;
@@ -102,14 +119,14 @@
return true;
}
-bool FilterBasedPlayerWorkerHandler::ProcessWriteSampleEvent(
- const WriteSampleEventData& data,
- bool* written) {
+bool FilterBasedPlayerWorkerHandler::WriteSample(InputBuffer input_buffer,
+ bool* written) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
SB_DCHECK(written != NULL);
*written = true;
- if (data.sample_type == kSbMediaTypeAudio) {
+ if (input_buffer.sample_type() == kSbMediaTypeAudio) {
if (audio_renderer_->IsEndOfStreamWritten()) {
SB_LOG(WARNING) << "Try to write audio sample after EOS is reached";
} else {
@@ -118,20 +135,19 @@
return true;
}
- if (data.input_buffer->drm_info()) {
+ if (input_buffer.drm_info()) {
if (!SbDrmSystemIsValid(drm_system_)) {
return false;
}
- if (drm_system_->Decrypt(data.input_buffer) ==
- SbDrmSystemPrivate::kRetry) {
+ if (drm_system_->Decrypt(&input_buffer) == SbDrmSystemPrivate::kRetry) {
*written = false;
return true;
}
}
- audio_renderer_->WriteSample(*data.input_buffer);
+ audio_renderer_->WriteSample(input_buffer);
}
} else {
- SB_DCHECK(data.sample_type == kSbMediaTypeVideo);
+ SB_DCHECK(input_buffer.sample_type() == kSbMediaTypeVideo);
if (video_renderer_->IsEndOfStreamWritten()) {
SB_LOG(WARNING) << "Try to write video sample after EOS is reached";
} else {
@@ -139,26 +155,26 @@
*written = false;
return true;
}
- if (data.input_buffer->drm_info()) {
+ if (input_buffer.drm_info()) {
if (!SbDrmSystemIsValid(drm_system_)) {
return false;
}
- if (drm_system_->Decrypt(data.input_buffer) ==
- SbDrmSystemPrivate::kRetry) {
+ if (drm_system_->Decrypt(&input_buffer) == SbDrmSystemPrivate::kRetry) {
*written = false;
return true;
}
}
- video_renderer_->WriteSample(*data.input_buffer);
+ video_renderer_->WriteSample(input_buffer);
}
}
return true;
}
-bool FilterBasedPlayerWorkerHandler::ProcessWriteEndOfStreamEvent(
- const WriteEndOfStreamEventData& data) {
- if (data.stream_type == kSbMediaTypeAudio) {
+bool FilterBasedPlayerWorkerHandler::WriteEndOfStream(SbMediaType sample_type) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+ if (sample_type == kSbMediaTypeAudio) {
if (audio_renderer_->IsEndOfStreamWritten()) {
SB_LOG(WARNING) << "Try to write audio EOS after EOS is enqueued";
} else {
@@ -177,11 +193,12 @@
return true;
}
-bool FilterBasedPlayerWorkerHandler::ProcessSetPauseEvent(
- const SetPauseEventData& data) {
- paused_ = data.pause;
+bool FilterBasedPlayerWorkerHandler::SetPause(bool pause) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
- if (data.pause) {
+ paused_ = pause;
+
+ if (pause) {
audio_renderer_->Pause();
SB_DLOG(INFO) << "Playback paused.";
} else {
@@ -192,8 +209,26 @@
return true;
}
-bool FilterBasedPlayerWorkerHandler::ProcessUpdateEvent(
- const SetBoundsEventData& data) {
+#if SB_IS(PLAYER_PUNCHED_OUT)
+bool FilterBasedPlayerWorkerHandler::SetBounds(
+ const PlayerWorker::Bounds& bounds) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+ if (SbMemoryCompare(&bounds_, &bounds, sizeof(bounds_)) != 0) {
+ bounds_ = bounds;
+ // Force an update
+ job_queue_->Remove(update_closure_);
+ Update();
+ }
+
+ return true;
+}
+#endif // SB_IS(PLAYER_PUNCHED_OUT)
+
+// TODO: This should be driven by callbacks instead polling.
+void FilterBasedPlayerWorkerHandler::Update() {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
if ((*player_worker_.*get_player_state_cb_)() == kSbPlayerStatePrerolling) {
if (!audio_renderer_->IsSeekingInProgress() &&
!video_renderer_->IsSeekingInProgress()) {
@@ -217,7 +252,7 @@
#if SB_IS(PLAYER_PUNCHED_OUT)
shared::starboard::Application::Get()->HandleFrame(
- player_, frame, data.x, data.y, data.width, data.height);
+ player_, frame, bounds_.x, bounds_.y, bounds_.width, bounds_.height);
#endif // SB_IS(PLAYER_PUNCHED_OUT)
(*player_worker_.*update_media_time_cb_)(audio_renderer_->GetCurrentTime());
@@ -227,10 +262,12 @@
video_renderer_->Update();
}
- return true;
+ job_queue_->Schedule(update_closure_, kUpdateInterval);
}
-void FilterBasedPlayerWorkerHandler::ProcessStopEvent() {
+void FilterBasedPlayerWorkerHandler::Stop() {
+ job_queue_->Remove(update_closure_);
+
audio_renderer_.reset();
video_renderer_.reset();
diff --git a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index cde0391..370178d 100644
--- a/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/src/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -19,9 +19,11 @@
#include "starboard/configuration.h"
#include "starboard/media.h"
#include "starboard/player.h"
+#include "starboard/shared/starboard/player/closure.h"
#include "starboard/shared/starboard/player/filter/audio_renderer_internal.h"
#include "starboard/shared/starboard/player/filter/video_renderer_internal.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
#include "starboard/shared/starboard/player/player_worker.h"
#include "starboard/time.h"
@@ -39,22 +41,25 @@
const SbMediaAudioHeader& audio_header);
private:
- void Setup(PlayerWorker* player_worker,
- SbPlayer player,
- UpdateMediaTimeCB update_media_time_cb,
- GetPlayerStateCB get_player_state_cb,
- UpdatePlayerStateCB update_player_state_cb) SB_OVERRIDE;
- bool ProcessInitEvent() SB_OVERRIDE;
- bool ProcessSeekEvent(const SeekEventData& data) SB_OVERRIDE;
- bool ProcessWriteSampleEvent(const WriteSampleEventData& data,
- bool* written) SB_OVERRIDE;
- bool ProcessWriteEndOfStreamEvent(const WriteEndOfStreamEventData& data)
- SB_OVERRIDE;
- bool ProcessSetPauseEvent(const SetPauseEventData& data) SB_OVERRIDE;
- bool ProcessUpdateEvent(const SetBoundsEventData& data) SB_OVERRIDE;
- void ProcessStopEvent() SB_OVERRIDE;
+ bool Init(PlayerWorker* player_worker,
+ JobQueue* job_queue,
+ SbPlayer player,
+ UpdateMediaTimeCB update_media_time_cb,
+ GetPlayerStateCB get_player_state_cb,
+ UpdatePlayerStateCB update_player_state_cb) SB_OVERRIDE;
+ bool Seek(SbMediaTime seek_to_pts, int ticket) SB_OVERRIDE;
+ bool WriteSample(InputBuffer input_buffer, bool* written) SB_OVERRIDE;
+ bool WriteEndOfStream(SbMediaType sample_type) SB_OVERRIDE;
+ bool SetPause(bool pause) SB_OVERRIDE;
+#if SB_IS(PLAYER_PUNCHED_OUT)
+ bool SetBounds(const PlayerWorker::Bounds& bounds) SB_OVERRIDE;
+#endif // SB_IS(PLAYER_PUNCHED_OUT)
+ void Stop() SB_OVERRIDE;
+
+ void Update();
PlayerWorker* player_worker_;
+ JobQueue* job_queue_;
SbPlayer player_;
UpdateMediaTimeCB update_media_time_cb_;
GetPlayerStateCB get_player_state_cb_;
@@ -69,6 +74,10 @@
scoped_ptr<VideoRenderer> video_renderer_;
bool paused_;
+#if SB_IS(PLAYER_PUNCHED_OUT)
+ PlayerWorker::Bounds bounds_;
+#endif // SB_IS(PLAYER_PUNCHED_OUT)
+ Closure update_closure_;
};
} // namespace filter
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.cc b/src/starboard/shared/starboard/player/input_buffer_internal.cc
index 8431fc2..9794c14 100644
--- a/src/starboard/shared/starboard/player/input_buffer_internal.cc
+++ b/src/starboard/shared/starboard/player/input_buffer_internal.cc
@@ -1,196 +1,211 @@
-// 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 "starboard/shared/starboard/player/input_buffer_internal.h"
-
-#include <vector>
-
-#include "starboard/atomic.h"
-#include "starboard/log.h"
-#include "starboard/memory.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace player {
-
-class InputBuffer::ReferenceCountedBuffer {
- public:
- ReferenceCountedBuffer(SbPlayerDeallocateSampleFunc deallocate_sample_func,
- SbPlayer player,
- void* context,
- const void* sample_buffer,
- int sample_buffer_size,
- SbMediaTime sample_pts,
- const SbMediaVideoSampleInfo* video_sample_info,
- const SbDrmSampleInfo* sample_drm_info)
- : ref_count_(0),
- deallocate_sample_func_(deallocate_sample_func),
- player_(player),
- context_(context),
- data_(reinterpret_cast<const uint8_t*>(sample_buffer)),
- size_(sample_buffer_size),
- pts_(sample_pts),
- has_video_sample_info_(video_sample_info != NULL),
- has_drm_info_(sample_drm_info != NULL) {
- SB_DCHECK(deallocate_sample_func);
- if (has_video_sample_info_) {
- video_sample_info_ = *video_sample_info;
- }
- if (has_drm_info_) {
- SB_DCHECK(sample_drm_info->subsample_count > 0);
-
- subsamples_.assign(sample_drm_info->subsample_mapping,
- sample_drm_info->subsample_mapping +
- sample_drm_info->subsample_count);
- drm_info_ = *sample_drm_info;
- drm_info_.subsample_mapping =
- subsamples_.empty() ? NULL : &subsamples_[0];
- }
- }
-
- void AddRef() const { SbAtomicBarrier_Increment(&ref_count_, 1); }
- void Release() const {
- if (SbAtomicBarrier_Increment(&ref_count_, -1) == 0) {
- delete this;
- }
- }
-
- const uint8_t* data() const { return data_; }
- int size() const { return size_; }
- SbMediaTime pts() const { return pts_; }
- const SbMediaVideoSampleInfo* video_sample_info() const {
- return has_video_sample_info_ ? &video_sample_info_ : NULL;
- }
- const SbDrmSampleInfo* drm_info() const {
- return has_drm_info_ ? &drm_info_ : NULL;
- }
- void SetDecryptedContent(const void* buffer, int size) {
- SB_DCHECK(size == size_);
- SB_DCHECK(deallocate_sample_func_);
- DeallocateSampleBuffer();
-
- if (size > 0) {
- decrypted_data_.resize(size);
- SbMemoryCopy(&decrypted_data_[0], buffer, size);
- data_ = &decrypted_data_[0];
- } else {
- data_ = NULL;
- }
- size_ = size;
- has_drm_info_ = false;
- }
-
- private:
- ~ReferenceCountedBuffer() { DeallocateSampleBuffer(); }
-
- void DeallocateSampleBuffer() {
- if (deallocate_sample_func_) {
- deallocate_sample_func_(player_, context_, const_cast<uint8_t*>(data_));
- deallocate_sample_func_ = NULL;
- }
- }
-
- mutable SbAtomic32 ref_count_;
- SbPlayerDeallocateSampleFunc deallocate_sample_func_;
- SbPlayer player_;
- void* context_;
- const uint8_t* data_;
- int size_;
- SbMediaTime pts_;
- bool has_video_sample_info_;
- SbMediaVideoSampleInfo video_sample_info_;
- bool has_drm_info_;
- SbDrmSampleInfo drm_info_;
- std::vector<uint8_t> decrypted_data_;
- std::vector<SbDrmSubSampleMapping> subsamples_;
-
- SB_DISALLOW_COPY_AND_ASSIGN(ReferenceCountedBuffer);
-};
-
-InputBuffer::InputBuffer() : buffer_(NULL) {}
-
-InputBuffer::InputBuffer(SbPlayerDeallocateSampleFunc deallocate_sample_func,
- SbPlayer player,
- void* context,
- const void* sample_buffer,
- int sample_buffer_size,
- SbMediaTime sample_pts,
- const SbMediaVideoSampleInfo* video_sample_info,
- const SbDrmSampleInfo* sample_drm_info) {
- buffer_ = new ReferenceCountedBuffer(
- deallocate_sample_func, player, context, sample_buffer,
- sample_buffer_size, sample_pts, video_sample_info, sample_drm_info);
- buffer_->AddRef();
-}
-
-InputBuffer::InputBuffer(const InputBuffer& that) {
- buffer_ = that.buffer_;
- if (buffer_) {
- buffer_->AddRef();
- }
-}
-
-InputBuffer::~InputBuffer() {
- if (buffer_) {
- buffer_->Release();
- }
-}
-
-InputBuffer& InputBuffer::operator=(const InputBuffer& that) {
- if (that.buffer_) {
- that.buffer_->AddRef();
- }
- if (buffer_) {
- buffer_->Release();
- }
- buffer_ = that.buffer_;
-
- return *this;
-}
-
-const uint8_t* InputBuffer::data() const {
- SB_DCHECK(buffer_);
- return buffer_->data();
-}
-
-int InputBuffer::size() const {
- SB_DCHECK(buffer_);
- return buffer_->size();
-}
-
-SbMediaTime InputBuffer::pts() const {
- SB_DCHECK(buffer_);
- return buffer_->pts();
-}
-
-const SbMediaVideoSampleInfo* InputBuffer::video_sample_info() const {
- SB_DCHECK(buffer_);
- return buffer_->video_sample_info();
-}
-
-const SbDrmSampleInfo* InputBuffer::drm_info() const {
- SB_DCHECK(buffer_);
- return buffer_->drm_info();
-}
-
-void InputBuffer::SetDecryptedContent(const void* buffer, int size) {
- SB_DCHECK(buffer_);
- buffer_->SetDecryptedContent(buffer, size);
-}
-
-} // namespace player
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
+// 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 "starboard/shared/starboard/player/input_buffer_internal.h"
+
+#include <vector>
+
+#include "starboard/atomic.h"
+#include "starboard/log.h"
+#include "starboard/memory.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+class InputBuffer::ReferenceCountedBuffer {
+ public:
+ ReferenceCountedBuffer(SbMediaType sample_type,
+ SbPlayerDeallocateSampleFunc deallocate_sample_func,
+ SbPlayer player,
+ void* context,
+ const void* sample_buffer,
+ int sample_buffer_size,
+ SbMediaTime sample_pts,
+ const SbMediaVideoSampleInfo* video_sample_info,
+ const SbDrmSampleInfo* sample_drm_info)
+ : ref_count_(0),
+ sample_type_(sample_type),
+ deallocate_sample_func_(deallocate_sample_func),
+ player_(player),
+ context_(context),
+ data_(static_cast<const uint8_t*>(sample_buffer)),
+ size_(sample_buffer_size),
+ pts_(sample_pts),
+ has_video_sample_info_(video_sample_info != NULL),
+ has_drm_info_(sample_drm_info != NULL) {
+ SB_DCHECK(deallocate_sample_func);
+ if (has_video_sample_info_) {
+ video_sample_info_ = *video_sample_info;
+ }
+ if (has_drm_info_) {
+ SB_DCHECK(sample_drm_info->subsample_count > 0);
+
+ subsamples_.assign(sample_drm_info->subsample_mapping,
+ sample_drm_info->subsample_mapping +
+ sample_drm_info->subsample_count);
+ drm_info_ = *sample_drm_info;
+ drm_info_.subsample_mapping =
+ subsamples_.empty() ? NULL : &subsamples_[0];
+ }
+ }
+
+ void AddRef() const { SbAtomicBarrier_Increment(&ref_count_, 1); }
+ void Release() const {
+ if (SbAtomicBarrier_Increment(&ref_count_, -1) == 0) {
+ delete this;
+ }
+ }
+
+ SbMediaType sample_type() const { return sample_type_; }
+ const uint8_t* data() const { return data_; }
+ int size() const { return size_; }
+ SbMediaTime pts() const { return pts_; }
+ const SbMediaVideoSampleInfo* video_sample_info() const {
+ return has_video_sample_info_ ? &video_sample_info_ : NULL;
+ }
+ const SbDrmSampleInfo* drm_info() const {
+ return has_drm_info_ ? &drm_info_ : NULL;
+ }
+ void SetDecryptedContent(const void* buffer, int size) {
+ SB_DCHECK(size == size_);
+ SB_DCHECK(deallocate_sample_func_);
+ DeallocateSampleBuffer();
+
+ if (size > 0) {
+ decrypted_data_.resize(size);
+ SbMemoryCopy(&decrypted_data_[0], buffer, size);
+ data_ = &decrypted_data_[0];
+ } else {
+ data_ = NULL;
+ }
+ size_ = size;
+ has_drm_info_ = false;
+ }
+
+ private:
+ ~ReferenceCountedBuffer() { DeallocateSampleBuffer(); }
+
+ void DeallocateSampleBuffer() {
+ if (deallocate_sample_func_) {
+ deallocate_sample_func_(player_, context_, const_cast<uint8_t*>(data_));
+ deallocate_sample_func_ = NULL;
+ }
+ }
+
+ mutable SbAtomic32 ref_count_;
+ SbMediaType sample_type_;
+ SbPlayerDeallocateSampleFunc deallocate_sample_func_;
+ SbPlayer player_;
+ void* context_;
+ const uint8_t* data_;
+ int size_;
+ SbMediaTime pts_;
+ bool has_video_sample_info_;
+ SbMediaVideoSampleInfo video_sample_info_;
+ bool has_drm_info_;
+ SbDrmSampleInfo drm_info_;
+ std::vector<uint8_t> decrypted_data_;
+ std::vector<SbDrmSubSampleMapping> subsamples_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(ReferenceCountedBuffer);
+};
+
+InputBuffer::InputBuffer() : buffer_(NULL) {}
+
+InputBuffer::InputBuffer(SbMediaType sample_type,
+ SbPlayerDeallocateSampleFunc deallocate_sample_func,
+ SbPlayer player,
+ void* context,
+ const void* sample_buffer,
+ int sample_buffer_size,
+ SbMediaTime sample_pts,
+ const SbMediaVideoSampleInfo* video_sample_info,
+ const SbDrmSampleInfo* sample_drm_info) {
+ buffer_ = new ReferenceCountedBuffer(
+ sample_type, deallocate_sample_func, player, context, sample_buffer,
+ sample_buffer_size, sample_pts, video_sample_info, sample_drm_info);
+ buffer_->AddRef();
+}
+
+InputBuffer::InputBuffer(const InputBuffer& that) {
+ buffer_ = that.buffer_;
+ if (buffer_) {
+ buffer_->AddRef();
+ }
+}
+
+InputBuffer::~InputBuffer() {
+ reset();
+}
+
+InputBuffer& InputBuffer::operator=(const InputBuffer& that) {
+ if (that.buffer_) {
+ that.buffer_->AddRef();
+ }
+ if (buffer_) {
+ buffer_->Release();
+ }
+ buffer_ = that.buffer_;
+
+ return *this;
+}
+
+void InputBuffer::reset() {
+ if (buffer_) {
+ buffer_->Release();
+ buffer_ = NULL;
+ }
+}
+
+SbMediaType InputBuffer::sample_type() const {
+ SB_DCHECK(buffer_);
+ return buffer_->sample_type();
+}
+
+const uint8_t* InputBuffer::data() const {
+ SB_DCHECK(buffer_);
+ return buffer_->data();
+}
+
+int InputBuffer::size() const {
+ SB_DCHECK(buffer_);
+ return buffer_->size();
+}
+
+SbMediaTime InputBuffer::pts() const {
+ SB_DCHECK(buffer_);
+ return buffer_->pts();
+}
+
+const SbMediaVideoSampleInfo* InputBuffer::video_sample_info() const {
+ SB_DCHECK(buffer_);
+ return buffer_->video_sample_info();
+}
+
+const SbDrmSampleInfo* InputBuffer::drm_info() const {
+ SB_DCHECK(buffer_);
+ return buffer_->drm_info();
+}
+
+void InputBuffer::SetDecryptedContent(const void* buffer, int size) {
+ SB_DCHECK(buffer_);
+ buffer_->SetDecryptedContent(buffer, size);
+}
+
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/input_buffer_internal.h b/src/starboard/shared/starboard/player/input_buffer_internal.h
index 4a056b9..8f4e0ce 100644
--- a/src/starboard/shared/starboard/player/input_buffer_internal.h
+++ b/src/starboard/shared/starboard/player/input_buffer_internal.h
@@ -1,69 +1,72 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_
-#define STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_
-
-#include "starboard/drm.h"
-#include "starboard/media.h"
-#include "starboard/player.h"
-#include "starboard/shared/internal_only.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace player {
-
-// This class encapsulate a media buffer. It holds a reference-counted object
-// internally to ensure that the object of this class can be copied while the
-// underlying resources allocated is properly managed.
-// Note that pass this class as const reference doesn't guarantee that the
-// content of the internal buffer won't be changed.
-class InputBuffer {
- public:
- InputBuffer();
- InputBuffer(SbPlayerDeallocateSampleFunc deallocate_sample_func,
- SbPlayer player,
- void* context,
- const void* sample_buffer,
- int sample_buffer_size,
- SbMediaTime sample_pts,
- const SbMediaVideoSampleInfo* video_sample_info,
- const SbDrmSampleInfo* sample_drm_info);
- InputBuffer(const InputBuffer& that);
- ~InputBuffer();
-
- InputBuffer& operator=(const InputBuffer& that);
-
- bool valid() const { return buffer_ != NULL; }
-
- const uint8_t* data() const;
- int size() const;
- SbMediaTime pts() const;
- const SbMediaVideoSampleInfo* video_sample_info() const;
- const SbDrmSampleInfo* drm_info() const;
- void SetDecryptedContent(const void* buffer, int size);
-
- private:
- class ReferenceCountedBuffer;
-
- ReferenceCountedBuffer* buffer_;
-};
-
-} // namespace player
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_
+
+#include "starboard/drm.h"
+#include "starboard/media.h"
+#include "starboard/player.h"
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+// This class encapsulate a media buffer. It holds a reference-counted object
+// internally to ensure that the object of this class can be copied while the
+// underlying resources allocated is properly managed.
+// Note that pass this class as const reference doesn't guarantee that the
+// content of the internal buffer won't be changed.
+class InputBuffer {
+ public:
+ InputBuffer();
+ InputBuffer(SbMediaType sample_type,
+ SbPlayerDeallocateSampleFunc deallocate_sample_func,
+ SbPlayer player,
+ void* context,
+ const void* sample_buffer,
+ int sample_buffer_size,
+ SbMediaTime sample_pts,
+ const SbMediaVideoSampleInfo* video_sample_info,
+ const SbDrmSampleInfo* sample_drm_info);
+ InputBuffer(const InputBuffer& that);
+ ~InputBuffer();
+
+ InputBuffer& operator=(const InputBuffer& that);
+
+ bool is_valid() const { return buffer_ != NULL; }
+ void reset();
+
+ SbMediaType sample_type() const;
+ const uint8_t* data() const;
+ int size() const;
+ SbMediaTime pts() const;
+ const SbMediaVideoSampleInfo* video_sample_info() const;
+ const SbDrmSampleInfo* drm_info() const;
+ void SetDecryptedContent(const void* buffer, int size);
+
+ private:
+ class ReferenceCountedBuffer;
+
+ ReferenceCountedBuffer* buffer_;
+};
+
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_INPUT_BUFFER_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/player/job_queue.cc b/src/starboard/shared/starboard/player/job_queue.cc
new file mode 100644
index 0000000..254378d
--- /dev/null
+++ b/src/starboard/shared/starboard/player/job_queue.cc
@@ -0,0 +1,208 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/job_queue.h"
+
+#include <utility>
+
+#include "starboard/log.h"
+#include "starboard/once.h"
+#include "starboard/thread.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+namespace {
+
+SbOnceControl s_once_flag = SB_ONCE_INITIALIZER;
+SbThreadLocalKey s_thread_local_key = kSbThreadLocalKeyInvalid;
+
+void InitThreadLocalKey() {
+ s_thread_local_key = SbThreadCreateLocalKey(NULL);
+ SB_DCHECK(SbThreadIsValidLocalKey(s_thread_local_key));
+}
+
+void EnsureThreadLocalKeyInited() {
+ SbOnce(&s_once_flag, InitThreadLocalKey);
+ SB_DCHECK(SbThreadIsValidLocalKey(s_thread_local_key));
+}
+
+JobQueue* GetCurrentThreadJobQueue() {
+ EnsureThreadLocalKeyInited();
+ return static_cast<JobQueue*>(SbThreadGetLocalValue(s_thread_local_key));
+}
+
+void SetCurrentThreadJobQueue(JobQueue* job_queue) {
+ SB_DCHECK(job_queue != NULL);
+ SB_DCHECK(GetCurrentThreadJobQueue() == NULL);
+
+ EnsureThreadLocalKeyInited();
+ SbThreadSetLocalValue(s_thread_local_key, job_queue);
+}
+
+void ResetCurrentThreadJobQueue() {
+ SB_DCHECK(GetCurrentThreadJobQueue());
+
+ EnsureThreadLocalKeyInited();
+ SbThreadSetLocalValue(s_thread_local_key, NULL);
+}
+
+} // namespace
+
+JobQueue::JobQueue() : thread_id_(SbThreadGetId()), stopped_(false) {
+ SB_DCHECK(SbThreadIsValidId(thread_id_));
+ SetCurrentThreadJobQueue(this);
+}
+
+JobQueue::~JobQueue() {
+ SB_DCHECK(BelongsToCurrentThread());
+ StopSoon();
+ ResetCurrentThreadJobQueue();
+}
+
+void JobQueue::Schedule(Closure job, SbTimeMonotonic delay /*= 0*/) {
+ SB_DCHECK(job.is_valid());
+ SB_DCHECK(delay >= 0) << delay;
+
+ ScopedLock scoped_lock(mutex_);
+ if (stopped_) {
+ return;
+ }
+ if (delay <= 0) {
+ queue_.Put(job);
+ return;
+ }
+ SbTimeMonotonic time_to_run_job = SbTimeGetMonotonicNow() + delay;
+ time_to_job_map_.insert(std::make_pair(time_to_run_job, job));
+ if (time_to_job_map_.begin()->second == job) {
+ queue_.Wake();
+ }
+}
+
+void JobQueue::Remove(Closure job) {
+ SB_DCHECK(BelongsToCurrentThread());
+ SB_DCHECK(job.is_valid());
+
+ ScopedLock scoped_lock(mutex_);
+ queue_.Remove(job);
+ // std::multimap::erase() doesn't return an iterator until C++11. So this has
+ // to be done in a nested loop to delete multiple occurrences of |job|.
+ bool should_keep_running = true;
+ while (should_keep_running) {
+ should_keep_running = false;
+ for (TimeToJobMap::iterator iter = time_to_job_map_.begin();
+ iter != time_to_job_map_.end(); ++iter) {
+ if (iter->second == job) {
+ time_to_job_map_.erase(iter);
+ should_keep_running = true;
+ break;
+ }
+ }
+ }
+}
+
+void JobQueue::StopSoon() {
+ {
+ ScopedLock scoped_lock(mutex_);
+ stopped_ = true;
+ }
+
+ queue_.Wake();
+
+ for (;;) {
+ Closure job = queue_.Poll();
+ if (!job.is_valid()) {
+ break;
+ }
+ }
+ time_to_job_map_.clear();
+}
+
+void JobQueue::RunUntilStopped() {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ for (;;) {
+ {
+ ScopedLock scoped_lock(mutex_);
+ if (stopped_) {
+ return;
+ }
+ }
+ TryToRunOneJob(/*wait_for_next_job =*/true);
+ }
+}
+
+void JobQueue::RunUntilIdle() {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ while (TryToRunOneJob(/*wait_for_next_job =*/false)) {
+ }
+}
+
+bool JobQueue::BelongsToCurrentThread() const {
+ // The ctor already ensures that the current JobQueue is the only JobQueue of
+ // the thread, checking for thread id is more light-weighted then calling
+ // JobQueue::current() and compare the result with |this|.
+ return thread_id_ == SbThreadGetId();
+}
+
+// static
+JobQueue* JobQueue::current() {
+ return GetCurrentThreadJobQueue();
+}
+
+bool JobQueue::TryToRunOneJob(bool wait_for_next_job) {
+ SB_DCHECK(BelongsToCurrentThread());
+
+ Closure job;
+ // |kSbTimeMax| makes more sense here, but |kSbTimeDay| is much safer.
+ SbTimeMonotonic delay = kSbTimeDay;
+
+ {
+ ScopedLock scoped_lock(mutex_);
+ if (stopped_) {
+ return false;
+ }
+ if (!time_to_job_map_.empty()) {
+ TimeToJobMap::iterator first_delayed_job = time_to_job_map_.begin();
+ delay = first_delayed_job->first - SbTimeGetMonotonicNow();
+ if (delay <= 0) {
+ job = first_delayed_job->second;
+ time_to_job_map_.erase(first_delayed_job);
+ }
+ }
+ }
+
+ if (!job.is_valid()) {
+ if (wait_for_next_job) {
+ job = queue_.GetTimed(delay);
+ } else {
+ job = queue_.Poll();
+ }
+ }
+
+ if (!job.is_valid()) {
+ return false;
+ }
+
+ job.Run();
+ return true;
+}
+
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/starboard/player/job_queue.h b/src/starboard/shared/starboard/player/job_queue.h
new file mode 100644
index 0000000..36100ab
--- /dev/null
+++ b/src/starboard/shared/starboard/player/job_queue.h
@@ -0,0 +1,81 @@
+// 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 STARBOARD_SHARED_STARBOARD_PLAYER_JOB_QUEUE_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_JOB_QUEUE_H_
+
+#include <map>
+
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/condition_variable.h"
+#include "starboard/mutex.h"
+#include "starboard/queue.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/closure.h"
+#include "starboard/time.h"
+
+#ifndef __cplusplus
+#error "Only C++ files can include this header."
+#endif
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+// This class implements a job queue where closures can be posted to it on any
+// thread and will be processed on one thread that this job queue is linked to.
+// A thread can only have one job queue.
+class JobQueue {
+ public:
+ JobQueue();
+ ~JobQueue();
+
+ void Schedule(Closure job, SbTimeMonotonic delay = 0);
+ void Remove(Closure job);
+
+ // The processing of jobs may not be stopped when this function returns, but
+ // it is guaranteed that the processing will be stopped very soon. So it is
+ // safe to join the thread after this call returns.
+ void StopSoon();
+
+ void RunUntilStopped();
+ void RunUntilIdle();
+
+ bool BelongsToCurrentThread() const;
+ static JobQueue* current();
+
+ private:
+ typedef std::multimap<SbTimeMonotonic, Closure> TimeToJobMap;
+
+ // Return true if a job is run, otherwise return false. When there is no job
+ // ready to run currently and |wait_for_next_job| is true, the function will
+ // wait to until a job is available or if the |queue_| is woken up. Note that
+ // set |wait_for_next_job| to true doesn't guarantee that one job will always
+ // be run.
+ bool TryToRunOneJob(bool wait_for_next_job);
+
+ SbThreadId thread_id_;
+ Mutex mutex_;
+ Queue<Closure> queue_;
+ TimeToJobMap time_to_job_map_;
+ bool stopped_;
+};
+
+} // namespace player
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_STARBOARD_PLAYER_JOB_QUEUE_H_
diff --git a/src/starboard/shared/starboard/player/player_internal.cc b/src/starboard/shared/starboard/player/player_internal.cc
index 35726f1..9c8a25b 100644
--- a/src/starboard/shared/starboard/player/player_internal.cc
+++ b/src/starboard/shared/starboard/player/player_internal.cc
@@ -20,8 +20,6 @@
namespace {
-const SbTime kUpdateInterval = kSbTimeMillisecond;
-
SbMediaTime GetMediaTime(SbMediaTime media_pts,
SbTimeMonotonic media_pts_update_time) {
SbTimeMonotonic elapsed = SbTimeGetMonotonicNow() - media_pts_update_time;
@@ -54,7 +52,6 @@
decoder_status_func,
player_status_func,
this,
- kUpdateInterval,
context)) {}
void SbPlayerPrivate::Seek(SbMediaTime seek_to_pts, int ticket) {
@@ -66,8 +63,7 @@
ticket_ = ticket;
}
- PlayerWorker::SeekEventData data = {seek_to_pts, ticket};
- worker_->EnqueueEvent(data);
+ worker_->Seek(seek_to_pts, ticket);
}
void SbPlayerPrivate::WriteSample(
@@ -80,22 +76,20 @@
if (sample_type == kSbMediaTypeVideo) {
++total_video_frames_;
}
- InputBuffer* input_buffer = new InputBuffer(
- sample_deallocate_func_, this, context_, sample_buffer,
- sample_buffer_size, sample_pts, video_sample_info, sample_drm_info);
- PlayerWorker::WriteSampleEventData data = {sample_type, input_buffer};
- worker_->EnqueueEvent(data);
+ InputBuffer input_buffer(sample_type, sample_deallocate_func_, this, context_,
+ sample_buffer, sample_buffer_size, sample_pts,
+ video_sample_info, sample_drm_info);
+ worker_->WriteSample(input_buffer);
}
void SbPlayerPrivate::WriteEndOfStream(SbMediaType stream_type) {
- PlayerWorker::WriteEndOfStreamEventData data = {stream_type};
- worker_->EnqueueEvent(data);
+ worker_->WriteEndOfStream(stream_type);
}
#if SB_IS(PLAYER_PUNCHED_OUT)
void SbPlayerPrivate::SetBounds(int x, int y, int width, int height) {
- PlayerWorker::SetBoundsEventData data = {x, y, width, height};
- worker_->EnqueueEvent(data);
+ PlayerWorker::Bounds bounds = {x, y, width, height};
+ worker_->SetBounds(bounds);
// TODO: Wait until a frame is rendered with the updated bounds.
}
#endif
@@ -121,8 +115,7 @@
}
void SbPlayerPrivate::SetPause(bool pause) {
- PlayerWorker::SetPauseEventData data = {pause};
- worker_->EnqueueEvent(data);
+ worker_->SetPause(pause);
}
void SbPlayerPrivate::SetVolume(double volume) {
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc
index 5b36cb6..61155d6 100644
--- a/src/starboard/shared/starboard/player/player_worker.cc
+++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -15,19 +15,32 @@
#include "starboard/shared/starboard/player/player_worker.h"
#include "starboard/common/reset_and_return.h"
+#include "starboard/condition_variable.h"
#include "starboard/memory.h"
+#include "starboard/mutex.h"
namespace starboard {
namespace shared {
namespace starboard {
namespace player {
+namespace {
+
+struct ThreadParam {
+ explicit ThreadParam(PlayerWorker* player_worker)
+ : condition_variable(mutex), player_worker(player_worker) {}
+ Mutex mutex;
+ ConditionVariable condition_variable;
+ PlayerWorker* player_worker;
+};
+
+} // namespace
+
PlayerWorker::PlayerWorker(Host* host,
scoped_ptr<Handler> handler,
SbPlayerDecoderStatusFunc decoder_status_func,
SbPlayerStatusFunc player_status_func,
SbPlayer player,
- SbTime update_interval,
void* context)
: thread_(kSbThreadInvalid),
host_(host),
@@ -37,51 +50,30 @@
player_(player),
context_(context),
ticket_(SB_PLAYER_INITIAL_TICKET),
- player_state_(kSbPlayerStateInitialized),
- update_interval_(update_interval) {
+ player_state_(kSbPlayerStateInitialized) {
SB_DCHECK(host_ != NULL);
SB_DCHECK(handler_ != NULL);
- SB_DCHECK(update_interval_ > 0);
- handler_->Setup(this, player_, &PlayerWorker::UpdateMediaTime,
- &PlayerWorker::player_state,
- &PlayerWorker::UpdatePlayerState);
-
- pending_audio_sample_.input_buffer = NULL;
- pending_video_sample_.input_buffer = NULL;
-
- thread_ =
- SbThreadCreate(0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true,
- "player_worker", &PlayerWorker::ThreadEntryPoint, this);
+ ThreadParam thread_param(this);
+ thread_ = SbThreadCreate(0, kSbThreadPriorityHigh, kSbThreadNoAffinity, true,
+ "player_worker", &PlayerWorker::ThreadEntryPoint,
+ &thread_param);
SB_DCHECK(SbThreadIsValid(thread_));
-
- queue_.Put(new Event(Event::kInit));
+ ScopedLock scoped_lock(thread_param.mutex);
+ while (!job_queue_) {
+ thread_param.condition_variable.Wait();
+ }
+ SB_DCHECK(job_queue_);
}
PlayerWorker::~PlayerWorker() {
- queue_.Put(new Event(Event::kStop));
+ job_queue_->Schedule(Bind(&PlayerWorker::DoStop, this));
SbThreadJoin(thread_, NULL);
thread_ = kSbThreadInvalid;
// Now the whole pipeline has been torn down and no callback will be called.
// The caller can ensure that upon the return of SbPlayerDestroy() all side
// effects are gone.
-
- // There can be events inside the queue that is not processed. Clean them up.
- while (Event* event = queue_.GetTimed(0)) {
- if (event->type == Event::kWriteSample) {
- delete event->data.write_sample.input_buffer;
- }
- delete event;
- }
-
- if (pending_audio_sample_.input_buffer != NULL) {
- delete pending_audio_sample_.input_buffer;
- }
-
- if (pending_video_sample_.input_buffer != NULL) {
- delete pending_video_sample_.input_buffer;
- }
}
void PlayerWorker::UpdateMediaTime(SbMediaTime time) {
@@ -89,6 +81,8 @@
}
void PlayerWorker::UpdatePlayerState(SbPlayerState player_state) {
+ SB_DLOG_IF(WARNING, player_state == kSbPlayerStateError)
+ << "encountered kSbPlayerStateError";
player_state_ = player_state;
if (!player_status_func_) {
@@ -100,130 +94,121 @@
// static
void* PlayerWorker::ThreadEntryPoint(void* context) {
- PlayerWorker* player_worker = reinterpret_cast<PlayerWorker*>(context);
- player_worker->RunLoop();
+ ThreadParam* param = static_cast<ThreadParam*>(context);
+ SB_DCHECK(param != NULL);
+ {
+ ScopedLock scoped_lock(param->mutex);
+ param->player_worker->job_queue_.reset(new JobQueue);
+ param->condition_variable.Signal();
+ }
+ param->player_worker->RunLoop();
return NULL;
}
void PlayerWorker::RunLoop() {
- Event* event = queue_.Get();
- SB_DCHECK(event != NULL);
- SB_DCHECK(event->type == Event::kInit);
- delete event;
- bool running = DoInit();
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
- SetBoundsEventData bounds = {0, 0, 0, 0};
-
- while (running) {
- Event* event = queue_.GetTimed(update_interval_);
- if (event == NULL) {
- running &= DoUpdate(bounds);
- continue;
- }
-
- SB_DCHECK(event->type != Event::kInit);
-
- if (event->type == Event::kSeek) {
- running &= DoSeek(event->data.seek);
- } else if (event->type == Event::kWriteSample) {
- running &= DoWriteSample(event->data.write_sample);
- } else if (event->type == Event::kWriteEndOfStream) {
- running &= DoWriteEndOfStream(event->data.write_end_of_stream);
- } else if (event->type == Event::kSetPause) {
- running &= handler_->ProcessSetPauseEvent(event->data.set_pause);
- } else if (event->type == Event::kSetBounds) {
- if (SbMemoryCompare(&bounds, &event->data.set_bounds, sizeof(bounds)) !=
- 0) {
- bounds = event->data.set_bounds;
- running &= DoUpdate(bounds);
- }
- } else if (event->type == Event::kStop) {
- DoStop();
- running = false;
- } else {
- SB_NOTREACHED() << "event type " << event->type;
- }
- delete event;
- running &= DoUpdate(bounds);
- }
+ DoInit();
+ job_queue_->RunUntilStopped();
+ job_queue_.reset();
}
-bool PlayerWorker::DoInit() {
- if (handler_->ProcessInitEvent()) {
+void PlayerWorker::DoInit() {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+ if (handler_->Init(
+ this, job_queue_.get(), player_, &PlayerWorker::UpdateMediaTime,
+ &PlayerWorker::player_state, &PlayerWorker::UpdatePlayerState)) {
UpdatePlayerState(kSbPlayerStateInitialized);
- return true;
+ } else {
+ UpdatePlayerState(kSbPlayerStateError);
}
-
- UpdatePlayerState(kSbPlayerStateError);
- return false;
}
-bool PlayerWorker::DoSeek(const SeekEventData& data) {
+void PlayerWorker::DoSeek(SbMediaTime seek_to_pts, int ticket) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
SB_DCHECK(player_state_ != kSbPlayerStateDestroyed);
SB_DCHECK(player_state_ != kSbPlayerStateError);
- SB_DCHECK(ticket_ != data.ticket);
+ SB_DCHECK(ticket_ != ticket);
SB_DLOG(INFO) << "Try to seek to timestamp "
- << data.seek_to_pts / kSbMediaTimeSecond;
+ << seek_to_pts / kSbMediaTimeSecond;
- if (pending_audio_sample_.input_buffer != NULL) {
- delete pending_audio_sample_.input_buffer;
- pending_audio_sample_.input_buffer = NULL;
+ if (write_pending_sample_closure_.is_valid()) {
+ job_queue_->Remove(write_pending_sample_closure_);
+ write_pending_sample_closure_.reset();
}
- if (pending_video_sample_.input_buffer != NULL) {
- delete pending_video_sample_.input_buffer;
- pending_video_sample_.input_buffer = NULL;
+ pending_audio_buffer_.reset();
+ pending_video_buffer_.reset();
+
+ if (!handler_->Seek(seek_to_pts, ticket)) {
+ UpdatePlayerState(kSbPlayerStateError);
+ return;
}
- if (!handler_->ProcessSeekEvent(data)) {
- return false;
- }
-
- ticket_ = data.ticket;
+ ticket_ = ticket;
UpdatePlayerState(kSbPlayerStatePrerolling);
UpdateDecoderState(kSbMediaTypeAudio, kSbPlayerDecoderStateNeedsData);
UpdateDecoderState(kSbMediaTypeVideo, kSbPlayerDecoderStateNeedsData);
-
- return true;
}
-bool PlayerWorker::DoWriteSample(const WriteSampleEventData& data) {
+void PlayerWorker::DoWriteSample(InputBuffer input_buffer) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
if (player_state_ == kSbPlayerStateInitialized ||
player_state_ == kSbPlayerStateEndOfStream ||
player_state_ == kSbPlayerStateDestroyed ||
player_state_ == kSbPlayerStateError) {
SB_LOG(ERROR) << "Try to write sample when |player_state_| is "
<< player_state_;
- delete data.input_buffer;
- // Return true so the pipeline will continue running with the particular
- // call ignored.
- return true;
+ return;
}
- if (data.sample_type == kSbMediaTypeAudio) {
- SB_DCHECK(pending_audio_sample_.input_buffer == NULL);
+ if (input_buffer.sample_type() == kSbMediaTypeAudio) {
+ SB_DCHECK(!pending_audio_buffer_.is_valid());
} else {
- SB_DCHECK(pending_video_sample_.input_buffer == NULL);
+ SB_DCHECK(!pending_video_buffer_.is_valid());
}
bool written;
- bool result = handler_->ProcessWriteSampleEvent(data, &written);
- if (!written && result) {
- if (data.sample_type == kSbMediaTypeAudio) {
- pending_audio_sample_ = data;
- } else {
- pending_video_sample_ = data;
- }
+ bool result = handler_->WriteSample(input_buffer, &written);
+ if (!result) {
+ UpdatePlayerState(kSbPlayerStateError);
+ return;
+ }
+ if (written) {
+ UpdateDecoderState(input_buffer.sample_type(),
+ kSbPlayerDecoderStateNeedsData);
} else {
- delete data.input_buffer;
- if (result) {
- UpdateDecoderState(data.sample_type, kSbPlayerDecoderStateNeedsData);
+ if (input_buffer.sample_type() == kSbMediaTypeAudio) {
+ pending_audio_buffer_ = input_buffer;
+ } else {
+ pending_video_buffer_ = input_buffer;
+ }
+ if (!write_pending_sample_closure_.is_valid()) {
+ write_pending_sample_closure_ =
+ Bind(&PlayerWorker::DoWritePendingSamples, this);
+ job_queue_->Schedule(write_pending_sample_closure_);
}
}
- return result;
}
-bool PlayerWorker::DoWriteEndOfStream(const WriteEndOfStreamEventData& data) {
+void PlayerWorker::DoWritePendingSamples() {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ SB_DCHECK(write_pending_sample_closure_.is_valid());
+ write_pending_sample_closure_.reset();
+
+ if (pending_audio_buffer_.is_valid()) {
+ DoWriteSample(common::ResetAndReturn(&pending_audio_buffer_));
+ }
+ if (pending_video_buffer_.is_valid()) {
+ DoWriteSample(common::ResetAndReturn(&pending_video_buffer_));
+ }
+}
+
+void PlayerWorker::DoWriteEndOfStream(SbMediaType sample_type) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
SB_DCHECK(player_state_ != kSbPlayerStateDestroyed);
if (player_state_ == kSbPlayerStateInitialized ||
@@ -233,40 +218,43 @@
<< player_state_;
// Return true so the pipeline will continue running with the particular
// call ignored.
- return true;
+ return;
}
- if (data.stream_type == kSbMediaTypeAudio) {
- SB_DCHECK(pending_audio_sample_.input_buffer == NULL);
+ if (sample_type == kSbMediaTypeAudio) {
+ SB_DCHECK(!pending_audio_buffer_.is_valid());
} else {
- SB_DCHECK(pending_video_sample_.input_buffer == NULL);
+ SB_DCHECK(!pending_video_buffer_.is_valid());
}
- if (!handler_->ProcessWriteEndOfStreamEvent(data)) {
- return false;
+ if (!handler_->WriteEndOfStream(sample_type)) {
+ UpdatePlayerState(kSbPlayerStateError);
}
-
- return true;
}
-bool PlayerWorker::DoUpdate(const SetBoundsEventData& data) {
- if (pending_audio_sample_.input_buffer != NULL) {
- if (!DoWriteSample(common::ResetAndReturn(&pending_audio_sample_))) {
- return false;
- }
+#if SB_IS(PLAYER_PUNCHED_OUT)
+void PlayerWorker::DoSetBounds(Bounds bounds) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ if (!handler_->SetBounds(bounds)) {
+ UpdatePlayerState(kSbPlayerStateError);
}
- if (pending_video_sample_.input_buffer != NULL) {
- if (!DoWriteSample(common::ResetAndReturn(&pending_video_sample_))) {
- return false;
- }
+}
+#endif // SB_IS(PLAYER_PUNCHED_OUT)
+
+void PlayerWorker::DoSetPause(bool pause) {
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+
+ if (!handler_->SetPause(pause)) {
+ UpdatePlayerState(kSbPlayerStateError);
}
- return handler_->ProcessUpdateEvent(data);
}
void PlayerWorker::DoStop() {
- handler_->ProcessStopEvent();
+ SB_DCHECK(job_queue_->BelongsToCurrentThread());
+ handler_->Stop();
UpdatePlayerState(kSbPlayerStateDestroyed);
+ job_queue_->StopSoon();
}
void PlayerWorker::UpdateDecoderState(SbMediaType type,
diff --git a/src/starboard/shared/starboard/player/player_worker.h b/src/starboard/shared/starboard/player/player_worker.h
index 95ca389..ccddf39 100644
--- a/src/starboard/shared/starboard/player/player_worker.h
+++ b/src/starboard/shared/starboard/player/player_worker.h
@@ -19,9 +19,10 @@
#include "starboard/log.h"
#include "starboard/media.h"
#include "starboard/player.h"
-#include "starboard/queue.h"
#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/closure.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
#include "starboard/thread.h"
#include "starboard/time.h"
#include "starboard/window.h"
@@ -48,84 +49,14 @@
~Host() {}
};
- struct SeekEventData {
- SbMediaTime seek_to_pts;
- int ticket;
- };
-
- struct WriteSampleEventData {
- SbMediaType sample_type;
- InputBuffer* input_buffer;
- };
-
- struct WriteEndOfStreamEventData {
- SbMediaType stream_type;
- };
-
- struct SetPauseEventData {
- bool pause;
- };
-
- struct SetBoundsEventData {
+ struct Bounds {
int x;
int y;
int width;
int height;
};
- struct Event {
- public:
- enum Type {
- kInit,
- kSeek,
- kWriteSample,
- kWriteEndOfStream,
- kSetPause,
- kSetBounds,
- kStop,
- };
-
- // TODO: No longer use a union so individual members can have non trivial
- // ctor and WriteSampleEventData::input_buffer no longer has to be a
- // pointer.
- union Data {
- SeekEventData seek;
- WriteSampleEventData write_sample;
- WriteEndOfStreamEventData write_end_of_stream;
- SetPauseEventData set_pause;
- SetBoundsEventData set_bounds;
- };
-
- explicit Event(const SeekEventData& seek) : type(kSeek) {
- data.seek = seek;
- }
-
- explicit Event(const WriteSampleEventData& write_sample)
- : type(kWriteSample) {
- data.write_sample = write_sample;
- }
-
- explicit Event(const WriteEndOfStreamEventData& write_end_of_stream)
- : type(kWriteEndOfStream) {
- data.write_end_of_stream = write_end_of_stream;
- }
-
- explicit Event(const SetPauseEventData& set_pause) : type(kSetPause) {
- data.set_pause = set_pause;
- }
-
- explicit Event(const SetBoundsEventData& set_bounds) : type(kSetBounds) {
- data.set_bounds = set_bounds;
- }
-
- explicit Event(Type type) : type(type) {
- SB_DCHECK(type == kInit || type == kStop);
- }
-
- Type type;
- Data data;
- };
-
+ // All functions of this class will be called from the JobQueue thread.
class Handler {
public:
typedef void (PlayerWorker::*UpdateMediaTimeCB)(SbMediaTime media_time);
@@ -133,33 +64,27 @@
typedef void (PlayerWorker::*UpdatePlayerStateCB)(
SbPlayerState player_state);
- typedef PlayerWorker::SeekEventData SeekEventData;
- typedef PlayerWorker::WriteSampleEventData WriteSampleEventData;
- typedef PlayerWorker::WriteEndOfStreamEventData WriteEndOfStreamEventData;
- typedef PlayerWorker::SetPauseEventData SetPauseEventData;
- typedef PlayerWorker::SetBoundsEventData SetBoundsEventData;
-
virtual ~Handler() {}
- // This function will be called once inside PlayerWorker's ctor to setup the
- // callbacks required by the Handler.
- virtual void Setup(PlayerWorker* player_worker,
- SbPlayer player,
- UpdateMediaTimeCB update_media_time_cb,
- GetPlayerStateCB get_player_state_cb,
- UpdatePlayerStateCB update_player_state_cb) = 0;
-
// All the Process* functions return false to signal a fatal error. The
// event processing loop in PlayerWorker will termimate in this case.
- virtual bool ProcessInitEvent() = 0;
- virtual bool ProcessSeekEvent(const SeekEventData& data) = 0;
- virtual bool ProcessWriteSampleEvent(const WriteSampleEventData& data,
- bool* written) = 0;
- virtual bool ProcessWriteEndOfStreamEvent(
- const WriteEndOfStreamEventData& data) = 0;
- virtual bool ProcessSetPauseEvent(const SetPauseEventData& data) = 0;
- virtual bool ProcessUpdateEvent(const SetBoundsEventData& data) = 0;
- virtual void ProcessStopEvent() = 0;
+ // All function returns false on error.
+ virtual bool Init(PlayerWorker* player_worker,
+ JobQueue* job_queue,
+ SbPlayer player,
+ UpdateMediaTimeCB update_media_time_cb,
+ GetPlayerStateCB get_player_state_cb,
+ UpdatePlayerStateCB update_player_state_cb) = 0;
+ virtual bool Seek(SbMediaTime seek_to_pts, int ticket) = 0;
+ virtual bool WriteSample(InputBuffer input_buffer, bool* written) = 0;
+ virtual bool WriteEndOfStream(SbMediaType sample_type) = 0;
+ virtual bool SetPause(bool pause) = 0;
+ virtual bool SetBounds(const Bounds& bounds) = 0;
+
+ // Once this function returns, all processing on the Handler and related
+ // objects has to be stopped. The JobQueue will be destroyed immediately
+ // after and is no longer safe to access.
+ virtual void Stop() = 0;
};
PlayerWorker(Host* host,
@@ -167,14 +92,32 @@
SbPlayerDecoderStatusFunc decoder_status_func,
SbPlayerStatusFunc player_status_func,
SbPlayer player,
- SbTime update_interval,
void* context);
- virtual ~PlayerWorker();
+ ~PlayerWorker();
- template <typename EventData>
- void EnqueueEvent(const EventData& event_data) {
- SB_DCHECK(SbThreadIsValid(thread_));
- queue_.Put(new Event(event_data));
+ void Seek(SbMediaTime seek_to_pts, int ticket) {
+ job_queue_->Schedule(
+ Bind(&PlayerWorker::DoSeek, this, seek_to_pts, ticket));
+ }
+
+ void WriteSample(InputBuffer input_buffer) {
+ job_queue_->Schedule(
+ Bind(&PlayerWorker::DoWriteSample, this, input_buffer));
+ }
+
+ void WriteEndOfStream(SbMediaType sample_type) {
+ job_queue_->Schedule(
+ Bind(&PlayerWorker::DoWriteEndOfStream, this, sample_type));
+ }
+
+#if SB_IS(PLAYER_PUNCHED_OUT)
+ void SetBounds(Bounds bounds) {
+ job_queue_->Schedule(Bind(&PlayerWorker::DoSetBounds, this, bounds));
+ }
+#endif // SB_IS(PLAYER_PUNCHED_OUT)
+
+ void SetPause(bool pause) {
+ job_queue_->Schedule(Bind(&PlayerWorker::DoSetPause, this, pause));
}
void UpdateDroppedVideoFrames(int dropped_video_frames) {
@@ -189,17 +132,21 @@
static void* ThreadEntryPoint(void* context);
void RunLoop();
- bool DoInit();
- bool DoSeek(const SeekEventData& data);
- bool DoWriteSample(const WriteSampleEventData& data);
- bool DoWriteEndOfStream(const WriteEndOfStreamEventData& data);
- bool DoUpdate(const SetBoundsEventData& data);
+ void DoInit();
+ void DoSeek(SbMediaTime seek_to_pts, int ticket);
+ void DoWriteSample(InputBuffer input_buffer);
+ void DoWritePendingSamples();
+ void DoWriteEndOfStream(SbMediaType sample_type);
+#if SB_IS(PLAYER_PUNCHED_OUT)
+ void DoSetBounds(Bounds bounds);
+#endif // SB_IS(PLAYER_PUNCHED_OUT)
+ void DoSetPause(bool pause);
void DoStop();
void UpdateDecoderState(SbMediaType type, SbPlayerDecoderState state);
SbThread thread_;
- Queue<Event*> queue_;
+ scoped_ptr<JobQueue> job_queue_;
Host* host_;
scoped_ptr<Handler> handler_;
@@ -211,9 +158,9 @@
int ticket_;
SbPlayerState player_state_;
- const SbTime update_interval_;
- WriteSampleEventData pending_audio_sample_;
- WriteSampleEventData pending_video_sample_;
+ InputBuffer pending_audio_buffer_;
+ InputBuffer pending_video_buffer_;
+ Closure write_pending_sample_closure_;
};
} // namespace player
diff --git a/src/starboard/shared/starboard/player/video_frame_internal.cc b/src/starboard/shared/starboard/player/video_frame_internal.cc
index d8dd571..69818c1 100644
--- a/src/starboard/shared/starboard/player/video_frame_internal.cc
+++ b/src/starboard/shared/starboard/player/video_frame_internal.cc
@@ -1,254 +1,254 @@
-// 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 "starboard/shared/starboard/player/video_frame_internal.h"
-
-#include "starboard/log.h"
-#include "starboard/memory.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace player {
-
-namespace {
-
-bool s_yuv_to_rgb_lookup_table_initialized = false;
-int s_y_to_rgb[256];
-int s_v_to_r[256];
-int s_u_to_g[256];
-int s_v_to_g[256];
-int s_u_to_b[256];
-uint8_t s_clamp_table[256 * 5];
-
-void EnsureYUVToRGBLookupTableInitialized() {
- if (s_yuv_to_rgb_lookup_table_initialized) {
- return;
- }
-
- // The YUV to RGBA conversion is based on
- // http://www.equasys.de/colorconversion.html.
- // The formula is:
- // r = 1.164f * y + 1.793f * (v - 128);
- // g = 1.164f * y - 0.213f * (u - 128) - 0.533f * (v - 128);
- // b = 1.164f * y + 2.112f * (u - 128);
- // And r/g/b has to be clamped to [0, 255].
- //
- // We optimize the conversion algorithm by creating two kinds of lookup
- // tables. The color component table contains pre-calculated color component
- // values. The clamp table contains a map between |v| + 512 to the clamped
- // |v| to avoid conditional operation.
- // The minimum value of |v| can be 2.112f * (-128) = -271, the maximum value
- // of |v| can be 1.164f * 255 + 2.112f * 127 = 565. So we need 512 bytes at
- // each side of the clamp buffer.
- SbMemorySet(s_clamp_table, 0, 512);
- SbMemorySet(s_clamp_table + 768, 0xff, 512);
-
- for (int i = 0; i < 256; ++i) {
- s_y_to_rgb[i] = (static_cast<uint8_t>(i) - 16) * 1.164f;
- s_v_to_r[i] = (static_cast<uint8_t>(i) - 128) * 1.793f;
- s_u_to_g[i] = (static_cast<uint8_t>(i) - 128) * -0.213;
- s_v_to_g[i] = (static_cast<uint8_t>(i) - 128) * -0.533f;
- s_u_to_b[i] = (static_cast<uint8_t>(i) - 128) * 2.112f;
- s_clamp_table[512 + i] = i;
- }
-
- s_yuv_to_rgb_lookup_table_initialized = true;
-}
-
-uint8_t ClampColorComponent(int component) {
- return s_clamp_table[component + 512];
-}
-
-} // namespace
-
-VideoFrame::VideoFrame() {
- InitializeToInvalidFrame();
-}
-
-VideoFrame::VideoFrame(int width,
- int height,
- SbMediaTime pts,
- void* native_texture,
- void* native_texture_context,
- FreeNativeTextureFunc free_native_texture_func) {
- SB_DCHECK(native_texture != NULL);
- SB_DCHECK(free_native_texture_func != NULL);
-
- InitializeToInvalidFrame();
-
- format_ = kNativeTexture;
- width_ = width;
- height_ = height;
- pts_ = pts;
- native_texture_ = native_texture;
- native_texture_context_ = native_texture_context;
- free_native_texture_func_ = free_native_texture_func;
-}
-
-VideoFrame::~VideoFrame() {
- if (format_ == kNativeTexture) {
- free_native_texture_func_(native_texture_context_, native_texture_);
- }
-}
-
-int VideoFrame::GetPlaneCount() const {
- SB_DCHECK(format_ != kInvalid);
- SB_DCHECK(format_ != kNativeTexture);
-
- return static_cast<int>(planes_.size());
-}
-
-const VideoFrame::Plane& VideoFrame::GetPlane(int index) const {
- SB_DCHECK(format_ != kInvalid);
- SB_DCHECK(format_ != kNativeTexture);
- SB_DCHECK(index >= 0 && index < GetPlaneCount()) << "Invalid index: "
- << index;
- return planes_[index];
-}
-
-void* VideoFrame::native_texture() const {
- SB_DCHECK(format_ == kNativeTexture);
- return native_texture_;
-}
-
-scoped_refptr<VideoFrame> VideoFrame::ConvertTo(Format target_format) const {
- SB_DCHECK(format_ == kYV12);
- SB_DCHECK(target_format == kBGRA32);
-
- EnsureYUVToRGBLookupTableInitialized();
-
- scoped_refptr<VideoFrame> target_frame(new VideoFrame);
-
- target_frame->format_ = target_format;
- target_frame->width_ = width();
- target_frame->height_ = height();
- target_frame->pts_ = pts_;
- target_frame->pixel_buffer_.reset(new uint8_t[width() * height() * 4]);
- target_frame->planes_.push_back(
- Plane(width(), height(), width() * 4, target_frame->pixel_buffer_.get()));
-
- const uint8_t* y_data = GetPlane(0).data;
- const uint8_t* u_data = GetPlane(1).data;
- const uint8_t* v_data = GetPlane(2).data;
- uint8_t* bgra_data = target_frame->pixel_buffer_.get();
-
- int height = this->height();
- int width = this->width();
-
- for (int row = 0; row < height; ++row) {
- const uint8_t* y = &y_data[row * GetPlane(0).pitch_in_bytes];
- const uint8_t* u = &u_data[row / 2 * GetPlane(1).pitch_in_bytes];
- const uint8_t* v = &v_data[row / 2 * GetPlane(2).pitch_in_bytes];
- int v_to_r, u_to_g, v_to_g, u_to_b;
-
- for (int column = 0; column < width; ++column) {
- if (column % 2 == 0) {
- v_to_r = s_v_to_r[*v];
- u_to_g = s_u_to_g[*u];
- v_to_g = s_v_to_g[*v];
- u_to_b = s_u_to_b[*u];
- } else {
- ++u, ++v;
- }
-
- int y_to_rgb = s_y_to_rgb[*y];
- int r = y_to_rgb + v_to_r;
- int g = y_to_rgb + u_to_g + v_to_g;
- int b = y_to_rgb + u_to_b;
-
- *bgra_data++ = ClampColorComponent(b);
- *bgra_data++ = ClampColorComponent(g);
- *bgra_data++ = ClampColorComponent(r);
- *bgra_data++ = 0xff;
-
- ++y;
- }
- }
-
- return target_frame;
-}
-
-// static
-scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
- return new VideoFrame;
-}
-
-// static
-scoped_refptr<VideoFrame> VideoFrame::CreateYV12Frame(int width,
- int height,
- int pitch_in_bytes,
- SbMediaTime pts,
- const uint8_t* y,
- const uint8_t* u,
- const uint8_t* v) {
- scoped_refptr<VideoFrame> frame(new VideoFrame);
- frame->format_ = kYV12;
- frame->width_ = width;
- frame->height_ = height;
- frame->pts_ = pts;
-
- // U/V planes generally have half resolution of the Y plane. However, in the
- // extreme case that any dimension of Y plane is odd, we want to have an
- // extra pixel on U/V planes.
- int uv_height = height / 2 + height % 2;
- int uv_width = width / 2 + width % 2;
- int uv_pitch_in_bytes = pitch_in_bytes / 2 + pitch_in_bytes % 2;
-
- int y_plane_size_in_bytes = height * pitch_in_bytes;
- int uv_plane_size_in_bytes = uv_height * uv_pitch_in_bytes;
- frame->pixel_buffer_.reset(
- new uint8_t[y_plane_size_in_bytes + uv_plane_size_in_bytes * 2]);
- SbMemoryCopy(frame->pixel_buffer_.get(), y, y_plane_size_in_bytes);
- SbMemoryCopy(frame->pixel_buffer_.get() + y_plane_size_in_bytes, u,
- uv_plane_size_in_bytes);
- SbMemoryCopy(frame->pixel_buffer_.get() + y_plane_size_in_bytes +
- uv_plane_size_in_bytes,
- v, uv_plane_size_in_bytes);
-
- frame->planes_.push_back(
- Plane(width, height, pitch_in_bytes, frame->pixel_buffer_.get()));
- frame->planes_.push_back(
- Plane(uv_width, uv_height, uv_pitch_in_bytes,
- frame->pixel_buffer_.get() + y_plane_size_in_bytes));
- frame->planes_.push_back(Plane(uv_width, uv_height, uv_pitch_in_bytes,
- frame->pixel_buffer_.get() +
- y_plane_size_in_bytes +
- uv_plane_size_in_bytes));
- return frame;
-}
-