Import Cobalt 23.lts.6.1031897
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 860c540..1155938 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml
@@ -149,14 +149,6 @@ stages: [push] always_run: true pass_filenames: false - - id: run-py2-tests - name: Run Python 2 Tests - description: Run Python 2 unittests - entry: python precommit_hooks/run_python2_unittests.py - language: python - language_version: python2.7 - additional_dependencies: ['mock'] - types: [python] - id: osslint name: osslint entry: python precommit_hooks/osslint_wrapper.py
diff --git a/cobalt/browser/BUILD.gn b/cobalt/browser/BUILD.gn index 07a6de0..7dccc07 100644 --- a/cobalt/browser/BUILD.gn +++ b/cobalt/browser/BUILD.gn
@@ -158,6 +158,7 @@ "//cobalt/browser/memory_settings:browser_memory_settings", "//cobalt/browser/memory_tracker:memory_tracker_tool", "//cobalt/build:cobalt_build_id", + "//cobalt/build:cobalt_build_info", "//cobalt/cache", "//cobalt/configuration", "//cobalt/css_parser",
diff --git a/cobalt/browser/application.cc b/cobalt/browser/application.cc index fe4c098..7e541f0 100644 --- a/cobalt/browser/application.cc +++ b/cobalt/browser/application.cc
@@ -65,6 +65,7 @@ #include "cobalt/configuration/configuration.h" #include "cobalt/extension/crash_handler.h" #include "cobalt/extension/installation_manager.h" +#include "cobalt/h5vcc/h5vcc_crash_log.h" #include "cobalt/loader/image/image_decoder.h" #include "cobalt/math/size.h" #include "cobalt/script/javascript_engine.h" @@ -589,6 +590,26 @@ } } +void AddCrashLogApplicationState(base::ApplicationState state) { + std::string application_state = std::string(GetApplicationStateString(state)); + application_state.push_back('\0'); + + auto crash_handler_extension = + static_cast<const CobaltExtensionCrashHandlerApi*>( + SbSystemGetExtension(kCobaltExtensionCrashHandlerName)); + if (crash_handler_extension && crash_handler_extension->version >= 2) { + if (!crash_handler_extension->SetString("application_state", + application_state.c_str())) { + LOG(ERROR) << "Could not send application state to crash handler."; + } + return; + } + + // Crash handler is not supported, fallback to crash log dictionary. + h5vcc::CrashLogDictionary::GetInstance()->SetString("application_state", + application_state); +} + } // namespace // Helper stub to disable histogram tracking in StatisticsRecorder @@ -1198,6 +1219,7 @@ case kSbEventTypeStop: LOG(INFO) << "Got quit event."; if (watchdog) watchdog->UpdateState(base::kApplicationStateStopped); + AddCrashLogApplicationState(base::kApplicationStateStopped); Quit(); LOG(INFO) << "Finished quitting."; break; @@ -1316,6 +1338,7 @@ return; } if (watchdog) watchdog->UpdateState(browser_module_->GetApplicationState()); + AddCrashLogApplicationState(browser_module_->GetApplicationState()); } void Application::OnWindowSizeChangedEvent(const base::Event* event) {
diff --git a/cobalt/browser/web_module.cc b/cobalt/browser/web_module.cc index 3531cbf..f1158dd 100644 --- a/cobalt/browser/web_module.cc +++ b/cobalt/browser/web_module.cc
@@ -141,7 +141,21 @@ ~Impl(); #if defined(ENABLE_DEBUGGER) + void EnsureDebugModule( + debug::backend::DebuggerState* debugger_state = nullptr) { + if (!debug_overlay_) { + debug_overlay_.reset( + new debug::backend::RenderOverlay(render_tree_produced_callback_)); + } + if (!debug_module_) { + debug_module_.reset(new debug::backend::DebugModule( + &debugger_hooks_, web_context_->global_environment(), + debug_overlay_.get(), resource_provider_, window_, debugger_state)); + } + } + debug::backend::DebugDispatcher* debug_dispatcher() { + EnsureDebugModule(); DCHECK(debug_module_); return debug_module_->debug_dispatcher(); } @@ -229,7 +243,8 @@ void FreezeDebugger( std::unique_ptr<debug::backend::DebuggerState>* debugger_state) { - if (debugger_state) *debugger_state = debug_module_->Freeze(); + if (debugger_state && debug_module_) + *debugger_state = debug_module_->Freeze(); } #endif // defined(ENABLE_DEBUGGER) @@ -549,6 +564,7 @@ data.options.loader_thread_priority)); animated_image_tracker_.reset(new loader::image::AnimatedImageTracker( + web_context_->name().c_str(), data.options.animated_image_decode_thread_priority)); DCHECK_LE(0, data.options.image_cache_capacity); @@ -744,14 +760,9 @@ } #if defined(ENABLE_DEBUGGER) - debug_overlay_.reset( - new debug::backend::RenderOverlay(render_tree_produced_callback_)); - - debug_module_.reset(new debug::backend::DebugModule( - &debugger_hooks_, web_context_->global_environment(), - debug_overlay_.get(), resource_provider_, window_, - data.options.debugger_state)); -#endif // ENABLE_DEBUGGER + if (data.options.debugger_state) + EnsureDebugModule(data.options.debugger_state); +#endif report_unload_timing_info_callback_ = data.options.collect_unload_event_time_callback; @@ -982,7 +993,11 @@ last_render_tree_produced_time_)); #if defined(ENABLE_DEBUGGER) - debug_overlay_->OnRenderTreeProduced(layout_results_with_callback); + if (debug_overlay_) { + debug_overlay_->OnRenderTreeProduced(layout_results_with_callback); + } else { + render_tree_produced_callback_.Run(layout_results_with_callback); + } #else // ENABLE_DEBUGGER render_tree_produced_callback_.Run(layout_results_with_callback); #endif // ENABLE_DEBUGGER @@ -1003,6 +1018,7 @@ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); web_module_stat_tracker_->OnRenderTreeRasterized(produced_time, rasterized_time); + animated_image_tracker_->OnRenderTreeRasterized(); if (produced_time >= last_render_tree_produced_time_) { is_render_tree_rasterization_pending_ = false; } @@ -1075,7 +1091,7 @@ "\n Waiting for web debugger to connect " "\n-------------------------------------"; // This blocks until the web debugger connects. - debug_module_->debug_dispatcher()->SetPaused(true); + debug_dispatcher()->SetPaused(true); waiting_for_web_debugger_->store(false); } #endif // defined(ENABLE_DEBUGGER) @@ -1166,7 +1182,9 @@ #if defined(ENABLE_DEBUGGER) // The debug overlay may be holding onto a render tree, clear that out. - debug_overlay_->ClearInput(); + if (debug_overlay_) { + debug_overlay_->ClearInput(); + } #endif // Force garbage collection in |javascript_engine|.
diff --git a/cobalt/build/BUILD.gn b/cobalt/build/BUILD.gn index 6c6e1e8..7b6a002 100644 --- a/cobalt/build/BUILD.gn +++ b/cobalt/build/BUILD.gn
@@ -23,3 +23,9 @@ cobalt_version, ] } + +action("cobalt_build_info") { + script = "build_info.py" + outputs = [ "$root_gen_dir/build_info.json" ] + args = [ rebase_path(outputs[0], root_build_dir) ] +}
diff --git a/cobalt/build/build.id b/cobalt/build/build.id index 3d15dd7..20a1fde 100644 --- a/cobalt/build/build.id +++ b/cobalt/build/build.id
@@ -1 +1 @@ -1031871 \ No newline at end of file +1031897 \ No newline at end of file
diff --git a/cobalt/build/build_info.py b/cobalt/build/build_info.py new file mode 100755 index 0000000..52f4494 --- /dev/null +++ b/cobalt/build/build_info.py
@@ -0,0 +1,132 @@ +#!/usr/bin/env python +# Copyright 2023 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. +"""Generates a Cobalt Build Info json.""" + +import datetime +import json +import os +import re +import subprocess +import sys + +FILE_DIR = os.path.dirname(__file__) +COMMIT_COUNT_BUILD_ID_OFFSET = 1000000 + +_BUILD_ID_PATTERN = '^BUILD_NUMBER=([1-9][0-9]{6,})$' +_GIT_REV_PATTERN = '^GitOrigin-RevId: ([0-9a-f]{40})$' +_COBALT_VERSION_PATTERN = '^#define COBALT_VERSION "(.*)"$' + + +def get_build_id_and_git_rev_from_commits(cwd): + # Build id and git rev must come from the same commit. + output = subprocess.check_output( + ['git', 'log', '--grep', _BUILD_ID_PATTERN, '-1', '-E', '--pretty=%b'], + cwd=cwd).decode() + + # Gets build id. + compiled_build_id_pattern = re.compile(_BUILD_ID_PATTERN, flags=re.MULTILINE) + match_build_id = compiled_build_id_pattern.search(output) + if not match_build_id: + return None, None + build_id = match_build_id.group(1) + + # Gets git rev. + compiled_git_rev_pattern = re.compile(_GIT_REV_PATTERN, flags=re.MULTILINE) + match_git_rev = compiled_git_rev_pattern.search(output) + if not match_git_rev: + git_rev = '' + else: + git_rev = match_git_rev.group(1) + + return build_id, git_rev + + +def get_build_id_from_commit_count(cwd): + output = subprocess.check_output(['git', 'rev-list', '--count', 'HEAD'], + cwd=cwd) + build_id = int(output.strip().decode()) + COMMIT_COUNT_BUILD_ID_OFFSET + return str(build_id) + + +def _get_last_commit_with_format(placeholder, cwd): + output = subprocess.check_output( + ['git', 'log', '-1', f'--pretty=format:{placeholder}'], cwd=cwd) + return output.strip().decode() + + +def _get_hash_from_last_commit(cwd): + return _get_last_commit_with_format(r'%H', cwd=cwd) + + +def _get_author_from_last_commit(cwd): + return _get_last_commit_with_format(r'%an', cwd=cwd) + + +def _get_subject_from_last_commit(cwd): + return _get_last_commit_with_format(r'%s', cwd=cwd) + + +def _get_cobalt_version(): + version_header_path = os.path.join(os.path.dirname(FILE_DIR), 'version.h') + contents = '' + with open(version_header_path, 'r', encoding='utf-8') as f: + contents = f.read() + compiled_cobalt_version_pattern = re.compile( + _COBALT_VERSION_PATTERN, flags=re.MULTILINE) + return compiled_cobalt_version_pattern.search(contents).group(1) + + +def main(output_path, cwd=FILE_DIR): + """Writes a Cobalt build_info json file.""" + build_rev = _get_hash_from_last_commit(cwd=cwd) + build_id, git_rev = get_build_id_and_git_rev_from_commits(cwd=cwd) + if build_id is None: + build_id = get_build_id_from_commit_count(cwd=cwd) + git_rev = build_rev + cobalt_version = _get_cobalt_version() + build_time = datetime.datetime.now().ctime() + author = _get_author_from_last_commit(cwd=cwd) + commit = _get_subject_from_last_commit(cwd=cwd) + + info_json = { + 'build_id': build_id, + 'build_rev': build_rev, + 'git_rev': git_rev, + 'cobalt_version': cobalt_version, + 'build_time': build_time, + 'author': author, + 'commit': commit + } + + with open(output_path, 'w', encoding='utf-8') as f: + f.write(json.dumps(info_json, indent=4)) + + # Supports legacy build_info.txt. + output_text_path = os.path.join( + os.path.dirname(output_path), 'build_info.txt') + with open(output_text_path, 'w', encoding='utf-8') as f: + f.write(f'''\ +Build ID: {build_id} +Build Rev: {build_rev} +Git Rev: {git_rev} +Cobalt Version: {cobalt_version} +Build Time: {build_time} +Author: {author} +Commit: {commit} +''') + + +if __name__ == '__main__': + main(sys.argv[1])
diff --git a/cobalt/build/get_build_id.py b/cobalt/build/get_build_id.py index 9c19e4c..ba86bb3 100755 --- a/cobalt/build/get_build_id.py +++ b/cobalt/build/get_build_id.py
@@ -15,35 +15,10 @@ """Prints out the Cobalt Build ID.""" import os -import re -import subprocess +from cobalt.build import build_info from cobalt.build.build_number import GetOrGenerateNewBuildNumber -_FILE_DIR = os.path.dirname(__file__) -COMMIT_COUNT_BUILD_NUMBER_OFFSET = 1000000 - -# Matches numbers > 1000000. The pattern is basic so git log --grep is able to -# interpret it. -GIT_BUILD_NUMBER_PATTERN = r'[1-9]' + r'[0-9]' * 6 + r'[0-9]*' -BUILD_NUMBER_TAG_PATTERN = r'^BUILD_NUMBER={}$' - -# git log --grep can't handle capture groups. -BUILD_NUBER_PATTERN_WITH_CAPTURE = f'({GIT_BUILD_NUMBER_PATTERN})' - - -def get_build_number_from_commits(): - full_pattern = BUILD_NUMBER_TAG_PATTERN.format(GIT_BUILD_NUMBER_PATTERN) - output = subprocess.check_output( - ['git', 'log', '--grep', full_pattern, '-1', '--pretty=%b'], - cwd=_FILE_DIR).decode() - - full_pattern_with_capture = re.compile( - BUILD_NUMBER_TAG_PATTERN.format(BUILD_NUBER_PATTERN_WITH_CAPTURE), - flags=re.MULTILINE) - match = full_pattern_with_capture.search(output) - return match.group(1) if match else None - def get_build_number_from_server(): # Note $BUILD_ID_SERVER_URL will always be set in CI. @@ -57,24 +32,14 @@ return build_num -def get_build_number_from_commit_count(): - output = subprocess.check_output(['git', 'rev-list', '--count', 'HEAD'], - cwd=_FILE_DIR) - build_number = int(output.strip().decode('utf-8')) - return build_number + COMMIT_COUNT_BUILD_NUMBER_OFFSET - - -def main(): - build_number = get_build_number_from_commits() - +def main(cwd=build_info.FILE_DIR): + build_number, _ = build_info.get_build_id_and_git_rev_from_commits(cwd=cwd) if not build_number: build_number = get_build_number_from_server() - if not build_number: - build_number = get_build_number_from_commit_count() - - print(build_number) + build_number = build_info.get_build_id_from_commit_count(cwd=cwd) + return build_number if __name__ == '__main__': - main() + print(main())
diff --git a/cobalt/build/get_build_id_test.py b/cobalt/build/get_build_id_test.py new file mode 100644 index 0000000..26e7b2b --- /dev/null +++ b/cobalt/build/get_build_id_test.py
@@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# +# Copyright 2023 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. +# +"""Tests the get_build_id module.""" + +import os +import subprocess +import tempfile +import unittest + +from cobalt.build import build_info +from cobalt.build import get_build_id + +_TEST_BUILD_NUMBER = 1234 + build_info.COMMIT_COUNT_BUILD_ID_OFFSET + + +# TODO(b/282040638): fix and re-enabled this +@unittest.skipIf(os.name == 'nt', 'Broken on Windows') +class GetBuildIdTest(unittest.TestCase): + + def setUp(self): + self.test_dir = tempfile.TemporaryDirectory() # pylint: disable=consider-using-with + self.original_cwd = os.getcwd() + os.chdir(self.test_dir.name) + subprocess.check_call(['git', 'init']) + subprocess.check_call(['git', 'config', 'user.name', 'pytest']) + subprocess.check_call(['git', 'config', 'user.email', 'pytest@pytest.com']) + + def tearDown(self): + os.chdir(self.original_cwd) + self.test_dir.cleanup() + + def make_commit(self, message='Temporary commit'): + with tempfile.NamedTemporaryFile('w', dir=self.test_dir.name) as temp_file: + subprocess.check_call(['git', 'add', temp_file.name]) + subprocess.check_call(['git', 'commit', '-m', message]) + + def make_commit_with_build_number(self, build_number=_TEST_BUILD_NUMBER): + message = f'Subject line\n\nBUILD_NUMBER={build_number}' + self.make_commit(message) + + def testSanity(self): + self.make_commit() + head_rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']) + self.assertNotEqual(head_rev.strip().decode('utf-8'), '') + + def testGetBuildNumberFromCommitsSunnyDay(self): + self.make_commit_with_build_number() + build_number, _ = build_info.get_build_id_and_git_rev_from_commits( + cwd=self.test_dir.name) + self.assertEqual(int(build_number), _TEST_BUILD_NUMBER) + + def testGetBuildNumberFromCommitsSunnyDayGetMostRecent(self): + num_commits = 5 + for i in range(num_commits): + self.make_commit_with_build_number( + build_info.COMMIT_COUNT_BUILD_ID_OFFSET + i) + build_number, _ = build_info.get_build_id_and_git_rev_from_commits( + cwd=self.test_dir.name) + self.assertEqual( + int(build_number), + num_commits + build_info.COMMIT_COUNT_BUILD_ID_OFFSET - 1) + + def testGetBuildNumberFromCommitsRainyDayInvalidBuildNumber(self): + self.make_commit() + self.make_commit(f'BUILD_NUMBER={_TEST_BUILD_NUMBER}') + build_number, _ = build_info.get_build_id_and_git_rev_from_commits( + cwd=self.test_dir.name) + self.assertIsNone(build_number) + + def testGetBuildNumberFromCommitCountSunnyDay(self): + num_commits = 5 + for _ in range(num_commits): + self.make_commit() + build_number = build_info.get_build_id_from_commit_count( + cwd=self.test_dir.name) + self.assertEqual( + int(build_number), + num_commits + build_info.COMMIT_COUNT_BUILD_ID_OFFSET) + + def testCommitsOutrankCommitCount(self): + self.make_commit() + self.make_commit_with_build_number() + self.make_commit() + build_number = get_build_id.main(cwd=self.test_dir.name) + self.assertEqual(int(build_number), _TEST_BUILD_NUMBER) + + def testFallbackToCommitCount(self): + num_commits = 5 + for _ in range(num_commits): + self.make_commit() + build_number = get_build_id.main(cwd=self.test_dir.name) + self.assertEqual( + int(build_number), + num_commits + build_info.COMMIT_COUNT_BUILD_ID_OFFSET) + + +if __name__ == '__main__': + unittest.main()
diff --git a/cobalt/debug/BUILD.gn b/cobalt/debug/BUILD.gn index 91c5586..0bdf9b5 100644 --- a/cobalt/debug/BUILD.gn +++ b/cobalt/debug/BUILD.gn
@@ -17,6 +17,8 @@ sources = [ "backend/agent_base.cc", "backend/agent_base.h", + "backend/cobalt_agent.cc", + "backend/cobalt_agent.h", "backend/command_map.h", "backend/css_agent.cc", "backend/css_agent.h", @@ -90,6 +92,9 @@ "console/command_manager.cc", "console/command_manager.h", ] - deps = [ "//cobalt/base" ] + deps = [ + "//cobalt/base", + "//starboard:starboard_headers_only", + ] } }
diff --git a/cobalt/debug/backend/cobalt_agent.cc b/cobalt/debug/backend/cobalt_agent.cc new file mode 100644 index 0000000..63901c2 --- /dev/null +++ b/cobalt/debug/backend/cobalt_agent.cc
@@ -0,0 +1,85 @@ +// Copyright 2023 The Cobalt Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "cobalt/debug/backend/cobalt_agent.h" + +#include <set> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/values.h" +#include "cobalt/debug/console/command_manager.h" +#include "cobalt/debug/json_object.h" + +namespace cobalt { +namespace debug { +namespace backend { + +CobaltAgent::CobaltAgent(DebugDispatcher* dispatcher) + : AgentBase("Cobalt", dispatcher) { + commands_["getConsoleCommands"] = + base::Bind(&CobaltAgent::GetConsoleCommands, base::Unretained(this)); + commands_["sendConsoleCommand"] = + base::Bind(&CobaltAgent::SendConsoleCommand, base::Unretained(this)); + // dispatcher_->AddDomain(domain_, commands_.Bind()); +} + +void CobaltAgent::GetConsoleCommands(Command command) { + JSONObject response(new base::DictionaryValue()); + JSONList list(new base::ListValue()); + + console::ConsoleCommandManager* command_manager = + console::ConsoleCommandManager::GetInstance(); + DCHECK(command_manager); + if (command_manager) { + std::set<std::string> commands = command_manager->GetRegisteredCommands(); + for (auto& command_name : commands) { + JSONObject console_command(new base::DictionaryValue()); + console_command->SetString("command", command_name); + console_command->SetString("shortHelp", + command_manager->GetShortHelp(command_name)); + console_command->SetString("longHelp", + command_manager->GetLongHelp(command_name)); + list->Append(std::move(console_command)); + } + } + + JSONObject commands(new base::DictionaryValue()); + commands->Set("commands", std::move(list)); + response->Set("result", std::move(commands)); + command.SendResponse(response); +} + +void CobaltAgent::SendConsoleCommand(Command command) { + JSONObject params = JSONParse(command.GetParams()); + if (params) { + std::string console_command; + if (params->GetString("command", &console_command)) { + std::string message; + params->GetString("message", &message); + console::ConsoleCommandManager* console_command_manager = + console::ConsoleCommandManager::GetInstance(); + DCHECK(console_command_manager); + console_command_manager->HandleCommand(console_command, message); + command.SendResponse(); + return; + } + } + command.SendErrorResponse(Command::kInvalidParams, "Missing command."); +} + +} // namespace backend +} // namespace debug +} // namespace cobalt
diff --git a/cobalt/debug/backend/cobalt_agent.h b/cobalt/debug/backend/cobalt_agent.h new file mode 100644 index 0000000..27f8613 --- /dev/null +++ b/cobalt/debug/backend/cobalt_agent.h
@@ -0,0 +1,47 @@ +// Copyright 2023 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_DEBUG_BACKEND_COBALT_AGENT_H_ +#define COBALT_DEBUG_BACKEND_COBALT_AGENT_H_ + +#include "cobalt/debug/backend/agent_base.h" +#include "cobalt/debug/backend/debug_dispatcher.h" +#include "cobalt/debug/command.h" +#include "cobalt/debug/json_object.h" +#include "cobalt/dom/window.h" + +namespace cobalt { +namespace debug { +namespace backend { + +// Implements a small part of the the "Runtime" inspector protocol domain with +// just enough to support console input. When using the V8 JavaScript engine, +// this class is not needed since the V8 inspector implements the Runtime domain +// for us. +// +// https://chromedevtools.github.io/devtools-protocol/tot/Runtime +class CobaltAgent : public AgentBase { + public: + explicit CobaltAgent(DebugDispatcher* dispatcher); + + private: + void GetConsoleCommands(Command command); + void SendConsoleCommand(Command command); +}; + +} // namespace backend +} // namespace debug +} // namespace cobalt + +#endif // COBALT_DEBUG_BACKEND_COBALT_AGENT_H_
diff --git a/cobalt/debug/backend/debug_module.cc b/cobalt/debug/backend/debug_module.cc index 56e8b26..ea767af 100644 --- a/cobalt/debug/backend/debug_module.cc +++ b/cobalt/debug/backend/debug_module.cc
@@ -23,6 +23,7 @@ namespace backend { namespace { +constexpr char kCobaltAgent[] = "CobaltAgent"; constexpr char kScriptDebuggerAgent[] = "ScriptDebuggerAgent"; constexpr char kLogAgent[] = "LogAgent"; constexpr char kDomAgent[] = "DomAgent"; @@ -140,6 +141,7 @@ // directly handle one or more protocol domains. script_debugger_agent_.reset( new ScriptDebuggerAgent(debug_dispatcher_.get(), script_debugger_.get())); + cobalt_agent_.reset(new CobaltAgent(debug_dispatcher_.get())); log_agent_.reset(new LogAgent(debug_dispatcher_.get())); dom_agent_.reset(new DOMAgent(debug_dispatcher_.get())); css_agent_ = WrapRefCounted(new CSSAgent(debug_dispatcher_.get())); @@ -178,6 +180,7 @@ base::DictionaryValue* agents_state = data.debugger_state == nullptr ? nullptr : data.debugger_state->agents_state.get(); + cobalt_agent_->Thaw(RemoveAgentState(kCobaltAgent, agents_state)); script_debugger_agent_->Thaw( RemoveAgentState(kScriptDebuggerAgent, agents_state)); log_agent_->Thaw(RemoveAgentState(kLogAgent, agents_state)); @@ -200,6 +203,7 @@ debugger_state->agents_state.reset(new base::DictionaryValue()); base::DictionaryValue* agents_state = debugger_state->agents_state.get(); + StoreAgentState(agents_state, kCobaltAgent, cobalt_agent_->Freeze()); StoreAgentState(agents_state, kScriptDebuggerAgent, script_debugger_agent_->Freeze()); StoreAgentState(agents_state, kLogAgent, log_agent_->Freeze());
diff --git a/cobalt/debug/backend/debug_module.h b/cobalt/debug/backend/debug_module.h index da1778a..4736968 100644 --- a/cobalt/debug/backend/debug_module.h +++ b/cobalt/debug/backend/debug_module.h
@@ -20,6 +20,7 @@ #include "base/message_loop/message_loop.h" #include "cobalt/base/debugger_hooks.h" +#include "cobalt/debug/backend/cobalt_agent.h" #include "cobalt/debug/backend/css_agent.h" #include "cobalt/debug/backend/debug_backend.h" #include "cobalt/debug/backend/debug_dispatcher.h" @@ -134,6 +135,7 @@ // Wrappable object providing native helpers for backend JavaScript. scoped_refptr<DebugBackend> debug_backend_; + std::unique_ptr<CobaltAgent> cobalt_agent_; std::unique_ptr<LogAgent> log_agent_; std::unique_ptr<DOMAgent> dom_agent_; scoped_refptr<CSSAgent> css_agent_;
diff --git a/cobalt/debug/backend/log_agent.cc b/cobalt/debug/backend/log_agent.cc index 9fc974a..593fb0a 100644 --- a/cobalt/debug/backend/log_agent.cc +++ b/cobalt/debug/backend/log_agent.cc
@@ -14,13 +14,22 @@ #include "cobalt/debug/backend/log_agent.h" +#include "base/bind.h" #include "base/logging.h" +#include "cobalt/debug/console/command_manager.h" namespace cobalt { namespace debug { namespace backend { namespace { +const char kDebugLogCommand[] = "debug_log"; +const char kDebugLogCommandShortHelp[] = + "Turns browser debug logging on or off."; +const char kDebugLogCommandLongHelp[] = + "When turned on, browser logs are sent in such a way that they are visible " + "in devtools."; + // Error levels: constexpr char kInfoLevel[] = "info"; constexpr char kWarningLevel[] = "warning"; @@ -42,7 +51,21 @@ } } // namespace -LogAgent::LogAgent(DebugDispatcher* dispatcher) : AgentBase("Log", dispatcher) { +void LogAgent::OnDebugLog(const std::string& message) { + SetDebugLog(console::ConsoleCommandManager::CommandHandler::IsOnEnableOrTrue( + message)); +} +void LogAgent::SetDebugLog(bool enable) { + event_method_ = domain_ + (enable ? ".entryAdded" : ".browserEntryAdded"); +} + +LogAgent::LogAgent(DebugDispatcher* dispatcher) + : AgentBase("Log", dispatcher), + debug_log_command_handler_( + kDebugLogCommand, + base::Bind(&LogAgent::OnDebugLog, base::Unretained(this)), + kDebugLogCommandShortHelp, kDebugLogCommandLongHelp) { + SetDebugLog(false); // Get log output while still making it available elsewhere. log_message_handler_callback_id_ = base::LogMessageHandler::GetInstance()->AddCallback( @@ -63,9 +86,10 @@ // except it only shows up in the debug console and not in remote devtools. // TODO: Flesh out the rest of LogEntry properties (source, timestamp) JSONObject params(new base::DictionaryValue()); + params->SetString("entry.source", "other"); params->SetString("entry.text", str); params->SetString("entry.level", GetLogLevelFromSeverity(severity)); - dispatcher_->SendEvent(domain_ + ".browserEntryAdded", params); + dispatcher_->SendEvent(event_method_, params); } // Don't suppress the log message.
diff --git a/cobalt/debug/backend/log_agent.h b/cobalt/debug/backend/log_agent.h index 97e6fe0..5a6fb50 100644 --- a/cobalt/debug/backend/log_agent.h +++ b/cobalt/debug/backend/log_agent.h
@@ -19,6 +19,7 @@ #include "cobalt/base/log_message_handler.h" #include "cobalt/debug/backend/agent_base.h" #include "cobalt/debug/backend/debug_dispatcher.h" +#include "cobalt/debug/console/command_manager.h" namespace cobalt { namespace debug { @@ -33,6 +34,9 @@ explicit LogAgent(DebugDispatcher* dispatcher); ~LogAgent(); + void OnDebugLog(const std::string& message); + void SetDebugLog(bool enable); + private: // Called by LogMessageHandler for each log message. // May be called from any thread. @@ -46,6 +50,11 @@ // The callback id of our recipient of log messages so we can unregister it. base::LogMessageHandler::CallbackId log_message_handler_callback_id_; + + std::string event_method_; + + debug::console::ConsoleCommandManager::CommandHandler + debug_log_command_handler_; }; } // namespace backend
diff --git a/cobalt/debug/console/command_manager.cc b/cobalt/debug/console/command_manager.cc index a25879a..3c30ae3 100644 --- a/cobalt/debug/console/command_manager.cc +++ b/cobalt/debug/console/command_manager.cc
@@ -15,6 +15,7 @@ #include "cobalt/debug/console/command_manager.h" #include "base/logging.h" +#include "starboard/string.h" namespace cobalt { namespace debug { @@ -45,6 +46,16 @@ manager->UnregisterCommandHandler(this); } +// Returns true if the message is 'on', 'enable', or 'true'. +// static +bool ConsoleCommandManager::CommandHandler::IsOnEnableOrTrue( + const std::string& message) { + return (SbStringCompareNoCase("on", message.c_str()) == 0) || + (SbStringCompareNoCase("enable", message.c_str()) == 0) || + (SbStringCompareNoCase("true", message.c_str()) == 0); +} + + void ConsoleCommandManager::HandleCommand(const std::string& command, const std::string& message) const { DCHECK_GT(command.length(), size_t(0));
diff --git a/cobalt/debug/console/command_manager.h b/cobalt/debug/console/command_manager.h index 66a5601..1ace46c 100644 --- a/cobalt/debug/console/command_manager.h +++ b/cobalt/debug/console/command_manager.h
@@ -61,6 +61,9 @@ const std::string& short_help() const { return short_help_; } const std::string& long_help() const { return long_help_; } + // Returns true if the message is 'on', 'enable', or 'true'. + static bool IsOnEnableOrTrue(const std::string& message); + private: std::string command_; CommandCallback callback_;
diff --git a/cobalt/demos/content/media-element-demo/src/components/player.ts b/cobalt/demos/content/media-element-demo/src/components/player.ts index 7bd9c17..60e3a5c 100644 --- a/cobalt/demos/content/media-element-demo/src/components/player.ts +++ b/cobalt/demos/content/media-element-demo/src/components/player.ts
@@ -25,6 +25,18 @@ /** The <video> element. */ private videoEl!: HTMLVideoElement; + /** The video SourceBuffer */ + private videoSourceBuffer!: SourceBuffer; + + /** The audio SourceBuffer */ + private audioSourceBuffer!: SourceBuffer; + + /** max(videoSourceBuffer.writeHead - videoEl.currentTime) */ + private maxVideoWriteHeadDistance!: number; + + /** max(audioSourceBuffer.writeHead - videoEl.currentTime) */ + private maxAudioWriteHeadDistance!: number; + /** The element displaying video download buffer info. */ private videoDownloadBufferInfo!: Element; @@ -53,6 +65,8 @@ super(props); this.videos = convertToMediaArray(props.video); this.audios = convertToMediaArray(props.audio); + this.maxVideoWriteHeadDistance = 0; + this.maxAudioWriteHeadDistance = 0; } /** @override */ @@ -93,12 +107,37 @@ } private renderVideoInfo() { + var h5vccAudioConnectors = ''; + try { + h5vccAudioConnectors = this.videoEl.h5vccAudioConnectors; + } catch (error) {} renderComponent( VideoInfo, { duration: this.videoEl.duration, currentTime: this.videoEl.currentTime, + audioConnectors: h5vccAudioConnectors, }, this.videoInfo); + if (this.videoSourceBuffer) { + this.maxVideoWriteHeadDistance = + Math.max(this.maxVideoWriteHeadDistance, + this.videoSourceBuffer.writeHead - this.videoEl.currentTime); + renderComponent( + SourceBufferInfo, + {name: 'Video', sourceBuffer: this.videoSourceBuffer, + maxWriteHeadDistance: this.maxVideoWriteHeadDistance}, + this.videoSourceBufferInfo); + } + if (this.audioSourceBuffer) { + this.maxAudioWriteHeadDistance = + Math.max(this.maxAudioWriteHeadDistance, + this.audioSourceBuffer.writeHead - this.videoEl.currentTime); + renderComponent( + SourceBufferInfo, + {name: 'Audio', sourceBuffer: this.audioSourceBuffer, + maxWriteHeadDistance: this.maxAudioWriteHeadDistance}, + this.audioSourceBufferInfo); + } } private async play() { @@ -149,7 +188,7 @@ /** * Plays all videos as adaptive videos. - * TODO: dynmaically calculate the source buffer MIME. + * TODO: dynamically calculate the source buffer MIME. */ private playAdaptiveVideo() { const ms = new MediaSource(); @@ -158,12 +197,7 @@ if (this.videos.length > 0) { const videoSourceBuffer = ms.addSourceBuffer('video/mp4; codecs="avc1.640028"'); - videoSourceBuffer.addEventListener('updateend', () => { - renderComponent( - SourceBufferInfo, - {name: 'Video', sourceBuffer: videoSourceBuffer}, - this.videoSourceBufferInfo); - }); + this.videoSourceBuffer = videoSourceBuffer; const downloadBuffer = new DownloadBuffer(this.videos); downloadBuffer.register((reportMap) => { renderComponent( @@ -176,12 +210,7 @@ if (this.audios.length > 0) { const audioSourceBuffer = ms.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"'); - audioSourceBuffer.addEventListener('updateend', () => { - renderComponent( - SourceBufferInfo, - {name: 'Audio', sourceBuffer: audioSourceBuffer}, - this.audioSourceBufferInfo); - }); + this.audioSourceBuffer = audioSourceBuffer; const downloadBuffer = new DownloadBuffer(this.audios); downloadBuffer.register( (reportMap) => {renderComponent(
diff --git a/cobalt/demos/content/media-element-demo/src/components/source_buffer_info.ts b/cobalt/demos/content/media-element-demo/src/components/source_buffer_info.ts index 92343f3..6747f66 100644 --- a/cobalt/demos/content/media-element-demo/src/components/source_buffer_info.ts +++ b/cobalt/demos/content/media-element-demo/src/components/source_buffer_info.ts
@@ -1,9 +1,12 @@ interface Props { sourceBuffer: SourceBuffer; name: string; + maxWriteHeadDistance: number; } /** A component that displays the source buffer info. */ -export function SourceBufferInfo({sourceBuffer, name}: Props) { - return `<div>${name} buffered: ${sourceBuffer.buffered.end(0)} sec</div>`; +export function SourceBufferInfo({sourceBuffer, name, maxWriteHeadDistance}: Props) { + return `<div>${name} buffered: ${sourceBuffer.buffered.end(0)} sec` + + `, writeHead: ${sourceBuffer.writeHead} sec` + + `, maxWriteHeadDistance: ${maxWriteHeadDistance} sec</div>`; }
diff --git a/cobalt/demos/content/media-element-demo/src/components/video_info.ts b/cobalt/demos/content/media-element-demo/src/components/video_info.ts index 3f8306f..68012c8 100644 --- a/cobalt/demos/content/media-element-demo/src/components/video_info.ts +++ b/cobalt/demos/content/media-element-demo/src/components/video_info.ts
@@ -1,9 +1,13 @@ interface Props { duration: number; currentTime: number; + audioConnectors: string; } /** A component that displays video info. */ -export function VideoInfo({duration, currentTime}: Props) { +export function VideoInfo({duration, currentTime, audioConnectors}: Props) { + if (audioConnectors) { + return `<div>${currentTime} / ${duration} / audioConnectors: ${audioConnectors}</div>`; + } return `<div>${currentTime} / ${duration}</div>`; }
diff --git a/cobalt/dom/html_media_element.cc b/cobalt/dom/html_media_element.cc index d516ac8..054209f 100644 --- a/cobalt/dom/html_media_element.cc +++ b/cobalt/dom/html_media_element.cc
@@ -19,6 +19,7 @@ #include <limits> #include <memory> #include <utility> +#include <vector> #include "base/bind.h" #include "base/compiler_specific.h" @@ -26,6 +27,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" +#include "base/strings/string_util.h" #include "base/trace_event/trace_event.h" #include "cobalt/base/instance_counter.h" #include "cobalt/base/tokens.h" @@ -36,6 +38,7 @@ #include "cobalt/dom/media_settings.h" #include "cobalt/dom/media_source.h" #include "cobalt/dom/media_source_ready_state.h" +#include "cobalt/extension/audio_write_ahead.h" #include "cobalt/loader/fetcher_factory.h" #include "cobalt/media/url_fetcher_data_source.h" #include "cobalt/media/web_media_player_factory.h" @@ -45,6 +48,7 @@ #include "cobalt/web/dom_exception.h" #include "cobalt/web/event.h" #include "cobalt/web/web_settings.h" +#include "starboard/system.h" #include "cobalt/dom/eme/media_encrypted_event.h" #include "cobalt/dom/eme/media_encrypted_event_init.h" @@ -641,6 +645,32 @@ event_queue_.Enqueue(event); } +std::string HTMLMediaElement::h5vcc_audio_connectors( + script::ExceptionState* exception_state) const { + static const CobaltExtensionConfigurableAudioWriteAheadApi* extension_api = + static_cast<const CobaltExtensionConfigurableAudioWriteAheadApi*>( + SbSystemGetExtension( + kCobaltExtensionConfigurableAudioWriteAheadName)); + if (extension_api) { + if (!player_) { + web::DOMException::Raise(web::DOMException::kInvalidStateErr, + exception_state); + return std::string(); + } + + DCHECK_EQ(extension_api->name, + std::string(kCobaltExtensionConfigurableAudioWriteAheadName)); + DCHECK_EQ(extension_api->version, 1u); + + std::vector<std::string> configs = player_->GetAudioConnectors(); + return base::JoinString(configs, ";"); + } + + web::DOMException::Raise(web::DOMException::kNotSupportedErr, + exception_state); + return std::string(); +} + void HTMLMediaElement::CreateMediaPlayer() { TRACE_EVENT0("cobalt::dom", "HTMLMediaElement::CreateMediaPlayer()"); LOG(INFO) << "Create media player.";
diff --git a/cobalt/dom/html_media_element.h b/cobalt/dom/html_media_element.h index 4276fd8..612e21c 100644 --- a/cobalt/dom/html_media_element.h +++ b/cobalt/dom/html_media_element.h
@@ -146,6 +146,12 @@ // function won't modify the target of the |event| passed in. void ScheduleEvent(const scoped_refptr<web::Event>& event); + // Returns semicolon separated names of audio connectors, like + // "hdmi;bluetooth". + // TODO(b/267678497): The current interface is tentative, to be refined. + std::string h5vcc_audio_connectors( + script::ExceptionState* exception_state) const; + // Set max video capabilities. void SetMaxVideoCapabilities(const std::string& max_video_capabilities, script::ExceptionState* exception_state);
diff --git a/cobalt/dom/html_media_element.idl b/cobalt/dom/html_media_element.idl index 4a1a509..401060a 100644 --- a/cobalt/dom/html_media_element.idl +++ b/cobalt/dom/html_media_element.idl
@@ -61,4 +61,10 @@ attribute boolean controls; [RaisesException] attribute double volume; attribute boolean muted; + + // non standard, semicolon separated names of audio connectors, like + // "hdmi;bluetooth". It raises `NotSupportedError` on apps doesn't support + // this feature, or `InvalidStateError` if there isn't an active playback. + // TODO(b/267678497): The current interface is tentative, to be refined. + [RaisesException] readonly attribute DOMString h5vccAudioConnectors; };
diff --git a/cobalt/dom/source_buffer.cc b/cobalt/dom/source_buffer.cc index 267677b..039303f 100644 --- a/cobalt/dom/source_buffer.cc +++ b/cobalt/dom/source_buffer.cc
@@ -453,6 +453,17 @@ track_defaults_ = track_defaults; } +double SourceBuffer::write_head(script::ExceptionState* exception_state) const { + if (media_source_ == NULL) { + web::DOMException::Raise(web::DOMException::kInvalidStateErr, + exception_state); + return 0.0; + } + + DCHECK(chunk_demuxer_); + return chunk_demuxer_->GetWriteHead(id_).InSecondsF(); +} + void SourceBuffer::OnRemovedFromMediaSource() { if (media_source_ == NULL) { return;
diff --git a/cobalt/dom/source_buffer.h b/cobalt/dom/source_buffer.h index c611177..76d0aa7 100644 --- a/cobalt/dom/source_buffer.h +++ b/cobalt/dom/source_buffer.h
@@ -134,6 +134,9 @@ // Custom, not in any spec. // + // Return the highest presentation timestamp written to SbPlayer. + double write_head(script::ExceptionState* exception_state) const; + void OnRemovedFromMediaSource(); double GetHighestPresentationTimestamp() const;
diff --git a/cobalt/dom/source_buffer.idl b/cobalt/dom/source_buffer.idl index 1fd8053..bb755e2 100644 --- a/cobalt/dom/source_buffer.idl +++ b/cobalt/dom/source_buffer.idl
@@ -38,4 +38,10 @@ // [RaisesException] void abort(); [RaisesException] void remove(double start, unrestricted double end); [RaisesException] attribute TrackDefaultList trackDefaults; + + // Non standard interface (b/267678497). + // Returns the highest presentation timestamp written to SbPlayer, raises + // `InvalidStateError` if the SourceBuffer object has been removed from the + // MediaSource object. + [RaisesException] readonly attribute double writeHead; };
diff --git a/cobalt/extension/audio_write_ahead.h b/cobalt/extension/audio_write_ahead.h new file mode 100644 index 0000000..985b6d3 --- /dev/null +++ b/cobalt/extension/audio_write_ahead.h
@@ -0,0 +1,75 @@ +// Copyright 2023 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_EXTENSION_AUDIO_WRITE_AHEAD_H_ +#define COBALT_EXTENSION_AUDIO_WRITE_AHEAD_H_ + +#include "starboard/media.h" +#include "starboard/player.h" +#include "starboard/time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define kCobaltExtensionConfigurableAudioWriteAheadName \ + "dev.cobalt.extension.ConfigurableAudioWriteAhead" + +#define kCobaltExtensionPlayerWriteDurationLocal (kSbTimeSecond / 2) +#define kCobaltExtensionPlayerWriteDurationRemote (kSbTimeSecond * 10) + +typedef enum CobaltExtensionMediaAudioConnector { + kCobaltExtensionMediaAudioConnectorUnknown, + + kCobaltExtensionMediaAudioConnectorAnalog, + kCobaltExtensionMediaAudioConnectorBluetooth, + kCobaltExtensionMediaAudioConnectorBuiltIn, + kCobaltExtensionMediaAudioConnectorHdmi, + kCobaltExtensionMediaAudioConnectorRemoteWired, + kCobaltExtensionMediaAudioConnectorRemoteWireless, + kCobaltExtensionMediaAudioConnectorRemoteOther, + kCobaltExtensionMediaAudioConnectorSpdif, + kCobaltExtensionMediaAudioConnectorUsb, +} CobaltExtensionMediaAudioConnector; + +typedef struct CobaltExtensionMediaAudioConfiguration { + int index; + CobaltExtensionMediaAudioConnector connector; + SbTime latency; + SbMediaAudioCodingType coding_type; + int number_of_channels; +} CobaltExtensionMediaAudioConfiguration; + +typedef struct CobaltExtensionConfigurableAudioWriteAheadApi { + // Name should be the string + // |kCobaltExtensionConfigurableAudioWriteAheadName|. This helps to validate + // that the extension API is correct. + const char* name; + + // This specifies the version of the API that is implemented. + uint32_t version; + + // The fields below this point were added in version 1 or later. + + bool (*PlayerGetAudioConfiguration)( + SbPlayer player, int index, + CobaltExtensionMediaAudioConfiguration* out_audio_configuration); + +} CobaltExtensionConfigurableAudioWriteAheadApi; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // COBALT_EXTENSION_AUDIO_WRITE_AHEAD_H_
diff --git a/cobalt/extension/extension_test.cc b/cobalt/extension/extension_test.cc index b6cf3bc..2dfdae9 100644 --- a/cobalt/extension/extension_test.cc +++ b/cobalt/extension/extension_test.cc
@@ -20,6 +20,7 @@ #include "cobalt/extension/font.h" #include "cobalt/extension/free_space.h" #include "cobalt/extension/graphics.h" +#include "cobalt/extension/ifa.h" #include "cobalt/extension/installation_manager.h" #include "cobalt/extension/javascript_cache.h" #include "cobalt/extension/media_session.h" @@ -382,5 +383,27 @@ EXPECT_EQ(second_extension_api, extension_api) << "Extension struct should be a singleton"; } + +TEST(ExtensionTest, Ifa) { + typedef StarboardExtensionIfaApi ExtensionApi; + const char* kExtensionName = kStarboardExtensionIfaName; + + const ExtensionApi* extension_api = + static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName)); + if (!extension_api) { + return; + } + + EXPECT_STREQ(extension_api->name, kExtensionName); + EXPECT_EQ(extension_api->version, 1u); + EXPECT_NE(extension_api->GetAdvertisingId, nullptr); + EXPECT_NE(extension_api->GetLimitAdTracking, nullptr); + + const ExtensionApi* second_extension_api = + static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName)); + EXPECT_EQ(second_extension_api, extension_api) + << "Extension struct should be a singleton"; +} + } // namespace extension } // namespace cobalt
diff --git a/cobalt/extension/ifa.h b/cobalt/extension/ifa.h new file mode 100644 index 0000000..a207049 --- /dev/null +++ b/cobalt/extension/ifa.h
@@ -0,0 +1,58 @@ +// Copyright 2023 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_EXTENSION_IFA_H_ +#define STARBOARD_EXTENSION_IFA_H_ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define kStarboardExtensionIfaName "dev.cobalt.extension.Ifa" + +typedef struct StarboardExtensionIfaApi { + // Name should be the string |kCobaltExtensionIfaName|. + // This helps to validate that the extension API is correct. + const char* name; + + // This specifies the version of the API that is implemented. + uint32_t version; + + // The fields below this point were added in version 1 or later. + + // Advertising ID or IFA, typically a 128-bit UUID + // Please see https://iabtechlab.com/OTT-IFA for details. + // Corresponds to 'ifa' field. Note: `ifa_type` field is not provided. + // In Starboard 14 this the value is retrieved through the system + // property `kSbSystemPropertyAdvertisingId` defined in + // `starboard/system.h`. + bool (*GetAdvertisingId)(char* out_value, int value_length); + + // Limit advertising tracking, treated as boolean. Set to nonzero to indicate + // a true value. Corresponds to 'lmt' field. + // In Starboard 14 this the value is retrieved through the system + // property `kSbSystemPropertyLimitAdTracking` defined in + // `starboard/system.h`. + + bool (*GetLimitAdTracking)(char* out_value, int value_length); + +} CobaltExtensionIfaApi; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // STARBOARD_EXTENSION_IFA_H_
diff --git a/cobalt/h5vcc/h5vcc_crash_log.cc b/cobalt/h5vcc/h5vcc_crash_log.cc index a240d92..4088a84 100644 --- a/cobalt/h5vcc/h5vcc_crash_log.cc +++ b/cobalt/h5vcc/h5vcc_crash_log.cc
@@ -31,70 +31,49 @@ namespace cobalt { namespace h5vcc { -// We keep a global mapping of all registered logs. When a crash occurs, we -// will iterate through the mapping in this dictionary to extract all -// logged entries and write them to the system's crash logger via Starboard. -class CrashLogDictionary { - public: - static CrashLogDictionary* GetInstance() { - return base::Singleton<CrashLogDictionary, base::DefaultSingletonTraits< - CrashLogDictionary> >::get(); - } +CrashLogDictionary* CrashLogDictionary::GetInstance() { + return base::Singleton<CrashLogDictionary, base::DefaultSingletonTraits< + CrashLogDictionary>>::get(); +} - void SetString(const std::string& key, const std::string& value) { - base::AutoLock lock(mutex_); - // While the lock prevents contention between other calls to SetString(), - // the atomics guard against OnCrash(), which doesn't acquire |mutex_|, from - // accessing the data at the same time. In the case that OnCrash() is - // being called, we give up and skip adding the data. - if (base::subtle::Acquire_CompareAndSwap(&accessing_log_data_, 0, 1) == 0) { - string_log_map_[key] = value; - base::subtle::Release_Store(&accessing_log_data_, 0); - } +void CrashLogDictionary::SetString(const std::string& key, + const std::string& value) { + base::AutoLock lock(mutex_); + // While the lock prevents contention between other calls to SetString(), + // the atomics guard against OnCrash(), which doesn't acquire |mutex_|, from + // accessing the data at the same time. In the case that OnCrash() is + // being called, we give up and skip adding the data. + if (base::subtle::Acquire_CompareAndSwap(&accessing_log_data_, 0, 1) == 0) { + string_log_map_[key] = value; + base::subtle::Release_Store(&accessing_log_data_, 0); } +} - private: - CrashLogDictionary() : accessing_log_data_(0) { +CrashLogDictionary::CrashLogDictionary() : accessing_log_data_(0) { #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT) - SbCoreDumpRegisterHandler(&CoreDumpHandler, this); + SbCoreDumpRegisterHandler(&CoreDumpHandler, this); #endif - } +} - static void CoreDumpHandler(void* context) { - CrashLogDictionary* crash_log_dictionary = - static_cast<CrashLogDictionary*>(context); - crash_log_dictionary->OnCrash(); - } +void CrashLogDictionary::CoreDumpHandler(void* context) { + CrashLogDictionary* crash_log_dictionary = + static_cast<CrashLogDictionary*>(context); + crash_log_dictionary->OnCrash(); +} - void OnCrash() { +void CrashLogDictionary::OnCrash() { #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT) - // Check that we're not already updating log data. If we are, we just - // give up and skip recording any crash data, but hopefully this is rare. - if (base::subtle::Acquire_CompareAndSwap(&accessing_log_data_, 0, 1) == 0) { - for (StringMap::const_iterator iter = string_log_map_.begin(); - iter != string_log_map_.end(); ++iter) { - SbCoreDumpLogString(iter->first.c_str(), iter->second.c_str()); - } - base::subtle::Release_Store(&accessing_log_data_, 0); + // Check that we're not already updating log data. If we are, we just + // give up and skip recording any crash data, but hopefully this is rare. + if (base::subtle::Acquire_CompareAndSwap(&accessing_log_data_, 0, 1) == 0) { + for (StringMap::const_iterator iter = string_log_map_.begin(); + iter != string_log_map_.end(); ++iter) { + SbCoreDumpLogString(iter->first.c_str(), iter->second.c_str()); } -#endif + base::subtle::Release_Store(&accessing_log_data_, 0); } - - friend struct base::DefaultSingletonTraits<CrashLogDictionary>; - - base::subtle::Atomic32 accessing_log_data_; - - // It is possible for multiple threads to call the H5VCC interface at the - // same time, and they will all forward to this global singleton, so we - // use this mutex to protect against concurrent access. - base::Lock mutex_; - - typedef std::map<std::string, std::string> StringMap; - // Keeps track of all string values to be logged when a crash occurs. - StringMap string_log_map_; - - DISALLOW_COPY_AND_ASSIGN(CrashLogDictionary); -}; +#endif +} bool H5vccCrashLog::SetString(const std::string& key, const std::string& value) {
diff --git a/cobalt/h5vcc/h5vcc_crash_log.h b/cobalt/h5vcc/h5vcc_crash_log.h index b85c0c5..bb20a48 100644 --- a/cobalt/h5vcc/h5vcc_crash_log.h +++ b/cobalt/h5vcc/h5vcc_crash_log.h
@@ -15,8 +15,10 @@ #ifndef COBALT_H5VCC_H5VCC_CRASH_LOG_H_ #define COBALT_H5VCC_H5VCC_CRASH_LOG_H_ +#include <map> #include <string> +#include "base/memory/singleton.h" #include "cobalt/h5vcc/h5vcc_crash_type.h" #include "cobalt/h5vcc/watchdog_replace.h" #include "cobalt/h5vcc/watchdog_state.h" @@ -26,6 +28,38 @@ namespace cobalt { namespace h5vcc { +// We keep a global mapping of all registered logs. When a crash occurs, we +// will iterate through the mapping in this dictionary to extract all +// logged entries and write them to the system's crash logger via Starboard. +class CrashLogDictionary { + public: + static CrashLogDictionary* GetInstance(); + + void SetString(const std::string& key, const std::string& value); + + private: + CrashLogDictionary(); + + static void CoreDumpHandler(void* context); + + void OnCrash(); + + friend struct base::DefaultSingletonTraits<CrashLogDictionary>; + + base::subtle::Atomic32 accessing_log_data_; + + // It is possible for multiple threads to call the H5VCC interface at the + // same time, and they will all forward to this global singleton, so we + // use this mutex to protect against concurrent access. + base::Lock mutex_; + + typedef std::map<std::string, std::string> StringMap; + // Keeps track of all string values to be logged when a crash occurs. + StringMap string_log_map_; + + DISALLOW_COPY_AND_ASSIGN(CrashLogDictionary); +}; + class H5vccCrashLog : public script::Wrappable { public: H5vccCrashLog() {}
diff --git a/cobalt/h5vcc/h5vcc_system.cc b/cobalt/h5vcc/h5vcc_system.cc index b28e941..ad4de95 100644 --- a/cobalt/h5vcc/h5vcc_system.cc +++ b/cobalt/h5vcc/h5vcc_system.cc
@@ -20,6 +20,10 @@ #include "cobalt_build_id.h" // NOLINT(build/include_subdir) #include "starboard/system.h" +#if SB_API_VERSION < 14 +#include "cobalt/extension/ifa.h" +#endif // SB_API_VERSION < 14 + namespace cobalt { namespace h5vcc { @@ -54,29 +58,58 @@ std::string H5vccSystem::advertising_id() const { std::string result; -#if SB_API_VERSION >= 14 const size_t kSystemPropertyMaxLength = 1024; char property[kSystemPropertyMaxLength] = {0}; +#if SB_API_VERSION >= 14 if (!SbSystemGetProperty(kSbSystemPropertyAdvertisingId, property, SB_ARRAY_SIZE_INT(property))) { DLOG(FATAL) << "Failed to get kSbSystemPropertyAdvertisingId."; } else { result = property; } +#else + static auto const* ifa_extension = + static_cast<const StarboardExtensionIfaApi*>( + SbSystemGetExtension(kStarboardExtensionIfaName)); + if (ifa_extension && + strcmp(ifa_extension->name, kStarboardExtensionIfaName) == 0 && + ifa_extension->version >= 1) { + if (!ifa_extension->GetAdvertisingId(property, + SB_ARRAY_SIZE_INT(property))) { + DLOG(FATAL) << "Failed to get AdvertisingId from IFA extension."; + } else { + result = property; + } + } #endif return result; } bool H5vccSystem::limit_ad_tracking() const { bool result = false; -#if SB_API_VERSION >= 14 const size_t kSystemPropertyMaxLength = 1024; char property[kSystemPropertyMaxLength] = {0}; +#if SB_API_VERSION >= 14 if (!SbSystemGetProperty(kSbSystemPropertyLimitAdTracking, property, SB_ARRAY_SIZE_INT(property))) { DLOG(FATAL) << "Failed to get kSbSystemPropertyAdvertisingId."; } else { result = std::atoi(property); } +#else + static auto const* ifa_extension = + static_cast<const StarboardExtensionIfaApi*>( + SbSystemGetExtension(kStarboardExtensionIfaName)); + + if (ifa_extension && + strcmp(ifa_extension->name, kStarboardExtensionIfaName) == 0 && + ifa_extension->version >= 1) { + if (!ifa_extension->GetLimitAdTracking(property, + SB_ARRAY_SIZE_INT(property))) { + DLOG(FATAL) << "Failed to get LimitAdTracking from IFA extension."; + } else { + result = std::atoi(property); + } + } #endif return result; }
diff --git a/cobalt/loader/image/animated_image_tracker.cc b/cobalt/loader/image/animated_image_tracker.cc index 98dadca..c7a3e73 100644 --- a/cobalt/loader/image/animated_image_tracker.cc +++ b/cobalt/loader/image/animated_image_tracker.cc
@@ -15,7 +15,9 @@ #include "cobalt/loader/image/animated_image_tracker.h" #include <algorithm> +#include <utility> +#include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" #include "cobalt/base/polymorphic_downcast.h" @@ -24,8 +26,22 @@ namespace image { AnimatedImageTracker::AnimatedImageTracker( + const char* name, base::ThreadPriority animated_image_decode_thread_priority) - : animated_image_decode_thread_("AnimatedImage") { + : animated_image_decode_thread_("AnimatedImage"), + name_(name), + count_animated_images_active( + base::StringPrintf("Count.%s.AnimatedImage.Active", name), 0, + "Total number of active animated image decoders."), + count_animated_frames_decoded( + base::StringPrintf("Count.%s.AnimatedImage.DecodedFrames", name), 0, + "Total number of decoded animated image frames."), + count_animated_frames_decoding_underrun( + base::StringPrintf("Count.%s.AnimatedImage.DecodingUnderruns", name), + 0, "Number of underruns from decoding animated images"), + count_animated_frames_decoding_overrun( + base::StringPrintf("Count.%s.AnimatedImage.DecodingOverruns", name), + 0, "Number of overruns from decoding animated images") { TRACE_EVENT0("cobalt::loader::image", "AnimatedImageTracker::RecordImage()"); base::Thread::Options options(base::MessageLoop::TYPE_DEFAULT, 0 /* default stack size */); @@ -123,6 +139,17 @@ playing_urls_.clear(); } +void AnimatedImageTracker::OnRenderTreeRasterized() { + count_animated_images_active = playing_urls_.size(); + for (const auto& playing_url : playing_urls_) { + auto image = image_map_[playing_url.first].get(); + auto stats = image->GetFrameDeltaStats(); + count_animated_frames_decoded += stats.frames_decoded; + count_animated_frames_decoding_underrun += stats.frames_underrun; + count_animated_frames_decoding_overrun += stats.frames_overrun; + } +} + } // namespace image } // namespace loader } // namespace cobalt
diff --git a/cobalt/loader/image/animated_image_tracker.h b/cobalt/loader/image/animated_image_tracker.h index f6f38e4..6df920b 100644 --- a/cobalt/loader/image/animated_image_tracker.h +++ b/cobalt/loader/image/animated_image_tracker.h
@@ -20,6 +20,7 @@ #include "base/containers/small_map.h" #include "base/threading/thread.h" +#include "cobalt/base/c_val.h" #include "cobalt/base/unused.h" #include "cobalt/loader/image/image.h" #include "url/gurl.h" @@ -33,7 +34,8 @@ // playing status is updated hence decoding is turned on / off for it. class AnimatedImageTracker { public: - explicit AnimatedImageTracker( + AnimatedImageTracker( + const char* name, base::ThreadPriority animated_image_decode_thread_priority); ~AnimatedImageTracker(); @@ -54,6 +56,9 @@ // animations. void Reset(); + // Called from WebModule to compute image animation related stats. + void OnRenderTreeRasterized(); + private: void OnDisplayStart(loader::image::AnimatedImage* image); void OnDisplayEnd(loader::image::AnimatedImage* image); @@ -72,6 +77,15 @@ URLToIntMap current_url_counts_; URLSet playing_urls_; + // The name of the WebModule this AnimatedImage tracker belongs to, for CVals. + const std::string name_; + + // Animated image counters + base::CVal<int, base::CValPublic> count_animated_images_active; + base::CVal<int, base::CValPublic> count_animated_frames_decoded; + base::CVal<int, base::CValPublic> count_animated_frames_decoding_underrun; + base::CVal<int, base::CValPublic> count_animated_frames_decoding_overrun; + // Used to ensure that all AnimatedImageTracker methods are called on the // same thread (*not* |animated_image_decode_thread_|), the thread that we // were constructed on.
diff --git a/cobalt/loader/image/animated_webp_image.cc b/cobalt/loader/image/animated_webp_image.cc index ed0b959..8689168 100644 --- a/cobalt/loader/image/animated_webp_image.cc +++ b/cobalt/loader/image/animated_webp_image.cc
@@ -148,6 +148,8 @@ return; } is_playing_ = true; + current_stats.frames_underrun = 0; + current_stats.frames_overrun = 0; if (received_first_frame_) { StartDecoding(); @@ -171,7 +173,8 @@ void AnimatedWebPImage::StartDecoding() { TRACE_EVENT0("cobalt::loader::image", "AnimatedWebPImage::StartDecoding()"); lock_.AssertAcquired(); - current_frame_time_ = base::TimeTicks::Now(); + decoding_start_time_ = current_frame_time_ = base::TimeTicks::Now(); + current_stats.frames_decoded = 0; if (task_runner_->BelongsToCurrentThread()) { DecodeFrames(); } else { @@ -269,6 +272,7 @@ LOG(ERROR) << "Failed to decode WebP image frame."; return false; } + current_stats.frames_decoded++; } // Alpha blend the current frame on top of the buffer. @@ -354,6 +358,7 @@ // Always wait for a consumer to consume the previous frame before moving // forward with decoding the next frame. if (!frame_provider_->FrameConsumed()) { + current_stats.frames_overrun++; return false; } @@ -386,6 +391,7 @@ if (next_frame_time_ < current_time) { // Don't let the animation fall back for more than a frame. next_frame_time_ = current_time; + current_stats.frames_underrun++; } return true; @@ -431,6 +437,25 @@ return target_canvas; } +AnimatedImage::AnimatedImageDecodingStats +AnimatedWebPImage::GetFrameDeltaStats() { + AnimatedImageDecodingStats result; + if (current_stats.frames_decoded >= last_stats.frames_decoded) { + result.frames_decoded = + current_stats.frames_decoded - last_stats.frames_decoded; + result.frames_underrun = + current_stats.frames_underrun - last_stats.frames_underrun; + result.frames_overrun = + current_stats.frames_overrun - last_stats.frames_overrun; + } else { + // There was a reset somewhere + // Simply return total, this discards any overflow data we might have had. + result = current_stats; + } + last_stats = current_stats; + return result; +} + } // namespace image } // namespace loader } // namespace cobalt
diff --git a/cobalt/loader/image/animated_webp_image.h b/cobalt/loader/image/animated_webp_image.h index 135bf6a..4d1be04 100644 --- a/cobalt/loader/image/animated_webp_image.h +++ b/cobalt/loader/image/animated_webp_image.h
@@ -66,6 +66,8 @@ // Returns the render image of the frame for debugging scoped_refptr<render_tree::Image> GetFrameForDebugging(int target_frame); + AnimatedImageDecodingStats GetFrameDeltaStats() override; + private: ~AnimatedWebPImage() override; @@ -119,12 +121,17 @@ base::CancelableClosure decode_closure_; base::TimeTicks current_frame_time_; base::Optional<base::TimeTicks> next_frame_time_; + // The original encoded data. std::vector<uint8> data_buffer_; scoped_refptr<render_tree::Image> current_canvas_; scoped_refptr<FrameProvider> frame_provider_; base::Lock lock_; + base::TimeTicks decoding_start_time_; + AnimatedImageDecodingStats current_stats; + AnimatedImageDecodingStats last_stats; + // Makes sure that the thread that sets the task_runner is always consistent. // This is the thread sending Play()/Stop() calls, and is not necessarily // the same thread that the task_runner itself is running on.
diff --git a/cobalt/loader/image/image.h b/cobalt/loader/image/image.h index af99363..bac9849 100644 --- a/cobalt/loader/image/image.h +++ b/cobalt/loader/image/image.h
@@ -145,6 +145,16 @@ image_node_builder->destination_rect = destination_rect; image_node_builder->local_transform = local_transform; } + + // Frame counters for decoding. + struct AnimatedImageDecodingStats { + unsigned int frames_decoded = 0; + unsigned int frames_underrun = 0; + unsigned int frames_overrun = 0; + }; + + // Returns decoded frame stats since the last call, as a delta. + virtual AnimatedImageDecodingStats GetFrameDeltaStats() = 0; }; } // namespace image
diff --git a/cobalt/media/base/pipeline.h b/cobalt/media/base/pipeline.h index ccb3a1b..d2d8296 100644 --- a/cobalt/media/base/pipeline.h +++ b/cobalt/media/base/pipeline.h
@@ -98,6 +98,7 @@ const GetDecodeTargetGraphicsContextProviderFunc& get_decode_target_graphics_context_provider_func, bool allow_resume_after_suspend, bool allow_batched_sample_write, + SbTime audio_write_duration_local, SbTime audio_write_duration_remote, MediaLog* media_log, DecodeTargetProvider* decode_target_provider); virtual ~Pipeline() {} @@ -217,6 +218,9 @@ // be 0. virtual void GetNaturalVideoSize(gfx::Size* out_size) const = 0; + // Gets the names of audio connectors used by the audio output. + virtual std::vector<std::string> GetAudioConnectors() const = 0; + // Return true if loading progress has been made since the last time this // method was called. virtual bool DidLoadingProgress() const = 0;
diff --git a/cobalt/media/base/sbplayer_bridge.cc b/cobalt/media/base/sbplayer_bridge.cc index 10674e3..7d45642 100644 --- a/cobalt/media/base/sbplayer_bridge.cc +++ b/cobalt/media/base/sbplayer_bridge.cc
@@ -370,6 +370,36 @@ GetInfo_Locked(video_frames_decoded, video_frames_dropped, media_time); } +std::vector<CobaltExtensionMediaAudioConfiguration> +SbPlayerBridge::GetAudioConfigurations() { + DCHECK(sbplayer_interface_->IsAudioWriteAheadExtensionEnabled()); + base::AutoLock auto_lock(lock_); + + if (!SbPlayerIsValid(player_)) { + return std::vector<CobaltExtensionMediaAudioConfiguration>(); + } + + std::vector<CobaltExtensionMediaAudioConfiguration> configurations; + + // Set a limit to avoid infinite loop. + constexpr int kMaxAudioConfigurations = 32; + + for (int i = 0; i < kMaxAudioConfigurations; ++i) { + CobaltExtensionMediaAudioConfiguration configuration; + if (!sbplayer_interface_->GetAudioConfiguration(player_, i, + &configuration)) { + break; + } + + configurations.push_back(configuration); + } + + LOG_IF(WARNING, configurations.empty()) + << "Failed to find any audio configurations."; + + return configurations; +} + #if SB_HAS(PLAYER_WITH_URL) void SbPlayerBridge::GetUrlPlayerBufferedTimeRanges( base::TimeDelta* buffer_start_time, base::TimeDelta* buffer_length_time) {
diff --git a/cobalt/media/base/sbplayer_bridge.h b/cobalt/media/base/sbplayer_bridge.h index efb6ea8..80f1215 100644 --- a/cobalt/media/base/sbplayer_bridge.h +++ b/cobalt/media/base/sbplayer_bridge.h
@@ -24,6 +24,7 @@ #include "base/message_loop/message_loop.h" #include "base/synchronization/lock.h" #include "base/time/time.h" +#include "cobalt/extension/audio_write_ahead.h" #include "cobalt/media/base/cval_stats.h" #include "cobalt/media/base/decode_target_provider.h" #include "cobalt/media/base/decoder_buffer_cache.h" @@ -115,6 +116,7 @@ void SetPlaybackRate(double playback_rate); void GetInfo(uint32* video_frames_decoded, uint32* video_frames_dropped, base::TimeDelta* media_time); + std::vector<CobaltExtensionMediaAudioConfiguration> GetAudioConfigurations(); #if SB_HAS(PLAYER_WITH_URL) void GetUrlPlayerBufferedTimeRanges(base::TimeDelta* buffer_start_time,
diff --git a/cobalt/media/base/sbplayer_interface.cc b/cobalt/media/base/sbplayer_interface.cc index 7c44dae..968d526 100644 --- a/cobalt/media/base/sbplayer_interface.cc +++ b/cobalt/media/base/sbplayer_interface.cc
@@ -14,9 +14,29 @@ #include "cobalt/media/base/sbplayer_interface.h" +#include <string> + namespace cobalt { namespace media { +DefaultSbPlayerInterface::DefaultSbPlayerInterface() { + const CobaltExtensionConfigurableAudioWriteAheadApi* extension_api = + static_cast<const CobaltExtensionConfigurableAudioWriteAheadApi*>( + SbSystemGetExtension( + kCobaltExtensionConfigurableAudioWriteAheadName)); + if (!extension_api) { + return; + } + + DCHECK_EQ(extension_api->name, + std::string(kCobaltExtensionConfigurableAudioWriteAheadName)); + DCHECK_EQ(extension_api->version, 1u); + DCHECK_NE(extension_api->PlayerGetAudioConfiguration, nullptr); + + audio_write_duration_player_get_audio_configuration_ = + extension_api->PlayerGetAudioConfiguration; +} + SbPlayer DefaultSbPlayerInterface::Create( SbWindow window, const SbPlayerCreationParam* creation_param, SbPlayerDeallocateSampleFunc sample_deallocate_func, @@ -109,5 +129,17 @@ } #endif // SB_HAS(PLAYER_WITH_URL) +bool DefaultSbPlayerInterface::IsAudioWriteAheadExtensionEnabled() const { + return audio_write_duration_player_get_audio_configuration_ != nullptr; +} + +bool DefaultSbPlayerInterface::GetAudioConfiguration( + SbPlayer player, int index, + CobaltExtensionMediaAudioConfiguration* out_audio_configuration) { + DCHECK(IsAudioWriteAheadExtensionEnabled()); + return audio_write_duration_player_get_audio_configuration_( + player, index, out_audio_configuration); +} + } // namespace media } // namespace cobalt
diff --git a/cobalt/media/base/sbplayer_interface.h b/cobalt/media/base/sbplayer_interface.h index e79c1e6..751ff11 100644 --- a/cobalt/media/base/sbplayer_interface.h +++ b/cobalt/media/base/sbplayer_interface.h
@@ -15,6 +15,7 @@ #ifndef COBALT_MEDIA_BASE_SBPLAYER_INTERFACE_H_ #define COBALT_MEDIA_BASE_SBPLAYER_INTERFACE_H_ +#include "cobalt/extension/audio_write_ahead.h" #include "cobalt/media/base/cval_stats.h" #include "starboard/player.h" @@ -68,6 +69,11 @@ SbPlayer player, SbUrlPlayerExtraInfo* out_url_player_info) = 0; #endif // SB_HAS(PLAYER_WITH_URL) + virtual bool IsAudioWriteAheadExtensionEnabled() const = 0; + virtual bool GetAudioConfiguration( + SbPlayer player, int index, + CobaltExtensionMediaAudioConfiguration* out_audio_configuration) = 0; + // disabled by default, but can be enabled via h5vcc setting. void EnableCValStats(bool should_enable) { cval_stats_.Enable(should_enable); @@ -77,6 +83,8 @@ class DefaultSbPlayerInterface final : public SbPlayerInterface { public: + DefaultSbPlayerInterface(); + SbPlayer Create( SbWindow window, const SbPlayerCreationParam* creation_param, SbPlayerDeallocateSampleFunc sample_deallocate_func, @@ -113,6 +121,17 @@ void GetUrlPlayerExtraInfo( SbPlayer player, SbUrlPlayerExtraInfo* out_url_player_info) override; #endif // SB_HAS(PLAYER_WITH_URL) + + bool IsAudioWriteAheadExtensionEnabled() const override; + bool GetAudioConfiguration( + SbPlayer player, int index, + CobaltExtensionMediaAudioConfiguration* out_audio_configuration) override; + + private: + bool (*audio_write_duration_player_get_audio_configuration_)( + SbPlayer player, int index, + CobaltExtensionMediaAudioConfiguration* out_audio_configuration) = + nullptr; };
diff --git a/cobalt/media/base/sbplayer_pipeline.cc b/cobalt/media/base/sbplayer_pipeline.cc index 261af02..e14d0d0 100644 --- a/cobalt/media/base/sbplayer_pipeline.cc +++ b/cobalt/media/base/sbplayer_pipeline.cc
@@ -30,12 +30,14 @@ #include "base/trace_event/trace_event.h" #include "cobalt/base/c_val.h" #include "cobalt/base/startup_timer.h" +#include "cobalt/extension/audio_write_ahead.h" #include "cobalt/math/size.h" #include "cobalt/media/base/media_export.h" #include "cobalt/media/base/pipeline.h" #include "cobalt/media/base/playback_statistics.h" #include "cobalt/media/base/sbplayer_bridge.h" #include "cobalt/media/base/sbplayer_set_bounds_helper.h" +#include "starboard/common/media.h" #include "starboard/common/string.h" #include "starboard/configuration_constants.h" #include "starboard/time.h" @@ -66,6 +68,7 @@ using ::media::PipelineStatistics; using ::media::PipelineStatusCallback; using ::media::VideoDecoderConfig; +using ::starboard::GetCobaltExtensionMediaAudioConnectorName; static const int kRetryDelayAtSuspendInMilliseconds = 100; @@ -90,6 +93,35 @@ #endif // SB_HAS(PLAYER_WITH_URL) }; +bool HasRemoteAudioOutputs( + const std::vector<CobaltExtensionMediaAudioConfiguration>& configurations) { + for (auto&& configuration : configurations) { + const auto connector = configuration.connector; + switch (connector) { + case kCobaltExtensionMediaAudioConnectorUnknown: + case kCobaltExtensionMediaAudioConnectorAnalog: + case kCobaltExtensionMediaAudioConnectorBuiltIn: + case kCobaltExtensionMediaAudioConnectorHdmi: + case kCobaltExtensionMediaAudioConnectorSpdif: + case kCobaltExtensionMediaAudioConnectorUsb: + LOG(INFO) << "Encountered local audio connector: " + << GetCobaltExtensionMediaAudioConnectorName(connector); + break; + case kCobaltExtensionMediaAudioConnectorBluetooth: + case kCobaltExtensionMediaAudioConnectorRemoteWired: + case kCobaltExtensionMediaAudioConnectorRemoteWireless: + case kCobaltExtensionMediaAudioConnectorRemoteOther: + LOG(INFO) << "Encountered remote audio connector: " + << GetCobaltExtensionMediaAudioConnectorName(connector); + return true; + } + } + + LOG(INFO) << "No remote audio outputs found."; + + return false; +} + // SbPlayerPipeline is a PipelineBase implementation that uses the SbPlayer // interface internally. class MEDIA_EXPORT SbPlayerPipeline : public Pipeline, @@ -103,6 +135,7 @@ const GetDecodeTargetGraphicsContextProviderFunc& get_decode_target_graphics_context_provider_func, bool allow_resume_after_suspend, bool allow_batched_sample_write, + SbTime audio_write_duration_local, SbTime audio_write_duration_remote, MediaLog* media_log, DecodeTargetProvider* decode_target_provider); ~SbPlayerPipeline() override; @@ -148,6 +181,7 @@ TimeDelta GetMediaStartDate() const override; #endif // SB_HAS(PLAYER_WITH_URL) void GetNaturalVideoSize(gfx::Size* out_size) const override; + std::vector<std::string> GetAudioConnectors() const override; bool DidLoadingProgress() const override; PipelineStatistics GetStatistics() const override; @@ -322,19 +356,30 @@ DecodeTargetProvider* decode_target_provider_; + // The following two variables are used only when the Configurable Audio Write + // Ahead extension is enabled. + SbTime audio_write_duration_local_; + SbTime audio_write_duration_remote_; + + // When the Configurable Audio Write Ahead extension is enabled, + // the two variables below should always contain the same value. + // // Read audio from the stream if |timestamp_of_last_written_audio_| is less - // than |seek_time_| + |kAudioPrerollLimit|, this effectively allows 10 - // seconds of audio to be written to the SbPlayer after playback startup or - // seek. - static const SbTime kAudioPrerollLimit = 10 * kSbTimeSecond; - // Don't read audio from the stream more than |kAudioLimit| ahead of the - // current media time during playing. - static const SbTime kAudioLimit = kSbTimeSecond; + // than |seek_time_| + |audio_write_duration_for_preroll_|. When the + // Configurable Audio Write Ahead extension is disabled, this effectively + // allows 10 seconds of audio to be written to the SbPlayer after playback + // startup or seek. + SbTime audio_write_duration_ = kSbTimeSecond; + // Don't read audio from the stream more than |audio_write_duration_| ahead of + // the current media time during playing. + SbTime audio_write_duration_for_preroll_ = 10 * kSbTimeSecond; + // Only call GetMediaTime() from OnNeedData if it has been // |kMediaTimeCheckInterval| since the last call to GetMediaTime(). static const SbTime kMediaTimeCheckInterval = 0.1 * kSbTimeSecond; // Timestamp for the last written audio. SbTime timestamp_of_last_written_audio_ = 0; + // Last media time reported by GetMediaTime(). base::CVal<SbTime> last_media_time_; // Time when we last checked the media time. @@ -357,6 +402,7 @@ const GetDecodeTargetGraphicsContextProviderFunc& get_decode_target_graphics_context_provider_func, bool allow_resume_after_suspend, bool allow_batched_sample_write, + SbTime audio_write_duration_local, SbTime audio_write_duration_remote, MediaLog* media_log, DecodeTargetProvider* decode_target_provider) : pipeline_identifier_( base::StringPrintf("%X", g_pipeline_identifier_counter++)), @@ -406,7 +452,16 @@ pipeline_identifier_.c_str()), "", "The max video capabilities required for the media pipeline."), playback_statistics_(pipeline_identifier_) { - SbMediaSetAudioWriteDuration(kAudioLimit); + SbMediaSetAudioWriteDuration(audio_write_duration_); + DCHECK(sbplayer_interface_); + if (sbplayer_interface_->IsAudioWriteAheadExtensionEnabled()) { + audio_write_duration_local_ = audio_write_duration_local; + audio_write_duration_remote_ = audio_write_duration_remote; + } else { + LOG(INFO) << "Setting audio write duration to " << audio_write_duration_ + << ", the duration during preroll is " + << audio_write_duration_for_preroll_; + } } SbPlayerPipeline::~SbPlayerPipeline() { DCHECK(!player_bridge_); } @@ -571,6 +626,8 @@ if (demuxer_) { stop_cb_ = stop_cb; demuxer_->Stop(); + video_stream_ = nullptr; + audio_stream_ = nullptr; OnDemuxerStopped(); } else { stop_cb.Run(); @@ -780,6 +837,27 @@ *out_size = natural_size_; } +std::vector<std::string> SbPlayerPipeline::GetAudioConnectors() const { + if (sbplayer_interface_->IsAudioWriteAheadExtensionEnabled()) { + base::AutoLock auto_lock(lock_); + if (!player_bridge_) { + return std::vector<std::string>(); + } + + std::vector<std::string> connectors; + + auto configurations = player_bridge_->GetAudioConfigurations(); + for (auto&& configuration : configurations) { + connectors.push_back( + GetCobaltExtensionMediaAudioConnectorName(configuration.connector)); + } + + return connectors; + } + + return std::vector<std::string>(); +} + bool SbPlayerPipeline::DidLoadingProgress() const { base::AutoLock auto_lock(lock_); bool ret = did_loading_progress_; @@ -1035,6 +1113,18 @@ *decode_to_texture_output_mode_, decode_target_provider_, max_video_capabilities_, pipeline_identifier_)); if (player_bridge_->IsValid()) { + if (sbplayer_interface_->IsAudioWriteAheadExtensionEnabled()) { + // TODO(b/267678497): When `player_bridge_->GetAudioConfigurations()` + // returns no audio configurations, update the write durations again + // before the SbPlayer reaches `kSbPlayerStatePresenting`. + audio_write_duration_for_preroll_ = audio_write_duration_ = + HasRemoteAudioOutputs(player_bridge_->GetAudioConfigurations()) + ? audio_write_duration_remote_ + : audio_write_duration_local_; + LOG(INFO) << "SbPlayerBridge created, with audio write duration at " + << audio_write_duration_for_preroll_; + } + SetPlaybackRateTask(playback_rate_); SetVolumeTask(volume_); } else { @@ -1268,17 +1358,18 @@ GetMediaTime(); } - // Delay reading audio more than |kAudioLimit| ahead of playback after the - // player has received enough audio for preroll, taking into account that - // our estimate of playback time might be behind by + // Delay reading audio more than |audio_write_duration_| ahead of playback + // after the player has received enough audio for preroll, taking into + // account that our estimate of playback time might be behind by // |kMediaTimeCheckInterval|. if (timestamp_of_last_written_audio_ - seek_time_.ToSbTime() > - kAudioPrerollLimit) { + audio_write_duration_for_preroll_) { // The estimated time ahead of playback may be negative if no audio has // been written. SbTime time_ahead_of_playback = timestamp_of_last_written_audio_ - last_media_time_; - if (time_ahead_of_playback > (kAudioLimit + kMediaTimeCheckInterval)) { + if (time_ahead_of_playback > + (audio_write_duration_ + kMediaTimeCheckInterval)) { task_runner_->PostDelayedTask( FROM_HERE, base::Bind(&SbPlayerPipeline::DelayedNeedData, this, max_buffers), @@ -1348,6 +1439,15 @@ playback_statistics_.OnPresenting( video_stream_->video_decoder_config()); } + if (sbplayer_interface_->IsAudioWriteAheadExtensionEnabled()) { + audio_write_duration_for_preroll_ = audio_write_duration_ = + HasRemoteAudioOutputs(player_bridge_->GetAudioConfigurations()) + ? audio_write_duration_remote_ + : audio_write_duration_local_; + LOG(INFO) + << "SbPlayerBridge reaches kSbPlayerStatePresenting, with audio" + << " write duration at " << audio_write_duration_; + } break; } case kSbPlayerStateEndOfStream: @@ -1535,6 +1635,8 @@ std::string SbPlayerPipeline::AppendStatisticsString( const std::string& message) const { + DCHECK(task_runner_->BelongsToCurrentThread()); + if (nullptr == video_stream_) { return message + ", playback statistics: n/a."; } else { @@ -1602,12 +1704,14 @@ const GetDecodeTargetGraphicsContextProviderFunc& get_decode_target_graphics_context_provider_func, bool allow_resume_after_suspend, bool allow_batched_sample_write, + SbTime audio_write_duration_local, SbTime audio_write_duration_remote, MediaLog* media_log, DecodeTargetProvider* decode_target_provider) { - return new SbPlayerPipeline(interface, window, task_runner, - get_decode_target_graphics_context_provider_func, - allow_resume_after_suspend, - allow_batched_sample_write, media_log, - decode_target_provider); + return new SbPlayerPipeline( + interface, window, task_runner, + get_decode_target_graphics_context_provider_func, + allow_resume_after_suspend, allow_batched_sample_write, + audio_write_duration_local, audio_write_duration_remote, media_log, + decode_target_provider); } } // namespace media
diff --git a/cobalt/media/media_module.cc b/cobalt/media/media_module.cc index 95965a5..aedc5c6 100644 --- a/cobalt/media/media_module.cc +++ b/cobalt/media/media_module.cc
@@ -194,6 +194,18 @@ LOG(INFO) << (value ? "Enabling" : "Disabling") << " media metrics collection."; return true; + } else if (sbplayer_interface_->IsAudioWriteAheadExtensionEnabled()) { + if (name == "AudioWriteDurationLocal" && value > 0) { + audio_write_duration_local_ = value; + LOG(INFO) << "Set AudioWriteDurationLocal to " + << audio_write_duration_local_; + return true; + } else if (name == "AudioWriteDurationRemote" && value > 0) { + audio_write_duration_remote_ = value; + LOG(INFO) << "Set AudioWriteDurationRemote to " + << audio_write_duration_remote_; + return true; + } } return false; } @@ -206,12 +218,21 @@ window = system_window_->GetSbWindow(); } + SbTime audio_write_duration_local = -1; + SbTime audio_write_duration_remote = -1; + + if (sbplayer_interface_->IsAudioWriteAheadExtensionEnabled()) { + audio_write_duration_local = audio_write_duration_local_; + audio_write_duration_remote = audio_write_duration_remote_; + } + return std::unique_ptr<WebMediaPlayer>(new media::WebMediaPlayerImpl( sbplayer_interface_.get(), window, base::Bind(&MediaModule::GetSbDecodeTargetGraphicsContextProvider, base::Unretained(this)), client, this, options_.allow_resume_after_suspend, - allow_batched_sample_write_, &media_log_)); + allow_batched_sample_write_, audio_write_duration_local, + audio_write_duration_remote, &media_log_)); } void MediaModule::Suspend() {
diff --git a/cobalt/media/media_module.h b/cobalt/media/media_module.h index 6ca47b8..f4c2594 100644 --- a/cobalt/media/media_module.h +++ b/cobalt/media/media_module.h
@@ -24,6 +24,7 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/optional.h" +#include "cobalt/extension/audio_write_ahead.h" #include "cobalt/math/size.h" #include "cobalt/media/base/sbplayer_interface.h" #include "cobalt/media/can_play_type_handler.h" @@ -123,6 +124,12 @@ bool allow_batched_sample_write_ = false; + // The following two variables are used only when the Configurable Audio Write + // Ahead extension is enabled. + SbTime audio_write_duration_local_ = kCobaltExtensionPlayerWriteDurationLocal; + SbTime audio_write_duration_remote_ = + kCobaltExtensionPlayerWriteDurationRemote; + DecoderBufferAllocator decoder_buffer_allocator_; };
diff --git a/cobalt/media/player/web_media_player.h b/cobalt/media/player/web_media_player.h index 1322473..459ec6e 100644 --- a/cobalt/media/player/web_media_player.h +++ b/cobalt/media/player/web_media_player.h
@@ -130,6 +130,9 @@ virtual int GetNaturalWidth() const = 0; virtual int GetNaturalHeight() const = 0; + // Names of audio connectors used by the playback. + virtual std::vector<std::string> GetAudioConnectors() const = 0; + // Getters of playback state. virtual bool IsPaused() const = 0; virtual bool IsSeeking() const = 0;
diff --git a/cobalt/media/player/web_media_player_impl.cc b/cobalt/media/player/web_media_player_impl.cc index 8073366..8f9ba9a 100644 --- a/cobalt/media/player/web_media_player_impl.cc +++ b/cobalt/media/player/web_media_player_impl.cc
@@ -118,6 +118,7 @@ get_decode_target_graphics_context_provider_func, WebMediaPlayerClient* client, WebMediaPlayerDelegate* delegate, bool allow_resume_after_suspend, bool allow_batched_sample_write, + SbTime audio_write_duration_local, SbTime audio_write_duration_remote, ::media::MediaLog* const media_log) : pipeline_thread_("media_pipeline"), network_state_(WebMediaPlayer::kNetworkStateEmpty), @@ -146,6 +147,7 @@ Pipeline::Create(interface, window, pipeline_thread_.task_runner(), get_decode_target_graphics_context_provider_func, allow_resume_after_suspend_, allow_batched_sample_write_, + audio_write_duration_local, audio_write_duration_remote, media_log_, decode_target_provider_.get()); // Also we want to be notified of |main_loop_| destruction. @@ -428,6 +430,11 @@ return size.height(); } +std::vector<std::string> WebMediaPlayerImpl::GetAudioConnectors() const { + DCHECK_EQ(main_loop_, base::MessageLoop::current()); + return pipeline_->GetAudioConnectors(); +} + bool WebMediaPlayerImpl::IsPaused() const { DCHECK_EQ(main_loop_, base::MessageLoop::current());
diff --git a/cobalt/media/player/web_media_player_impl.h b/cobalt/media/player/web_media_player_impl.h index b64f318..9139ce8 100644 --- a/cobalt/media/player/web_media_player_impl.h +++ b/cobalt/media/player/web_media_player_impl.h
@@ -110,6 +110,8 @@ WebMediaPlayerDelegate* delegate, bool allow_resume_after_suspend, bool allow_batched_sample_write, + SbTime audio_write_duration_local, + SbTime audio_write_duration_remote, ::media::MediaLog* const media_log); ~WebMediaPlayerImpl() override; @@ -146,6 +148,9 @@ int GetNaturalWidth() const override; int GetNaturalHeight() const override; + // Names of audio connectors used by the playback. + std::vector<std::string> GetAudioConnectors() const override; + // Getters of playback state. bool IsPaused() const override; bool IsSeeking() const override;
diff --git a/cobalt/persistent_storage/persistent_settings.cc b/cobalt/persistent_storage/persistent_settings.cc index c4db368..c93ff54 100644 --- a/cobalt/persistent_storage/persistent_settings.cc +++ b/cobalt/persistent_storage/persistent_settings.cc
@@ -201,7 +201,7 @@ void PersistentSettings::SetPersistentSetting( const std::string& key, std::unique_ptr<base::Value> value, - base::OnceClosure closure) { + base::OnceClosure closure, bool blocking) { if (key == kValidated) { LOG(ERROR) << "Cannot set protected persistent setting: " << key; return; @@ -209,12 +209,12 @@ message_loop()->task_runner()->PostTask( FROM_HERE, base::BindOnce(&PersistentSettings::SetPersistentSettingHelper, base::Unretained(this), key, std::move(value), - std::move(closure))); + std::move(closure), blocking)); } void PersistentSettings::SetPersistentSettingHelper( const std::string& key, std::unique_ptr<base::Value> value, - base::OnceClosure closure) { + base::OnceClosure closure, bool blocking) { DCHECK_EQ(base::MessageLoop::current(), message_loop()); if (validated_initial_settings_) { base::AutoLock auto_lock(pref_store_lock_); @@ -223,7 +223,7 @@ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); writeable_pref_store()->SetValue( key, std::move(value), WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); - writeable_pref_store()->CommitPendingWrite(); + commit_pending_write(blocking); } else { LOG(ERROR) << "Cannot set persistent setting while unvalidated: " << key; } @@ -231,15 +231,17 @@ } void PersistentSettings::RemovePersistentSetting(const std::string& key, - base::OnceClosure closure) { + base::OnceClosure closure, + bool blocking) { message_loop()->task_runner()->PostTask( FROM_HERE, base::BindOnce(&PersistentSettings::RemovePersistentSettingHelper, - base::Unretained(this), key, std::move(closure))); + base::Unretained(this), key, std::move(closure), + blocking)); } void PersistentSettings::RemovePersistentSettingHelper( - const std::string& key, base::OnceClosure closure) { + const std::string& key, base::OnceClosure closure, bool blocking) { DCHECK_EQ(base::MessageLoop::current(), message_loop()); if (validated_initial_settings_) { base::AutoLock auto_lock(pref_store_lock_); @@ -248,7 +250,7 @@ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); writeable_pref_store()->RemoveValue( key, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); - writeable_pref_store()->CommitPendingWrite(); + commit_pending_write(blocking); } else { LOG(ERROR) << "Cannot remove persistent setting while unvalidated: " << key; }
diff --git a/cobalt/persistent_storage/persistent_settings.h b/cobalt/persistent_storage/persistent_settings.h index 0cf789f..ff3ea58 100644 --- a/cobalt/persistent_storage/persistent_settings.h +++ b/cobalt/persistent_storage/persistent_settings.h
@@ -71,11 +71,13 @@ void SetPersistentSetting( const std::string& key, std::unique_ptr<base::Value> value, - base::OnceClosure closure = std::move(base::DoNothing())); + base::OnceClosure closure = std::move(base::DoNothing()), + bool blocking = false); void RemovePersistentSetting( const std::string& key, - base::OnceClosure closure = std::move(base::DoNothing())); + base::OnceClosure closure = std::move(base::DoNothing()), + bool blocking = false); void DeletePersistentSettings( base::OnceClosure closure = std::move(base::DoNothing())); @@ -89,10 +91,10 @@ void SetPersistentSettingHelper(const std::string& key, std::unique_ptr<base::Value> value, - base::OnceClosure closure); + base::OnceClosure closure, bool blocking); void RemovePersistentSettingHelper(const std::string& key, - base::OnceClosure closure); + base::OnceClosure closure, bool blocking); void DeletePersistentSettingsHelper(base::OnceClosure closure); @@ -101,6 +103,18 @@ return pref_store_; } + void commit_pending_write(bool blocking) { + if (blocking) { + base::WaitableEvent written; + writeable_pref_store()->CommitPendingWrite( + base::OnceClosure(), + base::BindOnce(&base::WaitableEvent::Signal, Unretained(&written))); + written.Wait(); + } else { + writeable_pref_store()->CommitPendingWrite(); + } + } + // Persistent settings file path. std::string persistent_settings_file_;
diff --git a/cobalt/persistent_storage/persistent_settings_test.cc b/cobalt/persistent_storage/persistent_settings_test.cc index 3f88397..91b2bce 100644 --- a/cobalt/persistent_storage/persistent_settings_test.cc +++ b/cobalt/persistent_storage/persistent_settings_test.cc
@@ -12,6 +12,8 @@ // 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> @@ -20,9 +22,7 @@ #include "base/callback_forward.h" #include "base/synchronization/waitable_event.h" #include "base/test/scoped_task_environment.h" -#include "base/threading/platform_thread.h" #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" @@ -81,13 +81,13 @@ base::OnceClosure closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsInt("key", true)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(4.2), std::move(closure)); + "key", std::make_unique<base::Value>(4.2), std::move(closure), true); test_done_.Wait(); } @@ -99,32 +99,32 @@ base::OnceClosure closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", true)); - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", false)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(true), std::move(closure)); + "key", std::make_unique<base::Value>(true), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_FALSE( + EXPECT_FALSE( persistent_settings->GetPersistentSettingAsBool("key", true)); - ASSERT_FALSE( + EXPECT_FALSE( persistent_settings->GetPersistentSettingAsBool("key", false)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(false), std::move(closure)); + "key", std::make_unique<base::Value>(false), std::move(closure), true); test_done_.Wait(); } @@ -143,13 +143,13 @@ base::OnceClosure closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ(8, persistent_settings->GetPersistentSettingAsInt("key", 8)); + EXPECT_EQ(8, persistent_settings->GetPersistentSettingAsInt("key", 8)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(4.2), std::move(closure)); + "key", std::make_unique<base::Value>(4.2), std::move(closure), true); test_done_.Wait(); } @@ -161,36 +161,36 @@ base::OnceClosure closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ(-1, persistent_settings->GetPersistentSettingAsInt("key", 8)); + EXPECT_EQ(-1, persistent_settings->GetPersistentSettingAsInt("key", 8)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(-1), std::move(closure)); + "key", std::make_unique<base::Value>(-1), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ(0, persistent_settings->GetPersistentSettingAsInt("key", 8)); + EXPECT_EQ(0, persistent_settings->GetPersistentSettingAsInt("key", 8)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(0), std::move(closure)); + "key", std::make_unique<base::Value>(0), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ(42, persistent_settings->GetPersistentSettingAsInt("key", 8)); + EXPECT_EQ(42, persistent_settings->GetPersistentSettingAsInt("key", 8)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(42), std::move(closure)); + "key", std::make_unique<base::Value>(42), std::move(closure), true); test_done_.Wait(); } @@ -214,13 +214,13 @@ base::OnceClosure closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ("hello", persistent_settings->GetPersistentSettingAsString( + EXPECT_EQ("hello", persistent_settings->GetPersistentSettingAsString( "key", "hello")); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(4.2), std::move(closure)); + "key", std::make_unique<base::Value>(4.2), std::move(closure), true); test_done_.Wait(); } @@ -232,21 +232,21 @@ base::OnceClosure closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ("", persistent_settings->GetPersistentSettingAsString( + EXPECT_EQ("", persistent_settings->GetPersistentSettingAsString( "key", "hello")); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(""), std::move(closure)); + "key", std::make_unique<base::Value>(""), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ( + EXPECT_EQ( "hello there", persistent_settings->GetPersistentSettingAsString("key", "hello")); test_done->Signal(); @@ -254,46 +254,47 @@ persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>("hello there"), std::move(closure)); + "key", std::make_unique<base::Value>("hello there"), std::move(closure), + true); test_done_.Wait(); test_done_.Reset(); closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ("42", persistent_settings->GetPersistentSettingAsString( + EXPECT_EQ("42", persistent_settings->GetPersistentSettingAsString( "key", "hello")); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>("42"), std::move(closure)); + "key", std::make_unique<base::Value>("42"), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ("\n", persistent_settings->GetPersistentSettingAsString( + EXPECT_EQ("\n", persistent_settings->GetPersistentSettingAsString( "key", "hello")); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>("\n"), std::move(closure)); + "key", std::make_unique<base::Value>("\n"), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_EQ("\\n", persistent_settings->GetPersistentSettingAsString( + EXPECT_EQ("\\n", persistent_settings->GetPersistentSettingAsString( "key", "hello")); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>("\\n"), std::move(closure)); + "key", std::make_unique<base::Value>("\\n"), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); } @@ -307,10 +308,10 @@ [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { auto test_list = persistent_settings->GetPersistentSettingAsList("key"); - ASSERT_FALSE(test_list.empty()); - ASSERT_EQ(1, test_list.size()); - ASSERT_TRUE(test_list[0].is_string()); - ASSERT_EQ("hello", test_list[0].GetString()); + EXPECT_FALSE(test_list.empty()); + EXPECT_EQ(1, test_list.size()); + EXPECT_TRUE(test_list[0].is_string()); + EXPECT_EQ("hello", test_list[0].GetString()); test_done->Signal(); }, persistent_settings.get(), &test_done_); @@ -318,7 +319,7 @@ std::vector<base::Value> list; list.emplace_back("hello"); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(list), std::move(closure)); + "key", std::make_unique<base::Value>(list), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); @@ -326,19 +327,19 @@ [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { auto test_list = persistent_settings->GetPersistentSettingAsList("key"); - ASSERT_FALSE(test_list.empty()); - ASSERT_EQ(2, test_list.size()); - ASSERT_TRUE(test_list[0].is_string()); - ASSERT_EQ("hello", test_list[0].GetString()); - ASSERT_TRUE(test_list[1].is_string()); - ASSERT_EQ("there", test_list[1].GetString()); + EXPECT_FALSE(test_list.empty()); + EXPECT_EQ(2, test_list.size()); + EXPECT_TRUE(test_list[0].is_string()); + EXPECT_EQ("hello", test_list[0].GetString()); + EXPECT_TRUE(test_list[1].is_string()); + EXPECT_EQ("there", test_list[1].GetString()); test_done->Signal(); }, persistent_settings.get(), &test_done_); list.emplace_back("there"); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(list), std::move(closure)); + "key", std::make_unique<base::Value>(list), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); @@ -346,21 +347,21 @@ [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { auto test_list = persistent_settings->GetPersistentSettingAsList("key"); - ASSERT_FALSE(test_list.empty()); - ASSERT_EQ(3, test_list.size()); - ASSERT_TRUE(test_list[0].is_string()); - ASSERT_EQ("hello", test_list[0].GetString()); - ASSERT_TRUE(test_list[1].is_string()); - ASSERT_EQ("there", test_list[1].GetString()); - ASSERT_TRUE(test_list[2].is_int()); - ASSERT_EQ(42, test_list[2].GetInt()); + EXPECT_FALSE(test_list.empty()); + EXPECT_EQ(3, test_list.size()); + EXPECT_TRUE(test_list[0].is_string()); + EXPECT_EQ("hello", test_list[0].GetString()); + EXPECT_TRUE(test_list[1].is_string()); + EXPECT_EQ("there", test_list[1].GetString()); + EXPECT_TRUE(test_list[2].is_int()); + EXPECT_EQ(42, test_list[2].GetInt()); test_done->Signal(); }, persistent_settings.get(), &test_done_); list.emplace_back(42); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(list), std::move(closure)); + "key", std::make_unique<base::Value>(list), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); } @@ -375,10 +376,10 @@ base::WaitableEvent* test_done) { auto test_dict = persistent_settings->GetPersistentSettingAsDictionary("key"); - ASSERT_FALSE(test_dict.empty()); - ASSERT_EQ(1, test_dict.size()); - ASSERT_TRUE(test_dict["key_string"]->is_string()); - ASSERT_EQ("hello", test_dict["key_string"]->GetString()); + EXPECT_FALSE(test_dict.empty()); + EXPECT_EQ(1, test_dict.size()); + EXPECT_TRUE(test_dict["key_string"]->is_string()); + EXPECT_EQ("hello", test_dict["key_string"]->GetString()); test_done->Signal(); }, persistent_settings.get(), &test_done_); @@ -386,7 +387,7 @@ base::flat_map<std::string, std::unique_ptr<base::Value>> dict; dict.try_emplace("key_string", std::make_unique<base::Value>("hello")); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(dict), std::move(closure)); + "key", std::make_unique<base::Value>(dict), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); @@ -395,19 +396,19 @@ base::WaitableEvent* test_done) { auto test_dict = persistent_settings->GetPersistentSettingAsDictionary("key"); - ASSERT_FALSE(test_dict.empty()); - ASSERT_EQ(2, test_dict.size()); - ASSERT_TRUE(test_dict["key_string"]->is_string()); - ASSERT_EQ("hello", test_dict["key_string"]->GetString()); - ASSERT_TRUE(test_dict["key_int"]->is_int()); - ASSERT_EQ(42, test_dict["key_int"]->GetInt()); + EXPECT_FALSE(test_dict.empty()); + EXPECT_EQ(2, test_dict.size()); + EXPECT_TRUE(test_dict["key_string"]->is_string()); + EXPECT_EQ("hello", test_dict["key_string"]->GetString()); + EXPECT_TRUE(test_dict["key_int"]->is_int()); + EXPECT_EQ(42, test_dict["key_int"]->GetInt()); test_done->Signal(); }, persistent_settings.get(), &test_done_); dict.try_emplace("key_int", std::make_unique<base::Value>(42)); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(dict), std::move(closure)); + "key", std::make_unique<base::Value>(dict), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); } @@ -425,10 +426,10 @@ base::WaitableEvent* test_done) { auto test_dict = persistent_settings->GetPersistentSettingAsDictionary( "http://127.0.0.1:45019/"); - ASSERT_FALSE(test_dict.empty()); - ASSERT_EQ(1, test_dict.size()); - ASSERT_TRUE(test_dict["http://127.0.0.1:45019/"]->is_string()); - ASSERT_EQ("Dictionary URL Key Works!", + EXPECT_FALSE(test_dict.empty()); + EXPECT_EQ(1, test_dict.size()); + EXPECT_TRUE(test_dict["http://127.0.0.1:45019/"]->is_string()); + EXPECT_EQ("Dictionary URL Key Works!", test_dict["http://127.0.0.1:45019/"]->GetString()); test_done->Signal(); }, @@ -441,7 +442,7 @@ std::make_unique<base::Value>("Dictionary URL Key Works!")); persistent_settings->SetPersistentSetting("http://127.0.0.1:45019/", std::make_unique<base::Value>(dict), - std::move(closure)); + std::move(closure), true); test_done_.Wait(); test_done_.Reset(); @@ -450,12 +451,12 @@ base::WaitableEvent* test_done) { auto test_dict = persistent_settings->GetPersistentSettingAsDictionary( "http://127.0.0.1:45019/"); - ASSERT_TRUE(test_dict.empty()); + EXPECT_TRUE(test_dict.empty()); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->RemovePersistentSetting("http://127.0.0.1:45019/", - std::move(closure)); + std::move(closure), true); test_done_.Wait(); test_done_.Reset(); } @@ -471,29 +472,29 @@ base::OnceClosure closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", true)); - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", false)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(true), std::move(closure)); + "key", std::make_unique<base::Value>(true), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", true)); - ASSERT_FALSE( + EXPECT_FALSE( persistent_settings->GetPersistentSettingAsBool("key", false)); test_done->Signal(); }, persistent_settings.get(), &test_done_); - persistent_settings->RemovePersistentSetting("key", std::move(closure)); + persistent_settings->RemovePersistentSetting("key", std::move(closure), true); test_done_.Wait(); test_done_.Reset(); } @@ -509,24 +510,24 @@ base::OnceClosure closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", true)); - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", false)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(true), std::move(closure)); + "key", std::make_unique<base::Value>(true), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", true)); - ASSERT_FALSE( + EXPECT_FALSE( persistent_settings->GetPersistentSettingAsBool("key", false)); test_done->Signal(); }, @@ -547,22 +548,18 @@ base::OnceClosure closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", true)); - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", false)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(true), std::move(closure)); + "key", std::make_unique<base::Value>(true), std::move(closure), true); test_done_.Wait(); test_done_.Reset(); - // Sleep for one second to allow for the previous persistent_setting's - // JsonPrefStore instance time to write to disk before creating a new - // persistent_settings and JsonPrefStore instance. - base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); persistent_settings = std::make_unique<PersistentSettings>(kPersistentSettingsJson); ASSERT_TRUE(persistent_settings->GetPersistentSettingAsBool("key", true)); @@ -571,15 +568,15 @@ closure = base::BindOnce( [](PersistentSettings* persistent_settings, base::WaitableEvent* test_done) { - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", true)); - ASSERT_TRUE( + EXPECT_TRUE( persistent_settings->GetPersistentSettingAsBool("key", false)); test_done->Signal(); }, persistent_settings.get(), &test_done_); persistent_settings->SetPersistentSetting( - "key", std::make_unique<base::Value>(false), std::move(closure)); + "key", std::make_unique<base::Value>(false), std::move(closure), true); test_done_.Wait(); test_done_.Reset();
diff --git a/cobalt/version.h b/cobalt/version.h index 3182bc9..74f79af 100644 --- a/cobalt/version.h +++ b/cobalt/version.h
@@ -35,6 +35,6 @@ // release is cut. //. -#define COBALT_VERSION "23.lts.5" +#define COBALT_VERSION "23.lts.6" #endif // COBALT_VERSION_H_
diff --git a/docker-compose-windows.yml b/docker-compose-windows.yml index e577ee4..c60c372 100644 --- a/docker-compose-windows.yml +++ b/docker-compose-windows.yml
@@ -98,3 +98,14 @@ depends_on: - cobalt-build-win32-base image: cobalt-build-win-win32 + + runner-win-win32: + <<: *common-definitions + build: + context: ./docker/windows/runner + dockerfile: ./Dockerfile + args: + - RUNNER_VERSION=2.294.0 + depends_on: + - cobalt-build-win32-base + image: cobalt-runner-win-win32
diff --git a/docker/linux/base/build/Dockerfile b/docker/linux/base/build/Dockerfile index 60533d6..26590dd 100644 --- a/docker/linux/base/build/Dockerfile +++ b/docker/linux/base/build/Dockerfile
@@ -26,7 +26,6 @@ ninja-build \ pkgconf \ unzip \ - yasm \ && /opt/clean-after-apt.sh # === Get Nodejs pinned LTS version via NVM
diff --git a/docker/windows/base/build/Dockerfile b/docker/windows/base/build/Dockerfile index 19aa9b6..6e76594 100644 --- a/docker/windows/base/build/Dockerfile +++ b/docker/windows/base/build/Dockerfile
@@ -29,10 +29,14 @@ # of the execution, i.e. the full invocation string. COPY ./list_python_processes.py /list_python_processes.py +# Pin Choco to 1.4.0 to avoid required reboot in 2.0.0 +ENV chocolateyVersion '1.4.0' # Install deps via chocolatey. RUN iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'));` mkdir C:\choco-cache;` - choco install -y -c C:\choco-cache python3 -ia '/quiet InstallAllUsers=1 PrependPath=1 TargetDir="C:\Python3"';` + # Note: We pinned python 3.11.0 version here because 3.11.2 has a regression + # where the install arguments like TargetDir were being ignored. + choco install -y -c C:\choco-cache python3 --version 3.11.0 -ia '/quiet InstallAllUsers=1 PrependPath=1 TargetDir="C:\Python3"';` choco install -y -c C:\choco-cache winflexbison3 --params '/InstallDir:C:\bison';` choco install -y -c C:\choco-cache ninja;` choco install -y -c C:\choco-cache nodejs-lts;`
diff --git a/docker/windows/runner/Dockerfile b/docker/windows/runner/Dockerfile new file mode 100644 index 0000000..a494811 --- /dev/null +++ b/docker/windows/runner/Dockerfile
@@ -0,0 +1,34 @@ +# 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. +FROM cobalt-build-win32-base + +SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';$ProgressPreference='silentlyContinue';"] + +ARG RUNNER_VERSION + +RUN Invoke-WebRequest -Uri 'https://aka.ms/install-powershell.ps1' -OutFile install-powershell.ps1; \ + powershell -ExecutionPolicy Unrestricted -File ./install-powershell.ps1 -AddToPath + +RUN Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v$env:RUNNER_VERSION/actions-runner-win-x64-$env:RUNNER_VERSION.zip -OutFile runner.zip; \ + Expand-Archive -Path C:/runner.zip -DestinationPath C:/actions-runner; \ + Remove-Item -Path C:\runner.zip; \ + setx /M PATH $(${Env:PATH} + \";${Env:ProgramFiles}\Git\bin\") + +# Required for packaging artifacts. +RUN choco install -f -y 7zip --version=19.0 + +ENV TMP "C:\Users\ContainerAdministrator\AppData\Local\Temp2" + +ADD runner.ps1 C:/runner.ps1 +CMD ["pwsh", "-ExecutionPolicy", "Unrestricted", "-File", ".\\runner.ps1"]
diff --git a/docker/windows/runner/runner.ps1 b/docker/windows/runner/runner.ps1 new file mode 100644 index 0000000..9e96a5e --- /dev/null +++ b/docker/windows/runner/runner.ps1
@@ -0,0 +1,2 @@ +.\actions-runner\config.cmd --unattended --replace --url https://github.com/${env:RUNNER_REPO} --token $env:RUNNER_TOKEN --name $env:RUNNER_NAME --work $env:RUNNER_WORKDIR --labels $env:RUNNER_LABELS; +.\actions-runner\run.cmd;
diff --git a/net/url_request/url_fetcher_core.cc b/net/url_request/url_fetcher_core.cc index 0d77b78..6835662 100644 --- a/net/url_request/url_fetcher_core.cc +++ b/net/url_request/url_fetcher_core.cc
@@ -175,8 +175,9 @@ } void URLFetcherCore::Stop() { - if (delegate_task_runner_) // May be NULL in tests. + if (delegate_task_runner_) { // May be NULL in tests. DCHECK(delegate_task_runner_->RunsTasksInCurrentSequence()); + } delegate_ = NULL; fetcher_ = NULL; @@ -783,8 +784,11 @@ if (!extra_request_headers_.IsEmpty()) request_->SetExtraRequestHeaders(extra_request_headers_); +#if defined(STARBOARD) request_->SetLoadTimingInfoCallback(base::Bind(&URLFetcherCore::GetLoadTimingInfo, base::Unretained(this))); +#endif + request_->Start(); } @@ -1133,6 +1137,14 @@ #if defined(STARBOARD) void URLFetcherCore::GetLoadTimingInfo( const net::LoadTimingInfo& timing_info) { + delegate_task_runner_->PostTask( + FROM_HERE, + base::Bind(&URLFetcherCore::GetLoadTimingInfoInDelegateThread, + this, timing_info)); +} + +void URLFetcherCore::GetLoadTimingInfoInDelegateThread( + const net::LoadTimingInfo& timing_info) { // Check if the URLFetcherCore has been stopped before. if (delegate_) { delegate_->ReportLoadTimingInfo(timing_info);
diff --git a/net/url_request/url_fetcher_core.h b/net/url_request/url_fetcher_core.h index 02048d1..cfbd717 100644 --- a/net/url_request/url_fetcher_core.h +++ b/net/url_request/url_fetcher_core.h
@@ -165,6 +165,7 @@ static void SetIgnoreCertificateRequests(bool ignored); #if defined (STARBOARD) void GetLoadTimingInfo(const net::LoadTimingInfo& timing_info); + void GetLoadTimingInfoInDelegateThread(const net::LoadTimingInfo& timing_info); #endif // defined(STARBOARD) private: friend class base::RefCountedThreadSafe<URLFetcherCore>;
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 1478b37..7efa465 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
@@ -498,6 +498,9 @@ return; } nativeOnMediaCodecOutputFormatChanged(mNativeMediaCodecBridge); + if (mFrameRateEstimator != null) { + mFrameRateEstimator.reset(); + } } } };
diff --git a/starboard/common/media.cc b/starboard/common/media.cc index eb74ac8..f9cdac0 100644 --- a/starboard/common/media.cc +++ b/starboard/common/media.cc
@@ -639,6 +639,55 @@ return "invalid"; } +const char* GetMediaAudioConnectorName(SbMediaAudioConnector connector) { + switch (connector) { + case kSbMediaAudioConnectorNone: + return "none"; + case kSbMediaAudioConnectorAnalog: + return "analog"; + case kSbMediaAudioConnectorBluetooth: + return "bluetooth"; + case kSbMediaAudioConnectorHdmi: + return "hdmi"; + case kSbMediaAudioConnectorNetwork: + return "network"; + case kSbMediaAudioConnectorSpdif: + return "spdif"; + case kSbMediaAudioConnectorUsb: + return "usb"; + } + SB_NOTREACHED(); + return "invalid"; +} + +const char* GetCobaltExtensionMediaAudioConnectorName( + CobaltExtensionMediaAudioConnector connector) { + switch (connector) { + case kCobaltExtensionMediaAudioConnectorUnknown: + return "unknown"; + case kCobaltExtensionMediaAudioConnectorAnalog: + return "analog"; + case kCobaltExtensionMediaAudioConnectorBluetooth: + return "bluetooth"; + case kCobaltExtensionMediaAudioConnectorBuiltIn: + return "builtin"; + case kCobaltExtensionMediaAudioConnectorHdmi: + return "hdmi"; + case kCobaltExtensionMediaAudioConnectorRemoteWired: + return "remote-wired"; + case kCobaltExtensionMediaAudioConnectorRemoteWireless: + return "remote-wireless"; + case kCobaltExtensionMediaAudioConnectorRemoteOther: + return "remote-other"; + case kCobaltExtensionMediaAudioConnectorSpdif: + return "spdif"; + case kCobaltExtensionMediaAudioConnectorUsb: + return "usb"; + } + SB_NOTREACHED(); + return "invalid"; +} + const char* GetMediaPrimaryIdName(SbMediaPrimaryId primary_id) { switch (primary_id) { case kSbMediaPrimaryIdReserved0:
diff --git a/starboard/common/media.h b/starboard/common/media.h index a51641e..401551d 100644 --- a/starboard/common/media.h +++ b/starboard/common/media.h
@@ -17,12 +17,16 @@ #include <ostream> +#include "cobalt/extension/audio_write_ahead.h" #include "starboard/media.h" namespace starboard { const char* GetMediaAudioCodecName(SbMediaAudioCodec codec); const char* GetMediaVideoCodecName(SbMediaVideoCodec codec); +const char* GetMediaAudioConnectorName(SbMediaAudioConnector connector); +const char* GetCobaltExtensionMediaAudioConnectorName( + CobaltExtensionMediaAudioConnector connector); const char* GetMediaPrimaryIdName(SbMediaPrimaryId primary_id); const char* GetMediaTransferIdName(SbMediaTransferId transfer_id); const char* GetMediaMatrixIdName(SbMediaMatrixId matrix_id);
diff --git a/starboard/doc/evergreen/cobalt_evergreen_overview.md b/starboard/doc/evergreen/cobalt_evergreen_overview.md index cc21fa2..e3ce087 100644 --- a/starboard/doc/evergreen/cobalt_evergreen_overview.md +++ b/starboard/doc/evergreen/cobalt_evergreen_overview.md
@@ -90,38 +90,43 @@ ### Building Cobalt Evergreen Components -Cobalt Evergreen requires that there are two separate build(`gyp`) +Cobalt Evergreen requires that there are two separate build configurations used due to the separation of the Cobalt core(`libcobalt.so`) and -the platform-specific Starboard layer(`loader_app`). As a result, you will have -to initiate a separate gyp process for each. This is required since the Cobalt -core binary is built with the Google toolchain settings and the -platform-specific Starboard layer is built with partner toolchain -configurations. +the platform-specific layers. As a result, you will have to run `gen gn` +separately for each. This is required since the Cobalt core binary is built +with the Google toolchain settings and the components in the platform-specific +Starboard layer are built with partner toolchain configurations. -Cobalt Evergreen is built by a separate gyp platform using the Google toolchain: +Cobalt Evergreen is built by a separate GN platform using the Google toolchain. +For example: ``` -$ cobalt/build/gn.py -p evergreen-arm-softfp -c qa -$ ninja -C out/evergreen-arm-softfp_qa cobalt +$ gn gen out/evergreen-arm-softfp_qa --args="target_platform=\"evergreen-arm-softfp\" build_type=\"qa\" target_cpu=\"arm\" target_os=\"linux\" sb_api_version=14" +$ ninja -C out/evergreen-arm-softfp_qa cobalt_install ``` Which produces a shared library `libcobalt.so` targeted for specific architecture, ABI and Starboard version. -The gyp variable `sb_evergreen` is set to 1 when building `libcobalt.so`. +The GN variable `sb_is_evergreen` is set to `true` when building `libcobalt.so`. + +Finally, note that these instructions are for producing a local build of +`libcobalt.so`. For submissions to YTS and deployments to users' devices, an +official release build of `libcobalt.so` should be downloaded from GitHub. The partner port of Starboard is built with the partner’s toolchain and is linked into the `loader_app` which knows how to dynamically load `libcobalt.so`, and the `crashpad_handler` which handles crashes. ``` -$ cobalt/build/gn.py -p <partner_port_name> -c qa +$ gn gen out/<partner_port_name>_qa --args='target_platform="<partner_port_name>" build_type="qa" sb_api_version=14' $ ninja -C out/<partner_port_name>_qa loader_app crashpad_handler ``` -Partners should set `sb_evergreen_compatible` to 1 in their gyp platform config. -DO NOT set the `sb_evergreen` to 1 in your platform-specific configuration as it -is used only by Cobalt when building with the Google toolchain. +Partners should set `sb_is_evergreen_compatible` to `true` in their GN platform +config. DO NOT set the `sb_is_evergreen` to `true` in your platform-specific +configuration as it is used only by Cobalt when building with the Google +toolchain. Additionally, partners should install crash handlers as instructed in the [Installing Crash Handlers for Cobalt guide](../crash_handlers.md).
diff --git a/starboard/linux/shared/BUILD.gn b/starboard/linux/shared/BUILD.gn index f097404..f313fde 100644 --- a/starboard/linux/shared/BUILD.gn +++ b/starboard/linux/shared/BUILD.gn
@@ -56,6 +56,10 @@ sources = [ "//starboard/linux/shared/atomic_public.h", "//starboard/linux/shared/audio_sink_type_dispatcher.cc", + "//starboard/linux/shared/audio_write_ahead.cc", + "//starboard/linux/shared/audio_write_ahead.h", + "//starboard/linux/shared/audio_write_ahead_player_get_audio_configuration.cc", + "//starboard/linux/shared/audio_write_ahead_player_get_audio_configuration.h", "//starboard/linux/shared/configuration.cc", "//starboard/linux/shared/configuration.h", "//starboard/linux/shared/configuration_constants.cc", @@ -64,6 +68,8 @@ "//starboard/linux/shared/decode_target_internal.cc", "//starboard/linux/shared/decode_target_internal.h", "//starboard/linux/shared/decode_target_release.cc", + "//starboard/linux/shared/ifa.cc", + "//starboard/linux/shared/ifa.h", "//starboard/linux/shared/media_is_audio_supported.cc", "//starboard/linux/shared/media_is_video_supported.cc", "//starboard/linux/shared/netlink.cc",
diff --git a/starboard/linux/shared/audio_write_ahead.cc b/starboard/linux/shared/audio_write_ahead.cc new file mode 100644 index 0000000..ffcf1f1 --- /dev/null +++ b/starboard/linux/shared/audio_write_ahead.cc
@@ -0,0 +1,43 @@ +// Copyright 2023 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/linux/shared/audio_write_ahead.h" + +#include "cobalt/extension/audio_write_ahead.h" +#include "starboard/common/log.h" +#include "starboard/linux/shared/audio_write_ahead_player_get_audio_configuration.h" + +// Omit namespace linux due to symbol name conflict. + +namespace starboard { +namespace shared { + +namespace { + +const CobaltExtensionConfigurableAudioWriteAheadApi + kConfigurableAudioWriteAheadApi = { + kCobaltExtensionConfigurableAudioWriteAheadName, + 1, + &ConfigurableAudioWriteAheadPlayerGetAudioConfiguration, +}; + +} // namespace + +const void* GetConfigurableAudioWriteAheadApi() { + SB_LOG(INFO) << "Audio write ahead extension enabled."; + return &kConfigurableAudioWriteAheadApi; +} + +} // namespace shared +} // namespace starboard
diff --git a/starboard/linux/shared/audio_write_ahead.h b/starboard/linux/shared/audio_write_ahead.h new file mode 100644 index 0000000..15d19e8 --- /dev/null +++ b/starboard/linux/shared/audio_write_ahead.h
@@ -0,0 +1,28 @@ +// Copyright 2023 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_AUDIO_WRITE_AHEAD_H_ +#define STARBOARD_SHARED_AUDIO_WRITE_AHEAD_H_ + +// Omit namespace linux due to symbol name conflict. + +namespace starboard { +namespace shared { + +const void* GetConfigurableAudioWriteAheadApi(); + +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_AUDIO_WRITE_AHEAD_H_
diff --git a/starboard/linux/shared/audio_write_ahead_player_get_audio_configuration.cc b/starboard/linux/shared/audio_write_ahead_player_get_audio_configuration.cc new file mode 100644 index 0000000..c2e5f00 --- /dev/null +++ b/starboard/linux/shared/audio_write_ahead_player_get_audio_configuration.cc
@@ -0,0 +1,56 @@ +// Copyright 2023 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/linux/shared/audio_write_ahead_player_get_audio_configuration.h" + +#include <string> + +#include "starboard/audio_sink.h" +#include "starboard/common/log.h" +#include "starboard/system.h" + +// Omit namespace linux due to symbol name conflict. + +namespace starboard { +namespace shared { + +bool ConfigurableAudioWriteAheadPlayerGetAudioConfiguration( + SbPlayer player, + int index, + CobaltExtensionMediaAudioConfiguration* out_audio_configuration) { + SB_DCHECK(SbPlayerIsValid(player)); + SB_DCHECK(index >= 0); + SB_DCHECK(out_audio_configuration); + + if (index > 0) { + // We assume that |player| only uses the primary (index 0) audio output. + // For playbacks using more than one audio outputs, or using audio outputs + // other than the primary one, the platform should have its own + // `SbPlayerGetAudioConfiguration()` implementation. + return false; + } + + *out_audio_configuration = {}; + + out_audio_configuration->connector = + kCobaltExtensionMediaAudioConnectorUnknown; + out_audio_configuration->latency = 0; + out_audio_configuration->coding_type = kSbMediaAudioCodingTypePcm; + out_audio_configuration->number_of_channels = SbAudioSinkGetMaxChannels(); + + return true; +} + +} // namespace shared +} // namespace starboard
diff --git a/starboard/linux/shared/audio_write_ahead_player_get_audio_configuration.h b/starboard/linux/shared/audio_write_ahead_player_get_audio_configuration.h new file mode 100644 index 0000000..08126df --- /dev/null +++ b/starboard/linux/shared/audio_write_ahead_player_get_audio_configuration.h
@@ -0,0 +1,35 @@ +// Copyright 2023 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_AUDIO_WRITE_AHEAD_PLAYER_GET_AUDIO_CONFIGURATION_H_ +#define STARBOARD_SHARED_AUDIO_WRITE_AHEAD_PLAYER_GET_AUDIO_CONFIGURATION_H_ + +#include "cobalt/extension/audio_write_ahead.h" + +#include "starboard/player.h" + +// Omit namespace linux due to symbol name conflict. + +namespace starboard { +namespace shared { + +bool ConfigurableAudioWriteAheadPlayerGetAudioConfiguration( + SbPlayer player, + int index, + CobaltExtensionMediaAudioConfiguration* out_audio_configuration); + +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_SHARED_AUDIO_WRITE_AHEAD_PLAYER_GET_AUDIO_CONFIGURATION_H_
diff --git a/starboard/linux/shared/ifa.cc b/starboard/linux/shared/ifa.cc new file mode 100644 index 0000000..548f9bb --- /dev/null +++ b/starboard/linux/shared/ifa.cc
@@ -0,0 +1,65 @@ +// Copyright 2023 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/linux/shared/ifa.h" + +#include "cobalt/extension/ifa.h" + +#include "starboard/common/string.h" +#include "starboard/shared/environment.h" + +namespace starboard { +namespace shared { + +namespace { + +bool CopyStringAndTestIfSuccess(char* out_value, + int value_length, + const char* from_value) { + if (strlen(from_value) + 1 > value_length) + return false; + starboard::strlcpy(out_value, from_value, value_length); + return true; +} + +// Definitions of any functions included as components in the extension +// are added here. + +bool GetAdvertisingId(char* out_value, int value_length) { + return CopyStringAndTestIfSuccess( + out_value, value_length, + starboard::GetEnvironment("COBALT_ADVERTISING_ID").c_str()); +} + +bool GetLimitAdTracking(char* out_value, int value_length) { + return CopyStringAndTestIfSuccess( + out_value, value_length, + GetEnvironment("COBALT_LIMIT_AD_TRACKING").c_str()); +} + +const StarboardExtensionIfaApi kIfaApi = { + kStarboardExtensionIfaName, + 1, // API version that's implemented. + &GetAdvertisingId, + &GetLimitAdTracking, +}; + +} // namespace + +const void* GetIfaApi() { + return &kIfaApi; +} + +} // namespace shared +} // namespace starboard
diff --git a/starboard/linux/shared/ifa.h b/starboard/linux/shared/ifa.h new file mode 100644 index 0000000..fbe61ab --- /dev/null +++ b/starboard/linux/shared/ifa.h
@@ -0,0 +1,27 @@ +// Copyright 2023 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_LINUX_SHARED_IFA_H_ +#define STARBOARD_LINUX_SHARED_IFA_H_ + +// Omit namespace linux due to symbol name conflict. +namespace starboard { +namespace shared { + +const void* GetIfaApi(); + +} // namespace shared +} // namespace starboard + +#endif // STARBOARD_LINUX_SHARED_IFA_H_
diff --git a/starboard/linux/shared/system_get_extensions.cc b/starboard/linux/shared/system_get_extensions.cc index fba44bd..f6a7e8d 100644 --- a/starboard/linux/shared/system_get_extensions.cc +++ b/starboard/linux/shared/system_get_extensions.cc
@@ -14,13 +14,16 @@ #include "starboard/system.h" +#include "cobalt/extension/audio_write_ahead.h" #include "cobalt/extension/configuration.h" #include "cobalt/extension/crash_handler.h" #include "cobalt/extension/demuxer.h" #include "cobalt/extension/free_space.h" +#include "cobalt/extension/ifa.h" #include "cobalt/extension/memory_mapped_file.h" #include "cobalt/extension/platform_service.h" #include "starboard/common/string.h" +#include "starboard/linux/shared/ifa.h" #include "starboard/linux/shared/soft_mic_platform_service.h" #include "starboard/shared/ffmpeg/ffmpeg_demuxer.h" #include "starboard/shared/posix/free_space.h" @@ -30,6 +33,7 @@ #if SB_IS(EVERGREEN_COMPATIBLE) #include "starboard/elf_loader/evergreen_config.h" #endif +#include "starboard/linux/shared/audio_write_ahead.h" #include "starboard/linux/shared/configuration.h" const void* SbSystemGetExtension(const char* name) { @@ -59,6 +63,9 @@ if (strcmp(name, kCobaltExtensionFreeSpaceName) == 0) { return starboard::shared::posix::GetFreeSpaceApi(); } + if (strcmp(name, kCobaltExtensionConfigurableAudioWriteAheadName) == 0) { + return starboard::shared::GetConfigurableAudioWriteAheadApi(); + } if (strcmp(name, kCobaltExtensionDemuxerApi) == 0) { auto command_line = starboard::shared::starboard::Application::Get()->GetCommandLine(); @@ -67,5 +74,10 @@ return use_ffmpeg_demuxer ? starboard::shared::ffmpeg::GetFFmpegDemuxerApi() : NULL; } +#if SB_API_VERSION < 14 + if (strcmp(name, kStarboardExtensionIfaName) == 0) { + return starboard::shared::GetIfaApi(); + } +#endif // SB_API_VERSION < 14 return NULL; }
diff --git a/starboard/shared/win32/video_decoder.cc b/starboard/shared/win32/video_decoder.cc index fe58e7b..dd588a3 100644 --- a/starboard/shared/win32/video_decoder.cc +++ b/starboard/shared/win32/video_decoder.cc
@@ -468,8 +468,9 @@ ComPtr<IMFAttributes> attributes = transform->GetAttributes(); SB_DCHECK(attributes); - CheckResult(attributes->SetUINT32(MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT, - kMaxOutputSamples)); + CheckResult( + attributes->SetUINT32(MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT, + static_cast<UINT32>(GetMaxNumberOfCachedFrames()))); UpdateVideoArea(transform->GetCurrentOutputType()); @@ -704,7 +705,7 @@ // MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT. thread_lock_.Acquire(); bool input_full = thread_events_.size() >= kMaxInputSamples; - bool output_full = thread_outputs_.size() >= kMaxOutputSamples; + bool output_full = thread_outputs_.size() >= GetMaxNumberOfCachedFrames(); thread_lock_.Release(); Status status = input_full ? kBufferFull : kNeedMoreInput;
diff --git a/third_party/angle/include/GLSLANG/ShaderVars.h b/third_party/angle/include/GLSLANG/ShaderVars.h index 52f6ad0..5d2d146 100644 --- a/third_party/angle/include/GLSLANG/ShaderVars.h +++ b/third_party/angle/include/GLSLANG/ShaderVars.h
@@ -12,6 +12,7 @@ #include <algorithm> #include <array> +#include <cstdint> #include <string> #include <vector>
diff --git a/third_party/angle/src/common/angleutils.h b/third_party/angle/src/common/angleutils.h index 3a1391e..c8d36f5 100644 --- a/third_party/angle/src/common/angleutils.h +++ b/third_party/angle/src/common/angleutils.h
@@ -14,6 +14,7 @@ #include <climits> #include <cstdarg> #include <cstddef> +#include <cstdint> #include <set> #include <sstream> #include <string>
diff --git a/third_party/chromium/media/filters/chunk_demuxer.cc b/third_party/chromium/media/filters/chunk_demuxer.cc index 1d6e475..2b729a4 100644 --- a/third_party/chromium/media/filters/chunk_demuxer.cc +++ b/third_party/chromium/media/filters/chunk_demuxer.cc
@@ -180,6 +180,9 @@ DCHECK(state_ == UNINITIALIZED || state_ == RETURNING_ABORT_FOR_READS) << state_; +#if defined(STARBOARD) + write_head_ = time; +#endif // defined (STARBOARD) stream_->Seek(time); } @@ -225,6 +228,15 @@ return stream_->GarbageCollectIfNeeded(media_time, newDataSize); } +#if defined(STARBOARD) + +base::TimeDelta ChunkDemuxerStream::GetWriteHead() const { + base::AutoLock auto_lock(lock_); + return write_head_; +} + +#endif // defined(STARBOARD) + void ChunkDemuxerStream::OnMemoryPressure( base::TimeDelta media_time, base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, @@ -473,6 +485,7 @@ DemuxerStream::Status status = DemuxerStream::kAborted; std::vector<scoped_refptr<DecoderBuffer>> buffers; + base::TimeDelta write_head = write_head_; if (pending_config_change_) { status = kConfigChanged; @@ -491,6 +504,7 @@ << buffer->timestamp().InSecondsF() << ", dur " << buffer->duration().InSecondsF() << ", key " << buffer->is_key_frame(); + write_head = std::max(write_head, buffer->timestamp()); buffers.push_back(std::move(buffer)); status = DemuxerStream::kOk; } else if (stream_status == SourceBufferStreamStatus::kEndOfStream) { @@ -529,6 +543,8 @@ return; } + DCHECK_LE(write_head_, write_head); + write_head_ = write_head; std::move(read_cb_).Run(status, buffers); } #else // defined(STARBOARD) @@ -1077,6 +1093,25 @@ return itr->second->EvictCodedFrames(currentMediaTime, newDataSize); } +#if defined(STARBOARD) + +base::TimeDelta ChunkDemuxer::GetWriteHead(const std::string& id) const { + base::AutoLock auto_lock(lock_); + DCHECK(IsValidId(id)); + + auto iter = id_to_streams_map_.find(id); + if (iter == id_to_streams_map_.end() || + iter->second.empty()) { + // Handled just in case. + SB_NOTREACHED(); + return base::TimeDelta(); + } + + return iter->second[0]->GetWriteHead(); +} + +#endif // defined(STARBOARD) + bool ChunkDemuxer::AppendData(const std::string& id, const uint8_t* data, size_t length,
diff --git a/third_party/chromium/media/filters/chunk_demuxer.h b/third_party/chromium/media/filters/chunk_demuxer.h index fd67e6f..2e71291 100644 --- a/third_party/chromium/media/filters/chunk_demuxer.h +++ b/third_party/chromium/media/filters/chunk_demuxer.h
@@ -79,6 +79,10 @@ // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-eviction bool EvictCodedFrames(base::TimeDelta media_time, size_t newDataSize); +#if defined(STARBOARD) + base::TimeDelta GetWriteHead() const; +#endif // defined(STARBOARD) + void OnMemoryPressure( base::TimeDelta media_time, base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level, @@ -207,6 +211,9 @@ State state_ GUARDED_BY(lock_); ReadCB read_cb_ GUARDED_BY(lock_); bool is_enabled_ GUARDED_BY(lock_); +#if defined(STARBOARD) + base::TimeDelta write_head_ GUARDED_BY(lock_); +#endif // defined(STARBOARD) DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); }; @@ -383,6 +390,10 @@ base::TimeDelta currentMediaTime, size_t newDataSize) WARN_UNUSED_RESULT; +#if defined(STARBOARD) + base::TimeDelta GetWriteHead(const std::string& id) const; +#endif // defined(STARBOARD) + void OnMemoryPressure( base::TimeDelta currentMediaTime, base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level,
diff --git a/third_party/crashpad/util/misc/reinterpret_bytes.cc b/third_party/crashpad/util/misc/reinterpret_bytes.cc index 65ec33f..3fee722 100644 --- a/third_party/crashpad/util/misc/reinterpret_bytes.cc +++ b/third_party/crashpad/util/misc/reinterpret_bytes.cc
@@ -17,6 +17,7 @@ #include <string.h> #include <algorithm> +#include <cstdint> #include "base/logging.h"
diff --git a/third_party/devtools/front_end/cobalt/cobalt.js b/third_party/devtools/front_end/cobalt/cobalt.js index a085c62..c65ff59 100644 --- a/third_party/devtools/front_end/cobalt/cobalt.js +++ b/third_party/devtools/front_end/cobalt/cobalt.js
@@ -14,6 +14,7 @@ this._target = UI.context.flavor(SDK.Target); this._runtimeAgent = this._target.runtimeAgent(); + this._cobaltAgent = this._target.cobaltAgent(); this.element = this._shadowRoot.createChild('div'); this.element.textContent = 'Cobalt Console'; @@ -32,7 +33,7 @@ this.run(`(function() { window.h5vcc.traceEvent.stop();})()`); console.log("Stopped Trace"); })); - trace_files.forEach( (file) => { + trace_files.forEach((file) => { traceContainer.appendChild(UI.createTextButton(Common.UIString('Download ' + file[0]), event => { console.log("Download Trace"); const filename = file[1]; @@ -46,6 +47,39 @@ }); })); }); + const debugLogContainer = this.element.createChild('div', 'debug-log-container'); + debugLogContainer.appendChild(UI.createTextButton(Common.UIString('DebugLog On'), event => { + this._cobaltAgent.invoke_sendConsoleCommand({ + command: 'debug_log', message: 'on' + }); + })); + debugLogContainer.appendChild(UI.createTextButton(Common.UIString('DebugLog Off'), event => { + this._cobaltAgent.invoke_sendConsoleCommand({ + command: 'debug_log', message: 'off' + }); + })); + const consoleContainer = this.element.createChild('div', 'console-container'); + consoleContainer.appendChild(UI.createTextButton(Common.UIString('DebugCommand'), event => { + const outputElement = document.getElementsByClassName('console-output')[0]; + const command = document.getElementsByClassName('debug-command')[0].value; + if (command.length == 0) { + const result = this._cobaltAgent.invoke_getConsoleCommands().then(result => { + outputElement.innerHTML = JSON.stringify(result.commands, undefined, 2); + }); + } else { + const result = this._cobaltAgent.invoke_sendConsoleCommand({ + command: command, + message: document.getElementsByClassName('debug-message')[0].value + }).then(result => { + outputElement.innerHTML = JSON.stringify(result, undefined, 2); + }); + } + })); + consoleContainer.appendChild(UI.createLabel('Command:')); + consoleContainer.appendChild(UI.createInput('debug-command', 'text')); + consoleContainer.appendChild(UI.createLabel('Message:')); + consoleContainer.appendChild(UI.createInput('debug-message', 'text')); + consoleContainer.createChild('pre', 'console-output'); } async run(expression) {
diff --git a/third_party/devtools/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/devtools/third_party/blink/public/devtools_protocol/browser_protocol.pdl index 0091c32..e98162f 100644 --- a/third_party/devtools/third_party/blink/public/devtools_protocol/browser_protocol.pdl +++ b/third_party/devtools/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -8,6 +8,27 @@ major 1 minor 3 +# The Cobalt domain defines custom methods and events for Cobalt +experimental domain Cobalt + command disable + + command enable + + experimental type ConsoleCommand extends object + properties + string command + string shortHelp + string longHelp + + experimental command getConsoleCommands + returns + array of ConsoleCommand commands + + experimental command sendConsoleCommand + parameters + string command + optional string message + experimental domain Accessibility depends on DOM
diff --git a/third_party/v8/src/base/logging.h b/third_party/v8/src/base/logging.h index fe39f98..dbe1305 100644 --- a/third_party/v8/src/base/logging.h +++ b/third_party/v8/src/base/logging.h
@@ -5,6 +5,7 @@ #ifndef V8_BASE_LOGGING_H_ #define V8_BASE_LOGGING_H_ +#include <cstdint> #include <cstring> #include <sstream> #include <string>
diff --git a/third_party/v8/src/base/macros.h b/third_party/v8/src/base/macros.h index 515a9e3..a7cd7c0 100644 --- a/third_party/v8/src/base/macros.h +++ b/third_party/v8/src/base/macros.h
@@ -5,6 +5,7 @@ #ifndef V8_BASE_MACROS_H_ #define V8_BASE_MACROS_H_ +#include <cstdint> #include <limits> #include <type_traits>
diff --git a/third_party/v8/src/inspector/v8-string-conversions.h b/third_party/v8/src/inspector/v8-string-conversions.h index c1d69c1..eb33c68 100644 --- a/third_party/v8/src/inspector/v8-string-conversions.h +++ b/third_party/v8/src/inspector/v8-string-conversions.h
@@ -5,6 +5,7 @@ #ifndef V8_INSPECTOR_V8_STRING_CONVERSIONS_H_ #define V8_INSPECTOR_V8_STRING_CONVERSIONS_H_ +#include <cstdint> #include <string> // Conversion routines between UT8 and UTF16, used by string-16.{h,cc}. You may
diff --git a/tools/copy_and_filter_out_dir.py b/tools/copy_and_filter_out_dir.py index 8e01bf8..080a69f 100755 --- a/tools/copy_and_filter_out_dir.py +++ b/tools/copy_and_filter_out_dir.py
@@ -102,6 +102,12 @@ return 0 _IterateAndFilter(source_out_dir, dest_out_dir, copies) + + # Add build_info json to the out directory root. + source_build_info = os.path.join(source_out_dir, 'gen', 'build_info.json') + dest_build_info = os.path.join(dest_out_dir, 'build_info.json') + copies[source_build_info] = dest_build_info + for source, dest in copies.items(): dirname = os.path.dirname(dest) if not os.path.exists(dirname):
diff --git a/tools/lz4_compress/lz4_compress.cc b/tools/lz4_compress/lz4_compress.cc index 26421dc..08a79f5 100644 --- a/tools/lz4_compress/lz4_compress.cc +++ b/tools/lz4_compress/lz4_compress.cc
@@ -22,6 +22,7 @@ #include <stdio.h> +#include <cstdint> #include <iostream> #include <vector>