Import Cobalt 23.master.0.308707
diff --git a/cobalt/bindings/contexts.py b/cobalt/bindings/contexts.py
index 9285877..fb03640 100644
--- a/cobalt/bindings/contexts.py
+++ b/cobalt/bindings/contexts.py
@@ -244,7 +244,8 @@
not idl_type.is_callback_interface), 'Callback types not supported.'
element_cobalt_type = self.idl_type_to_cobalt_type(
self.resolve_typedef(result_idl_type))
- result = '::cobalt::script::Promise< %s >' % element_cobalt_type
+ result = 'std::unique_ptr<::cobalt::script::Promise< %s* > >' % (
+ element_cobalt_type)
return result
def idl_union_type_to_cobalt(self, idl_type):
diff --git a/cobalt/bindings/v8c/templates/interface.cc.template b/cobalt/bindings/v8c/templates/interface.cc.template
index a11e229..65f703c 100644
--- a/cobalt/bindings/v8c/templates/interface.cc.template
+++ b/cobalt/bindings/v8c/templates/interface.cc.template
@@ -40,6 +40,7 @@
#include "cobalt/script/v8c/entry_scope.h"
#include "cobalt/script/v8c/helpers.h"
#include "cobalt/script/v8c/native_promise.h"
+#include "cobalt/script/v8c/script_promise.h"
#include "cobalt/script/v8c/type_traits.h"
#include "cobalt/script/v8c/v8c_typed_arrays.h"
#include "cobalt/script/v8c/v8c_data_view.h"
diff --git a/cobalt/black_box_tests/testdata/service_worker_test.js b/cobalt/black_box_tests/testdata/service_worker_test.js
index 44ffc55..5b9e117 100644
--- a/cobalt/black_box_tests/testdata/service_worker_test.js
+++ b/cobalt/black_box_tests/testdata/service_worker_test.js
@@ -53,10 +53,10 @@
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);
- console.log('Client with frameType', clients[i].frameType);
- console.log('Client with id', clients[i].id);
- console.log('Client with type', clients[i].type);
+ console.log('Client with url', clients[i].url,
+ 'frameType', clients[i].frameType,
+ 'id', clients[i].id,
+ 'type', clients[i].type);
}
}, function (error) {
console.log(`(Unexpected) self.clients.matchAll(): ${error}`, error);
@@ -106,10 +106,10 @@
console.log('(Expected) self.clients.matchAll():', clients.length, clients);
// Note: This will return 0 clients if none are controlled so far.
for (var i = 0; i < clients.length; i++) {
- console.log('Client with url', clients[i].url);
- console.log('Client with frameType', clients[i].frameType);
- console.log('Client with id', clients[i].id);
- console.log('Client with type', clients[i].type);
+ console.log('Client with url', clients[i].url,
+ 'frameType', clients[i].frameType,
+ 'id', clients[i].id,
+ 'type', clients[i].type);
}
}, function (error) {
console.log(`(Unexpected) self.clients.matchAll(): ${error}`, error);
@@ -123,18 +123,18 @@
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);
- console.log('Client with frameType', clients[i].frameType);
- console.log('Client with id', clients[i].id);
- console.log('Client with type', clients[i].type);
+ console.log('Client with url', clients[i].url,
+ 'frameType', clients[i].frameType,
+ 'id', clients[i].id,
+ 'type', clients[i].type);
console.log('self.clients.get()');
self.clients.get(clients[i].id).then(function (client) {
console.log('(Expected) self.clients.get():', client);
- console.log('Client with url', client.url);
- console.log('Client with frameType', client.frameType);
- console.log('Client with id', client.id);
- console.log('Client with type', client.type);
+ console.log('Client with url', client.url,
+ 'frameType', client.frameType,
+ 'id', client.id,
+ 'type', client.type);
}, function (error) {
console.log(`(Unexpected) self.clients.get(): ${error}`, error);
});
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 cc0fcde..21cb0d7 100644
--- a/cobalt/black_box_tests/testdata/service_worker_test_claimable.js
+++ b/cobalt/black_box_tests/testdata/service_worker_test_claimable.js
@@ -27,37 +27,47 @@
});
}
+function delay_promise(delay) {
+ return new Promise(function (resolve) {
+ setTimeout(resolve.bind(null), delay)
+ });
+}
+
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')));
}
+
self.onactivate = function (e) {
console.log('onactivate event received', e);
// Claim should pass here, since the state is activating.
console.log('self.clients.claim()');
- self.clients.claim().then(function (result) {
+ e.waitUntil(self.clients.claim().then(function (result) {
console.log('(Expected) self.clients.claim():', result);
var options = {
includeUncontrolled: false, type: 'window'
};
- console.log('self.clients.matchAll(options)');
- 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);
- console.log('Client with frameType', clients[i].frameType);
- console.log('Client with id', clients[i].id);
- console.log('Client with type', clients[i].type);
- clients[i].postMessage(`You have been claimed, client with id ${clients[i].id}`);
- }
- }, function (error) {
- console.log(`(Unexpected) self.clients.matchAll(): ${error}`, error);
- });
+ e.waitUntil(delay_promise(1000).then(function () {
+ console.log('self.clients.matchAll(options)');
+ 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,
+ 'frameType', clients[i].frameType,
+ 'id', clients[i].id,
+ 'type', clients[i].type);
+ clients[i].postMessage(`You have been claimed, client with id ${clients[i].id}`);
+ }
+ }, 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);
@@ -73,10 +83,10 @@
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);
- console.log('Client with frameType', clients[i].frameType);
- console.log('Client with id', clients[i].id);
- console.log('Client with type', clients[i].type);
+ console.log('Client with url', clients[i].url,
+ 'frameType', clients[i].frameType,
+ 'id', clients[i].id,
+ 'type', clients[i].type);
}
}, function (error) {
console.log(`(Unexpected) self.clients.matchAll(): ${error}`, error);
diff --git a/cobalt/browser/BUILD.gn b/cobalt/browser/BUILD.gn
index 529dd2f..4c30f02 100644
--- a/cobalt/browser/BUILD.gn
+++ b/cobalt/browser/BUILD.gn
@@ -155,6 +155,7 @@
"//cobalt/browser/memory_settings:browser_memory_settings",
"//cobalt/browser/memory_tracker:memory_tracker_tool",
"//cobalt/build:cobalt_build_id",
+ "//cobalt/cache",
"//cobalt/configuration",
"//cobalt/css_parser",
"//cobalt/cssom",
diff --git a/cobalt/browser/application.cc b/cobalt/browser/application.cc
index d710cae..5fa7318 100644
--- a/cobalt/browser/application.cc
+++ b/cobalt/browser/application.cc
@@ -61,6 +61,7 @@
#include "cobalt/browser/switches.h"
#include "cobalt/browser/user_agent_platform_info.h"
#include "cobalt/browser/user_agent_string.h"
+#include "cobalt/cache/cache.h"
#include "cobalt/configuration/configuration.h"
#include "cobalt/extension/crash_handler.h"
#include "cobalt/extension/installation_manager.h"
@@ -648,6 +649,9 @@
watchdog::Watchdog::CreateInstance(persistent_settings_.get());
DCHECK(watchdog);
+ cobalt::cache::Cache::GetInstance()->set_persistent_settings(
+ persistent_settings_.get());
+
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
base::Optional<cssom::ViewportSize> requested_viewport_size =
GetRequestedViewportSize(command_line);
@@ -661,6 +665,7 @@
// Create the main components of our browser.
BrowserModule::Options options(web_options);
network_module_options.preferred_language = language;
+ network_module_options.persistent_settings = persistent_settings_.get();
options.persistent_settings = persistent_settings_.get();
options.command_line_auto_mem_settings =
memory_settings::GetSettings(*command_line);
@@ -860,7 +865,7 @@
#if SB_IS(EVERGREEN)
updater_module_.get(),
#endif
- options, persistent_settings_.get()));
+ options));
UpdateUserAgent();
diff --git a/cobalt/browser/browser_module.cc b/cobalt/browser/browser_module.cc
index 8b7b1d4..79dd071 100644
--- a/cobalt/browser/browser_module.cc
+++ b/cobalt/browser/browser_module.cc
@@ -219,16 +219,15 @@
} // namespace
-BrowserModule::BrowserModule(
- const GURL& url, base::ApplicationState initial_application_state,
- base::EventDispatcher* event_dispatcher,
- account::AccountManager* account_manager,
- network::NetworkModule* network_module,
+BrowserModule::BrowserModule(const GURL& url,
+ base::ApplicationState initial_application_state,
+ base::EventDispatcher* event_dispatcher,
+ account::AccountManager* account_manager,
+ network::NetworkModule* network_module,
#if SB_IS(EVERGREEN)
- updater::UpdaterModule* updater_module,
+ updater::UpdaterModule* updater_module,
#endif
- const Options& options,
- persistent_storage::PersistentSettings* persistent_settings)
+ const Options& options)
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(
weak_this_(weak_ptr_factory_.GetWeakPtr())),
@@ -298,8 +297,7 @@
next_timeline_id_(1),
current_splash_screen_timeline_id_(-1),
current_main_web_module_timeline_id_(-1),
- service_worker_registry_(network_module),
- persistent_settings_(persistent_settings) {
+ service_worker_registry_(network_module) {
TRACE_EVENT0("cobalt::browser", "BrowserModule::BrowserModule()");
// Apply platform memory setting adjustments and defaults.
@@ -2075,7 +2073,7 @@
dom_settings->window()->navigator()->user_agent_data();
h5vcc_settings.global_environment =
dom_settings->context()->global_environment();
- h5vcc_settings.persistent_settings = persistent_settings_;
+ h5vcc_settings.persistent_settings = options_.persistent_settings;
auto* h5vcc_object = new h5vcc::H5vcc(h5vcc_settings);
if (!web_module_created_callback_.is_null()) {
diff --git a/cobalt/browser/browser_module.h b/cobalt/browser/browser_module.h
index 0706039..a12551e 100644
--- a/cobalt/browser/browser_module.h
+++ b/cobalt/browser/browser_module.h
@@ -131,8 +131,7 @@
#if SB_IS(EVERGREEN)
updater::UpdaterModule* updater_module,
#endif
- const Options& options,
- persistent_storage::PersistentSettings* persistent_settings);
+ const Options& options);
~BrowserModule();
std::string GetUserAgent() { return network_module_->GetUserAgent(); }
@@ -724,8 +723,6 @@
// Manages the Service Workers.
ServiceWorkerRegistry service_worker_registry_;
-
- persistent_storage::PersistentSettings* persistent_settings_;
};
} // namespace browser
diff --git a/cobalt/build/ninja/README b/cobalt/build/ninja/README
deleted file mode 100644
index fe0f765..0000000
--- a/cobalt/build/ninja/README
+++ /dev/null
@@ -1,7 +0,0 @@
-ninja-win.exe built with MSVC 2012
-from
-https://github.com/REDACTED/ninja/commit/1287257fe343c4b6b0f760308a7bec52d86b0edf
-
-ninja-linux built with gcc
-from
-https://github.com/REDACTED/ninja/commit/613d89893e56acc8c3670a66a33fcd2caf7d9050
diff --git a/cobalt/build/ninja/ninja-linux b/cobalt/build/ninja/ninja-linux
deleted file mode 100755
index 1f30022..0000000
--- a/cobalt/build/ninja/ninja-linux
+++ /dev/null
Binary files differ
diff --git a/cobalt/build/ninja/ninja-linux32.armv7l b/cobalt/build/ninja/ninja-linux32.armv7l
deleted file mode 100644
index cb4c3ec..0000000
--- a/cobalt/build/ninja/ninja-linux32.armv7l
+++ /dev/null
Binary files differ
diff --git a/cobalt/build/ninja/ninja-win.exe b/cobalt/build/ninja/ninja-win.exe
deleted file mode 100755
index 4a68328..0000000
--- a/cobalt/build/ninja/ninja-win.exe
+++ /dev/null
Binary files differ
diff --git a/cobalt/cache/BUILD.gn b/cobalt/cache/BUILD.gn
index 7fe8ddd..edc1daf 100644
--- a/cobalt/cache/BUILD.gn
+++ b/cobalt/cache/BUILD.gn
@@ -22,6 +22,7 @@
"//base",
"//cobalt/base",
"//cobalt/configuration",
+ "//cobalt/persistent_storage:persistent_settings",
"//net",
"//starboard:starboard_headers_only",
]
diff --git a/cobalt/cache/cache.cc b/cobalt/cache/cache.cc
index 0600895..e2c568d 100644
--- a/cobalt/cache/cache.cc
+++ b/cobalt/cache/cache.cc
@@ -24,6 +24,8 @@
#include "base/optional.h"
#include "cobalt/configuration/configuration.h"
#include "cobalt/extension/javascript_cache.h"
+#include "cobalt/persistent_storage/persistent_settings.h"
+#include "net/disk_cache/cobalt/cobalt_backend_impl.h"
#include "starboard/configuration_constants.h"
#include "starboard/system.h"
@@ -86,14 +88,6 @@
return nullptr;
}
-bool CanCache(disk_cache::ResourceType resource_type, uint32_t data_size) {
- return cobalt::configuration::Configuration::GetInstance()
- ->CobaltCanStoreCompiledJavascript() &&
- data_size > 0u &&
- data_size >= GetMinSizeToCacheInBytes(resource_type) &&
- data_size <= GetMaxCacheStorageInBytes(resource_type);
-}
-
} // namespace
namespace cobalt {
@@ -158,6 +152,19 @@
return data;
}
+void Cache::set_enabled(bool enabled) { enabled_ = enabled; }
+
+void Cache::set_persistent_settings(
+ persistent_storage::PersistentSettings* persistent_settings) {
+ persistent_settings_ = persistent_settings;
+
+ // Guaranteed to be called before any calls to Retrieve()
+ // since set_persistent_settings() is called from the Application()
+ // constructor before the NetworkModule is initialized.
+ set_enabled(persistent_settings_->GetPersistentSettingAsBool(
+ disk_cache::kCacheEnabledPersistentSettingsKey, true));
+}
+
MemoryCappedDirectory* Cache::GetMemoryCappedDirectory(
disk_cache::ResourceType resource_type) {
base::AutoLock auto_lock(lock_);
@@ -221,5 +228,15 @@
}
}
+bool Cache::CanCache(disk_cache::ResourceType resource_type,
+ uint32_t data_size) {
+ return enabled_ &&
+ cobalt::configuration::Configuration::GetInstance()
+ ->CobaltCanStoreCompiledJavascript() &&
+ data_size > 0u &&
+ data_size >= GetMinSizeToCacheInBytes(resource_type) &&
+ data_size <= GetMaxCacheStorageInBytes(resource_type);
+}
+
} // namespace cache
} // namespace cobalt
diff --git a/cobalt/cache/cache.h b/cobalt/cache/cache.h
index 790da73..4f9e4fe 100644
--- a/cobalt/cache/cache.h
+++ b/cobalt/cache/cache.h
@@ -27,6 +27,7 @@
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "cobalt/cache/memory_capped_directory.h"
+#include "cobalt/persistent_storage/persistent_settings.h"
#include "net/disk_cache/cobalt/resource_type.h"
namespace base {
@@ -45,6 +46,11 @@
disk_cache::ResourceType resource_type, uint32_t key,
std::function<std::unique_ptr<std::vector<uint8_t>>()> generate);
+ void set_enabled(bool enabled);
+
+ void set_persistent_settings(
+ persistent_storage::PersistentSettings* persistent_settings);
+
private:
friend struct base::DefaultSingletonTraits<Cache>;
Cache() {}
@@ -56,6 +62,7 @@
void Notify(disk_cache::ResourceType resource_type, uint32_t key);
void TryStore(disk_cache::ResourceType resource_type, uint32_t key,
const std::vector<uint8_t>& data);
+ bool CanCache(disk_cache::ResourceType resource_type, uint32_t data_size);
mutable base::Lock lock_;
// The following map is only used when the JavaScript cache extension is
@@ -65,6 +72,9 @@
std::map<disk_cache::ResourceType,
std::map<uint32_t, std::vector<base::WaitableEvent*>>>
pending_;
+ bool enabled_;
+
+ persistent_storage::PersistentSettings* persistent_settings_;
DISALLOW_COPY_AND_ASSIGN(Cache);
}; // class Cache
diff --git a/cobalt/evergreen_tests/evergreen_tests.py b/cobalt/evergreen_tests/evergreen_tests.py
index 74eafb0..00d23ae 100644
--- a/cobalt/evergreen_tests/evergreen_tests.py
+++ b/cobalt/evergreen_tests/evergreen_tests.py
@@ -31,6 +31,7 @@
def _Exec(cmd, env=None):
+ """Executes a command in a subprocess and returns the result."""
try:
msg = 'Executing:\n ' + ' '.join(cmd)
logging.info(msg)
@@ -46,31 +47,8 @@
return 1
-def main():
- arg_parser = argparse.ArgumentParser()
- arg_parser.add_argument(
- '--no-can_mount_tmpfs',
- dest='can_mount_tmpfs',
- action='store_false',
- help='A temporary filesystem cannot be mounted on the target device.')
- arg_parser.add_argument(
- '--platform_under_test',
- default=_DEFAULT_PLATFORM_UNDER_TEST,
- help='The platform to run the tests on (e.g., linux or raspi).')
- authentication_method = arg_parser.add_mutually_exclusive_group()
- authentication_method.add_argument(
- '--public-key-auth',
- help='Public key authentication should be used with the remote device.',
- action='store_true')
- authentication_method.add_argument(
- '--password-auth',
- help='Password authentication should be used with the remote device.',
- action='store_true')
- command_line.AddLauncherArguments(arg_parser)
- args = arg_parser.parse_args()
-
- log_level.InitializeLogging(args)
-
+def _RunTests(arg_parser, args, use_compressed_system_image):
+ """Runs an instance of the Evergreen tests for the provided configuration."""
launcher_params = command_line.CreateLauncherParams(arg_parser)
# Creating an instance of the Evergreen abstract launcher implementation
@@ -86,7 +64,8 @@
loader_platform=launcher_params.loader_platform,
loader_config=launcher_params.loader_config,
loader_target='loader_app',
- loader_out_directory=launcher_params.loader_out_directory)
+ loader_out_directory=launcher_params.loader_out_directory,
+ use_compressed_library=use_compressed_system_image)
# The automated tests use the |OUT| environment variable as the path to a
# known directory structure containing the desired binaries. This path is
@@ -118,10 +97,52 @@
command.append('-a')
command.append('password')
+ if use_compressed_system_image:
+ command.append('-c')
+
command.append(args.platform_under_test)
return _Exec(command, env)
+def main():
+ arg_parser = argparse.ArgumentParser()
+ arg_parser.add_argument(
+ '--no-can_mount_tmpfs',
+ dest='can_mount_tmpfs',
+ action='store_false',
+ help='A temporary filesystem cannot be mounted on the target device.')
+ arg_parser.add_argument(
+ '--platform_under_test',
+ default=_DEFAULT_PLATFORM_UNDER_TEST,
+ help='The platform to run the tests on (e.g., linux or raspi).')
+ authentication_method = arg_parser.add_mutually_exclusive_group()
+ authentication_method.add_argument(
+ '--public-key-auth',
+ help='Public key authentication should be used with the remote device.',
+ action='store_true')
+ authentication_method.add_argument(
+ '--password-auth',
+ help='Password authentication should be used with the remote device.',
+ action='store_true')
+ arg_parser.add_argument(
+ '--no-rerun_using_compressed_system_image',
+ dest='rerun_using_compressed_system_image',
+ action='store_false',
+ help='Do not run a second instance of the tests with a compressed system '
+ 'image.')
+ command_line.AddLauncherArguments(arg_parser)
+ args = arg_parser.parse_args()
+
+ log_level.InitializeLogging(args)
+
+ uncompressed_system_image_result = _RunTests(arg_parser, args, False)
+ if args.rerun_using_compressed_system_image:
+ compressed_system_image_result = _RunTests(arg_parser, args, True)
+ return uncompressed_system_image_result or compressed_system_image_result
+
+ return uncompressed_system_image_result
+
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/cobalt/h5vcc/BUILD.gn b/cobalt/h5vcc/BUILD.gn
index 246c396..9c5a145 100644
--- a/cobalt/h5vcc/BUILD.gn
+++ b/cobalt/h5vcc/BUILD.gn
@@ -76,6 +76,7 @@
"//cobalt/base",
"//cobalt/browser:browser_switches",
"//cobalt/build:cobalt_build_id",
+ "//cobalt/cache",
"//cobalt/configuration",
"//cobalt/dom",
"//cobalt/media",
diff --git a/cobalt/h5vcc/h5vcc_storage.cc b/cobalt/h5vcc/h5vcc_storage.cc
index e9a9229..dbb6b3b 100644
--- a/cobalt/h5vcc/h5vcc_storage.cc
+++ b/cobalt/h5vcc/h5vcc_storage.cc
@@ -19,10 +19,14 @@
#include "base/files/file_util.h"
#include "base/values.h"
+#include "cobalt/cache/cache.h"
#include "cobalt/h5vcc/h5vcc_storage.h"
#include "cobalt/persistent_storage/persistent_settings.h"
#include "cobalt/storage/storage_manager.h"
+#include "net/disk_cache/cobalt/cobalt_backend_impl.h"
#include "net/disk_cache/cobalt/resource_type.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_transaction_factory.h"
#include "starboard/common/file.h"
#include "starboard/common/string.h"
@@ -31,6 +35,7 @@
namespace h5vcc {
namespace {
+
const char kTestFileName[] = "cache_test_file.json";
const uint32 kWriteBufferSize = 1024 * 1024;
@@ -69,7 +74,16 @@
network::NetworkModule* network_module,
persistent_storage::PersistentSettings* persistent_settings)
: network_module_(network_module),
- persistent_settings_(persistent_settings) {}
+ persistent_settings_(persistent_settings) {
+ http_cache_ = nullptr;
+ if (network_module == nullptr) {
+ return;
+ }
+ auto url_request_context = network_module_->url_request_context();
+ if (url_request_context->using_http_cache()) {
+ http_cache_ = url_request_context->http_transaction_factory()->GetCache();
+ }
+}
void H5vccStorage::ClearCookies() {
net::CookieStore* cookie_store =
@@ -334,5 +348,29 @@
return quota;
}
+void H5vccStorage::EnableCache() {
+ persistent_settings_->SetPersistentSetting(
+ disk_cache::kCacheEnabledPersistentSettingsKey,
+ std::make_unique<base::Value>(true));
+
+ cobalt::cache::Cache::GetInstance()->set_enabled(true);
+
+ if (http_cache_) {
+ http_cache_->set_mode(net::HttpCache::Mode::NORMAL);
+ }
+}
+
+void H5vccStorage::DisableCache() {
+ persistent_settings_->SetPersistentSetting(
+ disk_cache::kCacheEnabledPersistentSettingsKey,
+ std::make_unique<base::Value>(false));
+
+ cobalt::cache::Cache::GetInstance()->set_enabled(false);
+
+ if (http_cache_) {
+ http_cache_->set_mode(net::HttpCache::Mode::DISABLE);
+ }
+}
+
} // namespace h5vcc
} // namespace cobalt
diff --git a/cobalt/h5vcc/h5vcc_storage.h b/cobalt/h5vcc/h5vcc_storage.h
index 7f6368b..ff96351 100644
--- a/cobalt/h5vcc/h5vcc_storage.h
+++ b/cobalt/h5vcc/h5vcc_storage.h
@@ -26,6 +26,7 @@
#include "cobalt/network/network_module.h"
#include "cobalt/persistent_storage/persistent_settings.h"
#include "cobalt/script/wrappable.h"
+#include "net/http/http_cache.h"
namespace cobalt {
namespace h5vcc {
@@ -56,6 +57,10 @@
H5vccStorageSetQuotaResponse SetQuota(
H5vccStorageResourceTypeQuotaBytesDictionary quota);
+ void EnableCache();
+
+ void DisableCache();
+
DEFINE_WRAPPABLE_TYPE(H5vccStorage);
private:
@@ -63,6 +68,8 @@
persistent_storage::PersistentSettings* persistent_settings_;
+ net::HttpCache* http_cache_;
+
DISALLOW_COPY_AND_ASSIGN(H5vccStorage);
};
diff --git a/cobalt/h5vcc/h5vcc_storage.idl b/cobalt/h5vcc/h5vcc_storage.idl
index 3a45910..93a6d37 100644
--- a/cobalt/h5vcc/h5vcc_storage.idl
+++ b/cobalt/h5vcc/h5vcc_storage.idl
@@ -24,4 +24,7 @@
H5vccStorageResourceTypeQuotaBytesDictionary getQuota();
H5vccStorageSetQuotaResponse setQuota(H5vccStorageResourceTypeQuotaBytesDictionary quota);
+
+ void enableCache();
+ void disableCache();
};
diff --git a/cobalt/network/BUILD.gn b/cobalt/network/BUILD.gn
index a68d213..53821e9 100644
--- a/cobalt/network/BUILD.gn
+++ b/cobalt/network/BUILD.gn
@@ -51,6 +51,7 @@
"//cobalt/build:cobalt_build_id",
"//cobalt/configuration",
"//cobalt/network_bridge",
+ "//cobalt/persistent_storage:persistent_settings",
"//cobalt/storage",
"//starboard/common",
"//third_party/protobuf:protobuf_lite",
diff --git a/cobalt/network/network_module.cc b/cobalt/network/network_module.cc
index 4eae9a8..f192961 100644
--- a/cobalt/network/network_module.cc
+++ b/cobalt/network/network_module.cc
@@ -178,7 +178,8 @@
#endif
url_request_context_.reset(
new URLRequestContext(storage_manager_, options_.custom_proxy, net_log,
- options_.ignore_certificate_errors, task_runner()));
+ options_.ignore_certificate_errors, task_runner(),
+ options_.persistent_settings));
network_delegate_.reset(
new NetworkDelegate(options_.cookie_policy, options_.https_requirement));
url_request_context_->set_http_user_agent_settings(
diff --git a/cobalt/network/network_module.h b/cobalt/network/network_module.h
index be655f9..ee04620 100644
--- a/cobalt/network/network_module.h
+++ b/cobalt/network/network_module.h
@@ -28,6 +28,7 @@
#include "cobalt/network/network_delegate.h"
#include "cobalt/network/url_request_context.h"
#include "cobalt/network/url_request_context_getter.h"
+#include "cobalt/persistent_storage/persistent_settings.h"
#include "net/base/static_cookie_policy.h"
#include "url/gurl.h"
#if defined(DIAL_SERVER)
@@ -60,13 +61,15 @@
ignore_certificate_errors(false),
https_requirement(network::kHTTPSRequired),
preferred_language("en-US"),
- max_network_delay(0) {}
+ max_network_delay(0),
+ persistent_settings(nullptr) {}
net::StaticCookiePolicy::Type cookie_policy;
bool ignore_certificate_errors;
HTTPSRequirement https_requirement;
std::string preferred_language;
std::string custom_proxy;
SbTime max_network_delay;
+ persistent_storage::PersistentSettings* persistent_settings;
};
// Simple constructor intended to be used only by tests.
diff --git a/cobalt/network/url_request_context.cc b/cobalt/network/url_request_context.cc
index 020410c..34cc482 100644
--- a/cobalt/network/url_request_context.cc
+++ b/cobalt/network/url_request_context.cc
@@ -26,12 +26,14 @@
#include "cobalt/network/persistent_cookie_store.h"
#include "cobalt/network/proxy_config_service.h"
#include "cobalt/network/switches.h"
+#include "cobalt/persistent_storage/persistent_settings.h"
#include "net/cert/cert_net_fetcher.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/cert_verify_proc.h"
#include "net/cert/ct_policy_enforcer.h"
#include "net/cert/do_nothing_ct_verifier.h"
#include "net/cert_net/cert_net_fetcher_impl.h"
+#include "net/disk_cache/cobalt/cobalt_backend_impl.h"
#include "net/dns/host_cache.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_cache.h"
@@ -68,7 +70,8 @@
URLRequestContext::URLRequestContext(
storage::StorageManager* storage_manager, const std::string& custom_proxy,
net::NetLog* net_log, bool ignore_certificate_errors,
- scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
+ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
+ persistent_storage::PersistentSettings* persistent_settings)
: ALLOW_THIS_IN_INITIALIZER_LIST(storage_(this))
#if defined(ENABLE_DEBUGGER)
,
@@ -176,6 +179,8 @@
std::unique_ptr<net::HttpNetworkLayer>(
new net::HttpNetworkLayer(storage_.http_network_session())));
} else {
+ using_http_cache_ = true;
+
// TODO: Set max size of cache in Starboard.
const int cache_size_mb = 24;
auto http_cache = std::make_unique<net::HttpCache>(
@@ -185,6 +190,15 @@
base::FilePath(std::string(path.data())),
/* max_bytes */ 1024 * 1024 * cache_size_mb),
true);
+ if (persistent_settings != nullptr) {
+ auto cache_enabled = persistent_settings->GetPersistentSettingAsBool(
+ disk_cache::kCacheEnabledPersistentSettingsKey, true);
+
+ if (!cache_enabled) {
+ http_cache->set_mode(net::HttpCache::Mode::DISABLE);
+ }
+ }
+
storage_.set_http_transaction_factory(std::move(http_cache));
}
@@ -214,6 +228,8 @@
storage_.http_network_session()->SetEnableQuic(enable_quic);
}
+bool URLRequestContext::using_http_cache() { return using_http_cache_; }
+
#if defined(ENABLE_DEBUGGER)
void URLRequestContext::OnQuicToggle(const std::string& message) {
DCHECK(storage_.http_network_session());
diff --git a/cobalt/network/url_request_context.h b/cobalt/network/url_request_context.h
index 074fcdb..8f673c3 100644
--- a/cobalt/network/url_request_context.h
+++ b/cobalt/network/url_request_context.h
@@ -20,6 +20,7 @@
#include "base/basictypes.h"
#include "base/macros.h"
#include "base/threading/thread_checker.h"
+#include "cobalt/persistent_storage/persistent_settings.h"
#include "net/cookies/cookie_monster.h"
#include "net/log/net_log.h"
#include "net/url_request/url_request_context.h"
@@ -42,19 +43,24 @@
URLRequestContext(
storage::StorageManager* storage_manager, const std::string& custom_proxy,
net::NetLog* net_log, bool ignore_certificate_errors,
- scoped_refptr<base::SingleThreadTaskRunner> network_task_runner);
+ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
+ persistent_storage::PersistentSettings* persistent_settings);
~URLRequestContext() override;
void SetProxy(const std::string& custom_proxy_rules);
void SetEnableQuic(bool enable_quic);
+ bool using_http_cache();
+
private:
THREAD_CHECKER(thread_checker_);
net::URLRequestContextStorage storage_;
scoped_refptr<net::CookieMonster::PersistentCookieStore>
persistent_cookie_store_;
+ bool using_http_cache_;
+
#if defined(ENABLE_DEBUGGER)
// Command handler object for toggling the input fuzzer on/off.
debug::console::ConsoleCommandManager::CommandHandler
diff --git a/cobalt/persistent_storage/persistent_settings.cc b/cobalt/persistent_storage/persistent_settings.cc
index d420c0e..ffebc2a 100644
--- a/cobalt/persistent_storage/persistent_settings.cc
+++ b/cobalt/persistent_storage/persistent_settings.cc
@@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "cobalt/persistent_storage/persistent_settings.h"
+
#include <utility>
#include <vector>
#include "base/values.h"
-#include "cobalt/persistent_storage/persistent_settings.h"
#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/configuration_constants.h"
diff --git a/cobalt/script/promise.h b/cobalt/script/promise.h
index 6fa4b54..d9e1c17 100644
--- a/cobalt/script/promise.h
+++ b/cobalt/script/promise.h
@@ -15,6 +15,9 @@
#ifndef COBALT_SCRIPT_PROMISE_H_
#define COBALT_SCRIPT_PROMISE_H_
+#include <memory>
+
+#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "cobalt/script/exception_message.h"
#include "cobalt/script/script_exception.h"
@@ -42,21 +45,19 @@
public:
// Call the |resolve| function that was passed as an argument to the Promise's
// executor function supplying |result| as its argument.
- virtual void Resolve(const T& result) const { NOTREACHED(); }
+ virtual void Resolve(const T& result) const = 0;
// Call the |reject| function passed as an argument to the Promise's executor
// function.
- virtual void Reject() const { NOTREACHED(); }
- virtual void Reject(SimpleExceptionType exception) const { NOTREACHED(); }
- virtual void Reject(const scoped_refptr<ScriptException>& result) const {
- NOTREACHED();
- }
+ virtual void Reject() const = 0;
+ virtual void Reject(SimpleExceptionType exception) const = 0;
+ virtual void Reject(const scoped_refptr<ScriptException>& result) const = 0;
// Returns the value of the [[PromiseState]] field.
- virtual PromiseState State() const {
- NOTREACHED();
- return PromiseState::kRejected;
- }
+ virtual PromiseState State() const = 0;
+
+ virtual void AddStateChangeCallback(
+ std::unique_ptr<base::OnceCallback<void()>> callback) = 0;
virtual ~Promise() {}
};
diff --git a/cobalt/script/v8c/conversion_helpers.h b/cobalt/script/v8c/conversion_helpers.h
index 85aa597..81bf303 100644
--- a/cobalt/script/v8c/conversion_helpers.h
+++ b/cobalt/script/v8c/conversion_helpers.h
@@ -16,7 +16,6 @@
#define COBALT_SCRIPT_V8C_CONVERSION_HELPERS_H_
#include <cmath>
-
#include <limits>
#include <string>
#include <utility>
@@ -29,6 +28,7 @@
#include "cobalt/base/compiler.h"
#include "cobalt/base/enable_if.h"
#include "cobalt/base/token.h"
+#include "cobalt/script/promise.h"
#include "cobalt/script/sequence.h"
#include "cobalt/script/v8c/algorithm_helpers.h"
#include "cobalt/script/v8c/helpers.h"
@@ -713,11 +713,7 @@
template <typename T>
void FromJSValue(v8::Isolate* isolate, v8::Local<v8::Value> value,
int conversion_flags, ExceptionState* exception_state,
- script::Promise<T>* out_promise) {
- // TODO(b/228976500): Implement conversion from JS to native for Promise<T>.
- // https://webidl.spec.whatwg.org/#es-promise
- NOTIMPLEMENTED();
-}
+ script::Promise<T>* out_promise);
// script::Handle<T> -> JSValue
template <typename T>
diff --git a/cobalt/script/v8c/native_promise.h b/cobalt/script/v8c/native_promise.h
index 85069c7..ab8b323 100644
--- a/cobalt/script/v8c/native_promise.h
+++ b/cobalt/script/v8c/native_promise.h
@@ -15,12 +15,15 @@
#ifndef COBALT_SCRIPT_V8C_NATIVE_PROMISE_H_
#define COBALT_SCRIPT_V8C_NATIVE_PROMISE_H_
+#include <memory>
+
#include "base/logging.h"
#include "base/threading/thread_checker.h"
#include "cobalt/script/promise.h"
#include "cobalt/script/v8c/conversion_helpers.h"
#include "cobalt/script/v8c/entry_scope.h"
#include "cobalt/script/v8c/scoped_persistent.h"
+#include "cobalt/script/v8c/script_promise.h"
#include "cobalt/script/v8c/type_traits.h"
#include "cobalt/script/v8c/v8c_exception_state.h"
#include "cobalt/script/v8c/v8c_user_object_holder.h"
@@ -40,10 +43,9 @@
*out_value = v8::Undefined(isolate);
}
-// Shared functionality for NativePromise<T>. Does not implement the Resolve
-// function, since that needs to be specialized for Promise<T>.
+// Shared functionality for NativePromise<T>.
template <typename T>
-class NativePromise : public ScopedPersistent<v8::Value>, public Promise<T> {
+class NativePromise : public ScriptPromise<T> {
public:
// ScriptValue boilerplate.
typedef Promise<T> BaseType;
@@ -63,20 +65,20 @@
PromiseResultUndefined, T>::type;
NativePromise(v8::Isolate* isolate, v8::Local<v8::Value> resolver)
- : isolate_(isolate), ScopedPersistent(isolate, resolver) {
+ : ScriptPromise<T>(isolate, resolver) {
DCHECK(resolver->IsPromise());
}
void Resolve(const ResolveType& value) const override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!this->IsEmpty());
- DCHECK(State() == PromiseState::kPending);
- EntryScope entry_scope(isolate_);
- v8::Local<v8::Context> context = isolate_->GetCurrentContext();
+ DCHECK(this->State() == PromiseState::kPending);
+ EntryScope entry_scope(this->isolate());
+ v8::Local<v8::Context> context = this->isolate()->GetCurrentContext();
v8::Local<v8::Promise::Resolver> promise_resolver = this->resolver();
v8::Local<v8::Value> converted_value;
- ToJSValue(isolate_, value, &converted_value);
+ ToJSValue(this->isolate(), value, &converted_value);
v8::Maybe<bool> reject_result =
promise_resolver->Resolve(context, converted_value);
DCHECK(reject_result.FromJust());
@@ -85,25 +87,26 @@
void Reject() const override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!this->IsEmpty());
- DCHECK(State() == PromiseState::kPending);
- EntryScope entry_scope(isolate_);
- v8::Local<v8::Context> context = isolate_->GetCurrentContext();
+ DCHECK(this->State() == PromiseState::kPending);
+ EntryScope entry_scope(this->isolate());
+ v8::Local<v8::Context> context = this->isolate()->GetCurrentContext();
v8::Local<v8::Promise::Resolver> promise_resolver = this->resolver();
v8::Maybe<bool> reject_result =
- promise_resolver->Reject(context, v8::Undefined(isolate_));
+ promise_resolver->Reject(context, v8::Undefined(this->isolate()));
DCHECK(reject_result.FromJust());
}
void Reject(SimpleExceptionType exception) const override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!this->IsEmpty());
- DCHECK(State() == PromiseState::kPending);
- EntryScope entry_scope(isolate_);
- v8::Local<v8::Context> context = isolate_->GetCurrentContext();
+ DCHECK(this->State() == PromiseState::kPending);
+ EntryScope entry_scope(this->isolate());
+ v8::Local<v8::Context> context = this->isolate()->GetCurrentContext();
v8::Local<v8::Promise::Resolver> promise_resolver = this->resolver();
- v8::Local<v8::Value> error_result = CreateErrorObject(isolate_, exception);
+ v8::Local<v8::Value> error_result =
+ CreateErrorObject(this->isolate(), exception);
v8::Maybe<bool> reject_result =
promise_resolver->Reject(context, error_result);
DCHECK(reject_result.FromJust());
@@ -112,51 +115,22 @@
void Reject(const scoped_refptr<ScriptException>& result) const override {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(!this->IsEmpty());
- DCHECK(State() == PromiseState::kPending);
- EntryScope entry_scope(isolate_);
- v8::Local<v8::Context> context = isolate_->GetCurrentContext();
+ DCHECK(this->State() == PromiseState::kPending);
+ EntryScope entry_scope(this->isolate());
+ v8::Local<v8::Context> context = this->isolate()->GetCurrentContext();
v8::Local<v8::Promise::Resolver> promise_resolver = this->resolver();
v8::Local<v8::Value> converted_result;
- ToJSValue(isolate_, result, &converted_result);
+ ToJSValue(this->isolate(), result, &converted_result);
v8::Maybe<bool> reject_result =
promise_resolver->Reject(context, converted_result);
DCHECK(reject_result.FromJust());
}
- PromiseState State() const override {
- DCHECK(!this->IsEmpty());
- EntryScope entry_scope(isolate_);
-
- v8::Promise::PromiseState v8_promise_state = this->promise()->State();
- switch (v8_promise_state) {
- case v8::Promise::kPending:
- return PromiseState::kPending;
- case v8::Promise::kFulfilled:
- return PromiseState::kFulfilled;
- case v8::Promise::kRejected:
- return PromiseState::kRejected;
- }
- NOTREACHED();
- return PromiseState::kRejected;
- }
-
- v8::Local<v8::Promise> promise() const {
- DCHECK(!this->IsEmpty());
- return resolver()->GetPromise();
- }
-
private:
- v8::Isolate* isolate_;
-
// Thread checker ensures all calls to the Promise are made from the same
// thread that it is created in.
THREAD_CHECKER(thread_checker_);
-
- v8::Local<v8::Promise::Resolver> resolver() const {
- DCHECK(!this->IsEmpty());
- return this->Get().Get(isolate_).template As<v8::Promise::Resolver>();
- }
};
template <typename T>
diff --git a/cobalt/script/v8c/script_promise.h b/cobalt/script/v8c/script_promise.h
new file mode 100644
index 0000000..93c4561
--- /dev/null
+++ b/cobalt/script/v8c/script_promise.h
@@ -0,0 +1,148 @@
+// 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_SCRIPT_V8C_SCRIPT_PROMISE_H_
+#define COBALT_SCRIPT_V8C_SCRIPT_PROMISE_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/threading/thread_checker.h"
+#include "cobalt/script/promise.h"
+#include "cobalt/script/v8c/conversion_helpers.h"
+#include "cobalt/script/v8c/entry_scope.h"
+#include "cobalt/script/v8c/scoped_persistent.h"
+#include "cobalt/script/v8c/type_traits.h"
+#include "cobalt/script/v8c/v8c_exception_state.h"
+#include "cobalt/script/v8c/v8c_user_object_holder.h"
+#include "v8/include/v8.h"
+
+namespace cobalt {
+namespace script {
+namespace v8c {
+
+// Shared functionality for ScriptPromise<T>. Does not implement the Resolve
+// function, since that needs to be specialized for Promise<T>.
+template <typename T>
+class ScriptPromise : public ScopedPersistent<v8::Value>, public Promise<T> {
+ public:
+ // ScriptValue boilerplate.
+ typedef Promise<T> BaseType;
+
+ // Handle special case T=void, by swapping the input parameter |T| for
+ // |PromiseResultUndefined|. Combined with how |Promise| handles this
+ // special case, we're left with something like:
+ //
+ // NativePromise<T> -> Promise<T>
+ // ^
+ // | (T=PromiseResultUndefined)
+ // /
+ // NativePromise<void> -> Promise<void>
+ //
+ using ResolveType =
+ typename std::conditional<std::is_same<T, void>::value,
+ PromiseResultUndefined, T>::type;
+
+ ScriptPromise(v8::Isolate* isolate, v8::Local<v8::Value> resolver)
+ : isolate_(isolate), ScopedPersistent(isolate, resolver) {
+ DCHECK(resolver->IsPromise());
+ }
+
+ void Resolve(const ResolveType& value) const override { NOTREACHED(); }
+
+ void Reject() const override { NOTREACHED(); }
+
+ void Reject(SimpleExceptionType exception) const override { NOTREACHED(); }
+
+ void Reject(const scoped_refptr<ScriptException>& result) const override {
+ NOTREACHED();
+ }
+
+ PromiseState State() const override {
+ DCHECK(!this->IsEmpty());
+ EntryScope entry_scope(isolate_);
+
+ v8::Promise::PromiseState v8_promise_state = this->promise()->State();
+ switch (v8_promise_state) {
+ case v8::Promise::kPending:
+ return PromiseState::kPending;
+ case v8::Promise::kFulfilled:
+ return PromiseState::kFulfilled;
+ case v8::Promise::kRejected:
+ return PromiseState::kRejected;
+ }
+ NOTREACHED();
+ return PromiseState::kRejected;
+ }
+
+ void AddStateChangeCallback(
+ std::unique_ptr<base::OnceCallback<void()>> callback) override {
+ v8::Local<v8::Context> context = context_.NewLocal(isolate_);
+
+ auto callback_lambda = [](const v8::FunctionCallbackInfo<v8::Value>& info) {
+ auto* callback = static_cast<base::OnceCallback<void()>*>(
+ info.Data().As<v8::External>()->Value());
+ DCHECK(callback);
+ std::move(*callback).Run();
+ delete callback;
+ };
+ v8::Local<v8::Function> function =
+ v8::Function::New(isolate_->GetCurrentContext(), callback_lambda,
+ v8::External::New(isolate_, callback.release()))
+ .ToLocalChecked();
+ v8::Local<v8::Promise> result_promise;
+ if (!promise()
+ ->Then(context, function, function)
+ .ToLocal(&result_promise)) {
+ DLOG(ERROR) << "Unable to add promise state change callback.";
+ NOTREACHED();
+ }
+ }
+
+ v8::Local<v8::Promise> promise() const {
+ DCHECK(!this->IsEmpty());
+ return resolver()->GetPromise();
+ }
+
+ protected:
+ v8::Local<v8::Promise::Resolver> resolver() const {
+ DCHECK(!this->IsEmpty());
+ return this->Get().Get(isolate_).template As<v8::Promise::Resolver>();
+ }
+
+ v8::Isolate* isolate() const { return isolate_; }
+
+ private:
+ v8::Isolate* isolate_;
+ ScopedPersistent<v8::Context> context_;
+};
+
+// JSValue -> Promise
+template <typename T>
+void FromJSValue(v8::Isolate* isolate, v8::Local<v8::Value> value,
+ int conversion_flags, ExceptionState* exception_state,
+ std::unique_ptr<script::Promise<T*>>* out_promise) {
+ if (!value->IsPromise()) {
+ exception_state->SetSimpleException(kNotSupportedType);
+ return;
+ }
+ out_promise->reset(new ScriptPromise<T*>(isolate, value));
+}
+
+} // namespace v8c
+} // namespace script
+} // namespace cobalt
+
+#endif // COBALT_SCRIPT_V8C_SCRIPT_PROMISE_H_
diff --git a/cobalt/worker/extendable_event.h b/cobalt/worker/extendable_event.h
index 4e5d4ff..1765ad2 100644
--- a/cobalt/worker/extendable_event.h
+++ b/cobalt/worker/extendable_event.h
@@ -15,15 +15,26 @@
#ifndef COBALT_WORKER_EXTENDABLE_EVENT_H_
#define COBALT_WORKER_EXTENDABLE_EVENT_H_
+#include <memory>
#include <string>
+#include <utility>
+#include "base/bind.h"
#include "cobalt/base/token.h"
#include "cobalt/script/promise.h"
#include "cobalt/script/v8c/native_promise.h"
#include "cobalt/script/value_handle.h"
#include "cobalt/script/wrappable.h"
+#include "cobalt/web/context.h"
+#include "cobalt/web/dom_exception.h"
+#include "cobalt/web/environment_settings.h"
#include "cobalt/web/event.h"
+#include "cobalt/web/window_or_worker_global_scope.h"
#include "cobalt/worker/extendable_event_init.h"
+#include "cobalt/worker/service_worker_global_scope.h"
+#include "cobalt/worker/service_worker_jobs.h"
+#include "cobalt/worker/service_worker_object.h"
+#include "cobalt/worker/service_worker_registration_object.h"
namespace cobalt {
namespace worker {
@@ -35,16 +46,87 @@
ExtendableEvent(const std::string& type, const ExtendableEventInit& init_dict)
: Event(type, init_dict) {}
- void WaitUntil(script::EnvironmentSettings* settings,
- const script::Promise<script::ValueHandle>& promise) {
- // TODO(b/228976500): Implement WaitUntil().
- NOTIMPLEMENTED();
+ void WaitUntil(
+ script::EnvironmentSettings* settings,
+ std::unique_ptr<script::Promise<script::ValueHandle*>>& promise,
+ script::ExceptionState* exception_state) {
+ // Algorithm for waitUntil(), to add lifetime promise to event.
+ // https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
+
+ // 1. If event’s isTrusted attribute is false, throw an "InvalidStateError"
+ // DOMException.
+ // 2. If event is not active, throw an "InvalidStateError" DOMException.
+ if (!IsActive()) {
+ web::DOMException::Raise(web::DOMException::kInvalidStateErr,
+ exception_state);
+ return;
+ }
+ // 3. Add promise to event’s extend lifetime promises.
+ // 4. Increment event’s pending promises count by one.
+ ++pending_promise_count_;
+ // 5. Upon fulfillment or rejection of promise, queue a microtask to run
+ // these substeps:
+ std::unique_ptr<base::OnceCallback<void()>> callback(
+ new base::OnceCallback<void()>(std::move(
+ base::BindOnce(&ExtendableEvent::StateChange,
+ base::Unretained(this), settings, promise.get()))));
+ promise->AddStateChangeCallback(std::move(callback));
+ promise.release();
+ }
+
+ void StateChange(script::EnvironmentSettings* settings,
+ const script::Promise<script::ValueHandle*>* promise) {
+ // Implement the microtask called upon fulfillment or rejection of a
+ // promise, as part of the algorithm for waitUntil().
+ // https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
+ DCHECK(promise);
+ has_rejected_promise_ |=
+ promise->State() == script::PromiseState::kRejected;
+ // 5.1. Decrement event’s pending promises count by one.
+ --pending_promise_count_;
+ // 5.2. If event’s pending promises count is 0, then:
+ if (0 == pending_promise_count_) {
+ web::Context* context =
+ base::polymorphic_downcast<web::EnvironmentSettings*>(settings)
+ ->context();
+ ServiceWorkerJobs* jobs = context->service_worker_jobs();
+ DCHECK(jobs);
+ // 5.2.1. Let registration be the current global object's associated
+ // service worker's containing service worker registration.
+ jobs->message_loop()->task_runner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ServiceWorkerJobs::WaitUntilSubSteps,
+ base::Unretained(jobs),
+ base::Unretained(
+ context->GetWindowOrWorkerGlobalScope()
+ ->AsServiceWorker()
+ ->service_worker_object()
+ ->containing_service_worker_registration())));
+ }
+ delete promise;
+ }
+
+ bool IsActive() {
+ // An ExtendableEvent object is said to be active when its timed out flag
+ // is unset and either its pending promises count is greater than zero or
+ // its dispatch flag is set.
+ // https://w3c.github.io/ServiceWorker/#extendableevent-active
+ return !timed_out_flag_ &&
+ ((pending_promise_count_ > 0) || IsBeingDispatched());
}
DEFINE_WRAPPABLE_TYPE(ExtendableEvent);
protected:
~ExtendableEvent() override {}
+
+ private:
+ // https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises
+ // std::list<script::Promise<script::ValueHandle*>> extend_lifetime_promises_;
+ int pending_promise_count_ = 0;
+ bool has_rejected_promise_ = false;
+ // https://w3c.github.io/ServiceWorker/#extendableevent-timed-out-flag
+ bool timed_out_flag_ = false;
};
} // namespace worker
diff --git a/cobalt/worker/extendable_event.idl b/cobalt/worker/extendable_event.idl
index 2297cbc..8a12344 100644
--- a/cobalt/worker/extendable_event.idl
+++ b/cobalt/worker/extendable_event.idl
@@ -18,5 +18,5 @@
Exposed = ServiceWorker,
Constructor(DOMString type, optional ExtendableEventInit eventInitDict)
] interface ExtendableEvent : Event {
- [CallWith = EnvironmentSettings] void waitUntil(Promise<any> f);
+ [RaisesException, CallWith = EnvironmentSettings] void waitUntil(Promise<any> f);
};
diff --git a/cobalt/worker/service_worker_jobs.cc b/cobalt/worker/service_worker_jobs.cc
index f901854..9ba0101 100644
--- a/cobalt/worker/service_worker_jobs.cc
+++ b/cobalt/worker/service_worker_jobs.cc
@@ -44,6 +44,7 @@
#include "cobalt/worker/client.h"
#include "cobalt/worker/client_query_options.h"
#include "cobalt/worker/client_type.h"
+#include "cobalt/worker/extendable_event.h"
#include "cobalt/worker/frame_type.h"
#include "cobalt/worker/service_worker.h"
#include "cobalt/worker/service_worker_container.h"
@@ -891,7 +892,7 @@
// 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 web::Event(base::Tokens::install()));
+ 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.
@@ -1137,7 +1138,7 @@
// 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 web::Event(base::Tokens::activate()));
+ new ExtendableEvent(base::Tokens::activate()));
// 11.1.1.4. WaitForAsynchronousExtensions: Wait, in
// parallel, until e is not active.
},
@@ -1837,6 +1838,23 @@
std::move(promise_reference)));
}
+void ServiceWorkerJobs::WaitUntilSubSteps(
+ ServiceWorkerRegistrationObject* registration) {
+ TRACE_EVENT0("cobalt::worker", "ServiceWorkerJobs::WaitUntilSubSteps()");
+ DCHECK_EQ(message_loop_, base::MessageLoop::current());
+ // Sub steps for WaitUntil.
+ // https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
+ // 5.2.2. If registration is unregistered, invoke Try Clear Registration
+ // with registration.
+ if (scope_to_registration_map_.IsUnregistered(registration)) {
+ TryClearRegistration(registration);
+ }
+ // 5.2.3. If registration is not null, invoke Try Activate with
+ // registration.
+ if (registration) {
+ TryActivate(registration);
+ }
+}
void ServiceWorkerJobs::ClientsGetSubSteps(
web::EnvironmentSettings* settings,
ServiceWorkerObject* associated_service_worker,
diff --git a/cobalt/worker/service_worker_jobs.h b/cobalt/worker/service_worker_jobs.h
index 89dd24a..8e09831 100644
--- a/cobalt/worker/service_worker_jobs.h
+++ b/cobalt/worker/service_worker_jobs.h
@@ -204,6 +204,10 @@
const base::WeakPtr<ServiceWorkerObject>& service_worker,
std::unique_ptr<script::ValuePromiseVoid::Reference> promise_reference);
+ // Sub steps for WaitUntil.
+ // https://w3c.github.io/ServiceWorker/#dom-extendableevent-waituntil
+ void WaitUntilSubSteps(ServiceWorkerRegistrationObject* registration);
+
// Parallel sub steps (2) for algorithm for Clients.get(id):
// https://w3c.github.io/ServiceWorker/#clients-get
void ClientsGetSubSteps(
diff --git a/net/disk_cache/cobalt/cobalt_backend_impl.h b/net/disk_cache/cobalt/cobalt_backend_impl.h
index c2b82f7..5a4443b 100644
--- a/net/disk_cache/cobalt/cobalt_backend_impl.h
+++ b/net/disk_cache/cobalt/cobalt_backend_impl.h
@@ -31,6 +31,8 @@
namespace disk_cache {
+const char kCacheEnabledPersistentSettingsKey[] = "cacheEnabled";
+
// This class implements the Backend interface. An object of this class handles
// the operations of the cache without writing to disk.
class NET_EXPORT_PRIVATE CobaltBackendImpl final : public Backend {
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
index a858ea3..4b05f41 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
@@ -29,6 +29,7 @@
import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
import android.widget.FrameLayout;
+import dev.cobalt.media.AudioOutputManager;
import dev.cobalt.media.MediaCodecUtil;
import dev.cobalt.media.VideoSurfaceView;
import dev.cobalt.util.DisplayUtil;
@@ -37,6 +38,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
/** Native activity that has the required JNI methods called by the Starboard implementation. */
public abstract class CobaltActivity extends NativeActivity {
@@ -130,6 +132,8 @@
}
DisplayUtil.cacheDefaultDisplay(this);
+ DisplayUtil.addDisplayListener(this);
+ AudioOutputManager.addAudioDeviceListener(this);
getStarboardBridge().onActivityStart(this, keyboardEditor);
super.onStart();
@@ -248,11 +252,12 @@
return;
}
- String customProxy = String.format("--proxy=\"http=http://%s:%d\"", config.first, port);
+ String customProxy =
+ String.format(Locale.US, "--proxy=\"http=http://%s:%d\"", config.first, port);
Log.i(TAG, "addCustomProxyArgs: " + customProxy);
args.add(customProxy);
} catch (NumberFormatException e) {
- Log.w(TAG, String.format("http.proxyPort: %s is not valid number", config.second), e);
+ Log.w(TAG, "http.proxyPort: %s is not valid number", config.second, e);
}
}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/ArtworkLoader.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/ArtworkLoader.java
index f131da7..758030d 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/ArtworkLoader.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/ArtworkLoader.java
@@ -29,6 +29,7 @@
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.util.Locale;
/** Loads MediaImage artwork, and caches one image. */
public class ArtworkLoader {
@@ -97,7 +98,7 @@
private Size parseImageSize(MediaImage image) {
try {
String sizeStr = image.sizes.split("\\s+", -1)[0];
- return Size.parseSize(sizeStr.toLowerCase());
+ return Size.parseSize(sizeStr.toLowerCase(Locale.US));
} catch (NumberFormatException | NullPointerException e) {
return new Size(0, 0);
}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
index 5a13b01..ebd8967 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
@@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
/** Creates and destroys AudioTrackBridge and handles the volume change. */
@@ -103,9 +104,9 @@
// AudioFormat.
Log.v(
TAG,
- String.format(
- "Setting |hasAudioDeviceChanged| to true for audio device %s, %s.",
- info.getProductName(), getDeviceTypeNameV23(info.getType())));
+ "Setting |hasAudioDeviceChanged| to true for audio device %s, %s.",
+ info.getProductName(),
+ getDeviceTypeNameV23(info.getType()));
hasAudioDeviceChanged.set(true);
break;
}
@@ -116,9 +117,8 @@
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
Log.v(
TAG,
- String.format(
- "onAudioDevicesAdded() called, |initialDevicesAdded| is: %b.",
- initialDevicesAdded));
+ "onAudioDevicesAdded() called, |initialDevicesAdded| is: %b.",
+ initialDevicesAdded);
if (initialDevicesAdded) {
handleConnectedDeviceChange(addedDevices);
return;
@@ -236,7 +236,7 @@
return "TYPE_WIRED_HEADSET";
default:
// This may include constants introduced after API 23.
- return String.format("TYPE_UNKNOWN (%d)", device_type);
+ return String.format(Locale.US, "TYPE_UNKNOWN (%d)", device_type);
}
}
@@ -278,7 +278,7 @@
break;
default:
// This may include constants introduced after API 23.
- encodings_in_string.append(String.format("UNKNOWN (%d)", encodings[i]));
+ encodings_in_string.append(String.format(Locale.US, "UNKNOWN (%d)", encodings[i]));
break;
}
if (i != encodings.length - 1) {
@@ -304,12 +304,11 @@
for (AudioDeviceInfo info : deviceInfos) {
Log.i(
TAG,
- String.format(
- " Audio Device: %s, channels: %s, sample rates: %s, encodings: %s",
- getDeviceTypeNameV23(info.getType()),
- Arrays.toString(info.getChannelCounts()),
- Arrays.toString(info.getSampleRates()),
- getEncodingNames(info.getEncodings())));
+ " Audio Device: %s, channels: %s, sample rates: %s, encodings: %s",
+ getDeviceTypeNameV23(info.getType()),
+ Arrays.toString(info.getChannelCounts()),
+ Arrays.toString(info.getSampleRates()),
+ getEncodingNames(info.getEncodings()));
}
}
@@ -348,10 +347,11 @@
if (AudioTrackBridge.AV_SYNC_HEADER_V1_SIZE % frameSizeInBytes != 0) {
Log.w(
TAG,
- String.format(
- "Disable tunnel mode due to sampleSizeInBytes (%d) * numberOfChannels (%d) isn't"
- + " aligned to AV_SYNC_HEADER_V1_SIZE (%d).",
- sampleSizeInBytes, numberOfChannels, AudioTrackBridge.AV_SYNC_HEADER_V1_SIZE));
+ "Disable tunnel mode due to sampleSizeInBytes (%d) * numberOfChannels (%d) isn't"
+ + " aligned to AV_SYNC_HEADER_V1_SIZE (%d).",
+ sampleSizeInBytes,
+ numberOfChannels,
+ AudioTrackBridge.AV_SYNC_HEADER_V1_SIZE);
return -1;
}
}
@@ -366,10 +366,10 @@
if (Build.VERSION.SDK_INT < 23) {
Log.i(
TAG,
- String.format(
- "Passthrough on encoding %d is rejected on api %d, as passthrough is only"
- + " supported on api 23 or later.",
- encoding, Build.VERSION.SDK_INT));
+ "Passthrough on encoding %d is rejected on api %d, as passthrough is only"
+ + " supported on api 23 or later.",
+ encoding,
+ Build.VERSION.SDK_INT);
return false;
}
@@ -382,10 +382,9 @@
if (info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) {
Log.i(
TAG,
- String.format(
- "Passthrough on encoding %d is disabled because Bluetooth output device is"
- + " connected.",
- encoding));
+ "Passthrough on encoding %d is disabled because Bluetooth output device is"
+ + " connected.",
+ encoding);
return false;
}
}
@@ -396,34 +395,31 @@
if (hasPassthroughSupportForV23(deviceInfos, encoding)) {
Log.i(
TAG,
- String.format(
- "Passthrough on encoding %d is supported, as hasPassthroughSupportForV23() returns"
- + " true.",
- encoding));
+ "Passthrough on encoding %d is supported, as hasPassthroughSupportForV23() returns"
+ + " true.",
+ encoding);
} else {
if (Build.VERSION.SDK_INT < 29) {
Log.i(
TAG,
- String.format(
- "Passthrough on encoding %d is rejected, as"
- + " hasDirectSurroundPlaybackSupportForV29() is not called for api %d.",
- encoding, Build.VERSION.SDK_INT));
+ "Passthrough on encoding %d is rejected, as"
+ + " hasDirectSurroundPlaybackSupportForV29() is not called for api %d.",
+ encoding,
+ Build.VERSION.SDK_INT);
return false;
}
if (hasDirectSurroundPlaybackSupportForV29(encoding, DEFAULT_SURROUND_SAMPLE_RATE)) {
Log.i(
TAG,
- String.format(
- "Passthrough on encoding %d is supported, as"
- + " hasDirectSurroundPlaybackSupportForV29() returns true.",
- encoding));
+ "Passthrough on encoding %d is supported, as"
+ + " hasDirectSurroundPlaybackSupportForV29() returns true.",
+ encoding);
} else {
Log.i(
TAG,
- String.format(
- "Passthrough on encoding %d is not supported, as"
- + " hasDirectSurroundPlaybackSupportForV29() returns false.",
- encoding));
+ "Passthrough on encoding %d is not supported, as"
+ + " hasDirectSurroundPlaybackSupportForV29() returns false.",
+ encoding);
return false;
}
}
@@ -445,10 +441,9 @@
// HDMI and SPDIF are connected, where the output should fallback to AC3.
Log.w(
TAG,
- String.format(
- "Passthrough on encoding %d is disabled because creating AudioTrack raises"
- + " exception: ",
- encoding),
+ "Passthrough on encoding %d is disabled because creating AudioTrack raises"
+ + " exception: ",
+ encoding,
e);
return false;
}
@@ -475,31 +470,29 @@
// an empty array indicates that the device supports arbitrary encodings.
Log.i(
TAG,
- String.format(
- "Passthrough on encoding %d is supported on %s, because getEncodings() returns"
- + " an empty array.",
- encoding, getDeviceTypeNameV23(type)));
+ "Passthrough on encoding %d is supported on %s, because getEncodings() returns"
+ + " an empty array.",
+ encoding,
+ getDeviceTypeNameV23(type));
return true;
}
for (int i = 0; i < encodings.length; ++i) {
if (encodings[i] == encoding) {
Log.i(
TAG,
- String.format(
- "Passthrough on encoding %d is supported on %s.",
- encoding, getDeviceTypeNameV23(type)));
+ "Passthrough on encoding %d is supported on %s.",
+ encoding,
+ getDeviceTypeNameV23(type));
return true;
}
}
Log.i(
TAG,
- String.format(
- "Passthrough on encoding %d is not supported on %s.",
- encoding, getDeviceTypeNameV23(type)));
+ "Passthrough on encoding %d is not supported on %s.",
+ encoding,
+ getDeviceTypeNameV23(type));
}
- Log.i(
- TAG,
- String.format("Passthrough on encoding %d is not supported on any devices.", encoding));
+ Log.i(TAG, "Passthrough on encoding %d is not supported on any devices.", encoding);
return false;
}
@@ -511,19 +504,15 @@
&& encoding != AudioFormat.ENCODING_E_AC3_JOC) {
Log.w(
TAG,
- String.format(
- "hasDirectSurroundPlaybackSupportForV29() encountered unsupported encoding %d.",
- encoding));
+ "hasDirectSurroundPlaybackSupportForV29() encountered unsupported encoding %d.",
+ encoding);
return false;
}
boolean supported =
AudioTrack.isDirectPlaybackSupported(
getPassthroughAudioFormatFor(encoding, sampleRate), getDefaultAudioAttributes());
- Log.i(
- TAG,
- String.format(
- "isDirectPlaybackSupported() for encoding %d returned %b.", encoding, supported));
+ Log.i(TAG, "isDirectPlaybackSupported() for encoding %d returned %b.", encoding, supported);
return supported;
}
@@ -550,4 +539,31 @@
private boolean getAndResetHasAudioDeviceChanged() {
return hasAudioDeviceChanged.getAndSet(false);
}
+
+ private static AudioDeviceCallback audioDeviceCallback =
+ new AudioDeviceCallback() {
+ @Override
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ nativeOnAudioDeviceChanged();
+ }
+
+ @Override
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ nativeOnAudioDeviceChanged();
+ }
+ };
+
+ private static boolean audioDeviceListenerAdded = false;
+
+ public static void addAudioDeviceListener(Context context) {
+ if (audioDeviceListenerAdded) {
+ return;
+ }
+
+ AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ audioManager.registerAudioDeviceCallback(audioDeviceCallback, null);
+ audioDeviceListenerAdded = true;
+ }
+
+ private static native void nativeOnAudioDeviceChanged();
}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
index 4c11399..5050078 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
@@ -27,6 +27,7 @@
import dev.cobalt.util.UsedByNative;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Locale;
/**
* A wrapper of the android AudioTrack class. Android AudioTrack would not start playing until the
@@ -96,9 +97,12 @@
audioTrack = null;
String errorMessage =
String.format(
+ Locale.US,
"Enable tunnel mode when frame size is unaligned, "
+ "sampleType: %d, channel: %d, sync header size: %d.",
- sampleType, channelCount, AV_SYNC_HEADER_V1_SIZE);
+ sampleType,
+ channelCount,
+ AV_SYNC_HEADER_V1_SIZE);
Log.e(TAG, errorMessage);
throw new RuntimeException(errorMessage);
}
@@ -160,12 +164,11 @@
}
Log.i(
TAG,
- String.format(
- "AudioTrack created with buffer size %d (preferred: %d). The minimum buffer size is"
- + " %d.",
- audioTrackBufferSize,
- preferredBufferSizeInBytes,
- AudioTrack.getMinBufferSize(sampleRate, channelConfig, sampleType)));
+ "AudioTrack created with buffer size %d (preferred: %d). The minimum buffer size is"
+ + " %d.",
+ audioTrackBufferSize,
+ preferredBufferSizeInBytes,
+ AudioTrack.getMinBufferSize(sampleRate, channelConfig, sampleType));
}
public Boolean isAudioTrackValid() {
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
index a310b15..f36dad3 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java
@@ -515,9 +515,11 @@
Log.i(
TAG,
- String.format(
- "MediaSession state: %s, position: %d ms, speed: %f x, duration: %d ms",
- stateName, positionMs, speed, duration));
+ "MediaSession state: %s, position: %d ms, speed: %f x, duration: %d ms",
+ stateName,
+ positionMs,
+ speed,
+ duration);
playbackStateBuilder =
new PlaybackStateCompat.Builder()
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
index c5c66cd..eeed7a7 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -37,6 +37,7 @@
import dev.cobalt.util.UsedByNative;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Locale;
/** A wrapper of the MediaCodec class. */
@SuppressWarnings("unused")
@@ -141,7 +142,7 @@
return;
}
if (presentationTimeUs <= mLastFrameTimestampUs) {
- Log.v(TAG, String.format("Invalid output presentation timestamp."));
+ Log.v(TAG, "Invalid output presentation timestamp.");
return;
}
@@ -537,13 +538,10 @@
}
MediaCodec mediaCodec = null;
try {
- Log.i(TAG, String.format("Creating \"%s\" decoder.", decoderName));
+ Log.i(TAG, "Creating \"%s\" decoder.", decoderName);
mediaCodec = MediaCodec.createByCodecName(decoderName);
} catch (Exception e) {
- Log.e(
- TAG,
- String.format("Failed to create MediaCodec: %s, DecoderName: %s", mime, decoderName),
- e);
+ Log.e(TAG, "Failed to create MediaCodec: %s, DecoderName: %s", mime, decoderName, e);
return null;
}
if (mediaCodec == null) {
@@ -609,13 +607,16 @@
}
try {
- Log.i(TAG, String.format("Creating \"%s\" decoder.", decoderName));
+ Log.i(TAG, "Creating \"%s\" decoder.", decoderName);
mediaCodec = MediaCodec.createByCodecName(decoderName);
} catch (Exception e) {
String message =
String.format(
+ Locale.US,
"Failed to create MediaCodec: %s, mustSupportSecure: %s," + " DecoderName: %s",
- mime, crypto != null, decoderName);
+ mime,
+ crypto != null,
+ decoderName);
Log.e(TAG, message, e);
outCreateMediaCodecBridgeResult.mErrorMessage = message;
return;
@@ -1189,8 +1190,10 @@
+ (configurationData == null
? "|configurationData| is null."
: String.format(
+ Locale.US,
"Configuration data size (%d) is less than the required size (%d).",
- configurationData.length, MIN_OPUS_INITIALIZATION_DATA_BUFFER_SIZE)));
+ configurationData.length,
+ MIN_OPUS_INITIALIZATION_DATA_BUFFER_SIZE)));
return false;
}
// Both the number of samples to skip from the beginning of the stream and the amount of time
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
index 2d023be..e2e9b43 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
@@ -436,7 +436,7 @@
// Filter blacklisted video decoders.
String name = codecInfo.getName();
if (!isVp9AllowListed && videoCodecDenyList.contains(name)) {
- Log.v(TAG, String.format("Rejecting %s, reason: codec is on deny list", name));
+ Log.v(TAG, "Rejecting %s, reason: codec is on deny list", name);
continue;
}
if (name.endsWith(SECURE_DECODER_SUFFIX)) {
@@ -446,7 +446,7 @@
name.substring(0, name.length() - SECURE_DECODER_SUFFIX.length());
if (!isVp9AllowListed && videoCodecDenyList.contains(nameWithoutSecureSuffix)) {
String format = "Rejecting %s, reason: offpsec denylisted secure decoder";
- Log.v(TAG, String.format(format, name));
+ Log.v(TAG, format, name);
continue;
}
}
@@ -537,6 +537,7 @@
int fps) {
String decoderInfo =
String.format(
+ Locale.US,
"Searching for video decoder with parameters mimeType: %s, secure: %b, frameWidth:"
+ " %d, frameHeight: %d, bitrate: %d, fps: %d, mustSupportHdr: %b,"
+ " mustSupportSoftwareCodec: %b, mustSupportTunnelMode: %b,"
@@ -554,6 +555,7 @@
Log.v(TAG, decoderInfo);
String deviceInfo =
String.format(
+ Locale.US,
"brand: %s, model: %s, version: %s, API level: %d, isVp9AllowListed: %b",
Build.BRAND,
Build.MODEL,
@@ -606,9 +608,13 @@
|| !mustSupportSecure && requiresSecurePlayback) {
String message =
String.format(
+ Locale.US,
"Rejecting %s, reason: secure decoder requested: %b, "
+ "codec FEATURE_SecurePlayback supported: %b, required: %b",
- name, mustSupportSecure, supportsSecurePlayback, requiresSecurePlayback);
+ name,
+ mustSupportSecure,
+ supportsSecurePlayback,
+ requiresSecurePlayback);
Log.v(TAG, message);
continue;
}
@@ -623,9 +629,13 @@
|| !mustSupportTunnelMode && requiresTunneledPlayback) {
String message =
String.format(
+ Locale.US,
"Rejecting %s, reason: tunneled playback requested: %b, "
+ "codec FEATURE_TunneledPlayback supported: %b, required: %b",
- name, mustSupportTunnelMode, supportsTunneledPlayback, requiresTunneledPlayback);
+ name,
+ mustSupportTunnelMode,
+ supportsTunneledPlayback,
+ requiresTunneledPlayback);
Log.v(TAG, message);
continue;
}
@@ -652,31 +662,31 @@
if (frameWidth != 0 && frameHeight != 0) {
if (!videoCapabilities.isSizeSupported(frameWidth, frameHeight)) {
String format = "Rejecting %s, reason: width %s is not compatible with height %d";
- Log.v(TAG, String.format(format, name, frameWidth, frameHeight));
+ Log.v(TAG, format, name, frameWidth, frameHeight);
continue;
}
} else if (frameWidth != 0) {
if (!supportedWidths.contains(frameWidth)) {
String format = "Rejecting %s, reason: supported widths %s does not contain %d";
- Log.v(TAG, String.format(format, name, supportedWidths.toString(), frameWidth));
+ Log.v(TAG, format, name, supportedWidths.toString(), frameWidth);
continue;
}
} else if (frameHeight != 0) {
if (!supportedHeights.contains(frameHeight)) {
String format = "Rejecting %s, reason: supported heights %s does not contain %d";
- Log.v(TAG, String.format(format, name, supportedHeights.toString(), frameHeight));
+ Log.v(TAG, format, name, supportedHeights.toString(), frameHeight);
continue;
}
}
} else {
if (frameWidth != 0 && !supportedWidths.contains(frameWidth)) {
String format = "Rejecting %s, reason: supported widths %s does not contain %d";
- Log.v(TAG, String.format(format, name, supportedWidths.toString(), frameWidth));
+ Log.v(TAG, format, name, supportedWidths.toString(), frameWidth);
continue;
}
if (frameHeight != 0 && !supportedHeights.contains(frameHeight)) {
String format = "Rejecting %s, reason: supported heights %s does not contain %d";
- Log.v(TAG, String.format(format, name, supportedHeights.toString(), frameHeight));
+ Log.v(TAG, format, name, supportedHeights.toString(), frameHeight);
continue;
}
}
@@ -684,7 +694,7 @@
Range<Integer> bitrates = videoCapabilities.getBitrateRange();
if (bitrate != 0 && !bitrates.contains(bitrate)) {
String format = "Rejecting %s, reason: bitrate range %s does not contain %d";
- Log.v(TAG, String.format(format, name, bitrates.toString(), bitrate));
+ Log.v(TAG, format, name, bitrates.toString(), bitrate);
continue;
}
@@ -694,14 +704,14 @@
if (frameHeight != 0 && frameWidth != 0) {
if (!videoCapabilities.areSizeAndRateSupported(frameWidth, frameHeight, fps)) {
String format = "Rejecting %s, reason: supported frame rates %s does not contain %d";
- Log.v(TAG, String.format(format, name, supportedFrameRates.toString(), fps));
+ Log.v(TAG, format, name, supportedFrameRates.toString(), fps);
continue;
}
} else {
// At least one of frameHeight or frameWidth is 0
if (!supportedFrameRates.contains(fps)) {
String format = "Rejecting %s, reason: supported frame rates %s does not contain %d";
- Log.v(TAG, String.format(format, name, supportedFrameRates.toString(), fps));
+ Log.v(TAG, format, name, supportedFrameRates.toString(), fps);
continue;
}
}
@@ -709,7 +719,7 @@
} else {
if (fps != 0 && !supportedFrameRates.contains(fps)) {
String format = "Rejecting %s, reason: supported frame rates %s does not contain %d";
- Log.v(TAG, String.format(format, name, supportedFrameRates.toString(), fps));
+ Log.v(TAG, format, name, supportedFrameRates.toString(), fps);
continue;
}
}
@@ -855,6 +865,7 @@
for (ResolutionAndFrameRate resolutionAndFrameRate : supported) {
frameRateAndResolutionString +=
String.format(
+ Locale.US,
"[%d x %d, %.3f fps], ",
resolutionAndFrameRate.width,
resolutionAndFrameRate.height,
@@ -878,6 +889,7 @@
String name = info.getName();
decoderDumpString +=
String.format(
+ Locale.US,
"name: %s (%s, %s): ",
name,
supportedType,
@@ -897,6 +909,7 @@
getSupportedResolutionsAndFrameRates(videoCapabilities, isHdrCapable);
decoderDumpString +=
String.format(
+ Locale.US,
"\n\t\t"
+ "widths: %s, "
+ "heights: %s, "
@@ -923,6 +936,7 @@
|| isTunneledPlaybackSupported) {
decoderDumpString +=
String.format(
+ Locale.US,
"(%s%s%s",
isAdaptivePlaybackSupported ? "AdaptivePlayback, " : "",
isSecurePlaybackSupported ? "SecurePlayback, " : "",
@@ -938,13 +952,12 @@
}
Log.v(
TAG,
- String.format(
- " \n"
- + "==================================================\n"
- + "Full list of decoder features: [AdaptivePlayback, SecurePlayback,"
- + " TunneledPlayback]\n"
- + "Unsupported features for each codec are not listed\n"
- + decoderDumpString
- + "=================================================="));
+ " \n"
+ + "==================================================\n"
+ + "Full list of decoder features: [AdaptivePlayback, SecurePlayback,"
+ + " TunneledPlayback]\n"
+ + "Unsupported features for each codec are not listed\n"
+ + decoderDumpString
+ + "==================================================");
}
}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java b/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java
index 29a2f80..b96bb87 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java
@@ -16,6 +16,8 @@
import android.app.Activity;
import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
import android.util.DisplayMetrics;
import android.util.Size;
import android.util.SizeF;
@@ -139,4 +141,40 @@
private static DisplayMetrics getDisplayMetrics() {
return cachedDisplayMetrics;
}
+
+ private static DisplayListener displayerListener =
+ new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ nativeOnDisplayChanged();
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ nativeOnDisplayChanged();
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ nativeOnDisplayChanged();
+ }
+ };
+
+ private static boolean displayerListenerAdded = false;
+
+ public static void addDisplayListener(Context context) {
+ if (displayerListenerAdded) {
+ return;
+ }
+
+ DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ displayManager.registerDisplayListener(displayerListener, null);
+ displayerListenerAdded = true;
+
+ // Call nativeOnDisplayChanged() to reload supported hdr types here after a default
+ // Display created.
+ nativeOnDisplayChanged();
+ }
+
+ private static native void nativeOnDisplayChanged();
}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/util/Log.java b/starboard/android/apk/app/src/main/java/dev/cobalt/util/Log.java
index 0a75c79..cca7c02 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/util/Log.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/util/Log.java
@@ -15,6 +15,7 @@
package dev.cobalt.util;
import java.lang.reflect.Method;
+import java.util.Locale;
/**
* Logging wrapper to allow for better control of Proguard log stripping. Many dependent
@@ -59,9 +60,27 @@
}
}
- private static int logWithMethod(Method logMethod, String tag, String msg, Throwable tr) {
+ private static Throwable getThrowableToLog(Object[] args) {
+ if (args == null || args.length == 0) return null;
+ Object lastArg = args[args.length - 1];
+ if (!(lastArg instanceof Throwable)) return null;
+ return (Throwable) lastArg;
+ }
+
+ /** Returns a formatted log message, using the supplied format and arguments. */
+ private static String formatLog(String messageTemplate, Throwable tr, Object... params) {
+ if ((params != null) && ((tr == null && params.length > 0) || params.length > 1)) {
+ messageTemplate = String.format(Locale.US, messageTemplate, params);
+ }
+ return messageTemplate;
+ }
+
+ private static int logWithMethod(
+ Method logMethod, String tag, String messageTemplate, Object... args) {
try {
if (logMethod != null) {
+ Throwable tr = getThrowableToLog(args);
+ String msg = formatLog(messageTemplate, tr, args);
return (int) logMethod.invoke(null, tag, msg, tr);
}
} catch (Throwable e) {
@@ -70,47 +89,27 @@
return 0;
}
- public static int v(String tag, String msg) {
- return logWithMethod(logV, tag, msg, null);
+ public static int v(String tag, String messageTemplate, Object... args) {
+ return logWithMethod(logV, tag, messageTemplate, args);
}
- public static int v(String tag, String msg, Throwable tr) {
- return logWithMethod(logV, tag, msg, tr);
+ public static int d(String tag, String messageTemplate, Object... args) {
+ return logWithMethod(logD, tag, messageTemplate, args);
}
- public static int d(String tag, String msg) {
- return logWithMethod(logD, tag, msg, null);
+ public static int i(String tag, String messageTemplate, Object... args) {
+ return logWithMethod(logI, tag, messageTemplate, args);
}
- public static int d(String tag, String msg, Throwable tr) {
- return logWithMethod(logD, tag, msg, tr);
- }
-
- public static int i(String tag, String msg) {
- return logWithMethod(logI, tag, msg, null);
- }
-
- public static int i(String tag, String msg, Throwable tr) {
- return logWithMethod(logI, tag, msg, tr);
- }
-
- public static int w(String tag, String msg) {
- return logWithMethod(logW, tag, msg, null);
- }
-
- public static int w(String tag, String msg, Throwable tr) {
- return logWithMethod(logW, tag, msg, tr);
+ public static int w(String tag, String messageTemplate, Object... args) {
+ return logWithMethod(logW, tag, messageTemplate, args);
}
public static int w(String tag, Throwable tr) {
return logWithMethod(logW, tag, "", tr);
}
- public static int e(String tag, String msg) {
- return logWithMethod(logE, tag, msg, null);
- }
-
- public static int e(String tag, String msg, Throwable tr) {
- return logWithMethod(logE, tag, msg, tr);
+ public static int e(String tag, String messageTemplate, Object... args) {
+ return logWithMethod(logE, tag, messageTemplate, args);
}
}
diff --git a/starboard/android/shared/file_open.cc b/starboard/android/shared/file_open.cc
index 83d924a..0f398a4 100644
--- a/starboard/android/shared/file_open.cc
+++ b/starboard/android/shared/file_open.cc
@@ -30,7 +30,12 @@
// 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
@@ -67,8 +72,8 @@
bool* out_created,
SbFileError* out_error) {
if (!IsAndroidAssetPath(path)) {
- return ::starboard::shared::posix::impl::FileOpen(
- path, flags, out_created, out_error);
+ return ::starboard::shared::posix::impl::FileOpen(path, flags, out_created,
+ out_error);
}
// Assets are never created and are always read-only, whether it's actually an
diff --git a/starboard/android/shared/media_capabilities_cache.cc b/starboard/android/shared/media_capabilities_cache.cc
index 53360ea..e8090b5 100644
--- a/starboard/android/shared/media_capabilities_cache.cc
+++ b/starboard/android/shared/media_capabilities_cache.cc
@@ -20,12 +20,17 @@
#include "starboard/android/shared/media_common.h"
#include "starboard/common/log.h"
#include "starboard/once.h"
+#include "starboard/shared/starboard/media/key_system_supportability_cache.h"
+#include "starboard/shared/starboard/media/mime_supportability_cache.h"
namespace starboard {
namespace android {
namespace shared {
namespace {
+using ::starboard::shared::starboard::media::KeySystemSupportabilityCache;
+using ::starboard::shared::starboard::media::MimeSupportabilityCache;
+
// https://developer.android.com/reference/android/view/Display.HdrCapabilities.html#HDR_TYPE_HDR10
const jint HDR_TYPE_DOLBY_VISION = 1;
const jint HDR_TYPE_HDR10 = 2;
@@ -79,6 +84,13 @@
JniEnvExt* env = JniEnvExt::Get();
jintArray j_supported_hdr_types = static_cast<jintArray>(
env->CallStarboardObjectMethodOrAbort("getSupportedHdrTypes", "()[I"));
+
+ if (!j_supported_hdr_types) {
+ // Failed to get supported hdr types.
+ SB_LOG(ERROR) << "Failed to load supported hdr types.";
+ return std::set<SbMediaTransferId>();
+ }
+
jsize length = env->GetArrayLength(j_supported_hdr_types);
jint* numbers = env->GetIntArrayElements(j_supported_hdr_types, 0);
for (int i = 0; i < length; i++) {
@@ -461,6 +473,31 @@
max_audio_output_channels_ = -1;
}
+void MediaCapabilitiesCache::ReloadSupportedHdrTypes() {
+ ScopedLock scoped_lock(mutex_);
+ if (!is_initialized_) {
+ LazyInitialize_Locked();
+ return;
+ }
+ supported_transfer_ids_ = GetSupportedHdrTypes();
+}
+
+void MediaCapabilitiesCache::ReloadAudioOutputChannels() {
+ ScopedLock scoped_lock(mutex_);
+ if (!is_initialized_) {
+ LazyInitialize_Locked();
+ return;
+ }
+ max_audio_output_channels_ =
+ ::starboard::android::shared::GetMaxAudioOutputChannels();
+}
+
+MediaCapabilitiesCache::MediaCapabilitiesCache() {
+ // Enable mime and key system caches.
+ MimeSupportabilityCache::GetInstance()->SetCacheEnabled(true);
+ KeySystemSupportabilityCache::GetInstance()->SetCacheEnabled(true);
+}
+
void MediaCapabilitiesCache::LazyInitialize_Locked() {
mutex_.DCheckAcquired();
@@ -526,6 +563,20 @@
}
}
+extern "C" SB_EXPORT_PLATFORM void
+Java_dev_cobalt_util_DisplayUtil_nativeOnDisplayChanged() {
+ SB_DLOG(INFO) << "Display device has changed.";
+ MediaCapabilitiesCache::GetInstance()->ReloadSupportedHdrTypes();
+ MimeSupportabilityCache::GetInstance()->ClearCachedMimeSupportabilities();
+}
+
+extern "C" SB_EXPORT_PLATFORM void
+Java_dev_cobalt_media_AudioOutputManager_nativeOnAudioDeviceChanged() {
+ SB_DLOG(INFO) << "Audio device has changed.";
+ MediaCapabilitiesCache::GetInstance()->ReloadAudioOutputChannels();
+ MimeSupportabilityCache::GetInstance()->ClearCachedMimeSupportabilities();
+}
+
} // namespace shared
} // namespace android
} // namespace starboard
diff --git a/starboard/android/shared/media_capabilities_cache.h b/starboard/android/shared/media_capabilities_cache.h
index 46da91a..21d735b 100644
--- a/starboard/android/shared/media_capabilities_cache.h
+++ b/starboard/android/shared/media_capabilities_cache.h
@@ -160,8 +160,11 @@
void SetCacheEnabled(bool enabled) { is_enabled_ = enabled; }
void ClearCache();
+ void ReloadSupportedHdrTypes();
+ void ReloadAudioOutputChannels();
+
private:
- MediaCapabilitiesCache() {}
+ MediaCapabilitiesCache();
~MediaCapabilitiesCache() {}
MediaCapabilitiesCache(const MediaCapabilitiesCache&) = delete;
@@ -183,7 +186,7 @@
std::map<std::string, AudioCodecCapabilities> audio_codec_capabilities_map_;
std::map<std::string, VideoCodecCapabilities> video_codec_capabilities_map_;
- std::atomic_bool is_enabled_{false};
+ std::atomic_bool is_enabled_{true};
bool is_initialized_ = false;
bool is_widevine_supported_ = false;
bool is_cbcs_supported_ = false;
diff --git a/starboard/android/shared/media_is_video_supported.cc b/starboard/android/shared/media_is_video_supported.cc
index 1f5c851..9b8fcf5 100644
--- a/starboard/android/shared/media_is_video_supported.cc
+++ b/starboard/android/shared/media_is_video_supported.cc
@@ -80,6 +80,14 @@
decoder_cache_ttl_ms =
mime_type->GetParamIntValue("decoder_cache_ttl_ms", -1);
+
+ // Disable MediaCapabilitiesCache if "disablecache" option presented.
+ if (!mime_type->ValidateBoolParameter("disablecache")) {
+ return false;
+ }
+ if (mime_type->GetParamBoolValue("disablecache", false)) {
+ MediaCapabilitiesCache::GetInstance()->SetCacheEnabled(false);
+ }
}
if (must_support_tunnel_mode && decode_to_texture_required) {
diff --git a/starboard/android/shared/system_get_path.cc b/starboard/android/shared/system_get_path.cc
index 2ca0815..5e4610a 100644
--- a/starboard/android/shared/system_get_path.cc
+++ b/starboard/android/shared/system_get_path.cc
@@ -25,10 +25,35 @@
#include "starboard/common/string.h"
#include "starboard/directory.h"
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/elf_loader/evergreen_config.h" // nogncheck
+#endif
+
using ::starboard::android::shared::g_app_assets_dir;
using ::starboard::android::shared::g_app_cache_dir;
+using ::starboard::android::shared::g_app_files_dir;
using ::starboard::android::shared::g_app_lib_dir;
+#if SB_IS(EVERGREEN_COMPATIBLE)
+bool GetEvergreenContentPathOverride(char* out_path, int path_size) {
+ const starboard::elf_loader::EvergreenConfig* evergreen_config =
+ starboard::elf_loader::EvergreenConfig::GetInstance();
+ if (!evergreen_config) {
+ return true;
+ }
+ if (evergreen_config->content_path_.empty()) {
+ return true;
+ }
+
+ if (starboard::strlcpy(out_path, evergreen_config->content_path_.c_str(),
+ path_size) >= path_size) {
+ return false;
+ }
+
+ return true;
+}
+#endif
+
bool SbSystemGetPath(SbSystemPathId path_id, char* out_path, int path_size) {
if (!out_path || !path_size) {
return false;
@@ -43,9 +68,25 @@
if (starboard::strlcat(path, g_app_assets_dir, kPathSize) >= kPathSize) {
return false;
}
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+ if (!GetEvergreenContentPathOverride(path, kPathSize)) {
+ return false;
+ }
+#endif
break;
}
+ case kSbSystemPathStorageDirectory: {
+ if (starboard::strlcpy(path, g_app_files_dir, kPathSize) >= kPathSize) {
+ return false;
+ }
+ if (starboard::strlcat(path, "/storage", kPathSize) >= kPathSize) {
+ return false;
+ }
+ SbDirectoryCreate(path);
+ break;
+ }
case kSbSystemPathCacheDirectory: {
if (!SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize)) {
return false;
diff --git a/starboard/evergreen/shared/launcher.py b/starboard/evergreen/shared/launcher.py
index c8bf2d5..e6f331d 100644
--- a/starboard/evergreen/shared/launcher.py
+++ b/starboard/evergreen/shared/launcher.py
@@ -75,6 +75,8 @@
self.loader_out_directory = paths.BuildOutputDirectory(
self.loader_platform, self.loader_config)
+ self.use_compressed_library = kwargs.get('use_compressed_library')
+
# The relationship of loader platforms and configurations to evergreen
# platforms and configurations is many-to-many. We need a separate directory
# for each of them, i.e. linux-x64x11_debug__evergreen-x64_gold.
@@ -92,9 +94,18 @@
# Ensure the path, relative to the content of the ELF Loader, to the
# Evergreen target and its content are passed as command line switches.
+ library_path_param = '--evergreen_library=app/{}/lib/lib{}'.format(
+ self.target_name, self.target_name)
+ if self.use_compressed_library:
+ if self.target_name != 'cobalt':
+ raise ValueError(
+ '|use_compressed_library| only expected with |target_name| cobalt')
+ library_path_param += '.lz4'
+ else:
+ library_path_param += '.so'
+
target_command_line_params = [
- '--evergreen_library=app/{}/lib/lib{}.so'.format(
- self.target_name, self.target_name),
+ library_path_param,
'--evergreen_content=app/{}/content'.format(self.target_name)
]
@@ -171,6 +182,7 @@
self._StageTargetsAndContentsGyp()
def _StageTargetsAndContentsGnLinux(self):
+ """Stage targets and their contents for GN builds for Linux platforms."""
content_subdir = os.path.join('usr', 'share', 'cobalt')
# Copy loader content and binaries
@@ -199,13 +211,15 @@
target_content_dst = os.path.join(target_staging_dir, 'content')
shutil.copytree(target_content_src, target_content_dst)
- shlib_name = 'lib{}.so'.format(self.target_name)
+ shlib_name = 'lib{}'.format(self.target_name)
+ shlib_name += '.lz4' if self.use_compressed_library else '.so'
target_binary_src = os.path.join(target_install_path, 'lib', shlib_name)
target_binary_dst = os.path.join(target_staging_dir, 'lib', shlib_name)
os.makedirs(os.path.join(target_staging_dir, 'lib'))
shutil.copy(target_binary_src, target_binary_dst)
def _StageTargetsAndContentsGnRaspi(self):
+ """Stage targets and their contents for GN builds for Raspi platforms."""
# TODO(b/218889313): `content` is hardcoded on raspi and must be in the same
# directory as the binaries.
if 'raspi' in self.loader_platform:
@@ -251,13 +265,15 @@
target_content_dst = os.path.join(target_staging_dir, 'content')
shutil.copytree(target_content_src, target_content_dst)
- shlib_name = 'lib{}.so'.format(self.target_name)
+ shlib_name = 'lib{}'.format(self.target_name)
+ shlib_name += '.lz4' if self.use_compressed_library else '.so'
target_binary_src = os.path.join(target_install_path, 'lib', shlib_name)
target_binary_dst = os.path.join(target_staging_dir, 'lib', shlib_name)
os.makedirs(os.path.join(target_staging_dir, 'lib'))
shutil.copy(target_binary_src, target_binary_dst)
def _StageTargetsAndContentsGyp(self):
+ """Stage targets and their contents for GYP builds."""
# <outpath>/deploy/elf_loader_sandbox
staging_directory_loader = os.path.join(self.staging_directory, 'deploy',
self.loader_target)
diff --git a/starboard/evergreen/testing/README.md b/starboard/evergreen/testing/README.md
index bbd33fe..e401325 100644
--- a/starboard/evergreen/testing/README.md
+++ b/starboard/evergreen/testing/README.md
@@ -121,9 +121,9 @@
+-- content <-- loader content
+-- app
+-- cobalt
- +-- content <-- cobalt content
+ +-- content <-- cobalt content
+-- lib
- +-- libcobalt.so <-- cobalt binary
+ +-- libcobalt.{so,lz4} <-- cobalt binary
```
Note: This directory structure is the same as what would be generated by
diff --git a/starboard/evergreen/testing/linux/deploy_cobalt.sh b/starboard/evergreen/testing/linux/deploy_cobalt.sh
index 6dc6e59..a461fae 100755
--- a/starboard/evergreen/testing/linux/deploy_cobalt.sh
+++ b/starboard/evergreen/testing/linux/deploy_cobalt.sh
@@ -31,8 +31,8 @@
echo " Checking '${staging_dir}'"
- PATHS=("${staging_dir}/loader_app" \
- "${staging_dir}/content/app/cobalt/lib/libcobalt.so" \
+ PATHS=("${staging_dir}/loader_app" \
+ "${staging_dir}/content/app/cobalt/lib/libcobalt${SYSTEM_IMAGE_EXTENSION}" \
"${staging_dir}/content/app/cobalt/content/")
for file in "${PATHS[@]}"; do
diff --git a/starboard/evergreen/testing/raspi/deploy_cobalt.sh b/starboard/evergreen/testing/raspi/deploy_cobalt.sh
index 8fdbc22..9012f3c 100755
--- a/starboard/evergreen/testing/raspi/deploy_cobalt.sh
+++ b/starboard/evergreen/testing/raspi/deploy_cobalt.sh
@@ -31,8 +31,8 @@
echo " Checking '${staging_dir}'"
- PATHS=("${staging_dir}/loader_app" \
- "${staging_dir}/content/app/cobalt/lib/libcobalt.so" \
+ PATHS=("${staging_dir}/loader_app" \
+ "${staging_dir}/content/app/cobalt/lib/libcobalt${SYSTEM_IMAGE_EXTENSION}" \
"${staging_dir}/content/app/cobalt/content/")
for file in "${PATHS[@]}"; do
@@ -60,7 +60,7 @@
eval "${SSH} \"mkdir -p /home/pi/coeg/content/app/cobalt/lib\""
echo " Copying cobalt to system image directory"
- eval "${SCP} \"${staging_dir}/content/app/cobalt/lib/libcobalt.so pi@${RASPI_ADDR}:/home/pi/coeg/content/app/cobalt/lib/\""
+ eval "${SCP} \"${staging_dir}/content/app/cobalt/lib/libcobalt${SYSTEM_IMAGE_EXTENSION} pi@${RASPI_ADDR}:/home/pi/coeg/content/app/cobalt/lib/\""
echo " Copying content to system image directory"
eval "${SCP} \"-r ${staging_dir}/content/app/cobalt/content/ pi@${RASPI_ADDR}:/home/pi/coeg/content/app/cobalt/\""
diff --git a/starboard/evergreen/testing/run_all_tests.sh b/starboard/evergreen/testing/run_all_tests.sh
index efb9d0b..91c53fb 100755
--- a/starboard/evergreen/testing/run_all_tests.sh
+++ b/starboard/evergreen/testing/run_all_tests.sh
@@ -21,7 +21,9 @@
DIR="$(dirname "${0}")"
AUTH_METHOD="public-key"
-while getopts "d:a:" o; do
+USE_COMPRESSED_SYSTEM_IMAGE="false"
+SYSTEM_IMAGE_EXTENSION=".so"
+while getopts "d:a:c" o; do
case "${o}" in
d)
DEVICE_ID=${OPTARG}
@@ -29,6 +31,10 @@
a)
AUTH_METHOD=${OPTARG}
;;
+ c)
+ USE_COMPRESSED_SYSTEM_IMAGE="true"
+ SYSTEM_IMAGE_EXTENSION=".lz4"
+ ;;
esac
done
shift $((OPTIND-1))
@@ -40,8 +46,14 @@
source $DIR/setup.sh
-# Find all of the test files within the 'test' subdirectory.
-TESTS=($(eval "find ${DIR}/tests -maxdepth 1 -name '*_test.sh'"))
+if [[ "${USE_COMPRESSED_SYSTEM_IMAGE}" == "true" ]]; then
+ # It would be valid to run all test cases using a compressed system image but
+ # is probably excessive. Instead, just the Evergreen Lite case is run to test
+ # that the compressed system image can be successfully loaded.
+ TESTS=($(eval "find ${DIR}/tests -maxdepth 1 -name 'evergreen_lite_test.sh'"))
+else
+ TESTS=($(eval "find ${DIR}/tests -maxdepth 1 -name '*_test.sh'"))
+fi
COUNT=0
RETRIED=()
@@ -145,7 +157,7 @@
clean_up
-log "info" " [==========] Finished."
+log "info" " [==========] Finished testing with USE_COMPRESSED_SYSTEM_IMAGE=${USE_COMPRESSED_SYSTEM_IMAGE}."
if [[ "${#FAILED[@]}" -eq 0 ]]; then
exit 0
diff --git a/starboard/evergreen/testing/setup.sh b/starboard/evergreen/testing/setup.sh
index 57bdbc4..393ea6d 100755
--- a/starboard/evergreen/testing/setup.sh
+++ b/starboard/evergreen/testing/setup.sh
@@ -24,7 +24,7 @@
source $DIR/pprint.sh
-log "info" " [==========] Preparing Cobalt."
+log "info" " [==========] Preparing to test with USE_COMPRESSED_SYSTEM_IMAGE=${USE_COMPRESSED_SYSTEM_IMAGE}."
if [[ -z ${1} ]]; then
log "error" "A platform must be provided"
diff --git a/starboard/shared/starboard/media/BUILD.gn b/starboard/shared/starboard/media/BUILD.gn
index 28ba606..7baaf99 100644
--- a/starboard/shared/starboard/media/BUILD.gn
+++ b/starboard/shared/starboard/media/BUILD.gn
@@ -17,8 +17,6 @@
sources = [
"//starboard/shared/starboard/media/avc_util.cc",
"//starboard/shared/starboard/media/avc_util.h",
- "//starboard/shared/starboard/media/bitrate_supportability_cache.cc",
- "//starboard/shared/starboard/media/bitrate_supportability_cache.h",
"//starboard/shared/starboard/media/codec_util.cc",
"//starboard/shared/starboard/media/codec_util.h",
"//starboard/shared/starboard/media/key_system_supportability_cache.cc",
diff --git a/starboard/shared/starboard/media/bitrate_supportability_cache.cc b/starboard/shared/starboard/media/bitrate_supportability_cache.cc
deleted file mode 100644
index 01c2917..0000000
--- a/starboard/shared/starboard/media/bitrate_supportability_cache.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// 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 "starboard/shared/starboard/media/bitrate_supportability_cache.h"
-
-#include <map>
-
-#include "starboard/common/log.h"
-#include "starboard/common/mutex.h"
-#include "starboard/log.h"
-#include "starboard/media.h"
-#include "starboard/once.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace media {
-
-namespace {
-
-template <typename T>
-class BitrateSupportabilityContainer {
- public:
- Supportability GetSupportability(T codec, int bitrate) {
- // Reject invalid parameters.
- if (bitrate < 0) {
- return kSupportabilityNotSupported;
- }
- // Bitrate 0 is always supported.
- if (bitrate == 0) {
- return kSupportabilitySupported;
- }
-
- ScopedLock scoped_lock(mutex_);
- auto iter = supported_bitrate_ranges_.find(codec);
- if (iter == supported_bitrate_ranges_.end()) {
- return kSupportabilityUnknown;
- }
- const Range& range = iter->second;
- if (bitrate < range.minimum || bitrate > range.maximum) {
- return kSupportabilityNotSupported;
- }
- return kSupportabilitySupported;
- }
-
- void SetSupportedBitrate(T codec, int min, int max) {
- SB_DCHECK(min >= 0 && max >= min);
-
- ScopedLock scoped_lock(mutex_);
- supported_bitrate_ranges_[codec] = Range(min, max);
- }
- void ClearContainer() {
- ScopedLock scoped_lock(mutex_);
- supported_bitrate_ranges_.clear();
- }
-
- private:
- struct Range {
- Range() : minimum(0), maximum(0) {}
- Range(int min, int max) : minimum(min), maximum(max) {}
- int minimum;
- int maximum;
- };
-
- Mutex mutex_;
- std::map<T, Range> supported_bitrate_ranges_;
-};
-
-template <typename T>
-SB_ONCE_INITIALIZE_FUNCTION(BitrateSupportabilityContainer<T>, GetContainer);
-
-} // namespace
-
-// static
-SB_ONCE_INITIALIZE_FUNCTION(BitrateSupportabilityCache,
- BitrateSupportabilityCache::GetInstance);
-
-Supportability BitrateSupportabilityCache::GetBitrateSupportability(
- const ParsedMimeInfo& mime_info) {
- SB_DCHECK(mime_info.is_valid());
-
- if (!is_enabled_) {
- return kSupportabilityUnknown;
- }
-
- Supportability audio_supportability = kSupportabilitySupported;
- if (mime_info.has_audio_info()) {
- audio_supportability = GetContainer<SbMediaAudioCodec>()->GetSupportability(
- mime_info.audio_info().codec, mime_info.audio_info().bitrate);
- }
-
- Supportability video_supportability = kSupportabilitySupported;
- if (mime_info.has_video_info()) {
- video_supportability = GetContainer<SbMediaVideoCodec>()->GetSupportability(
- mime_info.video_info().codec, mime_info.video_info().bitrate);
- }
-
- if (audio_supportability == kSupportabilityNotSupported ||
- video_supportability == kSupportabilityNotSupported) {
- return kSupportabilityNotSupported;
- }
- if (audio_supportability == kSupportabilityUnknown ||
- video_supportability == kSupportabilityUnknown) {
- return kSupportabilityUnknown;
- }
- return kSupportabilitySupported;
-}
-
-void BitrateSupportabilityCache::SetSupportedBitrate(SbMediaAudioCodec codec,
- int min,
- int max) {
- SB_DCHECK(min >= 0 && min <= max) << "Invalid bitrate range.";
-
- if (!is_enabled_) {
- return;
- }
- GetContainer<SbMediaAudioCodec>()->SetSupportedBitrate(codec, min, max);
-}
-
-void BitrateSupportabilityCache::SetSupportedBitrate(SbMediaVideoCodec codec,
- int min,
- int max) {
- SB_DCHECK(min >= 0 && min <= max) << "Invalid bitrate range.";
-
- if (!is_enabled_) {
- return;
- }
- GetContainer<SbMediaVideoCodec>()->SetSupportedBitrate(codec, min, max);
-}
-
-void BitrateSupportabilityCache::ClearCache() {
- GetContainer<SbMediaAudioCodec>()->ClearContainer();
- GetContainer<SbMediaVideoCodec>()->ClearContainer();
-}
-
-} // namespace media
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
diff --git a/starboard/shared/starboard/media/bitrate_supportability_cache.h b/starboard/shared/starboard/media/bitrate_supportability_cache.h
deleted file mode 100644
index fc1f553..0000000
--- a/starboard/shared/starboard/media/bitrate_supportability_cache.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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 STARBOARD_SHARED_STARBOARD_MEDIA_BITRATE_SUPPORTABILITY_CACHE_H_
-#define STARBOARD_SHARED_STARBOARD_MEDIA_BITRATE_SUPPORTABILITY_CACHE_H_
-
-#include "starboard/media.h"
-#include "starboard/shared/internal_only.h"
-#include "starboard/shared/starboard/media/mime_supportability_cache.h"
-#include "starboard/shared/starboard/media/parsed_mime_info.h"
-
-namespace starboard {
-namespace shared {
-namespace starboard {
-namespace media {
-
-// TODO: add unit tests for BitrateSupportabilityCache
-class BitrateSupportabilityCache {
- public:
- static BitrateSupportabilityCache* GetInstance();
-
- // When cache is not enabled, GetBitrateSupportability() will always return
- // kSupportabilityUnknown, and SetSupportedBitrate() will do nothing.
- bool IsEnabled() const { return is_enabled_; }
- void SetCacheEnabled(bool enabled) { is_enabled_ = enabled; }
-
- // Get bitrate supportability.
- Supportability GetBitrateSupportability(const ParsedMimeInfo& mime_info);
- // Set supported bitrate range for the |codec|. Note that if supported bitrate
- // range is not set, MimeSupportabilityCache::GetMimeSupportability() will
- // always return kSupportabilityUnknown.
- void SetSupportedBitrate(SbMediaAudioCodec codec, int min, int max);
- void SetSupportedBitrate(SbMediaVideoCodec codec, int min, int max);
-
- // Clear all cached supported bitrate ranges.
- void ClearCache();
-
- private:
- // Class can only be instanced via the singleton
- BitrateSupportabilityCache() {}
- ~BitrateSupportabilityCache() {}
-
- BitrateSupportabilityCache(const BitrateSupportabilityCache&) = delete;
- BitrateSupportabilityCache& operator=(const BitrateSupportabilityCache&) =
- delete;
-
- std::atomic_bool is_enabled_{false};
-};
-
-} // namespace media
-} // namespace starboard
-} // namespace shared
-} // namespace starboard
-
-#endif // STARBOARD_SHARED_STARBOARD_MEDIA_BITRATE_SUPPORTABILITY_CACHE_H_
diff --git a/starboard/shared/starboard/media/key_system_supportability_cache.h b/starboard/shared/starboard/media/key_system_supportability_cache.h
index a1f2f68..e9ad962 100644
--- a/starboard/shared/starboard/media/key_system_supportability_cache.h
+++ b/starboard/shared/starboard/media/key_system_supportability_cache.h
@@ -25,7 +25,14 @@
namespace starboard {
namespace media {
-// TODO: add unit tests for KeySystemSupportabilityCache
+// KeySystemSupportabilityCache caches the supportabilities of the combinations
+// of codec and key system.
+//
+// Note: anytime the platform key system capabilities have changed, please
+// call KeySystemSupportabilityCache::ClearCache() to clear the outdated
+// results.
+//
+// TODO: add unit tests for KeySystemSupportabilityCache.
class KeySystemSupportabilityCache {
public:
static KeySystemSupportabilityCache* GetInstance();
diff --git a/starboard/shared/starboard/media/mime_supportability_cache.cc b/starboard/shared/starboard/media/mime_supportability_cache.cc
index 34024f3..d52761c 100644
--- a/starboard/shared/starboard/media/mime_supportability_cache.cc
+++ b/starboard/shared/starboard/media/mime_supportability_cache.cc
@@ -14,6 +14,7 @@
#include "starboard/shared/starboard/media/mime_supportability_cache.h"
+#include <cstring>
#include <queue>
#include <sstream>
#include <string>
@@ -34,147 +35,116 @@
namespace {
-const size_t kDefaultCacheMaxSize = 2000;
-
-class MimeSupportabilityContainer {
- public:
- struct Entry {
- ParsedMimeInfo mime_info;
- Supportability supportability = kSupportabilityUnknown;
-
- explicit Entry(const std::string& mime_string) : mime_info(mime_string) {}
- };
-
- // GetParsedMimeAndSupportability() will first try to find a cached Entry for
- // the mime string. If no cached entry, a new Entry will be created with
- // parsed mime information and supportability kSupportabilityUnknown.
- // Ideally, we should decouple mime parsing and cache functionality, but
- // considering that the cache is only for internal use, to avoid repeated
- // lookups, we do parsing in this function.
- const Entry& GetParsedMimeAndSupportability(const std::string& mime_string) {
- ScopedLock scoped_lock(mutex_);
- auto entry_iter = entries_.find(mime_string);
- if (entry_iter != entries_.end()) {
- return entry_iter->second;
- }
-
- // We can't find anything from the cache. Parse mime string and cache
- // parsed MimeType and ParsedMimeInfo.
- auto insert_result = entries_.insert({mime_string, Entry(mime_string)});
-
- fifo_queue_.push(insert_result.first);
- while (fifo_queue_.size() > max_size_) {
- entries_.erase(fifo_queue_.front());
- fifo_queue_.pop();
- }
- SB_DCHECK(entries_.size() == fifo_queue_.size());
-
- return insert_result.first->second;
+// RemoveAttributeFromMime() will return a new mime string with the specified
+// attribute removed. If |attribute_string| is not null, the removed attribute
+// string will be returned via |attribute_string|. Following are some examples:
+// mime: "video/webm; codecs=\"vp9\"; bitrate=300000"
+// attribute_name: "bitrate"
+// return: "video/webm; codecs=\"vp9\""
+// attribute_string: "bitrate=300000"
+//
+// mime: "video/webm; codecs=\"vp9\"; bitrate=300000; eotf=bt709"
+// attribute_name: "bitrate"
+// return: "video/webm; codecs=\"vp9\"; eotf=bt709"
+// attribute_string: "bitrate=300000"
+//
+// mime: "bitrate=300000"
+// attribute_name: "bitrate"
+// return: ""
+// attribute_string: "bitrate=300000"
+std::string RemoveAttributeFromMime(const char* mime,
+ const char* attribute_name,
+ std::string* attribute_string) {
+ size_t name_length = strlen(attribute_name);
+ if (name_length == 0) {
+ return mime;
}
- // CacheSupportability() will find the target entry and update the
- // supportability. If there's no existing entry, it will parse the mime
- // string and create one.
- void CacheSupportability(const std::string& mime_string,
- Supportability supportability) {
- SB_DCHECK(!mime_string.empty());
- SB_DCHECK(supportability != kSupportabilityUnknown);
+ std::string mime_without_attribute;
+ const char* start_pos = strstr(mime, attribute_name);
+ while (start_pos) {
+ if ((start_pos == mime || start_pos[-1] == ';' || isspace(start_pos[-1])) &&
+ (start_pos[name_length] &&
+ (start_pos[name_length] == '=' || isspace(start_pos[name_length])))) {
+ break;
+ }
+ start_pos += name_length;
+ start_pos = strstr(start_pos, attribute_name);
+ }
- {
- ScopedLock scoped_lock(mutex_);
- auto entry_iter = entries_.find(mime_string);
- if (entry_iter != entries_.end()) {
- entry_iter->second.supportability = supportability;
- return;
+ if (!start_pos) {
+ // Target attribute is not found.
+ return std::string(mime);
+ }
+ const char* end_pos = strstr(start_pos, ";");
+ if (end_pos) {
+ // There may be other attribute after target attribute.
+ if (attribute_string) {
+ // Returned |attribute_string| will not have a trailing ';'.
+ attribute_string->assign(start_pos, end_pos - start_pos);
+ }
+
+ end_pos++;
+ // Remove leading spaces.
+ while (*end_pos && isspace(*end_pos)) {
+ end_pos++;
+ }
+ if (*end_pos) {
+ // Append the string after target attribute.
+ mime_without_attribute = std::string(mime, start_pos - mime);
+ mime_without_attribute.append(end_pos);
+ } else {
+ // Target attribute is the last one. Remove trailing spaces.
+ size_t mime_length = start_pos - mime;
+ while (mime_length > 0 && (isspace(mime[mime_length - 1]))) {
+ mime_length--;
}
+ mime_without_attribute = std::string(mime, mime_length);
}
-
- // Parse the mime string and create an entry.
- GetParsedMimeAndSupportability(mime_string);
- // Update the supportability again.
- CacheSupportability(mime_string, supportability);
- }
-
- // ClearCachedSupportabilities() will reset all cached |supportability|, but
- // will not remove parsed mime infos.
- void ClearCachedSupportabilities() {
- ScopedLock scoped_lock(mutex_);
- for (auto& iter : entries_) {
- iter.second.supportability = kSupportabilityUnknown;
+ } else {
+ // It can't find a trailing ';'. The target attribute must be the last one.
+ size_t mime_length = start_pos - mime;
+ // Remove trailing spaces.
+ while (mime_length > 0 && (isspace(mime[mime_length - 1]))) {
+ mime_length--;
+ }
+ // Remove the trailing ';'.
+ if (mime_length > 0 && mime[mime_length - 1] == ';') {
+ mime_length--;
+ }
+ mime_without_attribute = std::string(mime, mime_length);
+ if (attribute_string) {
+ *attribute_string = std::string(start_pos);
}
}
+ return mime_without_attribute;
+}
- void SetCacheMaxSize(int size) { max_size_ = size; }
+// Note that if bitrate parsing failed, |bitrate| will be set to -1.
+void StripAndParseBitrate(const char* mime,
+ std::string* mime_without_bitrate,
+ int* bitrate) {
+ SB_DCHECK(mime_without_bitrate);
+ SB_DCHECK(bitrate);
- void DumpCache() {
- ScopedLock scoped_lock(mutex_);
- std::stringstream ss;
- ss << "\n========Dumping MimeInfoCache========";
- for (const auto& entry_iter : entries_) {
- const ParsedMimeInfo& mime_info = entry_iter.second.mime_info;
- ss << "\nMime: " << entry_iter.first;
- ss << "\n ParsedMimeInfo:";
- ss << "\n MimeType : " << mime_info.mime_type().ToString();
- if (mime_info.is_valid()) {
- if (mime_info.has_audio_info()) {
- const ParsedMimeInfo::AudioCodecInfo& audio_info =
- mime_info.audio_info();
- ss << "\n Audio Codec : "
- << GetMediaAudioCodecName(audio_info.codec);
- ss << "\n Channels : " << audio_info.channels;
- }
- if (mime_info.has_video_info()) {
- const ParsedMimeInfo::VideoCodecInfo& video_info =
- mime_info.video_info();
- ss << "\n Video Codec : "
- << GetMediaVideoCodecName(video_info.codec);
- ss << "\n Profile : " << video_info.profile;
- ss << "\n Level : " << video_info.level;
- ss << "\n BitDepth : " << video_info.bit_depth;
- ss << "\n PrimaryId : "
- << GetMediaPrimaryIdName(video_info.primary_id);
- ss << "\n TransferId : "
- << GetMediaTransferIdName(video_info.transfer_id);
- ss << "\n MatrixId : "
- << GetMediaMatrixIdName(video_info.matrix_id);
- ss << "\n Width : " << video_info.frame_width;
- ss << "\n Height : " << video_info.frame_height;
- ss << "\n Fps : " << video_info.fps;
- ss << "\n DecodeToTexture : "
- << (video_info.decode_to_texture_required ? "true" : "false");
- }
- } else {
- ss << "\n Mime info is not valid";
- }
+ std::string bitrate_string;
+ *mime_without_bitrate =
+ RemoveAttributeFromMime(mime, "bitrate", &bitrate_string);
- ss << "\n Supportability: ";
- switch (entry_iter.second.supportability) {
- case kSupportabilityUnknown:
- ss << "Unknown";
- break;
- case kSupportabilitySupported:
- ss << "Supported";
- break;
- case kSupportabilityNotSupported:
- ss << "NotSupported";
- break;
- }
- }
- ss << "\n========End of Dumping========";
-
- SB_DLOG(INFO) << ss.str();
+ if (bitrate_string.empty()) {
+ *bitrate = 0;
+ return;
}
- private:
- typedef std::unordered_map<std::string, Entry> Entries;
-
- Mutex mutex_;
- Entries entries_;
- std::queue<Entries::iterator> fifo_queue_;
- std::atomic_int max_size_{kDefaultCacheMaxSize};
-};
-
-SB_ONCE_INITIALIZE_FUNCTION(MimeSupportabilityContainer, GetContainer);
+ MimeType::Param param;
+ if (!MimeType::ParseParamString(bitrate_string, ¶m) ||
+ param.type != MimeType::kParamTypeInteger) {
+ *bitrate = -1;
+ return;
+ }
+ SB_DCHECK(param.name == "bitrate");
+ *bitrate = param.int_value;
+}
} // namespace
@@ -182,43 +152,182 @@
SB_ONCE_INITIALIZE_FUNCTION(MimeSupportabilityCache,
MimeSupportabilityCache::GetInstance);
-void MimeSupportabilityCache::SetCacheMaxSize(size_t size) {
- GetContainer()->SetCacheMaxSize(size);
-}
-
Supportability MimeSupportabilityCache::GetMimeSupportability(
- const std::string& mime,
+ const char* mime,
ParsedMimeInfo* mime_info) {
- // Get cached parsed mime infos and supportability. If no cache is found,
- // MimeSupportabilityContainer will parse the mime string, and return a parsed
- // MimeType and its parsed audio/video information.
- const MimeSupportabilityContainer::Entry& entry =
- GetContainer()->GetParsedMimeAndSupportability(mime);
+ SB_DCHECK(mime);
+ SB_DCHECK(mime_info);
- if (mime_info) {
- // Return cached ParsedMimeInfo.
- *mime_info = entry.mime_info;
+ // Strip the bitrate from mime string and check it separately.
+ std::string mime_without_bitrate;
+ int bitrate;
+ StripAndParseBitrate(mime, &mime_without_bitrate, &bitrate);
+
+ if (bitrate < 0) {
+ // The mime string contains an invalid bitrate attribute. In that case, we
+ // return an invalid ParsedMimeInfo with kSbMediaSupportTypeNotSupported.
+ *mime_info = ParsedMimeInfo(mime);
+ return kSupportabilityNotSupported;
}
- return is_enabled_ ? entry.supportability : kSupportabilityUnknown;
+ ScopedLock scoped_lock(mutex_);
+ Entry& entry = GetEntry_Locked(mime_without_bitrate);
+
+ // Return cached ParsedMimeInfo with real bitrate.
+ *mime_info = entry.mime_info;
+ mime_info->SetBitrate(bitrate);
+
+ if (!mime_info->is_valid()) {
+ // Return kSupportabilityNotSupported if we can't get a valid
+ // ParsedMimeInfo.
+ return kSupportabilityNotSupported;
+ }
+
+ return is_enabled_ ? IsBitrateSupported_Locked(entry, bitrate)
+ : kSupportabilityUnknown;
}
void MimeSupportabilityCache::CacheMimeSupportability(
- const std::string& mime,
+ const char* mime,
Supportability supportability) {
+ SB_DCHECK(mime);
+ SB_DCHECK(supportability != kSupportabilityUnknown);
+
if (!is_enabled_) {
return;
}
- if (supportability == kSupportabilityUnknown) {
- SB_LOG(WARNING) << "Rejected unknown supportability.";
+
+ // Strip bitrate as what we do in GetMimeSupportability().
+ std::string mime_without_bitrate;
+ int bitrate;
+ StripAndParseBitrate(mime, &mime_without_bitrate, &bitrate);
+
+ if (bitrate < 0) {
+ // The mime string contains an invalid bitrate attribute.
return;
}
- GetContainer()->CacheSupportability(mime, supportability);
+ ScopedLock scoped_lock(mutex_);
+ Entry& entry = GetEntry_Locked(mime_without_bitrate);
+
+ if (entry.mime_info.is_valid()) {
+ UpdateBitrateSupportability_Locked(&entry, bitrate, supportability);
+ }
}
void MimeSupportabilityCache::ClearCachedMimeSupportabilities() {
- GetContainer()->ClearCachedSupportabilities();
+ ScopedLock scoped_lock(mutex_);
+ for (auto& iter : entries_) {
+ iter.second.max_supported_bitrate = -1;
+ iter.second.min_unsupported_bitrate = INT_MAX;
+ }
+}
+
+void MimeSupportabilityCache::DumpCache() {
+ ScopedLock scoped_lock(mutex_);
+ std::stringstream ss;
+ ss << "\n========Dumping MimeSupportabilityCache========";
+ for (const auto& entry_iter : entries_) {
+ const ParsedMimeInfo& mime_info = entry_iter.second.mime_info;
+ ss << "\nMime: " << entry_iter.first;
+ ss << "\n ParsedMimeInfo:";
+ ss << "\n MimeType : " << mime_info.mime_type().ToString();
+ if (mime_info.is_valid()) {
+ if (mime_info.has_audio_info()) {
+ const ParsedMimeInfo::AudioCodecInfo& audio_info =
+ mime_info.audio_info();
+ ss << "\n Audio Codec : "
+ << GetMediaAudioCodecName(audio_info.codec);
+ ss << "\n Channels : " << audio_info.channels;
+ }
+ if (mime_info.has_video_info()) {
+ const ParsedMimeInfo::VideoCodecInfo& video_info =
+ mime_info.video_info();
+ ss << "\n Video Codec : "
+ << GetMediaVideoCodecName(video_info.codec);
+ ss << "\n Profile : " << video_info.profile;
+ ss << "\n Level : " << video_info.level;
+ ss << "\n BitDepth : " << video_info.bit_depth;
+ ss << "\n PrimaryId : "
+ << GetMediaPrimaryIdName(video_info.primary_id);
+ ss << "\n TransferId : "
+ << GetMediaTransferIdName(video_info.transfer_id);
+ ss << "\n MatrixId : " << GetMediaMatrixIdName(video_info.matrix_id);
+ ss << "\n Width : " << video_info.frame_width;
+ ss << "\n Height : " << video_info.frame_height;
+ ss << "\n Fps : " << video_info.fps;
+ ss << "\n DecodeToTexture : "
+ << (video_info.decode_to_texture_required ? "true" : "false");
+ }
+ } else {
+ ss << "\n Mime info is not valid";
+ }
+
+ ss << "\n MaxSupportedBitrate: "
+ << entry_iter.second.max_supported_bitrate;
+ ss << "\n MinUnsupportedBitrate: "
+ << entry_iter.second.min_unsupported_bitrate;
+ }
+ ss << "\n========End of Dumping========";
+
+ SB_DLOG(INFO) << ss.str();
+}
+
+MimeSupportabilityCache::Entry& MimeSupportabilityCache::GetEntry_Locked(
+ const std::string& mime_string) {
+ auto entry_iter = entries_.find(mime_string);
+ if (entry_iter != entries_.end()) {
+ return entry_iter->second;
+ }
+
+ // We can't find anything from the cache. Parse mime string and cache
+ // parsed ParsedMimeInfo.
+ auto insert_result = entries_.insert({mime_string, Entry(mime_string)});
+
+ // Keep cached items not exceeding max size.
+ fifo_queue_.push(insert_result.first);
+ while (fifo_queue_.size() > max_size_) {
+ entries_.erase(fifo_queue_.front());
+ fifo_queue_.pop();
+ }
+ SB_DCHECK(entries_.size() == fifo_queue_.size());
+
+ return insert_result.first->second;
+}
+
+Supportability MimeSupportabilityCache::IsBitrateSupported_Locked(
+ const Entry& entry,
+ int bitrate) const {
+ SB_DCHECK(bitrate >= 0);
+
+ if (bitrate <= entry.max_supported_bitrate) {
+ return kSupportabilitySupported;
+ }
+ if (bitrate >= entry.min_unsupported_bitrate) {
+ return kSupportabilityNotSupported;
+ }
+ return kSupportabilityUnknown;
+}
+
+void MimeSupportabilityCache::UpdateBitrateSupportability_Locked(
+ Entry* entry,
+ int bitrate,
+ Supportability supportability) {
+ SB_DCHECK(entry);
+ SB_DCHECK(bitrate >= 0);
+ SB_DCHECK(supportability != kSupportabilityUnknown);
+
+ if (supportability == kSupportabilitySupported) {
+ SB_DCHECK(bitrate < entry->min_unsupported_bitrate);
+ if (bitrate > entry->max_supported_bitrate) {
+ entry->max_supported_bitrate = bitrate;
+ }
+ } else if (supportability == kSupportabilityNotSupported) {
+ SB_DCHECK(bitrate > entry->max_supported_bitrate);
+ if (bitrate < entry->min_unsupported_bitrate) {
+ entry->min_unsupported_bitrate = bitrate;
+ }
+ }
}
} // namespace media
diff --git a/starboard/shared/starboard/media/mime_supportability_cache.h b/starboard/shared/starboard/media/mime_supportability_cache.h
index 7eb3d4f..c1e560b 100644
--- a/starboard/shared/starboard/media/mime_supportability_cache.h
+++ b/starboard/shared/starboard/media/mime_supportability_cache.h
@@ -16,8 +16,11 @@
#define STARBOARD_SHARED_STARBOARD_MEDIA_MIME_SUPPORTABILITY_CACHE_H_
#include <atomic>
+#include <queue>
#include <string>
+#include <unordered_map>
+#include "starboard/common/mutex.h"
#include "starboard/shared/internal_only.h"
#include "starboard/shared/starboard/media/parsed_mime_info.h"
@@ -32,34 +35,68 @@
kSupportabilityNotSupported,
} Supportability;
-// TODO: add unit tests for MimeSupportabilityCache
+// MimeSupportabilityCache caches the supportabilities of raw mime strings.
+// To increase cache hit rate, it strips bitrate from the raw mime string, and
+// stores a supported bitrate range for mime strings with same other attributes.
+//
+// Note: MimeSupportabilityCache leverage the assumption that if the
+// platform can support a codec with bitrate of n, the codec should also support
+// any bitrate less than n. If that assumption is not true, please do NOT enable
+// MimeSupportabilityCache.
+//
+// Note: anytime the platform codec capabilities have changed, please call
+// MimeSupportabilityCache::ClearCachedMimeSupportabilities() to clear the
+// outdated results.
+//
+// TODO: add unit tests for MimeSupportabilityCache.
class MimeSupportabilityCache {
public:
static MimeSupportabilityCache* GetInstance();
- // When cache is not enabled, GetMimeSupportability() will always return
- // kSupportabilityUnknown, and CacheMimeSupportability() will do nothing,
- // but GetMimeSupportability() will still return parsed ParsedMimeInfo.
+ // When cache is not enabled, GetMimeSupportability() will return cached
+ // ParsedMimeInfo with kSupportabilityUnknown, and CacheMimeSupportability()
+ // will do nothing.
bool IsEnabled() const { return is_enabled_; }
void SetCacheEnabled(bool enabled) { is_enabled_ = enabled; }
- void SetCacheMaxSize(size_t size);
+ // Set the max number of the cached ParsedMimeInfos and its supportabilities.
+ void SetCacheMaxSize(size_t size) { max_size_ = size; }
- // Get cached mime supportability. The parsed mime information would be
- // returned via |mime_info| if it is not NULL.
- Supportability GetMimeSupportability(const std::string& mime,
+ // Get cached ParsedMimeInfo and mime supportability. If there's no cached
+ // ParsedMimeInfo, it will parse the mime string and cache the result.
+ // If we cannot get a valid ParsedMimeInfo from |mime|,
+ // GetMimeSupportability() will return kSupportabilityNotSupported with an
+ // invalid ParsedMimeInfo. Ideally, we should decouple mime parsing and
+ // supportability cache, but considering that the cache is only for internal
+ // use, to avoid repeated lookups, we do parsing in this function for now.
+ // Note that |mime| and |mime_info| cannot be null.
+ Supportability GetMimeSupportability(const char* mime,
ParsedMimeInfo* mime_info);
- // Cache mime supportability. If there's no cached parsed mime info and
- // supportability for the mime, the function will parse the mime first and
- // then update its supportability.
- void CacheMimeSupportability(const std::string& mime,
- Supportability supportability);
- // Clear all cached supportabilities. Note that it will not remove cached
- // parsed mime infos.
+ // Update cached supportability of the mime string.
+ // Note that if |supportability| is kSupportabilityUnknown or we cannot
+ // get a valid ParsedMimeInfo from |mime|, CacheMimeSupportability()
+ // will not cache the supportability.
+ void CacheMimeSupportability(const char* mime, Supportability supportability);
+
+ // Clear all cached supportabilities. But it will not remove cached
+ // ParsedMimeInfos, as for the same mime string, the parsed results should be
+ // always the same.
void ClearCachedMimeSupportabilities();
+ void DumpCache();
+
private:
+ const int kDefaultCacheMaxSize = 2000;
+
+ struct Entry {
+ ParsedMimeInfo mime_info;
+ int max_supported_bitrate = -1;
+ int min_unsupported_bitrate = INT_MAX;
+
+ explicit Entry(const std::string& mime) : mime_info(mime) {}
+ };
+
// Class can only be instanced via the singleton
MimeSupportabilityCache() {}
~MimeSupportabilityCache() {}
@@ -67,6 +104,19 @@
MimeSupportabilityCache(const MimeSupportabilityCache&) = delete;
MimeSupportabilityCache& operator=(const MimeSupportabilityCache&) = delete;
+ Entry& GetEntry_Locked(const std::string& mime_string);
+ Supportability IsBitrateSupported_Locked(const Entry& entry,
+ int bitrate) const;
+ void UpdateBitrateSupportability_Locked(Entry* entry,
+ int bitrate,
+ Supportability supportability);
+
+ typedef std::unordered_map<std::string, Entry> Entries;
+
+ Mutex mutex_;
+ Entries entries_;
+ std::queue<Entries::iterator> fifo_queue_;
+ std::atomic_int max_size_{kDefaultCacheMaxSize};
std::atomic_bool is_enabled_{false};
};
diff --git a/starboard/shared/starboard/media/mime_util.cc b/starboard/shared/starboard/media/mime_util.cc
index 518338f..6c53aa7 100644
--- a/starboard/shared/starboard/media/mime_util.cc
+++ b/starboard/shared/starboard/media/mime_util.cc
@@ -21,7 +21,6 @@
#include "starboard/common/log.h"
#include "starboard/common/media.h"
#include "starboard/log.h"
-#include "starboard/shared/starboard/media/bitrate_supportability_cache.h"
#include "starboard/shared/starboard/media/key_system_supportability_cache.h"
#include "starboard/shared/starboard/media/media_support_internal.h"
#include "starboard/shared/starboard/media/mime_supportability_cache.h"
@@ -35,91 +34,6 @@
namespace {
-// RemoveAttributeFromMime() will return a new mime string with the specified
-// attribute removed. If |attribute_string| is not null, the removed attribute
-// string will be returned via |attribute_string|. Following are some examples:
-// mime: "video/webm; codecs=\"vp9\"; bitrate=300000"
-// attribute_name: "bitrate"
-// return: "video/webm; codecs=\"vp9\""
-// attribute_string: "bitrate=300000"
-//
-// mime: "video/webm; codecs=\"vp9\"; bitrate=300000; eotf=bt709"
-// attribute_name: "bitrate"
-// return: "video/webm; codecs=\"vp9\"; eotf=bt709"
-// attribute_string: "bitrate=300000"
-//
-// mime: "bitrate=300000"
-// attribute_name: "bitrate"
-// return: ""
-// attribute_string: "bitrate=300000"
-std::string RemoveAttributeFromMime(const char* mime,
- const char* attribute_name,
- std::string* attribute_string) {
- size_t name_length = strlen(attribute_name);
- if (name_length == 0) {
- return mime;
- }
-
- std::string mime_without_attribute;
- const char* start_pos = strstr(mime, attribute_name);
- while (start_pos) {
- if ((start_pos == mime || start_pos[-1] == ';' || isspace(start_pos[-1])) &&
- (start_pos[name_length] &&
- (start_pos[name_length] == '=' || isspace(start_pos[name_length])))) {
- break;
- }
- start_pos += name_length;
- start_pos = strstr(start_pos, attribute_name);
- }
-
- if (!start_pos) {
- // Target attribute is not found.
- return std::string(mime);
- }
- const char* end_pos = strstr(start_pos, ";");
- if (end_pos) {
- // There may be other attribute after target attribute.
- if (attribute_string) {
- // Returned |attribute_string| will not have a trailing ';'.
- attribute_string->assign(start_pos, end_pos - start_pos);
- }
-
- end_pos++;
- // Remove leading spaces.
- while (*end_pos && isspace(*end_pos)) {
- end_pos++;
- }
- if (*end_pos) {
- // Append the string after target attribute.
- mime_without_attribute = std::string(mime, start_pos - mime);
- mime_without_attribute.append(end_pos);
- } else {
- // Target attribute is the last one. Remove trailing spaces.
- size_t mime_length = start_pos - mime;
- while (mime_length > 0 && (isspace(mime[mime_length - 1]))) {
- mime_length--;
- }
- mime_without_attribute = std::string(mime, mime_length);
- }
- } else {
- // It can't find a trailing ';'. The target attribute must be the last one.
- size_t mime_length = start_pos - mime;
- // Remove trailing spaces.
- while (mime_length > 0 && (isspace(mime[mime_length - 1]))) {
- mime_length--;
- }
- // Remove the trailing ';'.
- if (mime_length > 0 && mime[mime_length - 1] == ';') {
- mime_length--;
- }
- mime_without_attribute = std::string(mime, mime_length);
- if (attribute_string) {
- *attribute_string = std::string(start_pos);
- }
- }
- return mime_without_attribute;
-}
-
// Use SbMediaGetAudioConfiguration() to check if the platform can support
// |channels|.
bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels) {
@@ -255,22 +169,6 @@
video_info.decode_to_texture_required);
}
-bool ValidateAndParseBitrate(const std::string& bitrate_string, int* bitrate) {
- SB_DCHECK(!bitrate_string.empty());
-
- MimeType::Param param;
- if (!MimeType::ParseParamString(bitrate_string, ¶m)) {
- return false;
- }
- if (param.type != MimeType::kParamTypeInteger) {
- return false;
- }
- if (bitrate) {
- *bitrate = param.int_value;
- }
- return true;
-}
-
} // namespace
SbMediaSupportType CanPlayMimeAndKeySystem(const char* mime,
@@ -278,38 +176,19 @@
SB_DCHECK(mime);
SB_DCHECK(key_system);
- // Remove bitrate from mime string and read bitrate if presents.
- std::string bitrate_string;
- std::string mime_without_bitrate =
- RemoveAttributeFromMime(mime, "bitrate", &bitrate_string);
- int bitrate = 0;
- if (!bitrate_string.empty()) {
- if (!ValidateAndParseBitrate(bitrate_string, &bitrate)) {
- return kSbMediaSupportTypeNotSupported;
- }
- }
-
- if (bitrate < 0) {
- // Reject invalid bitrate.
- return kSbMediaSupportTypeNotSupported;
- }
-
- // Get cached parsed mime infos and supportability. If it is not found in the
- // cache, MimeSupportabilityCache would parse the mime string and return a
- // ParsedMimeInfo.
+ // Get cached ParsedMimeInfo with its supportability. If it is not found in
+ // the cache, MimeSupportabilityCache would parse the mime string and return
+ // the ParsedMimeInfo with kSupportabilityUnknown.
ParsedMimeInfo mime_info;
Supportability mime_supportability =
- MimeSupportabilityCache::GetInstance()->GetMimeSupportability(
- mime_without_bitrate, &mime_info);
- // Overwrite the bitrate.
- mime_info.SetBitrate(bitrate);
+ MimeSupportabilityCache::GetInstance()->GetMimeSupportability(mime,
+ &mime_info);
if (mime_info.disable_cache()) {
// Disable all caches if required.
mime_supportability = kSupportabilityUnknown;
MimeSupportabilityCache::GetInstance()->SetCacheEnabled(false);
KeySystemSupportabilityCache::GetInstance()->SetCacheEnabled(false);
- BitrateSupportabilityCache::GetInstance()->SetCacheEnabled(false);
}
// Reject mime if cached result is not supported.
@@ -317,10 +196,10 @@
return kSbMediaSupportTypeNotSupported;
}
- // Reject mime if parsed mime info is invalid.
- if (!mime_info.is_valid()) {
- return kSbMediaSupportTypeNotSupported;
- }
+ // MimeSupportabilityCache::GetMimeSupportability() returns
+ // kSupportabilityNotSupported if ParsedMimeInfo is not valid, so |mime_info|
+ // must be valid here.
+ SB_DCHECK(mime_info.is_valid());
const MimeType& mime_type = mime_info.mime_type();
const std::vector<std::string>& codecs = mime_type.GetCodecs();
@@ -379,26 +258,14 @@
}
}
- // Get cached bitrate supportability.
- Supportability bitrate_supportability =
- BitrateSupportabilityCache::GetInstance()->GetBitrateSupportability(
- mime_info);
-
- // Reject mime if bitrate is not supported.
- if (bitrate_supportability == kSupportabilityNotSupported) {
- return kSbMediaSupportTypeNotSupported;
- }
-
- // Return supported if mime and bitrate are all supported.
- if (mime_supportability == kSupportabilitySupported &&
- bitrate_supportability == kSupportabilitySupported) {
+ // At this point, |key_system| is supported. Return supported here if
+ // mime is also supported. Otherwise, |mime_supportability| must be unknown.
+ if (mime_supportability == kSupportabilitySupported) {
return kSbMediaSupportTypeProbably;
}
+ SB_DCHECK(mime_supportability == kSupportabilityUnknown);
- // At this point, either mime or bitrate supportability must be unknown.
- // Call platform functions to check if they are supported.
- SB_DCHECK(mime_supportability == kSupportabilityUnknown ||
- bitrate_supportability == kSupportabilityUnknown);
+ // Call platform functions to check if it's supported.
if (mime_info.has_audio_info() && !IsSupportedAudioCodec(mime_info)) {
mime_supportability = kSupportabilityNotSupported;
} else if (mime_info.has_video_info() && !IsSupportedVideoCodec(mime_info)) {
@@ -407,13 +274,10 @@
mime_supportability = kSupportabilitySupported;
}
- // Cache mime supportability when bitrate supportability is known.
- if (bitrate_supportability == kSupportabilitySupported) {
- MimeSupportabilityCache::GetInstance()->CacheMimeSupportability(
- mime_without_bitrate, mime_supportability);
- }
+ // Cache mime supportability.
+ MimeSupportabilityCache::GetInstance()->CacheMimeSupportability(
+ mime, mime_supportability);
- SB_DCHECK(mime_supportability != kSupportabilityUnknown);
return mime_supportability == kSupportabilitySupported
? kSbMediaSupportTypeProbably
: kSbMediaSupportTypeNotSupported;
diff --git a/starboard/shared/starboard/player/player_create.cc b/starboard/shared/starboard/player/player_create.cc
index 16bc57f..7ffb717 100644
--- a/starboard/shared/starboard/player/player_create.cc
+++ b/starboard/shared/starboard/player/player_create.cc
@@ -138,8 +138,9 @@
const int64_t kDefaultBitRate = 0;
if (audio_codec != kSbMediaAudioCodecNone) {
const MimeType audio_mime_type(audio_mime);
- if (!SbMediaIsAudioSupported(audio_codec, &audio_mime_type,
- kDefaultBitRate)) {
+ if (!SbMediaIsAudioSupported(
+ audio_codec, strlen(audio_mime) > 0 ? &audio_mime_type : nullptr,
+ kDefaultBitRate)) {
SB_LOG(ERROR) << "Unsupported audio codec "
<< starboard::GetMediaAudioCodecName(audio_codec) << ".";
player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
@@ -160,11 +161,11 @@
if (video_codec != kSbMediaVideoCodecNone) {
const MimeType video_mime_type(video_mime);
if (!SbMediaIsVideoSupported(
- video_codec, &video_mime_type, kDefaultProfile, kDefaultLevel,
- kDefaultColorDepth, kSbMediaPrimaryIdUnspecified,
- kSbMediaTransferIdUnspecified, kSbMediaMatrixIdUnspecified,
- kDefaultFrameWidth, kDefaultFrameHeight, kDefaultBitRate,
- kDefaultFrameRate,
+ video_codec, strlen(video_mime) > 0 ? &video_mime_type : nullptr,
+ kDefaultProfile, kDefaultLevel, kDefaultColorDepth,
+ kSbMediaPrimaryIdUnspecified, kSbMediaTransferIdUnspecified,
+ kSbMediaMatrixIdUnspecified, kDefaultFrameWidth,
+ kDefaultFrameHeight, kDefaultBitRate, kDefaultFrameRate,
output_mode == kSbPlayerOutputModeDecodeToTexture)) {
SB_LOG(ERROR) << "Unsupported video codec "
<< starboard::GetMediaVideoCodecName(video_codec) << ".";