Import Cobalt 23.master.0.308777
diff --git a/cobalt/BUILD.gn b/cobalt/BUILD.gn
index d025533..e9c9fcb 100644
--- a/cobalt/BUILD.gn
+++ b/cobalt/BUILD.gn
@@ -47,6 +47,7 @@
if (sb_is_evergreen) {
deps += [
+ "//cobalt/updater:updater_test",
"//components/update_client:cobalt_slot_management_test",
"//components/update_client:update_client_test",
]
diff --git a/cobalt/base/BUILD.gn b/cobalt/base/BUILD.gn
index 1ba068f..898475d 100644
--- a/cobalt/base/BUILD.gn
+++ b/cobalt/base/BUILD.gn
@@ -72,6 +72,7 @@
"source_location.h",
"startup_timer.cc",
"startup_timer.h",
+ "statistics.h",
"stop_watch.cc",
"stop_watch.h",
"token.cc",
@@ -113,6 +114,7 @@
"c_val_time_interval_timer_stats_test.cc",
"circular_buffer_shell_unittest.cc",
"fixed_size_lru_cache_test.cc",
+ "statistics_test.cc",
"token_test.cc",
]
deps = [
diff --git a/cobalt/base/statistics.h b/cobalt/base/statistics.h
new file mode 100644
index 0000000..90dbe3e
--- /dev/null
+++ b/cobalt/base/statistics.h
@@ -0,0 +1,183 @@
+// Copyright 2022 The Cobalt Authors. 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_BASE_STATISTICS_H_
+#define COBALT_BASE_STATISTICS_H_
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+#include "starboard/types.h"
+
+namespace base {
+
+namespace internal {
+
+// The default function to convert a sample to its value by dividing.
+inline int64_t DefaultSampleToValueFunc(int64_t dividend, int64_t divisor) {
+ if (divisor == 0) {
+ divisor = 1;
+ }
+ return dividend / divisor;
+}
+
+} // namespace internal
+
+// Track the statistics of a series of generic samples reprensented as
+// dividends and divisors in integer types. The value of a sample is calculated
+// by `SampleToValueFunc` using its dividend and divisor.
+//
+// Example usages:
+// 1. Set dividends to bytes in int and divisors to SbTime, to track the
+// statistics of bandwidth.
+// 2. Set the divisor to always be 1, to track the statistics of a count or a
+// duration.
+//
+// Currently the class can produce the following statistics:
+// 1. Average, tracked across all samples.
+// 2. Median, tracked across the most recent |MaxSamples| samples.
+// 3. Min, tracked across all samples.
+// 4. Max, tracked across all samples.
+//
+// Notes:
+// 1. The accumulated values are stored in `int64_t`, and it is the user's
+// responsibility to avoid overflow.
+// 2. The class isn't multi-thread safe, and it is the user's responsibility
+// to synchronize the usage when necessary.
+// 3. Both the dividend and divisor of the samples should ideally be positive,
+// which is NOT enforced by the class.
+
+#if defined(COBALT_BUILD_TYPE_GOLD)
+
+template <typename DividendType, typename DivisorType, int MaxSamples,
+ int64_t (*SampleToValueFunc)(int64_t, int64_t) =
+ internal::DefaultSampleToValueFunc>
+class Statistics {
+ public:
+ constexpr Statistics() = default;
+
+ void AddSample(DividendType dividend, DivisorType divisor) {}
+ int64_t accumulated_dividend() const { return 0; }
+ int64_t accumulated_divisor() const { return 1; }
+ int64_t average() const { return 0; }
+ int64_t min() const { return 0; }
+ int64_t max() const { return 0; }
+
+ int64_t GetMedian() const { return 0; }
+};
+
+#else // defined(COBALT_BUILD_TYPE_GOLD)
+
+template <typename DividendType, typename DivisorType, size_t MaxSamples,
+ int64_t (*SampleToValueFunc)(int64_t, int64_t) =
+ internal::DefaultSampleToValueFunc>
+class Statistics {
+ public:
+ constexpr Statistics() = default;
+
+ void AddSample(DividendType dividend, DivisorType divisor) {
+ static_assert(MaxSamples > 0, "MaxSamples has to be greater than 0.");
+
+ auto& current =
+ samples_[(first_sample_index_ + number_of_samples_) % MaxSamples];
+ if (number_of_samples_ == MaxSamples) {
+ first_sample_index_ = (first_sample_index_ + 1) % MaxSamples;
+ } else {
+ ++number_of_samples_;
+ }
+
+ current.dividend = dividend;
+ current.divisor = divisor;
+ accumulated_dividend_ += dividend;
+ accumulated_divisor_ += divisor;
+
+ auto value = GetSampleValue(current);
+ if (first_sample_added_) {
+ min_ = std::min(min_, value);
+ max_ = std::max(max_, value);
+ } else {
+ min_ = max_ = value;
+ first_sample_added_ = true;
+ }
+ }
+
+ int64_t accumulated_dividend() const { return accumulated_dividend_; }
+ int64_t accumulated_divisor() const { return accumulated_divisor_; }
+
+ int64_t average() const {
+ return SampleToValueFunc(accumulated_dividend_, accumulated_divisor_);
+ }
+ int64_t min() const { return min_; }
+ int64_t max() const { return max_; }
+
+ // When there are even number of samples in the object, it is implementation
+ // specific to pick any number close to the middle, or the median of the
+ // numbers close to the middle. Use {1, 2, 3, 4} as an example, the median
+ // can be 3, or 4, or 3.5.
+ int64_t GetMedian() const {
+ if (number_of_samples_ == 0) {
+ return 0;
+ }
+
+ std::vector<Sample> copy;
+ copy.reserve(number_of_samples_);
+ if (first_sample_index_ + number_of_samples_ <= MaxSamples) {
+ copy.assign(samples_ + first_sample_index_,
+ samples_ + first_sample_index_ + number_of_samples_);
+ } else {
+ auto samples_to_copy = number_of_samples_;
+ copy.assign(samples_ + first_sample_index_, samples_ + MaxSamples);
+ samples_to_copy -= MaxSamples - first_sample_index_;
+ copy.insert(copy.end(), samples_, samples_ + samples_to_copy);
+ }
+
+ std::nth_element(copy.begin(), copy.begin() + number_of_samples_ / 2,
+ copy.end(), [](const Sample& left, const Sample& right) {
+ return GetSampleValue(left) < GetSampleValue(right);
+ });
+ return GetSampleValue(copy[number_of_samples_ / 2]);
+ }
+
+ private:
+ Statistics(const Statistics&) = delete;
+ Statistics& operator=(const Statistics&) = delete;
+
+ struct Sample {
+ DividendType dividend = 0;
+ DivisorType divisor = 0;
+ };
+
+ static int64_t GetSampleValue(const Sample& sample) {
+ return SampleToValueFunc(static_cast<int64_t>(sample.dividend),
+ static_cast<int64_t>(sample.divisor));
+ }
+
+ bool first_sample_added_ = false;
+
+ int64_t accumulated_dividend_ = 0;
+ int64_t accumulated_divisor_ = 0;
+ int64_t min_ = 0;
+ int64_t max_ = 0;
+
+ Sample samples_[MaxSamples] = {}; // Ring buffer for samples.
+ size_t number_of_samples_ = 0; // Number of samples in |samples_|.
+ size_t first_sample_index_ = 0; // Index of the first sample in |samples_|.
+};
+
+#endif // defined(COBALT_BUILD_TYPE_GOLD)
+
+} // namespace base
+
+#endif // COBALT_BASE_STATISTICS_H_
diff --git a/cobalt/base/statistics_test.cc b/cobalt/base/statistics_test.cc
new file mode 100644
index 0000000..beefdb9
--- /dev/null
+++ b/cobalt/base/statistics_test.cc
@@ -0,0 +1,165 @@
+// Copyright 2022 The Cobalt Authors. 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/base/statistics.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(StatisticsTest, ZeroSamples) {
+ Statistics<int, int, 1> statistics;
+
+ EXPECT_EQ(statistics.accumulated_dividend(), 0);
+ EXPECT_EQ(statistics.accumulated_divisor(), 0);
+ EXPECT_EQ(statistics.average(), 0);
+ EXPECT_EQ(statistics.min(), 0);
+ EXPECT_EQ(statistics.max(), 0);
+ EXPECT_EQ(statistics.GetMedian(), 0);
+}
+
+TEST(StatisticsTest, SingleSample) {
+ Statistics<int, int, 1> statistics;
+
+ statistics.AddSample(100, 10);
+
+ EXPECT_EQ(statistics.accumulated_dividend(), 100);
+ EXPECT_EQ(statistics.accumulated_divisor(), 10);
+ EXPECT_EQ(statistics.average(), 10);
+ EXPECT_EQ(statistics.min(), 10);
+ EXPECT_EQ(statistics.max(), 10);
+ EXPECT_EQ(statistics.GetMedian(), 10);
+}
+
+TEST(StatisticsTest, NotCrashOnZeroDivisor) {
+ Statistics<int, int, 4> statistics;
+
+ statistics.AddSample(100, 0);
+
+ EXPECT_EQ(statistics.accumulated_dividend(), 100);
+ EXPECT_EQ(statistics.accumulated_divisor(), 0);
+ // The only expectation for the following calls is that they don't crash.
+ statistics.average();
+ statistics.min();
+ statistics.max();
+ statistics.GetMedian();
+}
+
+TEST(StatisticsTest, MultipleSamples) {
+ Statistics<int, int, 4> statistics;
+
+ statistics.AddSample(10, 2);
+ statistics.AddSample(20, 2);
+ statistics.AddSample(30, 2);
+ EXPECT_EQ(statistics.accumulated_dividend(), 10 + 20 + 30);
+ EXPECT_EQ(statistics.accumulated_divisor(), 6);
+ EXPECT_EQ(statistics.average(), (10 + 20 + 30) / 6);
+ EXPECT_EQ(statistics.min(), 10 / 2);
+ EXPECT_EQ(statistics.max(), 30 / 2);
+ EXPECT_EQ(statistics.GetMedian(), 20 / 2);
+
+ statistics.AddSample(40, 2);
+ EXPECT_EQ(statistics.accumulated_dividend(), 10 + 20 + 30 + 40);
+ EXPECT_EQ(statistics.accumulated_divisor(), 8);
+ EXPECT_EQ(statistics.average(), (10 + 20 + 30 + 40) / 8);
+ EXPECT_EQ(statistics.min(), 10 / 2);
+ EXPECT_EQ(statistics.max(), 40 / 2);
+ // The median can be in the range of either number close to the middle, and is
+ // implementation specific.
+ EXPECT_GE(statistics.GetMedian(), 30 / 2);
+ EXPECT_LE(statistics.GetMedian(), 40 / 2);
+}
+
+TEST(StatisticsTest, MultipleSamplesDifferentOrders) {
+ Statistics<int, int, 10> statistics;
+ Statistics<int, int, 10> statistics_reversed;
+
+ for (int i = 0; i < 10; ++i) {
+ statistics.AddSample(i * i, i * 3);
+ }
+
+ for (int i = 9; i >= 0; --i) {
+ statistics_reversed.AddSample(i * i, i * 3);
+ }
+
+ EXPECT_EQ(statistics.accumulated_dividend(),
+ statistics_reversed.accumulated_dividend());
+ EXPECT_EQ(statistics.accumulated_divisor(),
+ statistics_reversed.accumulated_divisor());
+ EXPECT_EQ(statistics.average(), statistics_reversed.average());
+ EXPECT_EQ(statistics.min(), statistics_reversed.min());
+ EXPECT_EQ(statistics.max(), statistics_reversed.max());
+ EXPECT_EQ(statistics.GetMedian(), statistics_reversed.GetMedian());
+}
+
+TEST(StatisticsTest, MoreSamplesThanCapacity) {
+ Statistics<int, int, 1> statistics_1;
+ Statistics<int, int, 10> statistics_10;
+
+ for (int i = 0; i < 5; ++i) {
+ statistics_1.AddSample(i * i, i * 3);
+ statistics_10.AddSample(i * i, i * 3);
+ }
+
+ Statistics<int, int, 1> statistics_1_median_reference;
+ Statistics<int, int, 10> statistics_10_median_reference;
+
+ for (int i = 0; i < 10; ++i) {
+ statistics_1.AddSample(i * i * i, i * 5);
+ statistics_10.AddSample(i * i * i, i * 5);
+ statistics_1_median_reference.AddSample(i * i * i, i * 5);
+ statistics_10_median_reference.AddSample(i * i * i, i * 5);
+ }
+
+ // All statistics except median are tracked over all samples added, median is
+ // tracked on the samples cached, so:
+ // 1. All statistics except median are the same over `statistics_1` and
+ // `statistics_10`.
+ // 2. Medians of `statistics_1` and `statistics_1_median_reference` are the
+ // same, and the same to `statistics_10` and
+ // `statistics_10_median_reference`.
+ EXPECT_EQ(statistics_1.accumulated_dividend(),
+ statistics_10.accumulated_dividend());
+ EXPECT_EQ(statistics_1.accumulated_divisor(),
+ statistics_10.accumulated_divisor());
+ EXPECT_EQ(statistics_1.average(), statistics_10.average());
+ EXPECT_EQ(statistics_1.min(), statistics_10.min());
+ EXPECT_EQ(statistics_1.max(), statistics_10.max());
+
+ EXPECT_EQ(statistics_1.GetMedian(),
+ statistics_1_median_reference.GetMedian());
+ EXPECT_EQ(statistics_10.GetMedian(),
+ statistics_10_median_reference.GetMedian());
+}
+
+TEST(StatisticsTest, MedianWithOverflow) {
+ Statistics<int, int, 3> statistics;
+
+ statistics.AddSample(1, 1);
+ statistics.AddSample(11, 1);
+ statistics.AddSample(21, 1);
+ EXPECT_EQ(statistics.GetMedian(), 11);
+
+ // Removed 1, with {11, 21, 16} cached.
+ statistics.AddSample(16, 1);
+ EXPECT_EQ(statistics.GetMedian(), 16);
+
+ // Removed 11, with {21, 16, 26} cached.
+ statistics.AddSample(26, 1);
+ EXPECT_EQ(statistics.GetMedian(), 21);
+}
+
+} // namespace
+} // namespace base
diff --git a/cobalt/black_box_tests/testdata/service_worker_test.html b/cobalt/black_box_tests/testdata/service_worker_test.html
index 49ef9ad..7450faf 100644
--- a/cobalt/black_box_tests/testdata/service_worker_test.html
+++ b/cobalt/black_box_tests/testdata/service_worker_test.html
@@ -39,6 +39,36 @@
}
function test_claimable_worker() {
+ console.log('Adding ready promise.');
+ navigator.serviceWorker.ready.then(function (
+ registration) {
+ assertNotEqual(null, registration);
+ console.log('(Expected) Registration ready promise',
+ registration, ' with active worker ',
+ registration.active);
+ assertNotEqual(null, registration.active);
+ registration.active.postMessage(
+ 'Registration ready received for claimable worker.');
+ count_event();
+ registration.unregister()
+ .then(function (success) {
+ // Even a claimed registration will successfully
+ // unregister because unregistration takes effect after
+ // the page is unloaded, so it's not blocked by being
+ // the active service worker.
+ console.log('(Expected) unregister success :',
+ success);
+ count_event(41);
+ }, function (error) {
+ console.log('(Unexpected) unregister ' +
+ `${error}`, error);
+ assertIncludes('SecurityError: ', `${error}`);
+ notReached();
+ });
+ console.log('unregister started.');
+
+ });
+
navigator.serviceWorker.register('service_worker_test_claimable.js', {
scope: './',
}).then(function (registration) {
@@ -63,25 +93,6 @@
'Registration successful, current worker state is ' +
'active.');
}
-
- registration.unregister()
- .then(function (success) {
- // Even a claimed registration will successfully
- // unregister because unregistration takes effect after
- // the page is unloaded, so it's not blocked by being
- // the active service worker.
- console.log('(Expected) unregister success :',
- success);
- count_event(41);
- }, function (error) {
- console.log('(Unexpected) unregister ' +
- `${error}`, error);
- assertIncludes('SecurityError: ', `${error}`);
- notReached();
- });
- console.log('unregister started.');
-
-
}, function (error) {
console.log('(Unexpected) :', error);
notReached();
@@ -253,22 +264,11 @@
}
// Check that the registration has an activated worker after
- // some time.
+ // some time. The delay used here should be long enough for
+ // the service worker to complete activating and have the
+ // state 'activated'. It has to be longer than the combined
+ // delays in the install or activate event handlers.
window.setTimeout(function () {
- // TODO(b/234659851): Investigate whether this should
- // resolve or not, in this case where there already is
- // an active worker.
- navigator.serviceWorker.ready.then(function (
- registration) {
- assertNotEqual(null, registration);
- console.log('(Expected) Registration ready promise',
- registration, ' with active worker ',
- registration.active);
- assertNotEqual(null, registration.active);
- registration.active.postMessage(
- 'Registration ready received after waiting.');
- count_event();
- });
// Since these events are asynchronous, the service
// worker can be either of these states.
console.log(
@@ -319,6 +319,7 @@
console.log('(Unexpected) :', error);
notReached();
});
+
test_claimable_worker();
}, function (error) {
console.log('(Unexpected) unregister ' +
@@ -326,7 +327,7 @@
assertIncludes('SecurityError: ', `${error}`);
notReached();
});
- }, 500);
+ }, 1000);
// Test getRegistration for a non-registered scope.
navigator.serviceWorker.getRegistration('/bo/gus')
@@ -411,12 +412,14 @@
console.log('Done starting tests');
setupFinished();
+ // This delay has to be long enough to guarantee that the test has
+ // finished.
window.setTimeout(
() => {
console.log('Events:', expected_event_count)
assertEqual(41, expected_event_count);
onEndTest();
- }, 3000);
+ }, 7000);
</script>
</body>
diff --git a/cobalt/black_box_tests/testdata/service_worker_test.js b/cobalt/black_box_tests/testdata/service_worker_test.js
index 5b9e117..4b985a2 100644
--- a/cobalt/black_box_tests/testdata/service_worker_test.js
+++ b/cobalt/black_box_tests/testdata/service_worker_test.js
@@ -31,11 +31,11 @@
console.log('oninstall event received', e);
console.log('self.clients.claim()');
- self.clients.claim().then(function (clients) {
+ e.waitUntil(self.clients.claim().then(function (clients) {
console.log('(Unexpected) self.clients.claim():', clients);
}, function (error) {
console.log(`(Expected) self.clients.claim() not yet activated: ${error}`, error);
- });
+ }));
}
self.onactivate = function (e) {
@@ -43,14 +43,14 @@
// Claim should pass here, since the state is activating.
console.log('self.clients.claim()');
- self.clients.claim().then(function (clients) {
+ e.waitUntil(self.clients.claim().then(function (clients) {
console.log('(Expected) self.clients.claim():', clients);
var options = {
includeUncontrolled: false, type: 'window'
};
console.log('self.clients.matchAll(options)');
- self.clients.matchAll(options).then(function (clients) {
+ e.waitUntil(self.clients.matchAll(options).then(function (clients) {
console.log('(Expected) self.clients.matchAll():', clients.length, clients);
for (var i = 0; i < clients.length; i++) {
console.log('Client with url', clients[i].url,
@@ -60,11 +60,11 @@
}
}, function (error) {
console.log(`(Unexpected) self.clients.matchAll(): ${error}`, error);
- });
+ }));
}, function (error) {
console.log(`(Unexpected) self.clients.claim(): ${error}`, error);
- });
+ }));
}
console.log('self.registration', self.registration);
diff --git a/cobalt/black_box_tests/testdata/service_worker_test_claimable.js b/cobalt/black_box_tests/testdata/service_worker_test_claimable.js
index 21cb0d7..191c964 100644
--- a/cobalt/black_box_tests/testdata/service_worker_test_claimable.js
+++ b/cobalt/black_box_tests/testdata/service_worker_test_claimable.js
@@ -35,7 +35,9 @@
self.oninstall = function (e) {
console.log('oninstall event received', e);
- e.waitUntil(delay_promise(500).then(() => console.log('Promised delay.'), () => console.log('\nPromised rejected.\n')));
+ // Using a delay long enough to make it clearly visible in the log that the
+ // event is extended, and is delaying the activate event and ready promise.
+ e.waitUntil(delay_promise(1000).then(() => console.log('Promised delay.'), () => console.log('\nPromised rejected.\n')));
}
self.onactivate = function (e) {
@@ -49,6 +51,8 @@
var options = {
includeUncontrolled: false, type: 'window'
};
+ // Using a delay long enough to make it clearly visible in the log that the
+ // event is extended.
e.waitUntil(delay_promise(1000).then(function () {
console.log('self.clients.matchAll(options)');
e.waitUntil(self.clients.matchAll(options).then(function (clients) {
diff --git a/cobalt/browser/application.cc b/cobalt/browser/application.cc
index 5fa7318..b6a691f 100644
--- a/cobalt/browser/application.cc
+++ b/cobalt/browser/application.cc
@@ -645,6 +645,7 @@
std::make_unique<persistent_storage::PersistentSettings>(
kPersistentSettingsJson, message_loop_->task_runner());
+ // Initializes Watchdog.
watchdog::Watchdog* watchdog =
watchdog::Watchdog::CreateInstance(persistent_settings_.get());
DCHECK(watchdog);
diff --git a/cobalt/browser/user_agent_platform_info.cc b/cobalt/browser/user_agent_platform_info.cc
index 4c334e9..fe9f3e1 100644
--- a/cobalt/browser/user_agent_platform_info.cc
+++ b/cobalt/browser/user_agent_platform_info.cc
@@ -250,9 +250,12 @@
info.set_rasterizer_type(
renderer::GetDefaultRasterizerForPlatform().rasterizer_name);
-// Evergreen version
+// Evergreen info
#if SB_IS(EVERGREEN)
- info.set_evergreen_version(updater::GetCurrentEvergreenVersion());
+ updater::EvergreenLibraryMetadata evergreen_library_metadata =
+ updater::GetCurrentEvergreenLibraryMetadata();
+ info.set_evergreen_version(evergreen_library_metadata.version);
+ info.set_evergreen_file_type(evergreen_library_metadata.file_type);
if (!SbSystemGetExtension(kCobaltExtensionInstallationManagerName)) {
// If the installation manager is not initialized, the "evergreen_lite"
// command line parameter is specified and the system image is loaded.
@@ -380,6 +383,9 @@
} else if (!input.first.compare("evergreen_type")) {
info.set_evergreen_type(input.second);
LOG(INFO) << "Set evergreen type to " << input.second;
+ } else if (!input.first.compare("evergreen_file_type")) {
+ info.set_evergreen_file_type(input.second);
+ LOG(INFO) << "Set evergreen file type to " << input.second;
} else if (!input.first.compare("evergreen_version")) {
info.set_evergreen_version(input.second);
LOG(INFO) << "Set evergreen version to " << input.second;
@@ -481,6 +487,11 @@
evergreen_type_ = Sanitize(evergreen_type, isTCHARorForwardSlash);
}
+void UserAgentPlatformInfo::set_evergreen_file_type(
+ const std::string& evergreen_file_type) {
+ evergreen_file_type_ = Sanitize(evergreen_file_type, isTCHARorForwardSlash);
+}
+
void UserAgentPlatformInfo::set_evergreen_version(
const std::string& evergreen_version) {
evergreen_version_ = Sanitize(evergreen_version, isTCHAR);
diff --git a/cobalt/browser/user_agent_platform_info.h b/cobalt/browser/user_agent_platform_info.h
index 1f379f6..d71c7fe 100644
--- a/cobalt/browser/user_agent_platform_info.h
+++ b/cobalt/browser/user_agent_platform_info.h
@@ -66,6 +66,9 @@
return rasterizer_type_;
}
const std::string& evergreen_type() const override { return evergreen_type_; }
+ const std::string& evergreen_file_type() const override {
+ return evergreen_file_type_;
+ }
const std::string& evergreen_version() const override {
return evergreen_version_;
}
@@ -95,6 +98,7 @@
const std::string& javascript_engine_version);
void set_rasterizer_type(const std::string& rasterizer_type);
void set_evergreen_type(const std::string& evergreen_type);
+ void set_evergreen_file_type(const std::string& evergreen_file_type);
void set_evergreen_version(const std::string& evergreen_version);
void set_cobalt_version(const std::string& cobalt_version);
void set_cobalt_build_version_number(
@@ -116,6 +120,7 @@
std::string javascript_engine_version_;
std::string rasterizer_type_;
std::string evergreen_type_;
+ std::string evergreen_file_type_;
std::string evergreen_version_;
std::string cobalt_version_;
diff --git a/cobalt/browser/user_agent_string.cc b/cobalt/browser/user_agent_string.cc
index 3b36311..bd41231 100644
--- a/cobalt/browser/user_agent_string.cc
+++ b/cobalt/browser/user_agent_string.cc
@@ -46,6 +46,11 @@
// JavaScript Engine Name/Version
// Starboard/APIVersion,
// Device/FirmwareVersion (Brand, Model, ConnectionType)
+ //
+ // In the case of Evergreen, it contains three additional sections:
+ // Evergreen/Version
+ // Evergreen-Type
+ // Evergreen-FileType
// Mozilla/5.0 (ChromiumStylePlatform)
std::string user_agent = base::StringPrintf(
@@ -74,12 +79,19 @@
base::StringAppendF(&user_agent, " Evergreen/%s",
platform_info.evergreen_version().c_str());
}
+
// Evergreen type
if (!platform_info.evergreen_type().empty()) {
base::StringAppendF(&user_agent, " Evergreen-%s",
platform_info.evergreen_type().c_str());
}
+ // Evergreen file type
+ if (!platform_info.evergreen_file_type().empty()) {
+ base::StringAppendF(&user_agent, " Evergreen-%s",
+ platform_info.evergreen_file_type().c_str());
+ }
+
// Starboard/APIVersion,
if (!platform_info.starboard_version().empty()) {
base::StringAppendF(&user_agent, " %s",
diff --git a/cobalt/browser/user_agent_string_test.cc b/cobalt/browser/user_agent_string_test.cc
index 9ce2eb4..27e232b 100644
--- a/cobalt/browser/user_agent_string_test.cc
+++ b/cobalt/browser/user_agent_string_test.cc
@@ -38,6 +38,8 @@
platform_info.set_javascript_engine_version("");
platform_info.set_rasterizer_type("");
platform_info.set_evergreen_version("");
+ platform_info.set_evergreen_type("");
+ platform_info.set_evergreen_file_type("");
platform_info.set_cobalt_version("");
platform_info.set_cobalt_build_version_number("");
platform_info.set_build_configuration("");
@@ -242,7 +244,7 @@
user_agent_string.find("FooBar" TCHARORSLASH "BazQux"));
}
-TEST(UserAgentStringFactoryTest, SanitizedEvergreenType) {
+TEST(UserAgentStringFactoryTest, SanitizedEvergreenVersion) {
UserAgentPlatformInfo platform_info =
CreateOnlyOSNameAndVersionPlatformInfo();
platform_info.set_evergreen_version("Foo" NOT_TCHAR "Bar" TCHAR
@@ -251,6 +253,26 @@
EXPECT_NE(std::string::npos, user_agent_string.find("FooBar" TCHAR "BazQux"));
}
+TEST(UserAgentStringFactoryTest, SanitizedEvergreenType) {
+ UserAgentPlatformInfo platform_info =
+ CreateOnlyOSNameAndVersionPlatformInfo();
+ platform_info.set_evergreen_type("Foo" NOT_TCHARORSLASH "Bar" TCHARORSLASH
+ "Baz" NOT_TCHARORSLASH "Qux");
+ std::string user_agent_string = CreateUserAgentString(platform_info);
+ EXPECT_NE(std::string::npos,
+ user_agent_string.find("FooBar" TCHARORSLASH "BazQux"));
+}
+
+TEST(UserAgentStringFactoryTest, SanitizedEvergreenFileType) {
+ UserAgentPlatformInfo platform_info =
+ CreateOnlyOSNameAndVersionPlatformInfo();
+ platform_info.set_evergreen_file_type(
+ "Foo" NOT_TCHARORSLASH "Bar" TCHARORSLASH "Baz" NOT_TCHARORSLASH "Qux");
+ std::string user_agent_string = CreateUserAgentString(platform_info);
+ EXPECT_NE(std::string::npos,
+ user_agent_string.find("FooBar" TCHARORSLASH "BazQux"));
+}
+
TEST(UserAgentStringFactoryTest, SanitizedCobaltVersion) {
UserAgentPlatformInfo platform_info =
CreateOnlyOSNameAndVersionPlatformInfo();
diff --git a/cobalt/demos/content/watchdog-demo/index.html b/cobalt/demos/content/watchdog-demo/index.html
index 4114648..0213470 100644
--- a/cobalt/demos/content/watchdog-demo/index.html
+++ b/cobalt/demos/content/watchdog-demo/index.html
@@ -23,6 +23,9 @@
unregister: 'Unregister',
ping: 'Ping',
getWatchdogViolations: 'Get Watchdog Violations',
+ getPersistentSettingWatchdogEnable: 'Get Enable Watchdog',
+ setPersistentSettingWatchdogEnableTrue: 'Set Enable Watchdog True',
+ setPersistentSettingWatchdogEnableFalse: 'Set Enable Watchdog False',
getPersistentSettingWatchdogCrash: 'Get Can Trigger Crash',
setPersistentSettingWatchdogCrashTrue: 'Set Can Trigger Crash True',
setPersistentSettingWatchdogCrashFalse: 'Set Can Trigger Crash False',
@@ -63,6 +66,12 @@
ret = h5vcc.crashLog.ping('test-name', `test-ping`);
} else if (watchdogFunction == 'getWatchdogViolations') {
ret = h5vcc.crashLog.getWatchdogViolations();
+ } else if (watchdogFunction == 'getPersistentSettingWatchdogEnable') {
+ ret = h5vcc.crashLog.getPersistentSettingWatchdogEnable();
+ } else if (watchdogFunction == 'setPersistentSettingWatchdogEnableTrue') {
+ h5vcc.crashLog.setPersistentSettingWatchdogEnable(true);
+ } else if (watchdogFunction == 'setPersistentSettingWatchdogEnableFalse') {
+ h5vcc.crashLog.setPersistentSettingWatchdogEnable(false);
} else if (watchdogFunction == 'getPersistentSettingWatchdogCrash') {
ret = h5vcc.crashLog.getPersistentSettingWatchdogCrash();
} else if (watchdogFunction == 'setPersistentSettingWatchdogCrashTrue') {
diff --git a/cobalt/dom/media_source.cc b/cobalt/dom/media_source.cc
index 255da43..290996f 100644
--- a/cobalt/dom/media_source.cc
+++ b/cobalt/dom/media_source.cc
@@ -486,6 +486,7 @@
}
bool MediaSource::MediaElementHasMaxVideoCapabilities() const {
+ SB_DCHECK(attached_element_);
return has_max_video_capabilities_;
}
diff --git a/cobalt/dom/source_buffer.cc b/cobalt/dom/source_buffer.cc
index 806a970..a8b78e9 100644
--- a/cobalt/dom/source_buffer.cc
+++ b/cobalt/dom/source_buffer.cc
@@ -108,7 +108,8 @@
audio_tracks_(
new AudioTrackList(settings, media_source->GetMediaElement())),
video_tracks_(
- new VideoTrackList(settings, media_source->GetMediaElement())) {
+ new VideoTrackList(settings, media_source->GetMediaElement())),
+ metrics_(!media_source_->MediaElementHasMaxVideoCapabilities()) {
DCHECK(!id_.empty());
DCHECK(media_source_);
DCHECK(chunk_demuxer);
@@ -367,13 +368,16 @@
// RemoveMediaTracks();
// }
- if (!media_source_->MediaElementHasMaxVideoCapabilities()) {
- // TODO: Determine if the source buffer contains an audio or video stream,
- // and print the steam type along with the metrics.
- metrics_.PrintMetrics();
- }
+ // TODO: Determine if the source buffer contains an audio or video stream,
+ // maybe by implementing track support and get from the type of the
+ // track, and print the steam type along with the metrics.
+ metrics_.PrintCurrentMetricsAndUpdateAccumulatedMetrics();
chunk_demuxer_->RemoveId(id_);
+ if (chunk_demuxer_->GetAllStreams().empty()) {
+ metrics_.PrintAccumulatedMetrics();
+ }
+
chunk_demuxer_ = NULL;
media_source_ = NULL;
event_queue_ = NULL;
diff --git a/cobalt/dom/source_buffer_metrics.cc b/cobalt/dom/source_buffer_metrics.cc
index 1fbf9b6..f2ae570 100644
--- a/cobalt/dom/source_buffer_metrics.cc
+++ b/cobalt/dom/source_buffer_metrics.cc
@@ -17,7 +17,9 @@
#include <algorithm>
#include "base/logging.h"
+#include "cobalt/base/statistics.h"
#include "starboard/common/string.h"
+#include "starboard/types.h"
namespace cobalt {
namespace dom {
@@ -30,9 +32,30 @@
return duration == 0 ? 0 : size * kSbTimeSecond / duration;
}
+int64_t GetBandwidthForStatistics(int64_t size, int64_t duration) {
+ return GetBandwidth(static_cast<std::size_t>(size), duration);
+}
+
+using BandwidthStatistics =
+ base::Statistics<int64_t, SbTimeMonotonic, 1024, GetBandwidthForStatistics>;
+
+BandwidthStatistics s_accumulated_wall_time_bandwidth_;
+BandwidthStatistics s_accumulated_thread_time_bandwidth_;
+
+double GetWallToThreadTimeRatio(int64_t wall_time, int64_t thread_time) {
+ if (thread_time == 0) {
+ thread_time = 1;
+ }
+ return static_cast<double>(wall_time) / thread_time;
+}
+
} // namespace
void SourceBufferMetrics::StartTracking() {
+ if (!is_primary_video_) {
+ return;
+ }
+
DCHECK(!is_tracking_);
is_tracking_ = true;
wall_start_time_ = SbTimeGetMonotonicNow();
@@ -41,6 +64,10 @@
}
void SourceBufferMetrics::EndTracking(std::size_t size_appended) {
+ if (!is_primary_video_) {
+ return;
+ }
+
DCHECK(is_tracking_);
is_tracking_ = false;
@@ -67,24 +94,62 @@
}
}
-void SourceBufferMetrics::PrintMetrics() {
+void SourceBufferMetrics::PrintCurrentMetricsAndUpdateAccumulatedMetrics() {
+ if (!is_primary_video_) {
+ return;
+ }
+
+ s_accumulated_wall_time_bandwidth_.AddSample(total_size_, total_wall_time_);
+ s_accumulated_thread_time_bandwidth_.AddSample(total_size_,
+ total_thread_time_);
+
LOG_IF(INFO, total_thread_time_ > total_wall_time_)
<< "Total thread time " << total_thread_time_
<< " should not be greater than total wall time " << total_wall_time_
<< ".";
+
+ // clang-format off
LOG(INFO) << starboard::FormatString(
- "AppendBuffer() metrics:\n\t%-30s%zu B\n\t%-30s%d "
- "us (%d B/s)\n\t\t%-28s%d "
- "B/s\n\t\t%-28s%d B/s\n\t%-30s%d us (%d B/s)\n\t\t%-28s%d "
- "B/s\n\t\t%-28s%d B/s",
- "Total size of appended data:", total_size_, "Total append wall time",
+ "AppendBuffer() metrics:\n"
+ " Total size of appended data: %zu B, wall time / thread time = %02g\n"
+ " Total append wall time: %" PRId64 " us (%d B/s)\n"
+ " Max wall bandwidth: %d B/s\n"
+ " Min wall bandwidth: %d B/s\n"
+ " Total append thread time: %" PRId64 " us (%d B/s)\n"
+ " Max thread bandwidth: %d B/s\n"
+ " Min thread bandwidth: %d B/s\n",
+ total_size_,
+ GetWallToThreadTimeRatio(total_wall_time_, total_thread_time_),
total_wall_time_, GetBandwidth(total_size_, total_wall_time_),
- "Max wall bandwidth:", max_wall_bandwidth_,
- "Min wall bandwidth:", min_wall_bandwidth_,
- "Total append thread time:", total_thread_time_,
- GetBandwidth(total_size_, total_thread_time_),
- "Max thread bandwidth:", max_thread_bandwidth_,
- "Min thread bandwidth:", min_thread_bandwidth_);
+ max_wall_bandwidth_, min_wall_bandwidth_, total_thread_time_,
+ GetBandwidth(total_size_, total_thread_time_), max_thread_bandwidth_,
+ min_thread_bandwidth_);
+ // clang-format on
+}
+
+void SourceBufferMetrics::PrintAccumulatedMetrics() {
+ if (!is_primary_video_) {
+ return;
+ }
+
+ LOG(INFO) << starboard::FormatString(
+ "Accumulated AppendBuffer() metrics:\n"
+ " wall time / thread time = %02g\n"
+ " wall bandwidth statistics (B/s):\n"
+ " min %d, median %d, average %d, max %d\n"
+ " thread bandwidth statistics (B/s):\n"
+ " min %d, median %d, average %d, max %d",
+ GetWallToThreadTimeRatio(
+ s_accumulated_wall_time_bandwidth_.accumulated_divisor(),
+ s_accumulated_thread_time_bandwidth_.accumulated_divisor()),
+ static_cast<int>(s_accumulated_wall_time_bandwidth_.min()),
+ static_cast<int>(s_accumulated_wall_time_bandwidth_.GetMedian()),
+ static_cast<int>(s_accumulated_wall_time_bandwidth_.average()),
+ static_cast<int>(s_accumulated_wall_time_bandwidth_.max()),
+ static_cast<int>(s_accumulated_thread_time_bandwidth_.min()),
+ static_cast<int>(s_accumulated_thread_time_bandwidth_.GetMedian()),
+ static_cast<int>(s_accumulated_thread_time_bandwidth_.average()),
+ static_cast<int>(s_accumulated_thread_time_bandwidth_.max()));
}
#endif // !defined(COBALT_BUILD_TYPE_GOLD)
diff --git a/cobalt/dom/source_buffer_metrics.h b/cobalt/dom/source_buffer_metrics.h
index 67001fe..766ebac 100644
--- a/cobalt/dom/source_buffer_metrics.h
+++ b/cobalt/dom/source_buffer_metrics.h
@@ -24,34 +24,41 @@
class SourceBufferMetrics {
public:
- SourceBufferMetrics() = default;
+ explicit SourceBufferMetrics(bool is_primary_video) {}
~SourceBufferMetrics() = default;
void StartTracking() {}
void EndTracking(size_t size_appended) {}
- void PrintMetrics() {}
+ void PrintCurrentMetricsAndUpdateAccumulatedMetrics() {}
+ void PrintAccumulatedMetrics() {}
};
#else // defined(COBALT_BUILD_TYPE_GOLD)
class SourceBufferMetrics {
public:
- SourceBufferMetrics() = default;
+ explicit SourceBufferMetrics(bool is_primary_video)
+ : is_primary_video_(is_primary_video) {}
~SourceBufferMetrics() = default;
void StartTracking();
void EndTracking(size_t size_appended);
- void PrintMetrics();
+ void PrintCurrentMetricsAndUpdateAccumulatedMetrics();
+ void PrintAccumulatedMetrics();
private:
+ SourceBufferMetrics(const SourceBufferMetrics&) = delete;
+ SourceBufferMetrics& operator=(const SourceBufferMetrics&) = delete;
+
SbTimeMonotonic wall_start_time_ = 0;
SbTimeMonotonic thread_start_time_ = 0;
+ const bool is_primary_video_;
bool is_tracking_ = false;
size_t total_size_ = 0;
- int total_thread_time_ = 0;
- int total_wall_time_ = 0;
+ SbTime total_thread_time_ = 0;
+ SbTime total_wall_time_ = 0;
int max_thread_bandwidth_ = 0;
int min_thread_bandwidth_ = INT_MAX;
int max_wall_bandwidth_ = 0;
diff --git a/cobalt/h5vcc/h5vcc_crash_log.cc b/cobalt/h5vcc/h5vcc_crash_log.cc
index d61dca0..7a502b4 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.cc
+++ b/cobalt/h5vcc/h5vcc_crash_log.cc
@@ -186,6 +186,17 @@
return "";
}
+bool H5vccCrashLog::GetPersistentSettingWatchdogEnable() {
+ watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();
+ if (watchdog) return watchdog->GetPersistentSettingWatchdogEnable();
+ return true;
+}
+
+void H5vccCrashLog::SetPersistentSettingWatchdogEnable(bool enable_watchdog) {
+ watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();
+ if (watchdog) watchdog->SetPersistentSettingWatchdogEnable(enable_watchdog);
+}
+
bool H5vccCrashLog::GetPersistentSettingWatchdogCrash() {
watchdog::Watchdog* watchdog = watchdog::Watchdog::GetInstance();
if (watchdog) return watchdog->GetPersistentSettingWatchdogCrash();
diff --git a/cobalt/h5vcc/h5vcc_crash_log.h b/cobalt/h5vcc/h5vcc_crash_log.h
index 5b1557b..60a51b1 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.h
+++ b/cobalt/h5vcc/h5vcc_crash_log.h
@@ -44,6 +44,10 @@
std::string GetWatchdogViolations();
+ bool GetPersistentSettingWatchdogEnable();
+
+ void SetPersistentSettingWatchdogEnable(bool enable_watchdog);
+
bool GetPersistentSettingWatchdogCrash();
void SetPersistentSettingWatchdogCrash(bool can_trigger_crash);
diff --git a/cobalt/h5vcc/h5vcc_crash_log.idl b/cobalt/h5vcc/h5vcc_crash_log.idl
index 5c91aa0..f551cd7 100644
--- a/cobalt/h5vcc/h5vcc_crash_log.idl
+++ b/cobalt/h5vcc/h5vcc_crash_log.idl
@@ -32,7 +32,7 @@
// Returns true if Watchdog client was registered.
// name, Watchdog client to register.
// description, information on the Watchdog client.
- // monitor_state, application state up to which the client is monitored.
+ // monitor_state, application state to continue monitoring client up to.
// Inclusive.
// time_interval, maximum number of microseconds allowed between pings
// before triggering a Watchdog violation.
@@ -56,8 +56,47 @@
// Returns a json string containing the Watchdog violations since the last
// call. Clears internal cache of Watchdog violations to prevent duplicates.
+ // Timestamps are stored as strings due to int size constraints.
+ // Example json:
+ // {
+ // "test-name":{
+ // "description":"test-description",
+ // "violations":[
+ // {
+ // "monitorState":"kApplicationStateStarted",
+ // "pingInfos":[
+ // {
+ // "info":"test-ping",
+ // "timestampMicroseconds":"1658972623547006"
+ // }
+ // ],
+ // "registeredClients":[
+ // "test-name"
+ // ],
+ // "timeIntervalMicroseconds":"5000000",
+ // "timeWaitMicroseconds":"0",
+ // "timestampLastPingedMicroseconds":"1658972623547006",
+ // "timestampRegisteredMicroseconds":"1658972621890834",
+ // "timestampViolationMicroseconds":"1658972629489771",
+ // "violationDurationMicroseconds":"942764"
+ // }
+ // ]
+ // }
+ // }
DOMString getWatchdogViolations();
+ // Gets a persistent Watchdog setting that determines whether or not Watchdog
+ // is enabled. When disabled, Watchdog behaves like a stub except that
+ // persistent settings can still be get/set. Requires a restart to take
+ // effect.
+ boolean getPersistentSettingWatchdogEnable();
+
+ // Sets a persistent Watchdog setting that determines whether or not Watchdog
+ // is enabled. When disabled, Watchdog behaves like a stub except that
+ // persistent settings can still be get/set. Requires a restart to take
+ // effect.
+ void setPersistentSettingWatchdogEnable(boolean enable_watchdog);
+
// Gets a persistent Watchdog setting that determines whether or not a
// Watchdog violation will trigger a crash.
boolean getPersistentSettingWatchdogCrash();
diff --git a/cobalt/media/base/starboard_player.cc b/cobalt/media/base/starboard_player.cc
index 8aeb271..b6e50b3 100644
--- a/cobalt/media/base/starboard_player.cc
+++ b/cobalt/media/base/starboard_player.cc
@@ -24,6 +24,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
+#include "cobalt/base/statistics.h"
#include "cobalt/media/base/format_support_query_metrics.h"
#include "starboard/common/media.h"
#include "starboard/common/string.h"
@@ -34,6 +35,12 @@
namespace cobalt {
namespace media {
+namespace {
+
+base::Statistics<SbTime, int, 1024> s_player_presenting_delays_;
+
+} // namespace
+
StarboardPlayer::CallbackHelper::CallbackHelper(StarboardPlayer* player)
: player_(player) {}
@@ -977,16 +984,16 @@
std::string first_events_str;
if (set_drm_system_ready_cb_time_ == -1) {
first_events_str =
- starboard::FormatString("%-50s0 us", "SbPlayerCreate() called");
+ starboard::FormatString("%-40s0 us", "SbPlayerCreate() called");
} else if (set_drm_system_ready_cb_time_ < player_creation_time_) {
first_events_str = starboard::FormatString(
- "%-50s0 us\n%-50s%" PRId64 " us", "set_drm_system_ready_cb called",
+ "%-40s0 us\n%-40s%" PRId64 " us", "set_drm_system_ready_cb called",
"SbPlayerCreate() called",
player_creation_time_ - set_drm_system_ready_cb_time_);
} else {
first_events_str = starboard::FormatString(
- "%-50s0 us\n%-50s%" PRId64 " us", "SbPlayerCreate() called",
+ "%-40s0 us\n%-40s%" PRId64 " us", "SbPlayerCreate() called",
"set_drm_system_ready_cb called",
set_drm_system_ready_cb_time_ - player_creation_time_);
}
@@ -1004,16 +1011,27 @@
sb_player_state_presenting_time_ -
std::max(first_audio_sample_time_, first_video_sample_time_);
+ s_player_presenting_delays_.AddSample(player_presenting_time_delta, 1);
+
+ // clang-format off
LOG(INFO) << starboard::FormatString(
- "SbPlayer startup latencies\n%-50s%s\n%s\n%-50s%" PRId64
- " us\n%-50s%" PRId64 " us\n%-50s%" PRId64 "/%" PRId64 " us\n%-50s%" PRId64
- " us",
- "Event name", "time since last event", first_events_str.c_str(),
- "kSbPlayerStateInitialized received", player_initialization_time_delta,
- "kSbPlayerStatePrerolling received", player_preroll_time_delta,
- "First media sample(s) written [audio/video]",
- first_audio_sample_time_delta, first_video_sample_time_delta,
- "kSbPlayerStatePresenting received", player_presenting_time_delta);
+ "\nSbPlayer startup latencies\n"
+ " Event name time since last event\n"
+ " %s\n" // |first_events_str| populated above
+ " kSbPlayerStateInitialized received %" PRId64 " us\n"
+ " kSbPlayerStatePrerolling received %" PRId64 " us\n"
+ " First media sample(s) written [a/v] %" PRId64 "/%" PRId64 " us\n"
+ " kSbPlayerStatePresenting received %" PRId64 " us\n"
+ " kSbPlayerStatePresenting delay statistics (us):\n"
+ " min: %" PRId64 ", median: %" PRId64 ", average: %" PRId64
+ ", max: %" PRId64,
+ first_events_str.c_str(), player_initialization_time_delta,
+ player_preroll_time_delta, first_audio_sample_time_delta,
+ first_video_sample_time_delta, player_presenting_time_delta,
+ s_player_presenting_delays_.min(),
+ s_player_presenting_delays_.GetMedian(),
+ s_player_presenting_delays_.average(), s_player_presenting_delays_.max());
+ // clang-format on
}
} // namespace media
diff --git a/cobalt/renderer/pipeline.cc b/cobalt/renderer/pipeline.cc
index 4815da9..6198549 100644
--- a/cobalt/renderer/pipeline.cc
+++ b/cobalt/renderer/pipeline.cc
@@ -71,7 +71,7 @@
const char kWatchdogName[] = "renderer";
// The watchdog time interval in microseconds allowed between pings before
// triggering violations.
-const int64_t kWatchdogTimeInterval = 1000000;
+const int64_t kWatchdogTimeInterval = 2000000;
// The watchdog time wait in microseconds to initially wait before triggering
// violations.
const int64_t kWatchdogTimeWait = 2000000;
diff --git a/cobalt/updater/BUILD.gn b/cobalt/updater/BUILD.gn
index 3dcfbc7..4026027 100644
--- a/cobalt/updater/BUILD.gn
+++ b/cobalt/updater/BUILD.gn
@@ -97,3 +97,19 @@
"//starboard/loader_app:app_key",
]
}
+
+target(gtest_target_type, "updater_test") {
+ testonly = true
+
+ sources = [
+ "//starboard/common/test_main.cc",
+ "utils_test.cc",
+ ]
+
+ deps = [
+ ":updater",
+ "//base",
+ "//components/update_client",
+ "//testing/gtest",
+ ]
+}
diff --git a/cobalt/updater/utils.cc b/cobalt/updater/utils.cc
index 6c9cb3e..d1678de 100644
--- a/cobalt/updater/utils.cc
+++ b/cobalt/updater/utils.cc
@@ -12,6 +12,7 @@
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
@@ -20,6 +21,7 @@
#include "crypto/secure_hash.h"
#include "crypto/sha2.h"
#include "starboard/configuration_constants.h"
+#include "starboard/file.h"
#include "starboard/string.h"
#include "starboard/system.h"
@@ -28,12 +30,16 @@
namespace cobalt {
namespace updater {
namespace {
-// The default manifest version to assume when the actual manifest cannot be
-// parsed for any reason. This should not be used for installation manager
-// errors, or any other error unrelated to parsing the manifest.
-const char kDefaultManifestVersion[] = "1.0.0";
+// Path to compressed Cobalt library, relative to the installation directory.
+const char kCompressedLibraryPath[] = "lib/libcobalt.lz4";
+
+// Path to uncompressed Cobalt library, relative to the installation directory.
+const char kUncompressedLibraryPath[] = "lib/libcobalt.so";
+
} // namespace
+const char kDefaultManifestVersion[] = "1.0.0";
+
bool CreateProductDirectory(base::FilePath* path) {
if (!GetProductDirectoryPath(path)) {
LOG(ERROR) << "Can't get product directory path";
@@ -89,19 +95,73 @@
return base::Version();
}
-const std::string GetLoadedInstallationEvergreenVersion() {
+const std::string GetEvergreenFileType(const std::string& installation_path) {
+ std::string compressed_library_path = base::StrCat(
+ {installation_path, kSbFileSepString, kCompressedLibraryPath});
+ std::string uncompressed_library_path = base::StrCat(
+ {installation_path, kSbFileSepString, kUncompressedLibraryPath});
+
+ if (SbFileExists(compressed_library_path.c_str())) {
+ return "Compressed";
+ } else if (SbFileExists(uncompressed_library_path.c_str())) {
+ return "Uncompressed";
+ } else {
+ LOG(ERROR) << "Failed to get Evergreen file type. Defaulting to "
+ "FileTypeUnknown.";
+ return "FileTypeUnknown";
+ }
+}
+
+const base::FilePath GetLoadedInstallationPath() {
std::vector<char> system_path_content_dir(kSbFileMaxPath);
if (!SbSystemGetPath(kSbSystemPathContentDirectory,
system_path_content_dir.data(), kSbFileMaxPath)) {
LOG(ERROR) << "Failed to get system path content directory";
- return "";
+ return base::FilePath();
}
- // Get the parent directory of the system_path_content_dir, and read the
- // manifest.json there
- base::Version version = ReadEvergreenVersion(
- base::FilePath(std::string(system_path_content_dir.begin(),
- system_path_content_dir.end()))
- .DirName());
+ // Since the Cobalt library has already been loaded,
+ // kSbSystemPathContentDirectory points to the content dir of the running
+ // library and the installation dir is therefore its parent.
+ return base::FilePath(std::string(system_path_content_dir.begin(),
+ system_path_content_dir.end()))
+ .DirName();
+}
+
+const base::FilePath FindInstallationPath() {
+ // TODO(b/233914266): consider using base::NoDestructor to give the
+ // installation path static duration once found.
+
+ auto installation_manager =
+ static_cast<const CobaltExtensionInstallationManagerApi*>(
+ SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
+ if (!installation_manager) {
+ LOG(ERROR) << "Failed to get installation manager extension, getting the "
+ "installation path of the loaded library.";
+ return GetLoadedInstallationPath();
+ }
+ // Get the update version from the manifest file under the current
+ // installation path.
+ int index = installation_manager->GetCurrentInstallationIndex();
+ if (index == IM_EXT_ERROR) {
+ LOG(ERROR) << "Failed to get current installation index, getting the "
+ "installation path of the loaded library.";
+ return GetLoadedInstallationPath();
+ }
+ std::vector<char> installation_path(kSbFileMaxPath);
+ if (installation_manager->GetInstallationPath(
+ index, installation_path.data(), kSbFileMaxPath) == IM_EXT_ERROR) {
+ LOG(ERROR) << "Failed to get installation path from the installation "
+ "manager, getting the installation path of the loaded "
+ "library.";
+ return GetLoadedInstallationPath();
+ }
+ return base::FilePath(
+ std::string(installation_path.begin(), installation_path.end()));
+}
+
+const std::string GetValidOrDefaultEvergreenVersion(
+ const base::FilePath installation_path) {
+ base::Version version = ReadEvergreenVersion(installation_path);
if (!version.IsValid()) {
LOG(ERROR) << "Failed to get the Evergreen version. Defaulting to "
@@ -112,39 +172,20 @@
}
const std::string GetCurrentEvergreenVersion() {
- auto installation_manager =
- static_cast<const CobaltExtensionInstallationManagerApi*>(
- SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
- if (!installation_manager) {
- LOG(ERROR) << "Failed to get installation manager extension, getting "
- "the Evergreen version of the loaded installation.";
- return GetLoadedInstallationEvergreenVersion();
- }
- // Get the update version from the manifest file under the current
- // installation path.
- int index = installation_manager->GetCurrentInstallationIndex();
- if (index == IM_EXT_ERROR) {
- LOG(ERROR) << "Failed to get current installation index, getting the "
- "Evergreen version of the currently loaded installation.";
- return GetLoadedInstallationEvergreenVersion();
- }
- std::vector<char> installation_path(kSbFileMaxPath);
- if (installation_manager->GetInstallationPath(
- index, installation_path.data(), kSbFileMaxPath) == IM_EXT_ERROR) {
- LOG(ERROR) << "Failed to get installation path, getting the Evergreen "
- "version of the currently loaded installation.";
- return GetLoadedInstallationEvergreenVersion();
- }
+ base::FilePath installation_path = FindInstallationPath();
+ return GetValidOrDefaultEvergreenVersion(installation_path);
+}
- base::Version version = ReadEvergreenVersion(base::FilePath(
- std::string(installation_path.begin(), installation_path.end())));
+EvergreenLibraryMetadata GetCurrentEvergreenLibraryMetadata() {
+ EvergreenLibraryMetadata evergreen_library_metadata;
+ base::FilePath installation_path = FindInstallationPath();
- if (!version.IsValid()) {
- LOG(ERROR) << "Failed to get the Evergreen version. Defaulting to "
- << kDefaultManifestVersion << ".";
- return std::string(kDefaultManifestVersion);
- }
- return version.GetString();
+ evergreen_library_metadata.version =
+ GetValidOrDefaultEvergreenVersion(installation_path);
+ evergreen_library_metadata.file_type =
+ GetEvergreenFileType(installation_path.value());
+
+ return evergreen_library_metadata;
}
std::string GetLibrarySha256(int index) {
diff --git a/cobalt/updater/utils.h b/cobalt/updater/utils.h
index a0b5008..2b7b1ba 100644
--- a/cobalt/updater/utils.h
+++ b/cobalt/updater/utils.h
@@ -16,6 +16,16 @@
namespace cobalt {
namespace updater {
+// The default manifest version to assume when the actual manifest cannot be
+// parsed for any reason. This should not be used for installation manager
+// errors, or any other error unrelated to parsing the manifest.
+extern const char kDefaultManifestVersion[];
+
+struct EvergreenLibraryMetadata {
+ std::string version;
+ std::string file_type;
+};
+
// Create a directory where updater files or its data is stored.
bool CreateProductDirectory(base::FilePath* path);
@@ -23,10 +33,13 @@
// stored.
bool GetProductDirectoryPath(base::FilePath* path);
+// Returns the Evergreen library metadata of the current installation.
+EvergreenLibraryMetadata GetCurrentEvergreenLibraryMetadata();
+
// Returns the Evergreen version of the current installation.
const std::string GetCurrentEvergreenVersion();
-// Read the Evergreen version of the installation dir.
+// Reads the Evergreen version of the installation dir.
base::Version ReadEvergreenVersion(base::FilePath installation_dir);
// Returns the hash of the libcobalt.so binary for the installation
diff --git a/cobalt/updater/utils_test.cc b/cobalt/updater/utils_test.cc
new file mode 100644
index 0000000..91fc329
--- /dev/null
+++ b/cobalt/updater/utils_test.cc
@@ -0,0 +1,229 @@
+// Copyright 2022 The Cobalt Authors. 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/updater/utils.h"
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/strings/strcat.h"
+#include "base/values.h"
+#include "starboard/common/file.h"
+#include "starboard/directory.h"
+#include "starboard/file.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cobalt {
+namespace updater {
+namespace {
+
+const char kEvergreenManifestFilename[] = "manifest.json";
+const char kEvergreenLibDirname[] = "lib";
+
+class UtilsTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ temp_dir_path_.resize(kSbFileMaxPath);
+ ASSERT_TRUE(SbSystemGetPath(kSbSystemPathTempDirectory,
+ temp_dir_path_.data(), temp_dir_path_.size()));
+ }
+
+ void TearDown() override {
+ ASSERT_TRUE(starboard::SbFileDeleteRecursive(temp_dir_path_.data(), true));
+ }
+
+ void CreateManifest(const char* content, const std::string& directory) {
+ std::string manifest_path =
+ base::StrCat({directory, kSbFileSepString, kEvergreenManifestFilename});
+ SbFile sb_file =
+ SbFileOpen(manifest_path.c_str(), kSbFileOpenAlways | kSbFileRead,
+ nullptr, nullptr);
+ ASSERT_TRUE(SbFileIsValid(sb_file));
+ ASSERT_TRUE(SbFileClose(sb_file));
+
+ ASSERT_TRUE(
+ SbFileAtomicReplace(manifest_path.c_str(), content, strlen(content)));
+ }
+
+ void DeleteManifest(const std::string& directory) {
+ std::string manifest_path =
+ base::StrCat({directory, kSbFileSepString, kEvergreenManifestFilename});
+ ASSERT_TRUE(SbFileDelete(manifest_path.c_str()));
+ }
+
+ void CreateEmptyLibrary(const std::string& name,
+ const std::string& installation_path) {
+ std::string lib_path = base::StrCat(
+ {installation_path, kSbFileSepString, kEvergreenLibDirname});
+ ASSERT_TRUE(SbDirectoryCreate(lib_path.c_str()));
+
+ lib_path = base::StrCat({lib_path, kSbFileSepString, name});
+ SbFile sb_file = SbFileOpen(
+ lib_path.c_str(), kSbFileOpenAlways | kSbFileRead, nullptr, nullptr);
+ ASSERT_TRUE(SbFileIsValid(sb_file));
+ ASSERT_TRUE(SbFileClose(sb_file));
+ }
+
+ void DeleteLibraryDirRecursively(const std::string& installation_path) {
+ std::string lib_path = base::StrCat(
+ {installation_path, kSbFileSepString, kEvergreenLibDirname});
+ ASSERT_TRUE(starboard::SbFileDeleteRecursive(lib_path.c_str(), false));
+ }
+
+ std::vector<char> temp_dir_path_;
+};
+
+
+TEST_F(UtilsTest, ReadEvergreenVersionReturnsVersionForValidManifest) {
+ std::string installation_path = base::StrCat(
+ {temp_dir_path_.data(), kSbFileSepString, "some_installation_path"});
+ ASSERT_TRUE(SbDirectoryCreate(installation_path.c_str()));
+ char manifest_content[] = R"json(
+ {
+ "manifest_version": 2,
+ "name": "Cobalt",
+ "description": "Cobalt",
+ "version": "1.2.0"
+ })json";
+ CreateManifest(manifest_content, installation_path);
+
+ base::Version version =
+ ReadEvergreenVersion(base::FilePath(installation_path));
+
+ ASSERT_EQ(version.GetString(), "1.2.0");
+
+ DeleteManifest(installation_path);
+}
+
+TEST_F(UtilsTest,
+ ReadEvergreenVersionReturnsInvalidVersionForVersionlessManifest) {
+ std::string installation_path = base::StrCat(
+ {temp_dir_path_.data(), kSbFileSepString, "some_installation_path"});
+ ASSERT_TRUE(SbDirectoryCreate(installation_path.c_str()));
+ char versionless_manifest_content[] = R"json(
+ {
+ "manifest_version": 2,
+ "name": "Cobalt",
+ "description": "Cobalt",
+ })json";
+ CreateManifest(versionless_manifest_content, installation_path);
+
+ base::Version version =
+ ReadEvergreenVersion(base::FilePath(installation_path));
+
+ ASSERT_FALSE(version.IsValid());
+
+ DeleteManifest(installation_path);
+}
+
+TEST_F(UtilsTest, ReadEvergreenVersionReturnsInvalidVersionForMissingManifest) {
+ base::Version version =
+ ReadEvergreenVersion(base::FilePath("nonexistent_manifest_path"));
+
+ ASSERT_FALSE(version.IsValid());
+}
+
+TEST_F(UtilsTest,
+ ReturnsValidCurrentEvergreenVersionForManifestInLoadedInstallation) {
+ std::vector<char> system_path_content_dir(kSbFileMaxPath);
+ SbSystemGetPath(kSbSystemPathContentDirectory, system_path_content_dir.data(),
+ kSbFileMaxPath);
+ // Since the libupdater_test.so library has already been loaded,
+ // kSbSystemPathContentDirectory points to the content dir of the running
+ // library and the installation dir is therefore its parent.
+ std::string installation_path =
+ base::FilePath(std::string(system_path_content_dir.begin(),
+ system_path_content_dir.end()))
+ .DirName()
+ .value();
+ char manifest_content[] = R"json(
+ {
+ "manifest_version": 2,
+ "name": "Cobalt",
+ "description": "Cobalt",
+ "version": "1.2.0"
+ })json";
+ CreateManifest(manifest_content, installation_path);
+
+ std::string version = GetCurrentEvergreenVersion();
+
+ ASSERT_EQ(version, "1.2.0");
+
+ DeleteManifest(installation_path);
+}
+
+TEST_F(UtilsTest,
+ ReturnsDefaultEvergreenVersionForManifestMissingFromLoadedInstallation) {
+ std::string version = GetCurrentEvergreenVersion();
+
+ ASSERT_EQ(version, kDefaultManifestVersion);
+}
+
+TEST_F(UtilsTest,
+ ReturnsValidCurrentMetadataForValidFilesInLoadedInstallation) {
+ std::vector<char> system_path_content_dir(kSbFileMaxPath);
+ SbSystemGetPath(kSbSystemPathContentDirectory, system_path_content_dir.data(),
+ kSbFileMaxPath);
+ // Since the libupdater_test.so library has already been loaded,
+ // kSbSystemPathContentDirectory points to the content dir of the running
+ // library and the installation dir is therefore its parent.
+ std::string installation_path =
+ base::FilePath(std::string(system_path_content_dir.begin(),
+ system_path_content_dir.end()))
+ .DirName()
+ .value();
+ char manifest_content[] = R"json(
+ {
+ "manifest_version": 2,
+ "name": "Cobalt",
+ "description": "Cobalt",
+ "version": "1.2.0"
+ })json";
+ CreateManifest(manifest_content, installation_path);
+ CreateEmptyLibrary("libcobalt.so", installation_path);
+
+ EvergreenLibraryMetadata metadata = GetCurrentEvergreenLibraryMetadata();
+
+ ASSERT_EQ(metadata.version, "1.2.0");
+ ASSERT_EQ(metadata.file_type, "Uncompressed");
+
+ DeleteManifest(installation_path);
+ DeleteLibraryDirRecursively(installation_path);
+}
+
+TEST_F(UtilsTest,
+ ReturnsUnknownFileTypeInMetadataForUnexpectedLibInLoadedInstallation) {
+ std::vector<char> system_path_content_dir(kSbFileMaxPath);
+ SbSystemGetPath(kSbSystemPathContentDirectory, system_path_content_dir.data(),
+ kSbFileMaxPath);
+ // Since the libupdater_test.so library has already been loaded,
+ // kSbSystemPathContentDirectory points to the content dir of the running
+ // library and the installation dir is therefore its parent.
+ std::string installation_path =
+ base::FilePath(std::string(system_path_content_dir.begin(),
+ system_path_content_dir.end()))
+ .DirName()
+ .value();
+ CreateEmptyLibrary("libcobalt.unexpected", installation_path);
+
+ EvergreenLibraryMetadata metadata = GetCurrentEvergreenLibraryMetadata();
+
+ ASSERT_EQ(metadata.file_type, "FileTypeUnknown");
+
+ DeleteLibraryDirRecursively(installation_path);
+}
+
+} // namespace
+} // namespace updater
+} // namespace cobalt
diff --git a/cobalt/watchdog/singleton.h b/cobalt/watchdog/singleton.h
index 9b85980..8ae4933 100644
--- a/cobalt/watchdog/singleton.h
+++ b/cobalt/watchdog/singleton.h
@@ -57,9 +57,11 @@
static Type* GetInstance() { return s_singleton; }
static void DeleteInstance() {
- s_singleton->Type::Uninitialize();
- delete s_singleton;
- s_singleton = nullptr;
+ if (s_singleton) {
+ s_singleton->Type::Uninitialize();
+ delete s_singleton;
+ s_singleton = nullptr;
+ }
}
// Prevent copying and moving.
diff --git a/cobalt/watchdog/watchdog.cc b/cobalt/watchdog/watchdog.cc
index 3b6a888..544a47d 100644
--- a/cobalt/watchdog/watchdog.cc
+++ b/cobalt/watchdog/watchdog.cc
@@ -18,7 +18,8 @@
#include <vector>
#include "base/command_line.h"
-#include "base/values.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
#include "cobalt/watchdog/watchdog.h"
#include "starboard/common/file.h"
#include "starboard/common/log.h"
@@ -33,19 +34,24 @@
namespace {
-// The Watchdog violations json file names.
+// The Watchdog violations json filename.
const char kWatchdogViolationsJson[] = "watchdog.json";
-const char kWatchdogPreviousViolationsJson[] = "watchdog_old.json";
// The default number of microseconds between each monitor loop.
const int64_t kWatchdogSmallestTimeInterval = 1000000;
-// The maximum number of repeated Watchdog violations. Prevents excessive
-// Watchdog violation updates.
+// The maximum number of most recent repeated Watchdog violations.
const int64_t kWatchdogMaxViolations = 100;
-// The maximum number of most recent ping infos to store.
+// The maximum number of most recent ping infos.
const int64_t kWatchdogMaxPingInfos = 100;
// Persistent setting name and default setting for the boolean that controls
-// whether or not crashes can be triggered.
+// whether or not Watchdog is enabled. When disabled, Watchdog behaves like a
+// stub except that persistent settings can still be get/set. Requires a
+// restart to take effect.
+const char kPersistentSettingWatchdogEnable[] =
+ "kPersistentSettingWatchdogEnable";
+const bool kDefaultSettingWatchdogEnable = true;
+// Persistent setting name and default setting for the boolean that controls
+// whether or not a Watchdog violation will trigger a crash.
const char kPersistentSettingWatchdogCrash[] =
"kPersistentSettingWatchdogCrash";
const bool kDefaultSettingWatchdogCrash = false;
@@ -54,9 +60,13 @@
bool Watchdog::Initialize(
persistent_storage::PersistentSettings* persistent_settings) {
+ persistent_settings_ = persistent_settings;
+ is_disabled_ = !GetPersistentSettingWatchdogEnable();
+
+ if (is_disabled_) return true;
+
SB_CHECK(SbMutexCreate(&mutex_));
smallest_time_interval_ = kWatchdogSmallestTimeInterval;
- persistent_settings_ = persistent_settings;
#if defined(_DEBUG)
// Sets Watchdog delay settings from command line switch.
@@ -93,52 +103,36 @@
}
bool Watchdog::InitializeStub() {
- is_stub_ = true;
+ is_disabled_ = true;
return true;
}
void Watchdog::Uninitialize() {
+ if (is_disabled_) return;
+
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
is_monitoring_ = false;
SB_CHECK(SbMutexRelease(&mutex_));
SbThreadJoin(watchdog_thread_, nullptr);
}
-std::string Watchdog::GetWatchdogFilePath(bool current) {
- // Gets the Watchdog violations file path or the previous Watchdog violations
- // file path with lazy initialization.
+std::string Watchdog::GetWatchdogFilePath() {
+ // Gets the Watchdog violations file path with lazy initialization.
if (watchdog_file_ == "") {
- // Sets Watchdog violations file paths.
+ // Sets Watchdog violations file path.
std::vector<char> cache_dir(kSbFileMaxPath + 1, 0);
SbSystemGetPath(kSbSystemPathCacheDirectory, cache_dir.data(),
kSbFileMaxPath);
watchdog_file_ = std::string(cache_dir.data()) + kSbFileSepString +
std::string(kWatchdogViolationsJson);
- SB_LOG(INFO) << "Watchdog violations file path: " << watchdog_file_;
- watchdog_old_file_ = std::string(cache_dir.data()) + kSbFileSepString +
- std::string(kWatchdogPreviousViolationsJson);
- PreservePreviousWatchdogViolations();
+ SB_LOG(INFO) << "[Watchdog] Violations filepath: " << watchdog_file_;
}
- if (current) return watchdog_file_;
- return watchdog_old_file_;
-}
-
-void Watchdog::PreservePreviousWatchdogViolations() {
- // Copies the previous Watchdog violations file containing violations before
- // app start, if it exists, to preserve it.
- starboard::ScopedFile read_file(watchdog_file_.c_str(),
- kSbFileOpenOnly | kSbFileRead);
- if (read_file.IsValid()) {
- int64_t kFileSize = read_file.GetSize();
- std::string watchdog_content(kFileSize + 1, '\0');
- read_file.ReadAll(&watchdog_content[0], kFileSize);
- starboard::ScopedFile write_file(watchdog_old_file_.c_str(),
- kSbFileCreateAlways | kSbFileWrite);
- write_file.WriteAll(&watchdog_content[0], kFileSize);
- }
+ return watchdog_file_;
}
void Watchdog::UpdateState(base::ApplicationState state) {
+ if (is_disabled_) return;
+
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
state_ = state;
SB_CHECK(SbMutexRelease(&mutex_));
@@ -156,23 +150,25 @@
int64_t current_time = SbTimeToPosix(SbTimeGetNow());
SbTimeMonotonic current_monotonic_time = SbTimeGetMonotonicNow();
- std::string serialized_client_map = "";
+ base::Value registered_clients(base::Value::Type::LIST);
// Iterates through client map to monitor all registered clients.
- bool new_watchdog_violation = false;
+ bool watchdog_violation = false;
for (auto& it : static_cast<Watchdog*>(context)->client_map_) {
Client* client = it.second.get();
// Ignores and resets clients in idle states, clients whose monitor_state
// is below the current application state. Resets time_wait_microseconds
- // and time_interval_microseconds deltas.
+ // and time_interval_microseconds start values.
if (static_cast<Watchdog*>(context)->state_ > client->monitor_state) {
client->time_registered_monotonic_microseconds = current_monotonic_time;
- client->time_last_pinged_microseconds = current_monotonic_time;
+ client->time_last_updated_monotonic_microseconds =
+ current_monotonic_time;
continue;
}
SbTimeMonotonic time_delta =
- current_monotonic_time - client->time_last_pinged_microseconds;
+ current_monotonic_time -
+ client->time_last_updated_monotonic_microseconds;
SbTimeMonotonic time_wait =
current_monotonic_time -
client->time_registered_monotonic_microseconds;
@@ -180,46 +176,102 @@
// Watchdog violation
if (time_delta > client->time_interval_microseconds &&
time_wait > client->time_wait_microseconds) {
- // Reset time last pinged.
- client->time_last_pinged_microseconds = current_monotonic_time;
- // Get serialized client map.
- if (serialized_client_map == "") {
- serialized_client_map =
- static_cast<Watchdog*>(context)->GetSerializedClientMap();
- }
+ watchdog_violation = true;
- // Updates Watchdog violations.
- auto iter = (static_cast<Watchdog*>(context)->watchdog_violations_)
- .find(client->name);
- bool already_violated =
- iter !=
- (static_cast<Watchdog*>(context)->watchdog_violations_).end();
+ // Gets violation dictionary with key client name from violations_map_.
+ if (static_cast<Watchdog*>(context)->violations_map_ == nullptr)
+ InitializeViolationsMap(context);
+ base::Value* violation_dict =
+ (static_cast<Watchdog*>(context)->violations_map_)
+ ->FindKey(client->name);
- if (already_violated) {
- // Prevents excessive Watchdog violation updates.
- Violation* violation = iter->second.get();
- if (violation->violation_count <= kWatchdogMaxViolations)
- new_watchdog_violation = true;
- violation->ping_infos = client->ping_infos;
- violation->violation_time_microseconds = current_time;
- violation->violation_delta_microseconds = time_delta;
- violation->violation_count++;
- violation->serialized_client_map = serialized_client_map;
+ // Checks if new unique violation.
+ bool new_violation = false;
+ if (violation_dict == nullptr) {
+ new_violation = true;
} else {
- new_watchdog_violation = true;
- std::unique_ptr<Violation> violation(new Violation);
- *violation = *client;
- violation->violation_time_microseconds = current_time;
- violation->violation_delta_microseconds = time_delta;
- violation->violation_count = 1;
- violation->serialized_client_map = serialized_client_map;
- (static_cast<Watchdog*>(context)->watchdog_violations_)
- .emplace(violation->name, std::move(violation));
+ // Compares against last_pinged_timestamp_microsecond of last
+ // violation.
+ base::Value* violations = violation_dict->FindKey("violations");
+ int index = violations->GetList().size() - 1;
+ std::string timestamp_last_pinged_microseconds =
+ violations->GetList()[index]
+ .FindKey("timestampLastPingedMicroseconds")
+ ->GetString();
+ if (timestamp_last_pinged_microseconds !=
+ std::to_string(client->time_last_pinged_microseconds))
+ new_violation = true;
}
+
+ // New unique violation.
+ if (new_violation) {
+ // Creates new violation.
+ base::Value violation(base::Value::Type::DICTIONARY);
+ violation.SetKey("pingInfos", client->ping_infos.Clone());
+ violation.SetKey("monitorState",
+ base::Value(std::string(GetApplicationStateString(
+ client->monitor_state))));
+ violation.SetKey(
+ "timeIntervalMicroseconds",
+ base::Value(std::to_string(client->time_interval_microseconds)));
+ violation.SetKey(
+ "timeWaitMicroseconds",
+ base::Value(std::to_string(client->time_wait_microseconds)));
+ violation.SetKey("timestampRegisteredMicroseconds",
+ base::Value(std::to_string(
+ client->time_registered_microseconds)));
+ violation.SetKey("timestampLastPingedMicroseconds",
+ base::Value(std::to_string(
+ client->time_last_pinged_microseconds)));
+ violation.SetKey("timestampViolationMicroseconds",
+ base::Value(std::to_string(current_time)));
+ violation.SetKey(
+ "violationDurationMicroseconds",
+ base::Value(std::to_string(time_delta -
+ client->time_interval_microseconds)));
+ if (registered_clients.GetList().empty()) {
+ for (auto& it : static_cast<Watchdog*>(context)->client_map_) {
+ registered_clients.GetList().emplace_back(base::Value(it.first));
+ }
+ }
+ violation.SetKey("registeredClients", registered_clients.Clone());
+
+ // Adds new violation to violations_map_.
+ if (violation_dict == nullptr) {
+ base::Value dict(base::Value::Type::DICTIONARY);
+ dict.SetKey("description", base::Value(client->description));
+ base::Value list(base::Value::Type::LIST);
+ list.GetList().emplace_back(violation.Clone());
+ dict.SetKey("violations", list.Clone());
+ (static_cast<Watchdog*>(context)->violations_map_)
+ ->SetKey(client->name, dict.Clone());
+ } else {
+ base::Value* violations = violation_dict->FindKey("violations");
+ violations->GetList().emplace_back(violation.Clone());
+ if (violations->GetList().size() > kWatchdogMaxViolations)
+ violations->GetList().erase(violations->GetList().begin());
+ }
+ // Consecutive non-unique violation.
+ } else {
+ // Updates consecutive violation in violations_map_.
+ base::Value* violations = violation_dict->FindKey("violations");
+ int index = violations->GetList().size() - 1;
+ int64_t violation_duration =
+ std::stoll(violations->GetList()[index]
+ .FindKey("violationDurationMicroseconds")
+ ->GetString());
+ violations->GetList()[index].SetKey(
+ "violationDurationMicroseconds",
+ base::Value(std::to_string(violation_duration + time_delta)));
+ }
+
+ // Resets time last updated.
+ client->time_last_updated_monotonic_microseconds =
+ current_monotonic_time;
}
}
- if (new_watchdog_violation) {
- SerializeWatchdogViolations(context);
+ if (watchdog_violation) {
+ WriteWatchdogViolations(context);
MaybeTriggerCrash(context);
}
@@ -229,73 +281,34 @@
return nullptr;
}
-std::string Watchdog::GetSerializedClientMap() {
- // Gets the current list of registered clients from the client map and
- // returns it as a serialized json string.
- std::string serialized_client_map = "[";
- std::string comma = "";
- for (auto& it : client_map_) {
- serialized_client_map += (comma + "\"" + it.first + "\"");
- comma = ", ";
- }
- serialized_client_map += "]";
- return serialized_client_map;
-}
-
-void Watchdog::SerializeWatchdogViolations(void* context) {
- // Writes Watchdog violations to persistent storage as a partial json file.
- std::string watchdog_json = "";
- std::string comma = "";
- for (auto& it : static_cast<Watchdog*>(context)->watchdog_violations_) {
- Violation* violation = it.second.get();
- std::string ping_infos = "[";
- std::string inner_comma = "";
- while (violation->ping_infos.size() > 0) {
- ping_infos += (inner_comma + "\"" + violation->ping_infos.front() + "\"");
- violation->ping_infos.pop();
- inner_comma = ", ";
- }
- ping_infos += "]";
-
- std::ostringstream ss;
- ss << comma << " {\n"
- << " \"name\": \"" << violation->name << "\",\n"
- << " \"description\": \"" << violation->description << "\",\n"
- << " \"ping_infos\": " << ping_infos << ",\n"
- << " \"monitor_state\": \""
- << std::string(GetApplicationStateString(violation->monitor_state))
- << "\",\n"
- << " \"time_interval_microseconds\": "
- << violation->time_interval_microseconds << ",\n"
- << " \"time_wait_microseconds\": "
- << violation->time_wait_microseconds << ",\n"
- << " \"time_registered_microseconds\": "
- << violation->time_registered_microseconds << ",\n"
- << " \"violation_time_microseconds\": "
- << violation->violation_time_microseconds << ",\n"
- << " \"violation_delta_microseconds\": "
- << violation->violation_delta_microseconds << ",\n"
- << " \"violation_count\": " << violation->violation_count << ",\n"
- << " \"client_map\": " << violation->serialized_client_map << "\n"
- << " }";
- watchdog_json += ss.str();
- comma = ",\n";
- }
-
- // Appends previous Watchdog violations.
+void Watchdog::InitializeViolationsMap(void* context) {
+ // Loads the previous Watchdog violations file containing violations before
+ // app start, if it exists, to populate violations_map_.
starboard::ScopedFile read_file(
- (static_cast<Watchdog*>(context)->GetWatchdogFilePath(false)).c_str(),
+ (static_cast<Watchdog*>(context)->GetWatchdogFilePath()).c_str(),
kSbFileOpenOnly | kSbFileRead);
if (read_file.IsValid()) {
int64_t kFileSize = read_file.GetSize();
- std::string prev_watchdog_json(kFileSize + 1, '\0');
- read_file.ReadAll(&prev_watchdog_json[0], kFileSize);
- prev_watchdog_json.erase(prev_watchdog_json.find('\0'));
- watchdog_json += ",\n";
- watchdog_json += prev_watchdog_json;
+ std::vector<char> buffer(kFileSize + 1, 0);
+ read_file.ReadAll(buffer.data(), kFileSize);
+ std::string watchdog_json = std::string(buffer.data());
+ static_cast<Watchdog*>(context)->violations_map_ =
+ base::JSONReader::Read(watchdog_json);
}
+ if (static_cast<Watchdog*>(context)->violations_map_ == nullptr) {
+ SB_LOG(INFO) << "[Watchdog] No previous violations JSON.";
+ static_cast<Watchdog*>(context)->violations_map_ =
+ std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+ }
+}
- SB_LOG(INFO) << "Writing Watchdog violations:\n" << watchdog_json;
+void Watchdog::WriteWatchdogViolations(void* context) {
+ // Writes Watchdog violations to persistent storage as a json file.
+ std::string watchdog_json;
+ base::JSONWriter::Write(*(static_cast<Watchdog*>(context)->violations_map_),
+ &watchdog_json);
+
+ SB_LOG(INFO) << "[Watchdog] Writing violations to JSON:\n" << watchdog_json;
starboard::ScopedFile watchdog_file(
(static_cast<Watchdog*>(context)->GetWatchdogFilePath()).c_str(),
@@ -306,7 +319,7 @@
void Watchdog::MaybeTriggerCrash(void* context) {
if (static_cast<Watchdog*>(context)->GetPersistentSettingWatchdogCrash()) {
- SB_LOG(ERROR) << "Triggering Watchdog Violation Crash!";
+ SB_LOG(ERROR) << "[Watchdog] Triggering violation Crash!";
CHECK(false);
}
}
@@ -321,11 +334,13 @@
base::ApplicationState monitor_state,
int64_t time_interval, int64_t time_wait,
Replace replace) {
- // Watchdog stub
- if (is_stub_) return true;
+ if (is_disabled_) return true;
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
+ int64_t current_time = SbTimeToPosix(SbTimeGetNow());
+ SbTimeMonotonic current_monotonic_time = SbTimeGetMonotonicNow();
+
// If replace is PING or ALL, handles already registered cases.
if (replace != NONE) {
auto it = client_map_.find(name);
@@ -333,7 +348,9 @@
if (already_registered) {
if (replace == PING) {
- it->second->time_last_pinged_microseconds = SbTimeGetMonotonicNow();
+ it->second->time_last_pinged_microseconds = current_time;
+ it->second->time_last_updated_monotonic_microseconds =
+ current_monotonic_time;
SB_CHECK(SbMutexRelease(&mutex_));
return true;
}
@@ -345,49 +362,50 @@
std::unique_ptr<Client> client(new Client);
client->name = name;
client->description = description;
- client->ping_infos = std::queue<std::string>();
+ client->ping_infos = base::Value(base::Value::Type::LIST);
client->monitor_state = monitor_state;
client->time_interval_microseconds = time_interval;
client->time_wait_microseconds = time_wait;
- client->time_registered_microseconds = SbTimeToPosix(SbTimeGetNow());
- client->time_registered_monotonic_microseconds = SbTimeGetMonotonicNow();
- client->time_last_pinged_microseconds =
- client->time_registered_monotonic_microseconds;
+ client->time_registered_microseconds = current_time;
+ client->time_registered_monotonic_microseconds = current_monotonic_time;
+ client->time_last_pinged_microseconds = current_time;
+ client->time_last_updated_monotonic_microseconds = current_monotonic_time;
// Registers.
auto result = client_map_.emplace(name, std::move(client));
- // Checks for new smallest_time_interval_.
+ // Sets new smallest_time_interval_.
smallest_time_interval_ = std::min(smallest_time_interval_, time_interval);
SB_CHECK(SbMutexRelease(&mutex_));
if (result.second) {
- SB_DLOG(INFO) << "Watchdog Registered: " << name;
+ SB_DLOG(INFO) << "[Watchdog] Registered: " << name;
} else {
- SB_DLOG(ERROR) << "Watchdog Unable to Register: " << name;
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Register: " << name;
}
return result.second;
}
bool Watchdog::Unregister(const std::string& name, bool lock) {
- // Watchdog stub
- if (is_stub_) return true;
+ if (is_disabled_) return true;
if (lock) SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
// Unregisters.
auto result = client_map_.erase(name);
// Sets new smallest_time_interval_.
- smallest_time_interval_ = kWatchdogSmallestTimeInterval;
- for (auto& it : client_map_) {
- smallest_time_interval_ = std::min(smallest_time_interval_,
- it.second->time_interval_microseconds);
+ if (result) {
+ smallest_time_interval_ = kWatchdogSmallestTimeInterval;
+ for (auto& it : client_map_) {
+ smallest_time_interval_ = std::min(smallest_time_interval_,
+ it.second->time_interval_microseconds);
+ }
}
if (lock) SB_CHECK(SbMutexRelease(&mutex_));
if (result) {
- SB_DLOG(INFO) << "Watchdog Unregistered: " << name;
+ SB_DLOG(INFO) << "[Watchdog] Unregistered: " << name;
} else {
- SB_DLOG(ERROR) << "Watchdog Unable to Unregister: " << name;
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Unregister: " << name;
}
return result;
}
@@ -395,25 +413,35 @@
bool Watchdog::Ping(const std::string& name) { return Ping(name, ""); }
bool Watchdog::Ping(const std::string& name, const std::string& info) {
- // Watchdog stub
- if (is_stub_) return true;
+ if (is_disabled_) return true;
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
auto it = client_map_.find(name);
bool client_exists = it != client_map_.end();
if (client_exists) {
+ int64_t current_time = SbTimeToPosix(SbTimeGetNow());
+ SbTimeMonotonic current_monotonic_time = SbTimeGetMonotonicNow();
+
+ Client* client = it->second.get();
// Updates last ping.
- it->second->time_last_pinged_microseconds = SbTimeGetMonotonicNow();
+ client->time_last_pinged_microseconds = current_time;
+ client->time_last_updated_monotonic_microseconds = current_monotonic_time;
+
if (info != "") {
- int64_t current_time = SbTimeToPosix(SbTimeGetNow());
- it->second->ping_infos.push(std::to_string(current_time) + "\\n" + info +
- "\\n");
- if (it->second->ping_infos.size() > kWatchdogMaxPingInfos)
- it->second->ping_infos.pop();
+ // Creates new ping_info.
+ base::Value ping_info(base::Value::Type::DICTIONARY);
+ ping_info.SetKey("timestampMicroseconds",
+ base::Value(std::to_string(current_time)));
+ ping_info.SetKey("info", base::Value(info));
+
+ client->ping_infos.GetList().emplace_back(ping_info.Clone());
+ if (client->ping_infos.GetList().size() > kWatchdogMaxPingInfos)
+ client->ping_infos.GetList().erase(
+ client->ping_infos.GetList().begin());
}
} else {
- SB_DLOG(ERROR) << "Watchdog Unable to Ping: " << name;
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Ping: " << name;
}
SB_CHECK(SbMutexRelease(&mutex_));
return client_exists;
@@ -421,10 +449,9 @@
std::string Watchdog::GetWatchdogViolations() {
// Gets a json string containing the Watchdog violations since the last
- // call (up to a limit).
+ // call (up to the kWatchdogMaxViolations limit).
- // Watchdog stub
- if (is_stub_) return "";
+ if (is_disabled_) return "";
std::string watchdog_json = "";
SB_CHECK(SbMutexAcquire(&mutex_) == kSbMutexAcquired);
@@ -432,28 +459,47 @@
kSbFileOpenOnly | kSbFileRead);
if (read_file.IsValid()) {
int64_t kFileSize = read_file.GetSize();
- std::string watchdog_content(kFileSize + 1, '\0');
- read_file.ReadAll(&watchdog_content[0], kFileSize);
- watchdog_content.erase(watchdog_content.find('\0'));
- watchdog_json = "{\n \"watchdog_violations\": [\n";
- watchdog_json += watchdog_content;
- watchdog_json += "\n ]\n}";
+ std::vector<char> buffer(kFileSize + 1, 0);
+ read_file.ReadAll(buffer.data(), kFileSize);
+ watchdog_json = std::string(buffer.data());
// Removes all Watchdog violations.
- watchdog_violations_.clear();
+ if (violations_map_) {
+ base::DictionaryValue** dict = nullptr;
+ violations_map_->GetAsDictionary(dict);
+ if (dict) (*dict)->Clear();
+ }
starboard::SbFileDeleteRecursive(GetWatchdogFilePath().c_str(), true);
- starboard::SbFileDeleteRecursive(GetWatchdogFilePath(false).c_str(), true);
- SB_LOG(INFO) << "Reading Watchdog violations:\n" << watchdog_json;
+ SB_LOG(INFO) << "[Watchdog] Reading violations:\n" << watchdog_json;
} else {
- SB_LOG(INFO) << "No Watchdog Violations.";
+ SB_LOG(INFO) << "[Watchdog] No violations.";
}
SB_CHECK(SbMutexRelease(&mutex_));
return watchdog_json;
}
+bool Watchdog::GetPersistentSettingWatchdogEnable() {
+ // Watchdog stub
+ if (!persistent_settings_) return kDefaultSettingWatchdogEnable;
+
+ // Gets the boolean that controls whether or not Watchdog is enabled.
+ return persistent_settings_->GetPersistentSettingAsBool(
+ kPersistentSettingWatchdogEnable, kDefaultSettingWatchdogEnable);
+}
+
+void Watchdog::SetPersistentSettingWatchdogEnable(bool enable_watchdog) {
+ // Watchdog stub
+ if (!persistent_settings_) return;
+
+ // Sets the boolean that controls whether or not Watchdog is enabled.
+ persistent_settings_->SetPersistentSetting(
+ kPersistentSettingWatchdogEnable,
+ std::make_unique<base::Value>(enable_watchdog));
+}
+
bool Watchdog::GetPersistentSettingWatchdogCrash() {
// Watchdog stub
- if (is_stub_) return kDefaultSettingWatchdogCrash;
+ if (!persistent_settings_) return kDefaultSettingWatchdogCrash;
// Gets the boolean that controls whether or not crashes can be triggered.
return persistent_settings_->GetPersistentSettingAsBool(
@@ -462,7 +508,7 @@
void Watchdog::SetPersistentSettingWatchdogCrash(bool can_trigger_crash) {
// Watchdog stub
- if (is_stub_) return;
+ if (!persistent_settings_) return;
// Sets the boolean that controls whether or not crashes can be triggered.
persistent_settings_->SetPersistentSetting(
@@ -473,14 +519,14 @@
#if defined(_DEBUG)
// Sleeps threads based off of environment variables for Watchdog debugging.
void Watchdog::MaybeInjectDebugDelay(const std::string& name) {
- // Watchdog stub
- if (is_stub_) return;
+ if (is_disabled_) return;
starboard::ScopedLock scoped_lock(delay_lock_);
if (name != delay_name_) return;
SbTimeMonotonic current_time = SbTimeGetMonotonicNow();
+
if (time_last_delayed_microseconds_ == 0)
time_last_delayed_microseconds_ = current_time;
diff --git a/cobalt/watchdog/watchdog.h b/cobalt/watchdog/watchdog.h
index ed14db0..b4535f1 100644
--- a/cobalt/watchdog/watchdog.h
+++ b/cobalt/watchdog/watchdog.h
@@ -16,10 +16,10 @@
#define COBALT_WATCHDOG_WATCHDOG_H_
#include <memory>
-#include <queue>
#include <string>
#include <unordered_map>
+#include "base/values.h"
#include "cobalt/base/application_state.h"
#include "cobalt/persistent_storage/persistent_settings.h"
#include "cobalt/watchdog/singleton.h"
@@ -36,27 +36,7 @@
std::string name;
std::string description;
// List of strings optionally provided with each Ping.
- std::queue<std::string> ping_infos;
- // Application state to continue monitoring client up to.
- base::ApplicationState monitor_state;
- // Maximum number of microseconds allowed between pings before triggering a
- // Watchdog violation.
- int64_t time_interval_microseconds;
- // Number of microseconds to initially wait before Watchdog violations can be
- // triggered. Reapplies after client resumes from idle state due to
- // application state changes.
- int64_t time_wait_microseconds;
- int64_t time_registered_microseconds; // since epoch
- SbTimeMonotonic time_registered_monotonic_microseconds; // since (relative)
- SbTimeMonotonic time_last_pinged_microseconds; // since (relative)
-} Client;
-
-// Watchdog violation
-typedef struct Violation {
- std::string name;
- std::string description;
- // List of strings optionally provided with each Ping.
- std::queue<std::string> ping_infos;
+ base::Value ping_infos;
// Application state to continue monitoring client up to. Inclusive.
base::ApplicationState monitor_state;
// Maximum number of microseconds allowed between pings before triggering a
@@ -66,23 +46,22 @@
// triggered. Reapplies after client resumes from idle state due to
// application state changes.
int64_t time_wait_microseconds;
- int64_t time_registered_microseconds; // since epoch
- int64_t violation_time_microseconds; // since epoch
- int64_t violation_delta_microseconds; // over time_interval
- int64_t violation_count;
- // Client map as a serialized json string
- std::string serialized_client_map;
-
- void operator=(const Client& c) {
- name = c.name;
- description = c.description;
- ping_infos = c.ping_infos;
- monitor_state = c.monitor_state;
- time_interval_microseconds = c.time_wait_microseconds;
- time_wait_microseconds = c.time_wait_microseconds;
- time_registered_microseconds = c.time_registered_microseconds;
- }
-} Violation;
+ // Epoch time when client was registered.
+ int64_t time_registered_microseconds;
+ // Monotonically increasing timestamp when client was registered. Used as the
+ // start value for time wait calculations.
+ SbTimeMonotonic time_registered_monotonic_microseconds;
+ // Epoch time when client was last pinged. Set by Ping() and Register() when
+ // in PING replace mode or set initially by Register().
+ int64_t time_last_pinged_microseconds;
+ // Monotonically increasing timestamp when client was last updated. Set by
+ // Ping() and Register() when in PING replace mode or set initially by
+ // Register(). Also reset by Monitor() when in idle states or when a
+ // violation occurs. Prevents excessive violations as they must occur
+ // time_interval_microseconds apart rather than smallest_time_interval_
+ // apart. Used as the start value for time interval calculations.
+ SbTimeMonotonic time_last_updated_monotonic_microseconds;
+} Client;
// Register behavior with previously registered clients of the same name.
enum Replace {
@@ -107,6 +86,8 @@
bool Ping(const std::string& name);
bool Ping(const std::string& name, const std::string& info);
std::string GetWatchdogViolations();
+ bool GetPersistentSettingWatchdogEnable();
+ void SetPersistentSettingWatchdogEnable(bool enable_watchdog);
bool GetPersistentSettingWatchdogCrash();
void SetPersistentSettingWatchdogCrash(bool can_trigger_crash);
@@ -116,39 +97,36 @@
#endif // defined(_DEBUG)
private:
- std::string GetWatchdogFilePath(bool current = true);
- void PreservePreviousWatchdogViolations();
+ std::string GetWatchdogFilePath();
static void* Monitor(void* context);
- std::string GetSerializedClientMap();
- static void SerializeWatchdogViolations(void* context);
+ static void InitializeViolationsMap(void* context);
+ static void WriteWatchdogViolations(void* context);
static void MaybeTriggerCrash(void* context);
- // Watchdog violations file paths.
+ // Watchdog violations file path.
std::string watchdog_file_;
- std::string watchdog_old_file_;
+ // Access to persistent settings.
+ persistent_storage::PersistentSettings* persistent_settings_;
+ // Flag to disable Watchdog. When disabled, Watchdog behaves like a stub
+ // except that persistent settings can still be get/set.
+ bool is_disabled_;
// Creates a lock which ensures that each loop of monitor is atomic in that
// modifications to is_monitoring_, state_, smallest_time_interval_, and most
// importantly to the dictionaries containing Watchdog clients, client_map_
- // and watchdog_violations_, only occur in between loops of monitor. API
- // functions like Register(), Unregister(), Ping(), and
- // GetWatchdogViolations() will be called by various threads and interact
- // with these class variables.
+ // and violations_map_, only occur in between loops of monitor. API functions
+ // like Register(), Unregister(), Ping(), and GetWatchdogViolations() will be
+ // called by various threads and interact with these class variables.
SbMutex mutex_;
- // Time interval between monitor loops.
- int64_t smallest_time_interval_;
- // Access to persistent settings.
- persistent_storage::PersistentSettings* persistent_settings_;
- // Monitor thread.
- SbThread watchdog_thread_;
// Tracks application state.
base::ApplicationState state_ = base::kApplicationStateStarted;
+ // Time interval between monitor loops.
+ int64_t smallest_time_interval_;
// Dictionary of registered Watchdog clients.
std::unordered_map<std::string, std::unique_ptr<Client>> client_map_;
- // Dictionary of Watchdog violations.
- std::unordered_map<std::string, std::unique_ptr<Violation>>
- watchdog_violations_;
- // Flag to stub out Watchdog.
- bool is_stub_ = false;
+ // Dictionary of lists of Watchdog violations represented as dictionaries.
+ std::unique_ptr<base::Value> violations_map_;
+ // Monitor thread.
+ SbThread watchdog_thread_;
// Flag to stop monitor thread.
bool is_monitoring_;
diff --git a/cobalt/web/cobalt_ua_data_values.idl b/cobalt/web/cobalt_ua_data_values.idl
index 2dea119..d8832df 100644
--- a/cobalt/web/cobalt_ua_data_values.idl
+++ b/cobalt/web/cobalt_ua_data_values.idl
@@ -20,6 +20,7 @@
DOMString jsEngineVersion;
DOMString rasterizer;
DOMString evergreenType;
+ DOMString evergreenFileType;
DOMString evergreenVersion;
DOMString starboardVersion;
DOMString originalDesignManufacturer;
diff --git a/cobalt/web/cobalt_ua_data_values_interface.cc b/cobalt/web/cobalt_ua_data_values_interface.cc
index 35504a8..d1dd39a 100644
--- a/cobalt/web/cobalt_ua_data_values_interface.cc
+++ b/cobalt/web/cobalt_ua_data_values_interface.cc
@@ -61,6 +61,9 @@
if (init_dict.has_evergreen_type()) {
evergreen_type_ = init_dict.evergreen_type();
}
+ if (init_dict.has_evergreen_file_type()) {
+ evergreen_file_type_ = init_dict.evergreen_file_type();
+ }
if (init_dict.has_evergreen_version()) {
evergreen_version_ = init_dict.evergreen_version();
}
diff --git a/cobalt/web/cobalt_ua_data_values_interface.h b/cobalt/web/cobalt_ua_data_values_interface.h
index 91dff3a..15612a1 100644
--- a/cobalt/web/cobalt_ua_data_values_interface.h
+++ b/cobalt/web/cobalt_ua_data_values_interface.h
@@ -44,6 +44,9 @@
const std::string& js_engine_version() const { return js_engine_version_; }
const std::string& rasterizer() const { return rasterizer_; }
const std::string& evergreen_type() const { return evergreen_type_; }
+ const std::string& evergreen_file_type() const {
+ return evergreen_file_type_;
+ }
const std::string& evergreen_version() const { return evergreen_version_; }
const std::string& starboard_version() const { return starboard_version_; }
const std::string& original_design_manufacturer() const {
@@ -74,6 +77,7 @@
std::string js_engine_version_;
std::string rasterizer_;
std::string evergreen_type_;
+ std::string evergreen_file_type_;
std::string evergreen_version_;
std::string starboard_version_;
std::string original_design_manufacturer_;
diff --git a/cobalt/web/cobalt_ua_data_values_interface.idl b/cobalt/web/cobalt_ua_data_values_interface.idl
index ff1b119..6243b22 100644
--- a/cobalt/web/cobalt_ua_data_values_interface.idl
+++ b/cobalt/web/cobalt_ua_data_values_interface.idl
@@ -30,6 +30,7 @@
readonly attribute DOMString jsEngineVersion;
readonly attribute DOMString rasterizer;
readonly attribute DOMString evergreenType;
+ readonly attribute DOMString evergreenFileType;
readonly attribute DOMString evergreenVersion;
readonly attribute DOMString starboardVersion;
readonly attribute DOMString originalDesignManufacturer;
diff --git a/cobalt/web/navigator_ua_data.cc b/cobalt/web/navigator_ua_data.cc
index 93ec852..b2bae8b 100644
--- a/cobalt/web/navigator_ua_data.cc
+++ b/cobalt/web/navigator_ua_data.cc
@@ -56,6 +56,8 @@
platform_info->javascript_engine_version());
all_high_entropy_values_.set_rasterizer(platform_info->rasterizer_type());
all_high_entropy_values_.set_evergreen_type(platform_info->evergreen_type());
+ all_high_entropy_values_.set_evergreen_file_type(
+ platform_info->evergreen_file_type());
all_high_entropy_values_.set_evergreen_version(
platform_info->evergreen_version());
all_high_entropy_values_.set_starboard_version(
diff --git a/cobalt/web/user_agent_platform_info.h b/cobalt/web/user_agent_platform_info.h
index 995051d..4d7e51a 100644
--- a/cobalt/web/user_agent_platform_info.h
+++ b/cobalt/web/user_agent_platform_info.h
@@ -43,6 +43,7 @@
virtual const std::string& javascript_engine_version() const = 0;
virtual const std::string& rasterizer_type() const = 0;
virtual const std::string& evergreen_type() const = 0;
+ virtual const std::string& evergreen_file_type() const = 0;
virtual const std::string& evergreen_version() const = 0;
virtual const std::string& cobalt_version() const = 0;
diff --git a/cobalt/worker/clients.cc b/cobalt/worker/clients.cc
index 5df114d..b1ce469 100644
--- a/cobalt/worker/clients.cc
+++ b/cobalt/worker/clients.cc
@@ -75,7 +75,8 @@
jobs->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&ServiceWorkerJobs::ClientsGetSubSteps,
- base::Unretained(jobs), base::Unretained(settings_),
+ base::Unretained(jobs),
+ base::Unretained(settings_->context()),
base::Unretained(GetAssociatedServiceWorker(settings_)),
std::move(promise_reference), id));
@@ -103,7 +104,8 @@
jobs->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&ServiceWorkerJobs::ClientsMatchAllSubSteps,
- base::Unretained(jobs), base::Unretained(settings_),
+ base::Unretained(jobs),
+ base::Unretained(settings_->context()),
base::Unretained(GetAssociatedServiceWorker(settings_)),
std::move(promise_reference),
options.include_uncontrolled(), options.type()));
@@ -156,7 +158,7 @@
jobs->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&ServiceWorkerJobs::ClaimSubSteps, base::Unretained(jobs),
- base::Unretained(settings_),
+ base::Unretained(settings_->context()),
base::Unretained(GetAssociatedServiceWorker(settings_)),
std::move(promise_reference)));
// 4. Return promise.
diff --git a/cobalt/worker/extendable_event.h b/cobalt/worker/extendable_event.h
index 1765ad2..e7d7a6a 100644
--- a/cobalt/worker/extendable_event.h
+++ b/cobalt/worker/extendable_event.h
@@ -20,6 +20,8 @@
#include <utility>
#include "base/bind.h"
+#include "base/callback.h"
+#include "base/synchronization/waitable_event.h"
#include "cobalt/base/token.h"
#include "cobalt/script/promise.h"
#include "cobalt/script/v8c/native_promise.h"
@@ -42,7 +44,10 @@
class ExtendableEvent : public web::Event {
public:
explicit ExtendableEvent(const std::string& type) : Event(type) {}
- explicit ExtendableEvent(base::Token type) : Event(type) {}
+ explicit ExtendableEvent(base::Token type,
+ base::OnceCallback<void(bool)> done_callback =
+ base::OnceCallback<void(bool)>())
+ : Event(type), done_callback_(std::move(done_callback)) {}
ExtendableEvent(const std::string& type, const ExtendableEventInit& init_dict)
: Event(type, init_dict) {}
@@ -86,6 +91,9 @@
--pending_promise_count_;
// 5.2. If event’s pending promises count is 0, then:
if (0 == pending_promise_count_) {
+ if (done_callback_) {
+ std::move(done_callback_).Run(has_rejected_promise_);
+ }
web::Context* context =
base::polymorphic_downcast<web::EnvironmentSettings*>(settings)
->context();
@@ -115,6 +123,8 @@
((pending_promise_count_ > 0) || IsBeingDispatched());
}
+ bool has_rejected_promise() const { return has_rejected_promise_; }
+
DEFINE_WRAPPABLE_TYPE(ExtendableEvent);
protected:
@@ -127,6 +137,8 @@
bool has_rejected_promise_ = false;
// https://w3c.github.io/ServiceWorker/#extendableevent-timed-out-flag
bool timed_out_flag_ = false;
+
+ base::OnceCallback<void(bool)> done_callback_;
};
} // namespace worker
diff --git a/cobalt/worker/service_worker.h b/cobalt/worker/service_worker.h
index 2b68fc2..1e39dfe 100644
--- a/cobalt/worker/service_worker.h
+++ b/cobalt/worker/service_worker.h
@@ -45,7 +45,7 @@
//
void PostMessage(const std::string& message) {
DCHECK(message_port_);
- message_port_->PostMessage(message);
+ if (worker_->worker_global_scope()) message_port_->PostMessage(message);
}
// The scriptURL getter steps are to return the
@@ -82,7 +82,7 @@
worker_ = nullptr;
}
- ServiceWorkerObject* worker_ = nullptr;
+ scoped_refptr<ServiceWorkerObject> worker_;
scoped_refptr<web::MessagePort> message_port_;
ServiceWorkerState state_;
};
diff --git a/cobalt/worker/service_worker_global_scope.cc b/cobalt/worker/service_worker_global_scope.cc
index 9a7a6c9..cf7f3b6 100644
--- a/cobalt/worker/service_worker_global_scope.cc
+++ b/cobalt/worker/service_worker_global_scope.cc
@@ -169,7 +169,7 @@
FROM_HERE,
base::BindOnce(&ServiceWorkerJobs::SkipWaitingSubSteps,
base::Unretained(jobs),
- base::Unretained(environment_settings()),
+ base::Unretained(environment_settings()->context()),
service_worker_object_, std::move(promise_reference)));
// 3. Return promise.
return promise;
diff --git a/cobalt/worker/service_worker_jobs.cc b/cobalt/worker/service_worker_jobs.cc
index 9ba0101..0c31df7 100644
--- a/cobalt/worker/service_worker_jobs.cc
+++ b/cobalt/worker/service_worker_jobs.cc
@@ -25,9 +25,11 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_current.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/task_runner.h"
+#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "cobalt/base/tokens.h"
#include "cobalt/dom/visibility_state.h"
@@ -733,6 +735,19 @@
// 16. Let runResult be the result of running the Run Service Worker
// algorithm with worker and forceBypassCache.
auto* run_result = RunServiceWorker(worker.get(), force_bypass_cache);
+ bool run_result_is_success = run_result;
+
+ // Post a task for the remaining steps, to let tasks posted by
+ // RunServiceWorker, such as for registering the web context, execute first.
+ base::MessageLoop::current()->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&ServiceWorkerJobs::UpdateOnRunServiceWorker,
+ base::Unretained(this), state, std::move(worker),
+ run_result_is_success));
+}
+
+void ServiceWorkerJobs::UpdateOnRunServiceWorker(
+ scoped_refptr<UpdateJobState> state,
+ scoped_refptr<ServiceWorkerObject> worker, bool run_result) {
// 17. If runResult is failure or an abrupt completion, then:
if (!run_result) {
// 17.1. Invoke Reject Job Promise with job and TypeError.
@@ -799,7 +814,11 @@
// https://w3c.github.io/ServiceWorker/#installation-algorithm
// 1. Let installFailed be false.
- starboard::atomic_bool install_failed(false);
+ // Using a shared pointer because this flag is explicitly defined in the spec
+ // to be modified from the worker's event loop, at asynchronous promise
+ // completion that may occur after a timeout.
+ std::shared_ptr<starboard::atomic_bool> install_failed(
+ new starboard::atomic_bool(false));
// 2. Let newestWorker be the result of running Get Newest Worker algorithm
// passing registration as its argument.
@@ -873,11 +892,16 @@
auto* run_result = RunServiceWorker(installing_worker, force_bypass_cache);
if (!run_result) {
// 11.2.1. Set installFailed to true.
- install_failed.store(true);
+ install_failed->store(true);
// 11.3. Else:
} else {
// 11.3.1. Queue a task task on installingWorker’s event loop using the
// DOM manipulation task source to run the following steps:
+ // Using a shared pointer to ensure that it still exists when it is
+ // signaled from the callback after the timeout.
+ std::shared_ptr<base::WaitableEvent> done_event(new base::WaitableEvent(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED));
installing_worker->web_agent()
->context()
->message_loop()
@@ -885,14 +909,13 @@
->PostBlockingTask(
FROM_HERE,
base::Bind(
- [](ServiceWorkerObject* installing_worker) {
+ [](ServiceWorkerObject* installing_worker,
+ std::shared_ptr<base::WaitableEvent> done_event,
+ std::shared_ptr<starboard::atomic_bool> install_failed) {
// 11.3.1.1. Let e be the result of creating an event with
// ExtendableEvent.
- // TODO(b/228976500): implement this as ExtendableEvent.
// 11.3.1.2. Initialize e’s type attribute to install.
// 11.3.1.3. Dispatch e at installingWorker’s global object.
- installing_worker->worker_global_scope()->DispatchEvent(
- new ExtendableEvent(base::Tokens::install()));
// 11.3.1.4. WaitForAsynchronousExtensions: Run the
// following substeps in parallel:
// 11.3.1.4.1. Wait until e is not active.
@@ -903,17 +926,43 @@
// 11.3.1.4.4. Upon rejection of p, set installFailed to
// true.
// If task is discarded, set installFailed to true.
+ auto done_callback = base::BindOnce(
+ [](std::shared_ptr<base::WaitableEvent> done_event,
+ std::shared_ptr<starboard::atomic_bool>
+ install_failed,
+ bool was_rejected) {
+ if (was_rejected) install_failed->store(true);
+ done_event->Signal();
+ },
+ done_event, install_failed);
+ scoped_refptr<ExtendableEvent> event(new ExtendableEvent(
+ base::Tokens::install(), std::move(done_callback)));
+ installing_worker->worker_global_scope()->DispatchEvent(
+ event);
+ if (!event->IsActive()) {
+ // If the event handler doesn't use waitUntil(), it will
+ // already no longer be active, and there will never be a
+ // callback to signal the done event.
+ done_event->Signal();
+ }
},
- base::Unretained(installing_worker)));
+ base::Unretained(installing_worker), done_event,
+ install_failed));
// 11.3.2. Wait for task to have executed or been discarded.
- // Waiting is done inside PostBlockingTask above.
+ // This waiting is done inside PostBlockingTask above.
// 11.3.3. Wait for the step labeled WaitForAsynchronousExtensions to
// complete.
- NOTIMPLEMENTED();
+ // TODO(b/240164388): Investigate a better approach for combining waiting
+ // for the ExtendableEvent while also allowing use of algorithms that run
+ // on the same thread from the event handler.
+ while (!done_event->TimedWait(base::TimeDelta::FromMilliseconds(100))) {
+ base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
+ base::RunLoop().RunUntilIdle();
+ }
}
}
// 12. If installFailed is true, then:
- if (install_failed.load()) {
+ if (install_failed->load()) {
// 12.1. Run the Update Worker State algorithm passing registration’s
// installing worker and "redundant" as the arguments.
UpdateWorkerState(registration->installing_worker(),
@@ -1124,6 +1173,9 @@
active_worker->worker_global_scope()
->environment_settings()
->context());
+ std::shared_ptr<base::WaitableEvent> done_event(new base::WaitableEvent(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED));
active_worker->web_agent()
->context()
->message_loop()
@@ -1131,23 +1183,40 @@
->PostBlockingTask(
FROM_HERE,
base::Bind(
- [](ServiceWorkerObject* active_worker) {
+ [](ServiceWorkerObject* active_worker,
+ std::shared_ptr<base::WaitableEvent> done_event) {
+ auto done_callback = base::BindOnce(
+ [](std::shared_ptr<base::WaitableEvent> done_event,
+ bool) { done_event->Signal(); },
+ done_event);
+ scoped_refptr<ExtendableEvent> event(new ExtendableEvent(
+ base::Tokens::activate(), std::move(done_callback)));
// 11.1.1.1. Let e be the result of creating an event with
// ExtendableEvent.
- // TODO(b/228976500): implement this as ExtendableEvent.
// 11.1.1.2. Initialize e’s type attribute to activate.
// 11.1.1.3. Dispatch e at activeWorker’s global object.
- active_worker->worker_global_scope()->DispatchEvent(
- new ExtendableEvent(base::Tokens::activate()));
+ active_worker->worker_global_scope()->DispatchEvent(event);
// 11.1.1.4. WaitForAsynchronousExtensions: Wait, in
// parallel, until e is not active.
+ if (!event->IsActive()) {
+ // If the event handler doesn't use waitUntil(), it will
+ // already no longer be active, and there will never be a
+ // callback to signal the done event.
+ done_event->Signal();
+ }
},
- base::Unretained(active_worker)));
+ base::Unretained(active_worker), done_event));
// 11.1.2. Wait for task to have executed or been discarded.
- // Waiting is done inside PostBlockingTask above.
+ // This waiting is done inside PostBlockingTask above.
// 11.1.3. Wait for the step labeled WaitForAsynchronousExtensions to
// complete.
- NOTIMPLEMENTED();
+ // TODO(b/240164388): Investigate a better approach for combining waiting
+ // for the ExtendableEvent while also allowing use of algorithms that run
+ // on the same thread from the event handler.
+ while (!done_event->TimedWait(base::TimeDelta::FromMilliseconds(100))) {
+ base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
+ base::RunLoop().RunUntilIdle();
+ }
}
}
// 12. Run the Update Worker State algorithm passing registration’s active
@@ -1183,7 +1252,7 @@
ServiceWorkerObject* worker) {
// Algorithm for Service Worker Has No Pending Events
// https://w3c.github.io/ServiceWorker/#service-worker-has-no-pending-events
- // TODO(b/228976500): implement this from ExtendableEvent support.
+ // TODO(b/240174245): Implement this using the 'set of extended events'.
NOTIMPLEMENTED();
// 1. For each event of worker’s set of extended events:
@@ -1503,7 +1572,7 @@
service_worker_global_scope->set_closing_flag(true);
// 1.3. Remove all the items from serviceWorker’s set of extended events.
- // TODO(b/228976500): implement this with ExtendableEvent support.
+ // TODO(b/240174245): Implement 'set of extended events'.
// 1.4. If there are any tasks, whose task source is either the handle fetch
// task source or the handle functional event task source, queued in
@@ -1813,11 +1882,18 @@
}
void ServiceWorkerJobs::SkipWaitingSubSteps(
- web::EnvironmentSettings* client,
+ web::Context* client_context,
const base::WeakPtr<ServiceWorkerObject>& service_worker,
std::unique_ptr<script::ValuePromiseVoid::Reference> promise_reference) {
TRACE_EVENT0("cobalt::worker", "ServiceWorkerJobs::SkipWaitingSubSteps()");
DCHECK_EQ(message_loop(), base::MessageLoop::current());
+ // Check if the client web context is still active. This may trigger if
+ // skipWaiting() was called and service worker installation fails.
+ if (!IsWebContextRegistered(client_context)) {
+ promise_reference.release();
+ return;
+ }
+
// Algorithm for Sub steps of ServiceWorkerGlobalScope.skipWaiting():
// https://w3c.github.io/ServiceWorker/#dom-serviceworkerglobalscope-skipwaiting
@@ -1829,7 +1905,7 @@
TryActivate(service_worker->containing_service_worker_registration());
// 2.3. Resolve promise with undefined.
- client->context()->message_loop()->task_runner()->PostTask(
+ client_context->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<script::ValuePromiseVoid::Reference> promise) {
@@ -1855,13 +1931,20 @@
TryActivate(registration);
}
}
+
void ServiceWorkerJobs::ClientsGetSubSteps(
- web::EnvironmentSettings* settings,
+ web::Context* promise_context,
ServiceWorkerObject* associated_service_worker,
std::unique_ptr<script::ValuePromiseWrappable::Reference> promise_reference,
const std::string& id) {
TRACE_EVENT0("cobalt::worker", "ServiceWorkerJobs::ClientsGetSubSteps()");
DCHECK_EQ(message_loop_, base::MessageLoop::current());
+ // Check if the client web context is still active. This may trigger if
+ // Clients.get() was called and service worker installation fails.
+ if (!IsWebContextRegistered(promise_context)) {
+ promise_reference.release();
+ return;
+ }
// Parallel sub steps (2) for algorithm for Clients.get(id):
// https://w3c.github.io/ServiceWorker/#clients-get
// 2.1. For each service worker client client where the result of running
@@ -1884,27 +1967,26 @@
// 2.1.3. If client’s execution ready flag is set, then invoke Resolve Get
// Client Promise with client and promise, and abort these steps.
- ResolveGetClientPromise(client, settings, std::move(promise_reference));
+ ResolveGetClientPromise(client, promise_context,
+ std::move(promise_reference));
return;
}
}
// 2.2. Resolve promise with undefined.
- settings->context()->message_loop()->task_runner()->PostTask(
+ promise_context->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<script::ValuePromiseWrappable::Reference>
promise_reference) {
- TRACE_EVENT0(
- "cobalt::worker",
- "ServiceWorkerJobs::ResolveGetClientPromise() Resolve");
+ TRACE_EVENT0("cobalt::worker",
+ "ServiceWorkerJobs::ClientsGetSubSteps() Resolve");
promise_reference->value().Resolve(scoped_refptr<Client>());
},
std::move(promise_reference)));
}
void ServiceWorkerJobs::ResolveGetClientPromise(
- web::EnvironmentSettings* client,
- web::EnvironmentSettings* promise_relevant_settings,
+ web::EnvironmentSettings* client, web::Context* promise_context,
std::unique_ptr<script::ValuePromiseWrappable::Reference>
promise_reference) {
TRACE_EVENT0("cobalt::worker",
@@ -1935,21 +2017,18 @@
// 3.2. Queue a task to resolve promise with clientObject, on promise’s
// relevant settings object's responsible event loop using the DOM
// manipulation task source, and abort these steps.
- promise_relevant_settings->context()
- ->message_loop()
- ->task_runner()
- ->PostTask(
- FROM_HERE,
- base::BindOnce(
- [](std::unique_ptr<script::ValuePromiseWrappable::Reference>
- promise_reference,
- scoped_refptr<Client> client_object) {
- TRACE_EVENT0(
- "cobalt::worker",
- "ServiceWorkerJobs::ResolveGetClientPromise() Resolve");
- promise_reference->value().Resolve(client_object);
- },
- std::move(promise_reference), client_object));
+ promise_context->message_loop()->task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ [](std::unique_ptr<script::ValuePromiseWrappable::Reference>
+ promise_reference,
+ scoped_refptr<Client> client_object) {
+ TRACE_EVENT0(
+ "cobalt::worker",
+ "ServiceWorkerJobs::ResolveGetClientPromise() Resolve");
+ promise_reference->value().Resolve(client_object);
+ },
+ std::move(promise_reference), client_object));
return;
}
// 4. Else:
@@ -1968,8 +2047,7 @@
client->context()->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
- [](web::EnvironmentSettings* client,
- web::EnvironmentSettings* promise_relevant_settings,
+ [](web::EnvironmentSettings* client, web::Context* promise_context,
std::unique_ptr<script::ValuePromiseWrappable::Reference>
promise_reference) {
std::unique_ptr<WindowData> window_data(new WindowData);
@@ -1999,36 +2077,32 @@
// 4.4.6. Queue a task to run the following steps on promise’s
// relevant settings object's responsible event loop using
// the DOM manipulation task source:
- promise_relevant_settings->context()
- ->message_loop()
- ->task_runner()
- ->PostTask(
- FROM_HERE,
- base::BindOnce(
- [](std::unique_ptr<
- script::ValuePromiseWrappable::Reference>
- promise_reference,
- std::unique_ptr<WindowData> window_data) {
- // 4.4.6.1. If client’s discarded flag is set, resolve
- // promise with undefined and abort these
- // steps.
- // 4.4.6.2. Let windowClient be the result of running
- // Create Window Client with client,
- // frameType, visibilityState, focusState,
- // and ancestorOriginsList.
- scoped_refptr<WindowClient> window_client =
- WindowClient::Create(*window_data);
- // 4.4.6.3. Resolve promise with windowClient.
- promise_reference->value().Resolve(window_client);
- },
- std::move(promise_reference), std::move(window_data)));
+ promise_context->message_loop()->task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ [](std::unique_ptr<script::ValuePromiseWrappable::Reference>
+ promise_reference,
+ std::unique_ptr<WindowData> window_data) {
+ // 4.4.6.1. If client’s discarded flag is set, resolve
+ // promise with undefined and abort these
+ // steps.
+ // 4.4.6.2. Let windowClient be the result of running
+ // Create Window Client with client,
+ // frameType, visibilityState, focusState,
+ // and ancestorOriginsList.
+ scoped_refptr<WindowClient> window_client =
+ WindowClient::Create(*window_data);
+ // 4.4.6.3. Resolve promise with windowClient.
+ promise_reference->value().Resolve(window_client);
+ },
+ std::move(promise_reference), std::move(window_data)));
},
- client, promise_relevant_settings, std::move(promise_reference)));
+ client, promise_context, std::move(promise_reference)));
DCHECK_EQ(nullptr, promise_reference.get());
}
void ServiceWorkerJobs::ClientsMatchAllSubSteps(
- web::EnvironmentSettings* settings,
+ web::Context* client_context,
ServiceWorkerObject* associated_service_worker,
std::unique_ptr<script::ValuePromiseSequenceWrappable::Reference>
promise_reference,
@@ -2036,6 +2110,13 @@
TRACE_EVENT0("cobalt::worker",
"ServiceWorkerJobs::ClientsMatchAllSubSteps()");
DCHECK_EQ(message_loop_, base::MessageLoop::current());
+ // Check if the client web context is still active. This may trigger if
+ // Clients.matchAll() was called and service worker installation fails.
+ if (!IsWebContextRegistered(client_context)) {
+ promise_reference.release();
+ return;
+ }
+
// Parallel sub steps (2) for algorithm for Clients.matchAll():
// https://w3c.github.io/ServiceWorker/#clients-matchall
// 2.1. Let targetClients be a new list.
@@ -2106,7 +2187,7 @@
// browsingContext to client’s global object's browsing context.
// 2.5.1.5. Else, set browsingContext to client’s target browsing context.
- web::Context* browsing_context = settings->context();
+ web::Context* browsing_context = client_context;
// 2.5.1.6. Queue a task task to run the following substeps on
// browsingContext’s event loop using the user interaction task
@@ -2183,7 +2264,7 @@
// 2.6. Queue a task to run the following steps on promise’s relevant
// settings object's responsible event loop using the DOM manipulation
// task source:
- settings->context()->message_loop()->task_runner()->PostTask(
+ client_context->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<script::ValuePromiseSequenceWrappable::Reference>
@@ -2247,11 +2328,19 @@
}
void ServiceWorkerJobs::ClaimSubSteps(
- web::EnvironmentSettings* settings,
+ web::Context* client_context,
ServiceWorkerObject* associated_service_worker,
std::unique_ptr<script::ValuePromiseVoid::Reference> promise_reference) {
TRACE_EVENT0("cobalt::worker", "ServiceWorkerJobs::ClaimSubSteps()");
DCHECK_EQ(message_loop_, base::MessageLoop::current());
+
+ // Check if the client web context is still active. This may trigger if
+ // Clients.claim() was called and service worker installation fails.
+ if (!IsWebContextRegistered(client_context)) {
+ promise_reference.release();
+ return;
+ }
+
// Parallel sub steps (3) for algorithm for Clients.claim():
// https://w3c.github.io/ServiceWorker/#dom-clients-claim
std::list<web::EnvironmentSettings*> target_clients;
@@ -2263,9 +2352,9 @@
associated_service_worker->containing_service_worker_registration()
->storage_key();
for (auto& context : web_context_registrations_) {
- web::EnvironmentSettings* client = context->environment_settings();
// Don't claim to be our own service worker.
- if (client == settings) continue;
+ if (context == client_context) continue;
+ web::EnvironmentSettings* client = context->environment_settings();
url::Origin client_storage_key = client->ObtainStorageKey();
if (client_storage_key.IsSameOriginWith(storage_key)) {
// 3.1.1. If client’s execution ready flag is unset or client’s discarded
@@ -2314,7 +2403,7 @@
}
}
// 3.2. Resolve promise with undefined.
- settings->context()->message_loop()->task_runner()->PostTask(
+ client_context->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<script::ValuePromiseVoid::Reference> promise) {
diff --git a/cobalt/worker/service_worker_jobs.h b/cobalt/worker/service_worker_jobs.h
index 8e09831..11b037b 100644
--- a/cobalt/worker/service_worker_jobs.h
+++ b/cobalt/worker/service_worker_jobs.h
@@ -200,7 +200,7 @@
// Sub steps (2) of ServiceWorkerGlobalScope.skipWaiting().
// https://w3c.github.io/ServiceWorker/#dom-serviceworkerglobalscope-skipwaiting
void SkipWaitingSubSteps(
- web::EnvironmentSettings* client,
+ web::Context* client_context,
const base::WeakPtr<ServiceWorkerObject>& service_worker,
std::unique_ptr<script::ValuePromiseVoid::Reference> promise_reference);
@@ -211,7 +211,7 @@
// Parallel sub steps (2) for algorithm for Clients.get(id):
// https://w3c.github.io/ServiceWorker/#clients-get
void ClientsGetSubSteps(
- web::EnvironmentSettings* settings,
+ web::Context* client_context,
ServiceWorkerObject* associated_service_worker,
std::unique_ptr<script::ValuePromiseWrappable::Reference>
promise_reference,
@@ -220,15 +220,14 @@
// Algorithm for Resolve Get Client Promise:
// https://w3c.github.io/ServiceWorker/#resolve-get-client-promise
void ResolveGetClientPromise(
- web::EnvironmentSettings* client,
- web::EnvironmentSettings* promise_relevant_settings,
+ web::EnvironmentSettings* client, web::Context* promise_context,
std::unique_ptr<script::ValuePromiseWrappable::Reference>
promise_reference);
// Parallel sub steps (2) for algorithm for Clients.matchAll():
// https://w3c.github.io/ServiceWorker/#clients-matchall
void ClientsMatchAllSubSteps(
- web::EnvironmentSettings* settings,
+ web::Context* client_context,
ServiceWorkerObject* associated_service_worker,
std::unique_ptr<script::ValuePromiseSequenceWrappable::Reference>
promise_reference,
@@ -237,13 +236,17 @@
// Parallel sub steps (3) for algorithm for Clients.claim():
// https://w3c.github.io/ServiceWorker/#dom-clients-claim
void ClaimSubSteps(
- web::EnvironmentSettings* settings,
+ web::Context* client_context,
ServiceWorkerObject* associated_service_worker,
std::unique_ptr<script::ValuePromiseVoid::Reference> promise_reference);
// Registration of web contexts that may have service workers.
void RegisterWebContext(web::Context* context);
void UnregisterWebContext(web::Context* context);
+ bool IsWebContextRegistered(web::Context* context) {
+ return web_context_registrations_.end() !=
+ web_context_registrations_.find(context);
+ }
// https://w3c.github.io/ServiceWorker/#create-job
std::unique_ptr<Job> CreateJob(
@@ -344,6 +347,11 @@
void UpdateOnLoadingComplete(scoped_refptr<UpdateJobState> state,
const base::Optional<std::string>& error);
+ void UpdateOnRunServiceWorker(scoped_refptr<UpdateJobState> state,
+ scoped_refptr<ServiceWorkerObject> worker,
+ bool run_result);
+
+
// https://w3c.github.io/ServiceWorker/#unregister-algorithm
void Unregister(Job* job);
diff --git a/cobalt/worker/service_worker_registration.h b/cobalt/worker/service_worker_registration.h
index dd3107c..58a2e3d 100644
--- a/cobalt/worker/service_worker_registration.h
+++ b/cobalt/worker/service_worker_registration.h
@@ -83,7 +83,7 @@
void UnregisterTask(
std::unique_ptr<script::ValuePromiseBool::Reference> promise_reference);
- worker::ServiceWorkerRegistrationObject* registration_;
+ scoped_refptr<worker::ServiceWorkerRegistrationObject> registration_;
scoped_refptr<ServiceWorker> installing_;
scoped_refptr<ServiceWorker> waiting_;
scoped_refptr<ServiceWorker> active_;
diff --git a/docker/linux/base/build/Dockerfile b/docker/linux/base/build/Dockerfile
index 10098de..27050e3 100644
--- a/docker/linux/base/build/Dockerfile
+++ b/docker/linux/base/build/Dockerfile
@@ -63,10 +63,10 @@
CCACHE_DIR=${HOME}/ccache \
CCACHE_MAXSIZE=30G
-# == Set up ccache
+# === Set up ccache
RUN cd /tmp && mkdir ${HOME}/ccache
-# === Install portable sccache binary
+# === Install portable sccache binary
ARG SCCACHE=sccache-v0.3.0-x86_64-unknown-linux-musl.tar.gz
ARG SCCACHE_SHA256=e6cd8485f93d683a49c83796b9986f090901765aa4feb40d191b03ea770311d8
RUN cd /tmp \
diff --git a/precommit_hooks/gcheckstyle_wrapper.py b/precommit_hooks/gcheckstyle_wrapper.py
index 42f8675..bfadb84 100755
--- a/precommit_hooks/gcheckstyle_wrapper.py
+++ b/precommit_hooks/gcheckstyle_wrapper.py
@@ -24,6 +24,11 @@
if __name__ == '__main__':
gcheckstyle_args = sys.argv[1:]
+
+ if not checkstyle_path:
+ print('Checkstyle not available, skipping.')
+ sys.exit(0)
+
try:
sys.exit(subprocess.call([checkstyle_path] + gcheckstyle_args))
except FileNotFoundError:
diff --git a/starboard/android/shared/file_open.cc b/starboard/android/shared/file_open.cc
index 0f398a4..3e0caa6 100644
--- a/starboard/android/shared/file_open.cc
+++ b/starboard/android/shared/file_open.cc
@@ -26,17 +26,6 @@
namespace {
-// We don't package most font files in Cobalt content and fallback to the system
-// font file of the same name.
-const std::string kFontsXml("fonts.xml");
-const std::string kSystemFontsDir("/system/fonts/");
-
-#if SB_IS(EVERGREEN_COMPATIBLE)
-const std::string kCobaltFontsDir("/cobalt/assets/app/cobalt/content/fonts/");
-#else
-const std::string kCobaltFontsDir("/cobalt/assets/fonts/");
-#endif
-
// Returns the fallback for the given asset path, or an empty string if none.
// NOTE: While Cobalt now provides a mechanism for loading system fonts through
// SbSystemGetPath(), using the fallback logic within SbFileOpen() is
@@ -54,12 +43,24 @@
// straightforward mechanism for including vendor fonts via
// SbSystemGetPath().
std::string FallbackPath(const std::string& path) {
+ // We don't package most font files in Cobalt content and fallback to the
+ // system font file of the same name.
+ const std::string fonts_xml("fonts.xml");
+ const std::string system_fonts_dir("/system/fonts/");
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+ const std::string cobalt_fonts_dir(
+ "/cobalt/assets/app/cobalt/content/fonts/");
+#else
+ const std::string cobalt_fonts_dir("/cobalt/assets/fonts/");
+#endif
+
// Fonts fallback to the system fonts.
- if (path.compare(0, kCobaltFontsDir.length(), kCobaltFontsDir) == 0) {
- std::string file_name = path.substr(kCobaltFontsDir.length());
+ if (path.compare(0, cobalt_fonts_dir.length(), cobalt_fonts_dir) == 0) {
+ std::string file_name = path.substr(cobalt_fonts_dir.length());
// fonts.xml doesn't fallback.
- if (file_name != kFontsXml) {
- return kSystemFontsDir + file_name;
+ if (file_name != fonts_xml) {
+ return system_fonts_dir + file_name;
}
}
return std::string();
diff --git a/starboard/android/shared/test_filters.py b/starboard/android/shared/test_filters.py
index ff82758..a57ab71 100644
--- a/starboard/android/shared/test_filters.py
+++ b/starboard/android/shared/test_filters.py
@@ -45,11 +45,6 @@
# /etc/hosts.
'SbSocketAddressTypes/SbSocketResolveTest.Localhost/1',
- # These tests are taking longer due to interop on android. Work is
- # underway to investigate whether this is acceptable.
- 'SbMediaCanPlayMimeAndKeySystem.ValidatePerformance',
- 'SbMediaConfigurationTest.ValidatePerformance',
-
# SbDirectory has problems with empty Asset dirs.
'SbDirectoryCanOpenTest.SunnyDayStaticContent',
'SbDirectoryGetNextTest.SunnyDayStaticContent',
diff --git a/starboard/android/x86/test_filters.py b/starboard/android/x86/test_filters.py
index c33e6cf..1dbace9 100644
--- a/starboard/android/x86/test_filters.py
+++ b/starboard/android/x86/test_filters.py
@@ -22,7 +22,6 @@
'SbAccessibilityTest.CallSetCaptionsEnabled',
'SbAccessibilityTest.GetCaptionSettingsReturnIsValid',
'SbAudioSinkTest.*',
- 'SbMediaCanPlayMimeAndKeySystem.*',
'SbMicrophoneCloseTest.*',
'SbMicrophoneOpenTest.*',
'SbMicrophoneReadTest.*',
diff --git a/starboard/evergreen/shared/gyp_configuration.py b/starboard/evergreen/shared/gyp_configuration.py
index 0cc51a6..f1c6e43 100644
--- a/starboard/evergreen/shared/gyp_configuration.py
+++ b/starboard/evergreen/shared/gyp_configuration.py
@@ -27,7 +27,7 @@
def GetTestTargets(self):
tests = super(EvergreenConfiguration, self).GetTestTargets()
- tests.append('cobalt_slot_management_test')
+ tests.extend({'cobalt_slot_management_test', 'updater_test'})
return [test for test in tests if test not in self.__FORBIDDEN_TESTS]
__FORBIDDEN_TESTS = [ # pylint: disable=invalid-name
diff --git a/starboard/nplb/media_can_play_mime_and_key_system_test.cc b/starboard/nplb/media_can_play_mime_and_key_system_test.cc
index af93b99..223a749 100644
--- a/starboard/nplb/media_can_play_mime_and_key_system_test.cc
+++ b/starboard/nplb/media_can_play_mime_and_key_system_test.cc
@@ -704,13 +704,30 @@
}
}
+// Note: If the platform failed on this test, please improve the performance of
+// SbMediaCanPlayMimeAndKeySystem(). A few ideas:
+// 1. Cache codec and drm capabilities. Please make sure a codec or drm
+// capability query can be done quickly without too many calculations.
+// 2. Cache audio output and display configurations. On some platforms, it
+// takes time to get the audio/video configurations. Caching these
+// configurations can significantly reduce the latency on acquiring
+// configurations. Unlike codec and drm capabilities, audio/video
+// configurations may change during app runtime, the platform need to
+// update the cache if there's any change.
+// 3. Enable MimeSupportabilityCache and KeySystemSupportabilityCache. These
+// supportability caches will cache the results of previous queries, to
+// boost the queries of repeated mime and key system. Note that if there's
+// any capability change, the platform need to explicitly clear the
+// caches, otherwise they may return outdated results.
TEST(SbMediaCanPlayMimeAndKeySystem, ValidatePerformance) {
auto test_sequential_function_calls =
- [](const char** mime_params, int num_function_calls,
- SbTimeMonotonic max_time_delta, const char* query_type) {
+ [](const SbMediaCanPlayMimeAndKeySystemParam* mime_params,
+ int num_function_calls, SbTimeMonotonic max_time_delta,
+ const char* query_type) {
const SbTimeMonotonic time_start = SbTimeGetMonotonicNow();
for (int i = 0; i < num_function_calls; ++i) {
- SbMediaCanPlayMimeAndKeySystem(mime_params[i], "");
+ SbMediaCanPlayMimeAndKeySystem(mime_params[i].mime,
+ mime_params[i].key_system);
}
const SbTimeMonotonic time_last = SbTimeGetMonotonicNow();
const SbTimeMonotonic time_delta = time_last - time_start;
@@ -725,15 +742,30 @@
EXPECT_LE(time_delta, max_time_delta);
};
- test_sequential_function_calls(kSdrQueryParams,
- SB_ARRAY_SIZE_INT(kSdrQueryParams),
- 10 * kSbTimeMillisecond, "SDR queries");
- test_sequential_function_calls(kHdrQueryParams,
- SB_ARRAY_SIZE_INT(kHdrQueryParams),
- 20 * kSbTimeMillisecond, "HDR queries");
- test_sequential_function_calls(kDrmQueryParams,
- SB_ARRAY_SIZE_INT(kDrmQueryParams),
- 50 * kSbTimeMillisecond, "DRM queries");
+ // Warmup the cache.
+ test_sequential_function_calls(
+ kWarmupQueryParams, SB_ARRAY_SIZE_INT(kWarmupQueryParams),
+ 5 * kSbTimeMillisecond /* 9 calls */, "Warmup queries");
+ // First round of the queires.
+ test_sequential_function_calls(
+ kSdrQueryParams, SB_ARRAY_SIZE_INT(kSdrQueryParams),
+ 10 * kSbTimeMillisecond /* 38 calls */, "SDR queries");
+ test_sequential_function_calls(
+ kHdrQueryParams, SB_ARRAY_SIZE_INT(kHdrQueryParams),
+ 10 * kSbTimeMillisecond /* 82 calls */, "HDR queries");
+ test_sequential_function_calls(
+ kDrmQueryParams, SB_ARRAY_SIZE_INT(kDrmQueryParams),
+ 10 * kSbTimeMillisecond /* 81 calls */, "DRM queries");
+ // Second round of the queries.
+ test_sequential_function_calls(
+ kSdrQueryParams, SB_ARRAY_SIZE_INT(kSdrQueryParams),
+ 5 * kSbTimeMillisecond /* 38 calls */, "Cached SDR queries");
+ test_sequential_function_calls(
+ kHdrQueryParams, SB_ARRAY_SIZE_INT(kHdrQueryParams),
+ 5 * kSbTimeMillisecond /* 82 calls */, "Cached HDR queries");
+ test_sequential_function_calls(
+ kDrmQueryParams, SB_ARRAY_SIZE_INT(kDrmQueryParams),
+ 5 * kSbTimeMillisecond /* 81 calls */, "Cached DRM queries");
}
} // namespace
diff --git a/starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h b/starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h
index 544961b..01f555b 100644
--- a/starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h
+++ b/starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h
@@ -4,7 +4,7 @@
// 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
+// 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,
@@ -18,344 +18,517 @@
namespace starboard {
namespace nplb {
+struct SbMediaCanPlayMimeAndKeySystemParam {
+ const char* mime;
+ const char* key_system;
+};
+
+static SbMediaCanPlayMimeAndKeySystemParam kWarmupQueryParams[] = {
+ {"audio/mp4; codecs=\"mp4a.40.2\"", ""},
+ {"audio/webm; codecs=\"opus\"", ""},
+ {"video/webm; codecs=\"avc1.64002a\"", ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\";", ""},
+ {"video/webm; codecs=\"av01.0.09M.08\"", ""},
+ {"video/webm; codecs=\"av01.0.17M.10.0.110.09.16.09.0\"", ""},
+ {"audio/mp4; codecs=\"mp4a.40.2\"", "com.widevine.alpha"},
+ {"video/webm; codecs=\"avc1.64002a\"", "com.widevine.alpha"},
+};
+
// Query params from https://youtu.be/iXvy8ZeCs5M.
-static const char* kSdrQueryParams[] = {
- "video/mp4; codecs=\"avc1.42001E\"",
- "audio/mp4; codecs=\"mp4a.40.2\"",
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"",
- "video/webm; codecs=\"vp09.02.51.10.01.09.99.99.00\"",
- "audio/webm; codecs=\"opus\"",
- "audio/webm; codecs=\"opus\"; channels=2",
- "audio/webm; codecs=\"opus\"; channels=99",
- "video/mp4; codecs=av01.0.05M.08",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; width=640",
- "video/webm; codecs=\"vp9\"; width=99999",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; height=360",
- "video/webm; codecs=\"vp9\"; height=99999",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; framerate=30",
- "video/webm; codecs=\"vp9\"; framerate=9999",
- "video/webm; codecs=\"vp9\"; width=3840; height=2160; bitrate=2000000",
- "video/webm; codecs=\"vp9\"; width=3840; height=2160; bitrate=20000000",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; bitrate=300000",
- "video/webm; codecs=\"vp9\"; bitrate=2000000000",
- "video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
- "bitrate=233713",
- "video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
- "bitrate=422012",
- "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
- "video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
- "bitrate=110487",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; eotf=bt709",
- "video/webm; codecs=\"vp9\"; eotf=catavision",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
- "height=240; framerate=24; bitrate=191916; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
- "height=360; framerate=24; bitrate=400973; eotf=bt709",
- "audio/webm; codecs=\"opus\"; channels=2",
- "audio/webm; codecs=\"opus\"; channels=2",
- "video/mp4; codecs=\"av01.0.00M.08\"; width=256; height=144; framerate=24; "
- "bitrate=76146; eotf=bt709",
- "video/mp4; codecs=\"av01.0.00M.08\"; width=426; height=240; framerate=24; "
- "bitrate=156234; eotf=bt709",
- "video/mp4; codecs=\"av01.0.01M.08\"; width=640; height=360; framerate=24; "
- "bitrate=302046; eotf=bt709",
- "audio/webm; codecs=\"opus\"",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\""};
+static SbMediaCanPlayMimeAndKeySystemParam kSdrQueryParams[] = {
+ {"video/mp4; codecs=\"avc1.42001E\"", ""},
+ {"audio/mp4; codecs=\"mp4a.40.2\"", ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"", ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.99.99.00\"", ""},
+ {"audio/webm; codecs=\"opus\"", ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+ {"audio/webm; codecs=\"opus\"; channels=99", ""},
+ {"video/mp4; codecs=av01.0.05M.08", ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; width=640", ""},
+ {"video/webm; codecs=\"vp9\"; width=99999", ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; height=360", ""},
+ {"video/webm; codecs=\"vp9\"; height=99999", ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; framerate=30", ""},
+ {"video/webm; codecs=\"vp9\"; framerate=9999", ""},
+ {"video/webm; codecs=\"vp9\"; width=3840; height=2160; bitrate=2000000",
+ ""},
+ {"video/webm; codecs=\"vp9\"; width=3840; height=2160; bitrate=20000000",
+ ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; bitrate=300000", ""},
+ {"video/webm; codecs=\"vp9\"; bitrate=2000000000", ""},
+ {"video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
+ "bitrate=233713",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
+ "bitrate=422012",
+ ""},
+ {"audio/mp4; codecs=\"mp4a.40.2\"; channels=2", ""},
+ {"video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
+ "bitrate=110487",
+ ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; eotf=bt709", ""},
+ {"video/webm; codecs=\"vp9\"; eotf=catavision", ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
+ "height=240; framerate=24; bitrate=191916; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
+ "height=360; framerate=24; bitrate=400973; eotf=bt709",
+ ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+ {"video/mp4; codecs=\"av01.0.00M.08\"; width=256; height=144; "
+ "framerate=24; "
+ "bitrate=76146; eotf=bt709",
+ ""},
+ {"video/mp4; codecs=\"av01.0.00M.08\"; width=426; height=240; "
+ "framerate=24; "
+ "bitrate=156234; eotf=bt709",
+ ""},
+ {"video/mp4; codecs=\"av01.0.01M.08\"; width=640; height=360; "
+ "framerate=24; "
+ "bitrate=302046; eotf=bt709",
+ ""},
+ {"audio/webm; codecs=\"opus\"", ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"", ""},
+};
// Query params from https://youtu.be/1La4QzGeaaQ.
-static const char* kHdrQueryParams[] = {
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"",
- "video/webm; codecs=\"vp09.02.51.10.01.09.99.99.00\"",
- "audio/webm; codecs=\"opus\"",
- "audio/webm; codecs=\"opus\"; channels=2",
- "audio/webm; codecs=\"opus\"; channels=99",
- "video/mp4; codecs=av01.0.05M.08",
- "video/mp4; codecs=av99.0.05M.08",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; height=360",
- "video/webm; codecs=\"vp9\"; height=99999",
- "video/webm; codecs=\"vp9\"; width=3840; height=2160; bitrate=2000000",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; bitrate=300000",
- "video/webm; codecs=\"vp9\"; bitrate=2000000000",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; width=640",
- "video/webm; codecs=\"vp9\"; width=99999",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; framerate=30",
- "video/webm; codecs=\"vp9\"; framerate=9999",
- "video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=30; "
- "bitrate=296736",
- "video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=30; "
- "bitrate=700126",
- "video/mp4; codecs=\"avc1.4d401f\"; width=854; height=480; framerate=30; "
- "bitrate=1357113",
- "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=30; "
- "bitrate=2723992",
- "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
- "video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=30; "
- "bitrate=123753",
- "video/webm; codecs=\"vp9\"",
- "video/webm; codecs=\"vp9\"; eotf=bt709",
- "video/webm; codecs=\"vp9\"; eotf=catavision",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
- "height=240; framerate=30; bitrate=202710; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
- "height=360; framerate=30; bitrate=427339; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
- "height=480; framerate=30; bitrate=782821; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=30; bitrate=1542503; eotf=bt709",
- "audio/webm; codecs=\"opus\"; channels=2",
- "audio/webm; codecs=\"opus\"; channels=2",
- "video/mp4; codecs=\"avc1.4d4020\"; width=1280; height=720; framerate=60; "
- "bitrate=3488936",
- "video/mp4; codecs=\"avc1.64002a\"; width=1920; height=1080; framerate=60; "
- "bitrate=5833750",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=60; bitrate=2676194; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
- "height=1080; framerate=60; bitrate=4461346; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=2560; "
- "height=1440; framerate=60; bitrate=13384663; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=3840; "
- "height=2160; framerate=60; bitrate=26752474; eotf=bt709",
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=256; "
- "height=144; framerate=60; bitrate=245561",
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=426; "
- "height=240; framerate=60; bitrate=500223",
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=640; "
- "height=360; framerate=60; bitrate=1064485",
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=854; "
- "height=480; framerate=60; bitrate=1998847",
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=1280; "
- "height=720; framerate=60; bitrate=4556353",
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=1920; "
- "height=1080; framerate=60; bitrate=6946958",
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=2560; "
- "height=1440; framerate=60; bitrate=16930005",
- "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=3840; "
- "height=2160; framerate=60; bitrate=30184402",
- "video/mp4; codecs=\"av01.0.00M.10.0.110.09.16.09.0\"; width=256; "
- "height=144; framerate=30; bitrate=89195; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.00M.10.0.110.09.16.09.0\"; width=426; "
- "height=240; framerate=30; bitrate=172861; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.01M.10.0.110.09.16.09.0\"; width=640; "
- "height=360; framerate=30; bitrate=369517; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.04M.10.0.110.09.16.09.0\"; width=854; "
- "height=480; framerate=30; bitrate=695606; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.08M.10.0.110.09.16.09.0\"; width=1280; "
- "height=720; framerate=60; bitrate=2017563; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.09M.10.0.110.09.16.09.0\"; width=1920; "
- "height=1080; framerate=60; bitrate=3755257; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.12M.10.0.110.09.16.09.0\"; width=2560; "
- "height=1440; framerate=60; bitrate=8546165; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.13M.10.0.110.09.16.09.0\"; width=3840; "
- "height=2160; framerate=60; bitrate=17537773; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.17M.10.0.110.09.16.09.0\"; width=7680; "
- "height=4320; framerate=60; bitrate=37270368; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.00M.10.0.110.09.16.09.0\"; width=256; "
- "height=144; framerate=60; bitrate=193907; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.01M.10.0.110.09.16.09.0\"; width=426; "
- "height=240; framerate=60; bitrate=400353; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.04M.10.0.110.09.16.09.0\"; width=640; "
- "height=360; framerate=60; bitrate=817812; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.05M.10.0.110.09.16.09.0\"; width=854; "
- "height=480; framerate=60; bitrate=1558025; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.08M.10.0.110.09.16.09.0\"; width=1280; "
- "height=720; framerate=60; bitrate=4167668; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.09M.10.0.110.09.16.09.0\"; width=1920; "
- "height=1080; framerate=60; bitrate=6870811; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.12M.10.0.110.09.16.09.0\"; width=2560; "
- "height=1440; framerate=60; bitrate=17316706; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.13M.10.0.110.09.16.09.0\"; width=3840; "
- "height=2160; framerate=60; bitrate=31942925; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.17M.10.0.110.09.16.09.0\"; width=7680; "
- "height=4320; framerate=60; bitrate=66038840; eotf=smpte2084",
- "video/mp4; codecs=\"av01.0.17M.10.0.110.09.16.09.0\"; width=7680; "
- "height=4320; framerate=60; bitrate=45923436; eotf=smpte2084",
- "video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
- "bitrate=160590",
- "video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
- "bitrate=255156",
- "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
- "bitrate=490890",
- "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
- "bitrate=1000556",
- "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
- "bitrate=1810004",
- "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
- "video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
- "bitrate=82746",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
- "height=240; framerate=24; bitrate=178701; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
- "height=360; framerate=24; bitrate=371303; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
- "height=480; framerate=24; bitrate=579918; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=24; bitrate=999223; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
- "height=1080; framerate=24; bitrate=1814623; eotf=bt709",
- "audio/webm; codecs=\"opus\"; channels=2",
- "audio/webm; codecs=\"opus\"; channels=2"};
+static SbMediaCanPlayMimeAndKeySystemParam kHdrQueryParams[] = {
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"", ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.99.99.00\"", ""},
+ {"audio/webm; codecs=\"opus\"", ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+ {"audio/webm; codecs=\"opus\"; channels=99", ""},
+ {"video/mp4; codecs=av01.0.05M.08", ""},
+ {"video/mp4; codecs=av99.0.05M.08", ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; height=360", ""},
+ {"video/webm; codecs=\"vp9\"; height=99999", ""},
+ {"video/webm; codecs=\"vp9\"; width=3840; height=2160; bitrate=2000000",
+ ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; bitrate=300000", ""},
+ {"video/webm; codecs=\"vp9\"; bitrate=2000000000", ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; width=640", ""},
+ {"video/webm; codecs=\"vp9\"; width=99999", ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; framerate=30", ""},
+ {"video/webm; codecs=\"vp9\"; framerate=9999", ""},
+ {"video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=30; "
+ "bitrate=296736",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=30; "
+ "bitrate=700126",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401f\"; width=854; height=480; framerate=30; "
+ "bitrate=1357113",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=30; "
+ "bitrate=2723992",
+ ""},
+ {"audio/mp4; codecs=\"mp4a.40.2\"; channels=2", ""},
+ {"video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=30; "
+ "bitrate=123753",
+ ""},
+ {"video/webm; codecs=\"vp9\"", ""},
+ {"video/webm; codecs=\"vp9\"; eotf=bt709", ""},
+ {"video/webm; codecs=\"vp9\"; eotf=catavision", ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
+ "height=240; framerate=30; bitrate=202710; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
+ "height=360; framerate=30; bitrate=427339; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+ "height=480; framerate=30; bitrate=782821; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=30; bitrate=1542503; eotf=bt709",
+ ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+ {"video/mp4; codecs=\"avc1.4d4020\"; width=1280; height=720; framerate=60; "
+ "bitrate=3488936",
+ ""},
+ {"video/mp4; codecs=\"avc1.64002a\"; width=1920; height=1080; "
+ "framerate=60; "
+ "bitrate=5833750",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=60; bitrate=2676194; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+ "height=1080; framerate=60; bitrate=4461346; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=2560; "
+ "height=1440; framerate=60; bitrate=13384663; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=3840; "
+ "height=2160; framerate=60; bitrate=26752474; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=256; "
+ "height=144; framerate=60; bitrate=245561",
+ ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=426; "
+ "height=240; framerate=60; bitrate=500223",
+ ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=640; "
+ "height=360; framerate=60; bitrate=1064485",
+ ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=854; "
+ "height=480; framerate=60; bitrate=1998847",
+ ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=1280; "
+ "height=720; framerate=60; bitrate=4556353",
+ ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=1920; "
+ "height=1080; framerate=60; bitrate=6946958",
+ ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=2560; "
+ "height=1440; framerate=60; bitrate=16930005",
+ ""},
+ {"video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=3840; "
+ "height=2160; framerate=60; bitrate=30184402",
+ ""},
+ {"video/mp4; codecs=\"av01.0.00M.10.0.110.09.16.09.0\"; width=256; "
+ "height=144; framerate=30; bitrate=89195; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.00M.10.0.110.09.16.09.0\"; width=426; "
+ "height=240; framerate=30; bitrate=172861; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.01M.10.0.110.09.16.09.0\"; width=640; "
+ "height=360; framerate=30; bitrate=369517; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.04M.10.0.110.09.16.09.0\"; width=854; "
+ "height=480; framerate=30; bitrate=695606; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.08M.10.0.110.09.16.09.0\"; width=1280; "
+ "height=720; framerate=60; bitrate=2017563; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.09M.10.0.110.09.16.09.0\"; width=1920; "
+ "height=1080; framerate=60; bitrate=3755257; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.12M.10.0.110.09.16.09.0\"; width=2560; "
+ "height=1440; framerate=60; bitrate=8546165; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.13M.10.0.110.09.16.09.0\"; width=3840; "
+ "height=2160; framerate=60; bitrate=17537773; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.17M.10.0.110.09.16.09.0\"; width=7680; "
+ "height=4320; framerate=60; bitrate=37270368; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.00M.10.0.110.09.16.09.0\"; width=256; "
+ "height=144; framerate=60; bitrate=193907; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.01M.10.0.110.09.16.09.0\"; width=426; "
+ "height=240; framerate=60; bitrate=400353; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.04M.10.0.110.09.16.09.0\"; width=640; "
+ "height=360; framerate=60; bitrate=817812; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.05M.10.0.110.09.16.09.0\"; width=854; "
+ "height=480; framerate=60; bitrate=1558025; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.08M.10.0.110.09.16.09.0\"; width=1280; "
+ "height=720; framerate=60; bitrate=4167668; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.09M.10.0.110.09.16.09.0\"; width=1920; "
+ "height=1080; framerate=60; bitrate=6870811; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.12M.10.0.110.09.16.09.0\"; width=2560; "
+ "height=1440; framerate=60; bitrate=17316706; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.13M.10.0.110.09.16.09.0\"; width=3840; "
+ "height=2160; framerate=60; bitrate=31942925; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.17M.10.0.110.09.16.09.0\"; width=7680; "
+ "height=4320; framerate=60; bitrate=66038840; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"av01.0.17M.10.0.110.09.16.09.0\"; width=7680; "
+ "height=4320; framerate=60; bitrate=45923436; eotf=smpte2084",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
+ "bitrate=160590",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
+ "bitrate=255156",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+ "bitrate=490890",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+ "bitrate=1000556",
+ ""},
+ {"video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; "
+ "framerate=24; "
+ "bitrate=1810004",
+ ""},
+ {"audio/mp4; codecs=\"mp4a.40.2\"; channels=2", ""},
+ {"video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
+ "bitrate=82746",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
+ "height=240; framerate=24; bitrate=178701; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
+ "height=360; framerate=24; bitrate=371303; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+ "height=480; framerate=24; bitrate=579918; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=24; bitrate=999223; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+ "height=1080; framerate=24; bitrate=1814623; eotf=bt709",
+ ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+};
// Query params from https://youtu.be/1mSzHxMpji0.
-static const char* kDrmQueryParams[] = {
- "video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
- "bitrate=281854",
- "video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
- "bitrate=637760",
- "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
- "bitrate=1164612",
- "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
- "bitrate=4362827",
- "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
- "video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
- "bitrate=138907",
- "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
- "bitrate=1746306",
- "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
- "bitrate=3473564",
- "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
- "bitrate=3481130",
- "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
- "bitrate=5789806",
- "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
- "bitrate=5856175",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
- "height=480; framerate=24; bitrate=2629046; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=24; bitrate=1328071; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
- "height=1080; framerate=24; bitrate=2375894; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
- "height=240; framerate=24; bitrate=229634; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
- "height=360; framerate=24; bitrate=324585; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
- "height=480; framerate=24; bitrate=639196; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
- "height=480; framerate=24; bitrate=1055128; eotf=bt709; "
- "cryptoblockformat=subsample",
- "audio/mp4; codecs=\"ec-3\"; channels=6",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=24; bitrate=2111149; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=24; bitrate=3709033; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
- "height=1080; framerate=24; bitrate=3679792; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
- "height=1080; framerate=24; bitrate=5524689; eotf=bt709; "
- "cryptoblockformat=subsample",
- "audio/mp4; codecs=\"ac-3\"; channels=6",
- "video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
- "bitrate=281854",
- "video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
- "bitrate=637760",
- "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
- "bitrate=1164612",
- "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
- "bitrate=4362827",
- "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
- "video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
- "bitrate=138907",
- "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
- "bitrate=1746306",
- "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
- "bitrate=3473564",
- "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
- "bitrate=3481130",
- "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
- "bitrate=5789806",
- "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
- "bitrate=5856175",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
- "height=480; framerate=24; bitrate=2629046; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=24; bitrate=1328071; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
- "height=1080; framerate=24; bitrate=2375894; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
- "height=240; framerate=24; bitrate=229634; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
- "height=360; framerate=24; bitrate=324585; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
- "height=480; framerate=24; bitrate=639196; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
- "height=480; framerate=24; bitrate=1055128; eotf=bt709; "
- "cryptoblockformat=subsample",
- "audio/mp4; codecs=\"ec-3\"; channels=6",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=24; bitrate=2111149; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=24; bitrate=3709033; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
- "height=1080; framerate=24; bitrate=3679792; eotf=bt709; "
- "cryptoblockformat=subsample",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
- "height=1080; framerate=24; bitrate=5524689; eotf=bt709; "
- "cryptoblockformat=subsample",
- "audio/mp4; codecs=\"ac-3\"; channels=6",
- "video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
- "bitrate=149590",
- "video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
- "bitrate=261202",
- "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
- "bitrate=368187",
- "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
- "bitrate=676316",
- "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
- "bitrate=2691722",
- "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
- "video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
- "bitrate=84646",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
- "height=240; framerate=24; bitrate=192698; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
- "height=360; framerate=24; bitrate=342403; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
- "height=480; framerate=24; bitrate=514976; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
- "height=720; framerate=24; bitrate=852689; eotf=bt709",
- "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
- "height=1080; framerate=24; bitrate=2389269; eotf=bt709",
- "audio/webm; codecs=\"opus\"; channels=2",
- "audio/webm; codecs=\"opus\"; channels=2",
- "video/mp4; codecs=\"av01.0.00M.08\"; width=256; height=144; framerate=24; "
- "bitrate=74957; eotf=bt709",
- "video/mp4; codecs=\"av01.0.00M.08\"; width=426; height=240; framerate=24; "
- "bitrate=148691; eotf=bt709",
- "video/mp4; codecs=\"av01.0.01M.08\"; width=640; height=360; framerate=24; "
- "bitrate=305616; eotf=bt709",
- "video/mp4; codecs=\"av01.0.04M.08\"; width=854; height=480; framerate=24; "
- "bitrate=577104; eotf=bt709",
- "video/mp4; codecs=\"av01.0.05M.08\"; width=1280; height=720; "
- "framerate=24; bitrate=989646; eotf=bt709",
- "video/mp4; codecs=\"av01.0.08M.08\"; width=1920; height=1080; "
- "framerate=24; bitrate=1766589; eotf=bt709"};
+static SbMediaCanPlayMimeAndKeySystemParam kDrmQueryParams[] = {
+ {"video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
+ "bitrate=281854",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
+ "bitrate=637760",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+ "bitrate=1164612",
+ ""},
+ {"video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; "
+ "framerate=24; "
+ "bitrate=4362827",
+ ""},
+ {"audio/mp4; codecs=\"mp4a.40.2\"; channels=2", ""},
+ {"video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
+ "bitrate=138907",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+ "bitrate=1746306",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+ "bitrate=3473564",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+ "bitrate=3481130",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+ "bitrate=5789806",
+ ""},
+ {"video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; "
+ "framerate=24; "
+ "bitrate=5856175",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+ "height=480; framerate=24; bitrate=2629046; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=24; bitrate=1328071; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+ "height=1080; framerate=24; bitrate=2375894; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
+ "height=240; framerate=24; bitrate=229634; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
+ "height=360; framerate=24; bitrate=324585; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+ "height=480; framerate=24; bitrate=639196; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+ "height=480; framerate=24; bitrate=1055128; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"audio/mp4; codecs=\"ec-3\"; channels=6", ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=24; bitrate=2111149; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=24; bitrate=3709033; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+ "height=1080; framerate=24; bitrate=3679792; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+ "height=1080; framerate=24; bitrate=5524689; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"audio/mp4; codecs=\"ac-3\"; channels=6", ""},
+ {"video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
+ "bitrate=281854",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
+ "bitrate=637760",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+ "bitrate=1164612",
+ ""},
+ {"video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; "
+ "framerate=24; "
+ "bitrate=4362827",
+ ""},
+ {"audio/mp4; codecs=\"mp4a.40.2\"; channels=2", ""},
+ {"video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
+ "bitrate=138907",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+ "bitrate=1746306",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+ "bitrate=3473564",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+ "bitrate=3481130",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+ "bitrate=5789806",
+ ""},
+ {"video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; "
+ "framerate=24; "
+ "bitrate=5856175",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+ "height=480; framerate=24; bitrate=2629046; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=24; bitrate=1328071; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+ "height=1080; framerate=24; bitrate=2375894; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
+ "height=240; framerate=24; bitrate=229634; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
+ "height=360; framerate=24; bitrate=324585; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+ "height=480; framerate=24; bitrate=639196; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+ "height=480; framerate=24; bitrate=1055128; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"audio/mp4; codecs=\"ec-3\"; channels=6", ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=24; bitrate=2111149; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=24; bitrate=3709033; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+ "height=1080; framerate=24; bitrate=3679792; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+ "height=1080; framerate=24; bitrate=5524689; eotf=bt709; "
+ "cryptoblockformat=subsample",
+ ""},
+ {"audio/mp4; codecs=\"ac-3\"; channels=6", ""},
+ {"video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
+ "bitrate=149590",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
+ "bitrate=261202",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+ "bitrate=368187",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+ "bitrate=676316",
+ ""},
+ {"video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; "
+ "framerate=24; "
+ "bitrate=2691722",
+ ""},
+ {"audio/mp4; codecs=\"mp4a.40.2\"; channels=2", ""},
+ {"video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
+ "bitrate=84646",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
+ "height=240; framerate=24; bitrate=192698; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
+ "height=360; framerate=24; bitrate=342403; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+ "height=480; framerate=24; bitrate=514976; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+ "height=720; framerate=24; bitrate=852689; eotf=bt709",
+ ""},
+ {"video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+ "height=1080; framerate=24; bitrate=2389269; eotf=bt709",
+ ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+ {"audio/webm; codecs=\"opus\"; channels=2", ""},
+ {"video/mp4; codecs=\"av01.0.00M.08\"; width=256; height=144; "
+ "framerate=24; "
+ "bitrate=74957; eotf=bt709",
+ ""},
+ {"video/mp4; codecs=\"av01.0.00M.08\"; width=426; height=240; "
+ "framerate=24; "
+ "bitrate=148691; eotf=bt709",
+ ""},
+ {"video/mp4; codecs=\"av01.0.01M.08\"; width=640; height=360; "
+ "framerate=24; "
+ "bitrate=305616; eotf=bt709",
+ ""},
+ {"video/mp4; codecs=\"av01.0.04M.08\"; width=854; height=480; "
+ "framerate=24; "
+ "bitrate=577104; eotf=bt709",
+ ""},
+ {"video/mp4; codecs=\"av01.0.05M.08\"; width=1280; height=720; "
+ "framerate=24; bitrate=989646; eotf=bt709",
+ ""},
+ {"video/mp4; codecs=\"av01.0.08M.08\"; width=1920; height=1080; "
+ "framerate=24; bitrate=1766589; eotf=bt709",
+ ""},
+ {"video/mp4; codecs=\"avc1.4d4015\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"avc1.4d4015\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"avc1.4d401e\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"avc1.4d401e\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"avc1.4d401f\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"avc1.4d401f\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"avc1.640028\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"avc1.640028\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"avc1.4d400c\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"avc1.4d400c\"", "com.widevine.alpha"},
+ {"video/mp4; codecs=\"vp09.00.51.08.01.01.01.01.00\"",
+ "com.widevine.alpha"},
+ {"video/mp4; codecs=\"vp09.00.51.08.01.01.01.01.00\"",
+ "com.widevine.alpha"},
+ {"video/mp4; codecs=\"audio/mp4; codecs=\"mp4a.40.2\"",
+ "com.widevine.alpha"},
+};
} // namespace nplb
} // namespace starboard
diff --git a/starboard/shared/ffmpeg/BUILD.gn b/starboard/shared/ffmpeg/BUILD.gn
index b55636d..21316bb 100644
--- a/starboard/shared/ffmpeg/BUILD.gn
+++ b/starboard/shared/ffmpeg/BUILD.gn
@@ -12,6 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+declare_args() {
+ # Whether or not to enable the ffmpeg_demuxer_test target. The target will
+ # only work if ffmpeg 58.35.100 is installed on the build machine.
+ #
+ # TODO(b/239961799): Rework the test to run regardless of the installed
+ # ffmpeg version.
+ enable_ffmpeg_demuxer_test = false
+}
+
ffmpeg_specialization_sources = [
"ffmpeg_audio_decoder_impl.cc",
"ffmpeg_audio_decoder_impl.h",
@@ -97,27 +106,29 @@
public_configs = [ "//starboard/build/config:starboard_implementation" ]
}
-target(gtest_target_type, "ffmpeg_demuxer_test") {
- testonly = true
- configs += [ "//starboard/build/config:starboard_implementation" ]
- sources = ffmpeg_specialization_sources + [
- "ffmpeg_demuxer.h",
- "ffmpeg_demuxer.cc",
- "ffmpeg_demuxer_test.cc",
- ]
+if (enable_ffmpeg_demuxer_test) {
+ target(gtest_target_type, "ffmpeg_demuxer_test") {
+ testonly = true
+ configs += [ "//starboard/build/config:starboard_implementation" ]
+ sources = ffmpeg_specialization_sources + [
+ "ffmpeg_demuxer.h",
+ "ffmpeg_demuxer.cc",
+ "ffmpeg_demuxer_test.cc",
+ ]
- # Build only against one specified version of the ffmpeg includes. That means
- # that this binary will only work well when run on a machine with the given
- # version of ffmpeg installed. This test binary actually should have
- # specializations for all supported ffmpeg versions, or it should only test
- # the behavior of the abstraction layer without testing implementation
- # details.
- include_dirs = [ "//third_party/ffmpeg_includes/ffmpeg.58.35.100" ]
- deps = [
- "//cobalt/test:run_all_unittests",
- "//starboard",
- "//starboard/common",
- "//testing/gmock",
- "//testing/gtest",
- ]
+ # Build only against one specified version of the ffmpeg includes. That means
+ # that this binary will only work well when run on a machine with the given
+ # version of ffmpeg installed. This test binary actually should have
+ # specializations for all supported ffmpeg versions, or it should only test
+ # the behavior of the abstraction layer without testing implementation
+ # details.
+ include_dirs = [ "//third_party/ffmpeg_includes/ffmpeg.58.35.100" ]
+ deps = [
+ "//cobalt/test:run_all_unittests",
+ "//starboard",
+ "//starboard/common",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+ }
}
diff --git a/starboard/shared/ffmpeg/ffmpeg_common.h b/starboard/shared/ffmpeg/ffmpeg_common.h
index c10bbbd..9d4b499 100644
--- a/starboard/shared/ffmpeg/ffmpeg_common.h
+++ b/starboard/shared/ffmpeg/ffmpeg_common.h
@@ -32,6 +32,14 @@
#define LIBAVUTIL_VERSION_52_8 AV_VERSION_INT(52, 8, 0)
#endif
+#ifndef LIBAVCODEC_VERSION_57_100
+#define LIBAVCODEC_VERSION_57_100 AV_VERSION_INT(57, 100, 0)
+#endif
+
+#ifndef LIBAVFORMAT_VERSION_57_83
+#define LIBAVFORMAT_VERSION_57_83 AV_VERSION_INT(57, 83, 0)
+#endif
+
#if !defined(LIBAVUTIL_VERSION_MAJOR)
#error "LIBAVUTIL_VERSION_MAJOR not defined"
#endif // !defined(LIBAVUTIL_VERSION_MAJOR)
diff --git a/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.cc b/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.cc
index 6b8455e..b95d777 100644
--- a/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.cc
@@ -18,6 +18,7 @@
#include <cassert>
#include <cmath>
#include <cstdint>
+#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
@@ -305,10 +306,14 @@
CobaltExtensionDemuxerEncryptionScheme GetEncryptionScheme(
const AVStream& stream) {
+#if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
return FFmpegDemuxer::GetDispatch()->av_dict_get(
stream.metadata, "enc_key_id", nullptr, 0) == nullptr
? kCobaltExtensionDemuxerEncryptionSchemeUnencrypted
: kCobaltExtensionDemuxerEncryptionSchemeCenc;
+#else
+ return kCobaltExtensionDemuxerEncryptionSchemeUnencrypted;
+#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
}
int64_t ExtractStartTime(AVStream* stream) {
@@ -318,11 +323,11 @@
ConvertFromTimeBaseToMicros(stream->time_base, stream->start_time);
}
-#if LIBAVCODEC_LIBRARY_IS_FFMPEG
+#if LIBAVFORMAT_VERSION_INT >= LIBAVFORMAT_VERSION_57_83
const int32_t codec_id = stream->codecpar->codec_id;
#else
const int32_t codec_id = stream->codec->codec_id;
-#endif
+#endif // LIBAVFORMAT_VERSION_INT >= LIBAVFORMAT_VERSION_57_83
if (stream->first_dts != kNoFFmpegTimestamp
#if FFMPEG >= 560
@@ -363,6 +368,7 @@
input_formats.cbegin(), input_formats.cend(),
+[](const std::string& format) -> bool { return format == "webm"; });
+#if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
if (is_webm) {
const AVDictionaryEntry* entry = FFmpegDemuxer::GetDispatch()->av_dict_get(
format_context->metadata, "creation_time", nullptr, 0);
@@ -372,6 +378,7 @@
// is harder than it sounds in pure C++.
return 0;
}
+#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
return 0;
}
@@ -406,7 +413,12 @@
return;
}
auto* packet = static_cast<AVPacket*>(ptr);
+#if LIBAVCODEC_VERSION_INT >= LIBAVCODEC_VERSION_57_100
GetDispatch()->av_packet_free(&packet);
+#else
+ GetDispatch()->av_free_packet(packet);
+ GetDispatch()->av_free(packet);
+#endif // LIBAVCODEC_VERSION_INT >= LIBAVCODEC_VERSION_57_100
}
FFmpegDemuxerImpl<FFMPEG>::FFmpegDemuxerImpl(
@@ -467,7 +479,7 @@
// stream present.
for (int i = 0; i < format_context_->nb_streams; ++i) {
AVStream* stream = format_context_->streams[i];
-#if LIBAVCODEC_LIBRARY_IS_FFMPEG
+#if LIBAVFORMAT_VERSION_INT >= LIBAVFORMAT_VERSION_57_83
const AVCodecParameters* codec_parameters = stream->codecpar;
const AVMediaType codec_type = codec_parameters->codec_type;
const AVCodecID codec_id = codec_parameters->codec_id;
@@ -475,7 +487,7 @@
const AVCodecContext* codec = stream->codec;
const AVMediaType codec_type = codec->codec_type;
const AVCodecID codec_id = codec->codec_id;
-#endif
+#endif // LIBAVFORMAT_VERSION_INT >= LIBAVFORMAT_VERSION_57_83
// Skip streams which are not properly detected.
if (codec_id == AV_CODEC_ID_NONE) {
stream->discard = AVDISCARD_ALL;
@@ -590,9 +602,8 @@
buffer.data = packet->data;
buffer.data_size = packet->size;
-// The supported libav libraries don't define
-// AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL.
-#if LIBAVCODEC_LIBRARY_IS_FFMPEG
+// Only newer versions support AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL.
+#if LIBAVCODEC_VERSION_INT >= LIBAVCODEC_VERSION_57_100
std::vector<CobaltExtensionDemuxerSideData> side_data;
for (int i = 0; i < packet->side_data_elems; ++i) {
const AVPacketSideData& packet_side_data = packet->side_data[i];
@@ -613,7 +624,7 @@
buffer.side_data = side_data.data();
buffer.side_data_elements = side_data.size();
}
-#endif // LIBAVCODEC_LIBRARY_IS_FFMPEG
+#endif // LIBAVCODEC_VERSION_INT >= LIBAVCODEC_VERSION_57_100
read_cb(&buffer, read_cb_user_data);
}
@@ -633,6 +644,23 @@
return kCobaltExtensionDemuxerOk;
}
+FFmpegDemuxerImpl<FFMPEG>::ScopedAVPacket
+FFmpegDemuxerImpl<FFMPEG>::CreateScopedAVPacket() {
+ ScopedAVPacket packet;
+
+#if LIBAVCODEC_VERSION_INT >= LIBAVCODEC_VERSION_57_100
+ packet.reset(GetDispatch()->av_packet_alloc());
+#else
+ // av_packet_alloc is not available.
+ packet.reset(
+ static_cast<AVPacket*>(GetDispatch()->av_malloc(sizeof(AVPacket))));
+ memset(packet.get(), 0, sizeof(AVPacket));
+ GetDispatch()->av_init_packet(packet.get());
+#endif // LIBAVCODEC_VERSION_INT >= LIBAVCODEC_VERSION_57_100
+
+ return packet;
+}
+
// Returns the next packet of type |type|, or nullptr if EoS has been reached
// or an error was encountered.
FFmpegDemuxerImpl<FFMPEG>::ScopedAVPacket FFmpegDemuxerImpl<
@@ -640,13 +668,14 @@
// Handle the simple case: if we already have a packet buffered, just return
// it.
ScopedAVPacket packet = GetBufferedPacket(type);
- if (packet)
+ if (packet) {
return packet;
+ }
// Read another packet from FFmpeg. We may have to discard a packet if it's
// not from the right stream. Additionally, if we hit end-of-file or an
// error, we need to return null.
- packet.reset(GetDispatch()->av_packet_alloc());
+ packet = CreateScopedAVPacket();
while (true) {
int result = GetDispatch()->av_read_frame(format_context_, packet.get());
if (result < 0) {
@@ -665,7 +694,7 @@
// The caller doesn't need a video packet; just buffer it and allocate a
// new packet.
BufferPacket(std::move(packet), kCobaltExtensionDemuxerStreamTypeVideo);
- packet.reset(GetDispatch()->av_packet_alloc());
+ packet = CreateScopedAVPacket();
continue;
} else if (audio_stream_ && packet->stream_index == audio_stream_->index) {
if (type == kCobaltExtensionDemuxerStreamTypeAudio) {
@@ -676,13 +705,17 @@
// The caller doesn't need an audio packet; just buffer it and allocate
// a new packet.
BufferPacket(std::move(packet), kCobaltExtensionDemuxerStreamTypeAudio);
- packet.reset(GetDispatch()->av_packet_alloc());
+ packet = CreateScopedAVPacket();
continue;
}
- // This is a packet for a stream we don't care about. Unref it and keep
- // searching.
+// This is a packet for a stream we don't care about. Unref it (clear the
+// fields) and keep searching.
+#if LIBAVCODEC_VERSION_INT >= LIBAVCODEC_VERSION_57_100
GetDispatch()->av_packet_unref(packet.get());
+#else
+ GetDispatch()->av_free_packet(packet.get());
+#endif // LIBAVCODEC_VERSION_INT >= LIBAVCODEC_VERSION_57_100
}
SB_NOTREACHED();
@@ -730,7 +763,7 @@
config->encryption_scheme = GetEncryptionScheme(*audio_stream);
-#if LIBAVCODEC_LIBRARY_IS_FFMPEG
+#if LIBAVFORMAT_VERSION_INT >= LIBAVFORMAT_VERSION_57_83
std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> codec_context(
GetDispatch()->avcodec_alloc_context3(nullptr));
if (!codec_context) {
@@ -741,9 +774,9 @@
codec_context.get(), audio_stream->codecpar) < 0) {
return false;
}
-#else // LIBAVCODEC_LIBRARY_IS_FFMPEG
+#else
AVCodecContext* codec_context = audio_stream->codec;
-#endif // LIBAVCODEC_LIBRARY_IS_FFMPEG
+#endif // LIBAVFORMAT_VERSION_INT >= LIBAVFORMAT_VERSION_57_83
config->codec = AvCodecIdToAudioCodec(codec_context->codec_id);
config->sample_format =
@@ -788,7 +821,7 @@
bool FFmpegDemuxerImpl<FFMPEG>::ParseVideoConfig(
AVStream* video_stream,
CobaltExtensionDemuxerVideoDecoderConfig* config) {
-#if LIBAVCODEC_LIBRARY_IS_FFMPEG
+#if LIBAVFORMAT_VERSION_INT >= LIBAVFORMAT_VERSION_57_83
std::unique_ptr<AVCodecContext, ScopedPtrAVFreeContext> codec_context(
GetDispatch()->avcodec_alloc_context3(nullptr));
@@ -802,7 +835,7 @@
}
#else
AVCodecContext* codec_context = video_stream->codec;
-#endif
+#endif // LIBAVFORMAT_VERSION_INT >= LIBAVFORMAT_VERSION_57_83
config->visible_rect_x = 0;
config->visible_rect_y = 0;
diff --git a/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.h b/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.h
index 25f7308..cc609c5 100644
--- a/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.h
+++ b/starboard/shared/ffmpeg/ffmpeg_demuxer_impl.h
@@ -81,6 +81,12 @@
explicit FFmpegDemuxerImpl(CobaltExtensionDemuxerDataSource* data_source);
+ // Creates an empty ScopedAVPacket. The returned ScopedAVPacket will not be
+ // null.
+ // Since different versions of FFmpeg require different functions to create an
+ // AVPacket, this function abstracts away those differences.
+ ScopedAVPacket CreateScopedAVPacket();
+
// Returns the next packet of type |type|, or nullptr if EoS has been reached
// or an error was encountered.
ScopedAVPacket GetNextPacket(CobaltExtensionDemuxerStreamType type);
diff --git a/starboard/shared/ffmpeg/ffmpeg_dispatch.h b/starboard/shared/ffmpeg/ffmpeg_dispatch.h
index 1b46ff6..347566b 100644
--- a/starboard/shared/ffmpeg/ffmpeg_dispatch.h
+++ b/starboard/shared/ffmpeg/ffmpeg_dispatch.h
@@ -114,6 +114,7 @@
void (*av_free)(void* ptr);
AVPacket* (*av_packet_alloc)(void);
void (*av_packet_free)(AVPacket** pkt);
+ void (*av_free_packet)(AVPacket* pkt);
AVDictionaryEntry* (*av_dict_get)(const AVDictionary* m,
const char* key,
const AVDictionaryEntry* prev,
diff --git a/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc b/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc
index 748d897..6fc39d1 100644
--- a/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_dynamic_load_dispatch_impl.cc
@@ -262,10 +262,10 @@
INITSYMBOL(avutil_, av_freep);
INITSYMBOL(avutil_, av_frame_alloc);
INITSYMBOL(avutil_, av_free);
- INITSYMBOL(avutil_, av_dict_get);
INITSYMBOL(avutil_, av_rescale_rnd);
#if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
INITSYMBOL(avutil_, av_frame_free);
+ INITSYMBOL(avutil_, av_dict_get);
#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
INITSYMBOL(avutil_, av_frame_unref);
INITSYMBOL(avutil_, av_samples_get_buffer_size);
@@ -294,6 +294,7 @@
INITSYMBOL(avcodec_, av_packet_free);
INITSYMBOL(avcodec_, av_packet_unref);
INITSYMBOL(avcodec_, avcodec_parameters_to_context);
+ INITSYMBOL(avcodec_, av_free_packet);
// Load symbols from the avformat shared library.
INITSYMBOL(avformat_, avformat_version);
diff --git a/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc b/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc
index 20d7d1b..c3d2f52 100644
--- a/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_linked_dispatch_impl.cc
@@ -53,10 +53,13 @@
SB_DCHECK(ffmpeg->avutil_version);
INITSYMBOL(av_malloc);
INITSYMBOL(av_freep);
+ INITSYMBOL(av_free);
+ INITSYMBOL(av_rescale_rnd);
#if LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
INITSYMBOL(av_frame_alloc);
INITSYMBOL(av_frame_free);
INITSYMBOL(av_frame_unref);
+ INITSYMBOL(av_dict_get);
#endif // LIBAVUTIL_VERSION_INT >= LIBAVUTIL_VERSION_52_8
INITSYMBOL(av_samples_get_buffer_size);
INITSYMBOL(av_opt_set_int);
@@ -83,6 +86,22 @@
#endif // LIBAVUTIL_VERSION_INT > LIBAVUTIL_VERSION_52_8
INITSYMBOL(avcodec_align_dimensions2);
+#if LIBAVCODEC_LIBRARY_IS_FFMPEG
+ INITSYMBOL(av_packet_alloc);
+ INITSYMBOL(av_packet_free);
+ INITSYMBOL(av_packet_unref);
+ INITSYMBOL(avcodec_parameters_to_context);
+#endif // LIBAVCODEC_LIBRARY_IS_FFMPEG
+ INITSYMBOL(av_free_packet);
+
+ INITSYMBOL(av_read_frame);
+ INITSYMBOL(av_seek_frame);
+ INITSYMBOL(avformat_open_input);
+ INITSYMBOL(avformat_close_input);
+ INITSYMBOL(avformat_alloc_context);
+ INITSYMBOL(avformat_find_stream_info);
+ INITSYMBOL(avio_alloc_context);
+
// Load symbols from the avformat shared library.
INITSYMBOL(avformat_version);
SB_DCHECK(ffmpeg->avformat_version);
diff --git a/starboard/shared/starboard/player/filter/stub_video_decoder.cc b/starboard/shared/starboard/player/filter/stub_video_decoder.cc
index 1deb809..359b48c 100644
--- a/starboard/shared/starboard/player/filter/stub_video_decoder.cc
+++ b/starboard/shared/starboard/player/filter/stub_video_decoder.cc
@@ -14,7 +14,10 @@
#include "starboard/shared/starboard/player/filter/stub_video_decoder.h"
+#include <string>
+
#include "starboard/common/media.h"
+#include "starboard/shared/starboard/player/filter/cpu_video_frame.h"
namespace starboard {
namespace shared {
@@ -102,7 +105,7 @@
output_frame_timestamps_.insert(input_buffer->timestamp());
if (output_frame_timestamps_.size() > kMaxFramesToDelay) {
- output_frame = new VideoFrame(*output_frame_timestamps_.begin());
+ output_frame = CreateOutputFrame(*output_frame_timestamps_.begin());
output_frame_timestamps_.erase(output_frame_timestamps_.begin());
}
@@ -122,12 +125,32 @@
// If there are any remaining frames we need to output, send them all out
// before writing EOS.
for (const auto time : output_frame_timestamps_) {
- scoped_refptr<VideoFrame> output_frame = new VideoFrame(time);
+ scoped_refptr<VideoFrame> output_frame = CreateOutputFrame(time);
decoder_status_cb_(kBufferFull, output_frame);
}
decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
}
+scoped_refptr<VideoFrame> StubVideoDecoder::CreateOutputFrame(
+ SbTime timestamp) const {
+ int bits_per_channel = video_sample_info_.color_metadata.bits_per_channel;
+ if (bits_per_channel == 0) {
+ // Assume 8 bits when |bits_per_channel| is unknown (0).
+ bits_per_channel = 8;
+ }
+ int uv_stride = bits_per_channel > 8 ? video_sample_info_.frame_width
+ : video_sample_info_.frame_width / 2;
+ int y_stride = uv_stride * 2;
+ std::string data(y_stride * video_sample_info_.frame_height, 0);
+
+ return CpuVideoFrame::CreateYV12Frame(
+ bits_per_channel, video_sample_info_.frame_width,
+ video_sample_info_.frame_height, y_stride, uv_stride, timestamp,
+ reinterpret_cast<const uint8_t*>(data.data()),
+ reinterpret_cast<const uint8_t*>(data.data()),
+ reinterpret_cast<const uint8_t*>(data.data()));
+}
+
} // namespace filter
} // namespace player
} // namespace starboard
diff --git a/starboard/shared/starboard/player/filter/stub_video_decoder.h b/starboard/shared/starboard/player/filter/stub_video_decoder.h
index 670841c..0199274 100644
--- a/starboard/shared/starboard/player/filter/stub_video_decoder.h
+++ b/starboard/shared/starboard/player/filter/stub_video_decoder.h
@@ -52,6 +52,8 @@
void DecodeOneBuffer(const scoped_refptr<InputBuffer>& input_buffer);
void DecodeEndOfStream();
+ scoped_refptr<VideoFrame> CreateOutputFrame(SbTime timestamp) const;
+
DecoderStatusCB decoder_status_cb_;
media::VideoSampleInfo video_sample_info_;
diff --git a/starboard/shared/x11/application_x11.cc b/starboard/shared/x11/application_x11.cc
index a91bab6..a650884 100644
--- a/starboard/shared/x11/application_x11.cc
+++ b/starboard/shared/x11/application_x11.cc
@@ -18,12 +18,12 @@
#include <stdlib.h>
#include <unistd.h>
#define XK_3270 // for XK_3270_BackTab
-#include <X11/keysym.h>
-#include <X11/Xatom.h>
#include <X11/XF86keysym.h>
#include <X11/XKBlib.h>
+#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+#include <X11/keysym.h>
#include <algorithm>
#include <iomanip>
@@ -789,8 +789,8 @@
continue;
}
if (cpu_video_frame->format() != CpuVideoFrame::kBGRA32) {
- cpu_video_frame = cpu_video_frame->ConvertTo(
- CpuVideoFrame::kBGRA32);
+ cpu_video_frame =
+ cpu_video_frame->ConvertTo(CpuVideoFrame::kBGRA32);
}
current_video_frames_[player] = cpu_video_frame;
}
@@ -852,7 +852,11 @@
}
void ApplicationX11::PlayerSetBounds(SbPlayer player,
- int z_index, int x, int y, int width, int height) {
+ int z_index,
+ int x,
+ int y,
+ int width,
+ int height) {
ScopedLock lock(frame_mutex_);
bool player_exists =
diff --git a/third_party/web_platform_tests/tools/html5lib/html5lib/trie/_base.py b/third_party/web_platform_tests/tools/html5lib/html5lib/trie/_base.py
index 724486b..5f724b1 100644
--- a/third_party/web_platform_tests/tools/html5lib/html5lib/trie/_base.py
+++ b/third_party/web_platform_tests/tools/html5lib/html5lib/trie/_base.py
@@ -1,6 +1,6 @@
from __future__ import absolute_import, division, unicode_literals
-from collections import Mapping
+from collections.abc import Mapping
class Trie(Mapping):