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):