Import Cobalt 24.lts.20.1032766
diff --git a/.github/config/raspi-2-skia.json b/.github/config/raspi-2-skia.json
index f99fb73..e83ea1f 100644
--- a/.github/config/raspi-2-skia.json
+++ b/.github/config/raspi-2-skia.json
@@ -9,7 +9,7 @@
"platform":"raspi-2-skia",
"target_platform":"raspi-2-skia",
"target_cpu":"target_cpu=\\\"arm\\\"",
- "extra_gn_arguments": "is_clang=false"
+ "extra_gn_arguments": "build_with_separate_cobalt_toolchain=true use_asan=false"
}
]
}
diff --git a/.github/config/raspi-2.json b/.github/config/raspi-2.json
index ab2a1e6..9008437 100644
--- a/.github/config/raspi-2.json
+++ b/.github/config/raspi-2.json
@@ -14,8 +14,6 @@
},
"platforms": [
"raspi-2",
- "raspi-2-sbversion-13",
- "raspi-2-sbversion-14",
"raspi-2-sbversion-15"
],
"includes": [
@@ -24,25 +22,7 @@
"platform":"raspi-2",
"target_platform":"raspi-2",
"target_cpu":"target_cpu=\\\"arm\\\"",
- "extra_gn_arguments": "is_clang=false",
- "dimension": "release_version=regex:10.*"
- },
- {
- "name":"sbversion-13",
- "platform":"raspi-2-sbversion-13",
- "target_platform":"raspi-2",
- "target_cpu":"target_cpu=\\\"arm\\\"",
- "extra_gn_arguments":"is_clang=false",
- "sb_api_version": "sb_api_version=13",
- "dimension": "release_version=regex:10.*"
- },
- {
- "name":"sbversion-14",
- "platform":"raspi-2-sbversion-14",
- "target_platform":"raspi-2",
- "target_cpu":"target_cpu=\\\"arm\\\"",
- "extra_gn_arguments":"is_clang=false",
- "sb_api_version": "sb_api_version=14",
+ "extra_gn_arguments": "build_with_separate_cobalt_toolchain=true use_asan=false",
"dimension": "release_version=regex:10.*"
},
{
@@ -50,7 +30,7 @@
"platform":"raspi-2-sbversion-15",
"target_platform":"raspi-2",
"target_cpu":"target_cpu=\\\"arm\\\"",
- "extra_gn_arguments":"is_clang=false",
+ "extra_gn_arguments": "build_with_separate_cobalt_toolchain=true use_asan=false",
"sb_api_version": "sb_api_version=15",
"dimension": "release_version=regex:10.*"
}
diff --git a/base/android/jni_generator/AndroidManifest.xml b/base/android/jni_generator/AndroidManifest.xml
index 1555a81..aa0b7c5 100644
--- a/base/android/jni_generator/AndroidManifest.xml
+++ b/base/android/jni_generator/AndroidManifest.xml
@@ -7,7 +7,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.jni.generator">
- <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="33" />
+ <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="34" />
<application></application>
</manifest>
diff --git a/base/logging.cc b/base/logging.cc
index 809e141..5a159ff 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -478,9 +478,15 @@
case LOG_ERROR:
return kSbLogPriorityError;
case LOG_FATAL:
- case LOG_VERBOSE:
return kSbLogPriorityFatal;
+ case LOG_VERBOSE:
default:
+ if (level <= LOG_VERBOSE) {
+ // Verbose level can be any negative integer, sanity check its range to
+ // filter out potential errors.
+ DCHECK_GE(level, -256);
+ return kSbLogPriorityInfo;
+ }
NOTREACHED() << "Unrecognized log level.";
return kSbLogPriorityInfo;
}
diff --git a/cobalt/audio/audio_device.cc b/cobalt/audio/audio_device.cc
index 7aae3b4..874d1ba 100644
--- a/cobalt/audio/audio_device.cc
+++ b/cobalt/audio/audio_device.cc
@@ -32,6 +32,11 @@
namespace {
const int kRenderBufferSizeFrames = 1024;
const int kDefaultFramesPerChannel = 8 * kRenderBufferSizeFrames;
+
+int AlignUp(int value, int alignment) {
+ int decremented_value = value - 1;
+ return decremented_value + alignment - (decremented_value % alignment);
+}
} // namespace
class AudioDevice::Impl {
@@ -85,11 +90,13 @@
: number_of_channels_(number_of_channels),
output_sample_type_(GetPreferredOutputStarboardSampleType()),
render_callback_(callback),
- frames_per_channel_(std::max(SbAudioSinkGetMinBufferSizeInFrames(
- number_of_channels, output_sample_type_,
- kStandardOutputSampleRate) +
- kRenderBufferSizeFrames * 2,
- kDefaultFramesPerChannel)),
+ frames_per_channel_(
+ std::max(AlignUp(SbAudioSinkGetMinBufferSizeInFrames(
+ number_of_channels, output_sample_type_,
+ kStandardOutputSampleRate) +
+ kRenderBufferSizeFrames * 2,
+ kRenderBufferSizeFrames),
+ kDefaultFramesPerChannel)),
input_audio_bus_(static_cast<size_t>(number_of_channels),
static_cast<size_t>(kRenderBufferSizeFrames),
GetPreferredOutputSampleType(), AudioBus::kPlanar),
@@ -99,6 +106,7 @@
DCHECK(number_of_channels_ == 1 || number_of_channels_ == 2)
<< "Invalid number of channels: " << number_of_channels_;
DCHECK(render_callback_);
+ DCHECK(frames_per_channel_ % kRenderBufferSizeFrames == 0);
DCHECK(SbAudioSinkIsAudioFrameStorageTypeSupported(
kSbMediaAudioFrameStorageTypeInterleaved))
<< "Only interleaved frame storage is supported.";
diff --git a/cobalt/base/BUILD.gn b/cobalt/base/BUILD.gn
index a619003..f2652cc 100644
--- a/cobalt/base/BUILD.gn
+++ b/cobalt/base/BUILD.gn
@@ -60,6 +60,7 @@
"log_message_handler.cc",
"log_message_handler.h",
"message_queue.h",
+ "on_metric_upload_event.h",
"on_screen_keyboard_hidden_event.h",
"on_screen_keyboard_shown_event.h",
"path_provider.cc",
diff --git a/cobalt/base/on_metric_upload_event.h b/cobalt/base/on_metric_upload_event.h
new file mode 100644
index 0000000..d011f0a
--- /dev/null
+++ b/cobalt/base/on_metric_upload_event.h
@@ -0,0 +1,61 @@
+// 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_BASE_ON_METRIC_UPLOAD_EVENT_H_
+#define COBALT_BASE_ON_METRIC_UPLOAD_EVENT_H_
+
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/strings/string_util.h"
+#include "cobalt/base/event.h"
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/h5vcc/h5vcc_metric_type.h"
+
+namespace base {
+
+// Event sent when a metric payload is ready for upload.
+class OnMetricUploadEvent : public Event {
+ public:
+ OnMetricUploadEvent(const cobalt::h5vcc::H5vccMetricType& metric_type,
+ const std::string& serialized_proto)
+ : metric_type_(metric_type), serialized_proto_(serialized_proto) {}
+
+ explicit OnMetricUploadEvent(const Event* event) {
+ CHECK(event != nullptr);
+ const base::OnMetricUploadEvent* on_metric_upload_event =
+ base::polymorphic_downcast<const base::OnMetricUploadEvent*>(event);
+ metric_type_ = on_metric_upload_event->metric_type();
+ serialized_proto_ = on_metric_upload_event->serialized_proto();
+ }
+
+ const cobalt::h5vcc::H5vccMetricType& metric_type() const {
+ return metric_type_;
+ }
+
+ const std::string& serialized_proto() const { return serialized_proto_; }
+
+
+ BASE_EVENT_SUBCLASS(OnMetricUploadEvent);
+
+ private:
+ cobalt::h5vcc::H5vccMetricType metric_type_;
+ std::string serialized_proto_;
+};
+
+} // namespace base
+
+#endif // COBALT_BASE_ON_METRIC_UPLOAD_EVENT_H_
diff --git a/cobalt/browser/BUILD.gn b/cobalt/browser/BUILD.gn
index 25e387a..9b7b12c 100644
--- a/cobalt/browser/BUILD.gn
+++ b/cobalt/browser/BUILD.gn
@@ -118,7 +118,6 @@
"client_hint_headers.h",
"device_authentication.cc",
"device_authentication.h",
- "lifecycle_observer.h",
"on_screen_keyboard_starboard_bridge.cc",
"on_screen_keyboard_starboard_bridge.h",
"render_tree_combiner.cc",
diff --git a/cobalt/browser/application.cc b/cobalt/browser/application.cc
index 31cb00d..ebf95f7 100644
--- a/cobalt/browser/application.cc
+++ b/cobalt/browser/application.cc
@@ -460,7 +460,12 @@
}
int StringToLogLevel(const std::string& log_level) {
- if (log_level == "info") {
+ if (log_level == "verbose") {
+ // The lower the verbose level is, the more messages are logged. Set it to
+ // a lower enough value to ensure that all known verbose messages are
+ // logged.
+ return logging::LOG_VERBOSE - 15;
+ } else if (log_level == "info") {
return logging::LOG_INFO;
} else if (log_level == "warning") {
return logging::LOG_WARNING;
@@ -698,9 +703,6 @@
base::kApplicationStateStarted, kWatchdogTimeInterval,
kWatchdogTimeWait, watchdog::NONE);
- cobalt::cache::Cache::GetInstance()->set_persistent_settings(
- persistent_settings_.get());
-
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
base::Optional<cssom::ViewportSize> requested_viewport_size =
GetRequestedViewportSize(command_line);
@@ -1025,6 +1027,8 @@
base::TimeDelta::FromSeconds(duration_in_seconds));
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
+
+ AddCrashLogApplicationState(base::kApplicationStateStarted);
}
Application::~Application() {
@@ -1524,6 +1528,7 @@
metrics::kMetricEnabledSettingName, false);
auto metric_event_interval = persistent_settings_->GetPersistentSettingAsInt(
metrics::kMetricEventIntervalSettingName, 300);
+ metrics_services_manager_->SetEventDispatcher(&event_dispatcher_);
metrics_services_manager_->SetUploadInterval(metric_event_interval);
metrics_services_manager_->ToggleMetricsEnabled(is_metrics_enabled);
// Metric recording state initialization _must_ happen before we bootstrap
diff --git a/cobalt/browser/browser_module.cc b/cobalt/browser/browser_module.cc
index c6f7f18..f44b2b2 100644
--- a/cobalt/browser/browser_module.cc
+++ b/cobalt/browser/browser_module.cc
@@ -52,6 +52,7 @@
#include "cobalt/math/matrix3_f.h"
#include "cobalt/overlay_info/overlay_info_registry.h"
#include "cobalt/persistent_storage/persistent_settings.h"
+#include "cobalt/trace_event/scoped_trace_to_file.h"
#include "cobalt/ui_navigation/scroll_engine/scroll_engine.h"
#include "cobalt/web/csp_delegate_factory.h"
#include "cobalt/web/navigator_ua_data.h"
@@ -161,6 +162,13 @@
"is useful when trying to target testing to certain codecs, since other "
"codecs will get picked as a fallback as a result.";
+const char kNavigateTimedTrace[] = "navigate_timed_trace";
+const char kNavigateTimedTraceShortHelp[] =
+ "Request a timed trace from the next navigation.";
+const char kNavigateTimedTraceLongHelp[] =
+ "When this is called, a timed trace will start at the next navigation "
+ "and run for the given number of seconds.";
+
void ScreenshotCompleteCallback(const base::FilePath& output_path) {
DLOG(INFO) << "Screenshot written to " << output_path.value();
}
@@ -268,6 +276,11 @@
base::Unretained(this)),
kDisableMediaCodecsCommandShortHelp,
kDisableMediaCodecsCommandLongHelp)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(navigate_timed_trace_command_handler_(
+ kNavigateTimedTrace,
+ base::Bind(&BrowserModule::OnNavigateTimedTrace,
+ base::Unretained(this)),
+ kNavigateTimedTraceShortHelp, kNavigateTimedTraceLongHelp)),
#endif // defined(ENABLE_DEBUGGER)
has_resumed_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED),
@@ -289,7 +302,7 @@
platform_info_.reset(new browser::UserAgentPlatformInfo());
service_worker_registry_.reset(new ServiceWorkerRegistry(
- &web_settings_, network_module, platform_info_.get(), url));
+ &web_settings_, network_module, platform_info_.get()));
#if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
SbCoreDumpRegisterHandler(BrowserModule::CoreDumpHandler, this);
@@ -462,7 +475,9 @@
}
debug_console_.reset();
#endif
+
DestroySplashScreen();
+ DestroyScrollEngine();
// Make sure the WebModule is destroyed before the ServiceWorkerRegistry
if (web_module_) {
lifecycle_observers_.RemoveObserver(web_module_.get());
@@ -478,6 +493,7 @@
DLOG(INFO) << "In BrowserModule::Navigate " << url;
TRACE_EVENT1("cobalt::browser", "BrowserModule::Navigate()", "url",
url.spec());
+
// Reset the waitable event regardless of the thread. This ensures that the
// webdriver won't incorrectly believe that the webmodule has finished loading
// when it calls Navigate() and waits for the |web_module_loaded_| signal.
@@ -552,6 +568,17 @@
current_main_web_module_timeline_id_ = next_timeline_id_++;
main_web_module_layer_->Reset();
+
+#if defined(ENABLE_DEBUGGER)
+ // Check to see if a timed_trace has been set, indicating that we should
+ // begin a timed trace upon startup.
+ if (navigate_timed_trace_duration_ != base::TimeDelta()) {
+ trace_event::TraceToFileForDuration(
+ base::FilePath(FILE_PATH_LITERAL("timed_trace.json")),
+ navigate_timed_trace_duration_);
+ navigate_timed_trace_duration_ = base::TimeDelta();
+ }
+#endif // defined(ENABLE_DEBUGGER)
}
void BrowserModule::NavigateResetErrorHandling() {
@@ -625,7 +652,9 @@
}
void BrowserModule::NavigateSetupScrollEngine() {
+ DestroyScrollEngine();
scroll_engine_.reset(new ui_navigation::scroll_engine::ScrollEngine());
+ lifecycle_observers_.AddObserver(scroll_engine_.get());
scroll_engine_->thread()->Start();
}
@@ -662,8 +691,8 @@
}
options.provide_screenshot_function =
- base::Bind(&ScreenShotWriter::RequestScreenshotToMemoryUnencoded,
- base::Unretained(screen_shot_writer_.get()));
+ base::Bind(&BrowserModule::RequestScreenshotToMemoryUnencoded,
+ base::Unretained(this));
#if defined(ENABLE_DEBUGGER)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -777,6 +806,14 @@
return web_module_loaded_.TimedWait(timeout);
}
+void BrowserModule::EnsureScreenShotWriter() {
+ if (!screen_shot_writer_ && renderer_module_) {
+ screen_shot_writer_.reset(
+ new ScreenShotWriter(renderer_module_->pipeline()));
+ }
+}
+
+#if defined(ENABLE_WEBDRIVER) || defined(ENABLE_DEBUGGER)
void BrowserModule::RequestScreenshotToFile(
const base::FilePath& path,
loader::image::EncodedStaticImage::ImageFormat image_format,
@@ -784,7 +821,9 @@
const base::Closure& done_callback) {
TRACE_EVENT0("cobalt::browser", "BrowserModule::RequestScreenshotToFile()");
DCHECK_EQ(base::MessageLoop::current(), self_message_loop_);
+ EnsureScreenShotWriter();
DCHECK(screen_shot_writer_);
+ if (!screen_shot_writer_) return;
scoped_refptr<render_tree::Node> render_tree;
web_module_->DoSynchronousLayoutAndGetRenderTree(&render_tree);
@@ -802,7 +841,9 @@
const base::Optional<math::Rect>& clip_rect,
const ScreenShotWriter::ImageEncodeCompleteCallback& screenshot_ready) {
TRACE_EVENT0("cobalt::browser", "BrowserModule::RequestScreenshotToMemory()");
+ EnsureScreenShotWriter();
DCHECK(screen_shot_writer_);
+ if (!screen_shot_writer_) return;
// Note: This does not have to be called from self_message_loop_.
scoped_refptr<render_tree::Node> render_tree;
@@ -815,6 +856,19 @@
screen_shot_writer_->RequestScreenshotToMemory(image_format, render_tree,
clip_rect, screenshot_ready);
}
+#endif // defined(ENABLE_WEBDRIVER) || defined(ENABLE_DEBUGGER)
+
+// Request a screenshot to memory without compressing the image.
+void BrowserModule::RequestScreenshotToMemoryUnencoded(
+ const scoped_refptr<render_tree::Node>& render_tree_root,
+ const base::Optional<math::Rect>& clip_rect,
+ const renderer::Pipeline::RasterizationCompleteCallback& callback) {
+ EnsureScreenShotWriter();
+ DCHECK(screen_shot_writer_);
+ if (!screen_shot_writer_) return;
+ screen_shot_writer_->RequestScreenshotToMemoryUnencoded(render_tree_root,
+ clip_rect, callback);
+}
void BrowserModule::ProcessRenderTreeSubmissionQueue() {
TRACE_EVENT0("cobalt::browser",
@@ -1137,6 +1191,13 @@
SubmitCurrentRenderTreeToRenderer();
}
+void BrowserModule::OnNavigateTimedTrace(const std::string& time) {
+ double duration_in_seconds = 0;
+ base::StringToDouble(time, &duration_in_seconds);
+ navigate_timed_trace_duration_ =
+ base::TimeDelta::FromMilliseconds(static_cast<int64_t>(
+ duration_in_seconds * base::Time::kMillisecondsPerSecond));
+}
#endif // defined(ENABLE_DEBUGGER)
void BrowserModule::OnOnScreenKeyboardInputEventProduced(
@@ -1437,6 +1498,20 @@
}
}
+void BrowserModule::DestroyScrollEngine() {
+ TRACE_EVENT0("cobalt::browser", "BrowserModule::DestroyScrollEngine()");
+ if (base::MessageLoop::current() != self_message_loop_) {
+ self_message_loop_->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&BrowserModule::DestroyScrollEngine, weak_this_));
+ return;
+ }
+ if (scroll_engine_ &&
+ lifecycle_observers_.HasObserver(scroll_engine_.get())) {
+ lifecycle_observers_.RemoveObserver(scroll_engine_.get());
+ }
+ scroll_engine_.reset();
+}
+
#if defined(ENABLE_WEBDRIVER)
std::unique_ptr<webdriver::SessionDriver> BrowserModule::CreateSessionDriver(
const webdriver::protocol::SessionId& session_id) {
@@ -1744,7 +1819,7 @@
system_window_.get(),
RendererModuleWithCameraOptions(options_.renderer_module_options,
input_device_manager_->camera_3d())));
- screen_shot_writer_.reset(new ScreenShotWriter(renderer_module_->pipeline()));
+ screen_shot_writer_.reset();
}
void BrowserModule::DestroyRendererModule() {
@@ -2184,11 +2259,7 @@
auto url_request_context = network_module_->url_request_context();
auto http_cache = url_request_context->http_transaction_factory()->GetCache();
if (!http_cache) return;
- auto cache_backend = static_cast<disk_cache::CobaltBackendImpl*>(
- http_cache->GetCurrentBackend());
- if (cache_backend) {
- cache_backend->ValidatePersistentSettings();
- }
+ network_module_->url_request_context()->ValidateCachePersistentSettings();
}
} // namespace browser
diff --git a/cobalt/browser/browser_module.h b/cobalt/browser/browser_module.h
index 2011577..abf05fd 100644
--- a/cobalt/browser/browser_module.h
+++ b/cobalt/browser/browser_module.h
@@ -25,6 +25,7 @@
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
+#include "base/time/time.h"
#include "base/timer/timer.h"
#include "cobalt/base/accessibility_caption_settings_changed_event.h"
#include "cobalt/base/application_state.h"
@@ -151,6 +152,10 @@
void AddURLHandler(const URLHandler::URLHandlerCallback& callback);
void RemoveURLHandler(const URLHandler::URLHandlerCallback& callback);
+ // Start the ScreenShotWriter if it's not already running.
+ void EnsureScreenShotWriter();
+
+#if defined(ENABLE_WEBDRIVER) || defined(ENABLE_DEBUGGER)
// Request a screenshot to be written to the specified path. Callback will
// be fired after the screenshot has been written to disk.
void RequestScreenshotToFile(
@@ -164,6 +169,13 @@
loader::image::EncodedStaticImage::ImageFormat image_format,
const base::Optional<math::Rect>& clip_rect,
const ScreenShotWriter::ImageEncodeCompleteCallback& screenshot_ready);
+#endif // defined(ENABLE_WEBDRIVER) || defined(ENABLE_DEBUGGER)
+
+ // Request a screenshot to memory without compressing the image.
+ void RequestScreenshotToMemoryUnencoded(
+ const scoped_refptr<render_tree::Node>& render_tree_root,
+ const base::Optional<math::Rect>& clip_rect,
+ const renderer::Pipeline::RasterizationCompleteCallback& callback);
#if defined(ENABLE_WEBDRIVER)
std::unique_ptr<webdriver::SessionDriver> CreateSessionDriver(
@@ -361,6 +373,8 @@
// Destroys the splash screen, if currently displayed.
void DestroySplashScreen(base::TimeDelta close_time = base::TimeDelta());
+ void DestroyScrollEngine();
+
// Called when web module has received window.close().
void OnWindowClose(base::TimeDelta close_time);
@@ -383,6 +397,8 @@
const browser::WebModule::LayoutResults& layout_results);
void OnDebugConsoleRenderTreeProduced(
const browser::WebModule::LayoutResults& layout_results);
+
+ void OnNavigateTimedTrace(const std::string& time);
#endif // defined(ENABLE_DEBUGGER)
#if defined(ENABLE_WEBDRIVER)
@@ -658,6 +674,12 @@
// Saves the previous debugger state to be restored in the new WebModule.
std::unique_ptr<debug::backend::DebuggerState> debugger_state_;
+
+ // Amount of time to run a Timed Trace after Navigate
+ base::TimeDelta navigate_timed_trace_duration_;
+
+ debug::console::ConsoleCommandManager::CommandHandler
+ navigate_timed_trace_command_handler_;
#endif // defined(ENABLE_DEBUGGER)
// The splash screen. The pointer wrapped here should be non-NULL iff
diff --git a/cobalt/browser/idl_files.gni b/cobalt/browser/idl_files.gni
index 4663d09..41539c3 100644
--- a/cobalt/browser/idl_files.gni
+++ b/cobalt/browser/idl_files.gni
@@ -171,6 +171,7 @@
"//cobalt/h5vcc/h5vcc_screen.idl",
"//cobalt/h5vcc/h5vcc_system.idl",
"//cobalt/h5vcc/h5vcc_trace_event.idl",
+ "//cobalt/h5vcc/h5vcc_net_log.idl",
"//cobalt/h5vcc/h5vcc_updater.idl",
"//cobalt/media_capture/blob_event.idl",
diff --git a/cobalt/browser/metrics/BUILD.gn b/cobalt/browser/metrics/BUILD.gn
index 62c85fa..414d1b5 100644
--- a/cobalt/browser/metrics/BUILD.gn
+++ b/cobalt/browser/metrics/BUILD.gn
@@ -24,11 +24,11 @@
"cobalt_metrics_services_manager.h",
"cobalt_metrics_services_manager_client.cc",
"cobalt_metrics_services_manager_client.h",
- "cobalt_metrics_uploader_callback.h",
]
deps = [
"//base",
+ "//cobalt/base",
"//cobalt/browser:generated_types",
"//cobalt/h5vcc:metric_event_handler_wrapper",
"//components/metrics",
@@ -51,8 +51,9 @@
deps = [
":metrics",
"//base",
- "//cobalt//browser:test_dependencies_on_browser",
+ "//cobalt/base",
"//cobalt/browser:generated_types",
+ "//cobalt/browser:test_dependencies_on_browser",
"//cobalt/h5vcc",
"//cobalt/h5vcc:metric_event_handler_wrapper",
"//cobalt/test:run_all_unittests",
diff --git a/cobalt/browser/metrics/cobalt_metrics_log_uploader.cc b/cobalt/browser/metrics/cobalt_metrics_log_uploader.cc
index 49f1c81..955ef04 100644
--- a/cobalt/browser/metrics/cobalt_metrics_log_uploader.cc
+++ b/cobalt/browser/metrics/cobalt_metrics_log_uploader.cc
@@ -14,9 +14,12 @@
#include "cobalt/browser/metrics/cobalt_metrics_log_uploader.h"
+#include <memory>
+
#include "base/base64url.h"
#include "base/logging.h"
-#include "cobalt/browser/metrics/cobalt_metrics_uploader_callback.h"
+#include "cobalt/base/event_dispatcher.h"
+#include "cobalt/base/on_metric_upload_event.h"
#include "cobalt/h5vcc/h5vcc_metric_type.h"
#include "components/metrics/log_decoder.h"
#include "components/metrics/metrics_log_uploader.h"
@@ -49,7 +52,7 @@
const std::string& compressed_log_data, const std::string& log_hash,
const ::metrics::ReportingInfo& reporting_info) {
if (service_type_ == ::metrics::MetricsLogUploader::UMA) {
- if (upload_handler_ != nullptr) {
+ if (event_dispatcher_ != nullptr) {
std::string uncompressed_serialized_proto;
::metrics::DecodeLogData(compressed_log_data,
&uncompressed_serialized_proto);
@@ -67,8 +70,11 @@
base::Base64UrlEncode(cobalt_uma_event.SerializeAsString(),
base::Base64UrlEncodePolicy::INCLUDE_PADDING,
&base64_encoded_proto);
- upload_handler_->Run(h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma,
- base64_encoded_proto);
+
+ event_dispatcher_->DispatchEvent(
+ std::unique_ptr<base::Event>(new base::OnMetricUploadEvent(
+ h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma,
+ base64_encoded_proto)));
}
}
@@ -79,9 +85,9 @@
/*was_https*/ true);
}
-void CobaltMetricsLogUploader::SetOnUploadHandler(
- const CobaltMetricsUploaderCallback* upload_handler) {
- upload_handler_ = upload_handler;
+void CobaltMetricsLogUploader::SetEventDispatcher(
+ const base::EventDispatcher* event_dispatcher) {
+ event_dispatcher_ = event_dispatcher;
}
} // namespace metrics
diff --git a/cobalt/browser/metrics/cobalt_metrics_log_uploader.h b/cobalt/browser/metrics/cobalt_metrics_log_uploader.h
index fa71662..8b5b73d 100644
--- a/cobalt/browser/metrics/cobalt_metrics_log_uploader.h
+++ b/cobalt/browser/metrics/cobalt_metrics_log_uploader.h
@@ -20,7 +20,7 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
-#include "cobalt/browser/metrics/cobalt_metrics_uploader_callback.h"
+#include "cobalt/base/event_dispatcher.h"
#include "cobalt/h5vcc/metric_event_handler_wrapper.h"
#include "components/metrics/metrics_log_uploader.h"
#include "third_party/metrics_proto/reporting_info.pb.h"
@@ -43,6 +43,9 @@
virtual ~CobaltMetricsLogUploader() {}
+ // Set event dispatcher to be used to publish any metrics events (eg upload).
+ void SetEventDispatcher(const base::EventDispatcher* event_dispatcher);
+
// Uploads a log with the specified |compressed_log_data| and |log_hash|.
// |log_hash| is expected to be the hex-encoded SHA1 hash of the log data
// before compression.
@@ -50,15 +53,10 @@
const std::string& log_hash,
const ::metrics::ReportingInfo& reporting_info);
- // Sets the event handler wrapper to be called when metrics are ready for
- // upload. This should be the JavaScript H5vcc callback implementation.
- void SetOnUploadHandler(
- const CobaltMetricsUploaderCallback* metric_event_handler);
-
private:
const ::metrics::MetricsLogUploader::MetricServiceType service_type_;
const ::metrics::MetricsLogUploader::UploadCallback on_upload_complete_;
- const CobaltMetricsUploaderCallback* upload_handler_ = nullptr;
+ const base::EventDispatcher* event_dispatcher_ = nullptr;
};
} // namespace metrics
diff --git a/cobalt/browser/metrics/cobalt_metrics_log_uploader_test.cc b/cobalt/browser/metrics/cobalt_metrics_log_uploader_test.cc
index 78e7d18..f6927a8 100644
--- a/cobalt/browser/metrics/cobalt_metrics_log_uploader_test.cc
+++ b/cobalt/browser/metrics/cobalt_metrics_log_uploader_test.cc
@@ -18,7 +18,10 @@
#include "base/base64url.h"
#include "base/test/mock_callback.h"
-#include "cobalt/browser/metrics/cobalt_metrics_uploader_callback.h"
+#include "cobalt/base/event.h"
+#include "cobalt/base/event_dispatcher.h"
+#include "cobalt/base/on_metric_upload_event.h"
+#include "cobalt/h5vcc/h5vcc_metric_type.h"
#include "cobalt/h5vcc/h5vcc_metrics.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -26,7 +29,6 @@
#include "third_party/metrics_proto/cobalt_uma_event.pb.h"
#include "third_party/metrics_proto/reporting_info.pb.h"
#include "third_party/zlib/google/compression_utils.h"
-
namespace cobalt {
namespace browser {
namespace metrics {
@@ -43,6 +45,12 @@
class CobaltMetricsLogUploaderTest : public ::testing::Test {
public:
void SetUp() override {
+ dispatcher_ = std::make_unique<base::EventDispatcher>();
+ dispatcher_->AddEventCallback(
+ base::OnMetricUploadEvent::TypeId(),
+ base::Bind(&CobaltMetricsLogUploaderTest::OnMetricUploadEventHandler,
+ base::Unretained(this)));
+
uploader_ = std::make_unique<CobaltMetricsLogUploader>(
::metrics::MetricsLogUploader::MetricServiceType::UMA,
base::Bind(&CobaltMetricsLogUploaderTest::UploadCompleteCallback,
@@ -51,6 +59,13 @@
void TearDown() override {}
+ void OnMetricUploadEventHandler(const base::Event* event) {
+ std::unique_ptr<base::OnMetricUploadEvent> on_metric_upload_event(
+ new base::OnMetricUploadEvent(event));
+ last_metric_type_ = on_metric_upload_event->metric_type();
+ last_serialized_proto_ = on_metric_upload_event->serialized_proto();
+ }
+
void UploadCompleteCallback(int response_code, int error_code,
bool was_https) {
callback_count_++;
@@ -58,13 +73,14 @@
protected:
std::unique_ptr<CobaltMetricsLogUploader> uploader_;
+ std::unique_ptr<base::EventDispatcher> dispatcher_;
int callback_count_ = 0;
+ cobalt::h5vcc::H5vccMetricType last_metric_type_;
+ std::string last_serialized_proto_ = "";
};
TEST_F(CobaltMetricsLogUploaderTest, TriggersUploadHandler) {
- base::MockCallback<CobaltMetricsUploaderCallback> mock_upload_handler;
- const auto cb = mock_upload_handler.Get();
- uploader_->SetOnUploadHandler(&cb);
+ uploader_->SetEventDispatcher(dispatcher_.get());
::metrics::ReportingInfo dummy_reporting_info;
dummy_reporting_info.set_attempt_count(33);
::metrics::ChromeUserMetricsExtension uma_log;
@@ -87,12 +103,11 @@
base::Base64UrlEncode(cobalt_event.SerializeAsString(),
base::Base64UrlEncodePolicy::INCLUDE_PADDING,
&base64_encoded_proto);
- EXPECT_CALL(mock_upload_handler,
- Run(Eq(h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma),
- StrEq(base64_encoded_proto)))
- .Times(1);
uploader_->UploadLog(compressed_message, "fake_hash", dummy_reporting_info);
ASSERT_EQ(callback_count_, 1);
+ ASSERT_EQ(last_metric_type_,
+ cobalt::h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma);
+ ASSERT_EQ(last_serialized_proto_, base64_encoded_proto);
::metrics::ChromeUserMetricsExtension uma_log2;
uma_log2.set_session_id(456);
@@ -108,11 +123,10 @@
base::Base64UrlEncode(cobalt_event2.SerializeAsString(),
base::Base64UrlEncodePolicy::INCLUDE_PADDING,
&base64_encoded_proto2);
- EXPECT_CALL(mock_upload_handler,
- Run(Eq(h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma),
- StrEq(base64_encoded_proto2)))
- .Times(1);
uploader_->UploadLog(compressed_message2, "fake_hash", dummy_reporting_info);
+ ASSERT_EQ(last_metric_type_,
+ cobalt::h5vcc::H5vccMetricType::kH5vccMetricTypeCobaltUma);
+ ASSERT_EQ(last_serialized_proto_, base64_encoded_proto2);
ASSERT_EQ(callback_count_, 2);
}
@@ -121,17 +135,15 @@
::metrics::MetricsLogUploader::MetricServiceType::UKM,
base::Bind(&CobaltMetricsLogUploaderTest::UploadCompleteCallback,
base::Unretained(this))));
- base::MockCallback<CobaltMetricsUploaderCallback> mock_upload_handler;
- const auto cb = mock_upload_handler.Get();
- uploader_->SetOnUploadHandler(&cb);
+ uploader_->SetEventDispatcher(dispatcher_.get());
::metrics::ReportingInfo dummy_reporting_info;
::metrics::ChromeUserMetricsExtension uma_log;
uma_log.set_session_id(1234);
uma_log.set_client_id(1234);
std::string compressed_message;
compression::GzipCompress(uma_log.SerializeAsString(), &compressed_message);
- EXPECT_CALL(mock_upload_handler, Run(_, _)).Times(0);
uploader_->UploadLog(compressed_message, "fake_hash", dummy_reporting_info);
+ ASSERT_EQ(last_serialized_proto_, "");
// Even though we don't upload this log, we still need to trigger the complete
// callback so the metric code can keep running.
ASSERT_EQ(callback_count_, 1);
diff --git a/cobalt/browser/metrics/cobalt_metrics_service_client.cc b/cobalt/browser/metrics/cobalt_metrics_service_client.cc
index 2b4d21a..c3408a9 100644
--- a/cobalt/browser/metrics/cobalt_metrics_service_client.cc
+++ b/cobalt/browser/metrics/cobalt_metrics_service_client.cc
@@ -24,9 +24,9 @@
#include "base/memory/singleton.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
+#include "cobalt/base/event_dispatcher.h"
#include "cobalt/browser/metrics/cobalt_enabled_state_provider.h"
#include "cobalt/browser/metrics/cobalt_metrics_log_uploader.h"
-#include "cobalt/browser/metrics/cobalt_metrics_uploader_callback.h"
#include "components/metrics/enabled_state_provider.h"
#include "components/metrics/metrics_log_uploader.h"
#include "components/metrics/metrics_pref_names.h"
@@ -50,11 +50,11 @@
// Upload Handler.
const int kStandardUploadIntervalSeconds = 5 * 60; // 5 minutes.
-void CobaltMetricsServiceClient::SetOnUploadHandler(
- const CobaltMetricsUploaderCallback* uploader_callback) {
- upload_handler_ = uploader_callback;
+void CobaltMetricsServiceClient::SetEventDispatcher(
+ const base::EventDispatcher* event_dispatcher) {
+ event_dispatcher_ = event_dispatcher;
if (log_uploader_) {
- log_uploader_->SetOnUploadHandler(upload_handler_);
+ log_uploader_->SetEventDispatcher(event_dispatcher);
}
}
@@ -77,7 +77,8 @@
void CobaltMetricsServiceClient::SetMetricsClientId(
const std::string& client_id) {
- // TODO(b/286066035): What to do with client id here?
+ // ClientId is unnecessary within Cobalt. We expect the web client responsible
+ // for uploading these to have its own concept of device/client identifiers.
}
// TODO(b/286884542): Audit all stub implementations in this class and reaffirm
@@ -153,8 +154,8 @@
auto uploader = std::make_unique<CobaltMetricsLogUploader>(
service_type, on_upload_complete);
log_uploader_ = uploader.get();
- if (upload_handler_ != nullptr) {
- log_uploader_->SetOnUploadHandler(upload_handler_);
+ if (event_dispatcher_ != nullptr) {
+ log_uploader_->SetEventDispatcher(event_dispatcher_);
}
return uploader;
}
diff --git a/cobalt/browser/metrics/cobalt_metrics_service_client.h b/cobalt/browser/metrics/cobalt_metrics_service_client.h
index c7860f0..c8efa8f 100644
--- a/cobalt/browser/metrics/cobalt_metrics_service_client.h
+++ b/cobalt/browser/metrics/cobalt_metrics_service_client.h
@@ -23,9 +23,9 @@
#include "base/callback.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
+#include "cobalt/base/event_dispatcher.h"
#include "cobalt/browser/metrics/cobalt_enabled_state_provider.h"
#include "cobalt/browser/metrics/cobalt_metrics_log_uploader.h"
-#include "cobalt/browser/metrics/cobalt_metrics_uploader_callback.h"
#include "components/metrics/metrics_log_uploader.h"
#include "components/metrics/metrics_reporting_default_state.h"
#include "components/metrics/metrics_service.h"
@@ -48,10 +48,8 @@
public:
~CobaltMetricsServiceClient() override{};
- // Sets the uploader handler to be called when metrics are ready for
- // upload.
- void SetOnUploadHandler(
- const CobaltMetricsUploaderCallback* uploader_callback);
+ // Set event dispatcher to be used to publish any metrics events (eg upload).
+ void SetEventDispatcher(const base::EventDispatcher* event_dispatcher);
// Returns the MetricsService instance that this client is associated with.
// With the exception of testing contexts, the returned instance must be valid
@@ -165,10 +163,10 @@
CobaltMetricsLogUploader* log_uploader_ = nullptr;
- const CobaltMetricsUploaderCallback* upload_handler_ = nullptr;
-
uint32_t custom_upload_interval_ = UINT32_MAX;
+ const base::EventDispatcher* event_dispatcher_ = nullptr;
+
DISALLOW_COPY_AND_ASSIGN(CobaltMetricsServiceClient);
};
diff --git a/cobalt/browser/metrics/cobalt_metrics_services_manager.cc b/cobalt/browser/metrics/cobalt_metrics_services_manager.cc
index acb5d8b..f816858 100644
--- a/cobalt/browser/metrics/cobalt_metrics_services_manager.cc
+++ b/cobalt/browser/metrics/cobalt_metrics_services_manager.cc
@@ -17,6 +17,7 @@
#include <memory>
#include "base/logging.h"
+#include "cobalt/base/event_dispatcher.h"
#include "cobalt/browser/metrics/cobalt_metrics_service_client.h"
#include "cobalt/browser/metrics/cobalt_metrics_services_manager_client.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
@@ -41,23 +42,27 @@
return instance_;
}
-void CobaltMetricsServicesManager::DeleteInstance() { delete instance_; }
-
-void CobaltMetricsServicesManager::SetOnUploadHandler(
- const CobaltMetricsUploaderCallback* uploader_callback) {
- instance_->task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&CobaltMetricsServicesManager::SetOnUploadHandlerInternal,
- base::Unretained(instance_), uploader_callback));
+void CobaltMetricsServicesManager::DeleteInstance() {
+ delete instance_;
+ instance_ = nullptr;
}
-void CobaltMetricsServicesManager::SetOnUploadHandlerInternal(
- const CobaltMetricsUploaderCallback* uploader_callback) {
+void CobaltMetricsServicesManager::SetEventDispatcher(
+ base::EventDispatcher* event_dispatcher) {
+ if (instance_ != nullptr) {
+ instance_->task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&CobaltMetricsServicesManager::SetEventDispatcherInternal,
+ base::Unretained(instance_), event_dispatcher));
+ }
+}
+
+void CobaltMetricsServicesManager::SetEventDispatcherInternal(
+ base::EventDispatcher* event_dispatcher) {
CobaltMetricsServiceClient* client =
static_cast<CobaltMetricsServiceClient*>(GetMetricsServiceClient());
DCHECK(client);
- client->SetOnUploadHandler(uploader_callback);
- LOG(INFO) << "New Cobalt Telemetry metric upload handler bound.";
+ client->SetEventDispatcher(event_dispatcher);
}
void CobaltMetricsServicesManager::ToggleMetricsEnabled(bool is_enabled) {
diff --git a/cobalt/browser/metrics/cobalt_metrics_services_manager.h b/cobalt/browser/metrics/cobalt_metrics_services_manager.h
index da31294..20a4345 100644
--- a/cobalt/browser/metrics/cobalt_metrics_services_manager.h
+++ b/cobalt/browser/metrics/cobalt_metrics_services_manager.h
@@ -20,8 +20,8 @@
#include "base//memory/scoped_refptr.h"
#include "base/single_thread_task_runner.h"
+#include "cobalt/base/event_dispatcher.h"
#include "cobalt/browser/metrics/cobalt_metrics_services_manager_client.h"
-#include "cobalt/browser/metrics/cobalt_metrics_uploader_callback.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "components/metrics_services_manager/metrics_services_manager_client.h"
@@ -54,10 +54,9 @@
// Destructs the static instance of CobaltMetricsServicesManager.
static void DeleteInstance();
- // Sets the upload handler onto the current static instance of
- // CobaltMetricsServicesManager.
- static void SetOnUploadHandler(
- const CobaltMetricsUploaderCallback* uploader_callback);
+ // Sets an event dispatcher to call when metric events happen (e.g., on
+ // upload).
+ static void SetEventDispatcher(base::EventDispatcher* event_dispatcher);
// Toggles whether metric reporting is enabled via
// CobaltMetricsServicesManager.
@@ -68,8 +67,7 @@
static void SetUploadInterval(uint32_t interval_seconds);
private:
- void SetOnUploadHandlerInternal(
- const CobaltMetricsUploaderCallback* uploader_callback);
+ void SetEventDispatcherInternal(base::EventDispatcher* event_dispatcher);
void ToggleMetricsEnabledInternal(bool is_enabled);
diff --git a/cobalt/browser/metrics/cobalt_metrics_uploader_callback.h b/cobalt/browser/metrics/cobalt_metrics_uploader_callback.h
deleted file mode 100644
index db980c5..0000000
--- a/cobalt/browser/metrics/cobalt_metrics_uploader_callback.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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_BROWSER_METRICS_COBALT_METRICS_UPLOADER_CALLBACK_H_
-#define COBALT_BROWSER_METRICS_COBALT_METRICS_UPLOADER_CALLBACK_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "cobalt/h5vcc/h5vcc_metric_type.h"
-
-namespace cobalt {
-namespace browser {
-namespace metrics {
-
-typedef base::RepeatingCallback<void(
- const cobalt::h5vcc::H5vccMetricType& metric_type,
- const std::string& serialized_proto)>
- CobaltMetricsUploaderCallback;
-
-} // namespace metrics
-} // namespace browser
-} // namespace cobalt
-
-#endif // COBALT_BROWSER_METRICS_COBALT_METRICS_UPLOADER_CALLBACK_H_
diff --git a/cobalt/browser/service_worker_registry.cc b/cobalt/browser/service_worker_registry.cc
index 89b9395..2a62dac 100644
--- a/cobalt/browser/service_worker_registry.cc
+++ b/cobalt/browser/service_worker_registry.cc
@@ -49,7 +49,7 @@
ServiceWorkerRegistry::ServiceWorkerRegistry(
web::WebSettings* web_settings, network::NetworkModule* network_module,
- web::UserAgentPlatformInfo* platform_info, const GURL& url)
+ web::UserAgentPlatformInfo* platform_info)
: thread_("ServiceWorkerRegistry") {
if (!thread_.Start()) return;
DCHECK(message_loop());
@@ -73,7 +73,7 @@
message_loop()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&ServiceWorkerRegistry::Initialize, base::Unretained(this),
- web_settings, network_module, platform_info, url));
+ web_settings, network_module, platform_info));
// Register as a destruction observer to shut down the Web Agent once all
// pending tasks have been executed and the message loop is about to be
@@ -145,11 +145,11 @@
void ServiceWorkerRegistry::Initialize(
web::WebSettings* web_settings, network::NetworkModule* network_module,
- web::UserAgentPlatformInfo* platform_info, const GURL& url) {
+ web::UserAgentPlatformInfo* platform_info) {
TRACE_EVENT0("cobalt::browser", "ServiceWorkerRegistry::Initialize()");
DCHECK_EQ(base::MessageLoop::current(), message_loop());
service_worker_context_.reset(new worker::ServiceWorkerContext(
- web_settings, network_module, platform_info, message_loop(), url));
+ web_settings, network_module, platform_info, message_loop()));
}
} // namespace browser
diff --git a/cobalt/browser/service_worker_registry.h b/cobalt/browser/service_worker_registry.h
index f0e934e..de31979 100644
--- a/cobalt/browser/service_worker_registry.h
+++ b/cobalt/browser/service_worker_registry.h
@@ -35,8 +35,7 @@
public:
ServiceWorkerRegistry(web::WebSettings* web_settings,
network::NetworkModule* network_module,
- web::UserAgentPlatformInfo* platform_info,
- const GURL& url);
+ web::UserAgentPlatformInfo* platform_info);
~ServiceWorkerRegistry();
// The message loop this object is running on.
@@ -58,7 +57,7 @@
// the dedicated thread.
void Initialize(web::WebSettings* web_settings,
network::NetworkModule* network_module,
- web::UserAgentPlatformInfo* platform_info, const GURL& url);
+ web::UserAgentPlatformInfo* platform_info);
void PingWatchdog();
diff --git a/cobalt/browser/splash_screen.h b/cobalt/browser/splash_screen.h
index f50c2e4..3fc1202 100644
--- a/cobalt/browser/splash_screen.h
+++ b/cobalt/browser/splash_screen.h
@@ -57,7 +57,6 @@
}
// LifecycleObserver implementation.
- // LifecycleObserver implementation.
void Blur(SbTimeMonotonic timestamp) override { web_module_->Blur(0); }
void Conceal(render_tree::ResourceProvider* resource_provider,
SbTimeMonotonic timestamp) override {
diff --git a/cobalt/browser/switches.cc b/cobalt/browser/switches.cc
index 70bb756..11c7db6 100644
--- a/cobalt/browser/switches.cc
+++ b/cobalt/browser/switches.cc
@@ -229,7 +229,7 @@
const char kMinLogLevel[] = "min_log_level";
const char kMinLogLevelHelp[] =
- "Set the minimum logging level: info|warning|error|fatal.";
+ "Set the minimum logging level: verbose|info|warning|error|fatal.";
const char kDisableJavaScriptJit[] = "disable_javascript_jit";
const char kDisableJavaScriptJitHelp[] =
"Specifies that javascript jit should be disabled.";
diff --git a/cobalt/build/cobalt_configuration.py b/cobalt/build/cobalt_configuration.py
index e1320ab..ca1c943 100644
--- a/cobalt/build/cobalt_configuration.py
+++ b/cobalt/build/cobalt_configuration.py
@@ -133,8 +133,7 @@
'media_capture_test',
'media_session_test',
'media_stream_test',
- # TODO(b/292030213): Crashes on evergreen
- # 'media_test',
+ 'media_test',
'memory_store_test',
'metrics_test',
'nb_test',
diff --git a/cobalt/cache/cache.cc b/cobalt/cache/cache.cc
index d88a6ec..42eee8e 100644
--- a/cobalt/cache/cache.cc
+++ b/cobalt/cache/cache.cc
@@ -25,7 +25,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "cobalt/configuration/configuration.h"
-#include "cobalt/persistent_storage/persistent_settings.h"
#include "net/disk_cache/cobalt/cobalt_backend_impl.h"
#include "starboard/configuration_constants.h"
#include "starboard/extension/javascript_cache.h"
@@ -201,19 +200,6 @@
return nullptr;
}
-void Cache::set_enabled(bool enabled) { enabled_ = enabled; }
-
-void Cache::set_persistent_settings(
- persistent_storage::PersistentSettings* persistent_settings) {
- persistent_settings_ = persistent_settings;
-
- // Guaranteed to be called before any calls to Retrieve()
- // since set_persistent_settings() is called from the Application()
- // constructor before the NetworkModule is initialized.
- set_enabled(persistent_settings_->GetPersistentSettingAsBool(
- disk_cache::kCacheEnabledPersistentSettingsKey, true));
-}
-
MemoryCappedDirectory* Cache::GetMemoryCappedDirectory(
disk_cache::ResourceType resource_type) {
base::AutoLock auto_lock(lock_);
@@ -222,16 +208,6 @@
return it->second.get();
}
- // Read in size from persistent storage.
- auto metadata = disk_cache::kTypeMetadata[resource_type];
- if (persistent_settings_) {
- uint32_t bucket_size = static_cast<uint32_t>(
- persistent_settings_->GetPersistentSettingAsDouble(
- metadata.directory, metadata.max_size_bytes));
- disk_cache::kTypeMetadata[resource_type] = {metadata.directory,
- bucket_size};
- }
-
auto cache_directory = GetCacheDirectory(resource_type);
auto max_size = GetMaxCacheStorageInBytes(resource_type);
if (!cache_directory || !max_size) {
@@ -248,16 +224,9 @@
void Cache::Resize(disk_cache::ResourceType resource_type, uint32_t bytes) {
if (resource_type != disk_cache::ResourceType::kCacheApi &&
resource_type != disk_cache::ResourceType::kCompiledScript &&
- resource_type != disk_cache::ResourceType::kServiceWorkerScript)
+ resource_type != disk_cache::ResourceType::kServiceWorkerScript) {
return;
- if (bytes == disk_cache::kTypeMetadata[resource_type].max_size_bytes) return;
-
- if (persistent_settings_) {
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[resource_type].directory,
- std::make_unique<base::Value>(static_cast<double>(bytes)));
}
- disk_cache::kTypeMetadata[resource_type].max_size_bytes = bytes;
auto* memory_capped_directory = GetMemoryCappedDirectory(resource_type);
if (memory_capped_directory) {
memory_capped_directory->Resize(bytes);
@@ -273,7 +242,7 @@
case disk_cache::ResourceType::kCacheApi:
case disk_cache::ResourceType::kCompiledScript:
case disk_cache::ResourceType::kServiceWorkerScript:
- return disk_cache::kTypeMetadata[resource_type].max_size_bytes;
+ return disk_cache::settings::GetQuota(resource_type);
default:
return base::nullopt;
}
@@ -334,7 +303,7 @@
resource_type == disk_cache::ResourceType::kCacheApi) {
return true;
}
- if (!enabled_) {
+ if (!disk_cache::settings::GetCacheEnabled()) {
return false;
}
if (resource_type == disk_cache::ResourceType::kCompiledScript) {
diff --git a/cobalt/cache/cache.h b/cobalt/cache/cache.h
index 40bd225..e98f335 100644
--- a/cobalt/cache/cache.h
+++ b/cobalt/cache/cache.h
@@ -26,12 +26,12 @@
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/values.h"
#include "cobalt/cache/memory_capped_directory.h"
-#include "cobalt/persistent_storage/persistent_settings.h"
#include "net/disk_cache/cobalt/resource_type.h"
namespace base {
@@ -67,14 +67,9 @@
base::Optional<uint32_t> GetMaxCacheStorageInBytes(
disk_cache::ResourceType resource_type);
- void set_enabled(bool enabled);
-
- void set_persistent_settings(
- persistent_storage::PersistentSettings* persistent_settings);
-
private:
friend struct base::DefaultSingletonTraits<Cache>;
- Cache() {}
+ Cache() = default;
MemoryCappedDirectory* GetMemoryCappedDirectory(
disk_cache::ResourceType resource_type);
@@ -91,9 +86,6 @@
std::map<disk_cache::ResourceType,
std::map<uint32_t, std::vector<base::WaitableEvent*>>>
pending_;
- bool enabled_ = true;
-
- persistent_storage::PersistentSettings* persistent_settings_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(Cache);
}; // class Cache
diff --git a/cobalt/demos/content/BUILD.gn b/cobalt/demos/content/BUILD.gn
index 1d6ec9a..9d60175 100644
--- a/cobalt/demos/content/BUILD.gn
+++ b/cobalt/demos/content/BUILD.gn
@@ -32,6 +32,7 @@
"deviceorientation-demo/deviceorientation-demo.html",
"disable-jit/index.html",
"dom-gc-demo/dom-gc-demo.html",
+ "draw/index.html",
"dual-playback-demo/bear.mp4",
"dual-playback-demo/dual-playback-demo.html",
"eme-demo/eme-demo.html",
diff --git a/cobalt/demos/content/configure-source-buffer-memory/configure-source-buffer-memory.html b/cobalt/demos/content/configure-source-buffer-memory/configure-source-buffer-memory.html
new file mode 100644
index 0000000..3c40603
--- /dev/null
+++ b/cobalt/demos/content/configure-source-buffer-memory/configure-source-buffer-memory.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Configure Source Buffer Memory</title>
+ <style>
+ body {
+ background-color: #0f0;
+ }
+ video {
+ height: 240px;
+ width: 426px;
+ }
+ </style>
+</head>
+<body>
+ <script type="text/javascript" src="configure-source-buffer-memory.js"></script>
+ <video id="video"></video><br>
+ <div id="status"></div>
+</body>
+</html>
diff --git a/cobalt/demos/content/configure-source-buffer-memory/configure-source-buffer-memory.js b/cobalt/demos/content/configure-source-buffer-memory/configure-source-buffer-memory.js
new file mode 100644
index 0000000..3f2c675
--- /dev/null
+++ b/cobalt/demos/content/configure-source-buffer-memory/configure-source-buffer-memory.js
@@ -0,0 +1,142 @@
+// 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.
+
+"use strict";
+
+const QUOTA_EXCEEDED_ERROR_CODE = 22;
+const mimeCodec = 'video/webm; codecs="vp9"';
+const assetURL = 'vp9-720p.webm';
+
+const assetDuration = 15;
+const assetSize = 344064;
+
+let status_div;
+let video;
+
+
+function fetchAB(url, cb) {
+ console.log("Fetching.. ", url);
+ const xhr = new XMLHttpRequest();
+ xhr.open("get", url);
+ xhr.responseType = "arraybuffer";
+ xhr.onload = () => {
+ console.log("onLoad - calling Callback");
+ cb(xhr.response);
+ };
+ console.log('Sending request for media segment ...');
+ xhr.send();
+}
+
+function testAppendToBuffer(media_source, mem_limit) {
+ const mediaSource = media_source;
+ const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
+ if (mem_limit > 0) {
+ status_div.innerHTML += "Test SourceBuffer, setting memoryLimit to " + mem_limit + "<br>";
+ sourceBuffer.memoryLimit = mem_limit;
+ } else {
+ status_div.innerHTML += "Test SourceBuffer, leaving memoryLimit at default<br>";
+ }
+
+ let MIN_SIZE = 12 * 1024 * 1024;
+ let ESTIMATED_MIN_TIME = 12;
+
+ fetchAB(assetURL, (buf) => {
+ let expectedTime = 0;
+ let expectedSize = 0;
+ let appendCount = 0;
+
+
+ let onBufferFull = function(buffer_was_full) {
+ console.log("OnBufferFull! Quota exceeded? " + buffer_was_full + " appendCount:" + appendCount + " expectedTime:" + expectedTime);
+ status_div.innerHTML += "Finished! Quota exceeded? " + buffer_was_full + " appendCount:" + appendCount + " appended " + appendCount * assetSize + "<br>";
+ }
+
+ sourceBuffer.addEventListener("updateend", function onupdateend() {
+ console.log("Update end. State is " + sourceBuffer.updating);
+ appendCount++;
+ console.log("Append Count" + appendCount);
+ if (sourceBuffer.buffered.start(0) > 0 || expectedTime > sourceBuffer.buffered.end(0)) {
+ sourceBuffer.removeEventListener('updatedend', onupdateend);
+ onBufferFull(false);
+ } else {
+ expectedTime += assetDuration;
+ expectedSize += assetSize;
+ if (expectedSize > (10 * MIN_SIZE)) {
+ sourceBuffer.removeEventListener('updateend', onupdateend);
+ onBufferFull(false);
+ return;
+ }
+
+ try {
+ sourceBuffer.timestampOffset = expectedTime;
+ } catch(e) {
+ console.log("Unexpected error: " + e);
+ }
+
+ try {
+ sourceBuffer.appendBuffer(buf);
+ } catch(e) {
+ console.log("Wuff! QUOTA_EXCEEDED_ERROR!");
+ status_div.innerHTML += "Wuff! QUOTA_EXCEEDED<br>";
+ if (e.code == QUOTA_EXCEEDED_ERROR_CODE) {
+ sourceBuffer.removeEventListener('updateend', onupdateend);
+ onBufferFull(true);
+ } else {
+ console.log("Unexpected error: " + e);
+ }
+ }
+ }
+ });
+
+ console.log("First Append!");
+ sourceBuffer.appendBuffer(buf);
+ status_div.innerHTML += "First append. MemoryLimit is:" + sourceBuffer.memoryLimit + ".<br>";
+ });
+}
+
+function onSourceOpen() {
+ console.log("Source Open. This state:", this.readyState); // open
+ status_div.innerHTML += "Source Open. This state:" + this.readyState + "<br>";
+ status_div.innerHTML += "Lets test first source_buffer, defaults..<br>";
+ testAppendToBuffer(this, 0);
+
+ let new_mem_limit = 400 * 1024 * 1024;
+ status_div.innerHTML += "<br><br>Lets test second source_buffer, setting memory to:" + new_mem_limit + "<br>";
+ testAppendToBuffer(this, new_mem_limit);
+ video.play();
+}
+
+
+function createMediaSource() {
+
+ console.log('Video Get Element By Id...');
+ video = document.getElementById('video');
+ status_div = document.getElementById('status');
+ status_div.innerHTML += 'Video Get Element By Id...<br>';
+
+ console.log('Create Media Source...');
+ status_div.innerHTML += 'Create Media Source...<br>';
+ var mediaSource = new MediaSource;
+
+ console.log('Attaching MediaSource to video element ...');
+ status_div.innerHTML += 'Attaching MediaSource to video element ...<br>';
+ video.src = window.URL.createObjectURL(mediaSource);
+
+ console.log('Add event listener..');
+ status_div.innerHTML += 'Add event listener..<br>';
+ mediaSource.addEventListener('sourceopen', onSourceOpen);
+
+}
+
+addEventListener('load', createMediaSource);
diff --git a/cobalt/demos/content/configure-source-buffer-memory/vp9-720p.webm b/cobalt/demos/content/configure-source-buffer-memory/vp9-720p.webm
new file mode 100644
index 0000000..08a670e
--- /dev/null
+++ b/cobalt/demos/content/configure-source-buffer-memory/vp9-720p.webm
Binary files differ
diff --git a/cobalt/demos/content/draw/index.html b/cobalt/demos/content/draw/index.html
new file mode 100644
index 0000000..6e596c6
--- /dev/null
+++ b/cobalt/demos/content/draw/index.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <style>
+ .container {
+ width: 500px;
+ height: 500px;
+ position: relative;
+ background-color: white;
+ }
+
+ .pixel {
+ width: 5px;
+ height: 5px;
+ position: absolute;
+ background-color: red;
+ }
+ </style>
+</head>
+
+<body>
+ <div class="container" id="container"></div>
+</body>
+
+<script>
+ window.addEventListener('load', () => registerHandlers());
+
+ function registerHandlers() {
+ const container = document.getElementById('container');
+ let isDrawing = false;
+
+ container.addEventListener('pointerdown', (e) => {
+ isDrawing = true;
+ colorPixel(e);
+ });
+ document.addEventListener('pointerup', () => { isDrawing = false; });
+ container.addEventListener('pointermove', colorPixel);
+
+ function colorPixel(e) {
+ if (!isDrawing) return;
+
+ const x = Math.floor((e.clientX - container.offsetLeft) / 5) * 5;
+ const y = Math.floor((e.clientY - container.offsetTop) / 5) * 5;
+
+ const pixel = document.createElement('div');
+ pixel.classList.add('pixel');
+ pixel.style.left = `${x}px`;
+ pixel.style.top = `${y}px`;
+
+ container.appendChild(pixel);
+ }
+ }
+</script>
+
+</html>
diff --git a/cobalt/demos/content/multi-encrypted-video/multi-encrypted-video.html b/cobalt/demos/content/multi-encrypted-video/multi-encrypted-video.html
index 36e504f..8e3297a 100644
--- a/cobalt/demos/content/multi-encrypted-video/multi-encrypted-video.html
+++ b/cobalt/demos/content/multi-encrypted-video/multi-encrypted-video.html
@@ -23,7 +23,7 @@
margin: 0;
}
- #player-layer {
+ #primary-player-layer {
width: 100%;
height: 100%;
}
@@ -33,13 +33,14 @@
height: 100%;
}
- #ui-layer {
+ #secondary-player-layer {
position: absolute;
- top: 15%;
- height: 85%;
+ top: 60%;
+ height: 40%;
width: 100%;
- background-color: rgba(33, 33, 33, .75);
padding: 24px;
+ display: flex;
+ justify-content: center;
}
.item {
@@ -48,22 +49,23 @@
display: inline-block;
margin: 24px;
vertical-align: middle;
+ background-color: rgba(33, 33, 33, .75);
}
</style>
</head>
<body>
- <div id="player-layer">
- <video class="primary" id="primary-video" muted="1" autoplay="1"></video>
+ <div id="primary-player-layer">
+ <video id="primary-video" muted="1" autoplay="1"></video>
</div>
- <div id="ui-layer">
- <div class="item" style="background-color: #D44">
- <video class="secondary" id="secondary-video-1" muted="1" autoplay="1"></video>
+ <div id="secondary-player-layer">
+ <div class="item">
+ <video id="secondary-video-1" muted="1" autoplay="1"></video>
</div>
- <div class="item" style="background-color: #4D4">
- <video class="secondary" id="secondary-video-2" muted="1" autoplay="1"></video>
+ <div class="item">
+ <video id="secondary-video-2" muted="1" autoplay="1"></video>
</div>
- <div class="item" style="background-color: #44D">
- <video class="secondary" id="secondary-video-3" muted="1" autoplay="1"></video>
+ <div class="item">
+ <video id="secondary-video-3" muted="1" autoplay="1"></video>
</div>
</div>
<script src="multi-encrypted-video.js"></script>
diff --git a/cobalt/demos/content/multi-encrypted-video/multi-encrypted-video.js b/cobalt/demos/content/multi-encrypted-video/multi-encrypted-video.js
index 028d11c..27dcddf 100644
--- a/cobalt/demos/content/multi-encrypted-video/multi-encrypted-video.js
+++ b/cobalt/demos/content/multi-encrypted-video/multi-encrypted-video.js
@@ -12,6 +12,58 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+
+// Dictionary mapping string descriptions to media file descriptions in the
+// form of [contentType, url, maxVideoCapabilities (for videos only), licenseUrl]
+const MEDIA_FILES = {
+ 'av1_720p_60fps_drm': {
+ contentType: 'video/mp4; codecs="av01.0.05M.08"',
+ url: 'https://storage.googleapis.com/ytlr-cert.appspot.com/test-materials/media/av1-senc/sdr_720p60.mp4',
+ maxVideoCapabilities: 'width=1280; height=720',
+ licenseUrl: 'https://dash-mse-test.appspot.com/api/drm/widevine?drm_system=widevine&source=YOUTUBE&ip=0.0.0.0&ipbits=0&expire=19000000000&key=ik0&sparams=ip,ipbits,expire,drm_system,source,video_id&video_id=6508f99557a8385f&signature=5153900DAC410803EC269D252DAAA82BA6D8B825.495E631E406584A8EFCB4E9C9F3D45F6488B94E4',
+ },
+ // 40 MB
+ 'h264_720p_24fps_drm': {
+ contentType: 'video/mp4; codecs="avc1.640028"',
+ url: 'http://yt-dash-mse-test.commondatastorage.googleapis.com/media/oops_cenc-20121114-145-no-clear-start.mp4',
+ maxVideoCapabilities: 'width=1280; height=720',
+ licenseUrl: 'https://dash-mse-test.appspot.com/api/drm/widevine?drm_system=widevine&source=YOUTUBE&ip=0.0.0.0&ipbits=0&expire=19000000000&key=test_key1&sparams=ip,ipbits,expire,drm_system,source,video_id&video_id=03681262dc412c06&signature=9C4BE99E6F517B51FED1F0B3B31966D3C5DAB9D6.6A1F30BB35F3A39A4CA814B731450D4CBD198FFD',
+ },
+ // 38 MB
+ 'h264_720p_60fps_drm': {
+ contentType: 'video/mp4; codecs="avc1.640028"',
+ url: 'https://storage.googleapis.com/ytlr-cert.appspot.com/test-materials/media/drml3NoHdcp_h264_720p_60fps_cenc.mp4',
+ maxVideoCapabilities: 'width=1280; height=720',
+ licenseUrl: 'https://dash-mse-test.appspot.com/api/drm/widevine?drm_system=widevine&source=YOUTUBE&ip=0.0.0.0&ipbits=0&expire=19000000000&key=test_key1&sparams=ip,ipbits,expire,drm_system,source,video_id&video_id=03681262dc412c06&signature=9C4BE99E6F517B51FED1F0B3B31966D3C5DAB9D6.6A1F30BB35F3A39A4CA814B731450D4CBD198FFD',
+ },
+ // 32 MB
+ 'vp9_720p_60fps_drm': {
+ contentType: 'video/webm; codecs="vp9"',
+ url: 'https://storage.googleapis.com/ytlr-cert.appspot.com/test-materials/media/drml3NoHdcp_vp9_720p_60fps_enc.webm',
+ maxVideoCapabilities: 'width=1280; height=720',
+ licenseUrl: 'https://dash-mse-test.appspot.com/api/drm/widevine?drm_system=widevine&source=YOUTUBE&ip=0.0.0.0&ipbits=0&expire=19000000000&key=test_key1&sparams=ip,ipbits,expire,drm_system,source,video_id&video_id=f320151fa3f061b2&signature=81E7B33929F9F35922F7D2E96A5E7AC36F3218B2.673F553EE51A48438AE5E707AEC87A071B4FEF65'
+ },
+ // 1 MB
+ // Mono won't work with tunnel mode on many devices.
+ 'aac_mono_drm': {
+ contentType: 'audio/mp4; codecs="mp4a.40.2"',
+ url: 'http://yt-dash-mse-test.commondatastorage.googleapis.com/media/oops_cenc-20121114-148.mp4',
+ licenseUrl: 'https://dash-mse-test.appspot.com/api/drm/widevine?drm_system=widevine&source=YOUTUBE&ip=0.0.0.0&ipbits=0&expire=19000000000&key=test_key1&sparams=ip,ipbits,expire,drm_system,source,video_id&video_id=03681262dc412c06&signature=9C4BE99E6F517B51FED1F0B3B31966D3C5DAB9D6.6A1F30BB35F3A39A4CA814B731450D4CBD198FFD',
+ },
+ // 2.8 MB
+ 'aac_clear': {
+ contentType: 'audio/mp4; codecs="mp4a.40.2"',
+ url: 'http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car-20120827-8c.mp4',
+ },
+ // 1.7 MB
+ 'opus_clear': {
+ contentType: 'audio/webm; codecs="opus"',
+ url: 'https://storage.googleapis.com/ytlr-cert.appspot.com/test-materials/media/car_opus_med.webm',
+ },
+};
+
+mediaCache = {}
+
function fetchArrayBuffer(method, url, body, callback) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
@@ -22,6 +74,16 @@
xhr.send(body);
}
+async function fetchMediaData(mediaFileId) {
+ if (mediaFileId in mediaCache) {
+ return mediaCache[mediaFileId];
+ }
+
+ const response = await fetch(MEDIA_FILES[mediaFileId].url);
+ mediaCache[mediaFileId] = await response.arrayBuffer();
+ return mediaCache[mediaFileId];
+}
+
function extractLicense(licenseArrayBuffer) {
var licenseArray = new Uint8Array(licenseArrayBuffer);
var licenseStartIndex = licenseArray.length - 2;
@@ -37,80 +99,156 @@
return licenseArray.subarray(licenseStartIndex);
}
-var videoContentType = 'video/mp4; codecs="avc1.640028"';
-var audioContentType = 'audio/mp4; codecs="mp4a.40.2"';
-
-function play(videoElementId, keySystem) {
- navigator.requestMediaKeySystemAccess(keySystem, [{
- 'initDataTypes': ['cenc'],
- 'videoCapabilities': [{'contentType': videoContentType}],
- 'audioCapabilities': [{'contentType': audioContentType}]
- }]).then(function(mediaKeySystemAccess) {
- return mediaKeySystemAccess.createMediaKeys();
- }).then(function(mediaKeys) {
- var videoElement = document.getElementById(videoElementId);
-
- if (videoElementId != 'primary-video') {
- videoElement.setMaxVideoCapabilities('width=1280; height=720');
+async function createMediaKeySystem(isPrimaryVideo, audioContentType, videoContentType) {
+ const keySystems = isPrimaryVideo ? ['com.widevine.alpha'] : ['com.youtube.widevine.l3', 'com.widevine.alpha'];
+ for (keySystem of keySystems) {
+ try {
+ mediaKeySystemAccess = await navigator.requestMediaKeySystemAccess(keySystem, [{
+ 'initDataTypes': ['cenc', 'webm'],
+ 'audioCapabilities': [{'contentType': audioContentType}],
+ 'videoCapabilities': [{'contentType': videoContentType}]}]);
+ return mediaKeySystemAccess.createMediaKeys();
+ } catch {
+ console.log('create keySystem ' + keySystem + ' failed.')
+ continue;
}
-
- if (mediaKeys.getMetrics) {
- console.log('Found getMetrics(), calling it ...');
- try {
- mediaKeys.getMetrics();
- console.log('Calling getMetrics() succeeded.');
- } catch(e) {
- console.log('Calling getMetrics() failed.');
- }
- }
-
- videoElement.setMediaKeys(mediaKeys);
-
- mediaKeySession = mediaKeys.createSession();
- mediaKeySession.addEventListener('message', function(messageEvent) {
- var licenseServerUrl = 'https://dash-mse-test.appspot.com/api/drm/widevine?drm_system=widevine&source=YOUTUBE&ip=0.0.0.0&ipbits=0&expire=19000000000&key=test_key1&sparams=ip,ipbits,expire,drm_system,source,video_id&video_id=03681262dc412c06&signature=9C4BE99E6F517B51FED1F0B3B31966D3C5DAB9D6.6A1F30BB35F3A39A4CA814B731450D4CBD198FFD';
- fetchArrayBuffer('POST', licenseServerUrl, messageEvent.message,
- function(licenseArrayBuffer) {
- mediaKeySession.update(extractLicense(licenseArrayBuffer));
- });
- });
-
- videoElement.addEventListener('encrypted', function(encryptedEvent) {
- mediaKeySession.generateRequest(
- encryptedEvent.initDataType, encryptedEvent.initData);
- });
-
- var mediaSource = new MediaSource();
- mediaSource.addEventListener('sourceopen', function() {
- var videoSourceBuffer = mediaSource.addSourceBuffer(videoContentType);
- fetchArrayBuffer('GET',
- 'http://yt-dash-mse-test.commondatastorage.googleapis.com/media/oops_cenc-20121114-145-no-clear-start.mp4',
- null,
- function(videoArrayBuffer) {
- videoSourceBuffer.appendBuffer(videoArrayBuffer);
- });
-
- var audioSourceBuffer = mediaSource.addSourceBuffer(audioContentType);
- fetchArrayBuffer('GET',
- 'http://yt-dash-mse-test.commondatastorage.googleapis.com/media/oops_cenc-20121114-148.mp4',
- null,
- function(audioArrayBuffer) {
- audioSourceBuffer.appendBuffer(audioArrayBuffer);
- });
- });
-
- videoElement.src = URL.createObjectURL(mediaSource);
- videoElement.play();
- });
+ }
}
-play('primary-video', 'com.widevine.alpha');
-window.setTimeout(function() {
- play('secondary-video-1', 'com.youtube.widevine.l3');
-}, 10000);
-window.setTimeout(function() {
- play('secondary-video-2', 'com.youtube.widevine.l3');
-}, 20000);
-window.setTimeout(function() {
- play('secondary-video-3', 'com.youtube.widevine.l3');
-}, 30000);
+function createTunnelModeContentType(videoContentType, tunnelModeAttributeValue) {
+ return videoContentType + '; tunnelmode=' + tunnelModeAttributeValue;
+}
+
+function isTunnelModeSupported(videoContentType) {
+ if (!MediaSource.isTypeSupported(videoContentType)) {
+ // If the content type isn't supported at all, it won't be supported in
+ // tunnel mode.
+ return false;
+ }
+ if (MediaSource.isTypeSupported(createTunnelModeContentType(videoContentType, 'invalid'))) {
+ // The implementation doesn't understand the `tunnelmode` attribute.
+ return false;
+ }
+ return MediaSource.isTypeSupported(createTunnelModeContentType(videoContentType, 'true'));
+}
+
+async function play(videoElementId, videoFileId, optionalAudioFileId) {
+ const isPrimaryVideo = videoElementId == 'primary-video';
+
+ videoContentType = MEDIA_FILES[videoFileId].contentType;
+ if (isTunnelModeSupported(videoContentType)) {
+ videoContentType = createTunnelModeContentType(videoContentType, 'true');
+ }
+
+ var mediaKeys = await createMediaKeySystem(isPrimaryVideo, optionalAudioFileId ? MEDIA_FILES[optionalAudioFileId].contentType : MEDIA_FILES['opus_clear'].contentType, videoContentType);
+ var videoElement = document.getElementById(videoElementId);
+
+ if (!isPrimaryVideo && videoElement.setMaxVideoCapabilities) {
+ videoElement.setMaxVideoCapabilities(MEDIA_FILES[videoFileId].maxVideoCapabilities);
+ }
+
+ videoElement.setMediaKeys(mediaKeys);
+
+ mediaKeySession = mediaKeys.createSession();
+ var licenseServerUrl = MEDIA_FILES[videoFileId].licenseUrl;
+ mediaKeySession.addEventListener('message', function(messageEvent) {
+ fetchArrayBuffer('POST', licenseServerUrl, messageEvent.message,
+ function(licenseArrayBuffer) {
+ mediaKeySession.update(extractLicense(licenseArrayBuffer));
+ });
+ });
+
+ videoElement.addEventListener('encrypted', function(encryptedEvent) {
+ mediaKeySession.generateRequest(
+ encryptedEvent.initDataType, encryptedEvent.initData);
+ });
+
+ var mediaSource = new MediaSource();
+ mediaSource.addEventListener('sourceopen', async function() {
+ var videoSourceBuffer = mediaSource.addSourceBuffer(videoContentType);
+ var audioSourceBuffer;
+
+ if (optionalAudioFileId) {
+ audioSourceBuffer = mediaSource.addSourceBuffer(MEDIA_FILES[optionalAudioFileId].contentType);
+ }
+
+ var videoArrayBuffer = await fetchMediaData(videoFileId);
+ videoSourceBuffer.appendBuffer(videoArrayBuffer);
+
+ if (audioSourceBuffer) {
+ var audioArrayBuffer = await fetchMediaData(optionalAudioFileId);
+ audioSourceBuffer.appendBuffer(audioArrayBuffer);
+ }
+ });
+
+ videoElement.src = URL.createObjectURL(mediaSource);
+ videoElement.play();
+}
+
+function getGetParameters() {
+ var parsedParameters = {};
+
+ const urlComponents = window.location.href.split('?');
+ if (urlComponents.length < 2) {
+ return parsedParameters;
+ }
+
+ const query = urlComponents[1];
+ const parameters = query.split('&');
+
+ for (parameter of parameters) {
+ const split = parameter.split('=');
+ if (split.length == 0) {
+ continue;
+ }
+ if (split.length == 1) {
+ parsedParameters[split[0]] = '';
+ } else {
+ parsedParameters[split[0]] = split[1];
+ }
+ }
+
+ return parsedParameters;
+}
+
+function populateMediaFileIds() {
+ var mediaFileIds = [];
+ const getParameters = getGetParameters();
+
+ mediaFileIds['video0'] = getParameters['video0'] ?? 'vp9_720p_60fps_drm';
+ mediaFileIds['video1'] = getParameters['video1'] ?? 'h264_720p_24fps_drm';
+ mediaFileIds['video2'] = getParameters['video2'] ?? 'vp9_720p_60fps_drm';
+ mediaFileIds['video3'] = getParameters['video3'] ?? 'h264_720p_24fps_drm';
+ mediaFileIds['audio'] = getParameters['audio'] ?? 'opus_clear';
+
+ return mediaFileIds;
+}
+
+async function prefetchMediaData(mediaFileIds) {
+ for (mediaFileId of Object.keys(mediaFileIds)) {
+ await fetchMediaData(mediaFileIds[mediaFileId]);
+ }
+}
+
+async function main() {
+ if (window.h5vcc && window.h5vcc.settings) {
+ h5vcc.settings.set('MediaSource.EnableAvoidCopyingArrayBuffer', 1);
+ }
+
+ const mediaFileIds = populateMediaFileIds();
+ await prefetchMediaData(mediaFileIds);
+
+ play('primary-video', mediaFileIds['video0'], mediaFileIds['audio']);
+ window.setTimeout(function() {
+ play('secondary-video-1', mediaFileIds['video1']);
+ }, 10000);
+ window.setTimeout(function() {
+ play('secondary-video-2', mediaFileIds['video2']);
+ }, 20000);
+ window.setTimeout(function() {
+ play('secondary-video-3', mediaFileIds['video3']);
+ }, 30000);
+}
+
+
+main();
diff --git a/cobalt/dom/html_element.cc b/cobalt/dom/html_element.cc
index ef21c62..9ddbdcb 100644
--- a/cobalt/dom/html_element.cc
+++ b/cobalt/dom/html_element.cc
@@ -161,6 +161,22 @@
base::LazyInstance<NonTrivialStaticFields>::DestructorAtExit
non_trivial_static_fields = LAZY_INSTANCE_INITIALIZER;
+void InvalidateScrollAreaCacheOfAncestors(Node* node) {
+ for (Node* ancestor_node = node; ancestor_node;
+ ancestor_node = ancestor_node->parent_node()) {
+ Element* ancestor_element = ancestor_node->AsElement();
+ if (!ancestor_element) {
+ continue;
+ }
+ HTMLElement* ancestor_html_element = ancestor_element->AsHTMLElement();
+ if (!ancestor_html_element) {
+ continue;
+ }
+ if (ancestor_html_element->layout_boxes())
+ ancestor_html_element->layout_boxes()->scroll_area_cache().reset();
+ }
+}
+
} // namespace
void HTMLElement::RuleMatchingState::Clear() {
@@ -1167,6 +1183,7 @@
}
void HTMLElement::InvalidateLayoutBoxSizes() {
+ InvalidateScrollAreaCacheOfAncestors(parent_node());
if (layout_boxes_) {
layout_boxes_->InvalidateSizes();
@@ -1232,6 +1249,9 @@
void HTMLElement::OnUiNavScroll(SbTimeMonotonic /* time */) {
Document* document = node_document();
+ if (document->hidden()) {
+ return;
+ }
scoped_refptr<Window> window(document ? document->window() : nullptr);
DispatchEvent(new UIEvent(base::Tokens::scroll(), web::Event::kNotBubbles,
web::Event::kNotCancelable, window));
diff --git a/cobalt/dom/html_media_element.cc b/cobalt/dom/html_media_element.cc
index c3376ea..da10b2e 100644
--- a/cobalt/dom/html_media_element.cc
+++ b/cobalt/dom/html_media_element.cc
@@ -1755,6 +1755,10 @@
exception_state);
return;
}
+
+ LOG(INFO) << "max video capabilities is changed from \""
+ << max_video_capabilities_ << "\" to \"" << max_video_capabilities
+ << "\"";
max_video_capabilities_ = max_video_capabilities;
}
diff --git a/cobalt/dom/layout_boxes.h b/cobalt/dom/layout_boxes.h
index 440e982..b81a446 100644
--- a/cobalt/dom/layout_boxes.h
+++ b/cobalt/dom/layout_boxes.h
@@ -15,6 +15,8 @@
#ifndef COBALT_DOM_LAYOUT_BOXES_H_
#define COBALT_DOM_LAYOUT_BOXES_H_
+#include <utility>
+
#include "base/memory/ref_counted.h"
#include "cobalt/dom/directionality.h"
#include "cobalt/dom/dom_rect_list.h"
@@ -89,6 +91,9 @@
// Invalidate the layout box's render tree nodes.
virtual void InvalidateRenderTreeNodes() = 0;
+ virtual base::Optional<std::pair<dom::Directionality, math::RectF>>&
+ scroll_area_cache() = 0;
+
// Update the navigation item associated with the layout boxes.
virtual void SetUiNavItem(
const scoped_refptr<ui_navigation::NavItem>& item) = 0;
diff --git a/cobalt/dom/source_buffer.cc b/cobalt/dom/source_buffer.cc
index 039303f..1ad5a59 100644
--- a/cobalt/dom/source_buffer.cc
+++ b/cobalt/dom/source_buffer.cc
@@ -521,6 +521,39 @@
tracer->Trace(video_tracks_);
}
+size_t SourceBuffer::memory_limit(
+ script::ExceptionState* exception_state) const {
+ if (!chunk_demuxer_) {
+ web::DOMException::Raise(web::DOMException::kInvalidStateErr,
+ exception_state);
+ return 0;
+ }
+
+ size_t memory_limit = chunk_demuxer_->GetSourceBufferStreamMemoryLimit(id_);
+
+ if (memory_limit == 0) {
+ web::DOMException::Raise(web::DOMException::kInvalidStateErr,
+ exception_state);
+ }
+ return memory_limit;
+}
+
+void SourceBuffer::set_memory_limit(size_t memory_limit,
+ script::ExceptionState* exception_state) {
+ if (!chunk_demuxer_) {
+ web::DOMException::Raise(web::DOMException::kInvalidStateErr,
+ exception_state);
+ return;
+ }
+
+ if (memory_limit == 0) {
+ web::DOMException::Raise(web::DOMException::kNotSupportedErr,
+ exception_state);
+ return;
+ }
+ chunk_demuxer_->SetSourceBufferStreamMemoryLimit(id_, memory_limit);
+}
+
void SourceBuffer::OnInitSegmentReceived(std::unique_ptr<MediaTracks> tracks) {
if (!first_initialization_segment_received_) {
media_source_->SetSourceBufferActive(this, true);
diff --git a/cobalt/dom/source_buffer.h b/cobalt/dom/source_buffer.h
index 5869760..0da8e81 100644
--- a/cobalt/dom/source_buffer.h
+++ b/cobalt/dom/source_buffer.h
@@ -144,6 +144,9 @@
DEFINE_WRAPPABLE_TYPE(SourceBuffer);
void TraceMembers(script::Tracer* tracer) override;
+ size_t memory_limit(script::ExceptionState* exception_state) const;
+ void set_memory_limit(size_t limit, script::ExceptionState* exception_state);
+
private:
typedef ::media::MediaTracks MediaTracks;
typedef script::ArrayBuffer ArrayBuffer;
diff --git a/cobalt/dom/source_buffer.idl b/cobalt/dom/source_buffer.idl
index 0d81b03..a963802 100644
--- a/cobalt/dom/source_buffer.idl
+++ b/cobalt/dom/source_buffer.idl
@@ -39,4 +39,10 @@
// `InvalidStateError` if the SourceBuffer object has been removed from the
// MediaSource object.
[RaisesException] readonly attribute double writeHead;
+
+ // Non standard stream memory limit modifier. This will override the default
+ // stream memory limit which is tied to the resolution of the video.
+ // This will be passed down to the SourceBufferStream associated with this
+ // instance.
+ [RaisesException] attribute unsigned long long memoryLimit;
};
diff --git a/cobalt/dom/testing/mock_layout_boxes.h b/cobalt/dom/testing/mock_layout_boxes.h
index 7065808..3d00913 100644
--- a/cobalt/dom/testing/mock_layout_boxes.h
+++ b/cobalt/dom/testing/mock_layout_boxes.h
@@ -15,6 +15,8 @@
#ifndef COBALT_DOM_TESTING_MOCK_LAYOUT_BOXES_H_
#define COBALT_DOM_TESTING_MOCK_LAYOUT_BOXES_H_
+#include <utility>
+
#include "cobalt/dom/layout_boxes.h"
namespace cobalt {
@@ -57,6 +59,8 @@
MOCK_METHOD0(InvalidateSizes, void());
MOCK_METHOD0(InvalidateCrossReferences, void());
MOCK_METHOD0(InvalidateRenderTreeNodes, void());
+ MOCK_METHOD0(scroll_area_cache,
+ base::Optional<std::pair<dom::Directionality, math::RectF>>&());
MOCK_METHOD1(SetUiNavItem,
void(const scoped_refptr<ui_navigation::NavItem>& item));
};
diff --git a/cobalt/h5vcc/BUILD.gn b/cobalt/h5vcc/BUILD.gn
index 901f668..1de4356 100644
--- a/cobalt/h5vcc/BUILD.gn
+++ b/cobalt/h5vcc/BUILD.gn
@@ -45,6 +45,8 @@
"h5vcc_event_listener_container.h",
"h5vcc_metrics.cc",
"h5vcc_metrics.h",
+ "h5vcc_net_log.cc",
+ "h5vcc_net_log.h",
"h5vcc_platform_service.cc",
"h5vcc_platform_service.h",
"h5vcc_runtime.cc",
diff --git a/cobalt/h5vcc/h5vcc.cc b/cobalt/h5vcc/h5vcc.cc
index 8d957b8..ebf7b56 100644
--- a/cobalt/h5vcc/h5vcc.cc
+++ b/cobalt/h5vcc/h5vcc.cc
@@ -31,7 +31,8 @@
audio_config_array_ = new H5vccAudioConfigArray();
c_val_ = new dom::CValView();
crash_log_ = new H5vccCrashLog();
- metrics_ = new H5vccMetrics(settings.persistent_settings);
+ metrics_ =
+ new H5vccMetrics(settings.persistent_settings, settings.event_dispatcher);
runtime_ = new H5vccRuntime(settings.event_dispatcher);
settings_ =
new H5vccSettings(settings.set_web_setting_func, settings.media_module,
@@ -44,6 +45,7 @@
storage_ =
new H5vccStorage(settings.network_module, settings.persistent_settings);
trace_event_ = new H5vccTraceEvent();
+ net_log_ = new H5vccNetLog(settings.network_module);
#if SB_IS(EVERGREEN)
updater_ = new H5vccUpdater(settings.updater_module);
system_ = new H5vccSystem(updater_);
@@ -81,6 +83,7 @@
tracer->Trace(storage_);
tracer->Trace(system_);
tracer->Trace(trace_event_);
+ tracer->Trace(net_log_);
#if SB_IS(EVERGREEN)
tracer->Trace(updater_);
#endif
diff --git a/cobalt/h5vcc/h5vcc.h b/cobalt/h5vcc/h5vcc.h
index 0d60a7c..0852eaa 100644
--- a/cobalt/h5vcc/h5vcc.h
+++ b/cobalt/h5vcc/h5vcc.h
@@ -26,6 +26,7 @@
#include "cobalt/h5vcc/h5vcc_audio_config_array.h"
#include "cobalt/h5vcc/h5vcc_crash_log.h"
#include "cobalt/h5vcc/h5vcc_metrics.h"
+#include "cobalt/h5vcc/h5vcc_net_log.h"
#include "cobalt/h5vcc/h5vcc_runtime.h"
#include "cobalt/h5vcc/h5vcc_settings.h"
#include "cobalt/h5vcc/h5vcc_storage.h"
@@ -89,6 +90,7 @@
const scoped_refptr<H5vccTraceEvent>& trace_event() const {
return trace_event_;
}
+ const scoped_refptr<H5vccNetLog>& net_log() const { return net_log_; }
#if SB_IS(EVERGREEN)
const scoped_refptr<H5vccUpdater>& updater() const { return updater_; }
#endif
@@ -108,6 +110,7 @@
scoped_refptr<H5vccStorage> storage_;
scoped_refptr<H5vccSystem> system_;
scoped_refptr<H5vccTraceEvent> trace_event_;
+ scoped_refptr<H5vccNetLog> net_log_;
#if SB_IS(EVERGREEN)
scoped_refptr<H5vccUpdater> updater_;
#endif
diff --git a/cobalt/h5vcc/h5vcc.idl b/cobalt/h5vcc/h5vcc.idl
index 188e66b..2beb5dc 100644
--- a/cobalt/h5vcc/h5vcc.idl
+++ b/cobalt/h5vcc/h5vcc.idl
@@ -42,6 +42,7 @@
readonly attribute H5vccStorage storage;
readonly attribute H5vccSystem system;
readonly attribute H5vccTraceEvent traceEvent;
+ readonly attribute H5vccNetLog netLog;
[Conditional=SB_IS_EVERGREEN]
readonly attribute H5vccUpdater updater;
};
diff --git a/cobalt/h5vcc/h5vcc_metrics.cc b/cobalt/h5vcc/h5vcc_metrics.cc
index 0c9062a..583eafd 100644
--- a/cobalt/h5vcc/h5vcc_metrics.cc
+++ b/cobalt/h5vcc/h5vcc_metrics.cc
@@ -17,6 +17,9 @@
#include <string>
#include "base/values.h"
+#include "cobalt/base/event.h"
+#include "cobalt/base/event_dispatcher.h"
+#include "cobalt/base/on_metric_upload_event.h"
#include "cobalt/browser/metrics/cobalt_metrics_service_client.h"
#include "cobalt/browser/metrics/cobalt_metrics_services_manager.h"
#include "cobalt/h5vcc/h5vcc_metric_type.h"
@@ -25,17 +28,34 @@
namespace cobalt {
namespace h5vcc {
+
+H5vccMetrics::H5vccMetrics(
+ persistent_storage::PersistentSettings* persistent_settings,
+ base::EventDispatcher* event_dispatcher)
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ persistent_settings_(persistent_settings),
+ event_dispatcher_(event_dispatcher) {
+ DCHECK(event_dispatcher_);
+ on_metric_upload_event_callback_ =
+ base::Bind(&H5vccMetrics::OnMetricUploadEvent, base::Unretained(this));
+ event_dispatcher_->AddEventCallback(base::OnMetricUploadEvent::TypeId(),
+ on_metric_upload_event_callback_);
+}
+
+H5vccMetrics::~H5vccMetrics() {
+ event_dispatcher_->RemoveEventCallback(base::OnMetricUploadEvent::TypeId(),
+ on_metric_upload_event_callback_);
+}
+
+void H5vccMetrics::OnMetricUploadEvent(const base::Event* event) {
+ std::unique_ptr<base::OnMetricUploadEvent> on_metric_upload_event(
+ new base::OnMetricUploadEvent(event));
+ RunEventHandler(on_metric_upload_event->metric_type(),
+ on_metric_upload_event->serialized_proto());
+}
+
void H5vccMetrics::OnMetricEvent(
const h5vcc::MetricEventHandlerWrapper::ScriptValue& event_handler) {
- if (!uploader_callback_) {
- run_event_handler_callback_ = std::make_unique<
- cobalt::browser::metrics::CobaltMetricsUploaderCallback>(
- base::BindRepeating(&H5vccMetrics::RunEventHandler,
- base::Unretained(this)));
- browser::metrics::CobaltMetricsServicesManager::GetInstance()
- ->SetOnUploadHandler(run_event_handler_callback_.get());
- }
-
uploader_callback_ =
new h5vcc::MetricEventHandlerWrapper(this, event_handler);
}
@@ -43,16 +63,20 @@
void H5vccMetrics::RunEventHandler(
const cobalt::h5vcc::H5vccMetricType& metric_type,
const std::string& serialized_proto) {
- task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&H5vccMetrics::RunEventHandlerInternal, base::Unretained(this),
- metric_type, serialized_proto));
+ if (task_runner_ && task_runner_->HasAtLeastOneRef()) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&H5vccMetrics::RunEventHandlerInternal,
+ base::Unretained(this), metric_type, serialized_proto));
+ }
}
void H5vccMetrics::RunEventHandlerInternal(
const cobalt::h5vcc::H5vccMetricType& metric_type,
const std::string& serialized_proto) {
- uploader_callback_->callback.value().Run(metric_type, serialized_proto);
+ if (uploader_callback_ != nullptr && uploader_callback_->HasAtLeastOneRef()) {
+ uploader_callback_->callback.value().Run(metric_type, serialized_proto);
+ }
}
void H5vccMetrics::Enable() { ToggleMetricsEnabled(true); }
diff --git a/cobalt/h5vcc/h5vcc_metrics.h b/cobalt/h5vcc/h5vcc_metrics.h
index a8f69ea..93a444a 100644
--- a/cobalt/h5vcc/h5vcc_metrics.h
+++ b/cobalt/h5vcc/h5vcc_metrics.h
@@ -20,7 +20,8 @@
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "cobalt/browser/metrics/cobalt_metrics_uploader_callback.h"
+#include "cobalt/base/event.h"
+#include "cobalt/base/event_dispatcher.h"
#include "cobalt/h5vcc/h5vcc_metric_type.h"
#include "cobalt/h5vcc/metric_event_handler_wrapper.h"
#include "cobalt/persistent_storage/persistent_settings.h"
@@ -43,14 +44,15 @@
typedef MetricEventHandler H5vccMetricEventHandler;
explicit H5vccMetrics(
- persistent_storage::PersistentSettings* persistent_settings)
- : task_runner_(base::ThreadTaskRunnerHandle::Get()),
- persistent_settings_(persistent_settings) {}
+ persistent_storage::PersistentSettings* persistent_settings,
+ base::EventDispatcher* event_dispatcher);
+
+ ~H5vccMetrics();
H5vccMetrics(const H5vccMetrics&) = delete;
H5vccMetrics& operator=(const H5vccMetrics&) = delete;
- // Binds an event handler that will be invoked every time Cobalt wants to
+ // Binds a JS event handler that will be invoked every time Cobalt wants to
// upload a metrics payload.
void OnMetricEvent(
const MetricEventHandlerWrapper::ScriptValue& event_handler);
@@ -81,14 +83,20 @@
const cobalt::h5vcc::H5vccMetricType& metric_type,
const std::string& serialized_proto);
- scoped_refptr<h5vcc::MetricEventHandlerWrapper> uploader_callback_;
+ // Handler method triggered when EventDispatcher sends OnMetricUploadEvents.
+ void OnMetricUploadEvent(const base::Event* event);
- std::unique_ptr<cobalt::browser::metrics::CobaltMetricsUploaderCallback>
- run_event_handler_callback_;
+ scoped_refptr<h5vcc::MetricEventHandlerWrapper> uploader_callback_;
scoped_refptr<base::SingleThreadTaskRunner> const task_runner_;
persistent_storage::PersistentSettings* persistent_settings_;
+
+ // Non-owned reference used to receive application event callbacks, namely
+ // metric log upload events.
+ base::EventDispatcher* event_dispatcher_;
+
+ base::EventCallback on_metric_upload_event_callback_;
};
} // namespace h5vcc
diff --git a/cobalt/h5vcc/h5vcc_net_log.cc b/cobalt/h5vcc/h5vcc_net_log.cc
new file mode 100644
index 0000000..8a5b3a2
--- /dev/null
+++ b/cobalt/h5vcc/h5vcc_net_log.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 "cobalt/h5vcc/h5vcc_net_log.h"
+
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "cobalt/base/cobalt_paths.h"
+#include "cobalt/network/cobalt_net_log.h"
+
+namespace cobalt {
+namespace h5vcc {
+
+H5vccNetLog::H5vccNetLog(cobalt::network::NetworkModule* network_module)
+ : network_module_{network_module} {}
+
+void H5vccNetLog::Start() { network_module_->StartNetLog(); }
+
+void H5vccNetLog::Stop() { network_module_->StopNetLog(); }
+
+std::string H5vccNetLog::StopAndRead() {
+ base::FilePath netlog_path = network_module_->StopNetLog();
+ std::string netlog_output{};
+ if (!netlog_path.empty()) {
+ ReadFileToString(netlog_path, &netlog_output);
+ }
+ return netlog_output;
+}
+
+
+} // namespace h5vcc
+} // namespace cobalt
diff --git a/cobalt/h5vcc/h5vcc_net_log.h b/cobalt/h5vcc/h5vcc_net_log.h
new file mode 100644
index 0000000..801839b
--- /dev/null
+++ b/cobalt/h5vcc/h5vcc_net_log.h
@@ -0,0 +1,45 @@
+// 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_H5VCC_H5VCC_NET_LOG_H_
+#define COBALT_H5VCC_H5VCC_NET_LOG_H_
+
+#include <string>
+
+#include "cobalt/network/network_module.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace h5vcc {
+
+class H5vccNetLog : public script::Wrappable {
+ public:
+ explicit H5vccNetLog(cobalt::network::NetworkModule* network_module);
+
+ void Start();
+ void Stop();
+ std::string StopAndRead();
+
+ DEFINE_WRAPPABLE_TYPE(H5vccNetLog);
+
+ private:
+ cobalt::network::NetworkModule* network_module_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(H5vccNetLog);
+};
+
+} // namespace h5vcc
+} // namespace cobalt
+
+#endif // COBALT_H5VCC_H5VCC_NET_LOG_H_
diff --git a/starboard/shared/posix/time_zone_get_name.cc b/cobalt/h5vcc/h5vcc_net_log.idl
similarity index 60%
copy from starboard/shared/posix/time_zone_get_name.cc
copy to cobalt/h5vcc/h5vcc_net_log.idl
index 5a1e013..2c60792 100644
--- a/starboard/shared/posix/time_zone_get_name.cc
+++ b/cobalt/h5vcc/h5vcc_net_log.idl
@@ -1,4 +1,4 @@
-// Copyright 2015 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,13 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/time_zone.h"
-
-#include <time.h>
-
-const char* SbTimeZoneGetName() {
- // TODO: Using tzname assumes that tzset() has been called at some
- // point. That should happen as part of Starboard's main loop initialization,
- // but that doesn't exist yet.
- return tzname[0];
-}
+interface H5vccNetLog {
+ void start();
+ void stop();
+ DOMString stopAndRead();
+};
diff --git a/cobalt/h5vcc/h5vcc_runtime.cc b/cobalt/h5vcc/h5vcc_runtime.cc
index 15f682b..1ec7fcc 100644
--- a/cobalt/h5vcc/h5vcc_runtime.cc
+++ b/cobalt/h5vcc/h5vcc_runtime.cc
@@ -24,7 +24,8 @@
namespace h5vcc {
H5vccRuntime::H5vccRuntime(base::EventDispatcher* event_dispatcher)
: event_dispatcher_(event_dispatcher),
- message_loop_(base::MessageLoop::current()) {
+ task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+ DCHECK(task_runner_);
on_deep_link_ = new H5vccDeepLinkEventTarget(
base::Bind(&H5vccRuntime::GetUnconsumedDeepLink, base::Unretained(this)));
on_pause_ = new H5vccRuntimeEventTarget;
@@ -87,8 +88,9 @@
void H5vccRuntime::OnEventForDeepLink(const base::Event* event) {
std::unique_ptr<base::DeepLinkEvent> deep_link_event(
new base::DeepLinkEvent(event));
- if (base::MessageLoop::current() != message_loop_) {
- message_loop_->task_runner()->PostTask(
+ if (!task_runner_) return;
+ if (!task_runner_->RunsTasksInCurrentSequence()) {
+ task_runner_->PostTask(
FROM_HERE,
base::Bind(&H5vccRuntime::OnDeepLinkEvent, base::Unretained(this),
base::Passed(std::move(deep_link_event))));
diff --git a/cobalt/h5vcc/h5vcc_runtime.h b/cobalt/h5vcc/h5vcc_runtime.h
index 560543f..6c0ebc1 100644
--- a/cobalt/h5vcc/h5vcc_runtime.h
+++ b/cobalt/h5vcc/h5vcc_runtime.h
@@ -19,6 +19,7 @@
#include <string>
#include "base/callback.h"
+#include "base/single_thread_task_runner.h"
#include "base/threading/thread_checker.h"
#include "cobalt/base/deep_link_event.h"
#include "cobalt/base/event_dispatcher.h"
@@ -63,9 +64,9 @@
base::EventCallback deep_link_event_callback_;
base::OnceClosure consumed_callback_;
- // Track the message loop that created this object so deep link events are
- // handled from the same thread.
- base::MessageLoop* message_loop_;
+ // Track the task runner from where this object is created so deep link events
+ // are handled from the same task runner.
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
// Thread checker ensures all calls to DOM element are made from the same
// thread that it is created in.
diff --git a/cobalt/h5vcc/h5vcc_settings.cc b/cobalt/h5vcc/h5vcc_settings.cc
index e6c7b26..4c47039 100644
--- a/cobalt/h5vcc/h5vcc_settings.cc
+++ b/cobalt/h5vcc/h5vcc_settings.cc
@@ -23,16 +23,6 @@
namespace cobalt {
namespace h5vcc {
-namespace {
-// Only including needed video combinations for the moment.
-// option 0 disables all video codecs except h264
-// option 1 disables all video codecs except av1
-// option 2 disables all video codecs except vp9
-constexpr std::array<const char*, 3> kDisableCodecCombinations{
- {"av01;hev1;hvc1;vp09;vp8.vp9", "avc1;avc3;hev1;hvc1;vp09;vp8;vp9",
- "av01;avc1;avc3;hev1;hvc1;vp8"}};
-}; // namespace
-
H5vccSettings::H5vccSettings(
const SetSettingFunc& set_web_setting_func,
cobalt::media::MediaModule* media_module,
@@ -56,62 +46,47 @@
persistent_settings_(persistent_settings) {
}
-bool H5vccSettings::Set(const std::string& name, int32 value) const {
+bool H5vccSettings::Set(const std::string& name, SetValueType value) const {
const char kMediaPrefix[] = "Media.";
- const char kDisableMediaCodec[] = "DisableMediaCodec";
+ const char kMediaCodecBlockList[] = "MediaCodecBlockList";
const char kNavigatorUAData[] = "NavigatorUAData";
- const char kClientHintHeaders[] = "ClientHintHeaders";
const char kQUIC[] = "QUIC";
#if SB_IS(EVERGREEN)
const char kUpdaterMinFreeSpaceBytes[] = "Updater.MinFreeSpaceBytes";
#endif
- if (name == kDisableMediaCodec &&
- value < static_cast<int32>(kDisableCodecCombinations.size())) {
- can_play_type_handler_->SetDisabledMediaCodecs(
- kDisableCodecCombinations[value]);
+ if (name == kMediaCodecBlockList && value.IsType<std::string>() &&
+ value.AsType<std::string>().size() < 256) {
+ can_play_type_handler_->SetDisabledMediaCodecs(value.AsType<std::string>());
return true;
}
- if (set_web_setting_func_ && set_web_setting_func_.Run(name, value)) {
+ if (set_web_setting_func_ && value.IsType<int32>() &&
+ set_web_setting_func_.Run(name, value.AsType<int32>())) {
return true;
}
- if (name.rfind(kMediaPrefix, 0) == 0) {
- return media_module_ ? media_module_->SetConfiguration(
- name.substr(strlen(kMediaPrefix)), value)
- : false;
+ if (name.rfind(kMediaPrefix, 0) == 0 && value.IsType<int32>()) {
+ return media_module_
+ ? media_module_->SetConfiguration(
+ name.substr(strlen(kMediaPrefix)), value.AsType<int32>())
+ : false;
}
- if (name.compare(kNavigatorUAData) == 0 && value == 1) {
+ if (name.compare(kNavigatorUAData) == 0 && value.IsType<int32>() &&
+ value.AsType<int32>() == 1) {
global_environment_->BindTo("userAgentData", user_agent_data_, "navigator");
return true;
}
- if (name.compare(kClientHintHeaders) == 0) {
- if (!persistent_settings_) {
- return false;
- } else {
- persistent_settings_->SetPersistentSetting(
- network::kClientHintHeadersEnabledPersistentSettingsKey,
- std::make_unique<base::Value>(value));
- // Tell NetworkModule (if exists) to re-query persistent settings.
- if (network_module_) {
- network_module_
- ->SetEnableClientHintHeadersFlagsFromPersistentSettings();
- }
- return true;
- }
- }
-
- if (name.compare(kQUIC) == 0) {
+ if (name.compare(kQUIC) == 0 && value.IsType<int32>()) {
if (!persistent_settings_) {
return false;
} else {
persistent_settings_->SetPersistentSetting(
network::kQuicEnabledPersistentSettingsKey,
- std::make_unique<base::Value>(value != 0));
+ std::make_unique<base::Value>(value.AsType<int32>() != 0));
// Tell NetworkModule (if exists) to re-query persistent settings.
if (network_module_) {
network_module_->SetEnableQuicFromPersistentSettings();
@@ -121,8 +96,8 @@
}
#if SB_IS(EVERGREEN)
- if (name.compare(kUpdaterMinFreeSpaceBytes) == 0) {
- updater_module_->SetMinFreeSpaceBytes(value);
+ if (name.compare(kUpdaterMinFreeSpaceBytes) == 0 && value.IsType<int32>()) {
+ updater_module_->SetMinFreeSpaceBytes(value.AsType<int32>());
return true;
}
#endif
diff --git a/cobalt/h5vcc/h5vcc_settings.h b/cobalt/h5vcc/h5vcc_settings.h
index 34e7a44..f7e10cf 100644
--- a/cobalt/h5vcc/h5vcc_settings.h
+++ b/cobalt/h5vcc/h5vcc_settings.h
@@ -21,6 +21,7 @@
#include "cobalt/network/network_module.h"
#include "cobalt/persistent_storage/persistent_settings.h"
#include "cobalt/script/global_environment.h"
+#include "cobalt/script/union_type.h"
#include "cobalt/script/wrappable.h"
#include "cobalt/web/navigator_ua_data.h"
@@ -39,6 +40,8 @@
typedef base::Callback<bool(const std::string& name, int value)>
SetSettingFunc;
+ typedef script::UnionType2<int32, std::string> SetValueType;
+
H5vccSettings(const SetSettingFunc& set_web_setting_func,
cobalt::media::MediaModule* media_module,
cobalt::media::CanPlayTypeHandler* can_play_type_handler,
@@ -53,7 +56,7 @@
// Returns true when the setting is set successfully or if the setting has
// already been set to the expected value. Returns false when the setting is
// invalid or not set to the expected value.
- bool Set(const std::string& name, int32 value) const;
+ bool Set(const std::string& name, SetValueType value) const;
DEFINE_WRAPPABLE_TYPE(H5vccSettings);
diff --git a/cobalt/h5vcc/h5vcc_settings.idl b/cobalt/h5vcc/h5vcc_settings.idl
index fe250c8..34650a9 100644
--- a/cobalt/h5vcc/h5vcc_settings.idl
+++ b/cobalt/h5vcc/h5vcc_settings.idl
@@ -13,5 +13,5 @@
// limitations under the License.
interface H5vccSettings {
- boolean set(DOMString name, long value);
+ boolean set(DOMString name, (long or DOMString) value);
};
diff --git a/cobalt/h5vcc/h5vcc_storage.cc b/cobalt/h5vcc/h5vcc_storage.cc
index dac5b5c..346c104 100644
--- a/cobalt/h5vcc/h5vcc_storage.cc
+++ b/cobalt/h5vcc/h5vcc_storage.cc
@@ -74,13 +74,12 @@
}
void DeleteCacheResourceTypeDirectory(disk_cache::ResourceType type) {
- auto metadata = disk_cache::kTypeMetadata[type];
+ std::string directory = disk_cache::defaults::GetSubdirectory(type);
std::vector<char> cache_dir(kSbFileMaxPath + 1, 0);
SbSystemGetPath(kSbSystemPathCacheDirectory, cache_dir.data(),
kSbFileMaxPath);
base::FilePath cache_type_dir =
- base::FilePath(cache_dir.data())
- .Append(FILE_PATH_LITERAL(metadata.directory));
+ base::FilePath(cache_dir.data()).Append(FILE_PATH_LITERAL(directory));
ClearDirectory(cache_type_dir);
}
@@ -313,6 +312,11 @@
void H5vccStorage::SetAndSaveQuotaForBackend(disk_cache::ResourceType type,
uint32_t bytes) {
+ if (disk_cache::settings::GetQuota(type) == bytes) {
+ return;
+ }
+ disk_cache::settings::SetQuota(type, bytes);
+ network_module_->url_request_context()->UpdateCacheSizeSetting(type, bytes);
if (cache_backend_) {
cache_backend_->UpdateSizes(type, bytes);
@@ -332,21 +336,19 @@
return quota;
}
- quota.set_other(cache_backend_->GetQuota(disk_cache::kOther));
- quota.set_html(cache_backend_->GetQuota(disk_cache::kHTML));
- quota.set_css(cache_backend_->GetQuota(disk_cache::kCSS));
- quota.set_image(cache_backend_->GetQuota(disk_cache::kImage));
- quota.set_font(cache_backend_->GetQuota(disk_cache::kFont));
- quota.set_splash(cache_backend_->GetQuota(disk_cache::kSplashScreen));
+ quota.set_other(disk_cache::settings::GetQuota(disk_cache::kOther));
+ quota.set_html(disk_cache::settings::GetQuota(disk_cache::kHTML));
+ quota.set_css(disk_cache::settings::GetQuota(disk_cache::kCSS));
+ quota.set_image(disk_cache::settings::GetQuota(disk_cache::kImage));
+ quota.set_font(disk_cache::settings::GetQuota(disk_cache::kFont));
+ quota.set_splash(disk_cache::settings::GetQuota(disk_cache::kSplashScreen));
quota.set_uncompiled_js(
- cache_backend_->GetQuota(disk_cache::kUncompiledScript));
+ disk_cache::settings::GetQuota(disk_cache::kUncompiledScript));
quota.set_compiled_js(
- cobalt::cache::Cache::GetInstance()
- ->GetMaxCacheStorageInBytes(disk_cache::kCompiledScript)
- .value());
- quota.set_cache_api(cache_backend_->GetQuota(disk_cache::kCacheApi));
+ disk_cache::settings::GetQuota(disk_cache::kCompiledScript));
+ quota.set_cache_api(disk_cache::settings::GetQuota(disk_cache::kCacheApi));
quota.set_service_worker_js(
- cache_backend_->GetQuota(disk_cache::kServiceWorkerScript));
+ disk_cache::settings::GetQuota(disk_cache::kServiceWorkerScript));
uint32_t max_quota_size = 24 * 1024 * 1024;
#if SB_API_VERSION >= 14
@@ -366,7 +368,7 @@
disk_cache::kCacheEnabledPersistentSettingsKey,
std::make_unique<base::Value>(true));
- cobalt::cache::Cache::GetInstance()->set_enabled(true);
+ disk_cache::settings::SetCacheEnabled(true);
if (http_cache_) {
http_cache_->set_mode(net::HttpCache::Mode::NORMAL);
@@ -378,7 +380,7 @@
disk_cache::kCacheEnabledPersistentSettingsKey,
std::make_unique<base::Value>(false));
- cobalt::cache::Cache::GetInstance()->set_enabled(false);
+ disk_cache::settings::SetCacheEnabled(false);
if (http_cache_) {
http_cache_->set_mode(net::HttpCache::Mode::DISABLE);
diff --git a/cobalt/h5vcc/h5vcc_system.cc b/cobalt/h5vcc/h5vcc_system.cc
index 89c9d9a..addb717 100644
--- a/cobalt/h5vcc/h5vcc_system.cc
+++ b/cobalt/h5vcc/h5vcc_system.cc
@@ -21,6 +21,10 @@
#include "starboard/common/system_property.h"
#include "starboard/system.h"
+#if SB_API_VERSION < 14
+#include "starboard/extension/ifa.h"
+#endif // SB_API_VERSION < 14
+
using starboard::kSystemPropertyMaxLength;
namespace cobalt {
@@ -57,27 +61,56 @@
std::string H5vccSystem::advertising_id() const {
std::string result;
-#if SB_API_VERSION >= 14
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
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/layout/layout_boxes.h b/cobalt/layout/layout_boxes.h
index 42ee844..919e57e 100644
--- a/cobalt/layout/layout_boxes.h
+++ b/cobalt/layout/layout_boxes.h
@@ -67,6 +67,11 @@
//
const Boxes& boxes() { return boxes_; }
+ base::Optional<std::pair<dom::Directionality, math::RectF>>&
+ scroll_area_cache() override {
+ return scroll_area_cache_;
+ }
+
private:
// Returns the bounding rectangle of the border edges of the boxes.
math::RectF GetBoundingBorderRectangle() const;
diff --git a/cobalt/layout/topmost_event_target.cc b/cobalt/layout/topmost_event_target.cc
index 3981de4..a67dd1ad 100644
--- a/cobalt/layout/topmost_event_target.cc
+++ b/cobalt/layout/topmost_event_target.cc
@@ -142,6 +142,15 @@
return possible_scroll_targets;
}
+bool HasAnyScrollTarget(
+ const dom::PossibleScrollTargets* possible_scroll_targets) {
+ if (!possible_scroll_targets) {
+ return false;
+ }
+ return possible_scroll_targets->left || possible_scroll_targets->right ||
+ possible_scroll_targets->up || possible_scroll_targets->down;
+}
+
scoped_refptr<dom::HTMLElement> FindFirstElementWithScrollType(
dom::PossibleScrollTargets* possible_scroll_targets,
ui_navigation::scroll_engine::ScrollType major_scroll_axis,
@@ -280,15 +289,6 @@
}
}
-void SendStateChangeLeaveEvents(
- bool is_pointer_event, scoped_refptr<dom::HTMLElement> previous_element,
- dom::PointerEventInit* event_init) {
- scoped_refptr<dom::HTMLElement> target_element = nullptr;
- scoped_refptr<dom::Element> nearest_common_ancestor = nullptr;
- SendStateChangeLeaveEvents(is_pointer_event, previous_element, target_element,
- nearest_common_ancestor, event_init);
-}
-
void SendStateChangeEnterEvents(
bool is_pointer_event, scoped_refptr<dom::HTMLElement> previous_element,
scoped_refptr<dom::HTMLElement> target_element,
@@ -420,7 +420,10 @@
new dom::PointerEvent(base::Tokens::pointercancel(), web::Event::kBubbles,
web::Event::kNotCancelable, view, *event_init));
bool is_pointer_event = true;
- SendStateChangeLeaveEvents(is_pointer_event, element, event_init);
+ scoped_refptr<dom::HTMLElement> target_element = nullptr;
+ scoped_refptr<dom::Element> nearest_common_ancestor = nullptr;
+ SendStateChangeLeaveEvents(is_pointer_event, element, target_element,
+ nearest_common_ancestor, event_init);
}
math::Matrix3F GetCompleteTransformMatrix(dom::Element* element) {
@@ -563,6 +566,10 @@
FindPossibleScrollTargets(target_element);
pointer_state->SetPossibleScrollTargets(
pointer_id, std::move(initial_possible_scroll_targets));
+ if (HasAnyScrollTarget(initial_possible_scroll_targets.get())) {
+ pointer_state->SetPendingPointerCaptureTargetOverride(pointer_id,
+ target_element);
+ }
auto transform_matrix = GetCompleteTransformMatrix(target_element.get());
pointer_state->SetClientTransformMatrix(pointer_id, transform_matrix);
@@ -598,8 +605,11 @@
return;
}
- DispatchPointerEventsForScrollStart(target_element, event_init);
+ scoped_refptr<dom::HTMLElement> previous_html_element(
+ previous_html_element_weak_);
+ DispatchPointerEventsForScrollStart(previous_html_element, event_init);
pointer_state->SetWasCancelled(pointer_id);
+ pointer_state->ClearPendingPointerCaptureTargetOverride(pointer_id);
should_clear_pointer_state = true;
scroll_engine_->thread()->message_loop()->task_runner()->PostTask(
@@ -694,6 +704,12 @@
&event_init);
}
+ bool event_was_cancelled = pointer_event && pointer_state->GetWasCancelled(
+ pointer_event->pointer_id());
+ if (pointer_event && pointer_event->type() == base::Tokens::pointerup()) {
+ pointer_state->ClearWasCancelled(pointer_event->pointer_id());
+ }
+
scoped_refptr<dom::HTMLElement> previous_html_element(
previous_html_element_weak_);
@@ -702,14 +718,10 @@
scoped_refptr<dom::Element> nearest_common_ancestor(
GetNearestCommonAncestor(previous_html_element, target_element));
- SendStateChangeLeaveEvents(pointer_event, previous_html_element,
- target_element, nearest_common_ancestor,
- &event_init);
-
- bool event_was_cancelled = pointer_event && pointer_state->GetWasCancelled(
- pointer_event->pointer_id());
- if (pointer_event && pointer_event->type() == base::Tokens::pointerup()) {
- pointer_state->ClearWasCancelled(pointer_event->pointer_id());
+ if (!event_was_cancelled) {
+ SendStateChangeLeaveEvents(pointer_event, previous_html_element,
+ target_element, nearest_common_ancestor,
+ &event_init);
}
if (target_element) {
diff --git a/cobalt/layout_tests/web_platform_tests.cc b/cobalt/layout_tests/web_platform_tests.cc
index 455c37e..01039d3 100644
--- a/cobalt/layout_tests/web_platform_tests.cc
+++ b/cobalt/layout_tests/web_platform_tests.cc
@@ -219,7 +219,7 @@
new browser::UserAgentPlatformInfo());
std::unique_ptr<browser::ServiceWorkerRegistry> service_worker_registry(
new browser::ServiceWorkerRegistry(&web_settings, &network_module,
- platform_info.get(), url));
+ platform_info.get()));
browser::WebModule::Options web_module_options;
// Use test runner mode to allow the content itself to dictate when it is
diff --git a/cobalt/media/BUILD.gn b/cobalt/media/BUILD.gn
index 0e7c06d..bde4beb 100644
--- a/cobalt/media/BUILD.gn
+++ b/cobalt/media/BUILD.gn
@@ -43,6 +43,8 @@
"base/format_support_query_metrics.h",
"base/interleaved_sinc_resampler.cc",
"base/interleaved_sinc_resampler.h",
+ "base/metrics_provider.cc",
+ "base/metrics_provider.h",
"base/playback_statistics.cc",
"base/playback_statistics.h",
"base/sbplayer_bridge.cc",
diff --git a/cobalt/media/base/cval_stats_test.cc b/cobalt/media/base/cval_stats_test.cc
index 673e20c..ceba320 100644
--- a/cobalt/media/base/cval_stats_test.cc
+++ b/cobalt/media/base/cval_stats_test.cc
@@ -12,16 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifdef _WIN32
-#include <Windows.h>
-#else
-#include <unistd.h>
-#endif
+#include "cobalt/media/base/cval_stats.h"
#include <set>
#include <string>
-#include "cobalt/media/base/cval_stats.h"
+#include "starboard/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -33,21 +29,11 @@
const char kPipelineIdentifier[] = "test_pipeline";
-constexpr int kSleepTimeMs = 50;
+constexpr SbTime kSleepTime = 50; // 50 microseconds
namespace cobalt {
namespace media {
-namespace {
-void sleep_ms(int ms) {
-#ifdef _WIN32
- Sleep(ms);
-#else
- usleep(ms);
-#endif
-}
-} // namespace
-
TEST(MediaCValStatsTest, InitiallyEmpty) {
base::CValManager* cvm = base::CValManager::GetInstance();
EXPECT_TRUE(cvm);
@@ -71,7 +57,8 @@
CValStats cval_stats_;
cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
- sleep_ms(kSleepTimeMs);
+ SbThreadSleep(kSleepTime);
+
cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
base::Optional<std::string> result =
@@ -88,7 +75,7 @@
cval_stats_.Enable(true);
cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
- sleep_ms(kSleepTimeMs);
+ SbThreadSleep(kSleepTime);
cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
base::Optional<std::string> result =
@@ -114,7 +101,7 @@
for (int i = 0; i < kMediaDefaultMaxSamplesBeforeCalculation - 1; i++) {
cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
- sleep_ms(kSleepTimeMs);
+ SbThreadSleep(kSleepTime);
cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
}
@@ -141,7 +128,7 @@
for (int i = 0; i < kMediaDefaultMaxSamplesBeforeCalculation; i++) {
cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
- sleep_ms(kSleepTimeMs);
+ SbThreadSleep(kSleepTime);
cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
}
diff --git a/cobalt/media/base/metrics_provider.cc b/cobalt/media/base/metrics_provider.cc
new file mode 100644
index 0000000..510d5a8
--- /dev/null
+++ b/cobalt/media/base/metrics_provider.cc
@@ -0,0 +1,126 @@
+// 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/media/base/metrics_provider.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+
+namespace cobalt {
+namespace media {
+
+using starboard::ScopedLock;
+
+MediaMetricsProvider::~MediaMetricsProvider() {
+ if (!IsInitialized()) return;
+ ReportPipelineUMA();
+}
+
+void MediaMetricsProvider::Initialize(bool is_mse) {
+ if (IsInitialized()) {
+ return;
+ }
+
+ ScopedLock scoped_lock(mutex_);
+ media_info_.emplace(MediaInfo(is_mse));
+}
+
+void MediaMetricsProvider::OnError(const ::media::PipelineStatus status) {
+ DCHECK(IsInitialized());
+ ScopedLock scoped_lock(mutex_);
+ uma_info_.last_pipeline_status = status;
+}
+
+void MediaMetricsProvider::SetHasAudio(AudioCodec audio_codec) {
+ ScopedLock scoped_lock(mutex_);
+ uma_info_.audio_codec = audio_codec;
+ uma_info_.has_audio = true;
+}
+
+void MediaMetricsProvider::SetHasVideo(VideoCodec video_codec) {
+ ScopedLock scoped_lock(mutex_);
+ uma_info_.video_codec = video_codec;
+ uma_info_.has_video = true;
+}
+
+void MediaMetricsProvider::SetHasPlayed() {
+ ScopedLock scoped_lock(mutex_);
+ uma_info_.has_ever_played = true;
+}
+
+void MediaMetricsProvider::SetHaveEnough() {
+ ScopedLock scoped_lock(mutex_);
+ uma_info_.has_reached_have_enough = true;
+}
+
+void MediaMetricsProvider::SetIsEME() {
+ ScopedLock scoped_lock(mutex_);
+ // This may be called before Initialize().
+ uma_info_.is_eme = true;
+}
+
+void MediaMetricsProvider::ReportPipelineUMA() {
+ ScopedLock scoped_lock(mutex_);
+ if (uma_info_.has_video && uma_info_.has_audio) {
+ base::UmaHistogramExactLinear(GetUMANameForAVStream(uma_info_),
+ uma_info_.last_pipeline_status,
+ PipelineStatus::PIPELINE_STATUS_MAX + 1);
+ } else if (uma_info_.has_audio) {
+ base::UmaHistogramExactLinear("Cobalt.Media.PipelineStatus.AudioOnly",
+ uma_info_.last_pipeline_status,
+ PipelineStatus::PIPELINE_STATUS_MAX + 1);
+ } else if (uma_info_.has_video) {
+ base::UmaHistogramExactLinear("Cobalt.Media.PipelineStatus.VideoOnly",
+ uma_info_.last_pipeline_status,
+ PipelineStatus::PIPELINE_STATUS_MAX + 1);
+ } else {
+ // Note: This metric can be recorded as a result of normal operation with
+ // Media Source Extensions. If a site creates a MediaSource object but never
+ // creates a source buffer or appends data, PIPELINE_OK will be recorded.
+ base::UmaHistogramExactLinear("Cobalt.Media.PipelineStatus.Unsupported",
+ uma_info_.last_pipeline_status,
+ PipelineStatus::PIPELINE_STATUS_MAX + 1);
+ }
+
+ // Report whether this player ever saw a playback event. Used to measure the
+ // effectiveness of efforts to reduce loaded-but-never-used players.
+ if (uma_info_.has_reached_have_enough)
+ base::UmaHistogramBoolean("Cobalt.Media.HasEverPlayed",
+ uma_info_.has_ever_played);
+}
+
+std::string MediaMetricsProvider::GetUMANameForAVStream(
+ const PipelineInfo& player_info) const {
+ constexpr char kPipelineUmaPrefix[] =
+ "Cobalt.Media.PipelineStatus.AudioVideo.";
+ std::string uma_name = kPipelineUmaPrefix;
+ if (player_info.video_codec == VideoCodec::kVP9)
+ uma_name += "VP9";
+ else if (player_info.video_codec == VideoCodec::kH264)
+ uma_name += "H264";
+ else if (player_info.video_codec == VideoCodec::kAV1)
+ uma_name += "AV1";
+ else
+ uma_name += "Other";
+
+ return uma_name;
+}
+
+bool MediaMetricsProvider::IsInitialized() const {
+ ScopedLock scoped_lock(mutex_);
+ return media_info_.has_value();
+}
+
+} // namespace media
+} // namespace cobalt
diff --git a/cobalt/media/base/metrics_provider.h b/cobalt/media/base/metrics_provider.h
new file mode 100644
index 0000000..dff1df7
--- /dev/null
+++ b/cobalt/media/base/metrics_provider.h
@@ -0,0 +1,91 @@
+// 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_MEDIA_BASE_METRICS_PROVIDER_H_
+#define COBALT_MEDIA_BASE_METRICS_PROVIDER_H_
+
+#include <string>
+
+#include "starboard/common/mutex.h"
+#include "third_party/chromium/media/base/audio_codecs.h"
+#include "third_party/chromium/media/base/container_names.h"
+#include "third_party/chromium/media/base/pipeline_status.h"
+#include "third_party/chromium/media/base/timestamp_constants.h"
+#include "third_party/chromium/media/base/video_codecs.h"
+
+namespace cobalt {
+namespace media {
+
+using AudioCodec = ::media::AudioCodec;
+using VideoCodec = ::media::VideoCodec;
+using PipelineStatus = ::media::PipelineStatus;
+using VideoDecoderType = ::media::VideoDecoderType;
+
+class MediaMetricsProvider {
+ public:
+ MediaMetricsProvider() = default;
+ ~MediaMetricsProvider();
+
+ private:
+ struct PipelineInfo {
+ PipelineInfo() = default;
+ ~PipelineInfo() = default;
+ bool has_ever_played = false;
+ bool has_reached_have_enough = false;
+ bool has_audio = false;
+ bool has_video = false;
+ bool is_eme = false;
+ bool video_decoder_changed = false;
+ ::media::AudioCodec audio_codec;
+ ::media::VideoCodec video_codec;
+ ::media::PipelineStatus last_pipeline_status =
+ ::media::PipelineStatus::PIPELINE_OK;
+ };
+
+ struct MediaInfo {
+ explicit MediaInfo(bool is_mse) : is_mse{is_mse} {};
+ const bool is_mse;
+ };
+
+
+ public:
+ // based on mojom::MediaMetricsProvider
+ void Initialize(bool is_mse);
+ void OnError(const ::media::PipelineStatus status);
+ void SetHasAudio(::media::AudioCodec audio_codec);
+ void SetHasVideo(::media::VideoCodec video_codec);
+ void SetHasPlayed();
+ void SetHaveEnough();
+ void SetIsEME();
+
+ void ReportPipelineUMA();
+
+ private:
+ std::string GetUMANameForAVStream(const PipelineInfo& player_info) const;
+ bool IsInitialized() const;
+
+ private:
+ // UMA pipeline packaged data
+ PipelineInfo uma_info_;
+
+ // The values below are only set if `Initialize` has been called.
+ absl::optional<MediaInfo> media_info_;
+
+ starboard::Mutex mutex_;
+};
+
+} // namespace media
+} // namespace cobalt
+
+#endif // COBALT_MEDIA_BASE_METRICS_PROVIDER_H_
diff --git a/cobalt/media/base/sbplayer_bridge.cc b/cobalt/media/base/sbplayer_bridge.cc
index 92dc4da..0f3ec4c 100644
--- a/cobalt/media/base/sbplayer_bridge.cc
+++ b/cobalt/media/base/sbplayer_bridge.cc
@@ -39,6 +39,7 @@
namespace {
+using starboard::FormatString;
using starboard::GetPlayerOutputModeName;
class StatisticsWrapper {
@@ -707,9 +708,9 @@
// a method of querying that texture.
decode_target_provider_->SetGetCurrentSbDecodeTargetFunction(base::Bind(
&SbPlayerBridge::GetCurrentSbDecodeTarget, base::Unretained(this)));
- SB_LOG(INFO) << "Playing in decode-to-texture mode.";
+ LOG(INFO) << "Playing in decode-to-texture mode.";
} else {
- SB_LOG(INFO) << "Playing in punch-out mode.";
+ LOG(INFO) << "Playing in punch-out mode.";
}
decode_target_provider_->SetOutputMode(
@@ -777,9 +778,9 @@
// a method of querying that texture.
decode_target_provider_->SetGetCurrentSbDecodeTargetFunction(base::Bind(
&SbPlayerBridge::GetCurrentSbDecodeTarget, base::Unretained(this)));
- SB_LOG(INFO) << "Playing in decode-to-texture mode.";
+ LOG(INFO) << "Playing in decode-to-texture mode.";
} else {
- SB_LOG(INFO) << "Playing in punch-out mode.";
+ LOG(INFO) << "Playing in punch-out mode.";
}
decode_target_provider_->SetOutputMode(
@@ -1237,8 +1238,8 @@
if (default_output_mode != kSbPlayerOutputModeDecodeToTexture &&
video_stream_info_.codec != kSbMediaVideoCodecNone) {
- SB_DCHECK(video_stream_info_.mime);
- SB_DCHECK(video_stream_info_.max_video_capabilities);
+ DCHECK(video_stream_info_.mime);
+ DCHECK(video_stream_info_.max_video_capabilities);
// Set the `default_output_mode` to `kSbPlayerOutputModeDecodeToTexture` if
// any of the mime associated with it has `decode-to-texture=true` set.
@@ -1250,13 +1251,12 @@
"decode-to-texture=true");
if (is_decode_to_texture_preferred) {
- SB_LOG(INFO) << "Setting `default_output_mode` from \""
- << GetPlayerOutputModeName(default_output_mode) << "\" to \""
- << GetPlayerOutputModeName(
- kSbPlayerOutputModeDecodeToTexture)
- << "\" because mime is set to \"" << video_stream_info_.mime
- << "\", and max_video_capabilities is set to \""
- << video_stream_info_.max_video_capabilities << "\"";
+ LOG(INFO) << "Setting `default_output_mode` from \""
+ << GetPlayerOutputModeName(default_output_mode) << "\" to \""
+ << GetPlayerOutputModeName(kSbPlayerOutputModeDecodeToTexture)
+ << "\" because mime is set to \"" << video_stream_info_.mime
+ << "\", and max_video_capabilities is set to \""
+ << video_stream_info_.max_video_capabilities << "\"";
default_output_mode = kSbPlayerOutputModeDecodeToTexture;
}
}
@@ -1265,6 +1265,9 @@
auto output_mode =
sbplayer_interface_->GetPreferredOutputMode(&creation_param);
+
+ LOG(INFO) << "Output mode is set to " << GetPlayerOutputModeName(output_mode);
+
CHECK_NE(kSbPlayerOutputModeInvalid, output_mode);
return output_mode;
}
@@ -1272,15 +1275,14 @@
void SbPlayerBridge::LogStartupLatency() const {
std::string first_events_str;
if (set_drm_system_ready_cb_time_ == -1) {
- first_events_str =
- starboard::FormatString("%-40s0 us", "SbPlayerCreate() called");
+ first_events_str = FormatString("%-40s0 us", "SbPlayerCreate() called");
} else if (set_drm_system_ready_cb_time_ < player_creation_time_) {
- first_events_str = starboard::FormatString(
+ first_events_str = FormatString(
"%-40s0 us\n%-40s%" PRId64 " us", "set_drm_system_ready_cb called",
"SbPlayerCreate() called",
player_creation_time_ - set_drm_system_ready_cb_time_);
} else {
- first_events_str = starboard::FormatString(
+ first_events_str = FormatString(
"%-40s0 us\n%-40s%" PRId64 " us", "SbPlayerCreate() called",
"set_drm_system_ready_cb called",
set_drm_system_ready_cb_time_ - player_creation_time_);
@@ -1305,7 +1307,7 @@
1);
// clang-format off
- LOG(INFO) << starboard::FormatString(
+ LOG(INFO) << FormatString(
"\nSbPlayer startup latencies: %" PRId64 " us\n"
" Event name time since last event\n"
" %s\n" // |first_events_str| populated above
diff --git a/cobalt/media/base/sbplayer_pipeline.cc b/cobalt/media/base/sbplayer_pipeline.cc
index ee6a1dc..8b29628 100644
--- a/cobalt/media/base/sbplayer_pipeline.cc
+++ b/cobalt/media/base/sbplayer_pipeline.cc
@@ -111,7 +111,8 @@
#if SB_API_VERSION >= 15
SbTime audio_write_duration_local, SbTime audio_write_duration_remote,
#endif // SB_API_VERSION >= 15
- MediaLog* media_log, DecodeTargetProvider* decode_target_provider)
+ MediaLog* media_log, MediaMetricsProvider* media_metrics_provider,
+ DecodeTargetProvider* decode_target_provider)
: pipeline_identifier_(
base::StringPrintf("%X", g_pipeline_identifier_counter++)),
sbplayer_interface_(interface),
@@ -156,6 +157,7 @@
audio_write_duration_local_(audio_write_duration_local),
audio_write_duration_remote_(audio_write_duration_remote),
#endif // SB_API_VERSION >= 15
+ media_metrics_provider_(media_metrics_provider),
last_media_time_(base::StringPrintf("Media.Pipeline.%s.LastMediaTime",
pipeline_identifier_.c_str()),
0, "Last media time reported by the underlying player."),
@@ -337,6 +339,8 @@
if (demuxer_) {
stop_cb_ = stop_cb;
demuxer_->Stop();
+ video_stream_ = nullptr;
+ audio_stream_ = nullptr;
OnDemuxerStopped();
} else {
stop_cb.Run();
@@ -563,6 +567,13 @@
std::vector<std::string> connectors;
+#if SB_HAS(PLAYER_WITH_URL)
+ // Url based player does not support audio connectors.
+ if (is_url_based_) {
+ return connectors;
+ }
+#endif // SB_HAS(PLAYER_WITH_URL)
+
auto configurations = player_bridge_->GetAudioConfigurations();
for (auto&& configuration : configurations) {
connectors.push_back(GetMediaAudioConnectorName(configuration.connector));
@@ -999,12 +1010,17 @@
return;
}
+ if (stopped_) {
+ return;
+ }
+
+ DCHECK(player_bridge_);
+
DemuxerStream* stream =
type == DemuxerStream::AUDIO ? audio_stream_ : video_stream_;
DCHECK(stream);
- // In case if Stop() has been called.
- if (!player_bridge_) {
+ if (!player_bridge_ || !stream) {
return;
}
@@ -1157,6 +1173,14 @@
playback_statistics_.OnPresenting(
video_stream_->video_decoder_config());
}
+
+#if SB_HAS(PLAYER_WITH_URL)
+ // Url based player does not support |audio_write_duration_for_preroll_|.
+ if (is_url_based_) {
+ break;
+ }
+#endif // SB_HAS(PLAYER_WITH_URL)
+
#if SB_API_VERSION >= 15
audio_write_duration_for_preroll_ = audio_write_duration_ =
HasRemoteAudioOutputs(player_bridge_->GetAudioConfigurations())
@@ -1229,10 +1253,12 @@
if (stream->type() == DemuxerStream::AUDIO) {
const AudioDecoderConfig& decoder_config = stream->audio_decoder_config();
+ media_metrics_provider_->SetHasAudio(decoder_config.codec());
player_bridge_->UpdateAudioConfig(decoder_config, stream->mime_type());
} else {
DCHECK_EQ(stream->type(), DemuxerStream::VIDEO);
const VideoDecoderConfig& decoder_config = stream->video_decoder_config();
+ media_metrics_provider_->SetHasVideo(decoder_config.codec());
base::AutoLock auto_lock(lock_);
bool natural_size_changed =
(decoder_config.natural_size().width() != natural_size_.width() ||
@@ -1352,6 +1378,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 {
diff --git a/cobalt/media/base/sbplayer_pipeline.h b/cobalt/media/base/sbplayer_pipeline.h
index 59e19e5..dc851c8 100644
--- a/cobalt/media/base/sbplayer_pipeline.h
+++ b/cobalt/media/base/sbplayer_pipeline.h
@@ -28,6 +28,7 @@
#include "cobalt/base/c_val.h"
#include "cobalt/math/size.h"
#include "cobalt/media/base/media_export.h"
+#include "cobalt/media/base/metrics_provider.h"
#include "cobalt/media/base/pipeline.h"
#include "cobalt/media/base/playback_statistics.h"
#include "cobalt/media/base/sbplayer_bridge.h"
@@ -65,7 +66,8 @@
#if SB_API_VERSION >= 15
SbTime audio_write_duration_local, SbTime audio_write_duration_remote,
#endif // SB_API_VERSION >= 15
- MediaLog* media_log, DecodeTargetProvider* decode_target_provider);
+ MediaLog* media_log, MediaMetricsProvider* media_metrics_provider,
+ DecodeTargetProvider* decode_target_provider);
~SbPlayerPipeline() override;
void Suspend() override;
@@ -305,6 +307,8 @@
base::CVal<bool> ended_;
base::CVal<SbPlayerState> player_state_;
+ MediaMetricsProvider* media_metrics_provider_;
+
DecodeTargetProvider* decode_target_provider_;
#if SB_API_VERSION >= 15
diff --git a/cobalt/media/player/web_media_player_impl.cc b/cobalt/media/player/web_media_player_impl.cc
index 8a121a6..6941c04 100644
--- a/cobalt/media/player/web_media_player_impl.cc
+++ b/cobalt/media/player/web_media_player_impl.cc
@@ -156,7 +156,7 @@
#if SB_API_VERSION >= 15
audio_write_duration_local, audio_write_duration_remote,
#endif // SB_API_VERSION >= 15
- media_log_, decode_target_provider_.get());
+ media_log_, &media_metrics_provider_, decode_target_provider_.get());
// Also we want to be notified of |main_loop_| destruction.
main_loop_->AddDestructionObserver(this);
@@ -244,6 +244,7 @@
is_local_source_ = !url.SchemeIs("http") && !url.SchemeIs("https");
StartPipeline(url);
+ media_metrics_provider_.Initialize(false);
}
#endif // SB_HAS(PLAYER_WITH_URL)
@@ -270,6 +271,7 @@
state_.is_media_source = true;
StartPipeline(chunk_demuxer_.get());
+ media_metrics_provider_.Initialize(true);
}
void WebMediaPlayerImpl::LoadProgressive(
@@ -309,6 +311,7 @@
state_.is_progressive = true;
StartPipeline(progressive_demuxer_.get());
+ media_metrics_provider_.Initialize(false);
}
void WebMediaPlayerImpl::CancelLoad() {
@@ -324,6 +327,7 @@
pipeline_->SetPlaybackRate(state_.playback_rate);
media_log_->AddEvent<::media::MediaLogEvent::kPlay>();
+ media_metrics_provider_.SetHasPlayed();
}
void WebMediaPlayerImpl::Pause() {
@@ -653,6 +657,7 @@
if (suppress_destruction_errors_) return;
media_log_->NotifyError(error);
+ media_metrics_provider_.OnError(error);
if (ready_state_ == WebMediaPlayer::kReadyStateHaveNothing) {
// Any error that occurs before reaching ReadyStateHaveMetadata should
@@ -785,6 +790,7 @@
break;
case Pipeline::kPrerollCompleted:
SetReadyState(WebMediaPlayer::kReadyStateHaveEnoughData);
+ media_metrics_provider_.SetHaveEnough();
break;
}
}
@@ -963,6 +969,7 @@
NOTREACHED();
break;
}
+ media_metrics_provider_.SetIsEME();
}
WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() {
diff --git a/cobalt/media/player/web_media_player_impl.h b/cobalt/media/player/web_media_player_impl.h
index 9db4419..adb7ed2 100644
--- a/cobalt/media/player/web_media_player_impl.h
+++ b/cobalt/media/player/web_media_player_impl.h
@@ -60,6 +60,7 @@
#include "base/time/time.h"
#include "cobalt/math/size.h"
#include "cobalt/media/base/decode_target_provider.h"
+#include "cobalt/media/base/metrics_provider.h"
#include "cobalt/media/base/pipeline.h"
#include "cobalt/media/base/sbplayer_interface.h"
#include "cobalt/media/player/web_media_player.h"
@@ -306,6 +307,8 @@
::media::MediaLog* const media_log_;
+ MediaMetricsProvider media_metrics_provider_;
+
bool is_local_source_;
std::unique_ptr<::media::Demuxer> progressive_demuxer_;
diff --git a/cobalt/media_capture/media_devices.cc b/cobalt/media_capture/media_devices.cc
index 7a75b18..3c671f3 100644
--- a/cobalt/media_capture/media_devices.cc
+++ b/cobalt/media_capture/media_devices.cc
@@ -18,6 +18,7 @@
#include <string>
#include <utility>
+#include "base/metrics/histogram_functions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/media_capture/media_device_info.h"
@@ -66,6 +67,11 @@
return mic;
}
+void LogMicCreationSucceededHistogramItem(bool mic_creation_succeeded) {
+ base::UmaHistogramBoolean("Cobalt.MediaDevices.MicCreationSucceeded",
+ mic_creation_succeeded);
+}
+
} // namespace.
MediaDevices::MediaDevices(script::EnvironmentSettings* settings,
@@ -169,6 +175,9 @@
speech::MicrophoneManager::MicrophoneError error, std::string message) {
DLOG(INFO) << "MediaDevices::OnMicrophoneError " << message;
+ // Log failed mic creation in UMA histogram
+ LogMicCreationSucceededHistogramItem(false);
+
// No special error handling logic besides logging the message above, so just
// delegate to the OnMicrophoneStopped() functionality.
OnMicrophoneStopped();
@@ -195,6 +204,9 @@
}
void MediaDevices::OnMicrophoneSuccess() {
+ // Log successful mic creation in UMA histogram
+ LogMicCreationSucceededHistogramItem(true);
+
if (javascript_message_loop_->task_runner() !=
base::ThreadTaskRunnerHandle::Get()) {
javascript_message_loop_->task_runner()->PostTask(
diff --git a/cobalt/network/cobalt_net_log.cc b/cobalt/network/cobalt_net_log.cc
index 3df4117..f597b87 100644
--- a/cobalt/network/cobalt_net_log.cc
+++ b/cobalt/network/cobalt_net_log.cc
@@ -25,14 +25,29 @@
CobaltNetLog::CobaltNetLog(const base::FilePath& log_path,
net::NetLogCaptureMode capture_mode)
- : net_log_logger_(
- net::FileNetLogObserver::CreateUnbounded(log_path, nullptr)) {
- net_log_logger_->StartObserving(this, capture_mode);
-}
+ : capture_mode_(capture_mode),
+ net_log_logger_(
+ net::FileNetLogObserver::CreateUnbounded(log_path, nullptr)) {}
CobaltNetLog::~CobaltNetLog() {
// Remove the observers we own before we're destroyed.
- net_log_logger_->StopObserving(nullptr, base::OnceClosure());
+ StopObserving();
+}
+
+void CobaltNetLog::StartObserving() {
+ if (!is_observing_) {
+ is_observing_ = true;
+ net_log_logger_->StartObserving(this, capture_mode_);
+ } else {
+ DLOG(WARNING) << "Already observing NetLog.";
+ }
+}
+
+void CobaltNetLog::StopObserving() {
+ if (is_observing_) {
+ is_observing_ = false;
+ net_log_logger_->StopObserving(nullptr, base::OnceClosure());
+ }
}
} // namespace network
diff --git a/cobalt/network/cobalt_net_log.h b/cobalt/network/cobalt_net_log.h
index 4ee6fa8..dcc1e67 100644
--- a/cobalt/network/cobalt_net_log.h
+++ b/cobalt/network/cobalt_net_log.h
@@ -34,8 +34,13 @@
::net::NetLogCaptureMode capture_mode);
~CobaltNetLog() override;
+ void StartObserving();
+ void StopObserving();
+
private:
- std::unique_ptr<net::FileNetLogObserver> net_log_logger_;
+ bool is_observing_{false};
+ net::NetLogCaptureMode capture_mode_;
+ std::unique_ptr<net::FileNetLogObserver> net_log_logger_{nullptr};
DISALLOW_COPY_AND_ASSIGN(CobaltNetLog);
};
diff --git a/cobalt/network/network_module.cc b/cobalt/network/network_module.cc
index 8154b7c..0c8f67e 100644
--- a/cobalt/network/network_module.cc
+++ b/cobalt/network/network_module.cc
@@ -17,10 +17,12 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
+#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
+#include "cobalt/base/cobalt_paths.h"
#include "cobalt/network/network_system.h"
#include "cobalt/network/switches.h"
#include "net/url_request/static_http_user_agent_settings.h"
@@ -33,6 +35,7 @@
const char kCaptureModeIncludeCookiesAndCredentials[] =
"IncludeCookiesAndCredentials";
const char kCaptureModeIncludeSocketBytes[] = "IncludeSocketBytes";
+const char kDefaultNetLogName[] = "cobalt_netlog.json";
#endif
} // namespace
@@ -110,15 +113,6 @@
}
}
-void NetworkModule::SetEnableClientHintHeadersFlagsFromPersistentSettings() {
- // Called on initialization and when the persistent setting is changed.
- if (options_.persistent_settings != nullptr) {
- enable_client_hint_headers_flags_.store(
- options_.persistent_settings->GetPersistentSettingAsInt(
- kClientHintHeadersEnabledPersistentSettingsKey, 0));
- }
-}
-
void NetworkModule::EnsureStorageManagerStarted() {
DCHECK(storage_manager_);
storage_manager_->EnsureStarted();
@@ -137,8 +131,6 @@
http_user_agent_settings_.reset(new net::StaticHttpUserAgentSettings(
options_.preferred_language, user_agent_string));
- SetEnableClientHintHeadersFlagsFromPersistentSettings();
-
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
@@ -156,11 +148,12 @@
}
#if defined(ENABLE_NETWORK_LOGGING)
+ base::FilePath result;
+ base::PathService::Get(cobalt::paths::DIR_COBALT_DEBUG_OUT, &result);
+ net_log_path_ = result.Append(kDefaultNetLogName);
+ net::NetLogCaptureMode capture_mode;
if (command_line->HasSwitch(switches::kNetLog)) {
- // If this is not a valid path, net logs will be sent to VLOG(1).
- base::FilePath net_log_path =
- command_line->GetSwitchValuePath(switches::kNetLog);
- net::NetLogCaptureMode capture_mode;
+ net_log_path_ = command_line->GetSwitchValuePath(switches::kNetLog);
if (command_line->HasSwitch(switches::kNetLogCaptureMode)) {
std::string capture_mode_string =
command_line->GetSwitchValueASCII(switches::kNetLogCaptureMode);
@@ -170,7 +163,10 @@
capture_mode = net::NetLogCaptureMode::IncludeSocketBytes();
}
}
- net_log_.reset(new CobaltNetLog(net_log_path, capture_mode));
+ net_log_.reset(new CobaltNetLog(net_log_path_, capture_mode));
+ net_log_->StartObserving();
+ } else {
+ net_log_.reset(new CobaltNetLog(net_log_path_, capture_mode));
}
#endif
@@ -234,12 +230,27 @@
void NetworkModule::AddClientHintHeaders(
net::URLFetcher& url_fetcher, ClientHintHeadersCallType call_type) const {
- if (enable_client_hint_headers_flags_.load() & call_type) {
+ if (kEnabledClientHintHeaders & call_type) {
for (const auto& header : client_hint_headers_) {
url_fetcher.AddExtraRequestHeader(header);
}
}
}
+void NetworkModule::StartNetLog() {
+#if defined(ENABLE_NETWORK_LOGGING)
+ LOG(INFO) << "Starting NetLog capture";
+ net_log_->StartObserving();
+#endif
+}
+
+base::FilePath NetworkModule::StopNetLog() {
+#if defined(ENABLE_NETWORK_LOGGING)
+ LOG(INFO) << "Stopping NetLog capture";
+ net_log_->StopObserving();
+#endif
+ return net_log_path_;
+}
+
} // namespace network
} // namespace cobalt
diff --git a/cobalt/network/network_module.h b/cobalt/network/network_module.h
index b6c8bd2..18ea05c 100644
--- a/cobalt/network/network_module.h
+++ b/cobalt/network/network_module.h
@@ -49,7 +49,7 @@
namespace network {
// Used to differentiate type of network call for Client Hint Headers.
-// Values correspond to bit masks against |enable_client_hint_headers_flags_|.
+// Values correspond to bit masks against |kEnabledClientHintHeaders|.
enum ClientHintHeadersCallType : int32_t {
kCallTypeLoader = (1u << 0),
kCallTypeMedia = (1u << 1),
@@ -59,11 +59,10 @@
kCallTypeXHR = (1u << 5),
};
-const char kQuicEnabledPersistentSettingsKey[] = "QUICEnabled";
+// Determines which type of network calls should include Client Hint Headers.
+constexpr int32_t kEnabledClientHintHeaders = (kCallTypeLoader | kCallTypeXHR);
-// Holds bit mask flag, read into |enable_client_hint_headers_flags_|.
-const char kClientHintHeadersEnabledPersistentSettingsKey[] =
- "clientHintHeadersEnabled";
+const char kQuicEnabledPersistentSettingsKey[] = "QUICEnabled";
class NetworkSystem;
// NetworkModule wraps various networking-related components such as
@@ -132,9 +131,6 @@
void SetEnableQuicFromPersistentSettings();
- // Checks persistent settings to determine if Client Hint Headers are enabled.
- void SetEnableClientHintHeadersFlagsFromPersistentSettings();
-
// Adds the Client Hint Headers to the provided URLFetcher if enabled.
void AddClientHintHeaders(net::URLFetcher& url_fetcher,
ClientHintHeadersCallType call_type) const;
@@ -142,6 +138,11 @@
// From base::MessageLoop::DestructionObserver.
void WillDestroyCurrentMessageLoop() override;
+ // Used to capture NetLog from Devtools
+ void StartNetLog();
+ base::FilePath StopNetLog();
+
+
private:
void Initialize(const std::string& user_agent_string,
base::EventDispatcher* event_dispatcher);
@@ -149,7 +150,6 @@
std::unique_ptr<network_bridge::NetPoster> CreateNetPoster();
std::vector<std::string> client_hint_headers_;
- starboard::atomic_int32_t enable_client_hint_headers_flags_;
std::unique_ptr<storage::StorageManager> storage_manager_;
std::unique_ptr<base::Thread> thread_;
std::unique_ptr<URLRequestContext> url_request_context_;
@@ -163,7 +163,9 @@
scoped_refptr<net::DialServiceProxy> dial_service_proxy_;
#endif
std::unique_ptr<network_bridge::NetPoster> net_poster_;
- std::unique_ptr<CobaltNetLog> net_log_;
+
+ base::FilePath net_log_path_;
+ std::unique_ptr<CobaltNetLog> net_log_{nullptr};
Options options_;
DISALLOW_COPY_AND_ASSIGN(NetworkModule);
diff --git a/cobalt/network/url_request_context.cc b/cobalt/network/url_request_context.cc
index 7467b98..2efd31e 100644
--- a/cobalt/network/url_request_context.cc
+++ b/cobalt/network/url_request_context.cc
@@ -14,6 +14,8 @@
#include "cobalt/network/url_request_context.h"
+#include <algorithm>
+#include <map>
#include <memory>
#include <string>
#include <utility>
@@ -48,6 +50,49 @@
#include "net/url_request/data_protocol_handler.h"
#include "net/url_request/url_request_job_factory_impl.h"
+namespace {
+
+const char kPersistentSettingsJson[] = "cache_settings.json";
+
+void LoadDiskCacheQuotaSettings(
+ cobalt::persistent_storage::PersistentSettings* settings,
+ int64_t max_bytes) {
+ auto total_size = 0;
+ std::map<disk_cache::ResourceType, uint32_t> quotas;
+ for (int i = 0; i < disk_cache::kTypeCount; i++) {
+ disk_cache::ResourceType resource_type = (disk_cache::ResourceType)i;
+ std::string directory =
+ disk_cache::defaults::GetSubdirectory(resource_type);
+ uint32_t bucket_size =
+ static_cast<uint32_t>(settings->GetPersistentSettingAsDouble(
+ directory, disk_cache::defaults::GetQuota(resource_type)));
+ quotas[resource_type] = bucket_size;
+ total_size += bucket_size;
+ }
+
+ if (total_size <= max_bytes) {
+ for (int i = 0; i < disk_cache::kTypeCount; i++) {
+ disk_cache::ResourceType resource_type = (disk_cache::ResourceType)i;
+ disk_cache::settings::SetQuota(resource_type, quotas[resource_type]);
+ }
+ return;
+ }
+
+ // Sum of quotas exceeds |max_bytes|. Set quotas to default values.
+ for (int i = 0; i < disk_cache::kTypeCount; i++) {
+ disk_cache::ResourceType resource_type = (disk_cache::ResourceType)i;
+ uint32_t default_quota = disk_cache::defaults::GetQuota(resource_type);
+ disk_cache::settings::SetQuota(resource_type, default_quota);
+ std::string directory =
+ disk_cache::defaults::GetSubdirectory(resource_type);
+ settings->SetPersistentSetting(
+ directory,
+ std::make_unique<base::Value>(static_cast<double>(default_quota)));
+ }
+}
+
+} // namespace
+
namespace cobalt {
namespace network {
namespace {
@@ -189,17 +234,24 @@
// is less than 1 mb and subtract this from the max_cache_bytes.
max_cache_bytes -= (1 << 20);
+ // Initialize and read caching persistent settings
+ cache_persistent_settings_ =
+ std::make_unique<cobalt::persistent_storage::PersistentSettings>(
+ kPersistentSettingsJson);
+ LoadDiskCacheQuotaSettings(cache_persistent_settings_.get(),
+ max_cache_bytes);
+
auto http_cache = std::make_unique<net::HttpCache>(
storage_.http_network_session(),
std::make_unique<net::HttpCache::DefaultBackend>(
- net::DISK_CACHE, net::CACHE_BACKEND_COBALT,
+ net::DISK_CACHE, net::CACHE_BACKEND_DEFAULT,
base::FilePath(std::string(path.data())),
/* max_bytes */ max_cache_bytes),
true);
if (persistent_settings != nullptr) {
auto cache_enabled = persistent_settings->GetPersistentSettingAsBool(
disk_cache::kCacheEnabledPersistentSettingsKey, true);
-
+ disk_cache::settings::SetCacheEnabled(cache_enabled);
if (!cache_enabled) {
http_cache->set_mode(net::HttpCache::Mode::DISABLE);
}
@@ -243,5 +295,17 @@
}
#endif // defined(ENABLE_DEBUGGER)
+void URLRequestContext::UpdateCacheSizeSetting(disk_cache::ResourceType type,
+ uint32_t bytes) {
+ CHECK(cache_persistent_settings_);
+ cache_persistent_settings_->SetPersistentSetting(
+ disk_cache::defaults::GetSubdirectory(type),
+ std::make_unique<base::Value>(static_cast<double>(bytes)));
+}
+
+void URLRequestContext::ValidateCachePersistentSettings() {
+ cache_persistent_settings_->ValidatePersistentSettings();
+}
+
} // namespace network
} // namespace cobalt
diff --git a/cobalt/network/url_request_context.h b/cobalt/network/url_request_context.h
index 6e80479..a8e635c 100644
--- a/cobalt/network/url_request_context.h
+++ b/cobalt/network/url_request_context.h
@@ -15,6 +15,7 @@
#ifndef COBALT_NETWORK_URL_REQUEST_CONTEXT_H_
#define COBALT_NETWORK_URL_REQUEST_CONTEXT_H_
+#include <memory>
#include <string>
#include "base/basictypes.h"
@@ -22,6 +23,7 @@
#include "base/sequence_checker.h"
#include "cobalt/persistent_storage/persistent_settings.h"
#include "net/cookies/cookie_monster.h"
+#include "net/disk_cache/cobalt/resource_type.h"
#include "net/log/net_log.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
@@ -53,6 +55,9 @@
bool using_http_cache();
+ void UpdateCacheSizeSetting(disk_cache::ResourceType type, uint32_t bytes);
+ void ValidateCachePersistentSettings();
+
private:
SEQUENCE_CHECKER(sequence_checker_);
net::URLRequestContextStorage storage_;
@@ -70,6 +75,10 @@
void OnQuicToggle(const std::string&);
#endif // defined(ENABLE_DEBUGGER)
+ // Persistent settings module for Cobalt disk cache quotas
+ std::unique_ptr<cobalt::persistent_storage::PersistentSettings>
+ cache_persistent_settings_;
+
DISALLOW_COPY_AND_ASSIGN(URLRequestContext);
};
diff --git a/cobalt/persistent_storage/persistent_settings.cc b/cobalt/persistent_storage/persistent_settings.cc
index 52bc864..2f2e5dd 100644
--- a/cobalt/persistent_storage/persistent_settings.cc
+++ b/cobalt/persistent_storage/persistent_settings.cc
@@ -129,7 +129,9 @@
base::AutoLock auto_lock(pref_store_lock_);
auto persistent_settings = pref_store_->GetValues();
const base::Value* result = persistent_settings->FindKey(key);
- if (result && result->is_double()) return result->GetDouble();
+ if (result && result->is_double()) {
+ return result->GetDouble();
+ }
return default_setting;
}
diff --git a/cobalt/ui_navigation/scroll_engine/scroll_engine.cc b/cobalt/ui_navigation/scroll_engine/scroll_engine.cc
index 4c14f73..2c76797 100644
--- a/cobalt/ui_navigation/scroll_engine/scroll_engine.cc
+++ b/cobalt/ui_navigation/scroll_engine/scroll_engine.cc
@@ -395,6 +395,14 @@
}
}
+void ScrollEngine::Conceal(render_tree::ResourceProvider*, SbTimeMonotonic) {
+ nav_items_with_decaying_scroll_.clear();
+}
+
+void ScrollEngine::Freeze(SbTimeMonotonic) {
+ nav_items_with_decaying_scroll_.clear();
+}
+
} // namespace scroll_engine
} // namespace ui_navigation
} // namespace cobalt
diff --git a/cobalt/ui_navigation/scroll_engine/scroll_engine.h b/cobalt/ui_navigation/scroll_engine/scroll_engine.h
index 93e1e33..3e2653a 100644
--- a/cobalt/ui_navigation/scroll_engine/scroll_engine.h
+++ b/cobalt/ui_navigation/scroll_engine/scroll_engine.h
@@ -23,6 +23,7 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "cobalt/base/token.h"
+#include "cobalt/browser/lifecycle_observer.h"
#include "cobalt/dom/pointer_event.h"
#include "cobalt/dom/pointer_event_init.h"
#include "cobalt/math/vector2d_f.h"
@@ -50,7 +51,7 @@
base::Time time_stamp;
};
-class ScrollEngine {
+class ScrollEngine : public browser::LifecycleObserver {
public:
ScrollEngine();
~ScrollEngine();
@@ -73,6 +74,17 @@
base::Thread* thread() { return &scroll_engine_; }
+ // LifecycleObserver implementation.
+ void Blur(SbTimeMonotonic timestamp) override {}
+ void Conceal(render_tree::ResourceProvider* resource_provider,
+ SbTimeMonotonic timestamp) override;
+ void Freeze(SbTimeMonotonic timestamp) override;
+ void Unfreeze(render_tree::ResourceProvider* resource_provider,
+ SbTimeMonotonic timestamp) override {}
+ void Reveal(render_tree::ResourceProvider* resource_provider,
+ SbTimeMonotonic timestamp) override {}
+ void Focus(SbTimeMonotonic timestamp) override {}
+
private:
base::Thread scroll_engine_{"ScrollEngineThread"};
base::RepeatingTimer free_scroll_timer_;
diff --git a/cobalt/version.h b/cobalt/version.h
index bd8a169..f54bb3b 100644
--- a/cobalt/version.h
+++ b/cobalt/version.h
@@ -35,6 +35,6 @@
// release is cut.
//.
-#define COBALT_VERSION "24.lts.10"
+#define COBALT_VERSION "24.lts.20"
#endif // COBALT_VERSION_H_
diff --git a/cobalt/watchdog/watchdog.cc b/cobalt/watchdog/watchdog.cc
index 1e43688..f328ec1 100644
--- a/cobalt/watchdog/watchdog.cc
+++ b/cobalt/watchdog/watchdog.cc
@@ -43,9 +43,11 @@
// The minimum number of microseconds between writes.
const int64_t kWatchdogWriteWaitTime = 300000000;
// The maximum number of most recent ping infos.
-const int kWatchdogMaxPingInfos = 20;
+const int kWatchdogMaxPingInfos = 60;
// The maximum length of each ping info.
-const int kWatchdogMaxPingInfoLength = 128;
+const int kWatchdogMaxPingInfoLength = 1024;
+// The maximum number of milliseconds old of an unfetched Watchdog violation.
+const int64_t kWatchdogMaxViolationsAge = 86400000;
// Persistent setting name and default setting for the boolean that controls
// whether or not Watchdog is enabled. When disabled, Watchdog behaves like a
@@ -109,7 +111,6 @@
// Starts monitor thread.
is_monitoring_.store(true);
- InitializeViolationsMap(this);
SB_DCHECK(!SbThreadIsValid(watchdog_thread_));
watchdog_thread_ = SbThreadCreate(0, kSbThreadNoPriority, kSbThreadNoAffinity,
true, "Watchdog", &Watchdog::Monitor, this);
@@ -133,6 +134,29 @@
SbThreadJoin(watchdog_thread_, nullptr);
}
+std::shared_ptr<base::Value> Watchdog::GetViolationsMap() {
+ // Gets the Watchdog violations map with lazy initialization which loads the
+ // previous Watchdog violations file containing violations before app start,
+ // if it exists.
+ if (violations_map_ == nullptr) {
+ starboard::ScopedFile read_file(GetWatchdogFilePath().c_str(),
+ kSbFileOpenOnly | kSbFileRead);
+ if (read_file.IsValid()) {
+ int64_t kFileSize = read_file.GetSize();
+ std::vector<char> buffer(kFileSize + 1, 0);
+ read_file.ReadAll(buffer.data(), kFileSize);
+ violations_map_ = base::JSONReader::Read(std::string(buffer.data()));
+ }
+
+ if (violations_map_ == nullptr) {
+ SB_LOG(INFO) << "[Watchdog] No previous violations JSON.";
+ violations_map_ =
+ std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+ }
+ }
+ return violations_map_;
+}
+
std::string Watchdog::GetWatchdogFilePath() {
// Gets the Watchdog violations file path with lazy initialization.
if (watchdog_file_path_ == "") {
@@ -148,33 +172,17 @@
}
std::vector<std::string> Watchdog::GetWatchdogViolationClientNames() {
- if (pending_write_) WriteWatchdogViolations();
-
- std::string watchdog_json = ReadViolationFile(GetWatchdogFilePath().c_str());
std::vector<std::string> names;
- if (watchdog_json != "") {
- std::unique_ptr<base::Value> violations_map =
- base::JSONReader::Read(watchdog_json);
- for (const auto& it : violations_map->DictItems()) {
- names.push_back(it.first);
- }
+
+ if (is_disabled_) return names;
+
+ starboard::ScopedLock scoped_lock(mutex_);
+ for (const auto& it : GetViolationsMap()->DictItems()) {
+ names.push_back(it.first);
}
return names;
}
-void Watchdog::WriteWatchdogViolations() {
- // Writes Watchdog violations to persistent storage as a json file.
- std::string watchdog_json;
- base::JSONWriter::Write(*violations_map_, &watchdog_json);
- SB_LOG(INFO) << "[Watchdog] Writing violations to JSON:\n" << watchdog_json;
- starboard::ScopedFile watchdog_file(GetWatchdogFilePath().c_str(),
- kSbFileCreateAlways | kSbFileWrite);
- watchdog_file.WriteAll(watchdog_json.c_str(),
- static_cast<int>(watchdog_json.size()));
- pending_write_ = false;
- time_last_written_microseconds_ = SbTimeGetMonotonicNow();
-}
-
void Watchdog::UpdateState(base::ApplicationState state) {
if (is_disabled_) return;
@@ -182,43 +190,41 @@
state_ = state;
}
+void Watchdog::WriteWatchdogViolations() {
+ // Writes Watchdog violations to persistent storage as a json file.
+ std::string watchdog_json;
+ base::JSONWriter::Write(*GetViolationsMap(), &watchdog_json);
+ SB_LOG(INFO) << "[Watchdog] Writing violations to JSON:\n" << watchdog_json;
+ starboard::ScopedFile watchdog_file(GetWatchdogFilePath().c_str(),
+ kSbFileCreateAlways | kSbFileWrite);
+ watchdog_file.WriteAll(watchdog_json.c_str(),
+ static_cast<int>(watchdog_json.size()));
+ pending_write_ = false;
+ time_last_written_microseconds_ = SbTimeGetMonotonicNow();
+}
+
void* Watchdog::Monitor(void* context) {
starboard::ScopedLock scoped_lock(static_cast<Watchdog*>(context)->mutex_);
while (1) {
SbTimeMonotonic current_monotonic_time = SbTimeGetMonotonicNow();
-
- // Iterates through client map to monitor all registered clients.
bool watchdog_violation = false;
+
+ // Iterates through client map to monitor all name registered clients.
for (auto& it : static_cast<Watchdog*>(context)->client_map_) {
Client* client = it.second.get();
- // Ignores and resets clients in idle states, clients whose monitor_state
- // is below the current application state. Resets time_wait_microseconds
- // and time_interval_microseconds start values.
- if (static_cast<Watchdog*>(context)->state_ > client->monitor_state) {
- client->time_registered_monotonic_microseconds = current_monotonic_time;
- client->time_last_updated_monotonic_microseconds =
- current_monotonic_time;
- continue;
- }
-
- SbTimeMonotonic time_delta =
- current_monotonic_time -
- client->time_last_updated_monotonic_microseconds;
- SbTimeMonotonic time_wait =
- current_monotonic_time -
- client->time_registered_monotonic_microseconds;
-
- // Watchdog violation
- if (time_delta > client->time_interval_microseconds &&
- time_wait > client->time_wait_microseconds) {
+ if (MonitorClient(context, client, current_monotonic_time)) {
watchdog_violation = true;
- UpdateViolationsMap(context, client, time_delta);
-
- // Resets time last updated.
- client->time_last_updated_monotonic_microseconds =
- current_monotonic_time;
}
}
+
+ // Iterates through client list to monitor all client registered clients.
+ for (auto& it : static_cast<Watchdog*>(context)->client_list_) {
+ Client* client = it.get();
+ if (MonitorClient(context, client, current_monotonic_time)) {
+ watchdog_violation = true;
+ }
+ }
+
if (static_cast<Watchdog*>(context)->pending_write_)
MaybeWriteWatchdogViolations(context);
if (watchdog_violation) MaybeTriggerCrash(context);
@@ -233,11 +239,39 @@
return nullptr;
}
+bool Watchdog::MonitorClient(void* context, Client* client,
+ SbTimeMonotonic current_monotonic_time) {
+ // Ignores and resets clients in idle states, clients whose monitor_state
+ // is below the current application state. Resets time_wait_microseconds
+ // and time_interval_microseconds start values.
+ if (static_cast<Watchdog*>(context)->state_ > client->monitor_state) {
+ client->time_registered_monotonic_microseconds = current_monotonic_time;
+ client->time_last_updated_monotonic_microseconds = current_monotonic_time;
+ return false;
+ }
+
+ SbTimeMonotonic time_delta =
+ current_monotonic_time - client->time_last_updated_monotonic_microseconds;
+ SbTimeMonotonic time_wait =
+ current_monotonic_time - client->time_registered_monotonic_microseconds;
+
+ // Watchdog violation
+ if (time_delta > client->time_interval_microseconds &&
+ time_wait > client->time_wait_microseconds) {
+ UpdateViolationsMap(context, client, time_delta);
+ // Resets time last updated.
+ client->time_last_updated_monotonic_microseconds = current_monotonic_time;
+ return true;
+ }
+ return false;
+}
+
void Watchdog::UpdateViolationsMap(void* context, Client* client,
SbTimeMonotonic time_delta) {
- // Gets violation dictionary with key client name from violations_map_.
+ // Gets violation dictionary with key client name from violations map.
base::Value* violation_dict =
- (static_cast<Watchdog*>(context)->violations_map_)->FindKey(client->name);
+ (static_cast<Watchdog*>(context)->GetViolationsMap())
+ ->FindKey(client->name);
// Checks if new unique violation.
bool new_violation = false;
@@ -256,9 +290,8 @@
new_violation = true;
}
- // New unique violation.
if (new_violation) {
- // Creates new violation.
+ // New unique violation, creates violation in violations map.
base::Value violation(base::Value::Type::DICTIONARY);
violation.SetKey("pingInfos", client->ping_infos.Clone());
violation.SetKey("monitorState",
@@ -287,28 +320,26 @@
for (auto& it : static_cast<Watchdog*>(context)->client_map_) {
registered_clients.GetList().emplace_back(base::Value(it.first));
}
+ for (auto& it : static_cast<Watchdog*>(context)->client_list_) {
+ registered_clients.GetList().emplace_back(base::Value(it->name));
+ }
violation.SetKey("registeredClients", registered_clients.Clone());
- // Adds new violation to violations_map_.
+ // Adds new violation to violations map.
if (violation_dict == nullptr) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetKey("description", base::Value(client->description));
base::Value list(base::Value::Type::LIST);
list.GetList().emplace_back(violation.Clone());
dict.SetKey("violations", list.Clone());
- (static_cast<Watchdog*>(context)->violations_map_)
+ (static_cast<Watchdog*>(context)->GetViolationsMap())
->SetKey(client->name, dict.Clone());
} else {
base::Value* violations = violation_dict->FindKey("violations");
violations->GetList().emplace_back(violation.Clone());
}
- static_cast<Watchdog*>(context)->violations_count_++;
- if (static_cast<Watchdog*>(context)->violations_count_ >
- kWatchdogMaxViolations)
- EvictWatchdogViolation(context);
- // Consecutive non-unique violation.
} else {
- // Updates consecutive violation in violations_map_.
+ // Consecutive non-unique violation, updates violation in violations map.
base::Value* violations = violation_dict->FindKey("violations");
int last_index = violations->GetList().size() - 1;
int64_t violation_duration =
@@ -319,81 +350,68 @@
"violationDurationMilliseconds",
base::Value(std::to_string(violation_duration + (time_delta / 1000))));
}
-
static_cast<Watchdog*>(context)->pending_write_ = true;
-}
-std::string Watchdog::ReadViolationFile(const char* file_path) {
- starboard::ScopedFile read_file(file_path, kSbFileOpenOnly | kSbFileRead);
- if (read_file.IsValid()) {
- int64_t kFileSize = read_file.GetSize();
- std::vector<char> buffer(kFileSize + 1, 0);
- read_file.ReadAll(buffer.data(), kFileSize);
- return std::string(buffer.data());
+ int violations_count = 0;
+ for (const auto& it :
+ (static_cast<Watchdog*>(context)->GetViolationsMap())->DictItems()) {
+ base::Value& violation_dict = it.second;
+ base::Value* violations = violation_dict.FindKey("violations");
+ violations_count += violations->GetList().size();
}
- return "";
-}
-
-void Watchdog::InitializeViolationsMap(void* context) {
- // Loads the previous Watchdog violations file containing violations before
- // app start, if it exists, to populate violations_map_.
- static_cast<Watchdog*>(context)->violations_count_ = 0;
-
- std::string watchdog_json =
- static_cast<Watchdog*>(context)->ReadViolationFile(
- (static_cast<Watchdog*>(context)->GetWatchdogFilePath()).c_str());
- if (watchdog_json != "") {
- static_cast<Watchdog*>(context)->violations_map_ =
- base::JSONReader::Read(watchdog_json);
- }
-
- if (static_cast<Watchdog*>(context)->violations_map_ == nullptr) {
- SB_LOG(INFO) << "[Watchdog] No previous violations JSON.";
- static_cast<Watchdog*>(context)->violations_map_ =
- std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
- } else {
- for (const auto& it :
- (static_cast<Watchdog*>(context)->violations_map_)->DictItems()) {
- base::Value& violation_dict = it.second;
- base::Value* violations = violation_dict.FindKey("violations");
- static_cast<Watchdog*>(context)->violations_count_ +=
- violations->GetList().size();
- }
+ if (violations_count > kWatchdogMaxViolations) {
+ EvictWatchdogViolation(context);
}
}
void Watchdog::EvictWatchdogViolation(void* context) {
- // Evicts a violation in violations_map_ prioritizing first the most frequent
+ // Evicts a violation in violations map prioritizing first the most frequent
// violations (largest violations count by client name) and second the oldest
// violation.
std::string evicted_name = "";
int evicted_count = 0;
- int64_t evicted_timestamp = 0;
+ int64_t evicted_timestamp_millis = 0;
for (const auto& it :
- (static_cast<Watchdog*>(context)->violations_map_)->DictItems()) {
+ (static_cast<Watchdog*>(context)->GetViolationsMap())->DictItems()) {
std::string name = it.first;
base::Value& violation_dict = it.second;
base::Value* violations = violation_dict.FindKey("violations");
int count = violations->GetList().size();
- int64_t timestamp =
+ int64_t violation_timestamp_millis =
std::stoll(violations->GetList()[0]
.FindKey("timestampViolationMilliseconds")
->GetString());
if ((evicted_name == "") || (count > evicted_count) ||
- ((count == evicted_count) && (timestamp < evicted_timestamp))) {
+ ((count == evicted_count) &&
+ (violation_timestamp_millis < evicted_timestamp_millis))) {
evicted_name = name;
evicted_count = count;
- evicted_timestamp = timestamp;
+ evicted_timestamp_millis = violation_timestamp_millis;
}
}
base::Value* violation_dict =
- (static_cast<Watchdog*>(context)->violations_map_)->FindKey(evicted_name);
- base::Value* violations = violation_dict->FindKey("violations");
- violations->GetList().erase(violations->GetList().begin());
- static_cast<Watchdog*>(context)->violations_count_--;
+ (static_cast<Watchdog*>(context)->GetViolationsMap())
+ ->FindKey(evicted_name);
+
+ if (violation_dict != nullptr) {
+ base::Value* violations = violation_dict->FindKey("violations");
+ violations->GetList().erase(violations->GetList().begin());
+ static_cast<Watchdog*>(context)->pending_write_ = true;
+
+ // Removes empty violations.
+ if (violations->GetList().empty()) {
+ (static_cast<Watchdog*>(context)->GetViolationsMap())
+ ->RemoveKey(evicted_name);
+ }
+ if (static_cast<Watchdog*>(context)->GetViolationsMap()->DictEmpty()) {
+ starboard::SbFileDeleteRecursive(
+ static_cast<Watchdog*>(context)->GetWatchdogFilePath().c_str(), true);
+ static_cast<Watchdog*>(context)->pending_write_ = false;
+ }
+ }
}
void Watchdog::MaybeWriteWatchdogViolations(void* context) {
@@ -409,7 +427,7 @@
if (static_cast<Watchdog*>(context)->pending_write_)
static_cast<Watchdog*>(context)->WriteWatchdogViolations();
SB_LOG(ERROR) << "[Watchdog] Triggering violation Crash!";
- CHECK(false);
+ *(reinterpret_cast<volatile char*>(0)) = 0;
}
}
@@ -419,19 +437,6 @@
int64_t time_wait_microseconds, Replace replace) {
if (is_disabled_) return true;
- // Validates parameters.
- if (time_interval_microseconds < watchdog_monitor_frequency_ ||
- time_wait_microseconds < 0) {
- SB_DLOG(ERROR) << "[Watchdog] Unable to Register: " << name;
- if (time_interval_microseconds < watchdog_monitor_frequency_) {
- SB_DLOG(ERROR) << "[Watchdog] Time interval less than min: "
- << watchdog_monitor_frequency_;
- } else {
- SB_DLOG(ERROR) << "[Watchdog] Time wait is negative.";
- }
- return false;
- }
-
starboard::ScopedLock scoped_lock(mutex_);
int64_t current_time = SbTimeToPosix(SbTimeGetNow());
@@ -453,6 +458,65 @@
}
}
+ // Creates new client.
+ std::unique_ptr<Client> client = CreateClient(
+ name, description, monitor_state, time_interval_microseconds,
+ time_wait_microseconds, current_time, current_monotonic_time);
+ if (client == nullptr) return false;
+
+ // Registers.
+ auto result = client_map_.emplace(name, std::move(client));
+
+ if (result.second) {
+ SB_DLOG(INFO) << "[Watchdog] Registered: " << name;
+ } else {
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Register: " << name;
+ }
+ return result.second;
+}
+
+std::shared_ptr<Client> Watchdog::RegisterByClient(
+ std::string name, std::string description,
+ base::ApplicationState monitor_state, int64_t time_interval_microseconds,
+ int64_t time_wait_microseconds) {
+ if (is_disabled_) return nullptr;
+
+ starboard::ScopedLock scoped_lock(mutex_);
+
+ int64_t current_time = SbTimeToPosix(SbTimeGetNow());
+ SbTimeMonotonic current_monotonic_time = SbTimeGetMonotonicNow();
+
+ // Creates new client.
+ std::shared_ptr<Client> client = CreateClient(
+ name, description, monitor_state, time_interval_microseconds,
+ time_wait_microseconds, current_time, current_monotonic_time);
+ if (client == nullptr) return nullptr;
+
+ // Registers.
+ client_list_.emplace_back(client);
+
+ SB_DLOG(INFO) << "[Watchdog] Registered: " << name;
+ return client;
+}
+
+std::unique_ptr<Client> Watchdog::CreateClient(
+ std::string name, std::string description,
+ base::ApplicationState monitor_state, int64_t time_interval_microseconds,
+ int64_t time_wait_microseconds, int64_t current_time,
+ SbTimeMonotonic current_monotonic_time) {
+ // Validates parameters.
+ if (time_interval_microseconds < watchdog_monitor_frequency_ ||
+ time_wait_microseconds < 0) {
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Register: " << name;
+ if (time_interval_microseconds < watchdog_monitor_frequency_) {
+ SB_DLOG(ERROR) << "[Watchdog] Time interval less than min: "
+ << watchdog_monitor_frequency_;
+ } else {
+ SB_DLOG(ERROR) << "[Watchdog] Time wait is negative.";
+ }
+ return nullptr;
+ }
+
// Creates new Client.
std::unique_ptr<Client> client(new Client);
client->name = name;
@@ -466,15 +530,7 @@
client->time_last_pinged_microseconds = current_time;
client->time_last_updated_monotonic_microseconds = current_monotonic_time;
- // Registers.
- auto result = client_map_.emplace(name, std::move(client));
-
- if (result.second) {
- SB_DLOG(INFO) << "[Watchdog] Registered: " << name;
- } else {
- SB_DLOG(ERROR) << "[Watchdog] Unable to Register: " << name;
- }
- return result.second;
+ return std::move(client);
}
bool Watchdog::Unregister(const std::string& name, bool lock) {
@@ -493,11 +549,68 @@
return result;
}
+bool Watchdog::UnregisterByClient(std::shared_ptr<Client> client) {
+ if (is_disabled_) return true;
+
+ starboard::ScopedLock scoped_lock(mutex_);
+
+ std::string name = "";
+ if (client) name = client->name;
+
+ // Unregisters.
+ for (auto it = client_list_.begin(); it != client_list_.end(); it++) {
+ if (client == *it) {
+ client_list_.erase(it);
+ SB_DLOG(INFO) << "[Watchdog] Unregistered: " << name;
+ return true;
+ }
+ }
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Unregister: " << name;
+ return false;
+}
+
bool Watchdog::Ping(const std::string& name) { return Ping(name, ""); }
bool Watchdog::Ping(const std::string& name, const std::string& info) {
if (is_disabled_) return true;
+ starboard::ScopedLock scoped_lock(mutex_);
+
+ auto it = client_map_.find(name);
+ bool client_exists = it != client_map_.end();
+
+ if (client_exists) {
+ Client* client = it->second.get();
+ return PingHelper(client, name, info);
+ }
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Ping: " << name;
+ return false;
+}
+
+bool Watchdog::PingByClient(std::shared_ptr<Client> client) {
+ return PingByClient(client, "");
+}
+
+bool Watchdog::PingByClient(std::shared_ptr<Client> client,
+ const std::string& info) {
+ if (is_disabled_) return true;
+
+ std::string name = "";
+ if (client) name = client->name;
+
+ starboard::ScopedLock scoped_lock(mutex_);
+
+ for (auto it = client_list_.begin(); it != client_list_.end(); it++) {
+ if (client == *it) {
+ return PingHelper(client.get(), name, info);
+ }
+ }
+ SB_DLOG(ERROR) << "[Watchdog] Unable to Ping: " << name;
+ return false;
+}
+
+bool Watchdog::PingHelper(Client* client, const std::string& name,
+ const std::string& info) {
// Validates parameters.
if (info.length() > kWatchdogMaxPingInfoLength) {
SB_DLOG(ERROR) << "[Watchdog] Unable to Ping: " << name;
@@ -506,95 +619,109 @@
return false;
}
- starboard::ScopedLock scoped_lock(mutex_);
+ int64_t current_time = SbTimeToPosix(SbTimeGetNow());
+ SbTimeMonotonic current_monotonic_time = SbTimeGetMonotonicNow();
- auto it = client_map_.find(name);
- bool client_exists = it != client_map_.end();
+ // Updates last ping.
+ client->time_last_pinged_microseconds = current_time;
+ client->time_last_updated_monotonic_microseconds = current_monotonic_time;
- if (client_exists) {
- int64_t current_time = SbTimeToPosix(SbTimeGetNow());
- SbTimeMonotonic current_monotonic_time = SbTimeGetMonotonicNow();
+ if (info != "") {
+ // Creates new ping_info.
+ base::Value ping_info(base::Value::Type::DICTIONARY);
+ ping_info.SetKey("timestampMilliseconds",
+ base::Value(std::to_string(current_time / 1000)));
+ ping_info.SetKey("info", base::Value(info));
- Client* client = it->second.get();
- // Updates last ping.
- client->time_last_pinged_microseconds = current_time;
- client->time_last_updated_monotonic_microseconds = current_monotonic_time;
-
- if (info != "") {
- // Creates new ping_info.
- base::Value ping_info(base::Value::Type::DICTIONARY);
- ping_info.SetKey("timestampMilliseconds",
- base::Value(std::to_string(current_time / 1000)));
- ping_info.SetKey("info", base::Value(info));
-
- client->ping_infos.GetList().emplace_back(ping_info.Clone());
- if (client->ping_infos.GetList().size() > kWatchdogMaxPingInfos)
- client->ping_infos.GetList().erase(
- client->ping_infos.GetList().begin());
- }
- } else {
- SB_DLOG(ERROR) << "[Watchdog] Unable to Ping: " << name;
+ client->ping_infos.GetList().emplace_back(ping_info.Clone());
+ if (client->ping_infos.GetList().size() > kWatchdogMaxPingInfos)
+ client->ping_infos.GetList().erase(client->ping_infos.GetList().begin());
}
- return client_exists;
+ return true;
}
std::string Watchdog::GetWatchdogViolations(
const std::vector<std::string>& clients, bool clear) {
- // Gets a json string containing the Watchdog violations since the last
- // call (up to the kWatchdogMaxViolations limit).
+ // Gets a json string containing the Watchdog violations of the given clients
+ // since the last call (up to the kWatchdogMaxViolations limit).
if (is_disabled_) return "";
- std::string watchdog_json = "";
- std::string watchdog_json_fetched = "";
+ std::string fetched_violations_json = "";
starboard::ScopedLock scoped_lock(mutex_);
- if (pending_write_) WriteWatchdogViolations();
-
- if (!static_cast<base::DictionaryValue*>(violations_map_.get())->empty()) {
- // Get all Watchdog violations if clients is given.
+ if (!GetViolationsMap()->DictEmpty()) {
if (clients.empty()) {
- // Removes all Watchdog violations.
- base::JSONWriter::Write(*violations_map_, &watchdog_json_fetched);
+ // Gets all Watchdog violations if no clients are given.
+ base::JSONWriter::Write(*GetViolationsMap(), &fetched_violations_json);
if (clear) {
- static_cast<base::DictionaryValue*>(violations_map_.get())->Clear();
- violations_count_ = 0;
+ static_cast<base::DictionaryValue*>(GetViolationsMap().get())->Clear();
starboard::SbFileDeleteRecursive(GetWatchdogFilePath().c_str(), true);
}
} else {
- base::Value filtered_client_data(base::Value::Type::DICTIONARY);
- for (int i = 0; i < clients.size(); i++) {
- base::Value* violation_dict =
- static_cast<base::DictionaryValue*>(violations_map_.get())
- ->FindKey(clients[i]);
+ // Gets all Watchdog violations of the given clients.
+ base::Value fetched_violations(base::Value::Type::DICTIONARY);
+ for (std::string name : clients) {
+ base::Value* violation_dict = GetViolationsMap()->FindKey(name);
if (violation_dict != nullptr) {
- filtered_client_data.SetKey(clients[i], (*violation_dict).Clone());
+ fetched_violations.SetKey(name, (*violation_dict).Clone());
if (clear) {
- base::Value* violations = violation_dict->FindKey("violations");
- int violations_count = violations->GetList().size();
-
- static_cast<base::DictionaryValue*>(violations_map_.get())
- ->RemoveKey(clients[i]);
- violations_count_ -= violations_count;
- if (!static_cast<base::DictionaryValue*>(violations_map_.get())
- ->empty()) {
- WriteWatchdogViolations();
- } else {
- starboard::SbFileDeleteRecursive(GetWatchdogFilePath().c_str(),
- true);
- }
+ GetViolationsMap()->RemoveKey(name);
+ pending_write_ = true;
}
}
}
- if (!filtered_client_data.DictEmpty()) {
- base::JSONWriter::Write(filtered_client_data, &watchdog_json_fetched);
+ if (!fetched_violations.DictEmpty()) {
+ base::JSONWriter::Write(fetched_violations, &fetched_violations_json);
+ }
+ if (clear) {
+ EvictOldWatchdogViolations();
}
}
- SB_LOG(INFO) << "[Watchdog] Reading violations:\n" << watchdog_json_fetched;
+ SB_LOG(INFO) << "[Watchdog] Reading violations:\n"
+ << fetched_violations_json;
} else {
SB_LOG(INFO) << "[Watchdog] No violations.";
}
- return watchdog_json_fetched;
+ return fetched_violations_json;
+}
+
+void Watchdog::EvictOldWatchdogViolations() {
+ int64_t current_timestamp_millis = SbTimeToPosix(SbTimeGetNow()) / 1000;
+ int64_t cutoff_timestamp_millis =
+ current_timestamp_millis - kWatchdogMaxViolationsAge;
+ std::vector<std::string> empty_violations;
+
+ // Iterates through map removing old violations.
+ for (const auto& map_it : GetViolationsMap()->DictItems()) {
+ std::string name = map_it.first;
+ base::Value& violation_dict = map_it.second;
+ base::Value* violations = violation_dict.FindKey("violations");
+ for (auto list_it = violations->GetList().begin();
+ list_it != violations->GetList().end();) {
+ int64_t violation_timestamp_millis = std::stoll(
+ list_it->FindKey("timestampViolationMilliseconds")->GetString());
+
+ if (violation_timestamp_millis < cutoff_timestamp_millis) {
+ list_it = violations->GetList().erase(list_it);
+ pending_write_ = true;
+ } else {
+ list_it++;
+ }
+ }
+ if (violations->GetList().empty()) {
+ empty_violations.push_back(name);
+ }
+ }
+
+ // Removes empty violations.
+ for (std::string name : empty_violations) {
+ GetViolationsMap()->RemoveKey(name);
+ }
+ if (GetViolationsMap()->DictEmpty()) {
+ starboard::SbFileDeleteRecursive(GetWatchdogFilePath().c_str(), true);
+ pending_write_ = false;
+ }
}
bool Watchdog::GetPersistentSettingWatchdogEnable() {
diff --git a/cobalt/watchdog/watchdog.h b/cobalt/watchdog/watchdog.h
index 8aa9a7c..aed2ec1 100644
--- a/cobalt/watchdog/watchdog.h
+++ b/cobalt/watchdog/watchdog.h
@@ -88,9 +88,19 @@
base::ApplicationState monitor_state,
int64_t time_interval_microseconds,
int64_t time_wait_microseconds = 0, Replace replace = NONE);
+ std::shared_ptr<Client> RegisterByClient(std::string name,
+ std::string description,
+ base::ApplicationState monitor_state,
+ int64_t time_interval_microseconds,
+ int64_t time_wait_microseconds = 0);
bool Unregister(const std::string& name, bool lock = true);
+ bool UnregisterByClient(std::shared_ptr<Client> client);
bool Ping(const std::string& name);
bool Ping(const std::string& name, const std::string& info);
+ bool PingByClient(std::shared_ptr<Client> client);
+ bool PingByClient(std::shared_ptr<Client> client, const std::string& info);
+ bool PingHelper(Client* client, const std::string& name,
+ const std::string& info);
std::string GetWatchdogViolations(
const std::vector<std::string>& clients = {}, bool clear = true);
bool GetPersistentSettingWatchdogEnable();
@@ -104,12 +114,21 @@
#endif // defined(_DEBUG)
private:
+ std::shared_ptr<base::Value> GetViolationsMap();
void WriteWatchdogViolations();
- std::string ReadViolationFile(const char* file_path);
+ void EvictOldWatchdogViolations();
+ std::unique_ptr<Client> CreateClient(std::string name,
+ std::string description,
+ base::ApplicationState monitor_state,
+ int64_t time_interval_microseconds,
+ int64_t time_wait_microseconds,
+ int64_t current_time,
+ SbTimeMonotonic current_monotonic_time);
static void* Monitor(void* context);
+ static bool MonitorClient(void* context, Client* client,
+ SbTimeMonotonic current_monotonic_time);
static void UpdateViolationsMap(void* context, Client* client,
SbTimeMonotonic time_delta);
- static void InitializeViolationsMap(void* context);
static void EvictWatchdogViolation(void* context);
static void MaybeWriteWatchdogViolations(void* context);
static void MaybeTriggerCrash(void* context);
@@ -139,12 +158,13 @@
SbTimeMonotonic time_last_written_microseconds_ = 0;
// Number of microseconds between writes.
int64_t write_wait_time_microseconds_;
- // Dictionary of registered Watchdog clients.
+ // Dictionary of name registered Watchdog clients.
std::unordered_map<std::string, std::unique_ptr<Client>> client_map_;
+ // List of client registered Watchdog clients, parallel data structure to
+ // client_map_.
+ std::vector<std::shared_ptr<Client>> client_list_;
// Dictionary of lists of Watchdog violations represented as dictionaries.
- std::unique_ptr<base::Value> violations_map_;
- // Number of violations in violations_map_;
- int violations_count_;
+ std::shared_ptr<base::Value> violations_map_;
// Monitor thread.
SbThread watchdog_thread_;
// Flag to stop monitor thread.
diff --git a/cobalt/watchdog/watchdog_test.cc b/cobalt/watchdog/watchdog_test.cc
index 1cafc43..6668c2a 100644
--- a/cobalt/watchdog/watchdog_test.cc
+++ b/cobalt/watchdog/watchdog_test.cc
@@ -86,7 +86,6 @@
ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
base::kApplicationStateStarted,
kWatchdogMonitorFrequency));
-
ASSERT_FALSE(watchdog_->Register("test-name", "test-desc",
base::kApplicationStateStarted,
kWatchdogMonitorFrequency));
@@ -100,10 +99,6 @@
}
TEST_F(WatchdogTest, RegisterOnlyAcceptsValidParameters) {
- ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
- base::kApplicationStateStarted,
- kWatchdogMonitorFrequency, 0));
- ASSERT_TRUE(watchdog_->Unregister("test-name"));
ASSERT_FALSE(watchdog_->Register("test-name-1", "test-desc-1",
base::kApplicationStateStarted, 1, 0));
ASSERT_FALSE(watchdog_->Unregister("test-name-1"));
@@ -127,7 +122,37 @@
ASSERT_FALSE(watchdog_->Unregister("test-name-6"));
}
-TEST_F(WatchdogTest, UnmatchedUnregistersShouldFail) {
+TEST_F(WatchdogTest, RegisterByClientOnlyAcceptsValidParameters) {
+ std::shared_ptr<Client> client = watchdog_->RegisterByClient(
+ "test-name", "test-desc", base::kApplicationStateStarted, 1, 0);
+ ASSERT_EQ(client, nullptr);
+ ASSERT_FALSE(watchdog_->UnregisterByClient(client));
+ client = watchdog_->RegisterByClient("test-name", "test-desc",
+ base::kApplicationStateStarted, 0, 0);
+ ASSERT_EQ(client, nullptr);
+ ASSERT_FALSE(watchdog_->UnregisterByClient(client));
+ client = watchdog_->RegisterByClient("test-name", "test-desc",
+ base::kApplicationStateStarted, -1, 0);
+ ASSERT_EQ(client, nullptr);
+ ASSERT_FALSE(watchdog_->UnregisterByClient(client));
+ client = watchdog_->RegisterByClient("test-name", "test-desc",
+ base::kApplicationStateStarted,
+ kWatchdogMonitorFrequency, 1);
+ ASSERT_NE(client, nullptr);
+ ASSERT_TRUE(watchdog_->UnregisterByClient(client));
+ client = watchdog_->RegisterByClient("test-name", "test-desc",
+ base::kApplicationStateStarted,
+ kWatchdogMonitorFrequency, 0);
+ ASSERT_NE(client, nullptr);
+ ASSERT_TRUE(watchdog_->UnregisterByClient(client));
+ client = watchdog_->RegisterByClient("test-name", "test-desc",
+ base::kApplicationStateStarted,
+ kWatchdogMonitorFrequency, -1);
+ ASSERT_EQ(client, nullptr);
+ ASSERT_FALSE(watchdog_->UnregisterByClient(client));
+}
+
+TEST_F(WatchdogTest, UnmatchedUnregisterShouldFail) {
ASSERT_FALSE(watchdog_->Unregister("test-name"));
ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
base::kApplicationStateStarted,
@@ -136,30 +161,58 @@
ASSERT_FALSE(watchdog_->Unregister("test-name"));
}
-TEST_F(WatchdogTest, UnmatchedPingsShouldFail) {
+TEST_F(WatchdogTest, UnmatchedUnregisterByClientShouldFail) {
+ std::shared_ptr<Client> client_test(new Client);
+ ASSERT_FALSE(watchdog_->UnregisterByClient(client_test));
+ std::shared_ptr<Client> client = watchdog_->RegisterByClient(
+ "test-name", "test-desc", base::kApplicationStateStarted,
+ kWatchdogMonitorFrequency);
+ ASSERT_NE(client, nullptr);
+ ASSERT_TRUE(watchdog_->UnregisterByClient(client));
+ ASSERT_FALSE(watchdog_->UnregisterByClient(client));
+}
+
+TEST_F(WatchdogTest, UnmatchedPingShouldFail) {
ASSERT_FALSE(watchdog_->Ping("test-name"));
ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
base::kApplicationStateStarted,
kWatchdogMonitorFrequency));
ASSERT_TRUE(watchdog_->Ping("test-name"));
- ASSERT_TRUE(watchdog_->Ping("test-name"));
ASSERT_TRUE(watchdog_->Unregister("test-name"));
ASSERT_FALSE(watchdog_->Ping("test-name"));
}
+TEST_F(WatchdogTest, UnmatchedPingByClientShouldFail) {
+ std::shared_ptr<Client> client_test(new Client);
+ ASSERT_FALSE(watchdog_->PingByClient(client_test));
+ std::shared_ptr<Client> client = watchdog_->RegisterByClient(
+ "test-name", "test-desc", base::kApplicationStateStarted,
+ kWatchdogMonitorFrequency);
+ ASSERT_NE(client, nullptr);
+ ASSERT_TRUE(watchdog_->PingByClient(client));
+ ASSERT_TRUE(watchdog_->UnregisterByClient(client));
+ ASSERT_FALSE(watchdog_->PingByClient(client));
+}
+
TEST_F(WatchdogTest, PingOnlyAcceptsValidParameters) {
ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
base::kApplicationStateStarted,
kWatchdogMonitorFrequency));
ASSERT_TRUE(watchdog_->Ping("test-name", "42"));
- ASSERT_FALSE(
- watchdog_->Ping("test-name",
- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
- "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
- "xxxxxxxxxxxxxxxxxxxxxxxxxxx"));
+ ASSERT_FALSE(watchdog_->Ping("test-name", std::string(1025, 'x')));
ASSERT_TRUE(watchdog_->Unregister("test-name"));
}
+TEST_F(WatchdogTest, PingByClientOnlyAcceptsValidParameters) {
+ std::shared_ptr<Client> client = watchdog_->RegisterByClient(
+ "test-name", "test-desc", base::kApplicationStateStarted,
+ kWatchdogMonitorFrequency);
+ ASSERT_NE(client, nullptr);
+ ASSERT_TRUE(watchdog_->PingByClient(client, "42"));
+ ASSERT_FALSE(watchdog_->PingByClient(client, std::string(1025, 'x')));
+ ASSERT_TRUE(watchdog_->UnregisterByClient(client));
+}
+
TEST_F(WatchdogTest, ViolationsJsonShouldPersistAndBeValid) {
ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
@@ -281,11 +334,12 @@
std::string json = watchdog_->GetWatchdogViolations();
ASSERT_NE(json.find("test-name-1"), std::string::npos);
ASSERT_EQ(json.find("test-name-2"), std::string::npos);
- ASSERT_TRUE(watchdog_->Register("test-name-2", "test-desc-2",
- base::kApplicationStateStarted,
- kWatchdogMonitorFrequency));
+ std::shared_ptr<Client> client = watchdog_->RegisterByClient(
+ "test-name-2", "test-desc-2", base::kApplicationStateStarted,
+ kWatchdogMonitorFrequency);
+ ASSERT_NE(client, nullptr);
SbThreadSleep(kWatchdogSleepDuration);
- ASSERT_TRUE(watchdog_->Unregister("test-name-2"));
+ ASSERT_TRUE(watchdog_->UnregisterByClient(client));
json = watchdog_->GetWatchdogViolations();
ASSERT_EQ(json.find("test-name-1"), std::string::npos);
ASSERT_NE(json.find("test-name-2"), std::string::npos);
@@ -296,7 +350,7 @@
ASSERT_TRUE(watchdog_->Register("test-name", "test_desc",
base::kApplicationStateStarted,
kWatchdogMonitorFrequency));
- for (int i = 0; i < 21; i++) {
+ for (int i = 0; i < 61; i++) {
ASSERT_TRUE(watchdog_->Ping("test-name", std::to_string(i)));
}
SbThreadSleep(kWatchdogSleepDuration);
@@ -307,7 +361,7 @@
base::Value* violation_dict = violations_map->FindKey("test-name");
base::Value* violations = violation_dict->FindKey("violations");
base::Value* pingInfos = violations->GetList()[0].FindKey("pingInfos");
- ASSERT_EQ(pingInfos->GetList().size(), 20);
+ ASSERT_EQ(pingInfos->GetList().size(), 60);
ASSERT_EQ(pingInfos->GetList()[0].FindKey("info")->GetString(), "1");
ASSERT_TRUE(watchdog_->Unregister("test-name"));
}
@@ -408,7 +462,7 @@
ASSERT_TRUE(watchdog_->Unregister("test-name"));
}
-TEST_F(WatchdogTest, PingsShouldPreventViolations) {
+TEST_F(WatchdogTest, PingShouldPreventViolations) {
ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
base::kApplicationStateStarted,
kWatchdogMonitorFrequency));
@@ -438,6 +492,20 @@
ASSERT_TRUE(watchdog_->Unregister("test-name"));
}
+TEST_F(WatchdogTest, PingByClientShouldPreventViolations) {
+ std::shared_ptr<Client> client = watchdog_->RegisterByClient(
+ "test-name", "test-desc", base::kApplicationStateStarted,
+ kWatchdogMonitorFrequency);
+ SbThreadSleep(kWatchdogMonitorFrequency / 2);
+ ASSERT_TRUE(watchdog_->PingByClient(client));
+ SbThreadSleep(kWatchdogMonitorFrequency / 2);
+ ASSERT_TRUE(watchdog_->PingByClient(client));
+ ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+ SbThreadSleep(kWatchdogSleepDuration);
+ ASSERT_NE(watchdog_->GetWatchdogViolations(), "");
+ ASSERT_TRUE(watchdog_->UnregisterByClient(client));
+}
+
TEST_F(WatchdogTest, UnregisterShouldPreventViolations) {
ASSERT_TRUE(watchdog_->Register("test-name", "test-desc",
base::kApplicationStateStarted,
@@ -447,6 +515,15 @@
ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
}
+TEST_F(WatchdogTest, UnregisterByClientShouldPreventViolations) {
+ std::shared_ptr<Client> client = watchdog_->RegisterByClient(
+ "test-name", "test-desc", base::kApplicationStateStarted,
+ kWatchdogMonitorFrequency);
+ ASSERT_TRUE(watchdog_->UnregisterByClient(client));
+ SbThreadSleep(kWatchdogSleepDuration);
+ ASSERT_EQ(watchdog_->GetWatchdogViolations(), "");
+}
+
TEST_F(WatchdogTest, KillSwitchShouldPreventViolations) {
TearDown();
watchdog_ = new watchdog::Watchdog();
@@ -502,14 +579,13 @@
SbThreadSleep(kWatchdogSleepDuration);
ASSERT_TRUE(watchdog_->Unregister("test-name-1"));
ASSERT_TRUE(watchdog_->Unregister("test-name-2"));
+
std::vector<std::string> names = watchdog_->GetWatchdogViolationClientNames();
ASSERT_EQ(names.size(), 2);
- std::set<std::string> expected_names = {"test-name-1", "test-name-2"};
- for (std::vector<std::string>::const_iterator it = names.begin();
- it != names.end(); ++it) {
- const std::string name = *it;
- ASSERT_TRUE((expected_names.find(name) != expected_names.end()));
- }
+ ASSERT_TRUE(std::find(names.begin(), names.end(), "test-name-1") !=
+ names.end());
+ ASSERT_TRUE(std::find(names.begin(), names.end(), "test-name-2") !=
+ names.end());
watchdog_->GetWatchdogViolations();
names = watchdog_->GetWatchdogViolationClientNames();
ASSERT_EQ(names.size(), 0);
@@ -522,13 +598,10 @@
ASSERT_TRUE(watchdog_->Register("test-name-2", "test-desc-2",
base::kApplicationStateStarted,
kWatchdogMonitorFrequency));
- ASSERT_TRUE(watchdog_->Register("test-name-3", "test-desc-3",
- base::kApplicationStateStarted,
- kWatchdogMonitorFrequency));
SbThreadSleep(kWatchdogSleepDuration);
ASSERT_TRUE(watchdog_->Unregister("test-name-1"));
ASSERT_TRUE(watchdog_->Unregister("test-name-2"));
- ASSERT_TRUE(watchdog_->Unregister("test-name-3"));
+
const std::vector<std::string> clients = {"test-name-1"};
std::string json = watchdog_->GetWatchdogViolations(clients);
ASSERT_NE(json, "");
@@ -538,45 +611,29 @@
ASSERT_NE(violation_dict, nullptr);
violation_dict = violations_map->FindKey("test-name-2");
ASSERT_EQ(violation_dict, nullptr);
- violation_dict = violations_map->FindKey("test-name-3");
- ASSERT_EQ(violation_dict, nullptr);
-
- std::string file_json = "";
- starboard::ScopedFile read_file(watchdog_->GetWatchdogFilePath().c_str(),
- kSbFileOpenOnly | kSbFileRead);
- if (read_file.IsValid()) {
- int64_t kFileSize = read_file.GetSize();
- std::vector<char> buffer(kFileSize + 1, 0);
- read_file.ReadAll(buffer.data(), kFileSize);
- file_json = std::string(buffer.data());
- }
- ASSERT_NE(file_json, "");
- violations_map = base::JSONReader::Read(file_json);
- ASSERT_NE(violations_map, nullptr);
- violation_dict = violations_map->FindKey("test-name-2");
- ASSERT_NE(violation_dict, nullptr);
- violation_dict = violations_map->FindKey("test-name-3");
- ASSERT_NE(violation_dict, nullptr);
- violation_dict = violations_map->FindKey("test-name-1");
- ASSERT_EQ(violation_dict, nullptr);
-
json = watchdog_->GetWatchdogViolations(clients);
ASSERT_EQ(json, "");
+}
- const std::vector<std::string> clients2 = {"test-name-2", "test-name-3"};
- json = watchdog_->GetWatchdogViolations(clients2);
- ASSERT_NE(json, "");
- violations_map = base::JSONReader::Read(json);
- ASSERT_NE(violations_map, nullptr);
- violation_dict = violations_map->FindKey("test-name-1");
- ASSERT_EQ(violation_dict, nullptr);
- violation_dict = violations_map->FindKey("test-name-2");
- ASSERT_NE(violation_dict, nullptr);
- violation_dict = violations_map->FindKey("test-name-3");
- ASSERT_NE(violation_dict, nullptr);
- starboard::ScopedFile read_file_again(
- watchdog_->GetWatchdogFilePath().c_str(), kSbFileOpenOnly | kSbFileRead);
- ASSERT_EQ(read_file_again.IsValid(), false);
+TEST_F(WatchdogTest, EvictOldWatchdogViolations) {
+ // Creates old Violation json file.
+ std::unique_ptr<base::Value> dummy_map =
+ std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
+ dummy_map->SetKey("test-name-old",
+ CreateDummyViolationDict("test-desc-old", 0, 1));
+ std::string json;
+ base::JSONWriter::Write(*dummy_map, &json);
+ starboard::ScopedFile file(watchdog_->GetWatchdogFilePath().c_str(),
+ kSbFileCreateAlways | kSbFileWrite);
+ TearDown();
+ file.WriteAll(json.c_str(), static_cast<int>(json.size()));
+ watchdog_ = new watchdog::Watchdog();
+ watchdog_->InitializeCustom(nullptr, std::string(kWatchdogViolationsJson),
+ kWatchdogMonitorFrequency);
+
+ ASSERT_NE(watchdog_->GetWatchdogViolations({}, false), "");
+ ASSERT_EQ(watchdog_->GetWatchdogViolations({"test-name-new"}), "");
+ ASSERT_EQ(watchdog_->GetWatchdogViolations({}, false), "");
}
} // namespace watchdog
diff --git a/cobalt/webdriver/testdata/simple_test.py b/cobalt/webdriver/testdata/simple_test.py
index 5b5a9a1..168436b 100755
--- a/cobalt/webdriver/testdata/simple_test.py
+++ b/cobalt/webdriver/testdata/simple_test.py
@@ -176,7 +176,7 @@
"""
request = ElementRequest(session_id, element_id, GET, 'screenshot')
if request:
- with open(filename, 'w', encoding='utf-8') as f:
+ with open(filename, 'wb') as f:
f.write(binascii.a2b_base64(request['value']))
f.close()
diff --git a/cobalt/worker/service_worker_context.cc b/cobalt/worker/service_worker_context.cc
index b4b4bac..ad96c62 100644
--- a/cobalt/worker/service_worker_context.cc
+++ b/cobalt/worker/service_worker_context.cc
@@ -163,15 +163,14 @@
ServiceWorkerContext::ServiceWorkerContext(
web::WebSettings* web_settings, network::NetworkModule* network_module,
- web::UserAgentPlatformInfo* platform_info, base::MessageLoop* message_loop,
- const GURL& url)
+ web::UserAgentPlatformInfo* platform_info, base::MessageLoop* message_loop)
: message_loop_(message_loop) {
DCHECK_EQ(message_loop_, base::MessageLoop::current());
jobs_ =
std::make_unique<ServiceWorkerJobs>(this, network_module, message_loop);
ServiceWorkerPersistentSettings::Options options(web_settings, network_module,
- platform_info, this, url);
+ platform_info, this);
scope_to_registration_map_.reset(new ServiceWorkerRegistrationMap(options));
DCHECK(scope_to_registration_map_);
}
diff --git a/cobalt/worker/service_worker_context.h b/cobalt/worker/service_worker_context.h
index e51882d..460200f 100644
--- a/cobalt/worker/service_worker_context.h
+++ b/cobalt/worker/service_worker_context.h
@@ -51,7 +51,7 @@
ServiceWorkerContext(web::WebSettings* web_settings,
network::NetworkModule* network_module,
web::UserAgentPlatformInfo* platform_info,
- base::MessageLoop* message_loop, const GURL& url);
+ base::MessageLoop* message_loop);
~ServiceWorkerContext();
base::MessageLoop* message_loop() { return message_loop_; }
diff --git a/cobalt/worker/service_worker_jobs.cc b/cobalt/worker/service_worker_jobs.cc
index fa0451b..ecbb126 100644
--- a/cobalt/worker/service_worker_jobs.cc
+++ b/cobalt/worker/service_worker_jobs.cc
@@ -852,7 +852,8 @@
ServiceWorkerObject* installing_worker = registration->installing_worker();
// 11. If the result of running the Should Skip Event algorithm with
// installingWorker and "install" is false, then:
- if (!installing_worker->ShouldSkipEvent(base::Tokens::install())) {
+ if (installing_worker &&
+ !installing_worker->ShouldSkipEvent(base::Tokens::install())) {
// 11.1. Let forceBypassCache be true if job’s force bypass cache flag is
// set, and false otherwise.
bool force_bypass_cache = job->force_bypass_cache_flag;
diff --git a/cobalt/worker/service_worker_persistent_settings.cc b/cobalt/worker/service_worker_persistent_settings.cc
index fc81821..368ba1a 100644
--- a/cobalt/worker/service_worker_persistent_settings.cc
+++ b/cobalt/worker/service_worker_persistent_settings.cc
@@ -119,12 +119,6 @@
url::Origin storage_key =
url::Origin::Create(GURL(dict[kSettingsStorageKeyKey]->GetString()));
- // Only add persisted workers to the registration_map
- // if their storage_key matches the origin of the initial_url.
- if (!storage_key.IsSameOriginWith(url::Origin::Create(options_.url))) {
- continue;
- }
-
if (!CheckPersistentValue(key_string, kSettingsScopeUrlKey, dict,
base::Value::Type::STRING))
continue;
diff --git a/cobalt/worker/service_worker_persistent_settings.h b/cobalt/worker/service_worker_persistent_settings.h
index f855895..55142a1 100644
--- a/cobalt/worker/service_worker_persistent_settings.h
+++ b/cobalt/worker/service_worker_persistent_settings.h
@@ -48,17 +48,15 @@
Options(web::WebSettings* web_settings,
network::NetworkModule* network_module,
web::UserAgentPlatformInfo* platform_info,
- ServiceWorkerContext* service_worker_context, const GURL& url)
+ ServiceWorkerContext* service_worker_context)
: web_settings(web_settings),
network_module(network_module),
platform_info(platform_info),
- service_worker_context(service_worker_context),
- url(url) {}
+ service_worker_context(service_worker_context) {}
web::WebSettings* web_settings;
network::NetworkModule* network_module;
web::UserAgentPlatformInfo* platform_info;
ServiceWorkerContext* service_worker_context;
- const GURL& url;
};
explicit ServiceWorkerPersistentSettings(const Options& options);
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index 53e47b4..e69be69 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -141,6 +141,11 @@
"system_memory_stats_recorder_win.cc",
"system_session_analyzer_win.cc",
"system_session_analyzer_win.h",
+
+ # Files with unused symbols. ex: DriveMetricsProvider::HasSeekPenalty
+ # These files give linker errors about undefined symbols during modualr builds.
+ "drive_metrics_provider.cc",
+ "drive_metrics_provider.h",
]
}
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn
index ad64baf..46eba0c 100644
--- a/components/variations/BUILD.gn
+++ b/components/variations/BUILD.gn
@@ -87,6 +87,17 @@
]
}
+ if (use_cobalt_customizations) {
+ # Files with unused symbols. ex: base::FeatureList::RegisterFieldTrialOverride
+ # These files give linker errors about undefined symbols during modualr builds.
+ sources -= [
+ "variations_seed_processor.cc",
+ "variations_seed_processor.h",
+ "variations_seed_simulator.cc",
+ "variations_seed_simulator.h",
+ ]
+ }
+
if (!use_cobalt_customizations && (is_android || is_ios)) {
sources += [
"variations_request_scheduler_mobile.cc",
@@ -128,7 +139,6 @@
}
}
-# TODO(b/283258321): Re-enable as many tests as posible.
if (!use_cobalt_customizations) {
static_library("test_support") {
testonly = true
diff --git a/net/BUILD.gn b/net/BUILD.gn
index e694523..2fb59e7 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -427,6 +427,7 @@
"disk_cache/cobalt/cobalt_disk_cache.cc",
"disk_cache/cobalt/cobalt_backend_impl.cc",
"disk_cache/cobalt/cobalt_backend_impl.h",
+ "disk_cache/cobalt/resource_type.cc",
"disk_cache/cobalt/resource_type.h",
"disk_cache/net_log_parameters.cc",
"disk_cache/net_log_parameters.h",
@@ -4217,7 +4218,6 @@
"//base:i18n",
"//base/test:test_support",
"//base/third_party/dynamic_annotations",
- "//cobalt/persistent_storage:persistent_settings",
"//crypto",
"//testing/gmock",
"//testing/gtest",
diff --git a/net/android/unittest_support/AndroidManifest.xml b/net/android/unittest_support/AndroidManifest.xml
index d4f4261..63a5fea 100644
--- a/net/android/unittest_support/AndroidManifest.xml
+++ b/net/android/unittest_support/AndroidManifest.xml
@@ -10,7 +10,7 @@
android:versionCode="1"
android:versionName="1.0">
- <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="33" />
+ <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="34" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
diff --git a/net/base/cache_type.h b/net/base/cache_type.h
index debae51..c5e72ef 100644
--- a/net/base/cache_type.h
+++ b/net/base/cache_type.h
@@ -24,9 +24,6 @@
CACHE_BACKEND_DEFAULT,
CACHE_BACKEND_BLOCKFILE, // The |BackendImpl|.
CACHE_BACKEND_SIMPLE, // The |SimpleBackendImpl|.
-#if defined(STARBOARD)
- CACHE_BACKEND_COBALT // The |CobaltBackendImpl|,
-#endif
};
} // namespace net
diff --git a/net/disk_cache/cobalt/cobalt_backend_impl.cc b/net/disk_cache/cobalt/cobalt_backend_impl.cc
index b60ab38..1bf9108 100644
--- a/net/disk_cache/cobalt/cobalt_backend_impl.cc
+++ b/net/disk_cache/cobalt/cobalt_backend_impl.cc
@@ -30,8 +30,6 @@
namespace {
-const char kPersistentSettingsJson[] = "cache_settings.json";
-
void CompletionOnceCallbackHandler(
scoped_refptr<CobaltBackendImpl::RefCountedRunner> runner,
int result) {
@@ -53,36 +51,23 @@
return kOther;
}
-void ReadDiskCacheSize(
- cobalt::persistent_storage::PersistentSettings* settings,
- int64_t max_bytes) {
- auto total_size = 0;
- disk_cache::ResourceTypeMetadata kTypeMetadataNew[disk_cache::kTypeCount];
-
- for (int i = 0; i < disk_cache::kTypeCount; i++) {
- auto metadata = disk_cache::kTypeMetadata[i];
- uint32_t bucket_size =
- static_cast<uint32_t>(settings->GetPersistentSettingAsDouble(
- metadata.directory, metadata.max_size_bytes));
- kTypeMetadataNew[i] = {metadata.directory, bucket_size};
-
- total_size += bucket_size;
+bool NeedsBackend(ResourceType resource_type) {
+ switch (resource_type) {
+ case kHTML:
+ case kCSS:
+ case kImage:
+ case kFont:
+ case kUncompiledScript:
+ case kOther:
+ case kSplashScreen:
+ return true;
+ case kCompiledScript:
+ case kCacheApi:
+ case kServiceWorkerScript:
+ default:
+ return false;
}
-
- // Check if PersistentSettings values are valid and can replace the disk_cache::kTypeMetadata.
- if (total_size <= max_bytes) {
- std::copy(std::begin(kTypeMetadataNew), std::end(kTypeMetadataNew), std::begin(disk_cache::kTypeMetadata));
- return;
- }
-
- // PersistentSettings values are invalid and will be replaced by the default values in
- // disk_cache::kTypeMetadata.
- for (int i = 0; i < disk_cache::kTypeCount; i++) {
- auto metadata = disk_cache::kTypeMetadata[i];
- settings->SetPersistentSetting(
- metadata.directory,
- std::make_unique<base::Value>(static_cast<double>(metadata.max_size_bytes)));
- }
+ return false;
}
} // namespace
@@ -94,57 +79,43 @@
net::CacheType cache_type,
net::NetLog* net_log)
: weak_factory_(this) {
- persistent_settings_ =
- std::make_unique<cobalt::persistent_storage::PersistentSettings>(
- kPersistentSettingsJson);
- ReadDiskCacheSize(persistent_settings_.get(), max_bytes);
-
- // Initialize disk backend for each resource type.
int64_t total_size = 0;
for (int i = 0; i < kTypeCount; i++) {
- auto metadata = kTypeMetadata[i];
- base::FilePath dir = path.Append(FILE_PATH_LITERAL(metadata.directory));
- int64_t bucket_size = metadata.max_size_bytes;
+ ResourceType resource_type = (ResourceType)i;
+ if (!NeedsBackend(resource_type)) {
+ continue;
+ }
+ std::string sub_directory = defaults::GetSubdirectory(resource_type);
+ base::FilePath dir = path.Append(FILE_PATH_LITERAL(sub_directory));
+ uint32_t bucket_size = disk_cache::settings::GetQuota(resource_type);
total_size += bucket_size;
SimpleBackendImpl* simple_backend = new SimpleBackendImpl(
- dir, cleanup_tracker, /* file_tracker = */ nullptr, bucket_size,
+ dir, cleanup_tracker, /*file_tracker=*/nullptr, bucket_size,
cache_type, net_log);
- simple_backend_map_[(ResourceType)i] = simple_backend;
+ simple_backend_map_[resource_type] = simple_backend;
}
-
// Must be at least enough space for each backend.
DCHECK(total_size <= max_bytes);
}
CobaltBackendImpl::~CobaltBackendImpl() {
for (int i = 0; i < kTypeCount; i++) {
- delete simple_backend_map_[(ResourceType)i];
+ ResourceType resource_type = (ResourceType)i;
+ if (simple_backend_map_.count(resource_type) == 1) {
+ delete simple_backend_map_[resource_type];
+ }
}
simple_backend_map_.clear();
}
void CobaltBackendImpl::UpdateSizes(ResourceType type, uint32_t bytes) {
- if (bytes == disk_cache::kTypeMetadata[type].max_size_bytes)
+ if (simple_backend_map_.count(type) == 0) {
return;
-
- // Static cast value to double since base::Value cannot be a long.
- persistent_settings_->SetPersistentSetting(
- disk_cache::kTypeMetadata[type].directory,
- std::make_unique<base::Value>(static_cast<double>(bytes)));
-
- disk_cache::kTypeMetadata[type].max_size_bytes = bytes;
+ }
SimpleBackendImpl* simple_backend = simple_backend_map_[type];
simple_backend->SetMaxSize(bytes);
}
-uint32_t CobaltBackendImpl::GetQuota(ResourceType type) {
- return disk_cache::kTypeMetadata[type].max_size_bytes;
-}
-
-void CobaltBackendImpl::ValidatePersistentSettings() {
- persistent_settings_->ValidatePersistentSettings();
-}
-
net::Error CobaltBackendImpl::Init(CompletionOnceCallback completion_callback) {
auto closure_runner =
base::MakeRefCounted<RefCountedRunner>(std::move(completion_callback));
@@ -173,6 +144,9 @@
net::RequestPriority request_priority,
Entry** entry,
CompletionOnceCallback callback) {
+ if (simple_backend_map_.count(GetType(key)) == 0) {
+ return net::Error::ERR_BLOCKED_BY_CLIENT;
+ }
SimpleBackendImpl* simple_backend = simple_backend_map_[GetType(key)];
return simple_backend->OpenEntry(key, request_priority, entry,
std::move(callback));
@@ -183,11 +157,11 @@
Entry** entry,
CompletionOnceCallback callback) {
ResourceType type = GetType(key);
- auto quota = disk_cache::kTypeMetadata[type].max_size_bytes;
- if (quota == 0) {
+ auto quota = disk_cache::settings::GetQuota(type);
+ if (quota == 0 || simple_backend_map_.count(type) == 0) {
return net::Error::ERR_BLOCKED_BY_CLIENT;
}
- SimpleBackendImpl* simple_backend = simple_backend_map_[GetType(key)];
+ SimpleBackendImpl* simple_backend = simple_backend_map_[type];
return simple_backend->CreateEntry(key, request_priority, entry,
std::move(callback));
}
@@ -195,6 +169,9 @@
net::Error CobaltBackendImpl::DoomEntry(const std::string& key,
net::RequestPriority priority,
CompletionOnceCallback callback) {
+ if (simple_backend_map_.count(GetType(key)) == 0) {
+ return net::Error::ERR_BLOCKED_BY_CLIENT;
+ }
SimpleBackendImpl* simple_backend = simple_backend_map_[GetType(key)];
return simple_backend->DoomEntry(key, priority, std::move(callback));
}
@@ -273,6 +250,9 @@
}
void CobaltBackendImpl::OnExternalCacheHit(const std::string& key) {
+ if (simple_backend_map_.count(GetType(key)) == 0) {
+ return;
+ }
SimpleBackendImpl* simple_backend = simple_backend_map_[GetType(key)];
simple_backend->OnExternalCacheHit(key);
}
@@ -291,6 +271,10 @@
net::Error CobaltBackendImpl::DoomAllEntriesOfType(disk_cache::ResourceType type,
CompletionOnceCallback callback) {
+ if (simple_backend_map_.count(type) == 0) {
+ std::move(callback).Run(net::OK);
+ return net::OK;
+ }
SimpleBackendImpl* simple_backend = simple_backend_map_[type];
return simple_backend->DoomAllEntries(std::move(callback));
}
diff --git a/net/disk_cache/cobalt/cobalt_backend_impl.h b/net/disk_cache/cobalt/cobalt_backend_impl.h
index b3b1b54..b1bfe31 100644
--- a/net/disk_cache/cobalt/cobalt_backend_impl.h
+++ b/net/disk_cache/cobalt/cobalt_backend_impl.h
@@ -23,7 +23,6 @@
#include <utility>
#include "base/callback_helpers.h"
-#include "cobalt/persistent_storage/persistent_settings.h"
#include "net/base/completion_once_callback.h"
#include "net/disk_cache/cobalt/resource_type.h"
#include "net/disk_cache/disk_cache.h"
@@ -50,8 +49,6 @@
net::Error Init(CompletionOnceCallback completion_callback);
void UpdateSizes(ResourceType type, uint32_t bytes);
- uint32_t GetQuota(ResourceType type);
- void ValidatePersistentSettings();
// Backend interface.
net::CacheType GetCacheType() const override;
@@ -117,10 +114,6 @@
base::WeakPtrFactory<CobaltBackendImpl> weak_factory_;
std::map<ResourceType, SimpleBackendImpl*> simple_backend_map_;
-
- // Json PrefStore used for persistent settings.
- std::unique_ptr<cobalt::persistent_storage::PersistentSettings>
- persistent_settings_;
};
} // namespace disk_cache
diff --git a/net/disk_cache/cobalt/resource_type.cc b/net/disk_cache/cobalt/resource_type.cc
new file mode 100644
index 0000000..6a3f2f3
--- /dev/null
+++ b/net/disk_cache/cobalt/resource_type.cc
@@ -0,0 +1,175 @@
+// 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 "net/disk_cache/cobalt/resource_type.h"
+
+#include "base/logging.h"
+
+namespace disk_cache {
+namespace defaults {
+
+std::string GetSubdirectory(ResourceType resource_type) {
+ switch (resource_type) {
+ case kOther:
+ return "other";
+ case kHTML:
+ return "html";
+ case kCSS:
+ return "css";
+ case kImage:
+ return "image";
+ case kFont:
+ return "font";
+ case kSplashScreen:
+ return "splash";
+ case kUncompiledScript:
+ return "uncompiled_js";
+ case kCompiledScript:
+ return "compiled_js";
+ case kCacheApi:
+ return "cache_api";
+ case kServiceWorkerScript:
+ return "service_worker_js";
+ default:
+ NOTREACHED() << "Unexpected resource_type " << resource_type;
+ break;
+ }
+ return "";
+}
+
+uint32_t GetQuota(ResourceType resource_type) {
+ switch (resource_type) {
+ case kOther:
+ return 3 * 1024 * 1024;
+ case kHTML:
+ return 2 * 1024 * 1024;
+ case kCSS:
+ return 1 * 1024 * 1024;
+ case kImage:
+ return 0;
+ case kFont:
+ return 3 * 1024 * 1024;
+ case kSplashScreen:
+ return 2 * 1024 * 1024;
+ case kUncompiledScript:
+ return 3 * 1024 * 1024;
+ case kCompiledScript:
+ return 3 * 1024 * 1024;
+ case kCacheApi:
+ return 3 * 1024 * 1024;
+ case kServiceWorkerScript:
+ return 3 * 1024 * 1024;
+ default:
+ NOTREACHED() << "Unexpected resource_type " << resource_type;
+ break;
+ }
+ return 0;
+}
+
+} // namespace defaults
+
+namespace settings {
+
+namespace {
+
+starboard::atomic_int32_t other_quota = starboard::atomic_int32_t(defaults::GetQuota(kOther));
+starboard::atomic_int32_t html_quota = starboard::atomic_int32_t(defaults::GetQuota(kHTML));
+starboard::atomic_int32_t css_quota = starboard::atomic_int32_t(defaults::GetQuota(kCSS));
+starboard::atomic_int32_t image_quota = starboard::atomic_int32_t(defaults::GetQuota(kImage));
+starboard::atomic_int32_t font_quota = starboard::atomic_int32_t(defaults::GetQuota(kFont));
+starboard::atomic_int32_t splash_screen_quota = starboard::atomic_int32_t(defaults::GetQuota(kSplashScreen));
+starboard::atomic_int32_t uncompiled_script_quota = starboard::atomic_int32_t(defaults::GetQuota(kUncompiledScript));
+starboard::atomic_int32_t compiled_script_quota = starboard::atomic_int32_t(defaults::GetQuota(kCompiledScript));
+starboard::atomic_int32_t cache_api_quota = starboard::atomic_int32_t(defaults::GetQuota(kCacheApi));
+starboard::atomic_int32_t service_worker_script_quota = starboard::atomic_int32_t(defaults::GetQuota(kServiceWorkerScript));
+starboard::atomic_bool cache_enabled = starboard::atomic_bool(true);
+
+} // namespace
+
+uint32_t GetQuota(ResourceType resource_type) {
+ switch (resource_type) {
+ case kOther:
+ return other_quota.load();
+ case kHTML:
+ return html_quota.load();
+ case kCSS:
+ return css_quota.load();
+ case kImage:
+ return image_quota.load();
+ case kFont:
+ return font_quota.load();
+ case kSplashScreen:
+ return splash_screen_quota.load();
+ case kUncompiledScript:
+ return uncompiled_script_quota.load();
+ case kCompiledScript:
+ return compiled_script_quota.load();
+ case kCacheApi:
+ return cache_api_quota.load();
+ case kServiceWorkerScript:
+ return service_worker_script_quota.load();
+ default:
+ NOTREACHED() << "Unexpected resource_type " << resource_type;
+ }
+ return 0;
+}
+
+void SetQuota(ResourceType resource_type, uint32_t value) {
+ switch (resource_type) {
+ case kOther:
+ other_quota.store(static_cast<int32_t>(value));
+ break;
+ case kHTML:
+ html_quota.store(static_cast<int32_t>(value));
+ break;
+ case kCSS:
+ css_quota.store(static_cast<int32_t>(value));
+ break;
+ case kImage:
+ image_quota.store(static_cast<int32_t>(value));
+ break;
+ case kFont:
+ font_quota.store(static_cast<int32_t>(value));
+ break;
+ case kSplashScreen:
+ splash_screen_quota.store(static_cast<int32_t>(value));
+ break;
+ case kUncompiledScript:
+ uncompiled_script_quota.store(static_cast<int32_t>(value));
+ break;
+ case kCompiledScript:
+ compiled_script_quota.store(static_cast<int32_t>(value));
+ break;
+ case kCacheApi:
+ cache_api_quota.store(static_cast<int32_t>(value));
+ break;
+ case kServiceWorkerScript:
+ service_worker_script_quota.store(static_cast<int32_t>(value));
+ break;
+ default:
+ NOTREACHED() << "Unexpected resource_type " << resource_type;
+ break;
+ }
+}
+
+bool GetCacheEnabled() {
+ return cache_enabled.load();
+}
+
+void SetCacheEnabled(bool value) {
+ cache_enabled.store(value);
+}
+
+} // namespace settings
+} // namespace disk_cache
diff --git a/net/disk_cache/cobalt/resource_type.h b/net/disk_cache/cobalt/resource_type.h
index f291033..f401b65 100644
--- a/net/disk_cache/cobalt/resource_type.h
+++ b/net/disk_cache/cobalt/resource_type.h
@@ -17,6 +17,9 @@
#include <string>
+#include "starboard/common/atomic.h"
+#include "starboard/types.h"
+
namespace disk_cache {
/* Note: If adding a new resource type, add corresponding metadata below. */
@@ -34,22 +37,21 @@
kTypeCount = 10,
};
-struct ResourceTypeMetadata {
- std::string directory;
- uint32_t max_size_bytes;
-};
+namespace defaults {
-static uint32_t kInitialBytes = static_cast<uint32_t> (3 * 1024 * 1024);
-// These values are updated on start up in application.cc, using the
-// persisted values saved in settings.json.
-static ResourceTypeMetadata kTypeMetadata[] = {
- {"other", kInitialBytes}, {"html", 2 * 1024 * 1024},
- {"css", 1 * 1024 * 1024}, {"image", 0},
- {"font", kInitialBytes}, {"splash", 2 * 1024 * 1024},
- {"uncompiled_js", kInitialBytes}, {"compiled_js", kInitialBytes},
- {"cache_api", kInitialBytes}, {"service_worker_js", kInitialBytes},
-};
+std::string GetSubdirectory(ResourceType resource_type);
+uint32_t GetQuota(ResourceType resource_type);
+} // namespace defaults
+
+namespace settings {
+
+uint32_t GetQuota(ResourceType resource_type);
+void SetQuota(ResourceType resource_type, uint32_t value);
+bool GetCacheEnabled();
+void SetCacheEnabled(bool value);
+
+} // namespace settings
} // namespace disk_cache
#endif // NET_DISK_CACHE_COBALT_RESOURCE_TYPE_H_
diff --git a/net/disk_cache/disk_cache.cc b/net/disk_cache/disk_cache.cc
index b8abfac..81457ca 100644
--- a/net/disk_cache/disk_cache.cc
+++ b/net/disk_cache/disk_cache.cc
@@ -269,7 +269,8 @@
#if defined(OS_ANDROID)
nullptr,
#endif
- net_log, backend, base::OnceClosure(),
+ net_log, backend,
+ base::OnceClosure(),
std::move(callback));
}
@@ -286,7 +287,8 @@
base::android::ApplicationStatusListener* app_status_listener) {
return CreateCacheBackendImpl(type, backend_type, path, max_bytes, force,
std::move(app_status_listener), net_log,
- backend, base::OnceClosure(),
+ backend,
+ base::OnceClosure(),
std::move(callback));
}
#endif
diff --git a/net/test/android/javatests/AndroidManifest.xml b/net/test/android/javatests/AndroidManifest.xml
index af051a4..9bccb8e 100644
--- a/net/test/android/javatests/AndroidManifest.xml
+++ b/net/test/android/javatests/AndroidManifest.xml
@@ -8,7 +8,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="org.chromium.net.test.support">
- <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="33" />
+ <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="34" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
diff --git a/starboard/BUILD.gn b/starboard/BUILD.gn
index 3ab468c..aab526b 100644
--- a/starboard/BUILD.gn
+++ b/starboard/BUILD.gn
@@ -51,8 +51,13 @@
if (sb_filter_based_player) {
deps += [
"//starboard/shared/starboard/player/filter/testing:player_filter_tests($starboard_toolchain)",
- "//starboard/shared/starboard/player/filter/tools:audio_dmp_player($starboard_toolchain)",
+ "//starboard/shared/starboard/player/filter/testing:player_filter_tests_install($starboard_toolchain)",
]
+
+ # TODO: b/296715826 - Fix build error for windows modular builds.
+ if (!(sb_is_modular && is_host_win)) {
+ deps += [ "//starboard/shared/starboard/player/filter/tools:audio_dmp_player($starboard_toolchain)" ]
+ }
}
if (sb_enable_benchmark) {
@@ -71,6 +76,7 @@
data_deps = [
"//starboard/loader_app($starboard_toolchain)",
"//third_party/crashpad/handler:crashpad_handler(//$starboard_path/toolchain:native_target)",
+ "//third_party/crashpad/tools:crashpad_database_util(//$starboard_path/toolchain:native_target)",
]
}
}
@@ -109,7 +115,7 @@
}
} else {
public_deps += [
- ":starboard_platform_group($starboard_toolchain)",
+ ":starboard_platform_group_static($starboard_toolchain)",
"//starboard/common",
]
@@ -184,6 +190,10 @@
starboard_platform_target("starboard_platform_group") {
}
+ starboard_platform_target("starboard_platform_group_static") {
+ target_type = "group"
+ }
+
if (platform_tests_path == "") {
# If 'starboard_platform_tests' is not defined by the platform, then an
# empty 'starboard_platform_tests' target is defined.
diff --git a/starboard/android/apk/apk_sources.gni b/starboard/android/apk/apk_sources.gni
index d1f668c..4564202 100644
--- a/starboard/android/apk/apk_sources.gni
+++ b/starboard/android/apk/apk_sources.gni
@@ -47,6 +47,7 @@
"//starboard/android/apk/app/src/main/java/dev/cobalt/media/CobaltMediaSession.java",
"//starboard/android/apk/app/src/main/java/dev/cobalt/media/Log.java",
"//starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java",
+ "//starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecCapabilitiesLogger.java",
"//starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java",
"//starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java",
"//starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaImage.java",
diff --git a/starboard/android/apk/app/build.gradle b/starboard/android/apk/app/build.gradle
index b64b5d3..a50b50b 100644
--- a/starboard/android/apk/app/build.gradle
+++ b/starboard/android/apk/app/build.gradle
@@ -75,7 +75,7 @@
defaultConfig {
applicationId "dev.cobalt.coat"
minSdkVersion 24
- targetSdkVersion 31
+ targetSdkVersion 34
versionCode 1
versionName "${buildId}"
manifestPlaceholders = [
diff --git a/starboard/android/apk/app/src/app/AndroidManifest.xml b/starboard/android/apk/app/src/app/AndroidManifest.xml
index 6b2602d..0c23b47 100644
--- a/starboard/android/apk/app/src/app/AndroidManifest.xml
+++ b/starboard/android/apk/app/src/app/AndroidManifest.xml
@@ -31,6 +31,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<!-- This is needed when targeting API 28+ to use foreground services -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<!-- https://iabtechlab.com/OTT-IFA, AdvertisingIdClient.Info.getId() -->
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
index d942102..129b3c1 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
@@ -32,7 +32,7 @@
import androidx.annotation.CallSuper;
import com.google.androidgamesdk.GameActivity;
import dev.cobalt.media.AudioOutputManager;
-import dev.cobalt.media.MediaCodecUtil;
+import dev.cobalt.media.MediaCodecCapabilitiesLogger;
import dev.cobalt.media.VideoSurfaceView;
import dev.cobalt.util.DisplayUtil;
import dev.cobalt.util.Log;
@@ -146,7 +146,7 @@
protected void onStart() {
if (!isReleaseBuild()) {
getStarboardBridge().getAudioOutputManager().dumpAllOutputDevices();
- MediaCodecUtil.dumpAllDecoders();
+ MediaCodecCapabilitiesLogger.dumpAllDecoders();
}
if (forceCreateNewVideoSurfaceView) {
Log.w(TAG, "Force to create a new video surface.");
@@ -159,8 +159,6 @@
getStarboardBridge().onActivityStart(this, keyboardEditor);
super.onStart();
-
- nativeInitializeMediaCapabilitiesInBackground();
}
@Override
@@ -396,6 +394,4 @@
public long getAppStartTimestamp() {
return timeInNanoseconds;
}
-
- private static native void nativeInitializeMediaCapabilitiesInBackground();
}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java
index e24faf6..8f7742b 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/MediaPlaybackService.java
@@ -22,7 +22,7 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
-import android.os.Build;
+import android.content.pm.ServiceInfo;
import android.os.Build.VERSION;
import android.os.IBinder;
import android.os.RemoteException;
@@ -37,17 +37,19 @@
private static final int NOTIFICATION_ID = 193266736; // CL number for uniqueness.
private static final String NOTIFICATION_CHANNEL_ID = "dev.cobalt.coat media playback service";
private static final String NOTIFICATION_CHANNEL_NAME = "Media playback service";
+ private boolean channelCreated = true;
+ private NotificationManager notificationManager = null;
@Override
public void onCreate() {
- if (getStarboardBridge() == null) {
- Log.e(TAG, "StarboardBridge already destroyed.");
- return;
- }
Log.i(TAG, "Creating a Media playback foreground service.");
super.onCreate();
- getStarboardBridge().onServiceStart(this);
- createNotificationChannel();
+ if (getStarboardBridge() != null) {
+ getStarboardBridge().onServiceStart(this);
+ }
+ this.notificationManager =
+ (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
+ this.channelCreated = createNotificationChannel();
}
@Override
@@ -66,64 +68,79 @@
@Override
public void onDestroy() {
- if (getStarboardBridge() == null) {
- Log.e(TAG, "StarboardBridge already destroyed.");
- return;
- }
Log.i(TAG, "Destroying the Media playback service.");
- getStarboardBridge().onServiceDestroy(this);
+
+ if (VERSION.SDK_INT >= 26 && this.channelCreated) {
+ this.notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
+ }
+
+ if (getStarboardBridge() != null) {
+ getStarboardBridge().onServiceDestroy(this);
+ }
super.onDestroy();
}
public void startService() {
- try {
- startForeground(NOTIFICATION_ID, buildNotification());
- } catch (IllegalStateException e) {
- Log.e(TAG, "Failed to start Foreground Service", e);
+ if (this.channelCreated) {
+ try {
+ if (VERSION.SDK_INT >= 29) {
+ startForeground(
+ NOTIFICATION_ID, buildNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST);
+ } else {
+ startForeground(NOTIFICATION_ID, buildNotification());
+ }
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to start Foreground Service", e);
+ }
}
}
public void stopService() {
// Let service itself handle notification deletion.
- stopForeground(true);
+ if (this.channelCreated) {
+ stopForeground(true);
+ }
stopSelf();
}
- private void createNotificationChannel() {
- if (Build.VERSION.SDK_INT >= 26) {
+ private boolean createNotificationChannel() {
+ if (VERSION.SDK_INT >= 26) {
try {
createNotificationChannelInternalV26();
} catch (RemoteException e) {
Log.e(TAG, "Failed to create Notification Channel.", e);
+ return false;
}
}
+ return true;
}
@RequiresApi(26)
private void createNotificationChannelInternalV26() throws RemoteException {
- NotificationManager notificationManager =
- (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel =
new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME,
notificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("Channel for showing persistent notification");
- notificationManager.createNotificationChannel(channel);
+ this.notificationManager.createNotificationChannel(channel);
}
Notification buildNotification() {
+ String channelId = "";
+ if (VERSION.SDK_INT >= 26) {
+ // Channel with ID=NOTIFICATION_CHANNEL_ID is created for version >= 26
+ channelId = NOTIFICATION_CHANNEL_ID;
+ }
+
NotificationCompat.Builder builder =
- new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
+ new NotificationCompat.Builder(this, channelId)
.setShowWhen(false)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setSmallIcon(android.R.drawable.stat_sys_warning)
.setContentTitle("Media playback service")
.setContentText("Media playback service is running");
- if (VERSION.SDK_INT >= 26) {
- builder.setChannelId(NOTIFICATION_CHANNEL_ID);
- }
return builder.build();
}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index 61f4bca..5d6e8f9 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -96,7 +96,7 @@
private final Holder<Activity> activityHolder;
private final Holder<Service> serviceHolder;
private final String[] args;
- private final String startDeepLink;
+ private String startDeepLink;
private final Runnable stopRequester =
new Runnable() {
@Override
@@ -105,7 +105,8 @@
}
};
- private volatile boolean starboardStopped = false;
+ private volatile boolean starboardApplicationStopped = false;
+ private volatile boolean starboardApplicationReady = false;
private final HashMap<String, CobaltService.Factory> cobaltServiceFactories = new HashMap<>();
private final HashMap<String, CobaltService> cobaltServices = new HashMap<>();
@@ -166,7 +167,7 @@
}
protected void onActivityDestroy(Activity activity) {
- if (starboardStopped) {
+ if (starboardApplicationStopped) {
// We can't restart the starboard app, so kill the process for a clean start next time.
Log.i(TAG, "Activity destroyed after shutdown; killing app.");
System.exit(0);
@@ -265,7 +266,7 @@
@SuppressWarnings("unused")
@UsedByNative
protected void afterStopped() {
- starboardStopped = true;
+ starboardApplicationStopped = true;
ttsHelper.shutdown();
userAuthorizer.shutdown();
for (CobaltService service : cobaltServices.values()) {
@@ -285,8 +286,21 @@
@SuppressWarnings("unused")
@UsedByNative
+ protected void starboardApplicationStarted() {
+ starboardApplicationReady = true;
+ }
+
+ @SuppressWarnings("unused")
+ @UsedByNative
+ protected void starboardApplicationStopping() {
+ starboardApplicationReady = false;
+ starboardApplicationStopped = true;
+ }
+
+ @SuppressWarnings("unused")
+ @UsedByNative
public void requestStop(int errorLevel) {
- if (!starboardStopped) {
+ if (starboardApplicationReady) {
Log.i(TAG, "Request to stop");
nativeStopApp(errorLevel);
}
@@ -305,7 +319,10 @@
}
public boolean onSearchRequested() {
- return nativeOnSearchRequested();
+ if (starboardApplicationReady) {
+ return nativeOnSearchRequested();
+ }
+ return false;
}
private native boolean nativeOnSearchRequested();
@@ -349,7 +366,13 @@
/** Sends an event to the web app to navigate to the given URL */
public void handleDeepLink(String url) {
- nativeHandleDeepLink(url);
+ if (starboardApplicationReady) {
+ nativeHandleDeepLink(url);
+ } else {
+ // If this deep link event is received before the starboard application
+ // is ready, it replaces the start deep link.
+ startDeepLink = url;
+ }
}
private native void nativeHandleDeepLink(String url);
@@ -760,6 +783,10 @@
return service;
}
+ public CobaltService getOpenedCobaltService(String serviceName) {
+ return cobaltServices.get(serviceName);
+ }
+
@SuppressWarnings("unused")
@UsedByNative
void closeCobaltService(String serviceName) {
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
index 9fb274f..0e02ebc 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioOutputManager.java
@@ -142,39 +142,6 @@
audioTrackBridgeList.remove(audioTrackBridge);
}
- /** Returns the maximum number of HDMI channels. */
- @SuppressWarnings("unused")
- @UsedByNative
- int getMaxChannels() {
- // The aac audio decoder on this platform will switch its output from 5.1
- // to stereo right before providing the first output buffer when
- // attempting to decode 5.1 input. Since this heavily violates invariants
- // of the shared starboard player framework, disable 5.1 on this platform.
- // It is expected that we will be able to resolve this issue with Xiaomi
- // by Android P, so only do this workaround for SDK versions < 27.
- if (android.os.Build.MODEL.equals("MIBOX3") && android.os.Build.VERSION.SDK_INT < 27) {
- return 2;
- }
-
- AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- AudioDeviceInfo[] deviceInfos = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
- int maxChannels = 2;
- for (AudioDeviceInfo info : deviceInfos) {
- int type = info.getType();
- if (type == AudioDeviceInfo.TYPE_HDMI || type == AudioDeviceInfo.TYPE_HDMI_ARC) {
- int[] channelCounts = info.getChannelCounts();
- if (channelCounts.length == 0) {
- // An empty array indicates that the device supports arbitrary channel masks.
- return 8;
- }
- for (int count : channelCounts) {
- maxChannels = Math.max(maxChannels, count);
- }
- }
- }
- return maxChannels;
- }
-
/** Stores info from AudioDeviceInfo to be passed to the native app. */
@SuppressWarnings("unused")
@UsedByNative
@@ -208,8 +175,19 @@
outDeviceInfo.type = deviceInfos[index].getType();
outDeviceInfo.channels = 2;
+ // The aac audio decoder on this platform will switch its output from 5.1
+ // to stereo right before providing the first output buffer when
+ // attempting to decode 5.1 input. Since this heavily violates invariants
+ // of the shared starboard player framework, disable 5.1 on this platform.
+ // It is expected that we will be able to resolve this issue with Xiaomi
+ // by Android P, so only do this workaround for SDK versions < 27.
+ if (android.os.Build.MODEL.equals("MIBOX3") && android.os.Build.VERSION.SDK_INT < 27) {
+ return true;
+ }
+
int[] channelCounts = deviceInfos[index].getChannelCounts();
if (channelCounts.length == 0) {
+ // An empty array indicates that the device supports arbitrary channel masks.
outDeviceInfo.channels = 8;
} else {
for (int count : channelCounts) {
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 8db14f6..8164add 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
@@ -279,15 +279,15 @@
}
private boolean formatHasCropValues() {
- if (!mFormatHasCropValues.isPresent()) {
+ if (!mFormatHasCropValues.isPresent() && mFormat != null) {
boolean hasCropValues =
mFormat.containsKey(KEY_CROP_RIGHT)
&& mFormat.containsKey(KEY_CROP_LEFT)
&& mFormat.containsKey(KEY_CROP_BOTTOM)
&& mFormat.containsKey(KEY_CROP_TOP);
- mFormatHasCropValues = Optional.of(hasCropValues);
+ mFormatHasCropValues = Optional.ofNullable(hasCropValues);
}
- return mFormatHasCropValues.get();
+ return mFormatHasCropValues.orElse(false);
}
@SuppressWarnings("unused")
@@ -299,13 +299,17 @@
@SuppressWarnings("unused")
@UsedByNative
private int textureWidth() {
- return mFormat.getInteger(MediaFormat.KEY_WIDTH);
+ return (mFormat != null && mFormat.containsKey(MediaFormat.KEY_WIDTH))
+ ? mFormat.getInteger(MediaFormat.KEY_WIDTH)
+ : 0;
}
@SuppressWarnings("unused")
@UsedByNative
private int textureHeight() {
- return mFormat.getInteger(MediaFormat.KEY_HEIGHT);
+ return (mFormat != null && mFormat.containsKey(MediaFormat.KEY_HEIGHT))
+ ? mFormat.getInteger(MediaFormat.KEY_HEIGHT)
+ : 0;
}
@SuppressWarnings("unused")
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecCapabilitiesLogger.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecCapabilitiesLogger.java
new file mode 100644
index 0000000..f1c640d
--- /dev/null
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecCapabilitiesLogger.java
@@ -0,0 +1,285 @@
+// Copyright 2017 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.
+
+package dev.cobalt.media;
+
+import static dev.cobalt.media.Log.TAG;
+
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities;
+import android.media.MediaCodecList;
+import android.os.Build;
+import dev.cobalt.util.Log;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+/** Utility class to log MediaCodec capabilities. */
+public class MediaCodecCapabilitiesLogger {
+ /** Utility class to save the maximum supported resolution and frame rate of a decoder. */
+ static class ResolutionAndFrameRate {
+ public ResolutionAndFrameRate(Integer width, Integer height, Double frameRate) {
+ this.width = width;
+ this.height = height;
+ this.frameRate = frameRate;
+ }
+
+ public boolean isCovered(Integer width, Integer height, Double frameRate) {
+ return this.width >= width && this.height >= height && this.frameRate >= frameRate;
+ }
+
+ public Integer width = -1;
+ public Integer height = -1;
+ public Double frameRate = -1.0;
+ }
+
+ /** Returns a string detailing SDR and HDR capabilities of a decoder. */
+ private static String getSupportedResolutionsAndFrameRates(
+ VideoCapabilities videoCapabilities, boolean isHdrCapable) {
+ ArrayList<ArrayList<Integer>> resolutionList =
+ new ArrayList<>(
+ Arrays.asList(
+ new ArrayList<>(Arrays.asList(7680, 4320)),
+ new ArrayList<>(Arrays.asList(3840, 2160)),
+ new ArrayList<>(Arrays.asList(2560, 1440)),
+ new ArrayList<>(Arrays.asList(1920, 1080)),
+ new ArrayList<>(Arrays.asList(1280, 720))));
+ ArrayList<Double> frameRateList =
+ new ArrayList<>(Arrays.asList(60.0, 59.997, 50.0, 48.0, 30.0, 29.997, 25.0, 24.0, 23.997));
+ ArrayList<ResolutionAndFrameRate> supportedResolutionsAndFrameRates = new ArrayList<>();
+ for (Double frameRate : frameRateList) {
+ for (ArrayList<Integer> resolution : resolutionList) {
+ boolean isResolutionAndFrameRateCovered = false;
+ for (ResolutionAndFrameRate resolutionAndFrameRate : supportedResolutionsAndFrameRates) {
+ if (resolutionAndFrameRate.isCovered(resolution.get(0), resolution.get(1), frameRate)) {
+ isResolutionAndFrameRateCovered = true;
+ break;
+ }
+ }
+ if (videoCapabilities.areSizeAndRateSupported(
+ resolution.get(0), resolution.get(1), frameRate)) {
+ if (!isResolutionAndFrameRateCovered) {
+ supportedResolutionsAndFrameRates.add(
+ new ResolutionAndFrameRate(resolution.get(0), resolution.get(1), frameRate));
+ }
+ continue;
+ }
+ if (isResolutionAndFrameRateCovered) {
+ // This configuration should be covered by a supported configuration, return long form.
+ return getLongFormSupportedResolutionsAndFrameRates(
+ resolutionList, frameRateList, videoCapabilities, isHdrCapable);
+ }
+ }
+ }
+ return convertResolutionAndFrameRatesToString(supportedResolutionsAndFrameRates, isHdrCapable);
+ }
+
+ /**
+ * Like getSupportedResolutionsAndFrameRates(), but returns the full information for each frame
+ * rate and resolution combination.
+ */
+ private static String getLongFormSupportedResolutionsAndFrameRates(
+ ArrayList<ArrayList<Integer>> resolutionList,
+ ArrayList<Double> frameRateList,
+ VideoCapabilities videoCapabilities,
+ boolean isHdrCapable) {
+ ArrayList<ResolutionAndFrameRate> supported = new ArrayList<>();
+ for (Double frameRate : frameRateList) {
+ for (ArrayList<Integer> resolution : resolutionList) {
+ if (videoCapabilities.areSizeAndRateSupported(
+ resolution.get(0), resolution.get(1), frameRate)) {
+ supported.add(
+ new ResolutionAndFrameRate(resolution.get(0), resolution.get(1), frameRate));
+ }
+ }
+ }
+ return convertResolutionAndFrameRatesToString(supported, isHdrCapable);
+ }
+
+ /** Convert a list of ResolutionAndFrameRate to a human readable string. */
+ private static String convertResolutionAndFrameRatesToString(
+ ArrayList<ResolutionAndFrameRate> supported, boolean isHdrCapable) {
+ if (supported.isEmpty()) {
+ return "None.";
+ }
+ String frameRateAndResolutionString = "";
+ for (ResolutionAndFrameRate resolutionAndFrameRate : supported) {
+ frameRateAndResolutionString +=
+ String.format(
+ Locale.US,
+ "[%d x %d, %.3f fps], ",
+ resolutionAndFrameRate.width,
+ resolutionAndFrameRate.height,
+ resolutionAndFrameRate.frameRate);
+ }
+ frameRateAndResolutionString += isHdrCapable ? "hdr/sdr" : "sdr";
+ return frameRateAndResolutionString;
+ }
+
+ private interface CodecFeatureSupported {
+ boolean isSupported(String name, CodecCapabilities codecCapabilities);
+ }
+
+ static TreeMap<String, CodecFeatureSupported> featureMap;
+
+ private static void ensurefeatureMapInitialized() {
+ if (featureMap != null) {
+ return;
+ }
+ featureMap = new TreeMap<>();
+ featureMap.put(
+ "AdaptivePlayback",
+ (name, codecCapabilities) -> {
+ return codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback);
+ });
+ if (Build.VERSION.SDK_INT >= 29) {
+ featureMap.put(
+ "FrameParsing",
+ (name, codecCapabilities) -> {
+ return codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_FrameParsing);
+ });
+ }
+ if (Build.VERSION.SDK_INT >= 30) {
+ featureMap.put(
+ "LowLatency",
+ (name, codecCapabilities) -> {
+ return codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_LowLatency);
+ });
+ }
+ if (Build.VERSION.SDK_INT >= 29) {
+ featureMap.put(
+ "MultipleFrames",
+ (name, codecCapabilities) -> {
+ return codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_MultipleFrames);
+ });
+ }
+ if (Build.VERSION.SDK_INT >= 26) {
+ featureMap.put(
+ "PartialFrame",
+ (name, codecCapabilities) -> {
+ return codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_PartialFrame);
+ });
+ }
+ featureMap.put(
+ "SecurePlayback",
+ (name, codecCapabilities) -> {
+ return codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
+ });
+ featureMap.put(
+ "TunneledPlayback",
+ (name, codecCapabilities) -> {
+ return codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback);
+ });
+ }
+
+ private static String getAllFeatureNames() {
+ ensurefeatureMapInitialized();
+ return featureMap.keySet().toString();
+ }
+
+ private static String getSupportedFeaturesAsString(
+ String name, CodecCapabilities codecCapabilities) {
+ StringBuilder featuresAsString = new StringBuilder();
+
+ ensurefeatureMapInitialized();
+ for (Map.Entry<String, CodecFeatureSupported> entry : featureMap.entrySet()) {
+ if (entry.getValue().isSupported(name, codecCapabilities)) {
+ if (featuresAsString.length() > 0) {
+ featuresAsString.append(", ");
+ }
+ featuresAsString.append(entry.getKey());
+ }
+ }
+ return featuresAsString.toString();
+ }
+
+ /**
+ * Debug utility function that can be locally added to dump information about all decoders on a
+ * particular system.
+ */
+ public static void dumpAllDecoders() {
+ StringBuilder decoderDumpString = new StringBuilder();
+ for (MediaCodecInfo info : new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos()) {
+ if (info.isEncoder()) {
+ continue;
+ }
+ for (String supportedType : info.getSupportedTypes()) {
+ String name = info.getName();
+ decoderDumpString.append(
+ String.format(
+ Locale.US,
+ "name: %s (%s, %s):",
+ name,
+ supportedType,
+ MediaCodecUtil.isCodecDenyListed(name) ? "denylisted" : "not denylisted"));
+ CodecCapabilities codecCapabilities = info.getCapabilitiesForType(supportedType);
+ VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
+ String resultName =
+ (codecCapabilities.isFeatureSupported(
+ MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback)
+ && !name.endsWith(MediaCodecUtil.getSecureDecoderSuffix()))
+ ? (name + MediaCodecUtil.getSecureDecoderSuffix())
+ : name;
+ boolean isHdrCapable =
+ MediaCodecUtil.isHdrCapableVideoDecoder(
+ codecCapabilities.getMimeType(), codecCapabilities);
+ if (videoCapabilities != null) {
+ String frameRateAndResolutionString =
+ getSupportedResolutionsAndFrameRates(videoCapabilities, isHdrCapable);
+ decoderDumpString.append(
+ String.format(
+ Locale.US,
+ "\n\t"
+ + "widths: %s, "
+ + "heights: %s, "
+ + "bitrates: %s, "
+ + "framerates: %s, "
+ + "supported sizes and framerates: %s",
+ videoCapabilities.getSupportedWidths().toString(),
+ videoCapabilities.getSupportedHeights().toString(),
+ videoCapabilities.getBitrateRange().toString(),
+ videoCapabilities.getSupportedFrameRates().toString(),
+ frameRateAndResolutionString));
+ }
+ String featuresAsString = getSupportedFeaturesAsString(name, codecCapabilities);
+ if (featuresAsString.isEmpty()) {
+ decoderDumpString.append(" No extra features supported");
+ } else {
+ decoderDumpString.append("\n\tsupported features: ");
+ decoderDumpString.append(featuresAsString);
+ }
+ decoderDumpString.append("\n");
+ }
+ }
+ Log.v(
+ TAG,
+ "\n"
+ + "==================================================\n"
+ + "Full list of decoder features: "
+ + getAllFeatureNames()
+ + "\nUnsupported features for each codec are not listed\n"
+ + decoderDumpString.toString()
+ + "==================================================");
+ }
+}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
index 9afa2bc..50610e0 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecUtil.java
@@ -28,7 +28,6 @@
import dev.cobalt.util.Log;
import dev.cobalt.util.UsedByNative;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -442,7 +441,7 @@
// Filter blacklisted video decoders.
String name = codecInfo.getName();
- if (!isVp9AllowListed && videoCodecDenyList.contains(name)) {
+ if (!isVp9AllowListed && isCodecDenyListed(name)) {
Log.v(TAG, "Rejecting %s, reason: codec is on deny list", name);
continue;
}
@@ -451,7 +450,7 @@
// denylisted.
String nameWithoutSecureSuffix =
name.substring(0, name.length() - SECURE_DECODER_SUFFIX.length());
- if (!isVp9AllowListed && videoCodecDenyList.contains(nameWithoutSecureSuffix)) {
+ if (!isVp9AllowListed && isCodecDenyListed(nameWithoutSecureSuffix)) {
String format = "Rejecting %s, reason: offpsec denylisted secure decoder";
Log.v(TAG, format, name);
continue;
@@ -466,6 +465,16 @@
return codecCapabilityInfos.toArray(array);
}
+ /** Returns whether the codec is denylisted. */
+ public static boolean isCodecDenyListed(String codecName) {
+ return videoCodecDenyList.contains(codecName);
+ }
+
+ /** Simply returns SECURE_DECODER_SUFFIX to allow access to it. */
+ public static String getSecureDecoderSuffix() {
+ return SECURE_DECODER_SUFFIX;
+ }
+
/** Determine whether codecCapabilities is capable of playing HDR. */
public static boolean isHdrCapableVideoDecoder(
String mimeType, CodecCapabilities codecCapabilities) {
@@ -570,7 +579,7 @@
for (VideoDecoderCache.CachedDecoder decoder :
VideoDecoderCache.getCachedDecoders(mimeType, decoderCacheTtlMs)) {
String name = decoder.info.getName();
- if (!isVp9AllowListed && videoCodecDenyList.contains(name)) {
+ if (!isVp9AllowListed && isCodecDenyListed(name)) {
Log.v(TAG, "Rejecting " + name + ", reason: codec is on deny list");
continue;
}
@@ -596,7 +605,7 @@
// denylisted.
String nameWithoutSecureSuffix =
name.substring(0, name.length() - SECURE_DECODER_SUFFIX.length());
- if (!isVp9AllowListed && videoCodecDenyList.contains(nameWithoutSecureSuffix)) {
+ if (!isVp9AllowListed && isCodecDenyListed(nameWithoutSecureSuffix)) {
Log.v(TAG, "Rejecting " + name + ", reason: denylisted secure decoder");
}
}
@@ -738,8 +747,7 @@
* "" otherwise.
*/
@UsedByNative
- public static String findAudioDecoder(
- String mimeType, int bitrate, boolean mustSupportTunnelMode) {
+ public static String findAudioDecoder(String mimeType, int bitrate) {
// Note: MediaCodecList is sorted by the framework such that the best decoders come first.
for (MediaCodecInfo info : new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos()) {
if (info.isEncoder()) {
@@ -757,202 +765,9 @@
if (!bitrateRange.contains(bitrate)) {
continue;
}
- if (mustSupportTunnelMode
- && !codecCapabilities.isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback)) {
- continue;
- }
- // TODO: Determine if we can safely check if an audio codec requires the tunneled playback
- // feature. i.e., reject when |mustSupportTunnelMode| == false
- // and codecCapabilities.isFeatureRequired(CodecCapabilities.FEATURE_TunneledPlayback) ==
- // true.
return name;
}
}
return "";
}
-
- /** Utility class to save the maximum supported resolution and frame rate of a decoder. */
- static class ResolutionAndFrameRate {
- public ResolutionAndFrameRate(Integer width, Integer height, Double frameRate) {
- this.width = width;
- this.height = height;
- this.frameRate = frameRate;
- }
-
- public boolean isCovered(Integer width, Integer height, Double frameRate) {
- return this.width >= width && this.height >= height && this.frameRate >= frameRate;
- }
-
- public Integer width = -1;
- public Integer height = -1;
- public Double frameRate = -1.0;
- }
-
- /** Returns a string detailing SDR and HDR capabilities of a decoder. */
- public static String getSupportedResolutionsAndFrameRates(
- VideoCapabilities videoCapabilities, boolean isHdrCapable) {
- ArrayList<ArrayList<Integer>> resolutionList =
- new ArrayList<>(
- Arrays.asList(
- new ArrayList<>(Arrays.asList(7680, 4320)),
- new ArrayList<>(Arrays.asList(3840, 2160)),
- new ArrayList<>(Arrays.asList(2560, 1440)),
- new ArrayList<>(Arrays.asList(1920, 1080)),
- new ArrayList<>(Arrays.asList(1280, 720))));
- ArrayList<Double> frameRateList =
- new ArrayList<>(Arrays.asList(60.0, 59.997, 50.0, 48.0, 30.0, 29.997, 25.0, 24.0, 23.997));
- ArrayList<ResolutionAndFrameRate> supportedResolutionsAndFrameRates = new ArrayList<>();
- for (Double frameRate : frameRateList) {
- for (ArrayList<Integer> resolution : resolutionList) {
- boolean isResolutionAndFrameRateCovered = false;
- for (ResolutionAndFrameRate resolutionAndFrameRate : supportedResolutionsAndFrameRates) {
- if (resolutionAndFrameRate.isCovered(resolution.get(0), resolution.get(1), frameRate)) {
- isResolutionAndFrameRateCovered = true;
- break;
- }
- }
- if (videoCapabilities.areSizeAndRateSupported(
- resolution.get(0), resolution.get(1), frameRate)) {
- if (!isResolutionAndFrameRateCovered) {
- supportedResolutionsAndFrameRates.add(
- new ResolutionAndFrameRate(resolution.get(0), resolution.get(1), frameRate));
- }
- continue;
- }
- if (isResolutionAndFrameRateCovered) {
- // This configuration should be covered by a supported configuration, return long form.
- return getLongFormSupportedResolutionsAndFrameRates(
- resolutionList, frameRateList, videoCapabilities, isHdrCapable);
- }
- }
- }
- return convertResolutionAndFrameRatesToString(supportedResolutionsAndFrameRates, isHdrCapable);
- }
-
- /**
- * Like getSupportedResolutionsAndFrameRates(), but returns the full information for each frame
- * rate and resolution combination.
- */
- public static String getLongFormSupportedResolutionsAndFrameRates(
- ArrayList<ArrayList<Integer>> resolutionList,
- ArrayList<Double> frameRateList,
- VideoCapabilities videoCapabilities,
- boolean isHdrCapable) {
- ArrayList<ResolutionAndFrameRate> supported = new ArrayList<>();
- for (Double frameRate : frameRateList) {
- for (ArrayList<Integer> resolution : resolutionList) {
- if (videoCapabilities.areSizeAndRateSupported(
- resolution.get(0), resolution.get(1), frameRate)) {
- supported.add(
- new ResolutionAndFrameRate(resolution.get(0), resolution.get(1), frameRate));
- }
- }
- }
- return convertResolutionAndFrameRatesToString(supported, isHdrCapable);
- }
-
- public static String convertResolutionAndFrameRatesToString(
- ArrayList<ResolutionAndFrameRate> supported, boolean isHdrCapable) {
- if (supported.isEmpty()) {
- return "None. ";
- }
- String frameRateAndResolutionString = "";
- for (ResolutionAndFrameRate resolutionAndFrameRate : supported) {
- frameRateAndResolutionString +=
- String.format(
- Locale.US,
- "[%d x %d, %.3f fps], ",
- resolutionAndFrameRate.width,
- resolutionAndFrameRate.height,
- resolutionAndFrameRate.frameRate);
- }
- frameRateAndResolutionString += isHdrCapable ? "hdr/sdr, " : "sdr, ";
- return frameRateAndResolutionString;
- }
-
- /**
- * Debug utility function that can be locally added to dump information about all decoders on a
- * particular system.
- */
- public static void dumpAllDecoders() {
- String decoderDumpString = "";
- for (MediaCodecInfo info : new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos()) {
- if (info.isEncoder()) {
- continue;
- }
- for (String supportedType : info.getSupportedTypes()) {
- String name = info.getName();
- decoderDumpString +=
- String.format(
- Locale.US,
- "name: %s (%s, %s): ",
- name,
- supportedType,
- videoCodecDenyList.contains(name) ? "denylisted" : "not denylisted");
- CodecCapabilities codecCapabilities = info.getCapabilitiesForType(supportedType);
- VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
- String resultName =
- (codecCapabilities.isFeatureSupported(
- MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback)
- && !name.endsWith(SECURE_DECODER_SUFFIX))
- ? (name + SECURE_DECODER_SUFFIX)
- : name;
- boolean isHdrCapable =
- isHdrCapableVideoDecoder(codecCapabilities.getMimeType(), codecCapabilities);
- if (videoCapabilities != null) {
- String frameRateAndResolutionString =
- getSupportedResolutionsAndFrameRates(videoCapabilities, isHdrCapable);
- decoderDumpString +=
- String.format(
- Locale.US,
- "\n\t\t"
- + "widths: %s, "
- + "heights: %s, "
- + "bitrates: %s, "
- + "framerates: %s, "
- + "supported sizes and framerates: %s",
- videoCapabilities.getSupportedWidths().toString(),
- videoCapabilities.getSupportedHeights().toString(),
- videoCapabilities.getBitrateRange().toString(),
- videoCapabilities.getSupportedFrameRates().toString(),
- frameRateAndResolutionString);
- }
- boolean isAdaptivePlaybackSupported =
- codecCapabilities.isFeatureSupported(
- MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback);
- boolean isSecurePlaybackSupported =
- codecCapabilities.isFeatureSupported(
- MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback);
- boolean isTunneledPlaybackSupported =
- codecCapabilities.isFeatureSupported(
- MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback);
- if (isAdaptivePlaybackSupported
- || isSecurePlaybackSupported
- || isTunneledPlaybackSupported) {
- decoderDumpString +=
- String.format(
- Locale.US,
- "(%s%s%s",
- isAdaptivePlaybackSupported ? "AdaptivePlayback, " : "",
- isSecurePlaybackSupported ? "SecurePlayback, " : "",
- isTunneledPlaybackSupported ? "TunneledPlayback, " : "");
- // Remove trailing space and comma
- decoderDumpString = decoderDumpString.substring(0, decoderDumpString.length() - 2);
- decoderDumpString += ")";
- } else {
- decoderDumpString += " No extra features supported";
- }
- decoderDumpString += "\n";
- }
- }
- Log.v(
- TAG,
- " \n"
- + "==================================================\n"
- + "Full list of decoder features: [AdaptivePlayback, SecurePlayback,"
- + " TunneledPlayback]\n"
- + "Unsupported features for each codec are not listed\n"
- + decoderDumpString
- + "==================================================");
- }
}
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
index d4a12a5..190a160 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java
@@ -373,6 +373,11 @@
mSchemeUUID = schemeUUID;
mMediaDrm = new MediaDrm(schemeUUID);
+ // Get info of hdcp connection
+ if (Build.VERSION.SDK_INT >= 29) {
+ getConnectedHdcpLevelInfoV29(mMediaDrm);
+ }
+
mNativeMediaDrmBridge = nativeMediaDrmBridge;
if (!isNativeMediaDrmBridgeValid()) {
throw new IllegalArgumentException(
@@ -738,6 +743,40 @@
mediaDrm.close();
}
+ @RequiresApi(29)
+ private void getConnectedHdcpLevelInfoV29(MediaDrm mediaDrm) {
+ int hdcpLevel = mediaDrm.getConnectedHdcpLevel();
+ switch (hdcpLevel) {
+ case MediaDrm.HDCP_V1:
+ Log.i(TAG, "MediaDrm HDCP Level is HDCP_V1.");
+ break;
+ case MediaDrm.HDCP_V2:
+ Log.i(TAG, "MediaDrm HDCP Level is HDCP_V2.");
+ break;
+ case MediaDrm.HDCP_V2_1:
+ Log.i(TAG, "MediaDrm HDCP Level is HDCP_V2_1.");
+ break;
+ case MediaDrm.HDCP_V2_2:
+ Log.i(TAG, "MediaDrm HDCP Level is HDCP_V2_2.");
+ break;
+ case MediaDrm.HDCP_V2_3:
+ Log.i(TAG, "MediaDrm HDCP Level is HDCP_V2_3.");
+ break;
+ case MediaDrm.HDCP_NONE:
+ Log.i(TAG, "MediaDrm HDCP Level is HDCP_NONE.");
+ break;
+ case MediaDrm.HDCP_NO_DIGITAL_OUTPUT:
+ Log.i(TAG, "MediaDrm HDCP Level is HDCP_NO_DIGITAL_OUTPUT.");
+ break;
+ case MediaDrm.HDCP_LEVEL_UNKNOWN:
+ Log.i(TAG, "MediaDrm HDCP Level is HDCP_LEVEL_UNKNOWN.");
+ break;
+ default:
+ Log.i(TAG, String.format("Unknown MediaDrm HDCP level %d.", hdcpLevel));
+ break;
+ }
+ }
+
private boolean isNativeMediaDrmBridgeValid() {
return mNativeMediaDrmBridge != INVALID_NATIVE_MEDIA_DRM_BRIDGE;
}
diff --git a/starboard/android/shared/android_main.cc b/starboard/android/shared/android_main.cc
index dbdb8c0..7bccf50 100644
--- a/starboard/android/shared/android_main.cc
+++ b/starboard/android/shared/android_main.cc
@@ -17,6 +17,7 @@
#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/android/shared/jni_utils.h"
#include "starboard/android/shared/log_internal.h"
+#include "starboard/common/atomic.h"
#include "starboard/common/file.h"
#include "starboard/common/semaphore.h"
#include "starboard/common/string.h"
@@ -48,7 +49,7 @@
// Safeguard to avoid sending AndroidCommands either when there is no instance
// of the Starboard application, or after the run loop has exited and the
// ALooper receiving the commands is no longer being polled.
-bool g_app_running = false;
+atomic_bool g_app_running;
std::vector<std::string> GetArgs() {
std::vector<std::string> args;
@@ -235,7 +236,7 @@
// Mark the app running before signaling app created so there's no race to
// allow sending the first AndroidCommand after onCreate() returns.
- g_app_running = true;
+ g_app_running.store(true);
// Signal GameActivity_onCreate() that it may proceed.
g_app_created_semaphore->Put();
@@ -243,12 +244,12 @@
// Enter the Starboard run loop until stopped.
int error_level =
app.Run(std::move(command_line), GetStartDeepLink().c_str());
-#endif // SB_API_VERSION >= 15
// Mark the app not running before informing StarboardBridge that the app is
// stopped so that we won't send any more AndroidCommands as a result of
// shutting down the Activity.
- g_app_running = false;
+ g_app_running.store(false);
+#endif // SB_API_VERSION >= 15
// Our launcher.py looks for this to know when the app (test) is done.
SB_LOG(INFO) << "***Application Stopped*** " << error_level;
@@ -262,13 +263,13 @@
}
void OnStart(GameActivity* activity) {
- if (g_app_running) {
+ if (g_app_running.load()) {
ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kStart);
}
}
void OnResume(GameActivity* activity) {
- if (g_app_running) {
+ if (g_app_running.load()) {
// Stop the MediaPlaybackService if activity state transits from background
// to foreground. Note that the MediaPlaybackService may already have
// been stopped before Cobalt's lifecycle state transits from Concealed
@@ -279,7 +280,7 @@
}
void OnPause(GameActivity* activity) {
- if (g_app_running) {
+ if (g_app_running.load()) {
// Start the MediaPlaybackService before activity state transits from
// foreground to background.
ApplicationAndroid::Get()->StartMediaPlaybackService();
@@ -288,28 +289,28 @@
}
void OnStop(GameActivity* activity) {
- if (g_app_running) {
+ if (g_app_running.load()) {
ApplicationAndroid::Get()->SendAndroidCommand(AndroidCommand::kStop);
}
}
bool OnTouchEvent(GameActivity* activity,
const GameActivityMotionEvent* event) {
- if (g_app_running) {
+ if (g_app_running.load()) {
return ApplicationAndroid::Get()->SendAndroidMotionEvent(event);
}
return false;
}
bool OnKey(GameActivity* activity, const GameActivityKeyEvent* event) {
- if (g_app_running) {
+ if (g_app_running.load()) {
return ApplicationAndroid::Get()->SendAndroidKeyEvent(event);
}
return false;
}
void OnWindowFocusChanged(GameActivity* activity, bool focused) {
- if (g_app_running) {
+ if (g_app_running.load()) {
ApplicationAndroid::Get()->SendAndroidCommand(
focused ? AndroidCommand::kWindowFocusGained
: AndroidCommand::kWindowFocusLost);
@@ -317,14 +318,14 @@
}
void OnNativeWindowCreated(GameActivity* activity, ANativeWindow* window) {
- if (g_app_running) {
+ if (g_app_running.load()) {
ApplicationAndroid::Get()->SendAndroidCommand(
AndroidCommand::kNativeWindowCreated, window);
}
}
void OnNativeWindowDestroyed(GameActivity* activity, ANativeWindow* window) {
- if (g_app_running) {
+ if (g_app_running.load()) {
ApplicationAndroid::Get()->SendAndroidCommand(
AndroidCommand::kNativeWindowDestroyed);
}
@@ -380,7 +381,7 @@
Java_dev_cobalt_coat_VolumeStateReceiver_nativeVolumeChanged(JNIEnv* env,
jobject jcaller,
jint volumeDelta) {
- if (g_app_running) {
+ if (g_app_running.load()) {
SbKey key =
volumeDelta > 0 ? SbKey::kSbKeyVolumeUp : SbKey::kSbKeyVolumeDown;
ApplicationAndroid::Get()->SendKeyboardInject(key);
@@ -390,7 +391,7 @@
extern "C" SB_EXPORT_PLATFORM void
Java_dev_cobalt_coat_VolumeStateReceiver_nativeMuteChanged(JNIEnv* env,
jobject jcaller) {
- if (g_app_running) {
+ if (g_app_running.load()) {
ApplicationAndroid::Get()->SendKeyboardInject(SbKey::kSbKeyVolumeMute);
}
}
@@ -407,9 +408,13 @@
CommandLine command_line(GetArgs());
LogInit(command_line);
+#if SB_IS(EVERGREEN_COMPATIBLE)
+ InstallCrashpadHandler(command_line);
+#endif // SB_IS(EVERGREEN_COMPATIBLE)
+
// Mark the app running before signaling app created so there's no race to
// allow sending the first AndroidCommand after onCreate() returns.
- g_app_running = true;
+ g_app_running.store(true);
// Signal GameActivity_onCreate() that it may proceed.
g_app_created_semaphore->Put();
@@ -417,6 +422,12 @@
// Enter the Starboard run loop until stopped.
int error_level =
app.Run(std::move(command_line), GetStartDeepLink().c_str());
+
+ // Mark the app not running before informing StarboardBridge that the app is
+ // stopped so that we won't send any more AndroidCommands as a result of
+ // shutting down the Activity.
+ g_app_running.store(false);
+
return error_level;
}
#endif // SB_API_VERSION >= 15
diff --git a/starboard/android/shared/application_android.cc b/starboard/android/shared/application_android.cc
index 33b26ad..189517e 100644
--- a/starboard/android/shared/application_android.cc
+++ b/starboard/android/shared/application_android.cc
@@ -140,9 +140,15 @@
jobject local_ref = env->CallStarboardObjectMethodOrAbort(
"getResourceOverlay", "()Ldev/cobalt/coat/ResourceOverlay;");
resource_overlay_ = env->ConvertLocalRefToGlobalRef(local_ref);
+
+ env->CallStarboardVoidMethodOrAbort("starboardApplicationStarted", "()V");
}
ApplicationAndroid::~ApplicationAndroid() {
+ // Inform StarboardBridge that
+ JniEnvExt* env = JniEnvExt::Get();
+ env->CallStarboardVoidMethodOrAbort("starboardApplicationStopping", "()V");
+
// The application is exiting.
// Release the global reference.
if (resource_overlay_) {
@@ -161,6 +167,7 @@
{
// Signal for any potentially waiting window creation or destroy commands.
ScopedLock lock(android_command_mutex_);
+ application_destroying_.store(true);
android_command_condition_.Signal();
}
}
@@ -412,7 +419,7 @@
switch (type) {
case AndroidCommand::kNativeWindowCreated:
case AndroidCommand::kNativeWindowDestroyed:
- while (native_window_ != data) {
+ while ((native_window_ != data) && !application_destroying_.load()) {
android_command_condition_.Wait();
}
break;
diff --git a/starboard/android/shared/application_android.h b/starboard/android/shared/application_android.h
index e8917c1..bca3b1f 100644
--- a/starboard/android/shared/application_android.h
+++ b/starboard/android/shared/application_android.h
@@ -25,6 +25,7 @@
#include "starboard/android/shared/input_events_generator.h"
#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
#include "starboard/common/condition_variable.h"
#include "starboard/common/mutex.h"
#include "starboard/common/scoped_ptr.h"
@@ -154,6 +155,9 @@
// already requested it be stopped.
SbAtomic32 android_stop_count_ = 0;
+ // Set to true in the destructor to ensure other threads stop waiting.
+ atomic_bool application_destroying_;
+
// The last Activity lifecycle state command received.
AndroidCommand::CommandType activity_state_;
diff --git a/starboard/android/shared/audio_decoder.cc b/starboard/android/shared/audio_decoder.cc
index dfa32dd..60e7a44 100644
--- a/starboard/android/shared/audio_decoder.cc
+++ b/starboard/android/shared/audio_decoder.cc
@@ -94,9 +94,10 @@
output_cb_ = output_cb;
error_cb_ = error_cb;
-
- media_decoder_->Initialize(
- std::bind(&AudioDecoder::ReportError, this, _1, _2));
+ if (media_decoder_) {
+ media_decoder_->Initialize(
+ std::bind(&AudioDecoder::ReportError, this, _1, _2));
+ }
}
void AudioDecoder::Decode(const InputBuffers& input_buffers,
@@ -108,15 +109,20 @@
audio_frame_discarder_.OnInputBuffers(input_buffers);
+#if STARBOARD_ANDROID_SHARED_AUDIO_DECODER_VERBOSE
for (const auto& input_buffer : input_buffers) {
VERBOSE_MEDIA_LOG() << "T1: timestamp " << input_buffer->timestamp();
}
+#endif
- media_decoder_->WriteInputBuffers(input_buffers);
+ if (media_decoder_) {
+ media_decoder_->WriteInputBuffers(input_buffers);
+ }
ScopedLock lock(decoded_audios_mutex_);
- if (media_decoder_->GetNumberOfPendingTasks() + decoded_audios_.size() <=
- kMaxPendingWorkSize) {
+ if (media_decoder_ &&
+ (media_decoder_->GetNumberOfPendingTasks() + decoded_audios_.size() <=
+ kMaxPendingWorkSize)) {
Schedule(consumed_cb);
} else {
consumed_cb_ = consumed_cb;
@@ -128,7 +134,9 @@
SB_DCHECK(output_cb_);
SB_DCHECK(media_decoder_);
- media_decoder_->WriteEndOfStream();
+ if (media_decoder_) {
+ media_decoder_->WriteEndOfStream();
+ }
}
scoped_refptr<AudioDecoder::DecodedAudio> AudioDecoder::Read(
diff --git a/starboard/android/shared/audio_sink_min_required_frames_tester.cc b/starboard/android/shared/audio_sink_min_required_frames_tester.cc
index b8139a2..1d156b9 100644
--- a/starboard/android/shared/audio_sink_min_required_frames_tester.cc
+++ b/starboard/android/shared/audio_sink_min_required_frames_tester.cc
@@ -113,6 +113,7 @@
frame_buffers[0] = silence_buffer.data();
// Set default values.
+ has_error_ = false;
min_required_frames_ = task.default_required_frames;
total_consumed_frames_ = 0;
last_underrun_count_ = -1;
@@ -148,6 +149,13 @@
min_required_frames_ = max_required_frames_;
}
+ if (has_error_) {
+ SB_LOG(ERROR) << "There's an error while running the test. Fallback to "
+ "max required frames "
+ << max_required_frames_ << ".";
+ min_required_frames_ = max_required_frames_;
+ }
+
if (start_threshold > min_required_frames_) {
SB_LOG(INFO) << "Audio sink min required frames is overwritten from "
<< min_required_frames_ << " to audio track start threshold "
@@ -197,9 +205,10 @@
const std::string& error_message,
void* context) {
SB_LOG(ERROR) << "Error occurred while writing frames: " << error_message;
- // TODO: Handle errors during minimum frames test, maybe by terminating the
- // test earlier.
- SB_NOTREACHED();
+
+ MinRequiredFramesTester* tester =
+ static_cast<MinRequiredFramesTester*>(context);
+ tester->has_error_ = true;
}
void MinRequiredFramesTester::UpdateSourceStatus(int* frames_in_buffer,
diff --git a/starboard/android/shared/audio_sink_min_required_frames_tester.h b/starboard/android/shared/audio_sink_min_required_frames_tester.h
index 669217a..934bdb9 100644
--- a/starboard/android/shared/audio_sink_min_required_frames_tester.h
+++ b/starboard/android/shared/audio_sink_min_required_frames_tester.h
@@ -107,6 +107,7 @@
std::vector<const TestTask> test_tasks_;
AudioTrackAudioSink* audio_sink_ = nullptr;
int min_required_frames_;
+ std::atomic_bool has_error_;
// Used only by audio sink thread.
int total_consumed_frames_;
diff --git a/starboard/android/shared/audio_track_audio_sink_type.cc b/starboard/android/shared/audio_track_audio_sink_type.cc
index ada20e9..98810e7 100644
--- a/starboard/android/shared/audio_track_audio_sink_type.cc
+++ b/starboard/android/shared/audio_track_audio_sink_type.cc
@@ -18,6 +18,7 @@
#include <string>
#include <vector>
+#include "starboard/android/shared/media_capabilities_cache.h"
#include "starboard/common/string.h"
#include "starboard/shared/starboard/media/media_util.h"
#include "starboard/shared/starboard/player/filter/common.h"
@@ -50,8 +51,10 @@
const size_t kSilenceFramesPerAppend = 1024;
-const int kMaxRequiredFrames = 16 * 1024;
-const int kRequiredFramesIncrement = 2 * 1024;
+const int kMaxRequiredFramesLocal = 16 * 1024;
+const int kMaxRequiredFramesRemote = 32 * 1024;
+const int kMaxRequiredFrames = kMaxRequiredFramesRemote;
+const int kRequiredFramesIncrement = 4 * 1024;
const int kMinStablePlayedFrames = 12 * 1024;
const int kSampleFrequency22050 = 22050;
@@ -67,6 +70,38 @@
return (max_frames + 15) / 16 * 16; // align to 16
}
+bool HasRemoteAudioOutput() {
+#if SB_API_VERSION >= 15
+ // SbPlayerBridge::GetAudioConfigurations() reads up to 32 configurations. The
+ // limit here is to avoid infinite loop and also match
+ // SbPlayerBridge::GetAudioConfigurations().
+ const int kMaxAudioConfigurations = 32;
+ SbMediaAudioConfiguration configuration;
+ int index = 0;
+ while (index < kMaxAudioConfigurations &&
+ MediaCapabilitiesCache::GetInstance()->GetAudioConfiguration(
+ index, &configuration)) {
+ switch (configuration.connector) {
+ case kSbMediaAudioConnectorUnknown:
+ case kSbMediaAudioConnectorAnalog:
+ case kSbMediaAudioConnectorBuiltIn:
+ case kSbMediaAudioConnectorHdmi:
+ case kSbMediaAudioConnectorSpdif:
+ case kSbMediaAudioConnectorUsb:
+ break;
+ case kSbMediaAudioConnectorBluetooth:
+ case kSbMediaAudioConnectorRemoteWired:
+ case kSbMediaAudioConnectorRemoteWireless:
+ case kSbMediaAudioConnectorRemoteOther:
+ return true;
+ }
+ index++;
+ }
+ return false;
+#endif // SB_API_VERSION >= 15
+ return false;
+}
+
} // namespace
AudioTrackAudioSink::AudioTrackAudioSink(
@@ -487,11 +522,18 @@
auto onMinRequiredFramesForWebAudioReceived =
[&](int number_of_channels, SbMediaAudioSampleType sample_type,
int sample_rate, int min_required_frames) {
+ bool has_remote_audio_output = HasRemoteAudioOutput();
SB_LOG(INFO) << "Received min required frames " << min_required_frames
<< " for " << number_of_channels << " channels, "
- << sample_rate << "hz.";
+ << sample_rate << "hz, with "
+ << (has_remote_audio_output ? "remote" : "local")
+ << " audio output device.";
ScopedLock lock(min_required_frames_map_mutex_);
- min_required_frames_map_[sample_rate] = min_required_frames;
+ has_remote_audio_output_ = has_remote_audio_output;
+ min_required_frames_map_[sample_rate] =
+ std::min(min_required_frames, has_remote_audio_output_
+ ? kMaxRequiredFramesRemote
+ : kMaxRequiredFramesLocal);
};
SbMediaAudioSampleType sample_type = kSbMediaAudioSampleTypeFloat32;
@@ -512,20 +554,27 @@
int channels,
SbMediaAudioSampleType sample_type,
int sampling_frequency_hz) {
- if (sampling_frequency_hz <= kSampleFrequency22050) {
- ScopedLock lock(min_required_frames_map_mutex_);
- if (min_required_frames_map_.find(kSampleFrequency22050) !=
- min_required_frames_map_.end()) {
- return min_required_frames_map_[kSampleFrequency22050];
- }
- } else if (sampling_frequency_hz <= kSampleFrequency48000) {
- ScopedLock lock(min_required_frames_map_mutex_);
- if (min_required_frames_map_.find(kSampleFrequency48000) !=
- min_required_frames_map_.end()) {
- return min_required_frames_map_[kSampleFrequency48000];
+ bool has_remote_audio_output = HasRemoteAudioOutput();
+ ScopedLock lock(min_required_frames_map_mutex_);
+ if (has_remote_audio_output == has_remote_audio_output_) {
+ // There's no audio output type change, we can use the numbers we got from
+ // the tests at app launch.
+ if (sampling_frequency_hz <= kSampleFrequency22050) {
+ if (min_required_frames_map_.find(kSampleFrequency22050) !=
+ min_required_frames_map_.end()) {
+ return min_required_frames_map_[kSampleFrequency22050];
+ }
+ } else if (sampling_frequency_hz <= kSampleFrequency48000) {
+ if (min_required_frames_map_.find(kSampleFrequency48000) !=
+ min_required_frames_map_.end()) {
+ return min_required_frames_map_[kSampleFrequency48000];
+ }
}
}
- return kMaxRequiredFrames;
+ // We cannot find a matched result from our tests, or the audio output type
+ // has changed. We use the default max required frames to avoid underruns.
+ return has_remote_audio_output ? kMaxRequiredFramesRemote
+ : kMaxRequiredFramesLocal;
}
} // namespace shared
diff --git a/starboard/android/shared/audio_track_audio_sink_type.h b/starboard/android/shared/audio_track_audio_sink_type.h
index 88974ff..b115deb 100644
--- a/starboard/android/shared/audio_track_audio_sink_type.h
+++ b/starboard/android/shared/audio_track_audio_sink_type.h
@@ -97,6 +97,7 @@
// The minimum frames required to avoid underruns of different frequencies.
std::map<int, int> min_required_frames_map_;
MinRequiredFramesTester min_required_frames_tester_;
+ bool has_remote_audio_output_ = false;
};
class AudioTrackAudioSink : public SbAudioSinkPrivate {
diff --git a/starboard/android/shared/input_events_generator.cc b/starboard/android/shared/input_events_generator.cc
index 0b345242..8115b6b 100644
--- a/starboard/android/shared/input_events_generator.cc
+++ b/starboard/android/shared/input_events_generator.cc
@@ -679,6 +679,12 @@
PushKeyEvent(key, type, window_, android_event, events);
}
+ // Cobalt does not handle the kSbKeyUnknown event, return false,
+ // so the key event can be handled by the next receiver.
+ if (key == kSbKeyUnknown) {
+ return false;
+ }
+
return true;
}
diff --git a/starboard/android/shared/jni_env_ext.cc b/starboard/android/shared/jni_env_ext.cc
index 06535d5..98a65fd 100644
--- a/starboard/android/shared/jni_env_ext.cc
+++ b/starboard/android/shared/jni_env_ext.cc
@@ -72,7 +72,7 @@
}
JniEnvExt* JniEnvExt::Get() {
- JNIEnv* env;
+ JNIEnv* env = nullptr;
if (JNI_OK != g_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
// Tell the JVM our thread name so it doesn't change it.
char thread_name[16];
diff --git a/starboard/android/shared/media_capabilities_cache.cc b/starboard/android/shared/media_capabilities_cache.cc
index 7b8997b..fe96cc8 100644
--- a/starboard/android/shared/media_capabilities_cache.cc
+++ b/starboard/android/shared/media_capabilities_cache.cc
@@ -40,6 +40,114 @@
const char SECURE_DECODER_SUFFIX[] = ".secure";
+// Constants for output types from
+// https://developer.android.com/reference/android/media/AudioDeviceInfo.
+constexpr int TYPE_AUX_LINE = 19;
+constexpr int TYPE_BLE_BROADCAST = 30;
+constexpr int TYPE_BLE_HEADSET = 26;
+constexpr int TYPE_BLE_SPEAKER = 27;
+constexpr int TYPE_BLUETOOTH_A2DP = 8;
+constexpr int TYPE_BLUETOOTH_SCO = 7;
+constexpr int TYPE_BUILTIN_EARPIECE = 1;
+constexpr int TYPE_BUILTIN_MIC = 15;
+constexpr int TYPE_BUILTIN_SPEAKER = 2;
+constexpr int TYPE_BUILTIN_SPEAKER_SAFE = 24;
+constexpr int TYPE_BUS = 21;
+constexpr int TYPE_DOCK = 13;
+constexpr int TYPE_DOCK_ANALOG = 31;
+constexpr int TYPE_FM = 14;
+constexpr int TYPE_FM_TUNER = 16;
+constexpr int TYPE_HDMI = 9;
+constexpr int TYPE_HDMI_ARC = 10;
+constexpr int TYPE_HDMI_EARC = 29;
+constexpr int TYPE_HEARING_AID = 23;
+constexpr int TYPE_IP = 20;
+constexpr int TYPE_LINE_ANALOG = 5;
+constexpr int TYPE_LINE_DIGITAL = 6;
+constexpr int TYPE_REMOTE_SUBMIX = 25;
+constexpr int TYPE_TELEPHONY = 18;
+constexpr int TYPE_TV_TUNER = 17;
+constexpr int TYPE_UNKNOWN = 0;
+constexpr int TYPE_USB_ACCESSORY = 12;
+constexpr int TYPE_USB_DEVICE = 11;
+constexpr int TYPE_USB_HEADSET = 22;
+constexpr int TYPE_WIRED_HEADPHONES = 4;
+constexpr int TYPE_WIRED_HEADSET = 3;
+
+#if SB_API_VERSION >= 15
+SbMediaAudioConnector GetConnectorFromAndroidOutputType(
+ int android_output_device_type) {
+ switch (android_output_device_type) {
+ case TYPE_AUX_LINE:
+ return kSbMediaAudioConnectorAnalog;
+ case TYPE_BLE_BROADCAST:
+ return kSbMediaAudioConnectorBluetooth;
+ case TYPE_BLE_HEADSET:
+ return kSbMediaAudioConnectorBluetooth;
+ case TYPE_BLE_SPEAKER:
+ return kSbMediaAudioConnectorBluetooth;
+ case TYPE_BLUETOOTH_A2DP:
+ return kSbMediaAudioConnectorBluetooth;
+ case TYPE_BLUETOOTH_SCO:
+ return kSbMediaAudioConnectorBluetooth;
+ case TYPE_BUILTIN_EARPIECE:
+ return kSbMediaAudioConnectorBuiltIn;
+ case TYPE_BUILTIN_MIC:
+ return kSbMediaAudioConnectorBuiltIn;
+ case TYPE_BUILTIN_SPEAKER:
+ return kSbMediaAudioConnectorBuiltIn;
+ case TYPE_BUILTIN_SPEAKER_SAFE:
+ return kSbMediaAudioConnectorBuiltIn;
+ case TYPE_BUS:
+ return kSbMediaAudioConnectorUnknown;
+ case TYPE_DOCK:
+ return kSbMediaAudioConnectorUnknown;
+ case TYPE_DOCK_ANALOG:
+ return kSbMediaAudioConnectorAnalog;
+ case TYPE_FM:
+ return kSbMediaAudioConnectorUnknown;
+ case TYPE_FM_TUNER:
+ return kSbMediaAudioConnectorUnknown;
+ case TYPE_HDMI:
+ return kSbMediaAudioConnectorHdmi;
+ case TYPE_HDMI_ARC:
+ return kSbMediaAudioConnectorHdmi;
+ case TYPE_HDMI_EARC:
+ return kSbMediaAudioConnectorHdmi;
+ case TYPE_HEARING_AID:
+ return kSbMediaAudioConnectorUnknown;
+ case TYPE_IP:
+ return kSbMediaAudioConnectorRemoteWired;
+ case TYPE_LINE_ANALOG:
+ return kSbMediaAudioConnectorAnalog;
+ case TYPE_LINE_DIGITAL:
+ return kSbMediaAudioConnectorUnknown;
+ case TYPE_REMOTE_SUBMIX:
+ return kSbMediaAudioConnectorRemoteOther;
+ case TYPE_TELEPHONY:
+ return kSbMediaAudioConnectorUnknown;
+ case TYPE_TV_TUNER:
+ return kSbMediaAudioConnectorUnknown;
+ case TYPE_UNKNOWN:
+ return kSbMediaAudioConnectorUnknown;
+ case TYPE_USB_ACCESSORY:
+ return kSbMediaAudioConnectorUsb;
+ case TYPE_USB_DEVICE:
+ return kSbMediaAudioConnectorUsb;
+ case TYPE_USB_HEADSET:
+ return kSbMediaAudioConnectorUsb;
+ case TYPE_WIRED_HEADPHONES:
+ return kSbMediaAudioConnectorAnalog;
+ case TYPE_WIRED_HEADSET:
+ return kSbMediaAudioConnectorAnalog;
+ }
+
+ SB_LOG(WARNING) << "Encountered unknown audio output device type "
+ << android_output_device_type;
+ return kSbMediaAudioConnectorUnknown;
+}
+#endif // SB_API_VERSION >= 15
+
bool EndsWith(const std::string& str, const std::string& suffix) {
if (str.size() < suffix.size()) {
return false;
@@ -135,13 +243,47 @@
encoding) == JNI_TRUE;
}
-int GetMaxAudioOutputChannels() {
+bool GetAudioConfiguration(int index,
+ SbMediaAudioConfiguration* configuration) {
+ *configuration = {};
+
JniEnvExt* env = JniEnvExt::Get();
ScopedLocalJavaRef<jobject> j_audio_output_manager(
env->CallStarboardObjectMethodOrAbort(
"getAudioOutputManager", "()Ldev/cobalt/media/AudioOutputManager;"));
- return static_cast<int>(env->CallIntMethodOrAbort(
- j_audio_output_manager.Get(), "getMaxChannels", "()I"));
+ ScopedLocalJavaRef<jobject> j_output_device_info(env->NewObjectOrAbort(
+ "dev/cobalt/media/AudioOutputManager$OutputDeviceInfo", "()V"));
+
+ bool succeeded = env->CallBooleanMethodOrAbort(
+ j_audio_output_manager.Get(), "getOutputDeviceInfo",
+ "(ILdev/cobalt/media/AudioOutputManager$OutputDeviceInfo;)Z", index,
+ j_output_device_info.Get());
+
+ if (!succeeded) {
+ SB_LOG(WARNING)
+ << "Call to AudioOutputManager.getOutputDeviceInfo() failed.";
+ return false;
+ }
+
+ auto call_int_method = [env, &j_output_device_info](const char* name) {
+ return env->CallIntMethodOrAbort(j_output_device_info.Get(), name, "()I");
+ };
+
+#if SB_API_VERSION >= 15
+ configuration->connector =
+ GetConnectorFromAndroidOutputType(call_int_method("getType"));
+#else // SB_API_VERSION >= 15
+ configuration->connector = kSbMediaAudioConnectorHdmi;
+#endif // SB_API_VERSION >= 15
+ configuration->latency = 0;
+ configuration->coding_type = kSbMediaAudioCodingTypePcm;
+ configuration->number_of_channels = call_int_method("getChannels");
+
+ if (configuration->connector != kSbMediaAudioConnectorHdmi) {
+ configuration->number_of_channels = 2;
+ }
+
+ return true;
}
} // namespace
@@ -311,21 +453,27 @@
return supported;
}
-int MediaCapabilitiesCache::GetMaxAudioOutputChannels() {
+bool MediaCapabilitiesCache::GetAudioConfiguration(
+ int index,
+ SbMediaAudioConfiguration* configuration) {
+ SB_DCHECK(index >= 0);
if (!is_enabled_) {
- return ::starboard::android::shared::GetMaxAudioOutputChannels();
+ return ::starboard::android::shared::GetAudioConfiguration(index,
+ configuration);
}
ScopedLock scoped_lock(mutex_);
UpdateMediaCapabilities_Locked();
- return max_audio_output_channels_;
+ if (index < audio_configurations_.size()) {
+ *configuration = audio_configurations_[index];
+ return true;
+ }
+ return false;
}
bool MediaCapabilitiesCache::HasAudioDecoderFor(const std::string& mime_type,
- int bitrate,
- bool must_support_tunnel_mode) {
- return !FindAudioDecoder(mime_type, bitrate, must_support_tunnel_mode)
- .empty();
+ int bitrate) {
+ return !FindAudioDecoder(mime_type, bitrate).empty();
}
bool MediaCapabilitiesCache::HasVideoDecoderFor(
@@ -347,16 +495,14 @@
std::string MediaCapabilitiesCache::FindAudioDecoder(
const std::string& mime_type,
- int bitrate,
- bool must_support_tunnel_mode) {
+ int bitrate) {
if (!is_enabled_) {
JniEnvExt* env = JniEnvExt::Get();
ScopedLocalJavaRef<jstring> j_mime(
env->NewStringStandardUTFOrAbort(mime_type.c_str()));
jobject j_decoder_name = env->CallStaticObjectMethodOrAbort(
"dev/cobalt/media/MediaCodecUtil", "findAudioDecoder",
- "(Ljava/lang/String;IZ)Ljava/lang/String;", j_mime.Get(), bitrate,
- must_support_tunnel_mode);
+ "(Ljava/lang/String;I)Ljava/lang/String;", j_mime.Get(), bitrate);
return env->GetStringStandardUTFOrAbort(
static_cast<jstring>(j_decoder_name));
}
@@ -365,11 +511,6 @@
UpdateMediaCapabilities_Locked();
for (auto& audio_capability : audio_codec_capabilities_map_[mime_type]) {
- // Reject if tunnel mode is required but codec doesn't support it.
- if (must_support_tunnel_mode &&
- !audio_capability->is_tunnel_mode_supported()) {
- continue;
- }
// Reject if bitrate is not supported.
if (!audio_capability->IsBitrateSupported(bitrate)) {
continue;
@@ -463,25 +604,6 @@
return "";
}
-void MediaCapabilitiesCache::ClearCache() {
- ScopedLock scoped_lock(mutex_);
- is_initialized_ = false;
- supported_hdr_types_is_dirty_ = true;
- audio_output_channels_is_dirty_ = true;
- is_widevine_supported_ = false;
- is_cbcs_supported_ = false;
- supported_transfer_ids_.clear();
- passthrough_supportabilities_.clear();
- audio_codec_capabilities_map_.clear();
- video_codec_capabilities_map_.clear();
- max_audio_output_channels_ = -1;
-}
-
-void MediaCapabilitiesCache::Initialize() {
- ScopedLock scoped_lock(mutex_);
- UpdateMediaCapabilities_Locked();
-}
-
MediaCapabilitiesCache::MediaCapabilitiesCache() {
// Enable mime and key system caches.
MimeSupportabilityCache::GetInstance()->SetCacheEnabled(true);
@@ -490,22 +612,20 @@
void MediaCapabilitiesCache::UpdateMediaCapabilities_Locked() {
mutex_.DCheckAcquired();
+ if (capabilities_is_dirty_.exchange(false)) {
+ // We use a different cache strategy (load and cache) for passthrough
+ // supportabilities, so we only clear |passthrough_supportabilities_| here.
+ passthrough_supportabilities_.clear();
- if (supported_hdr_types_is_dirty_.exchange(false)) {
+ audio_codec_capabilities_map_.clear();
+ video_codec_capabilities_map_.clear();
+ audio_configurations_.clear();
+ is_widevine_supported_ = GetIsWidevineSupported();
+ is_cbcs_supported_ = GetIsCbcsSupported();
supported_transfer_ids_ = GetSupportedHdrTypes();
+ LoadCodecInfos_Locked();
+ LoadAudioConfigurations_Locked();
}
- if (audio_output_channels_is_dirty_.exchange(false)) {
- max_audio_output_channels_ =
- ::starboard::android::shared::GetMaxAudioOutputChannels();
- }
-
- if (is_initialized_) {
- return;
- }
- is_widevine_supported_ = GetIsWidevineSupported();
- is_cbcs_supported_ = GetIsCbcsSupported();
- LoadCodecInfos_Locked();
- is_initialized_ = true;
}
void MediaCapabilitiesCache::LoadCodecInfos_Locked() {
@@ -558,33 +678,37 @@
}
}
+void MediaCapabilitiesCache::LoadAudioConfigurations_Locked() {
+ SB_DCHECK(audio_configurations_.empty());
+ mutex_.DCheckAcquired();
+
+ // SbPlayerBridge::GetAudioConfigurations() reads up to 32 configurations. The
+ // limit here is to avoid infinite loop and also match
+ // SbPlayerBridge::GetAudioConfigurations().
+ const int kMaxAudioConfigurations = 32;
+ SbMediaAudioConfiguration configuration;
+ while (audio_configurations_.size() < kMaxAudioConfigurations &&
+ ::starboard::android::shared::GetAudioConfiguration(
+ audio_configurations_.size(), &configuration)) {
+ audio_configurations_.push_back(configuration);
+ }
+}
+
extern "C" SB_EXPORT_PLATFORM void
Java_dev_cobalt_util_DisplayUtil_nativeOnDisplayChanged() {
- SB_DLOG(INFO) << "Display device has changed.";
- MediaCapabilitiesCache::GetInstance()->ClearSupportedHdrTypes();
+ // Display device change could change hdr capabilities.
+ MediaCapabilitiesCache::GetInstance()->ClearCache();
MimeSupportabilityCache::GetInstance()->ClearCachedMimeSupportabilities();
}
extern "C" SB_EXPORT_PLATFORM void
Java_dev_cobalt_media_AudioOutputManager_nativeOnAudioDeviceChanged() {
- SB_DLOG(INFO) << "Audio device has changed.";
- MediaCapabilitiesCache::GetInstance()->ClearAudioOutputChannels();
+ // Audio output device change could change passthrough decoder capabilities,
+ // so we have to reload codec capabilities.
+ MediaCapabilitiesCache::GetInstance()->ClearCache();
MimeSupportabilityCache::GetInstance()->ClearCachedMimeSupportabilities();
}
-void* MediaCapabilitiesCacheInitializationThreadEntry(void* context) {
- SB_LOG(INFO) << "Initialize MediaCapabilitiesCache in background.";
- MediaCapabilitiesCache::GetInstance()->Initialize();
- return 0;
-}
-
-extern "C" SB_EXPORT_PLATFORM void
-Java_dev_cobalt_coat_CobaltActivity_nativeInitializeMediaCapabilitiesInBackground() {
- SbThreadCreate(0, kSbThreadPriorityNormal, kSbThreadNoAffinity, false,
- "media_capabilities_cache_thread",
- MediaCapabilitiesCacheInitializationThreadEntry, nullptr);
-}
-
} // namespace shared
} // namespace android
} // namespace starboard
diff --git a/starboard/android/shared/media_capabilities_cache.h b/starboard/android/shared/media_capabilities_cache.h
index db3879e..656398a 100644
--- a/starboard/android/shared/media_capabilities_cache.h
+++ b/starboard/android/shared/media_capabilities_cache.h
@@ -125,11 +125,10 @@
bool IsPassthroughSupported(SbMediaAudioCodec codec);
- int GetMaxAudioOutputChannels();
+ bool GetAudioConfiguration(int index,
+ SbMediaAudioConfiguration* configuration);
- bool HasAudioDecoderFor(const std::string& mime_type,
- int bitrate,
- bool must_support_tunnel_mode);
+ bool HasAudioDecoderFor(const std::string& mime_type, int bitrate);
bool HasVideoDecoderFor(const std::string& mime_type,
bool must_support_secure,
@@ -141,9 +140,7 @@
int bitrate,
int fps);
- std::string FindAudioDecoder(const std::string& mime_type,
- int bitrate,
- bool must_support_tunnel_mode);
+ std::string FindAudioDecoder(const std::string& mime_type, int bitrate);
std::string FindVideoDecoder(const std::string& mime_type,
bool must_support_secure,
@@ -158,11 +155,7 @@
bool IsEnabled() const { return is_enabled_; }
void SetCacheEnabled(bool enabled) { is_enabled_ = enabled; }
- void ClearCache();
-
- void Initialize();
- void ClearSupportedHdrTypes() { supported_hdr_types_is_dirty_ = true; }
- void ClearAudioOutputChannels() { audio_output_channels_is_dirty_ = true; }
+ void ClearCache() { capabilities_is_dirty_ = true; }
private:
MediaCapabilitiesCache();
@@ -172,6 +165,7 @@
MediaCapabilitiesCache& operator=(const MediaCapabilitiesCache&) = delete;
void UpdateMediaCapabilities_Locked();
+ void LoadAudioConfigurations_Locked();
void LoadCodecInfos_Locked();
Mutex mutex_;
@@ -186,14 +180,12 @@
std::map<std::string, AudioCodecCapabilities> audio_codec_capabilities_map_;
std::map<std::string, VideoCodecCapabilities> video_codec_capabilities_map_;
-
- std::atomic_bool is_enabled_{true};
- std::atomic_bool supported_hdr_types_is_dirty_{true};
- std::atomic_bool audio_output_channels_is_dirty_{true};
- bool is_initialized_ = false;
+ std::vector<SbMediaAudioConfiguration> audio_configurations_;
bool is_widevine_supported_ = false;
bool is_cbcs_supported_ = false;
- int max_audio_output_channels_ = -1;
+
+ std::atomic_bool is_enabled_{true};
+ std::atomic_bool capabilities_is_dirty_{true};
};
} // namespace shared
diff --git a/starboard/android/shared/media_codec_bridge.cc b/starboard/android/shared/media_codec_bridge.cc
index fe97038..87bdf32 100644
--- a/starboard/android/shared/media_codec_bridge.cc
+++ b/starboard/android/shared/media_codec_bridge.cc
@@ -169,8 +169,7 @@
std::string decoder_name =
MediaCapabilitiesCache::GetInstance()->FindAudioDecoder(
- mime, /* bitrate = */ 0,
- /* must_support_tunnel_mode = */ false);
+ mime, /* bitrate = */ 0);
if (decoder_name.empty()) {
SB_LOG(ERROR) << "Failed to find decoder for " << audio_stream_info.codec
diff --git a/starboard/android/shared/media_decoder.cc b/starboard/android/shared/media_decoder.cc
index 78425b7..cf93733 100644
--- a/starboard/android/shared/media_decoder.cc
+++ b/starboard/android/shared/media_decoder.cc
@@ -146,9 +146,11 @@
MediaDecoder::~MediaDecoder() {
SB_DCHECK(thread_checker_.CalledOnValidThread());
-
destroying_.store(true);
- condition_variable_.Signal();
+ {
+ ScopedLock scoped_lock(mutex_);
+ condition_variable_.Signal();
+ }
if (SbThreadIsValid(decoder_thread_)) {
SbThreadJoin(decoder_thread_, NULL);
@@ -184,11 +186,15 @@
void MediaDecoder::WriteInputBuffers(const InputBuffers& input_buffers) {
SB_DCHECK(thread_checker_.CalledOnValidThread());
- SB_DCHECK(!input_buffers.empty());
if (stream_ended_.load()) {
SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
return;
}
+ if (input_buffers.empty()) {
+ SB_LOG(ERROR) << "No input buffer to decode.";
+ SB_DCHECK(!input_buffers.empty());
+ return;
+ }
if (!SbThreadIsValid(decoder_thread_)) {
decoder_thread_ = SbThreadCreate(
@@ -463,24 +469,20 @@
}
jint status;
- if (event.type == Event::kWriteCodecConfig) {
- if (!drm_system_ || (drm_system_ && drm_system_->IsReady())) {
- status = media_codec_bridge_->QueueInputBuffer(dequeue_input_result.index,
- kNoOffset, size, kNoPts,
- BUFFER_FLAG_CODEC_CONFIG);
- } else {
- status = MEDIA_CODEC_NO_KEY;
- }
+ if (drm_system_ && !drm_system_->IsReady()) {
+ // Drm system initialization is asynchronous. If there's a drm system, we
+ // should wait until it's initialized to avoid errors.
+ status = MEDIA_CODEC_NO_KEY;
+ } else if (event.type == Event::kWriteCodecConfig) {
+ status = media_codec_bridge_->QueueInputBuffer(dequeue_input_result.index,
+ kNoOffset, size, kNoPts,
+ BUFFER_FLAG_CODEC_CONFIG);
} else if (event.type == Event::kWriteInputBuffer) {
jlong pts_us = input_buffer->timestamp();
if (drm_system_ && input_buffer->drm_info()) {
- if (drm_system_->IsReady()) {
- status = media_codec_bridge_->QueueSecureInputBuffer(
- dequeue_input_result.index, kNoOffset, *input_buffer->drm_info(),
- pts_us);
- } else {
- status = MEDIA_CODEC_NO_KEY;
- }
+ status = media_codec_bridge_->QueueSecureInputBuffer(
+ dequeue_input_result.index, kNoOffset, *input_buffer->drm_info(),
+ pts_us);
} else {
status = media_codec_bridge_->QueueInputBuffer(
dequeue_input_result.index, kNoOffset, size, pts_us, kNoBufferFlags);
diff --git a/starboard/android/shared/media_get_audio_configuration.cc b/starboard/android/shared/media_get_audio_configuration.cc
index 6b645b6..572fceb 100644
--- a/starboard/android/shared/media_get_audio_configuration.cc
+++ b/starboard/android/shared/media_get_audio_configuration.cc
@@ -19,112 +19,6 @@
#include "starboard/android/shared/media_capabilities_cache.h"
#include "starboard/common/media.h"
-// Constants for output types from
-// https://developer.android.com/reference/android/media/AudioDeviceInfo.
-constexpr int TYPE_AUX_LINE = 19;
-constexpr int TYPE_BLE_BROADCAST = 30;
-constexpr int TYPE_BLE_HEADSET = 26;
-constexpr int TYPE_BLE_SPEAKER = 27;
-constexpr int TYPE_BLUETOOTH_A2DP = 8;
-constexpr int TYPE_BLUETOOTH_SCO = 7;
-constexpr int TYPE_BUILTIN_EARPIECE = 1;
-constexpr int TYPE_BUILTIN_MIC = 15;
-constexpr int TYPE_BUILTIN_SPEAKER = 2;
-constexpr int TYPE_BUILTIN_SPEAKER_SAFE = 24;
-constexpr int TYPE_BUS = 21;
-constexpr int TYPE_DOCK = 13;
-constexpr int TYPE_DOCK_ANALOG = 31;
-constexpr int TYPE_FM = 14;
-constexpr int TYPE_FM_TUNER = 16;
-constexpr int TYPE_HDMI = 9;
-constexpr int TYPE_HDMI_ARC = 10;
-constexpr int TYPE_HDMI_EARC = 29;
-constexpr int TYPE_HEARING_AID = 23;
-constexpr int TYPE_IP = 20;
-constexpr int TYPE_LINE_ANALOG = 5;
-constexpr int TYPE_LINE_DIGITAL = 6;
-constexpr int TYPE_REMOTE_SUBMIX = 25;
-constexpr int TYPE_TELEPHONY = 18;
-constexpr int TYPE_TV_TUNER = 17;
-constexpr int TYPE_UNKNOWN = 0;
-constexpr int TYPE_USB_ACCESSORY = 12;
-constexpr int TYPE_USB_DEVICE = 11;
-constexpr int TYPE_USB_HEADSET = 22;
-constexpr int TYPE_WIRED_HEADPHONES = 4;
-constexpr int TYPE_WIRED_HEADSET = 3;
-
-SbMediaAudioConnector GetConnectorFromAndroidOutputType(
- int android_output_device_type) {
- switch (android_output_device_type) {
- case TYPE_AUX_LINE:
- return kSbMediaAudioConnectorAnalog;
- case TYPE_BLE_BROADCAST:
- return kSbMediaAudioConnectorBluetooth;
- case TYPE_BLE_HEADSET:
- return kSbMediaAudioConnectorBluetooth;
- case TYPE_BLE_SPEAKER:
- return kSbMediaAudioConnectorBluetooth;
- case TYPE_BLUETOOTH_A2DP:
- return kSbMediaAudioConnectorBluetooth;
- case TYPE_BLUETOOTH_SCO:
- return kSbMediaAudioConnectorBluetooth;
- case TYPE_BUILTIN_EARPIECE:
- return kSbMediaAudioConnectorBuiltIn;
- case TYPE_BUILTIN_MIC:
- return kSbMediaAudioConnectorBuiltIn;
- case TYPE_BUILTIN_SPEAKER:
- return kSbMediaAudioConnectorBuiltIn;
- case TYPE_BUILTIN_SPEAKER_SAFE:
- return kSbMediaAudioConnectorBuiltIn;
- case TYPE_BUS:
- return kSbMediaAudioConnectorUnknown;
- case TYPE_DOCK:
- return kSbMediaAudioConnectorUnknown;
- case TYPE_DOCK_ANALOG:
- return kSbMediaAudioConnectorAnalog;
- case TYPE_FM:
- return kSbMediaAudioConnectorUnknown;
- case TYPE_FM_TUNER:
- return kSbMediaAudioConnectorUnknown;
- case TYPE_HDMI:
- return kSbMediaAudioConnectorHdmi;
- case TYPE_HDMI_ARC:
- return kSbMediaAudioConnectorHdmi;
- case TYPE_HDMI_EARC:
- return kSbMediaAudioConnectorHdmi;
- case TYPE_HEARING_AID:
- return kSbMediaAudioConnectorUnknown;
- case TYPE_IP:
- return kSbMediaAudioConnectorRemoteWired;
- case TYPE_LINE_ANALOG:
- return kSbMediaAudioConnectorAnalog;
- case TYPE_LINE_DIGITAL:
- return kSbMediaAudioConnectorUnknown;
- case TYPE_REMOTE_SUBMIX:
- return kSbMediaAudioConnectorRemoteOther;
- case TYPE_TELEPHONY:
- return kSbMediaAudioConnectorUnknown;
- case TYPE_TV_TUNER:
- return kSbMediaAudioConnectorUnknown;
- case TYPE_UNKNOWN:
- return kSbMediaAudioConnectorUnknown;
- case TYPE_USB_ACCESSORY:
- return kSbMediaAudioConnectorUsb;
- case TYPE_USB_DEVICE:
- return kSbMediaAudioConnectorUsb;
- case TYPE_USB_HEADSET:
- return kSbMediaAudioConnectorUsb;
- case TYPE_WIRED_HEADPHONES:
- return kSbMediaAudioConnectorAnalog;
- case TYPE_WIRED_HEADSET:
- return kSbMediaAudioConnectorAnalog;
- }
-
- SB_LOG(WARNING) << "Encountered unknown audio output device type "
- << android_output_device_type;
- return kSbMediaAudioConnectorUnknown;
-}
-
// TODO(b/284140486): Refine the implementation so it works when the audio
// outputs are changed during the query.
bool SbMediaGetAudioConfiguration(
@@ -146,56 +40,13 @@
return false;
}
- *out_configuration = {};
-
- JniEnvExt* env = JniEnvExt::Get();
- ScopedLocalJavaRef<jobject> j_audio_output_manager(
- env->CallStarboardObjectMethodOrAbort(
- "getAudioOutputManager", "()Ldev/cobalt/media/AudioOutputManager;"));
- ScopedLocalJavaRef<jobject> j_output_device_info(env->NewObjectOrAbort(
- "dev/cobalt/media/AudioOutputManager$OutputDeviceInfo", "()V"));
-
- bool succeeded = env->CallBooleanMethodOrAbort(
- j_audio_output_manager.Get(), "getOutputDeviceInfo",
- "(ILdev/cobalt/media/AudioOutputManager$OutputDeviceInfo;)Z",
- output_index, j_output_device_info.Get());
-
- if (!succeeded) {
- SB_LOG(WARNING)
- << "Call to AudioOutputManager.getOutputDeviceInfo() failed.";
- return false;
- }
-
- auto call_int_method = [env, &j_output_device_info](const char* name) {
- return env->CallIntMethodOrAbort(j_output_device_info.Get(), name, "()I");
- };
-
- out_configuration->connector =
- GetConnectorFromAndroidOutputType(call_int_method("getType"));
- out_configuration->latency = 0;
- out_configuration->coding_type = kSbMediaAudioCodingTypePcm;
- out_configuration->number_of_channels = call_int_method("getChannels");
-
- if (out_configuration->connector == kSbMediaAudioConnectorHdmi) {
- // Keep the previous logic for HDMI to reduce risk.
- // TODO(b/284140486): Update this using same logic as other connectors.
- int channels =
- MediaCapabilitiesCache::GetInstance()->GetMaxAudioOutputChannels();
- if (channels < 2) {
- SB_LOG(WARNING) << "The supported channels from output device is "
- << channels << ", set to 2 channels instead.";
- out_configuration->number_of_channels = 2;
- } else {
- out_configuration->number_of_channels = channels;
- }
- } else {
- out_configuration->number_of_channels = 2;
- }
+ bool result = MediaCapabilitiesCache::GetInstance()->GetAudioConfiguration(
+ output_index, out_configuration);
SB_LOG(INFO) << "Audio connector type for index " << output_index << " is "
<< GetMediaAudioConnectorName(out_configuration->connector)
<< " and it has " << out_configuration->number_of_channels
<< " channels.";
- return true;
+ return result;
}
diff --git a/starboard/android/shared/media_is_audio_supported.cc b/starboard/android/shared/media_is_audio_supported.cc
index 46b73e5..9844406 100644
--- a/starboard/android/shared/media_is_audio_supported.cc
+++ b/starboard/android/shared/media_is_audio_supported.cc
@@ -40,7 +40,6 @@
return false;
}
- bool enable_tunnel_mode = false;
bool enable_audio_passthrough = true;
if (mime_type) {
if (!mime_type->is_valid()) {
@@ -53,13 +52,6 @@
return false;
}
- // Allows for enabling tunneled playback. Disabled by default.
- // (https://source.android.com/devices/tv/multimedia-tunneling)
- if (!mime_type->ValidateBoolParameter("tunnelmode")) {
- return false;
- }
- enable_tunnel_mode = mime_type->GetParamBoolValue("tunnelmode", false);
-
// Enables audio passthrough if the codec supports it.
if (!mime_type->ValidateBoolParameter("audiopassthrough")) {
return false;
@@ -75,14 +67,6 @@
}
}
- if (enable_tunnel_mode && !SbAudioSinkIsAudioSampleTypeSupported(
- kSbMediaAudioSampleTypeInt16Deprecated)) {
- SB_LOG(WARNING)
- << "Tunnel mode is rejected because int16 sample is required "
- "but not supported.";
- return false;
- }
-
// Android uses a libopus based opus decoder for clear content, or a platform
// opus decoder for encrypted content, if available.
if (audio_codec == kSbMediaAudioCodecOpus) {
@@ -90,8 +74,7 @@
}
bool media_codec_supported =
- MediaCapabilitiesCache::GetInstance()->HasAudioDecoderFor(
- mime, bitrate, enable_tunnel_mode);
+ MediaCapabilitiesCache::GetInstance()->HasAudioDecoderFor(mime, bitrate);
if (!media_codec_supported) {
return false;
diff --git a/starboard/android/shared/player_components_factory.h b/starboard/android/shared/player_components_factory.h
index 18db0ba..57d3b44 100644
--- a/starboard/android/shared/player_components_factory.h
+++ b/starboard/android/shared/player_components_factory.h
@@ -303,7 +303,6 @@
MimeType audio_mime_type(audio_mime);
if (!audio_mime.empty()) {
if (!audio_mime_type.is_valid() ||
- !audio_mime_type.ValidateBoolParameter("tunnelmode") ||
!audio_mime_type.ValidateBoolParameter("enableaudiodevicecallback") ||
!audio_mime_type.ValidateBoolParameter("enablepcmcontenttypemovie")) {
*error_message =
@@ -331,20 +330,15 @@
bool enable_tunnel_mode = false;
if (creation_parameters.audio_codec() != kSbMediaAudioCodecNone &&
creation_parameters.video_codec() != kSbMediaVideoCodecNone) {
- bool enable_tunnel_mode =
- audio_mime_type.GetParamBoolValue("tunnelmode", false) &&
+ enable_tunnel_mode =
video_mime_type.GetParamBoolValue("tunnelmode", false);
- if (!enable_tunnel_mode) {
- SB_LOG(INFO) << "Tunnel mode is disabled. "
- << "Audio mime parameter \"tunnelmode\" value: "
- << audio_mime_type.GetParamStringValue("tunnelmode",
- "<not provided>")
- << ", video mime parameter \"tunnelmode\" value: "
- << video_mime_type.GetParamStringValue("tunnelmode",
- "<not provided>")
- << ".";
- }
+ SB_LOG(INFO) << "Tunnel mode is "
+ << (enable_tunnel_mode ? "enabled. " : "disabled. ")
+ << "Video mime parameter \"tunnelmode\" value: "
+ << video_mime_type.GetParamStringValue("tunnelmode",
+ "<not provided>")
+ << ".";
} else {
SB_LOG(INFO) << "Tunnel mode requires both an audio and video stream. "
<< "Audio codec: "
@@ -365,12 +359,19 @@
SB_LOG_IF(INFO, !force_improved_support_check)
<< "Improved support check is disabled for queries under 4K.";
bool force_secure_pipeline_under_tunnel_mode = false;
- if (enable_tunnel_mode &&
- IsTunnelModeSupported(creation_parameters,
- &force_secure_pipeline_under_tunnel_mode,
- force_improved_support_check)) {
- tunnel_mode_audio_session_id = GenerateAudioSessionId(
- creation_parameters, force_improved_support_check);
+ if (enable_tunnel_mode) {
+ if (IsTunnelModeSupported(creation_parameters,
+ &force_secure_pipeline_under_tunnel_mode,
+ force_improved_support_check)) {
+ tunnel_mode_audio_session_id = GenerateAudioSessionId(
+ creation_parameters, force_improved_support_check);
+ SB_LOG(INFO) << "Generated tunnel mode audio session id "
+ << tunnel_mode_audio_session_id;
+ } else {
+ SB_LOG(INFO) << "IsTunnelModeSupported() failed, disable tunnel mode.";
+ }
+ } else {
+ SB_LOG(INFO) << "Tunnel mode not enabled.";
}
if (tunnel_mode_audio_session_id == -1) {
diff --git a/starboard/android/shared/video_decoder.cc b/starboard/android/shared/video_decoder.cc
index 723a1cc..44e899b 100644
--- a/starboard/android/shared/video_decoder.cc
+++ b/starboard/android/shared/video_decoder.cc
@@ -29,6 +29,8 @@
#include "starboard/android/shared/media_common.h"
#include "starboard/android/shared/video_render_algorithm.h"
#include "starboard/android/shared/window_internal.h"
+#include "starboard/common/media.h"
+#include "starboard/common/player.h"
#include "starboard/common/string.h"
#include "starboard/configuration.h"
#include "starboard/decode_target.h"
@@ -397,6 +399,13 @@
TeardownCodec();
}
}
+
+ SB_LOG(INFO) << "Created VideoDecoder for codec "
+ << GetMediaVideoCodecName(video_codec_) << ", with output mode "
+ << GetPlayerOutputModeName(output_mode_)
+ << ", max video capabilities \"" << max_video_capabilities_
+ << "\", and tunnel mode audio session id "
+ << tunnel_mode_audio_session_id_;
}
VideoDecoder::~VideoDecoder() {
diff --git a/starboard/android/shared/video_window.cc b/starboard/android/shared/video_window.cc
index 5e401ec..d6998e3 100644
--- a/starboard/android/shared/video_window.cc
+++ b/starboard/android/shared/video_window.cc
@@ -211,20 +211,23 @@
return;
}
+ JniEnvExt* env = JniEnvExt::Get();
+ if (!env) {
+ SB_LOG(INFO) << "Tried to clear video window when JniEnvExt was null.";
+ return;
+ }
+
if (force_reset_surface) {
- JniEnvExt::Get()->CallStarboardVoidMethodOrAbort("resetVideoSurface",
- "()V");
+ env->CallStarboardVoidMethodOrAbort("resetVideoSurface", "()V");
return;
} else if (g_reset_surface_on_clear_window) {
int width = ANativeWindow_getWidth(g_native_video_window);
int height = ANativeWindow_getHeight(g_native_video_window);
if (width <= height) {
- JniEnvExt::Get()->CallStarboardVoidMethodOrAbort("resetVideoSurface",
- "()V");
+ env->CallStarboardVoidMethodOrAbort("resetVideoSurface", "()V");
return;
}
}
-
ClearNativeWindow(g_native_video_window);
}
diff --git a/starboard/build/config/BUILDCONFIG.gn b/starboard/build/config/BUILDCONFIG.gn
index 4075ed3..315e402 100644
--- a/starboard/build/config/BUILDCONFIG.gn
+++ b/starboard/build/config/BUILDCONFIG.gn
@@ -37,6 +37,10 @@
build_with_separate_cobalt_toolchain = false
}
+_is_on_pythonpath = exec_script("//starboard/build/is_on_path.py", [], "json")
+assert(!is_starboard || _is_on_pythonpath,
+ "The current repo is not first on the PYTHONPATH.")
+
assert(!(is_starboard && is_native_target_build),
"Targets should be built for Starboard or natively, but not both")
@@ -99,7 +103,7 @@
host_toolchain = "//starboard/build/toolchain/$host_os:$_host_toolchain_cpu"
if (build_with_separate_cobalt_toolchain) {
- cobalt_toolchain = "//starboard/build/toolchain:clang"
+ cobalt_toolchain = "//$starboard_path/toolchain:cobalt"
starboard_toolchain = "//$starboard_path/toolchain:starboard"
} else {
cobalt_toolchain = "//$starboard_path/toolchain:target"
@@ -253,7 +257,8 @@
}
}
- if (defined(invoker.install_content) && invoker.install_content) {
+ if (defined(invoker.install_content) && invoker.install_content &&
+ current_toolchain == default_toolchain) {
# We're using a custom script to copy the files here because rebase_path
# can't be used with {{}} expansions in the outputs of a copy target.
action("${target_name}_install_content") {
@@ -294,6 +299,13 @@
rebase_path(files_list, root_build_dir),
]
}
+ } else if (current_toolchain == starboard_toolchain &&
+ build_with_separate_cobalt_toolchain) {
+ outer_target_name = target_name
+ group("${target_name}_install_content") {
+ forward_variables_from(invoker, [ "testonly" ])
+ deps = [ ":${outer_target_name}_install_content($default_toolchain)" ]
+ }
}
}
@@ -401,8 +413,11 @@
forward_variables_from(invoker, [ "testonly" ])
deps = [
":${actual_target_name}_loader($starboard_toolchain)",
- ":${actual_target_name}_loader_copy($starboard_toolchain)",
+ ":${actual_target_name}_loader_install($starboard_toolchain)",
]
+ if (!is_host_win) {
+ deps += [ ":${actual_target_name}_loader_copy($starboard_toolchain)" ]
+ }
}
if (current_toolchain == starboard_toolchain) {
executable("${actual_target_name}_loader") {
@@ -410,6 +425,10 @@
forward_variables_from(invoker, [ "testonly" ])
sources = [ "//$starboard_path/starboard_loader.cc" ]
+ if (defined(extra_platform_loader_sources)) {
+ sources += extra_platform_loader_sources
+ }
+
if (use_asan) {
sources += [ "//$starboard_path/sanitizer_options.cc" ]
}
@@ -424,18 +443,25 @@
ldflags = [
"-Wl,-rpath=" + rebase_path("$root_build_dir/starboard"),
"-Wl,-rpath=" + rebase_path("$root_build_dir"),
+ "-Wl,-rpath=\$ORIGIN/../lib",
+ "-Wl,-rpath=\$ORIGIN",
]
deps = [
":$original_target_name($cobalt_toolchain)",
"//starboard:starboard_platform_group($starboard_toolchain)",
]
+ if (!separate_install_targets_for_bundling) {
+ deps += [ "//starboard:starboard_platform_group_install($starboard_toolchain)" ]
+ }
}
- copy("${actual_target_name}_loader_copy") {
- forward_variables_from(invoker, [ "testonly" ])
- sources = [ "$root_out_dir/${actual_target_name}_loader" ]
- outputs = [ "$root_build_dir/${actual_target_name}_loader" ]
- deps = [ ":${actual_target_name}_loader" ]
+ if (!is_host_win) {
+ copy("${actual_target_name}_loader_copy") {
+ forward_variables_from(invoker, [ "testonly" ])
+ sources = [ "$root_out_dir/${actual_target_name}_loader" ]
+ outputs = [ "$root_build_dir/${actual_target_name}_loader" ]
+ deps = [ ":${actual_target_name}_loader" ]
+ }
}
}
}
diff --git a/starboard/build/config/base_configuration.gni b/starboard/build/config/base_configuration.gni
index 1edd94b..6c9d354 100644
--- a/starboard/build/config/base_configuration.gni
+++ b/starboard/build/config/base_configuration.gni
@@ -44,9 +44,6 @@
# Directory path to static contents' data.
sb_static_contents_output_data_dir = "$root_out_dir/content/data"
- # Whether this is a modular build.
- sb_is_modular = false
-
# Whether this is an Evergreen build.
sb_is_evergreen = false
diff --git a/starboard/build/config/install.gni b/starboard/build/config/install.gni
index 9ef3f3d..b58479a 100644
--- a/starboard/build/config/install.gni
+++ b/starboard/build/config/install.gni
@@ -16,7 +16,7 @@
# Top-level directory for staging deploy build output. Platform install
# actions should use ${sb_install_output_dir} defined in this file to place
# artifacts for each deploy target in its own subdirectoy.
- sb_install_output_dir = "$root_out_dir/install"
+ sb_install_output_dir = "$root_build_dir/install"
# Sub-directory for install content.
sb_install_content_subdir = ""
diff --git a/starboard/build/config/modular/BUILD.gn b/starboard/build/config/modular/BUILD.gn
new file mode 100644
index 0000000..d266109
--- /dev/null
+++ b/starboard/build/config/modular/BUILD.gn
@@ -0,0 +1,210 @@
+# 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.
+
+config("modular") {
+ cflags = [
+ "-ffunction-sections",
+ "-fdata-sections",
+ "-nostdlibinc",
+ "-isystem" + rebase_path("//third_party/llvm-project/libcxxabi/include",
+ root_build_dir),
+ "-isystem" + rebase_path("//third_party/llvm-project/libunwind/include",
+ root_build_dir),
+ "-isystem" + rebase_path("//third_party/llvm-project/libcxx/include",
+ root_build_dir),
+ "-isystem" + rebase_path("//third_party/musl/include", root_build_dir),
+ "-isystem" + rebase_path("//third_party/musl/arch/generic", root_build_dir),
+ ]
+
+ if (!is_host_win) {
+ # Causes error on windows. clang++: error: unsupported option '-fPIC' for target 'x86_64-pc-windows-msvc'
+ cflags += [ "-fPIC" ]
+ }
+
+ cflags_cc = [
+ "-nostdinc++",
+ "-std=c++17",
+ ]
+
+ defines = [
+ # Ensure that the Starboardized __external_threading file is included.
+ "_LIBCPP_HAS_THREAD_API_EXTERNAL",
+
+ # Ensure that only the forward declarations and type definitions are included
+ # in __external_threading.
+ "_LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL",
+
+ # Enable GNU extensions to get prototypes like ffsl.
+ "_GNU_SOURCE=1",
+
+ "_LIBCPP_HAS_MUSL_LIBC",
+ "__STDC_FORMAT_MACROS", # so that we get PRI*
+
+ # File format of the shared object we will generate.
+ "__ELF__",
+
+ # Use scalar portable implementations instead of Clang/GCC vector
+ # extensions in SkVx.h.
+ "SKNX_NO_SIMD",
+
+ # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some
+ # nasty macros (|Status|, for example) that conflict with Chromium base.
+ "MESA_EGL_NO_X11_HEADERS",
+ ]
+
+ if (is_debug) {
+ cflags += [
+ "-O0",
+ "-frtti",
+ ]
+ if (!cobalt_fastbuild) {
+ cflags += [
+ # This flag causes an increase in binary size on certain platforms. Refer b/297357707
+ "-g",
+ ]
+ }
+ } else if (is_devel) {
+ cflags += [
+ "-O2",
+ "-frtti",
+ ]
+ if (!cobalt_fastbuild) {
+ cflags += [
+ # This flag causes an increase in binary size on certain platforms. Refer b/297357707
+ "-g",
+ ]
+ }
+ } else {
+ cflags += [
+ "-fno-rtti",
+ "-gline-tables-only",
+ ]
+ }
+
+ if (is_clang) {
+ cflags += [
+ "-fcolor-diagnostics",
+
+ # Default visibility to hidden, to enable dead stripping.
+ "-fvisibility=hidden",
+
+ # Warns on switches on enums that cover all enum values but
+ # also contain a default: branch. Chrome is full of that.
+ "-Wno-covered-switch-default",
+
+ # protobuf uses hash_map.
+ "-Wno-deprecated",
+
+ "-fno-exceptions",
+
+ # Enable unwind tables used by libunwind for stack traces.
+ "-funwind-tables",
+
+ # Disable usage of frame pointers.
+ "-fomit-frame-pointer",
+
+ # Don"t warn about the "struct foo f = {0};" initialization pattern.
+ "-Wno-missing-field-initializers",
+
+ # Do not warn for implicit sign conversions.
+ "-Wno-sign-conversion",
+
+ "-fno-strict-aliasing", # See http://crbug.com/32204
+
+ "-Wno-unnamed-type-template-args",
+
+ # Triggered by the COMPILE_ASSERT macro.
+ "-Wno-unused-local-typedef",
+
+ # Do not warn if a function or variable cannot be implicitly
+ # instantiated.
+ "-Wno-undefined-var-template",
+
+ # Do not warn about an implicit exception spec mismatch.
+ "-Wno-implicit-exception-spec-mismatch",
+
+ # It's OK not to use some input parameters.
+ "-Wno-unused-parameter",
+ "-Wno-conversion",
+ "-Wno-bitwise-op-parentheses",
+ "-Wno-shift-op-parentheses",
+ "-Wno-shorten-64-to-32",
+ "-fno-use-cxa-atexit",
+ ]
+ }
+
+ if (is_clang_16 || is_host_win) {
+ cflags += [
+ # Do not remove null pointer checks.
+ "-fno-delete-null-pointer-checks",
+ ]
+ }
+
+ if (use_asan) {
+ cflags += [
+ "-fsanitize=address",
+ "-fno-omit-frame-pointer",
+ ]
+
+ defines += [ "ADDRESS_SANITIZER" ]
+
+ if (asan_symbolizer_path != "") {
+ defines += [ "ASAN_SYMBOLIZER_PATH=\"${asan_symbolizer_path}\"" ]
+ }
+ } else if (use_tsan) {
+ cflags += [
+ "-fsanitize=thread",
+ "-fno-omit-frame-pointer",
+ ]
+
+ defines += [ "THREAD_SANITIZER" ]
+ }
+}
+
+config("speed") {
+ cflags = [ "-O2" ]
+}
+
+config("size") {
+ cflags = [ "-Os" ]
+}
+
+config("pedantic_warnings") {
+ cflags = [
+ "-Wall",
+ "-Wextra",
+ "-Wunreachable-code",
+ ]
+}
+
+config("no_pedantic_warnings") {
+ cflags = [
+ # 'this' pointer cannot be NULL...pointer may be assumed
+ # to always convert to true.
+ "-Wno-undefined-bool-conversion",
+
+ # Skia doesn't use overrides.
+ "-Wno-inconsistent-missing-override",
+
+ # Do not warn for implicit type conversions that may change a value.
+ "-Wno-conversion",
+
+ # shifting a negative signed value is undefined
+ "-Wno-shift-negative-value",
+
+ # Width of bit-field exceeds width of its type- value will be truncated
+ "-Wno-bitfield-width",
+ "-Wno-undefined-var-template",
+ ]
+}
diff --git a/starboard/build/config/modular/variables.gni b/starboard/build/config/modular/variables.gni
new file mode 100644
index 0000000..7b220fa
--- /dev/null
+++ b/starboard/build/config/modular/variables.gni
@@ -0,0 +1,20 @@
+# 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.
+
+assert(current_toolchain == default_toolchain,
+ "Cannot access variables for non-default toolchains")
+
+if (!is_host_win) {
+ is_clang_16 = true
+}
diff --git a/starboard/build/config/modular/x64/BUILD.gn b/starboard/build/config/modular/x64/BUILD.gn
new file mode 100644
index 0000000..3eb0f53
--- /dev/null
+++ b/starboard/build/config/modular/x64/BUILD.gn
@@ -0,0 +1,31 @@
+# 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.
+
+config("sabi_flags") {
+ cflags = [
+ "-march=x86-64",
+ "-target",
+ "x86_64-unknown-linux-elf",
+ ]
+}
+
+config("x64") {
+ configs = [
+ "//starboard/build/config/sabi",
+ ":sabi_flags",
+ ]
+
+ cflags = [ "-isystem" +
+ rebase_path("//third_party/musl/arch/x86_64", root_build_dir) ]
+}
diff --git a/starboard/build/config/starboard_target_type.gni b/starboard/build/config/starboard_target_type.gni
index c1ebc17..c3c5916 100644
--- a/starboard/build/config/starboard_target_type.gni
+++ b/starboard/build/config/starboard_target_type.gni
@@ -25,13 +25,17 @@
}
template("starboard_platform_target") {
- target(starboard_target_type, target_name) {
+ target_type = starboard_target_type
+ if (defined(invoker.target_type)) {
+ target_type = invoker.target_type
+ }
+ target(target_type, target_name) {
forward_variables_from(invoker, [ "extra_configs" ])
if (defined(invoker.extra_configs)) {
configs += extra_configs
}
- if (starboard_target_type == "shared_library") {
+ if (target_type == "shared_library") {
build_loader = false
}
public_deps = [
diff --git a/starboard/build/is_on_path.py b/starboard/build/is_on_path.py
new file mode 100644
index 0000000..c9c149b
--- /dev/null
+++ b/starboard/build/is_on_path.py
@@ -0,0 +1,32 @@
+#!/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.
+"""Script for checking if the current repo on the path."""
+
+import os
+
+
+def main():
+ try:
+ # Try to import this file and compare its path to the current file.
+ import starboard.build.is_on_path # pylint: disable=import-outside-toplevel
+ this_file = os.path.realpath(__file__)
+ imported_file = os.path.realpath(starboard.build.is_on_path.__file__)
+ print(str(this_file == imported_file).lower())
+ except ImportError:
+ print('false')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/starboard/build/toolchain/BUILD.gn b/starboard/build/toolchain/BUILD.gn
index 21a6a3c..5f665db 100644
--- a/starboard/build/toolchain/BUILD.gn
+++ b/starboard/build/toolchain/BUILD.gn
@@ -64,8 +64,5 @@
if (defined(native_snarl_linker)) {
using_snarl_linker = true
}
- toolchain_args = {
- is_clang = true
- }
}
}
diff --git a/starboard/build/toolchain/cobalt_toolchains.gni b/starboard/build/toolchain/cobalt_toolchains.gni
new file mode 100644
index 0000000..4277164
--- /dev/null
+++ b/starboard/build/toolchain/cobalt_toolchains.gni
@@ -0,0 +1,53 @@
+# 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.
+
+import("//build/config/win/visual_studio_version.gni")
+import("//build/toolchain/gcc_toolchain.gni")
+
+template("cobalt_clang_toolchain") {
+ gcc_toolchain(target_name) {
+ forward_variables_from(invoker.variables,
+ [
+ "native_linker_path",
+ "executable_extension",
+ "tail_lib_dependencies",
+ "shlib_extension",
+ ])
+ assert(defined(native_linker_path),
+ "native_linker_path has to be defined by the platform")
+ if (!is_host_win) {
+ prefix = rebase_path("$clang_base_path/bin", root_build_dir)
+ cc = "$prefix/clang"
+ cxx = "$prefix/clang++"
+ ld = native_linker_path
+ readelf = "readelf"
+ ar = "${prefix}/llvm-ar"
+ nm = "nm"
+ } else {
+ prefix = llvm_clang_path
+ cc = "$prefix/clang.exe"
+ cxx = "$prefix/clang++.exe"
+ ld = native_linker_path
+ readelf = "$prefix/llvm-readobj.exe"
+ ar = "${prefix}/llvm-ar.exe"
+ nm = "${prefix}/llvm-nm.exe"
+ }
+ toolchain_args = {
+ if (defined(invoker.toolchain_args)) {
+ forward_variables_from(invoker.toolchain_args, "*")
+ }
+ is_clang = true
+ }
+ }
+}
diff --git a/starboard/common/metrics/stats_tracker.h b/starboard/common/metrics/stats_tracker.h
index 9406175..7a217d9 100644
--- a/starboard/common/metrics/stats_tracker.h
+++ b/starboard/common/metrics/stats_tracker.h
@@ -52,8 +52,6 @@
StatsTracker& stats_tracker() {
if (!stats_tracker_) {
- SB_DLOG_IF(ERROR, !undefined_logged_)
- << "[once] StatsTracker is not defined.";
undefined_logged_ = true;
return undefined_stats_tracker_;
}
diff --git a/starboard/configuration.h b/starboard/configuration.h
index 651f131..da4eff5 100644
--- a/starboard/configuration.h
+++ b/starboard/configuration.h
@@ -35,7 +35,7 @@
// The minimum API version allowed by this version of the Starboard headers,
// inclusive.
-#define SB_MINIMUM_API_VERSION 12
+#define SB_MINIMUM_API_VERSION 13
// The maximum API version allowed by this version of the Starboard headers,
// inclusive.
diff --git a/starboard/doc/evergreen/cobalt_evergreen_overview.md b/starboard/doc/evergreen/cobalt_evergreen_overview.md
index 2f35e8b..f258cbb 100644
--- a/starboard/doc/evergreen/cobalt_evergreen_overview.md
+++ b/starboard/doc/evergreen/cobalt_evergreen_overview.md
@@ -305,8 +305,8 @@
1. Build the `crashpad_database_util` target and deploy it onto the device.
```
-$ cobalt/build/gn.py -p <partner_port_name> -c qa
-$ ninja -C out/<partner_port_name>_qa crashpad_database_util
+$ gn gen out/<partner_port_name>_qa --args='target_platform="<partner_port_name>" build_type="qa"'
+$ ninja -C out/<partner_port_name>_qa native_target/crashpad_database_util
```
2. Remove the existing state for crashpad as it throttles uploads to 1 per hour:
```
diff --git a/starboard/elf_loader/BUILD.gn b/starboard/elf_loader/BUILD.gn
index bca4126..184ccb2 100644
--- a/starboard/elf_loader/BUILD.gn
+++ b/starboard/elf_loader/BUILD.gn
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import("//starboard/build/config/os_definitions.gni")
+
_elf_loader_sources = [
"dynamic_section.cc",
"dynamic_section.h",
@@ -88,7 +90,8 @@
}
}
-if (current_toolchain == starboard_toolchain) {
+# TODO: b/309493306 - Stop building evergreen targets for all non-evergreen platforms.
+if (current_toolchain == starboard_toolchain && !is_host_win) {
target(starboard_level_final_executable_type, "elf_loader_sandbox") {
data_deps = [ "//third_party/icu:icudata" ]
if (cobalt_font_package == "empty") {
@@ -152,31 +155,34 @@
}
}
-target(gtest_target_type, "elf_loader_test") {
- testonly = true
- sources = [ "//starboard/common/test_main.cc" ]
- deps = [
- "//starboard",
- "//testing/gmock",
- "//testing/gtest",
- ]
-
- if (target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
- target_cpu == "arm64") {
- sources += [
- "dynamic_section_test.cc",
- "elf_header_test.cc",
- "elf_loader_test.cc",
- "lz4_file_impl_test.cc",
- "program_table_test.cc",
- "relocations_test.cc",
- ]
- deps += [
- ":copy_elf_loader_testdata",
- ":elf_loader",
+# TODO: b/309493306 - Stop building evergreen targets for all non-evergreen platforms.
+if (!is_host_win) {
+ target(gtest_target_type, "elf_loader_test") {
+ testonly = true
+ sources = [ "//starboard/common/test_main.cc" ]
+ deps = [
+ "//starboard",
+ "//testing/gmock",
+ "//testing/gtest",
]
- data_deps = [ ":copy_elf_loader_testdata" ]
+ if (target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
+ target_cpu == "arm64") {
+ sources += [
+ "dynamic_section_test.cc",
+ "elf_header_test.cc",
+ "elf_loader_test.cc",
+ "lz4_file_impl_test.cc",
+ "program_table_test.cc",
+ "relocations_test.cc",
+ ]
+ deps += [
+ ":copy_elf_loader_testdata",
+ ":elf_loader",
+ ]
+
+ data_deps = [ ":copy_elf_loader_testdata" ]
+ }
}
}
diff --git a/starboard/elf_loader/sandbox.cc b/starboard/elf_loader/sandbox.cc
index 2f2eb05..d86595e 100644
--- a/starboard/elf_loader/sandbox.cc
+++ b/starboard/elf_loader/sandbox.cc
@@ -58,7 +58,7 @@
GetEvergreenInfo(&evergreen_info);
if (!third_party::crashpad::wrapper::AddEvergreenInfoToCrashpad(
evergreen_info)) {
- SB_LOG(ERROR) << "Could not send Cobalt library information into Crashapd.";
+ SB_LOG(ERROR) << "Could not send Cobalt library information into Crashpad.";
} else {
SB_LOG(INFO) << "Loaded Cobalt library information into Crashpad.";
}
diff --git a/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn b/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
index c4403ea..d3474e2 100644
--- a/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
@@ -27,8 +27,10 @@
":sabi_flags",
"//starboard/evergreen/arm/shared/platform_configuration",
]
- ldflags = [
- "-Wl,-m",
- "-Wl,armelf",
- ]
+ if (sb_is_evergreen) {
+ ldflags = [
+ "-Wl,-m",
+ "-Wl,armelf",
+ ]
+ }
}
diff --git a/starboard/evergreen/shared/platform_configuration/BUILD.gn b/starboard/evergreen/shared/platform_configuration/BUILD.gn
index 2340c86..3bb69ba 100644
--- a/starboard/evergreen/shared/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/shared/platform_configuration/BUILD.gn
@@ -13,69 +13,30 @@
# limitations under the License.
config("platform_configuration") {
- ldflags = [
- "-fuse-ld=lld",
- "-Wl,--build-id",
- "-Wl,--gc-sections",
- "-Wl,-X",
- "-Wl,-v",
- "-Wl,-eh-frame-hdr",
- "-Wl,--fini=__cxa_finalize",
- "-Wl,-shared",
- "-Wl,-L$clang_base_path",
- "-Wl,-L/usr/lib",
- "-Wl,-L/lib",
- "-Wl,-u GetEvergreenSabiString",
- ]
+ configs = [ "//starboard/build/config/modular" ]
+ ldflags = []
+ if (sb_is_evergreen) {
+ ldflags += [
+ "-fuse-ld=lld",
+ "-Wl,--build-id",
+ "-Wl,--gc-sections",
+ "-Wl,-X",
+ "-Wl,-v",
+ "-Wl,-eh-frame-hdr",
+ "-Wl,--fini=__cxa_finalize",
+ "-Wl,-shared",
+ "-Wl,-L$clang_base_path",
+ "-Wl,-L/usr/lib",
+ "-Wl,-L/lib",
+ "-Wl,-u GetEvergreenSabiString",
+ ]
+ }
if (sb_is_evergreen) {
ldflags += [ "-nostdlib" ]
}
- cflags = [
- "-ffunction-sections",
- "-fdata-sections",
- "-fPIC",
- "-nostdlibinc",
- "-isystem" + rebase_path("//third_party/llvm-project/libcxxabi/include",
- root_build_dir),
- "-isystem" + rebase_path("//third_party/llvm-project/libunwind/include",
- root_build_dir),
- "-isystem" + rebase_path("//third_party/llvm-project/libcxx/include",
- root_build_dir),
- "-isystem" + rebase_path("//third_party/musl/include", root_build_dir),
- "-isystem" + rebase_path("//third_party/musl/arch/generic", root_build_dir),
- ]
-
- cflags_cc = [
- "-nostdinc++",
- "-std=c++17",
- ]
defines = [
- # Ensure that the Starboardized __external_threading file is included.
- "_LIBCPP_HAS_THREAD_API_EXTERNAL",
-
- # Ensure that only the forward declarations and type definitions are included
- # in __external_threading.
- "_LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL",
-
- # Enable GNU extensions to get prototypes like ffsl.
- "_GNU_SOURCE=1",
-
- "_LIBCPP_HAS_MUSL_LIBC",
- "__STDC_FORMAT_MACROS", # so that we get PRI*
-
- # File format of the shared object we will generate.
- "__ELF__",
-
- # Use scalar portable implementations instead of Clang/GCC vector
- # extensions in SkVx.h.
- "SKNX_NO_SIMD",
-
- # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some
- # nasty macros (|Status|, for example) that conflict with Chromium base.
- "MESA_EGL_NO_X11_HEADERS",
-
# During Evergreen updates the CRX package is kept in-memory, instead of
# on the file system, before getting unpacked.
# TODO(b/158043520): we need to make significant customizations to Chromium
@@ -88,150 +49,21 @@
"IN_MEMORY_UPDATES",
]
- if (is_debug) {
- cflags += [
- "-O0",
- "-frtti",
- "-g",
- ]
- } else if (is_devel) {
- cflags += [
- "-O2",
- "-frtti",
- "-g",
- ]
- } else {
- cflags += [
- "-gline-tables-only",
- "-fno-rtti",
- ]
- }
-
- if (is_clang) {
- cflags += [
- "-fcolor-diagnostics",
-
- # Default visibility to hidden, to enable dead stripping.
- "-fvisibility=hidden",
-
- # Warns on switches on enums that cover all enum values but
- # also contain a default: branch. Chrome is full of that.
- "-Wno-covered-switch-default",
-
- # protobuf uses hash_map.
- "-Wno-deprecated",
-
- "-fno-exceptions",
-
- # Enable unwind tables used by libunwind for stack traces.
- "-funwind-tables",
-
- # Disable usage of frame pointers.
- "-fomit-frame-pointer",
-
- # Don"t warn about the "struct foo f = {0};" initialization pattern.
- "-Wno-missing-field-initializers",
-
- # Do not warn for implicit sign conversions.
- "-Wno-sign-conversion",
-
- "-fno-strict-aliasing", # See http://crbug.com/32204
-
- "-Wno-unnamed-type-template-args",
-
- # Triggered by the COMPILE_ASSERT macro.
- "-Wno-unused-local-typedef",
-
- # Do not warn if a function or variable cannot be implicitly
- # instantiated.
- "-Wno-undefined-var-template",
-
- # Do not warn about an implicit exception spec mismatch.
- "-Wno-implicit-exception-spec-mismatch",
-
- # It's OK not to use some input parameters.
- "-Wno-unused-parameter",
- "-Wno-conversion",
- "-Wno-bitwise-op-parentheses",
- "-Wno-shift-op-parentheses",
- "-Wno-shorten-64-to-32",
- "-fno-use-cxa-atexit",
- ]
- }
-
- if (is_clang_16) {
- cflags += [
- # Do not remove null pointer checks.
- "-fno-delete-null-pointer-checks",
- ]
- }
-
if (use_asan) {
- cflags += [
- "-fsanitize=address",
- "-fno-omit-frame-pointer",
- ]
-
ldflags += [
"-fsanitize=address",
# Force linking of the helpers in sanitizer_options.cc
"-Wl,-u_sanitizer_options_link_helper",
]
-
- defines += [ "ADDRESS_SANITIZER" ]
-
- if (asan_symbolizer_path != "") {
- defines += [ "ASAN_SYMBOLIZER_PATH=\"${asan_symbolizer_path}\"" ]
- }
} else if (use_tsan) {
- cflags += [
- "-fsanitize=thread",
- "-fno-omit-frame-pointer",
- ]
-
ldflags += [ "-fsanitize=thread" ]
-
- defines += [ "THREAD_SANITIZER" ]
}
}
-config("speed") {
- cflags = [ "-O2" ]
-}
-
config("size") {
- cflags = [ "-Os" ]
- if (is_qa || is_gold) {
+ configs = [ "//starboard/build/config/modular:size" ]
+ if (sb_is_evergreen && (is_qa || is_gold)) {
ldflags = [ "-Wl,--icf=safe" ]
}
}
-
-config("pedantic_warnings") {
- cflags = [
- "-Wall",
- "-Wextra",
- "-Wunreachable-code",
- ]
-}
-
-config("no_pedantic_warnings") {
- cflags = [
- # 'this' pointer cannot be NULL...pointer may be assumed
- # to always convert to true.
- "-Wno-undefined-bool-conversion",
-
- # Skia doesn't use overrides.
- "-Wno-inconsistent-missing-override",
-
- # Do not warn for implicit type conversions that may change a value.
- "-Wno-conversion",
-
- # shifting a negative signed value is undefined
- "-Wno-shift-negative-value",
-
- # Width of bit-field exceeds width of its type- value will be truncated
- "-Wno-bitfield-width",
- "-Wno-undefined-var-template",
- ]
-}
diff --git a/starboard/evergreen/shared/platform_configuration/configuration.gni b/starboard/evergreen/shared/platform_configuration/configuration.gni
index 4327e1d..98bf65f 100644
--- a/starboard/evergreen/shared/platform_configuration/configuration.gni
+++ b/starboard/evergreen/shared/platform_configuration/configuration.gni
@@ -29,13 +29,13 @@
starboard_level_final_executable_type = "shared_library"
starboard_level_gtest_target_type = "shared_library"
-speed_config_path = "//starboard/evergreen/shared/platform_configuration:speed"
+speed_config_path = "//starboard/build/config/modular:speed"
size_config_path = "//starboard/evergreen/shared/platform_configuration:size"
pedantic_warnings_config_path =
- "//starboard/evergreen/shared/platform_configuration:pedantic_warnings"
+ "//starboard/build/config/modular:pedantic_warnings"
no_pedantic_warnings_config_path =
- "//starboard/evergreen/shared/platform_configuration:no_pedantic_warnings"
+ "//starboard/build/config/modular:no_pedantic_warnings"
cobalt_licenses_platform = "evergreen"
diff --git a/starboard/evergreen/x64/platform_configuration/BUILD.gn b/starboard/evergreen/x64/platform_configuration/BUILD.gn
index d49869b..ead85ee 100644
--- a/starboard/evergreen/x64/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/x64/platform_configuration/BUILD.gn
@@ -12,18 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-config("sabi_flags") {
- cflags = [
- "-march=x86-64",
- "-target",
- "x86_64-unknown-linux-elf",
- ]
-}
-
config("platform_configuration") {
configs = [
- "//starboard/build/config/sabi",
- ":sabi_flags",
+ "//starboard/build/config/modular/x64",
"//starboard/evergreen/shared/platform_configuration",
]
@@ -31,6 +22,4 @@
"-Wl,-m",
"-Wl,elf_x86_64",
]
- cflags = [ "-isystem" +
- rebase_path("//third_party/musl/arch/x86_64", root_build_dir) ]
}
diff --git a/starboard/extension/extension_test.cc b/starboard/extension/extension_test.cc
index d32cc84..874cce1 100644
--- a/starboard/extension/extension_test.cc
+++ b/starboard/extension/extension_test.cc
@@ -21,12 +21,14 @@
#include "starboard/extension/font.h"
#include "starboard/extension/free_space.h"
#include "starboard/extension/graphics.h"
+#include "starboard/extension/ifa.h"
#include "starboard/extension/installation_manager.h"
#include "starboard/extension/javascript_cache.h"
#include "starboard/extension/media_session.h"
#include "starboard/extension/memory_mapped_file.h"
#include "starboard/extension/platform_info.h"
#include "starboard/extension/platform_service.h"
+#include "starboard/extension/time_zone.h"
#include "starboard/extension/updater_notification.h"
#include "starboard/extension/url_fetcher_observer.h"
#include "starboard/system.h"
@@ -438,5 +440,46 @@
<< "Extension struct should be a singleton";
}
+TEST(ExtensionTest, TimeZone) {
+ typedef StarboardExtensionTimeZoneApi ExtensionApi;
+ const char* kExtensionName = kStarboardExtensionTimeZoneName;
+
+ 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->SetTimeZone, 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";
+}
+
+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 starboard
diff --git a/starboard/extension/ifa.h b/starboard/extension/ifa.h
new file mode 100644
index 0000000..a207049
--- /dev/null
+++ b/starboard/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/starboard/extension/time_zone.h b/starboard/extension/time_zone.h
new file mode 100644
index 0000000..bfddcea
--- /dev/null
+++ b/starboard/extension/time_zone.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 STARBOARD_EXTENSION_TIME_ZONE_H_
+#define STARBOARD_EXTENSION_TIME_ZONE_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define kStarboardExtensionTimeZoneName "dev.starboard.extension.TimeZone"
+
+typedef struct StarboardExtensionTimeZoneApi {
+ // Name should be the string |kStarboardExtensionSetTimeZoneName|.
+ // 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;
+
+ // Sets the current time zone to the specified time zone name.
+ // Note: This function should not be called with a NULL or empty
+ // string. It does not actually change the system clock, so it
+ // will not affect the time displayed on the system clock or
+ // used by other system processes.
+ bool (*SetTimeZone)(const char* time_zone_name);
+
+} StarboardExtensionTimeZoneApi;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // STARBOARD_EXTENSION_TIME_ZONE_H_
diff --git a/starboard/linux/shared/BUILD.gn b/starboard/linux/shared/BUILD.gn
index 57b7908..4b4e3fa 100644
--- a/starboard/linux/shared/BUILD.gn
+++ b/starboard/linux/shared/BUILD.gn
@@ -66,6 +66,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",
@@ -80,6 +82,8 @@
"//starboard/linux/shared/system_get_extensions.cc",
"//starboard/linux/shared/system_get_path.cc",
"//starboard/linux/shared/system_has_capability.cc",
+ "//starboard/linux/shared/time_zone.cc",
+ "//starboard/linux/shared/time_zone.h",
"//starboard/shared/alsa/alsa_audio_sink_type.cc",
"//starboard/shared/alsa/alsa_audio_sink_type.h",
"//starboard/shared/alsa/alsa_util.cc",
@@ -133,6 +137,7 @@
"//starboard/shared/linux/thread_get_id.cc",
"//starboard/shared/linux/thread_get_name.cc",
"//starboard/shared/linux/thread_set_name.cc",
+ "//starboard/shared/linux/time_zone_get_name.cc",
"//starboard/shared/nouser/user_get_current.cc",
"//starboard/shared/nouser/user_get_property.cc",
"//starboard/shared/nouser/user_get_signed_in.cc",
@@ -217,7 +222,6 @@
"//starboard/shared/posix/time_get_now.cc",
"//starboard/shared/posix/time_is_time_thread_now_supported.cc",
"//starboard/shared/posix/time_zone_get_current.cc",
- "//starboard/shared/posix/time_zone_get_name.cc",
"//starboard/shared/pthread/condition_variable_broadcast.cc",
"//starboard/shared/pthread/condition_variable_create.cc",
"//starboard/shared/pthread/condition_variable_destroy.cc",
diff --git a/starboard/linux/shared/ifa.cc b/starboard/linux/shared/ifa.cc
new file mode 100644
index 0000000..21b17dc
--- /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 "starboard/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/shared/posix/time_zone_get_name.cc b/starboard/linux/shared/ifa.h
similarity index 61%
rename from starboard/shared/posix/time_zone_get_name.cc
rename to starboard/linux/shared/ifa.h
index 5a1e013..fbe61ab 100644
--- a/starboard/shared/posix/time_zone_get_name.cc
+++ b/starboard/linux/shared/ifa.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,13 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/time_zone.h"
+#ifndef STARBOARD_LINUX_SHARED_IFA_H_
+#define STARBOARD_LINUX_SHARED_IFA_H_
-#include <time.h>
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace shared {
-const char* SbTimeZoneGetName() {
- // TODO: Using tzname assumes that tzset() has been called at some
- // point. That should happen as part of Starboard's main loop initialization,
- // but that doesn't exist yet.
- return tzname[0];
-}
+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 4a0b280..efc9169 100644
--- a/starboard/linux/shared/system_get_extensions.cc
+++ b/starboard/linux/shared/system_get_extensions.cc
@@ -20,9 +20,13 @@
#include "starboard/extension/demuxer.h"
#include "starboard/extension/enhanced_audio.h"
#include "starboard/extension/free_space.h"
+#include "starboard/extension/ifa.h"
#include "starboard/extension/memory_mapped_file.h"
#include "starboard/extension/platform_service.h"
+#include "starboard/extension/time_zone.h"
+#include "starboard/linux/shared/ifa.h"
#include "starboard/linux/shared/soft_mic_platform_service.h"
+#include "starboard/linux/shared/time_zone.h"
#include "starboard/shared/enhanced_audio/enhanced_audio.h"
#include "starboard/shared/ffmpeg/ffmpeg_demuxer.h"
#include "starboard/shared/posix/free_space.h"
@@ -74,5 +78,13 @@
return use_ffmpeg_demuxer ? starboard::shared::ffmpeg::GetFFmpegDemuxerApi()
: NULL;
}
+ if (strcmp(name, kStarboardExtensionTimeZoneName) == 0) {
+ return starboard::shared::GetTimeZoneApi();
+ }
+#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/linux/shared/system_get_path.cc b/starboard/linux/shared/system_get_path.cc
index 0f70002..999a016 100644
--- a/starboard/linux/shared/system_get_path.cc
+++ b/starboard/linux/shared/system_get_path.cc
@@ -113,6 +113,19 @@
}
#endif
+bool GetParentDirectory(char* out_path) {
+ if (!out_path) {
+ return false;
+ }
+ char* last_slash = const_cast<char*>(strrchr(out_path, '/'));
+ if (!last_slash) {
+ return false;
+ }
+
+ *last_slash = '\0';
+ return true;
+}
+
// Places up to |path_size| - 1 characters of the path to the directory
// containing the current executable in |out_path|, ensuring it is
// NULL-terminated. Returns success status. The result being greater than
@@ -122,14 +135,7 @@
if (!GetExecutablePath(out_path, path_size)) {
return false;
}
-
- char* last_slash = const_cast<char*>(strrchr(out_path, '/'));
- if (!last_slash) {
- return false;
- }
-
- *last_slash = '\0';
- return true;
+ return GetParentDirectory(out_path);
}
// Gets only the name portion of the current executable.
@@ -168,6 +174,11 @@
if (!GetExecutableDirectory(out_path, path_size)) {
return false;
}
+#ifdef USE_COMMON_CONTENT_DIR
+ if (!GetParentDirectory(out_path)) {
+ return false;
+ }
+#endif
if (starboard::strlcat(out_path, "/content", path_size) >= path_size) {
return false;
}
diff --git a/starboard/linux/shared/time_zone.cc b/starboard/linux/shared/time_zone.cc
new file mode 100644
index 0000000..30919af
--- /dev/null
+++ b/starboard/linux/shared/time_zone.cc
@@ -0,0 +1,60 @@
+// 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/time_zone.h"
+
+#include "starboard/extension/time_zone.h"
+
+#include <stdlib.h>
+#include <time.h>
+#include <cstring>
+
+#include "starboard/common/log.h"
+
+namespace starboard {
+namespace shared {
+
+namespace {
+
+// Definitions of any functions included as components in the extension
+// are added here.
+
+bool SetTimeZone(const char* time_zone_name) {
+ if (time_zone_name == nullptr || strlen(time_zone_name) == 0) {
+ SB_LOG(ERROR) << "Set time zone failed!";
+ SB_LOG(ERROR) << "Time zone name can't be null or empty string.";
+ return false;
+ }
+ if (setenv("TZ", time_zone_name, 1) != 0) {
+ SB_LOG(WARNING) << "Set time zone failed!";
+ return false;
+ }
+ tzset();
+ return true;
+}
+
+const StarboardExtensionTimeZoneApi kTimeZoneApi = {
+ kStarboardExtensionTimeZoneName,
+ 1, // API version that's implemented.
+ &SetTimeZone,
+};
+
+} // namespace
+
+const void* GetTimeZoneApi() {
+ return &kTimeZoneApi;
+}
+
+} // namespace shared
+} // namespace starboard
diff --git a/starboard/linux/shared/time_zone.h b/starboard/linux/shared/time_zone.h
new file mode 100644
index 0000000..6f063b0
--- /dev/null
+++ b/starboard/linux/shared/time_zone.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_TIME_ZONE_H_
+#define STARBOARD_LINUX_SHARED_TIME_ZONE_H_
+
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace shared {
+
+const void* GetTimeZoneApi();
+
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_LINUX_SHARED_TIME_ZONE_H_
diff --git a/starboard/linux/x64x11/main.cc b/starboard/linux/x64x11/main.cc
index 5cfd197..1929336 100644
--- a/starboard/linux/x64x11/main.cc
+++ b/starboard/linux/x64x11/main.cc
@@ -50,7 +50,6 @@
: starboard::common::GetCACertificatesPath(evergreen_content_path);
if (ca_certificates_path.empty()) {
SB_LOG(ERROR) << "Failed to get CA certificates path";
- return 1;
}
#if !SB_IS(MODULAR)
diff --git a/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn b/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn
index ab9cf36..cbdd8af 100644
--- a/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn
+++ b/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn
@@ -16,6 +16,7 @@
if (current_toolchain == default_toolchain && sb_is_modular &&
!sb_is_evergreen) {
configs = [ "//starboard/evergreen/x64/platform_configuration" ]
+ ldflags = [ "-Wl,--gc-sections" ]
} else {
configs = [
":libraries",
diff --git a/starboard/linux/x64x11/toolchain/BUILD.gn b/starboard/linux/x64x11/toolchain/BUILD.gn
index bcee641..79b7991 100644
--- a/starboard/linux/x64x11/toolchain/BUILD.gn
+++ b/starboard/linux/x64x11/toolchain/BUILD.gn
@@ -13,12 +13,19 @@
# limitations under the License.
import("//build/config/clang/clang.gni")
+import("//starboard/build/toolchain/cobalt_toolchains.gni")
import("//starboard/shared/toolchain/overridable_gcc_toolchain.gni")
overridable_clang_toolchain("starboard") {
clang_base_path = clang_base_path
}
+cobalt_clang_toolchain("cobalt") {
+ variables = {
+ native_linker_path = "$clang_base_path/bin/clang++"
+ }
+}
+
overridable_clang_toolchain("target") {
clang_base_path = clang_base_path
}
diff --git a/starboard/loader_app/BUILD.gn b/starboard/loader_app/BUILD.gn
index e0384a1..053c0c2 100644
--- a/starboard/loader_app/BUILD.gn
+++ b/starboard/loader_app/BUILD.gn
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import("//starboard/build/config/os_definitions.gni")
+
_common_loader_app_sources = [
"loader_app.cc",
"loader_app_switches.cc",
@@ -247,20 +249,23 @@
]
}
-target(gtest_target_type, "installation_manager_test") {
- testonly = true
- sources = [
- "//starboard/common/test_main.cc",
- "installation_manager_test.cc",
- "pending_restart_test.cc",
- ]
- deps = [
- ":installation_manager",
- ":installation_store_proto",
- ":pending_restart",
- "//testing/gmock",
- "//testing/gtest",
- ]
+# TODO: b/309493306 - Stop building evergreen targets for all non-evergreen platforms.
+if (!is_host_win) {
+ target(gtest_target_type, "installation_manager_test") {
+ testonly = true
+ sources = [
+ "//starboard/common/test_main.cc",
+ "installation_manager_test.cc",
+ "pending_restart_test.cc",
+ ]
+ deps = [
+ ":installation_manager",
+ ":installation_store_proto",
+ ":pending_restart",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+ }
}
static_library("slot_management") {
@@ -285,22 +290,25 @@
}
}
-target(gtest_target_type, "slot_management_test") {
- testonly = true
- sources = [
- "//starboard/common/test_main.cc",
- "slot_management_test.cc",
- ]
- deps = [
- ":app_key_files",
- ":drain_file",
- ":installation_manager",
- ":installation_store_proto",
- ":slot_management",
- "//starboard/elf_loader:sabi_string",
- "//testing/gmock",
- "//testing/gtest",
- ]
+# TODO: b/309493306 - Stop building evergreen targets for all non-evergreen platforms.
+if (!is_host_win) {
+ target(gtest_target_type, "slot_management_test") {
+ testonly = true
+ sources = [
+ "//starboard/common/test_main.cc",
+ "slot_management_test.cc",
+ ]
+ deps = [
+ ":app_key_files",
+ ":drain_file",
+ ":installation_manager",
+ ":installation_store_proto",
+ ":slot_management",
+ "//starboard/elf_loader:sabi_string",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+ }
}
static_library("pending_restart") {
@@ -332,15 +340,18 @@
deps = [ "//starboard" ]
}
-target(gtest_target_type, "reset_evergreen_update_test") {
- testonly = true
- sources = [
- "//starboard/common/test_main.cc",
- "reset_evergreen_update_test.cc",
- ]
- deps = [
- ":reset_evergreen_update",
- "//testing/gmock",
- "//testing/gtest",
- ]
+# TODO: b/309493306 - Stop building evergreen targets for all non-evergreen platforms.
+if (!is_host_win) {
+ target(gtest_target_type, "reset_evergreen_update_test") {
+ testonly = true
+ sources = [
+ "//starboard/common/test_main.cc",
+ "reset_evergreen_update_test.cc",
+ ]
+ deps = [
+ ":reset_evergreen_update",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+ }
}
diff --git a/starboard/loader_app/loader_app.cc b/starboard/loader_app/loader_app.cc
index 95f7ece..93cf9f4 100644
--- a/starboard/loader_app/loader_app.cc
+++ b/starboard/loader_app/loader_app.cc
@@ -143,7 +143,7 @@
GetEvergreenInfo(&evergreen_info);
if (!third_party::crashpad::wrapper::AddEvergreenInfoToCrashpad(
evergreen_info)) {
- SB_LOG(ERROR) << "Could not send Cobalt library information into Crashapd.";
+ SB_LOG(ERROR) << "Could not send Cobalt library information into Crashpad.";
} else {
SB_LOG(INFO) << "Loaded Cobalt library information into Crashpad.";
}
diff --git a/starboard/loader_app/slot_management.cc b/starboard/loader_app/slot_management.cc
index fc5549a..8b1af98 100644
--- a/starboard/loader_app/slot_management.cc
+++ b/starboard/loader_app/slot_management.cc
@@ -289,7 +289,7 @@
if (!third_party::crashpad::wrapper::AddEvergreenInfoToCrashpad(
evergreen_info)) {
SB_LOG(ERROR)
- << "Could not send Cobalt library information into Crashapd.";
+ << "Could not send Cobalt library information into Crashpad.";
} else {
SB_LOG(INFO) << "Loaded Cobalt library information into Crashpad.";
}
diff --git a/starboard/nplb/BUILD.gn b/starboard/nplb/BUILD.gn
index 664751b..3f314cf 100644
--- a/starboard/nplb/BUILD.gn
+++ b/starboard/nplb/BUILD.gn
@@ -271,7 +271,7 @@
cflags = [ "-Wno-enum-constexpr-conversion" ]
}
- # TODO b/296238576 Add these tests for windows based platform modular builds
+ # TODO: b/297808555 - Add these tests for windows based platform modular builds.
if (sb_is_modular && !sb_is_evergreen && is_host_win) {
sources -= [
"maximum_player_configuration_explorer.cc",
@@ -279,6 +279,7 @@
"maximum_player_configuration_explorer_test.cc",
"media_buffer_test.cc",
"media_set_audio_write_duration_test.cc",
+ "multiple_player_test.cc",
"player_create_test.cc",
"player_creation_param_helpers.cc",
"player_creation_param_helpers.h",
diff --git a/starboard/nplb/media_configuration_test.cc b/starboard/nplb/media_configuration_test.cc
index 07c5937..432b12a 100644
--- a/starboard/nplb/media_configuration_test.cc
+++ b/starboard/nplb/media_configuration_test.cc
@@ -13,7 +13,9 @@
// limitations under the License.
#include "starboard/media.h"
+
#include "starboard/nplb/performance_helpers.h"
+#include "starboard/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace starboard {
@@ -25,9 +27,13 @@
const int count_audio_output = SbMediaGetAudioOutputCount();
for (int i = 0; i < count_audio_output; ++i) {
+ constexpr int kNumberOfCalls = 100;
+ constexpr SbTime kMaxAverageTimePerCall = 500;
+
SbMediaAudioConfiguration configuration;
- TEST_PERF_FUNCWITHARGS_DEFAULT(SbMediaGetAudioConfiguration, i,
- &configuration);
+ TEST_PERF_FUNCWITHARGS_EXPLICIT(kNumberOfCalls, kMaxAverageTimePerCall,
+ SbMediaGetAudioConfiguration, i,
+ &configuration);
}
}
diff --git a/starboard/nplb/performance_helpers.h b/starboard/nplb/performance_helpers.h
index 62639df..7a9b538 100644
--- a/starboard/nplb/performance_helpers.h
+++ b/starboard/nplb/performance_helpers.h
@@ -38,11 +38,16 @@
// Measure time pre calls to |f|.
const SbTimeMonotonic time_start = SbTimeGetMonotonicNow();
+ SbLogPriority initial_log_level = starboard::logging::GetMinLogLevel();
+ starboard::logging::SetMinLogLevel(kSbLogPriorityFatal);
+
// Call |f| |count_calls| times.
for (int i = 0; i < count_calls; ++i) {
f(args...);
}
+ starboard::logging::SetMinLogLevel(initial_log_level);
+
// Measure time post calls to |f|.
const SbTimeMonotonic time_last = SbTimeGetMonotonicNow();
const SbTimeMonotonic time_delta = time_last - time_start;
diff --git a/starboard/nplb/player_write_sample_test.cc b/starboard/nplb/player_write_sample_test.cc
index 414a4ea..5bc1653 100644
--- a/starboard/nplb/player_write_sample_test.cc
+++ b/starboard/nplb/player_write_sample_test.cc
@@ -263,11 +263,15 @@
SbTime current_time_offset = 0;
int num_of_buffers_per_write =
player_fixture.ConvertDurationToAudioBufferCount(kDurationPerWrite);
+ int count = 0;
while (current_time_offset < kDurationToPlay) {
+ const SbTime kDurationToDiscard =
+ count % 2 == 0 ? kSbTimeSecond : kSbTimeMax;
+ count++;
// Discard from front.
for (int i = 0; i < kNumberOfBuffersToDiscard; i++) {
samples.AddAudioSamples(written_buffer_index, 1, current_time_offset,
- kSbTimeSecond, 0);
+ kDurationToDiscard, 0);
}
samples.AddAudioSamples(written_buffer_index, num_of_buffers_per_write);
@@ -277,7 +281,7 @@
// Discard from back.
for (int i = 0; i < kNumberOfBuffersToDiscard; i++) {
samples.AddAudioSamples(written_buffer_index, 1, current_time_offset, 0,
- kSbTimeSecond);
+ kDurationToDiscard);
}
}
samples.AddAudioEOS();
diff --git a/starboard/nplb/time_zone_get_current_test.cc b/starboard/nplb/time_zone_get_current_test.cc
index 0c136d0..0c2b3b8 100644
--- a/starboard/nplb/time_zone_get_current_test.cc
+++ b/starboard/nplb/time_zone_get_current_test.cc
@@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "starboard/extension/time_zone.h"
#include "starboard/nplb/time_constants.h"
+#include "starboard/system.h"
#include "starboard/time_zone.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -34,9 +36,64 @@
// ... and +24 hours from the Prime Meridian, inclusive
EXPECT_LE(zone, 24 * 60);
- if (zone == 0) {
- SB_LOG(WARNING) << "SbTimeZoneGetCurrent() returns 0. This is only correct "
- "if the current time zone is the same as UTC";
+ static auto const* time_zone_extension =
+ static_cast<const StarboardExtensionTimeZoneApi*>(
+ SbSystemGetExtension(kStarboardExtensionTimeZoneName));
+ if (time_zone_extension) {
+ ASSERT_STREQ(time_zone_extension->name, kStarboardExtensionTimeZoneName);
+ ASSERT_EQ(time_zone_extension->version, 1u);
+ time_zone_extension->SetTimeZone("UTC");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 0);
+
+ // Atlantic time zone, UTC−04:00
+ time_zone_extension->SetTimeZone("America/Puerto_Rico");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 240);
+
+ // Eastern time zone, UTC−05:00
+ time_zone_extension->SetTimeZone("America/New_York");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 300);
+
+ time_zone_extension->SetTimeZone("US/Eastern");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 300);
+
+ // Central time zone, UTC−06:00
+ time_zone_extension->SetTimeZone("America/Chicago");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 360);
+
+ // Mountain time zone, UTC−07:00
+ time_zone_extension->SetTimeZone("US/Mountain");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 420);
+
+ // Pacific time zone, UTC-08:00
+ time_zone_extension->SetTimeZone("US/Pacific");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 480);
+
+ // Alaska time zone, UTC-09:00
+ time_zone_extension->SetTimeZone("US/Alaska");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 540);
+
+ // Hawaii-Aleutian time zone, UTC-10:00
+ time_zone_extension->SetTimeZone("Pacific/Honolulu");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 600);
+
+ // American Samoa time zone, UTC-11:00
+ time_zone_extension->SetTimeZone("US/Samoa");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, 660);
+
+ // American Samoa time zone, UTC+10:00
+ time_zone_extension->SetTimeZone("Pacific/Guam");
+ zone = SbTimeZoneGetCurrent();
+ EXPECT_EQ(zone, -600);
}
}
diff --git a/starboard/nplb/time_zone_get_name_test.cc b/starboard/nplb/time_zone_get_name_test.cc
index a3e013c..318cbfc 100644
--- a/starboard/nplb/time_zone_get_name_test.cc
+++ b/starboard/nplb/time_zone_get_name_test.cc
@@ -12,10 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <string.h>
+
+#include "starboard/common/log.h"
+#include "starboard/extension/time_zone.h"
#include "starboard/nplb/time_constants.h"
+#include "starboard/system.h"
#include "starboard/time_zone.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using testing::AnyOf;
+using testing::MatchesRegex;
+
namespace starboard {
namespace nplb {
namespace {
@@ -41,6 +50,27 @@
// ":Pacific/Kiritimati" is the western-most timezone at UTC+14.
}
+TEST(SbTimeZoneGetNameTest, IsIANAFormat) {
+ const char* name = SbTimeZoneGetName();
+ SB_LOG(INFO) << "time zone name: " << name;
+ char cpy[100];
+ snprintf(cpy, sizeof(cpy), "%s", name);
+ char* continent = strtok(cpy, "/");
+ // The time zone ID starts with a Continent or Ocean name.
+ EXPECT_THAT(
+ continent,
+ testing::AnyOf(std::string("Asia"), std::string("America"),
+ std::string("Africa"), std::string("Europe"),
+ std::string("Australia"), std::string("Pacific"),
+ std::string("Atlantic"), std::string("Antarctica"),
+ // time zone can be "Etc/UTC" if unset(such as on
+ // CI builders), shouldn't happen in production.
+ // TODO(b/304351956): Remove Etc after fixing builders.
+ std::string("Indian"), std::string("Etc")));
+ char* city = strtok(NULL, "/");
+ EXPECT_TRUE(strlen(city) != 0);
+}
+
} // namespace
} // namespace nplb
} // namespace starboard
diff --git a/starboard/raspi/2/BUILD.gn b/starboard/raspi/2/BUILD.gn
index 92dbb16..0b84634 100644
--- a/starboard/raspi/2/BUILD.gn
+++ b/starboard/raspi/2/BUILD.gn
@@ -22,3 +22,12 @@
configs += [ "//starboard/build/config:starboard_implementation" ]
public_deps = [ "//starboard/raspi/shared:starboard_platform" ]
}
+
+if (sb_is_modular) {
+ static_library("starboard_platform_with_main") {
+ check_includes = false
+ sources = [ "//starboard/raspi/shared/main.cc" ]
+ configs += [ "//starboard/build/config:starboard_implementation" ]
+ public_deps = [ ":starboard_platform" ]
+ }
+}
diff --git a/starboard/raspi/2/args.gn b/starboard/raspi/2/args.gn
index 0b190f8..9c29777 100644
--- a/starboard/raspi/2/args.gn
+++ b/starboard/raspi/2/args.gn
@@ -15,4 +15,5 @@
target_platform = "raspi-2"
target_os = "linux"
target_cpu = "arm"
-is_clang = false
+build_with_separate_cobalt_toolchain = true
+use_asan = false
diff --git a/starboard/raspi/2/platform_configuration/BUILD.gn b/starboard/raspi/2/platform_configuration/BUILD.gn
index ef03b60..b1d0422 100644
--- a/starboard/raspi/2/platform_configuration/BUILD.gn
+++ b/starboard/raspi/2/platform_configuration/BUILD.gn
@@ -17,11 +17,16 @@
"//starboard/build/config/sabi",
"//starboard/raspi/shared/platform_configuration",
]
- cflags = [
- "-march=armv7-a",
- "-mfpu=neon-vfpv4",
- "-mfloat-abi=hard",
- "-mcpu=cortex-a8",
- "-mtune=cortex-a8",
- ]
+ if (current_toolchain != default_toolchain || !sb_is_modular) {
+ cflags = [
+ "-march=armv7-a",
+ "-mfpu=neon-vfpv4",
+ "-mfloat-abi=hard",
+ "-mcpu=cortex-a8",
+ "-mtune=cortex-a8",
+ ]
+ }
+ if (sb_is_modular && !sb_is_evergreen) {
+ defines = [ "USE_COMMON_CONTENT_DIR" ]
+ }
}
diff --git a/starboard/raspi/2/platform_configuration/configuration.gni b/starboard/raspi/2/platform_configuration/configuration.gni
index 3b23661..ad65232 100644
--- a/starboard/raspi/2/platform_configuration/configuration.gni
+++ b/starboard/raspi/2/platform_configuration/configuration.gni
@@ -13,10 +13,14 @@
# limitations under the License.
import("//starboard/raspi/shared/platform_configuration/configuration.gni")
+if (current_toolchain != default_toolchain ||
+ !build_with_separate_cobalt_toolchain) {
+ arm_float_abi = "hard"
-arm_float_abi = "hard"
+ sb_evergreen_compatible_use_libunwind = true
+ sb_is_evergreen_compatible = true
+}
-sb_evergreen_compatible_use_libunwind = true
-sb_is_evergreen_compatible = true
-
-separate_install_targets_for_bundling = true
+if (!build_with_separate_cobalt_toolchain) {
+ separate_install_targets_for_bundling = true
+}
diff --git a/starboard/raspi/2/skia/BUILD.gn b/starboard/raspi/2/skia/BUILD.gn
index bcb5b92..80f549c 100644
--- a/starboard/raspi/2/skia/BUILD.gn
+++ b/starboard/raspi/2/skia/BUILD.gn
@@ -22,3 +22,9 @@
configs += [ "//starboard/build/config:starboard_implementation" ]
public_deps = [ "//starboard/raspi/shared:starboard_platform" ]
}
+
+if (build_with_separate_cobalt_toolchain) {
+ group("starboard_platform_with_main") {
+ deps = [ "//starboard/raspi/2:starboard_platform_with_main" ]
+ }
+}
diff --git a/starboard/shared/posix/time_zone_get_name.cc b/starboard/raspi/2/skia/starboard_loader.cc
similarity index 61%
copy from starboard/shared/posix/time_zone_get_name.cc
copy to starboard/raspi/2/skia/starboard_loader.cc
index 5a1e013..065bfaa 100644
--- a/starboard/shared/posix/time_zone_get_name.cc
+++ b/starboard/raspi/2/skia/starboard_loader.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,13 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/time_zone.h"
+#include "starboard/event.h"
-#include <time.h>
-
-const char* SbTimeZoneGetName() {
- // TODO: Using tzname assumes that tzset() has been called at some
- // point. That should happen as part of Starboard's main loop initialization,
- // but that doesn't exist yet.
- return tzname[0];
+int main(int argc, char** argv) {
+ return SbRunStarboardMain(argc, argv, SbEventHandle);
}
diff --git a/starboard/raspi/2/skia/toolchain/BUILD.gn b/starboard/raspi/2/skia/toolchain/BUILD.gn
index 569a254..8524670 100644
--- a/starboard/raspi/2/skia/toolchain/BUILD.gn
+++ b/starboard/raspi/2/skia/toolchain/BUILD.gn
@@ -14,6 +14,7 @@
import("//build/config/clang/clang.gni")
import("//build/toolchain/gcc_toolchain.gni")
+import("//starboard/build/toolchain/cobalt_toolchains.gni")
import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
gcc_toolchain("target") {
@@ -23,7 +24,27 @@
ar = gcc_toolchain_ar
- tail_lib_dependencies = "-l:libpthread.so.0"
+ tail_lib_dependencies = "-l:libpthread.so.0 -l:libdl.so.2"
+
+ toolchain_args = {
+ is_clang = false
+ }
+}
+
+cobalt_clang_toolchain("cobalt") {
+ variables = {
+ native_linker_path = gcc_toolchain_cxx
+ }
+}
+
+gcc_toolchain("starboard") {
+ cc = gcc_toolchain_cc
+ cxx = gcc_toolchain_cxx
+ ld = cxx
+
+ ar = gcc_toolchain_ar
+
+ tail_lib_dependencies = "-l:libpthread.so.0 -l:libdl.so.2"
toolchain_args = {
is_clang = false
diff --git a/starboard/shared/posix/time_zone_get_name.cc b/starboard/raspi/2/starboard_loader.cc
similarity index 61%
copy from starboard/shared/posix/time_zone_get_name.cc
copy to starboard/raspi/2/starboard_loader.cc
index 5a1e013..065bfaa 100644
--- a/starboard/shared/posix/time_zone_get_name.cc
+++ b/starboard/raspi/2/starboard_loader.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -12,13 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/time_zone.h"
+#include "starboard/event.h"
-#include <time.h>
-
-const char* SbTimeZoneGetName() {
- // TODO: Using tzname assumes that tzset() has been called at some
- // point. That should happen as part of Starboard's main loop initialization,
- // but that doesn't exist yet.
- return tzname[0];
+int main(int argc, char** argv) {
+ return SbRunStarboardMain(argc, argv, SbEventHandle);
}
diff --git a/starboard/raspi/2/toolchain/BUILD.gn b/starboard/raspi/2/toolchain/BUILD.gn
index dcce760..2c49b20 100644
--- a/starboard/raspi/2/toolchain/BUILD.gn
+++ b/starboard/raspi/2/toolchain/BUILD.gn
@@ -13,8 +13,30 @@
# limitations under the License.
import("//build/toolchain/gcc_toolchain.gni")
+import("//starboard/build/toolchain/cobalt_toolchains.gni")
import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
+cobalt_clang_toolchain("cobalt") {
+ variables = {
+ native_linker_path = gcc_toolchain_cxx
+ }
+}
+
+gcc_toolchain("starboard") {
+ cc = gcc_toolchain_cc
+ cxx = gcc_toolchain_cxx
+ ld = cxx
+
+ # We use whatever 'ar' resolves to.
+ ar = gcc_toolchain_ar
+
+ tail_lib_dependencies = "-l:libpthread.so.0 -l:libdl.so.2"
+
+ toolchain_args = {
+ is_clang = false
+ }
+}
+
gcc_toolchain("target") {
cc = gcc_toolchain_cc
cxx = gcc_toolchain_cxx
diff --git a/starboard/raspi/shared/BUILD.gn b/starboard/raspi/shared/BUILD.gn
index a81b614..39ca20e 100644
--- a/starboard/raspi/shared/BUILD.gn
+++ b/starboard/raspi/shared/BUILD.gn
@@ -35,13 +35,14 @@
"//starboard/linux/shared/system_get_connection_type.cc",
"//starboard/linux/shared/system_get_path.cc",
"//starboard/linux/shared/system_has_capability.cc",
+ "//starboard/linux/shared/time_zone.cc",
+ "//starboard/linux/shared/time_zone.h",
"//starboard/raspi/shared/application_dispmanx.cc",
"//starboard/raspi/shared/audio_sink_type_dispatcher.cc",
"//starboard/raspi/shared/dispmanx_util.cc",
"//starboard/raspi/shared/dispmanx_util.h",
"//starboard/raspi/shared/graphics.cc",
"//starboard/raspi/shared/graphics.h",
- "//starboard/raspi/shared/main.cc",
"//starboard/raspi/shared/media_is_video_supported.cc",
"//starboard/raspi/shared/open_max/decode_target_create.cc",
"//starboard/raspi/shared/open_max/decode_target_create.h",
@@ -112,6 +113,7 @@
"//starboard/shared/linux/thread_get_id.cc",
"//starboard/shared/linux/thread_get_name.cc",
"//starboard/shared/linux/thread_set_name.cc",
+ "//starboard/shared/linux/time_zone_get_name.cc",
"//starboard/shared/nouser/user_get_current.cc",
"//starboard/shared/nouser/user_get_property.cc",
"//starboard/shared/nouser/user_get_signed_in.cc",
@@ -188,7 +190,6 @@
"//starboard/shared/posix/time_get_now.cc",
"//starboard/shared/posix/time_is_time_thread_now_supported.cc",
"//starboard/shared/posix/time_zone_get_current.cc",
- "//starboard/shared/posix/time_zone_get_name.cc",
"//starboard/shared/pthread/condition_variable_broadcast.cc",
"//starboard/shared/pthread/condition_variable_create.cc",
"//starboard/shared/pthread/condition_variable_destroy.cc",
@@ -333,10 +334,15 @@
"//starboard/shared/stub/window_set_on_screen_keyboard_keep_focus.cc",
"//starboard/shared/stub/window_show_on_screen_keyboard.cc",
"//starboard/shared/stub/window_update_on_screen_keyboard_suggestions.cc",
+ "run_starboard_main.cc",
]
sources += common_player_sources
+ if (!sb_is_modular) {
+ sources += [ "//starboard/raspi/shared/main.cc" ]
+ }
+
configs += [ "//starboard/build/config:starboard_implementation" ]
public_deps = [
@@ -378,17 +384,20 @@
public_deps = [ "//starboard/elf_loader:evergreen_info" ]
}
-target(gtest_target_type, "starboard_platform_tests") {
- testonly = true
+if (current_toolchain == starboard_toolchain) {
+ target(starboard_level_gtest_target_type, "starboard_platform_tests") {
+ build_loader = false
+ testonly = true
- sources = player_tests_sources + [ "//starboard/common/test_main.cc" ]
+ sources = player_tests_sources + [ "//starboard/common/test_main.cc" ]
- configs += [ "//starboard/build/config:starboard_implementation" ]
+ configs += [ "//starboard/build/config:starboard_implementation" ]
- deps = [
- "//starboard",
- "//starboard/shared/starboard/player/filter/testing:test_util",
- "//testing/gmock",
- "//testing/gtest",
- ]
+ deps = [
+ "//starboard:starboard_with_main",
+ "//starboard/shared/starboard/player/filter/testing:test_util",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+ }
}
diff --git a/starboard/raspi/shared/install_target.gni b/starboard/raspi/shared/install_target.gni
index 90ac725..7413840 100644
--- a/starboard/raspi/shared/install_target.gni
+++ b/starboard/raspi/shared/install_target.gni
@@ -21,7 +21,7 @@
# subdir and install content subdir.
if (invoker.type == "executable") {
# install_subdir = "bin"
- install_subdir = ""
+ install_subdir = installable_target_name
source_name = installable_target_name
} else if (invoker.type == "shared_library") {
install_subdir = "lib"
@@ -29,28 +29,37 @@
} else {
assert(false, "You can only install an executable or shared library.")
}
+ output = "$sb_install_output_dir/$install_subdir/$source_name"
+ input = "$root_out_dir/$source_name"
- action(target_name) {
- forward_variables_from(invoker, [ "testonly" ])
+ if (is_gold || cobalt_fastbuild) {
+ action(target_name) {
+ forward_variables_from(invoker, [ "testonly" ])
- script = "//starboard/build/run_bash.py"
+ script = "//starboard/build/run_bash.py"
- strip_executable = gcc_toolchain_strip
- inputs = [
- strip_executable,
- "$root_out_dir/$source_name",
- ]
+ strip_executable = gcc_toolchain_strip
+ inputs = [ input ]
- deps = invoker.deps
- deps += [ ":$installable_target_name" ]
+ deps = invoker.deps
+ deps += [ invoker.installable_target_dep ]
- outputs = [ "$sb_install_output_dir/$install_subdir/$installable_target_name/$source_name" ]
+ outputs = [ output ]
- args = [
- rebase_path(strip_executable, root_build_dir),
- "-o",
- rebase_path(outputs[0], root_out_dir),
- rebase_path("$root_out_dir/$source_name", root_out_dir),
- ]
+ args = [
+ rebase_path(strip_executable, root_build_dir),
+ "-o",
+ rebase_path(outputs[0], root_build_dir),
+ rebase_path(inputs[0], root_build_dir),
+ ]
+ }
+ } else {
+ copy(target_name) {
+ forward_variables_from(invoker, [ "testonly" ])
+ sources = [ input ]
+ outputs = [ output ]
+ deps = invoker.deps
+ deps += [ invoker.installable_target_dep ]
+ }
}
}
diff --git a/starboard/raspi/shared/launcher.py b/starboard/raspi/shared/launcher.py
index 6aad68d..455467b 100644
--- a/starboard/raspi/shared/launcher.py
+++ b/starboard/raspi/shared/launcher.py
@@ -30,6 +30,10 @@
from starboard.raspi.shared import retry
+class TargetPathError(ValueError):
+ pass
+
+
# pylint: disable=unused-argument
def _sigint_or_sigterm_handler(signum, frame):
"""Clean up and exit with status |signum|.
@@ -83,8 +87,8 @@
_PROMPT_WAIT_MAX_RETRIES = 5
# Wait up to 10 seconds for the password prompt from the raspi
_PEXPECT_PASSWORD_TIMEOUT_MAX_RETRIES = 10
- # Wait up to 900 seconds for new output from the raspi
- _PEXPECT_READLINE_TIMEOUT_MAX_RETRIES = 900
+ # Wait up to 600 seconds for new output from the raspi
+ _PEXPECT_READLINE_TIMEOUT_MAX_RETRIES = 600
# Delay between subsequent SSH commands
_INTER_COMMAND_DELAY_SECONDS = 1.5
@@ -126,31 +130,43 @@
self.last_run_pexpect_cmd = ''
+ def _GetAndCheckTestFile(self, target_name):
+ # TODO(b/218889313): This should reference the bin/ subdir when that's
+ # used.
+ test_dir = os.path.join(self.out_directory, 'install', target_name)
+ test_file = target_name
+ test_path = os.path.join(test_dir, test_file)
+
+ if not os.path.isfile(test_path):
+ raise TargetPathError(f'TargetPath ({test_path}) must be a file.')
+ return test_file
+
+ def _GetAndCheckTestFileWithFallback(self):
+ try:
+ return self._GetAndCheckTestFile(self.target_name + '_loader')
+ except TargetPathError:
+ # For starboard level test targets like player_filter_tests built as an
+ # executable, return the target name.
+ return self._GetAndCheckTestFile(self.target_name)
+
def _InitPexpectCommands(self):
"""Initializes all of the pexpect commands needed for running the test."""
# Ensure no trailing slashes
self.out_directory = self.out_directory.rstrip('/')
- # TODO(b/218889313): This should reference the bin/ subdir when that's
- # used.
- test_dir = os.path.join(self.out_directory, 'install', self.target_name)
- test_file = self.target_name
-
- test_path = os.path.join(test_dir, test_file)
- if not os.path.isfile(test_path):
- raise ValueError(f'TargetPath ({test_path}) must be a file.')
+ test_file = self._GetAndCheckTestFileWithFallback()
raspi_user_hostname = Launcher._RASPI_USERNAME + '@' + self.device_id
# Use the basename of the out directory as a common directory on the device
# so content can be reused for several targets w/o re-syncing for each one.
raspi_test_dir = os.path.basename(self.out_directory)
- raspi_test_path = os.path.join(raspi_test_dir, test_file)
+ raspi_test_path = os.path.join(raspi_test_dir, test_file, test_file)
# rsync command setup
- options = '-avzLhc'
- source = test_dir + '/'
+ options = '-avzLh'
+ source = os.path.join(self.out_directory, 'install') + '/'
destination = f'{raspi_user_hostname}:~/{raspi_test_dir}/'
self.rsync_command = 'rsync ' + options + ' ' + source + ' ' + destination
@@ -242,28 +258,25 @@
def _PexpectReadLines(self):
"""Reads all lines from the pexpect process."""
- # pylint: disable=unnecessary-lambda
- @retry.retry(
- exceptions=Launcher._RETRY_EXCEPTIONS,
- retries=Launcher._PEXPECT_READLINE_TIMEOUT_MAX_RETRIES,
- backoff=lambda: self.shutdown_initiated.is_set(),
- wrap_exceptions=False)
- def _readloop():
- while True:
- # Sanitize the line to remove ansi color codes.
- line = Launcher._PEXPECT_SANITIZE_LINE_RE.sub(
- '', self.pexpect_process.readline())
- self.output_file.flush()
- if not line:
- return
- # Check for the test complete tag. It will be followed by either a
- # success or failure tag.
- if line.startswith(self.test_complete_tag):
- if line.find(self.test_success_tag) != -1:
- self.return_value = 0
- return
-
- _readloop()
+ while True:
+ # pylint: disable=unnecessary-lambda
+ line = retry.with_retry(
+ self.pexpect_process.readline,
+ exceptions=Launcher._RETRY_EXCEPTIONS,
+ retries=Launcher._PEXPECT_READLINE_TIMEOUT_MAX_RETRIES,
+ backoff=lambda: self.shutdown_initiated.is_set(),
+ wrap_exceptions=False)
+ # Sanitize the line to remove ansi color codes.
+ line = Launcher._PEXPECT_SANITIZE_LINE_RE.sub('', line)
+ self.output_file.flush()
+ if not line:
+ return
+ # Check for the test complete tag. It will be followed by either a
+ # success or failure tag.
+ if line.startswith(self.test_complete_tag):
+ if line.find(self.test_success_tag) != -1:
+ self.return_value = 0
+ return
def _Sleep(self, val):
self._PexpectSendLine(f'sleep {val};echo {Launcher._SSH_SLEEP_SIGNAL}')
diff --git a/starboard/raspi/shared/main.cc b/starboard/raspi/shared/main.cc
index e0aa5bf..b07e33e 100644
--- a/starboard/raspi/shared/main.cc
+++ b/starboard/raspi/shared/main.cc
@@ -49,7 +49,6 @@
: starboard::common::GetCACertificatesPath(evergreen_content_path);
if (ca_certificates_path.empty()) {
SB_LOG(ERROR) << "Failed to get CA certificates path";
- return 1;
}
bool start_handler_at_crash =
@@ -72,11 +71,3 @@
starboard::shared::signal::UninstallCrashSignalHandlers();
return result;
}
-
-#if SB_API_VERSION >= 15
-int SbRunStarboardMain(int argc, char** argv, SbEventHandleCallback callback) {
- starboard::raspi::shared::ApplicationDispmanx application(callback);
- int result = application.Run(argc, argv);
- return result;
-}
-#endif // SB_API_VERSION >= 15
diff --git a/starboard/raspi/shared/platform_configuration/BUILD.gn b/starboard/raspi/shared/platform_configuration/BUILD.gn
index b1c930e..e2c7f03 100644
--- a/starboard/raspi/shared/platform_configuration/BUILD.gn
+++ b/starboard/raspi/shared/platform_configuration/BUILD.gn
@@ -16,6 +16,39 @@
raspi_home = getenv("RASPI_HOME")
}
+config("common_flags") {
+ ldflags = [
+ "--sysroot=$raspi_home/busterroot",
+
+ # This is a quirk of Raspbian, these are required to link any GL-related
+ # libraries.
+ "-L$raspi_home/busterroot/opt/vc/lib",
+ "-Wl,-rpath=$raspi_home/busterroot/opt/vc/lib",
+ "-L$raspi_home/busterroot/usr/lib/arm-linux-gnueabihf",
+ "-Wl,-rpath=$raspi_home/busterroot/usr/lib/arm-linux-gnueabihf",
+ "-L$raspi_home/busterroot/lib/arm-linux-gnueabihf",
+ "-Wl,-rpath=$raspi_home/busterroot/lib/arm-linux-gnueabihf",
+
+ # Cleanup unused sections
+ "-Wl,-gc-sections",
+ "-Wl,--unresolved-symbols=ignore-all",
+ ]
+ libs = [
+ "asound",
+ "rt",
+ "openmaxil",
+ "bcm_host",
+ "vcos",
+ "vchiq_arm",
+ "brcmGLESv2",
+ "brcmEGL",
+
+ # Static libs must be last, to avoid __dlopen linker errors
+ "EGL_static",
+ "GLESv2_static",
+ ]
+}
+
config("compiler_flags") {
cflags = []
cflags_c = []
@@ -38,23 +71,6 @@
cflags += [ "-Wno-unused-but-set-variable" ]
}
- ldflags += [
- "--sysroot=$raspi_home/busterroot",
-
- # This is a quirk of Raspbian, these are required to link any GL-related
- # libraries.
- "-L$raspi_home/busterroot/opt/vc/lib",
- "-Wl,-rpath=$raspi_home/busterroot/opt/vc/lib",
- "-L$raspi_home/busterroot/usr/lib/arm-linux-gnueabihf",
- "-Wl,-rpath=$raspi_home/busterroot/usr/lib/arm-linux-gnueabihf",
- "-L$raspi_home/busterroot/lib/arm-linux-gnueabihf",
- "-Wl,-rpath=$raspi_home/busterroot/lib/arm-linux-gnueabihf",
-
- # Cleanup unused sections
- "-Wl,-gc-sections",
- "-Wl,--unresolved-symbols=ignore-in-shared-libs",
- ]
-
cflags += [
# Generated by code in the raspi/shared/open_max.
"-Wno-sign-compare",
@@ -121,27 +137,16 @@
}
config("platform_configuration") {
- libs = [
- "asound",
- "dl",
- "pthread",
- "rt",
- "openmaxil",
- "bcm_host",
- "vcos",
- "vchiq_arm",
- "brcmGLESv2",
- "brcmEGL",
+ configs = [ ":common_flags" ]
+ if (current_toolchain == default_toolchain && sb_is_modular) {
+ configs += [ "//starboard/evergreen/arm/hardfp/platform_configuration" ]
+ } else {
+ configs +=
+ [ "//starboard/raspi/shared/platform_configuration:compiler_flags" ]
- # Static libs must be last, to avoid __dlopen linker errors
- "EGL_static",
- "GLESv2_static",
- ]
-
- configs = [ "//starboard/raspi/shared/platform_configuration:compiler_flags" ]
-
- if (is_debug || is_devel) {
- configs += [ "//build/config/compiler:rtti" ]
+ if (is_debug || is_devel) {
+ configs += [ "//build/config/compiler:rtti" ]
+ }
}
}
diff --git a/starboard/raspi/shared/platform_configuration/configuration.gni b/starboard/raspi/shared/platform_configuration/configuration.gni
index bd06d18..b1e053e 100644
--- a/starboard/raspi/shared/platform_configuration/configuration.gni
+++ b/starboard/raspi/shared/platform_configuration/configuration.gni
@@ -12,26 +12,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import("//starboard/build/config/base_configuration.gni")
+if (current_toolchain == default_toolchain &&
+ build_with_separate_cobalt_toolchain) {
+ import(
+ "//starboard/evergreen/arm/softfp/platform_configuration/configuration.gni")
-arm_float_abi = "hard"
+ platform_tests_path = "//starboard/raspi/shared:starboard_platform_tests_install($starboard_toolchain)"
+ cobalt_font_package = "standard"
+} else {
+ import("//starboard/build/config/base_configuration.gni")
-sb_static_contents_output_data_dir = "$root_out_dir/content"
+ arm_float_abi = "hard"
-no_pedantic_warnings_config_path =
- "//starboard/raspi/shared/platform_configuration:no_pedantic_warnings"
-pedantic_warnings_config_path =
- "//starboard/raspi/shared/platform_configuration:pedantic_warnings"
-sabi_path = "//starboard/sabi/arm/hardfp/sabi-v$sb_api_version.json"
+ sb_static_contents_output_data_dir = "$root_out_dir/content"
-platform_tests_path = "//starboard/raspi/shared:starboard_platform_tests"
+ no_pedantic_warnings_config_path =
+ "//starboard/raspi/shared/platform_configuration:no_pedantic_warnings"
+ pedantic_warnings_config_path =
+ "//starboard/raspi/shared/platform_configuration:pedantic_warnings"
+ sabi_path = "//starboard/sabi/arm/hardfp/sabi-v$sb_api_version.json"
+ platform_tests_path = "//starboard/raspi/shared:starboard_platform_tests_install($starboard_toolchain)"
+
+ speed_config_path = "//starboard/raspi/shared/platform_configuration:speed"
+ size_config_path = "//starboard/raspi/shared/platform_configuration:size"
+
+ # TODO(b/219073252): Enable -fno-exceptions and don't mix it with -fexceptions.
+ enable_exceptions_override = true
+
+ v8_enable_webassembly = true
+
+ is_raspi = true
+}
install_target_path = "//starboard/raspi/shared/install_target.gni"
-
-speed_config_path = "//starboard/raspi/shared/platform_configuration:speed"
-size_config_path = "//starboard/raspi/shared/platform_configuration:size"
-
-# TODO(b/219073252): Enable -fno-exceptions and don't mix it with -fexceptions.
-enable_exceptions_override = true
-
-v8_enable_webassembly = true
diff --git a/starboard/raspi/shared/run_starboard_main.cc b/starboard/raspi/shared/run_starboard_main.cc
new file mode 100644
index 0000000..9e957af
--- /dev/null
+++ b/starboard/raspi/shared/run_starboard_main.cc
@@ -0,0 +1,24 @@
+// Copyright 2016 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/event.h"
+#include "starboard/raspi/shared/application_dispmanx.h"
+
+#if SB_API_VERSION >= 15
+int SbRunStarboardMain(int argc, char** argv, SbEventHandleCallback callback) {
+ starboard::raspi::shared::ApplicationDispmanx application(callback);
+ int result = application.Run(argc, argv);
+ return result;
+}
+#endif // SB_API_VERSION >= 15
diff --git a/starboard/raspi/shared/system_get_extensions.cc b/starboard/raspi/shared/system_get_extensions.cc
index bd7854d..511fb79 100644
--- a/starboard/raspi/shared/system_get_extensions.cc
+++ b/starboard/raspi/shared/system_get_extensions.cc
@@ -18,11 +18,13 @@
#include "starboard/extension/configuration.h"
#include "starboard/extension/crash_handler.h"
#include "starboard/extension/graphics.h"
+#include "starboard/extension/time_zone.h"
#include "starboard/shared/starboard/crash_handler.h"
#if SB_IS(EVERGREEN_COMPATIBLE)
#include "starboard/elf_loader/evergreen_config.h"
#endif
+#include "starboard/linux/shared/time_zone.h"
#include "starboard/raspi/shared/configuration.h"
#include "starboard/raspi/shared/graphics.h"
@@ -48,5 +50,8 @@
if (strcmp(name, kCobaltExtensionCrashHandlerName) == 0) {
return starboard::common::GetCrashHandlerApi();
}
+ if (strcmp(name, kStarboardExtensionTimeZoneName) == 0) {
+ return starboard::shared::GetTimeZoneApi();
+ }
return NULL;
}
diff --git a/starboard/raspi/shared/test_filters.py b/starboard/raspi/shared/test_filters.py
index d031a5d..21a428f 100644
--- a/starboard/raspi/shared/test_filters.py
+++ b/starboard/raspi/shared/test_filters.py
@@ -16,6 +16,7 @@
from starboard.tools.testing import test_filter
# pylint: disable=line-too-long
+
_FILTERED_TESTS = {
'nplb': [
'SbAudioSinkTest.*',
diff --git a/starboard/shared/ffmpeg/BUILD.gn b/starboard/shared/ffmpeg/BUILD.gn
index ec30571..5ad4693 100644
--- a/starboard/shared/ffmpeg/BUILD.gn
+++ b/starboard/shared/ffmpeg/BUILD.gn
@@ -18,18 +18,29 @@
"ffmpeg_common.h",
"ffmpeg_demuxer_impl.cc",
"ffmpeg_demuxer_impl.h",
- "ffmpeg_video_decoder_impl.cc",
- "ffmpeg_video_decoder_impl.h",
]
+if (!defined(is_raspi)) {
+ ffmpeg_specialization_sources += [
+ # TODO(b/291783511): This code is unimplemented on certain platforms.
+ "ffmpeg_video_decoder_impl.cc",
+ "ffmpeg_video_decoder_impl.h",
+ ]
+}
+
static_library("ffmpeg_dynamic_load") {
check_includes = false
sources = [
"ffmpeg_dynamic_load_audio_decoder_impl.cc",
"ffmpeg_dynamic_load_demuxer_impl.cc",
"ffmpeg_dynamic_load_dispatch_impl.cc",
- "ffmpeg_dynamic_load_video_decoder_impl.cc",
]
+ if (!defined(is_raspi)) {
+ sources += [
+ # TODO(b/291783511): This code is unimplemented on certain platforms.
+ "ffmpeg_dynamic_load_video_decoder_impl.cc",
+ ]
+ }
public_deps = [
":ffmpeg.57.107.100",
":ffmpeg.58.35.100",
diff --git a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
index 4cf444b..1086f3f 100644
--- a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
@@ -140,14 +140,17 @@
SB_DCHECK(output_cb_);
SB_CHECK(codec_context_ != NULL);
- const auto& input_buffer = input_buffers[0];
-
Schedule(consumed_cb);
+ if (input_buffers.empty() || !input_buffers[0]) {
+ SB_LOG(ERROR) << "No input buffer to decode.";
+ return;
+ }
if (stream_ended_) {
SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
return;
}
+ const auto& input_buffer = input_buffers[0];
AVPacket packet;
ffmpeg_->av_init_packet(&packet);
diff --git a/starboard/shared/libfdkaac/fdk_aac_audio_decoder.cc b/starboard/shared/libfdkaac/fdk_aac_audio_decoder.cc
index 693c133..1df5b8b 100644
--- a/starboard/shared/libfdkaac/fdk_aac_audio_decoder.cc
+++ b/starboard/shared/libfdkaac/fdk_aac_audio_decoder.cc
@@ -52,6 +52,10 @@
SB_DCHECK(output_cb_);
SB_DCHECK(decoder_ != NULL);
+ if (input_buffers.empty() || !input_buffers[0]) {
+ SB_LOG(ERROR) << "No input buffer to decode.";
+ return;
+ }
if (stream_ended_) {
SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
return;
diff --git a/starboard/shared/linux/time_zone_get_name.cc b/starboard/shared/linux/time_zone_get_name.cc
new file mode 100644
index 0000000..4b1819e
--- /dev/null
+++ b/starboard/shared/linux/time_zone_get_name.cc
@@ -0,0 +1,75 @@
+// Copyright 2015 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 <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "starboard/common/log.h"
+#include "starboard/time_zone.h"
+
+#define TZDEFAULT "/etc/localtime"
+#define TZZONEINFOTAIL "/zoneinfo/"
+#define isNonDigit(ch) (ch < '0' || '9' < ch)
+
+static char gTimeZoneBuffer[PATH_MAX];
+static char* gTimeZoneBufferPtr = NULL;
+
+static bool isValidOlsonID(const char* id) {
+ int32_t idx = 0;
+
+ /* Determine if this is something like Iceland (Olson ID)
+ or AST4ADT (non-Olson ID) */
+ while (id[idx] && isNonDigit(id[idx]) && id[idx] != ',') {
+ idx++;
+ }
+
+ /* If we went through the whole string, then it might be okay.
+ The timezone is sometimes set to "CST-7CDT", "CST6CDT5,J129,J131/19:30",
+ "GRNLNDST3GRNLNDDT" or similar, so we cannot use it.
+ The rest of the time it could be an Olson ID. George */
+ return static_cast<bool>(id[idx] == 0 || strcmp(id, "PST8PDT") == 0 ||
+ strcmp(id, "MST7MDT") == 0 ||
+ strcmp(id, "CST6CDT") == 0 ||
+ strcmp(id, "EST5EDT") == 0);
+}
+
+// Similar to how ICU::putil.cpp gets IANA(Olsen) timezone ID.
+const char* SbTimeZoneGetName() {
+ /*
+ This is a trick to look at the name of the link to get the Olson ID
+ because the tzfile contents is underspecified.
+ This isn't guaranteed to work because it may not be a symlink.
+ But this is production-tested solution for most versions of Linux.
+ */
+
+ if (gTimeZoneBufferPtr == NULL) {
+ int32_t ret = (int32_t)readlink(TZDEFAULT, gTimeZoneBuffer,
+ sizeof(gTimeZoneBuffer) - 1);
+ if (0 < ret) {
+ int32_t tzZoneInfoTailLen = strlen(TZZONEINFOTAIL);
+ gTimeZoneBuffer[ret] = 0;
+ char* tzZoneInfoTailPtr = strstr(gTimeZoneBuffer, TZZONEINFOTAIL);
+
+ if (tzZoneInfoTailPtr != NULL &&
+ isValidOlsonID(tzZoneInfoTailPtr + tzZoneInfoTailLen)) {
+ return (gTimeZoneBufferPtr = tzZoneInfoTailPtr + tzZoneInfoTailLen);
+ }
+ }
+ SB_NOTREACHED();
+ return "";
+ } else {
+ return gTimeZoneBufferPtr;
+ }
+}
diff --git a/starboard/shared/starboard/media/codec_util.cc b/starboard/shared/starboard/media/codec_util.cc
index 1c2c1b3..ff3b325 100644
--- a/starboard/shared/starboard/media/codec_util.cc
+++ b/starboard/shared/starboard/media/codec_util.cc
@@ -72,7 +72,8 @@
return !(*this == that);
}
-SbMediaAudioCodec GetAudioCodecFromString(const char* codec) {
+SbMediaAudioCodec GetAudioCodecFromString(const char* codec,
+ const char* subtype) {
#if SB_API_VERSION < 15
const bool kCheckAc3Audio = kSbHasAc3Audio;
#else
@@ -105,7 +106,9 @@
}
// For WAV, the "codecs" parameter of a MIME type refers to the WAVE format
// ID, where 1 represents PCM: https://datatracker.ietf.org/doc/html/rfc2361
- if (strcmp(codec, "1") == 0) {
+ const bool is_wav =
+ strcmp(subtype, "wav") == 0 || strcmp(subtype, "wave") == 0;
+ if (is_wav && strcmp(codec, "1") == 0) {
return kSbMediaAudioCodecPcm;
}
#endif // SB_API_VERSION >= 14
diff --git a/starboard/shared/starboard/media/codec_util.h b/starboard/shared/starboard/media/codec_util.h
index 11f3102..b6f6b61 100644
--- a/starboard/shared/starboard/media/codec_util.h
+++ b/starboard/shared/starboard/media/codec_util.h
@@ -64,7 +64,11 @@
optional<AvcParameterSets> avc_parameter_sets_;
};
-SbMediaAudioCodec GetAudioCodecFromString(const char* codec);
+// Attempts to determine an SbMediaAudioCodec from |codec|, returning
+// kSbMediaAudioCodecNone if no match is found. |subtype| may be checked in
+// cases of ambiguous codec strings.
+SbMediaAudioCodec GetAudioCodecFromString(const char* codec,
+ const char* subtype);
} // namespace media
} // namespace starboard
diff --git a/starboard/shared/starboard/media/codec_util_test.cc b/starboard/shared/starboard/media/codec_util_test.cc
index 2a334be..a57d769 100644
--- a/starboard/shared/starboard/media/codec_util_test.cc
+++ b/starboard/shared/starboard/media/codec_util_test.cc
@@ -16,6 +16,7 @@
#include <vector>
+#include "starboard/media.h"
#include "starboard/shared/starboard/media/avc_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -214,6 +215,60 @@
EXPECT_FALSE(config_h264 == config_vp9);
}
+TEST(CodecUtilTest, ParsesAacCodecs) {
+ EXPECT_EQ(GetAudioCodecFromString("mp4a.40.2", ""), kSbMediaAudioCodecAac);
+ EXPECT_EQ(GetAudioCodecFromString("mp4a.40.5", ""), kSbMediaAudioCodecAac);
+}
+
+#if SB_API_VERSION < 15
+const bool kCheckAc3Audio = kSbHasAc3Audio;
+#else
+const bool kCheckAc3Audio = true;
+#endif // SB_API_VERSION < 15
+
+TEST(CodecUtilTest, ParsesAc3CodecIfEnabled) {
+ EXPECT_EQ(GetAudioCodecFromString("ac-3", ""),
+ kCheckAc3Audio ? kSbMediaAudioCodecAc3 : kSbMediaAudioCodecNone);
+}
+
+TEST(CodecUtilTest, ParsesEac3CodecIfEnabled) {
+ EXPECT_EQ(GetAudioCodecFromString("ec-3", ""),
+ kCheckAc3Audio ? kSbMediaAudioCodecEac3 : kSbMediaAudioCodecNone);
+}
+
+TEST(CodecUtilTest, ParsesOpusCodec) {
+ EXPECT_EQ(GetAudioCodecFromString("opus", ""), kSbMediaAudioCodecOpus);
+}
+
+TEST(CodecUtilTest, ParsesVorbisCodec) {
+ EXPECT_EQ(GetAudioCodecFromString("vorbis", ""), kSbMediaAudioCodecVorbis);
+}
+
+#if SB_API_VERSION >= 14
+TEST(CodecUtilTest, ParsesMp3Codecs) {
+ EXPECT_EQ(GetAudioCodecFromString("mp3", ""), kSbMediaAudioCodecMp3);
+ EXPECT_EQ(GetAudioCodecFromString("mp4a.69", ""), kSbMediaAudioCodecMp3);
+ EXPECT_EQ(GetAudioCodecFromString("mp4a.6B", ""), kSbMediaAudioCodecMp3);
+}
+
+TEST(CodecUtilTest, ParsesFlacCodec) {
+ EXPECT_EQ(GetAudioCodecFromString("flac", ""), kSbMediaAudioCodecFlac);
+}
+
+TEST(CodecUtilTest, ParsesPcmCodecForWav) {
+ EXPECT_EQ(GetAudioCodecFromString("1", "wav"), kSbMediaAudioCodecPcm);
+ EXPECT_EQ(GetAudioCodecFromString("1", "wave"), kSbMediaAudioCodecPcm);
+}
+
+TEST(CodecUtilTest, DoesNotParse1AsPcmForNonWavSubtypes) {
+ EXPECT_EQ(GetAudioCodecFromString("1", ""), kSbMediaAudioCodecNone);
+ EXPECT_EQ(GetAudioCodecFromString("1", "mp4"), kSbMediaAudioCodecNone);
+ EXPECT_EQ(GetAudioCodecFromString("1", "mp3"), kSbMediaAudioCodecNone);
+ EXPECT_EQ(GetAudioCodecFromString("1", "mpeg"), kSbMediaAudioCodecNone);
+ EXPECT_EQ(GetAudioCodecFromString("1", "webm"), kSbMediaAudioCodecNone);
+}
+#endif // SB_API_VERSION >= 14
+
} // namespace
} // namespace media
} // namespace starboard
diff --git a/starboard/shared/starboard/media/media_tests.gni b/starboard/shared/starboard/media/media_tests.gni
index 4777863..269a60c 100644
--- a/starboard/shared/starboard/media/media_tests.gni
+++ b/starboard/shared/starboard/media/media_tests.gni
@@ -18,6 +18,7 @@
"//starboard/shared/starboard/media/media_util_test.cc",
"//starboard/shared/starboard/media/mime_type_test.cc",
"//starboard/shared/starboard/media/mime_util_test.cc",
+ "//starboard/shared/starboard/media/parsed_mime_info_test.cc",
"//starboard/shared/starboard/media/video_capabilities_test.cc",
"//starboard/shared/starboard/media/vp9_util_test.cc",
]
diff --git a/starboard/shared/starboard/media/mime_util.cc b/starboard/shared/starboard/media/mime_util.cc
index 0ef4b2d..52650f8 100644
--- a/starboard/shared/starboard/media/mime_util.cc
+++ b/starboard/shared/starboard/media/mime_util.cc
@@ -37,23 +37,19 @@
// Use SbMediaGetAudioConfiguration() to check if the platform can support
// |channels|.
bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels) {
- // TODO(b/284140486, b/297426689): Consider removing the call to
- // `SbMediaGetAudioOutputCount()` completely as the loop will be terminated
- // once `SbMediaGetAudioConfiguration()` returns false.
- int count = SbMediaGetAudioOutputCount();
-
- for (int output_index = 0; output_index < count; ++output_index) {
- SbMediaAudioConfiguration configuration;
- if (!SbMediaGetAudioConfiguration(output_index, &configuration)) {
- break;
- }
-
+ // SbPlayerBridge::GetAudioConfigurations() reads up to 32 configurations. The
+ // limit here is to avoid infinite loop and also match
+ // SbPlayerBridge::GetAudioConfigurations().
+ const int kMaxAudioConfigurations = 32;
+ int output_index = 0;
+ SbMediaAudioConfiguration configuration;
+ while (output_index < kMaxAudioConfigurations &&
+ SbMediaGetAudioConfiguration(output_index++, &configuration)) {
if (configuration.coding_type == coding_type &&
configuration.number_of_channels >= channels) {
return true;
}
}
-
return false;
}
diff --git a/starboard/shared/starboard/media/parsed_mime_info.cc b/starboard/shared/starboard/media/parsed_mime_info.cc
index e61cb84..ae2e147 100644
--- a/starboard/shared/starboard/media/parsed_mime_info.cc
+++ b/starboard/shared/starboard/media/parsed_mime_info.cc
@@ -101,7 +101,8 @@
SB_DCHECK(mime_type_.is_valid());
SB_DCHECK(!has_audio_info());
- SbMediaAudioCodec audio_codec = GetAudioCodecFromString(codec.c_str());
+ SbMediaAudioCodec audio_codec =
+ GetAudioCodecFromString(codec.c_str(), mime_type_.subtype().c_str());
if (audio_codec == kSbMediaAudioCodecNone) {
return false;
}
diff --git a/starboard/shared/starboard/media/parsed_mime_info_test.cc b/starboard/shared/starboard/media/parsed_mime_info_test.cc
new file mode 100644
index 0000000..94c1f7b
--- /dev/null
+++ b/starboard/shared/starboard/media/parsed_mime_info_test.cc
@@ -0,0 +1,98 @@
+// 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/shared/starboard/media/parsed_mime_info.h"
+
+#include "starboard/media.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace media {
+namespace {
+
+#if SB_API_VERSION < 15
+const bool kCheckAc3Audio = kSbHasAc3Audio;
+#else
+const bool kCheckAc3Audio = true;
+#endif // SB_API_VERSION < 15
+
+TEST(ParsedMimeInfoTest, ParsesAacLowComplexityCodec) {
+ ParsedMimeInfo mime_info("audio/mp4; codecs=\"mp4a.40.2\"");
+ ASSERT_TRUE(mime_info.has_audio_info());
+ EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecAac);
+}
+
+TEST(ParsedMimeInfoTest, ParsesAacHighEfficiencyCodec) {
+ ParsedMimeInfo mime_info("audio/mp4; codecs=\"mp4a.40.5\"");
+ ASSERT_TRUE(mime_info.has_audio_info());
+ EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecAac);
+}
+
+TEST(ParsedMimeInfoTest, ParsesAc3Codec) {
+ ParsedMimeInfo mime_info("audio/mp4; codecs=\"ac-3\"");
+ ASSERT_EQ(mime_info.has_audio_info(), kCheckAc3Audio);
+
+ if (kCheckAc3Audio) {
+ EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecAc3);
+ }
+}
+
+TEST(ParsedMimeInfoTest, ParsesEac3Codec) {
+ ParsedMimeInfo mime_info("audio/mp4; codecs=\"ec-3\"");
+ ASSERT_EQ(mime_info.has_audio_info(), kCheckAc3Audio);
+
+ if (kCheckAc3Audio) {
+ EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecEac3);
+ }
+}
+
+TEST(ParsedMimeInfoTest, ParsesOpusCodec) {
+ ParsedMimeInfo mime_info("audio/webm; codecs=\"opus\"");
+ ASSERT_TRUE(mime_info.has_audio_info());
+ EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecOpus);
+}
+
+TEST(ParsedMimeInfoTest, ParsesVorbisCodec) {
+ ParsedMimeInfo mime_info("audio/webm; codecs=\"vorbis\"");
+ ASSERT_TRUE(mime_info.has_audio_info());
+ EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecVorbis);
+}
+
+#if SB_API_VERSION >= 14
+TEST(ParsedMimeInfoTest, ParsesMp3Codec) {
+ ParsedMimeInfo mime_info("audio/mpeg; codecs=\"mp3\"");
+ ASSERT_TRUE(mime_info.has_audio_info());
+ EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecMp3);
+}
+
+TEST(ParsedMimeInfoTest, ParsesFlacCodec) {
+ ParsedMimeInfo mime_info("audio/ogg; codecs=\"flac\"");
+ ASSERT_TRUE(mime_info.has_audio_info());
+ EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecFlac);
+}
+
+TEST(ParsedMimeInfoTest, ParsesPcmCodec) {
+ ParsedMimeInfo mime_info("audio/wav; codecs=\"1\"");
+ ASSERT_TRUE(mime_info.has_audio_info());
+ EXPECT_EQ(mime_info.audio_info().codec, kSbMediaAudioCodecPcm);
+}
+#endif // SB_API_VERSION >= 14
+
+} // namespace
+} // namespace media
+} // namespace starboard
+} // namespace shared
+} // namespace starboard
diff --git a/starboard/shared/starboard/player/decoded_audio_internal.cc b/starboard/shared/starboard/player/decoded_audio_internal.cc
index 2834bc2..0d90fe9 100644
--- a/starboard/shared/starboard/player/decoded_audio_internal.cc
+++ b/starboard/shared/starboard/player/decoded_audio_internal.cc
@@ -142,17 +142,26 @@
SB_DCHECK(discarded_duration_from_back >= 0);
SB_DCHECK(storage_type() == kSbMediaAudioFrameStorageTypeInterleaved);
- const auto bytes_per_frame = GetBytesPerSample(sample_type()) * channels_;
- auto discarded_frames_from_front =
- AudioDurationToFrames(discarded_duration_from_front, sample_rate);
+ if (discarded_duration_from_front == 0 && discarded_duration_from_back == 0) {
+ return;
+ }
- discarded_frames_from_front = std::min(discarded_frames_from_front, frames());
+ const auto bytes_per_frame = GetBytesPerSample(sample_type()) * channels_;
+ int current_frames = frames();
+ int discarded_frames_from_front =
+ (discarded_duration_from_front >=
+ AudioFramesToDuration(current_frames, sample_rate))
+ ? current_frames
+ : AudioDurationToFrames(discarded_duration_from_front, sample_rate);
offset_in_bytes_ += bytes_per_frame * discarded_frames_from_front;
size_in_bytes_ -= bytes_per_frame * discarded_frames_from_front;
- auto discarded_frames_from_back =
- AudioDurationToFrames(discarded_duration_from_back, sample_rate);
- discarded_frames_from_back = std::min(discarded_frames_from_back, frames());
+ current_frames = frames();
+ int discarded_frames_from_back =
+ (discarded_duration_from_back >=
+ AudioFramesToDuration(current_frames, sample_rate))
+ ? current_frames
+ : AudioDurationToFrames(discarded_duration_from_back, sample_rate);
size_in_bytes_ -= bytes_per_frame * discarded_frames_from_back;
}
diff --git a/starboard/shared/starboard/player/filter/audio_frame_discarder.cc b/starboard/shared/starboard/player/filter/audio_frame_discarder.cc
index b1ad4fb..b8f2c36 100644
--- a/starboard/shared/starboard/player/filter/audio_frame_discarder.cc
+++ b/starboard/shared/starboard/player/filter/audio_frame_discarder.cc
@@ -43,8 +43,12 @@
void AudioFrameDiscarder::AdjustForDiscardedDurations(
int sample_rate,
scoped_refptr<DecodedAudio>* decoded_audio) {
- SB_DCHECK(decoded_audio);
- SB_DCHECK(*decoded_audio);
+ if (!decoded_audio || !*decoded_audio) {
+ SB_LOG(ERROR) << "No input buffer to adjust.";
+ SB_DCHECK(decoded_audio);
+ SB_DCHECK(*decoded_audio);
+ return;
+ }
InputBufferInfo input_info;
{
diff --git a/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index 4a1713e..0816518 100644
--- a/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -39,6 +39,9 @@
using std::placeholders::_1;
using std::placeholders::_2;
+typedef shared::starboard::player::PlayerWorker::Handler::HandlerResult
+ HandlerResult;
+
// TODO: Make this configurable inside SbPlayerCreate().
const SbTimeMonotonic kUpdateInterval = 200 * kSbTimeMillisecond;
@@ -89,7 +92,7 @@
update_job_ = std::bind(&FilterBasedPlayerWorkerHandler::Update, this);
}
-bool FilterBasedPlayerWorkerHandler::Init(
+HandlerResult FilterBasedPlayerWorkerHandler::Init(
SbPlayer player,
UpdateMediaInfoCB update_media_info_cb,
GetPlayerStateCB get_player_state_cb,
@@ -126,11 +129,10 @@
SB_LOG(ERROR) << "Audio channels requested " << required_audio_channels
<< ", but currently supported less than or equal to "
<< supported_audio_channels;
- OnError(
- kSbPlayerErrorCapabilityChanged,
+ std::string error_message =
FormatString("Required channel %d is greater than maximum channel %d",
- required_audio_channels, supported_audio_channels));
- return false;
+ required_audio_channels, supported_audio_channels);
+ return HandlerResult{false, error_message};
}
}
@@ -140,14 +142,14 @@
{
::starboard::ScopedLock lock(player_components_existence_mutex_);
- std::string error_message;
- player_components_ =
- factory->CreateComponents(creation_parameters, &error_message);
+ std::string components_error_message;
+ player_components_ = factory->CreateComponents(creation_parameters,
+ &components_error_message);
if (!player_components_) {
- OnError(kSbPlayerErrorDecode,
- FormatString("Failed to create player components with error: %s",
- error_message.c_str()));
- return false;
+ std::string error_message =
+ FormatString("Failed to create player components with error: %s.",
+ components_error_message.c_str());
+ return HandlerResult{false, error_message};
}
media_time_provider_ = player_components_->GetMediaTimeProvider();
audio_renderer_ = player_components_->GetAudioRenderer();
@@ -187,16 +189,17 @@
update_job_token_ = Schedule(update_job_, kUpdateInterval);
- return true;
+ return HandlerResult{true};
}
-bool FilterBasedPlayerWorkerHandler::Seek(SbTime seek_to_time, int ticket) {
+HandlerResult FilterBasedPlayerWorkerHandler::Seek(SbTime seek_to_time,
+ int ticket) {
SB_DCHECK(BelongsToCurrentThread());
SB_LOG(INFO) << "Seek to " << seek_to_time << ", and media time provider is "
<< media_time_provider_;
if (!media_time_provider_) {
- return false;
+ return HandlerResult{false, "Invalid media time provider"};
}
if (seek_to_time < 0) {
@@ -211,10 +214,10 @@
media_time_provider_->Seek(seek_to_time);
audio_prerolled_ = false;
video_prerolled_ = false;
- return true;
+ return HandlerResult{true};
}
-bool FilterBasedPlayerWorkerHandler::WriteSamples(
+HandlerResult FilterBasedPlayerWorkerHandler::WriteSamples(
const InputBuffers& input_buffers,
int* samples_written) {
SB_DCHECK(!input_buffers.empty());
@@ -227,19 +230,19 @@
*samples_written = 0;
if (input_buffers.front()->sample_type() == kSbMediaTypeAudio) {
if (!audio_renderer_) {
- return false;
+ return HandlerResult{false, "Invalid audio renderer."};
}
if (audio_renderer_->IsEndOfStreamWritten()) {
SB_LOG(WARNING) << "Try to write audio sample after EOS is reached";
} else {
if (!audio_renderer_->CanAcceptMoreData()) {
- return true;
+ return HandlerResult{true};
}
for (const auto& input_buffer : input_buffers) {
if (input_buffer->drm_info()) {
if (!SbDrmSystemIsValid(drm_system_)) {
- return false;
+ return HandlerResult{false, "Invalid DRM system."};
}
DumpInputHash(input_buffer);
SbDrmSystemPrivate::DecryptStatus decrypt_status =
@@ -250,10 +253,10 @@
InputBuffers(input_buffers.begin(),
input_buffers.begin() + *samples_written));
}
- return true;
+ return HandlerResult{true};
}
if (decrypt_status == SbDrmSystemPrivate::kFailure) {
- return false;
+ return HandlerResult{false, "Sample decryption failure."};
}
}
DumpInputHash(input_buffer);
@@ -265,19 +268,19 @@
SB_DCHECK(input_buffers.front()->sample_type() == kSbMediaTypeVideo);
if (!video_renderer_) {
- return false;
+ return HandlerResult{false, "Invalid video renderer."};
}
if (video_renderer_->IsEndOfStreamWritten()) {
SB_LOG(WARNING) << "Try to write video sample after EOS is reached";
} else {
if (!video_renderer_->CanAcceptMoreData()) {
- return true;
+ return HandlerResult{true};
}
for (const auto& input_buffer : input_buffers) {
if (input_buffer->drm_info()) {
if (!SbDrmSystemIsValid(drm_system_)) {
- return false;
+ return HandlerResult{false, "Invalid DRM system."};
}
DumpInputHash(input_buffer);
SbDrmSystemPrivate::DecryptStatus decrypt_status =
@@ -288,10 +291,10 @@
InputBuffers(input_buffers.begin(),
input_buffers.begin() + *samples_written));
}
- return true;
+ return HandlerResult{true};
}
if (decrypt_status == SbDrmSystemPrivate::kFailure) {
- return false;
+ return HandlerResult{false, "Sample decryption failure."};
}
}
DumpInputHash(input_buffer);
@@ -301,16 +304,17 @@
}
}
- return true;
+ return HandlerResult{true};
}
-bool FilterBasedPlayerWorkerHandler::WriteEndOfStream(SbMediaType sample_type) {
+HandlerResult FilterBasedPlayerWorkerHandler::WriteEndOfStream(
+ SbMediaType sample_type) {
SB_DCHECK(BelongsToCurrentThread());
if (sample_type == kSbMediaTypeAudio) {
if (!audio_renderer_) {
SB_LOG(INFO) << "Audio EOS enqueued when renderer is NULL.";
- return false;
+ return HandlerResult{false, "Audio EOS enqueued when renderer is NULL."};
}
if (audio_renderer_->IsEndOfStreamWritten()) {
SB_LOG(WARNING) << "Try to write audio EOS after EOS is enqueued";
@@ -321,7 +325,7 @@
} else {
if (!video_renderer_) {
SB_LOG(INFO) << "Video EOS enqueued when renderer is NULL.";
- return false;
+ return HandlerResult{false, "Video EOS enqueued when renderer is NULL."};
}
if (video_renderer_->IsEndOfStreamWritten()) {
SB_LOG(WARNING) << "Try to write video EOS after EOS is enqueued";
@@ -331,17 +335,17 @@
}
}
- return true;
+ return HandlerResult{true};
}
-bool FilterBasedPlayerWorkerHandler::SetPause(bool pause) {
+HandlerResult FilterBasedPlayerWorkerHandler::SetPause(bool pause) {
SB_DCHECK(BelongsToCurrentThread());
SB_LOG(INFO) << "Set pause from " << paused_ << " to " << pause
<< ", and media time provider is " << media_time_provider_;
if (!media_time_provider_) {
- return false;
+ return HandlerResult{false, "Invalid media time provider."};
}
paused_ = pause;
@@ -351,11 +355,12 @@
} else {
media_time_provider_->Play();
}
-
- return true;
+ Update();
+ return HandlerResult{true};
}
-bool FilterBasedPlayerWorkerHandler::SetPlaybackRate(double playback_rate) {
+HandlerResult FilterBasedPlayerWorkerHandler::SetPlaybackRate(
+ double playback_rate) {
SB_DCHECK(BelongsToCurrentThread());
SB_LOG(INFO) << "Set playback rate from " << playback_rate_ << " to "
@@ -365,11 +370,12 @@
playback_rate_ = playback_rate;
if (!media_time_provider_) {
- return false;
+ return HandlerResult{false, "Invalid media time provider."};
}
media_time_provider_->SetPlaybackRate(playback_rate_);
- return true;
+ Update();
+ return HandlerResult{true};
}
void FilterBasedPlayerWorkerHandler::SetVolume(double volume) {
@@ -384,7 +390,7 @@
}
}
-bool FilterBasedPlayerWorkerHandler::SetBounds(const Bounds& bounds) {
+HandlerResult FilterBasedPlayerWorkerHandler::SetBounds(const Bounds& bounds) {
SB_DCHECK(BelongsToCurrentThread());
if (memcmp(&bounds_, &bounds, sizeof(bounds_)) != 0) {
@@ -407,7 +413,7 @@
}
}
- return true;
+ return HandlerResult{true};
}
void FilterBasedPlayerWorkerHandler::OnError(SbPlayerError error,
@@ -500,6 +506,7 @@
update_media_info_cb_(media_time, dropped_frames, !is_underflow);
}
+ RemoveJobByToken(update_job_token_);
update_job_token_ = Schedule(update_job_, kUpdateInterval);
}
diff --git a/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index 6f3ed97..e553774 100644
--- a/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -48,19 +48,19 @@
SbDecodeTargetGraphicsContextProvider* provider);
private:
- bool Init(SbPlayer player,
- UpdateMediaInfoCB update_media_info_cb,
- GetPlayerStateCB get_player_state_cb,
- UpdatePlayerStateCB update_player_state_cb,
- UpdatePlayerErrorCB update_player_error_cb) override;
- bool Seek(SbTime seek_to_time, int ticket) override;
- bool WriteSamples(const InputBuffers& input_buffers,
- int* samples_written) override;
- bool WriteEndOfStream(SbMediaType sample_type) override;
- bool SetPause(bool pause) override;
- bool SetPlaybackRate(double playback_rate) override;
+ HandlerResult Init(SbPlayer player,
+ UpdateMediaInfoCB update_media_info_cb,
+ GetPlayerStateCB get_player_state_cb,
+ UpdatePlayerStateCB update_player_state_cb,
+ UpdatePlayerErrorCB update_player_error_cb) override;
+ HandlerResult Seek(SbTime seek_to_time, int ticket) override;
+ HandlerResult WriteSamples(const InputBuffers& input_buffers,
+ int* samples_written) override;
+ HandlerResult WriteEndOfStream(SbMediaType sample_type) override;
+ HandlerResult SetPause(bool pause) override;
+ HandlerResult SetPlaybackRate(double playback_rate) override;
void SetVolume(double volume) override;
- bool SetBounds(const Bounds& bounds) override;
+ HandlerResult SetBounds(const Bounds& bounds) override;
void Stop() override;
void Update();
diff --git a/starboard/shared/starboard/player/filter/stub_audio_decoder.cc b/starboard/shared/starboard/player/filter/stub_audio_decoder.cc
index 2f4a7b4..39b5017 100644
--- a/starboard/shared/starboard/player/filter/stub_audio_decoder.cc
+++ b/starboard/shared/starboard/player/filter/stub_audio_decoder.cc
@@ -158,11 +158,12 @@
for (const auto& input_buffer : input_buffers) {
DecodeOneBuffer(input_buffer);
}
- decoder_thread_->job_queue()->Schedule(consumed_cb);
+ Schedule(consumed_cb);
}
void StubAudioDecoder::DecodeOneBuffer(
const scoped_refptr<InputBuffer>& input_buffer) {
+ SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
const int kMaxInputBeforeMultipleDecodedAudios = 4;
if (last_input_buffer_) {
@@ -189,7 +190,7 @@
if (total_input_count_ % kMaxInputBeforeMultipleDecodedAudios != 0) {
ScopedLock lock(decoded_audios_mutex_);
decoded_audios_.push(decoded_audio);
- decoder_thread_->job_queue()->Schedule(output_cb_);
+ Schedule(output_cb_);
} else {
// Divide the content of `decoded_audio` as multiple DecodedAudio objects
// to ensure that the user of AudioDecoders works with output in
@@ -230,7 +231,7 @@
ScopedLock lock(decoded_audios_mutex_);
decoded_audios_.push(current_decoded_audio);
- decoder_thread_->job_queue()->Schedule(output_cb_);
+ Schedule(output_cb_);
}
}
}
@@ -274,11 +275,11 @@
ScopedLock lock(decoded_audios_mutex_);
decoded_audios_.push(decoded_audio);
- decoder_thread_->job_queue()->Schedule(output_cb_);
+ Schedule(output_cb_);
}
ScopedLock lock(decoded_audios_mutex_);
decoded_audios_.push(new DecodedAudio());
- decoder_thread_->job_queue()->Schedule(output_cb_);
+ Schedule(output_cb_);
}
} // namespace filter
diff --git a/starboard/shared/starboard/player/filter/testing/BUILD.gn b/starboard/shared/starboard/player/filter/testing/BUILD.gn
index 9d6f6b9..d1cb5be 100644
--- a/starboard/shared/starboard/player/filter/testing/BUILD.gn
+++ b/starboard/shared/starboard/player/filter/testing/BUILD.gn
@@ -49,41 +49,41 @@
data_deps =
[ "//starboard/shared/starboard/player:player_download_test_data" ]
}
-}
-if (host_os != "win" && current_toolchain == starboard_toolchain) {
- target(final_executable_type, "player_filter_benchmarks") {
+ if (host_os != "win") {
+ target(final_executable_type, "player_filter_benchmarks") {
+ testonly = true
+
+ sources = [
+ "//starboard/common/benchmark_main.cc",
+ "audio_decoder_benchmark.cc",
+ ]
+
+ public_deps = [
+ ":test_util",
+ "//third_party/google_benchmark",
+ ]
+
+ deps = cobalt_platform_dependencies
+ }
+ }
+
+ static_library("test_util") {
testonly = true
sources = [
- "//starboard/common/benchmark_main.cc",
- "audio_decoder_benchmark.cc",
+ "test_util.cc",
+ "test_util.h",
]
+ public_configs = [ "//starboard/build/config:starboard_implementation" ]
+
public_deps = [
- ":test_util",
- "//third_party/google_benchmark",
+ "//starboard",
+ "//starboard/shared/starboard/media:media_util",
+ "//starboard/shared/starboard/player:player_download_test_data",
+ "//starboard/shared/starboard/player:video_dmp",
+ "//testing/gtest",
]
-
- deps = cobalt_platform_dependencies
}
}
-
-static_library("test_util") {
- testonly = true
-
- sources = [
- "test_util.cc",
- "test_util.h",
- ]
-
- public_configs = [ "//starboard/build/config:starboard_implementation" ]
-
- public_deps = [
- "//starboard",
- "//starboard/shared/starboard/media:media_util",
- "//starboard/shared/starboard/player:player_download_test_data",
- "//starboard/shared/starboard/player:video_dmp",
- "//testing/gtest",
- ]
-}
diff --git a/starboard/shared/starboard/player/filter/testing/test_util.cc b/starboard/shared/starboard/player/filter/testing/test_util.cc
index 25ad281..39ae17d 100644
--- a/starboard/shared/starboard/player/filter/testing/test_util.cc
+++ b/starboard/shared/starboard/player/filter/testing/test_util.cc
@@ -181,14 +181,32 @@
const auto& video_stream_info = dmp_reader.video_stream_info();
const std::string video_mime = dmp_reader.video_mime_type();
const MimeType video_mime_type(video_mime.c_str());
- if (SbMediaIsVideoSupported(
- dmp_reader.video_codec(),
- video_mime.size() > 0 ? &video_mime_type : nullptr, -1, -1, 8,
- kSbMediaPrimaryIdUnspecified, kSbMediaTransferIdUnspecified,
- kSbMediaMatrixIdUnspecified, video_stream_info.frame_width,
- video_stream_info.frame_height, dmp_reader.video_bitrate(),
- dmp_reader.video_fps(), false)) {
- test_params.push_back(std::make_tuple(filename, output_mode));
+ // SbMediaIsVideoSupported may return false for gpu based decoder that in
+ // fact supports av1 or/and vp9 because the system can make async
+ // initialization at startup.
+ // To minimize probability of false negative we check result few times
+ static bool decoder_has_been_checked_once = false;
+ int counter = 5;
+ const SbMediaVideoCodec video_codec = dmp_reader.video_codec();
+ bool need_to_check_with_wait = video_codec == kSbMediaVideoCodecAv1 ||
+ video_codec == kSbMediaVideoCodecVp9;
+ do {
+ if (SbMediaIsVideoSupported(
+ video_codec, video_mime.size() > 0 ? &video_mime_type : nullptr,
+ -1, -1, 8, kSbMediaPrimaryIdUnspecified,
+ kSbMediaTransferIdUnspecified, kSbMediaMatrixIdUnspecified,
+ video_stream_info.frame_width, video_stream_info.frame_height,
+ dmp_reader.video_bitrate(), dmp_reader.video_fps(), false)) {
+ test_params.push_back(std::make_tuple(filename, output_mode));
+ break;
+ } else if (need_to_check_with_wait && !decoder_has_been_checked_once) {
+ SbThreadSleep(kSbTimeSecond);
+ } else {
+ break;
+ }
+ } while (--counter);
+ if (need_to_check_with_wait) {
+ decoder_has_been_checked_once = true;
}
}
}
diff --git a/starboard/shared/starboard/player/player_worker.cc b/starboard/shared/starboard/player/player_worker.cc
index 5ee9bc0..09947e8 100644
--- a/starboard/shared/starboard/player/player_worker.cc
+++ b/starboard/shared/starboard/player/player_worker.cc
@@ -33,6 +33,9 @@
using std::placeholders::_2;
using std::placeholders::_3;
+typedef shared::starboard::player::PlayerWorker::Handler::HandlerResult
+ HandlerResult;
+
#ifdef SB_MEDIA_PLAYER_THREAD_STACK_SIZE
const int kPlayerStackSize = SB_MEDIA_PLAYER_THREAD_STACK_SIZE;
#else // SB_MEDIA_PLAYER_THREAD_STACK_SIZE
@@ -158,9 +161,16 @@
}
void PlayerWorker::UpdatePlayerError(SbPlayerError error,
+ HandlerResult result,
const std::string& error_message) {
+ SB_DCHECK(!result.success);
+ std::string complete_error_message = error_message;
+ if (!result.error_message.empty()) {
+ complete_error_message += " Error: " + result.error_message;
+ }
+
SB_LOG(WARNING) << "Encountered player error " << error
- << " with message: " << error_message;
+ << " with message: " << complete_error_message;
// Only report the first error.
if (error_occurred_.exchange(true)) {
return;
@@ -168,7 +178,7 @@
if (!player_error_func_) {
return;
}
- player_error_func_(player_, context_, error, error_message.c_str());
+ player_error_func_(player_, context_, error, complete_error_message.c_str());
}
// static
@@ -197,17 +207,18 @@
SB_DCHECK(job_queue_->BelongsToCurrentThread());
Handler::UpdatePlayerErrorCB update_player_error_cb;
- update_player_error_cb =
- std::bind(&PlayerWorker::UpdatePlayerError, this, _1, _2);
- if (handler_->Init(
- player_, std::bind(&PlayerWorker::UpdateMediaInfo, this, _1, _2, _3),
- std::bind(&PlayerWorker::player_state, this),
- std::bind(&PlayerWorker::UpdatePlayerState, this, _1),
- update_player_error_cb)) {
+ update_player_error_cb = std::bind(&PlayerWorker::UpdatePlayerError, this, _1,
+ HandlerResult{false}, _2);
+ HandlerResult result = handler_->Init(
+ player_, std::bind(&PlayerWorker::UpdateMediaInfo, this, _1, _2, _3),
+ std::bind(&PlayerWorker::player_state, this),
+ std::bind(&PlayerWorker::UpdatePlayerState, this, _1),
+ update_player_error_cb);
+ if (result.success) {
UpdatePlayerState(kSbPlayerStateInitialized);
} else {
- UpdatePlayerError(kSbPlayerErrorDecode,
- "Failed to initialize PlayerWorker with unknown error.");
+ UpdatePlayerError(kSbPlayerErrorDecode, result,
+ "Failed to initialize PlayerWorker.");
}
}
@@ -232,8 +243,9 @@
pending_audio_buffers_.clear();
pending_video_buffers_.clear();
- if (!handler_->Seek(seek_to_time, ticket)) {
- UpdatePlayerError(kSbPlayerErrorDecode, "Failed seek.");
+ HandlerResult result = handler_->Seek(seek_to_time, ticket);
+ if (!result.success) {
+ UpdatePlayerError(kSbPlayerErrorDecode, result, "Failed seek.");
return;
}
@@ -273,9 +285,10 @@
SB_DCHECK(pending_video_buffers_.empty());
}
int samples_written;
- bool result = handler_->WriteSamples(input_buffers, &samples_written);
- if (!result) {
- UpdatePlayerError(kSbPlayerErrorDecode, "Failed to write sample.");
+ HandlerResult result =
+ handler_->WriteSamples(input_buffers, &samples_written);
+ if (!result.success) {
+ UpdatePlayerError(kSbPlayerErrorDecode, result, "Failed to write sample.");
return;
}
if (samples_written == input_buffers.size()) {
@@ -341,31 +354,37 @@
SB_DCHECK(pending_video_buffers_.empty());
}
- if (!handler_->WriteEndOfStream(sample_type)) {
- UpdatePlayerError(kSbPlayerErrorDecode, "Failed to write end of stream.");
+ HandlerResult result = handler_->WriteEndOfStream(sample_type);
+ if (!result.success) {
+ UpdatePlayerError(kSbPlayerErrorDecode, result,
+ "Failed to write end of stream.");
}
}
void PlayerWorker::DoSetBounds(Bounds bounds) {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
- if (!handler_->SetBounds(bounds)) {
- UpdatePlayerError(kSbPlayerErrorDecode, "Failed to set bounds");
+ HandlerResult result = handler_->SetBounds(bounds);
+ if (!result.success) {
+ UpdatePlayerError(kSbPlayerErrorDecode, result, "Failed to set bounds.");
}
}
void PlayerWorker::DoSetPause(bool pause) {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
- if (!handler_->SetPause(pause)) {
- UpdatePlayerError(kSbPlayerErrorDecode, "Failed to set pause.");
+ HandlerResult result = handler_->SetPause(pause);
+ if (!result.success) {
+ UpdatePlayerError(kSbPlayerErrorDecode, result, "Failed to set pause.");
}
}
void PlayerWorker::DoSetPlaybackRate(double playback_rate) {
SB_DCHECK(job_queue_->BelongsToCurrentThread());
- if (!handler_->SetPlaybackRate(playback_rate)) {
- UpdatePlayerError(kSbPlayerErrorDecode, "Failed to set playback rate.");
+ HandlerResult result = handler_->SetPlaybackRate(playback_rate);
+ if (!result.success) {
+ UpdatePlayerError(kSbPlayerErrorDecode, result,
+ "Failed to set playback rate.");
}
}
diff --git a/starboard/shared/starboard/player/player_worker.h b/starboard/shared/starboard/player/player_worker.h
index cf27fb3..96bae3d 100644
--- a/starboard/shared/starboard/player/player_worker.h
+++ b/starboard/shared/starboard/player/player_worker.h
@@ -63,6 +63,13 @@
// All functions of this class will be called from the JobQueue thread.
class Handler {
public:
+ // Stores the success status of Handler operations. If |success| is false,
+ // |error_message| may be set with details of the error.
+ struct HandlerResult {
+ bool success;
+ std::string error_message;
+ };
+
typedef PlayerWorker::Bounds Bounds;
typedef ::starboard::shared::starboard::player::InputBuffer InputBuffer;
typedef ::starboard::shared::starboard::player::InputBuffers InputBuffers;
@@ -79,22 +86,23 @@
Handler() = default;
virtual ~Handler() {}
- // All the following functions return false to signal a fatal error. The
- // event processing loop in PlayerWorker will terminate in this case.
- virtual bool Init(SbPlayer player,
- UpdateMediaInfoCB update_media_info_cb,
- GetPlayerStateCB get_player_state_cb,
- UpdatePlayerStateCB update_player_state_cb,
- UpdatePlayerErrorCB update_player_error_cb) = 0;
- virtual bool Seek(SbTime seek_to_time, int ticket) = 0;
- virtual bool WriteSamples(const InputBuffers& input_buffers,
- int* samples_written) = 0;
- virtual bool WriteEndOfStream(SbMediaType sample_type) = 0;
- virtual bool SetPause(bool pause) = 0;
- virtual bool SetPlaybackRate(double playback_rate) = 0;
+ // All the following functions set |HandlerResult.success| to false to
+ // signal a fatal error. The event processing loop in PlayerWorker will
+ // terminate in this case.
+ virtual HandlerResult Init(SbPlayer player,
+ UpdateMediaInfoCB update_media_info_cb,
+ GetPlayerStateCB get_player_state_cb,
+ UpdatePlayerStateCB update_player_state_cb,
+ UpdatePlayerErrorCB update_player_error_cb) = 0;
+ virtual HandlerResult Seek(SbTime seek_to_time, int ticket) = 0;
+ virtual HandlerResult WriteSamples(const InputBuffers& input_buffers,
+ int* samples_written) = 0;
+ virtual HandlerResult WriteEndOfStream(SbMediaType sample_type) = 0;
+ virtual HandlerResult SetPause(bool pause) = 0;
+ virtual HandlerResult SetPlaybackRate(double playback_rate) = 0;
virtual void SetVolume(double volume) = 0;
- virtual bool SetBounds(const Bounds& bounds) = 0;
+ virtual HandlerResult SetBounds(const Bounds& bounds) = 0;
// Once this function returns, all processing on the Handler and related
// objects has to be stopped. The JobQueue will be destroyed immediately
@@ -187,7 +195,9 @@
SbPlayerState player_state() const { return player_state_; }
void UpdatePlayerState(SbPlayerState player_state);
- void UpdatePlayerError(SbPlayerError error, const std::string& message);
+ void UpdatePlayerError(SbPlayerError error,
+ Handler::HandlerResult result,
+ const std::string& message);
static void* ThreadEntryPoint(void* context);
void RunLoop();
diff --git a/starboard/shared/uwp/application_uwp.cc b/starboard/shared/uwp/application_uwp.cc
index c1192f5..b0f18dc 100644
--- a/starboard/shared/uwp/application_uwp.cc
+++ b/starboard/shared/uwp/application_uwp.cc
@@ -660,7 +660,8 @@
TryAddCommandArgsFromStarboardFile(&args_);
CommandLine cmd_line(args_);
if (cmd_line.HasSwitch(kNetArgsCommandSwitchWait)) {
- SbTime timeout = kSbTimeSecond * 2;
+ // Wait for net args is flaky and needs extended wait time on Xbox.
+ SbTime timeout = kSbTimeSecond * 30;
std::string val = cmd_line.GetSwitchValue(kNetArgsCommandSwitchWait);
if (!val.empty()) {
timeout = atoi(val.c_str());
diff --git a/starboard/shared/uwp/extended_resources_manager.cc b/starboard/shared/uwp/extended_resources_manager.cc
index 5b0c78b..21d2821 100644
--- a/starboard/shared/uwp/extended_resources_manager.cc
+++ b/starboard/shared/uwp/extended_resources_manager.cc
@@ -26,7 +26,7 @@
#include "starboard/time.h"
#include "starboard/xb1/shared/internal_shims.h"
#if defined(INTERNAL_BUILD)
-#include "internal/starboard/xb1/av1_video_decoder.h"
+#include "internal/starboard/xb1/dav1d_video_decoder.h"
#include "internal/starboard/xb1/vpx_video_decoder.h"
#include "third_party/internal/libvpx_xb1/libvpx/d3dx12.h"
#endif // defined(INTERNAL_BUILD)
@@ -41,12 +41,36 @@
using ::starboard::shared::starboard::media::MimeSupportabilityCache;
using Windows::Foundation::Metadata::ApiInformation;
#if defined(INTERNAL_BUILD)
-using ::starboard::xb1::shared::Av1VideoDecoder;
+using ::starboard::xb1::shared::Dav1dVideoDecoder;
+using ::starboard::xb1::shared::GpuVideoDecoderBase;
using ::starboard::xb1::shared::VpxVideoDecoder;
#endif // defined(INTERNAL_BUILD)
const SbTime kReleaseTimeout = kSbTimeSecond;
+// kFrameBuffersPoolMemorySize is the size of gpu memory heap for common use
+// by vpx & av1 sw decoders.
+// This value must be greater then max(av1_min_value, vpx_min_value), where
+// av1_min_value & vpx_min_value are minimal required memory size for sw av1 &
+// vpx decoders.
+//
+// Vpx sw decoder needs 13 internal frame buffers for work and at least
+// 8 buffers for preroll.
+// The size of fb is 13762560 for 4K SDR and 12976128 for 2K HDR
+// So, vpx decoder needs minimum 13762560 * (13 + preroll_size) = 289013760
+// bytes.
+//
+// Av1 sw decoder needs 13 internal buffers and 8 buffers for preroll.
+// The size of fb is 5996544 for 2K SDR and 11993088 for 2K HDR
+// av1 decoder needs minimum 11993088 * (13 + preroll_size) = 251854848 bytes.
+//
+// So, the value 289013760 is minimal for reliable decoders working.
+//
+// To make playback more smooth it is better to increase the output queue size
+// up to 30-50 frames, but it should not exceed memory budgetd.
+// So, the value of 440 Mb looks as compromise.
+const uint64_t kFrameBuffersPoolMemorySize = 440 * 1024 * 1024;
+
bool IsExtendedResourceModeRequired() {
if (!::starboard::xb1::shared::CanAcquire()) {
return false;
@@ -150,6 +174,7 @@
bool ExtendedResourcesManager::GetD3D12Objects(
Microsoft::WRL::ComPtr<ID3D12Device>* device,
+ Microsoft::WRL::ComPtr<ID3D12Heap>* buffer_heap,
void** command_queue) {
if (HasNonrecoverableFailure()) {
SB_LOG(WARNING) << "The D3D12 device has encountered a nonrecoverable "
@@ -184,8 +209,8 @@
D3D12_HEAP_PROPERTIES prop = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
D3D12_RESOURCE_DESC desc = CD3DX12_RESOURCE_DESC::Buffer(1024 * 1024);
HRESULT result = d3d12device_->CreateCommittedResource(
- &prop, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST,
- nullptr, IID_PPV_ARGS(&res));
+ &prop, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr,
+ IID_PPV_ARGS(&res));
if (result != S_OK) {
SB_LOG(WARNING) << "The D3D12 device is not in a good state, can not use "
"GPU based decoders.";
@@ -196,11 +221,25 @@
*device = d3d12device_;
*command_queue = d3d12queue_.Get();
+ *buffer_heap = d3d12FrameBuffersHeap_.Get();
return true;
}
bool ExtendedResourcesManager::GetD3D12ObjectsInternal() {
if (!d3d12device_) {
+ UINT dxgiFactoryFlags = 0;
+#if defined(_DEBUG)
+ {
+ // This can help to debug DX issues. If something goes wrong in DX,
+ // Debug Layer outputs detailed log
+ ComPtr<ID3D12Debug> debugController;
+ HRESULT hr = D3D12GetDebugInterface(IID_PPV_ARGS(&debugController));
+ if (SUCCEEDED(hr)) {
+ debugController->EnableDebugLayer();
+ }
+ }
+#endif
+
if (FAILED(D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&d3d12device_)))) {
// GPU based vp9 decoding will be temporarily disabled.
@@ -221,8 +260,26 @@
}
SB_DCHECK(d3d12queue_);
}
+ if (!d3d12FrameBuffersHeap_) {
+ D3D12_HEAP_DESC heap_desc;
+ heap_desc.SizeInBytes = kFrameBuffersPoolMemorySize;
+ heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
+ heap_desc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heap_desc.Properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heap_desc.Properties.CreationNodeMask = 0;
+ heap_desc.Properties.VisibleNodeMask = 0;
+ heap_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
+ heap_desc.Flags = D3D12_HEAP_FLAG_NONE;
- return d3d12device_ && d3d12queue_;
+ if (FAILED(d3d12device_->CreateHeap(
+ &heap_desc, IID_PPV_ARGS(&d3d12FrameBuffersHeap_)))) {
+ SB_LOG(WARNING) << "Failed to create d3d12 buffer.";
+ return false;
+ }
+ SB_DCHECK(d3d12FrameBuffersHeap_);
+ }
+
+ return d3d12device_ && d3d12queue_ && d3d12FrameBuffersHeap_;
}
bool ExtendedResourcesManager::AcquireExtendedResourcesInternal() {
@@ -335,7 +392,7 @@
"shader compile.";
return;
}
- if (Av1VideoDecoder::CompileShaders(d3d12device_, d3d12queue_.Get())) {
+ if (Dav1dVideoDecoder::CompileShaders(d3d12device_)) {
is_av1_shader_compiled_ = true;
SB_LOG(INFO) << "Gpu based AV1 decoder finished compiling its shaders.";
} else {
@@ -352,7 +409,8 @@
return;
}
- if (VpxVideoDecoder::CompileShaders(d3d12device_, d3d12queue_.Get())) {
+ if (VpxVideoDecoder::CompileShaders(d3d12device_, d3d12FrameBuffersHeap_,
+ d3d12queue_.Get())) {
is_vp9_shader_compiled_ = true;
SB_LOG(INFO) << "Gpu based VP9 decoder finished compiling its shaders.";
} else {
@@ -372,10 +430,6 @@
void ExtendedResourcesManager::ReleaseExtendedResourcesInternal() {
SB_DCHECK(thread_checker_.CalledOnValidThread());
-#if defined(INTERNAL_BUILD)
- Av1VideoDecoder::ClearFrameBufferPool();
-#endif // defined(INTERNAL_BUILD)
-
ScopedLock scoped_lock(mutex_);
if (!is_extended_resources_acquired_.load()) {
SB_LOG(INFO) << "Extended resources hasn't been acquired,"
@@ -410,7 +464,7 @@
SB_LOG(INFO) << "CreateEvent() failed with " << GetLastError();
}
#if defined(INTERNAL_BUILD)
- Av1VideoDecoder::ReleaseShaders();
+ Dav1dVideoDecoder::ReleaseShaders();
VpxVideoDecoder::ReleaseShaders();
#endif // #if defined(INTERNAL_BUILD)
is_av1_shader_compiled_ = false;
@@ -418,27 +472,41 @@
} else {
SB_LOG(INFO) << "CreateFence() failed with " << hr;
}
+#if defined(INTERNAL_BUILD)
+ // Clear frame buffers used for rendering queue
+ GpuVideoDecoderBase::ClearFrameBuffersPool();
+#endif // #if defined(INTERNAL_BUILD)
}
if (d3d12queue_) {
#if !defined(COBALT_BUILD_TYPE_GOLD)
d3d12queue_->AddRef();
ULONG reference_count = d3d12queue_->Release();
- SB_DLOG(INFO) << "Reference count of |d3d12queue_| is "
- << reference_count;
+ SB_LOG(INFO) << "Reference count of |d3d12queue_| is " << reference_count;
#endif
d3d12queue_.Reset();
}
+ if (d3d12FrameBuffersHeap_) {
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+ d3d12FrameBuffersHeap_->AddRef();
+ ULONG reference_count = d3d12FrameBuffersHeap_->Release();
+ SB_LOG(INFO) << "Reference count of |d3d12FrameBuffersHeap_| is "
+ << reference_count;
+#endif
+ d3d12FrameBuffersHeap_.Reset();
+ }
+
if (d3d12device_) {
#if !defined(COBALT_BUILD_TYPE_GOLD)
d3d12device_->AddRef();
ULONG reference_count = d3d12device_->Release();
- SB_DLOG(INFO) << "Reference count of |d3d12device_| is "
- << reference_count;
+ SB_LOG(INFO) << "Reference count of |d3d12device_| is "
+ << reference_count;
#endif
d3d12device_.Reset();
}
+
} catch (const std::exception& e) {
SB_LOG(ERROR) << "Exception on releasing extended resources: " << e.what();
OnNonrecoverableFailure();
diff --git a/starboard/shared/uwp/extended_resources_manager.h b/starboard/shared/uwp/extended_resources_manager.h
index fbd8a5b..54b966f 100644
--- a/starboard/shared/uwp/extended_resources_manager.h
+++ b/starboard/shared/uwp/extended_resources_manager.h
@@ -47,8 +47,10 @@
void ReleaseExtendedResources();
void Quit();
- // Returns true when the d3d12 device and command queue can be used.
+ // Returns true when the d3d12 device, buffer heap
+ // and command queue can be used.
bool GetD3D12Objects(Microsoft::WRL::ComPtr<ID3D12Device>* device,
+ Microsoft::WRL::ComPtr<ID3D12Heap>* buffer_heap,
void** command_queue);
bool IsGpuDecoderReady() const {
@@ -91,6 +93,8 @@
Queue<Event> event_queue_;
Microsoft::WRL::ComPtr<ID3D12Device> d3d12device_;
Microsoft::WRL::ComPtr<ID3D12CommandQueue> d3d12queue_;
+ // heap for frame buffers (for the decoder and output queue) memory allocation
+ Microsoft::WRL::ComPtr<ID3D12Heap> d3d12FrameBuffersHeap_;
// This is set to true when a release of extended resources is requested.
// Anything delaying the release should be expedited when this is set.
diff --git a/starboard/shared/uwp/player_components_factory.cc b/starboard/shared/uwp/player_components_factory.cc
index 6963151..c8511be 100644
--- a/starboard/shared/uwp/player_components_factory.cc
+++ b/starboard/shared/uwp/player_components_factory.cc
@@ -43,7 +43,7 @@
#include "starboard/xb1/shared/video_decoder_uwp.h"
#if defined(INTERNAL_BUILD)
-#include "internal/starboard/xb1/av1_video_decoder.h"
+#include "internal/starboard/xb1/dav1d_video_decoder.h"
#include "internal/starboard/xb1/vpx_video_decoder.h"
#endif // defined(INTERNAL_BUILD)
@@ -236,9 +236,10 @@
SB_DCHECK(output_mode == kSbPlayerOutputModeDecodeToTexture);
Microsoft::WRL::ComPtr<ID3D12Device> d3d12device;
+ Microsoft::WRL::ComPtr<ID3D12Heap> d3d12buffer_heap;
void* d3d12queue = nullptr;
if (!uwp::ExtendedResourcesManager::GetInstance()->GetD3D12Objects(
- &d3d12device, &d3d12queue)) {
+ &d3d12device, &d3d12buffer_heap, &d3d12queue)) {
// Somehow extended resources get lost. Returns directly to trigger an
// error to the player.
*error_message =
@@ -248,24 +249,25 @@
return false;
}
SB_DCHECK(d3d12device);
+ SB_DCHECK(d3d12buffer_heap);
SB_DCHECK(d3d12queue);
#if defined(INTERNAL_BUILD)
using GpuVp9VideoDecoder = ::starboard::xb1::shared::VpxVideoDecoder;
- using GpuAv1VideoDecoder = ::starboard::xb1::shared::Av1VideoDecoder;
+ using GpuAv1VideoDecoder = ::starboard::xb1::shared::Dav1dVideoDecoder;
if (video_codec == kSbMediaVideoCodecVp9) {
video_decoder->reset(new GpuVp9VideoDecoder(
creation_parameters.decode_target_graphics_context_provider(),
creation_parameters.video_stream_info(), is_hdr_video, d3d12device,
- d3d12queue));
+ d3d12buffer_heap, d3d12queue));
}
if (video_codec == kSbMediaVideoCodecAv1) {
video_decoder->reset(new GpuAv1VideoDecoder(
creation_parameters.decode_target_graphics_context_provider(),
creation_parameters.video_stream_info(), is_hdr_video, d3d12device,
- d3d12queue));
+ d3d12buffer_heap, d3d12queue));
}
#endif // defined(INTERNAL_BUILD)
diff --git a/starboard/shared/uwp/time_zone_get_name.cc b/starboard/shared/uwp/time_zone_get_name.cc
new file mode 100644
index 0000000..58fc8b9
--- /dev/null
+++ b/starboard/shared/uwp/time_zone_get_name.cc
@@ -0,0 +1,45 @@
+// 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/time_zone.h"
+
+#include <Windows.h>
+#include <string>
+
+#include "starboard/once.h"
+#include "starboard/shared/win32/wchar_utils.h"
+
+namespace {
+class TimeZoneString {
+ public:
+ static TimeZoneString* Get();
+ const char* value() const { return value_.c_str(); }
+
+ private:
+ TimeZoneString() {
+ Windows::Globalization::Calendar ^ calendar =
+ ref new Windows::Globalization::Calendar();
+ Platform::String ^ time_zone = calendar->GetTimeZone();
+ value_ = starboard::shared::win32::platformStringToString(time_zone);
+ }
+ std::string value_;
+};
+
+SB_ONCE_INITIALIZE_FUNCTION(TimeZoneString, TimeZoneString::Get);
+} // namespace.
+
+const char* SbTimeZoneGetName() {
+ const char* output = TimeZoneString::Get()->value();
+ return output;
+}
diff --git a/starboard/shared/uwp/wasapi_audio_sink.cc b/starboard/shared/uwp/wasapi_audio_sink.cc
index 2beffa1..d8f5990 100644
--- a/starboard/shared/uwp/wasapi_audio_sink.cc
+++ b/starboard/shared/uwp/wasapi_audio_sink.cc
@@ -97,9 +97,9 @@
return false;
}
- hr = device_->Activate(
- IID_IAudioEndpointVolume, CLSCTX_ALL, NULL,
- reinterpret_cast<void**>(audio_endpoint_volume_.GetAddressOf()));
+ hr = audio_client_->GetService(
+ IID_ISimpleAudioVolume,
+ reinterpret_cast<void**>(audio_volume_.GetAddressOf()));
if (hr != S_OK) {
SB_LOG(ERROR) << "Failed to initialize volume handler, error code: "
<< std::hex << hr;
@@ -294,7 +294,7 @@
}
double volume = volume_.load();
if (current_volume_ != volume) {
- hr = audio_endpoint_volume_->SetMasterVolumeLevelScalar(volume, NULL);
+ hr = audio_volume_->SetMasterVolume(volume, NULL);
CHECK_HRESULT_OK(hr);
current_volume_ = volume;
}
diff --git a/starboard/shared/uwp/wasapi_audio_sink.h b/starboard/shared/uwp/wasapi_audio_sink.h
index 86863a4..3d94f53 100644
--- a/starboard/shared/uwp/wasapi_audio_sink.h
+++ b/starboard/shared/uwp/wasapi_audio_sink.h
@@ -16,11 +16,9 @@
#define STARBOARD_SHARED_UWP_WASAPI_AUDIO_SINK_H_
#include <Audioclient.h>
-#include <endpointvolume.h>
#include <mmdeviceapi.h>
#include <wrl\client.h>
-#include <atomic>
#include <functional>
#include <queue>
@@ -119,7 +117,6 @@
};
const IID IID_IAudioClock = __uuidof(IAudioClock);
-const IID IID_IAudioEndpointVolume = __uuidof(IAudioEndpointVolume);
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_ISimpleAudioVolume = __uuidof(ISimpleAudioVolume);
@@ -168,7 +165,7 @@
Microsoft::WRL::ComPtr<IMMDevice> device_;
Microsoft::WRL::ComPtr<IAudioClient3> audio_client_;
Microsoft::WRL::ComPtr<IAudioRenderClient> render_client_;
- Microsoft::WRL::ComPtr<IAudioEndpointVolume> audio_endpoint_volume_;
+ Microsoft::WRL::ComPtr<ISimpleAudioVolume> audio_volume_;
Mutex audio_clock_mutex_;
Microsoft::WRL::ComPtr<IAudioClock> audio_clock_;
diff --git a/starboard/shared/widevine/drm_system_widevine.cc b/starboard/shared/widevine/drm_system_widevine.cc
index 08729a8..3320eba 100644
--- a/starboard/shared/widevine/drm_system_widevine.cc
+++ b/starboard/shared/widevine/drm_system_widevine.cc
@@ -157,9 +157,10 @@
SB_ONCE_INITIALIZE_FUNCTION(Mutex, GetInitializationMutex);
void EnsureWidevineCdmIsInitialized(const std::string& company_name,
- const std::string& model_name) {
+ const std::string& model_name,
+ WidevineStorage* storage) {
+ SB_DCHECK(storage) << "|storage| is NULL.";
static WidevineClock s_clock;
- static WidevineStorage s_storage(GetWidevineStoragePath());
static WidevineTimer s_timer;
static bool s_initialized = false;
@@ -190,7 +191,7 @@
log_level = wv3cdm::kSilent;
#endif // COBALT_BUILD_TYPE_GOLD
wv3cdm::Status status =
- wv3cdm::initialize(wv3cdm::kNoSecureOutput, client_info, &s_storage,
+ wv3cdm::initialize(wv3cdm::kNoSecureOutput, client_info, storage,
&s_clock, &s_timer, log_level);
SB_DCHECK(status == wv3cdm::kSuccess);
s_initialized = true;
@@ -235,9 +236,10 @@
}
#endif // !defined(COBALT_BUILD_TYPE_GOLD)
- EnsureWidevineCdmIsInitialized(company_name, model_name);
+ static WidevineStorage s_storage(GetWidevineStoragePath());
+ EnsureWidevineCdmIsInitialized(company_name, model_name, &s_storage);
const bool kEnablePrivacyMode = true;
- cdm_.reset(wv3cdm::create(this, NULL, kEnablePrivacyMode));
+ cdm_.reset(wv3cdm::create(this, &s_storage, kEnablePrivacyMode));
SB_DCHECK(cdm_);
// Get cert scope and pass to widevine.
diff --git a/starboard/shared/win32/dx_context_video_decoder.cc b/starboard/shared/win32/dx_context_video_decoder.cc
index 1ca7403..b576659 100644
--- a/starboard/shared/win32/dx_context_video_decoder.cc
+++ b/starboard/shared/win32/dx_context_video_decoder.cc
@@ -45,7 +45,7 @@
query_display(display, EGL_DEVICE_EXT, &egl_device);
SB_DCHECK(egl_device != 0);
- intptr_t device;
+ intptr_t device = 0;
query_device(reinterpret_cast<EGLDeviceEXT>(egl_device),
EGL_D3D11_DEVICE_ANGLE, &device);
diff --git a/starboard/shared/win32/test_filters.py b/starboard/shared/win32/test_filters.py
index 2619466..352adbb 100644
--- a/starboard/shared/win32/test_filters.py
+++ b/starboard/shared/win32/test_filters.py
@@ -15,7 +15,15 @@
from starboard.tools.testing import test_filter
-_FILTERED_TESTS = {}
+_FILTERED_TESTS = {
+ 'nplb': [
+ # Windows uses a special time zone format that ICU accepts, so we don't
+ # enforce IANA.
+ # TODO(b/304335954): Re-enable the test for UWP after fixing DST
+ # implementation.
+ 'SbTimeZoneGetNameTest.IsIANAFormat',
+ ],
+}
class TestFilters(object):
diff --git a/starboard/shared/win32/time_zone_get_name.cc b/starboard/shared/win32/time_zone_get_name.cc
index bc92e3b..9c31522 100644
--- a/starboard/shared/win32/time_zone_get_name.cc
+++ b/starboard/shared/win32/time_zone_get_name.cc
@@ -28,6 +28,10 @@
const char* value() const { return value_.c_str(); }
private:
+ // Returns a string representing a time zone name, e.g. "EST" for Eastern
+ // Standard Time or "PDT" for Pacific Daylight Time. There isn't a native way
+ // to convert these to IANA name format on Windows without UWP, so we're
+ // making use of GetDynamicTimeZoneInformation for now.
TimeZoneString() {
DYNAMIC_TIME_ZONE_INFORMATION time_zone_info;
DWORD zone_id = GetDynamicTimeZoneInformation(&time_zone_info);
diff --git a/starboard/shared/win32/video_decoder.cc b/starboard/shared/win32/video_decoder.cc
index b69cdaf..f889019 100644
--- a/starboard/shared/win32/video_decoder.cc
+++ b/starboard/shared/win32/video_decoder.cc
@@ -194,6 +194,10 @@
HardwareDecoderContext hardware_context = GetDirectXForHardwareDecoding();
d3d_device_ = hardware_context.dx_device_out;
device_manager_ = hardware_context.dxgi_device_manager_out;
+ if (!d3d_device_ || !device_manager_) {
+ return;
+ }
+
HRESULT hr = d3d_device_.As(&video_device_);
if (FAILED(hr)) {
return;
diff --git a/starboard/time_zone.h b/starboard/time_zone.h
index ce9e424..d140338 100644
--- a/starboard/time_zone.h
+++ b/starboard/time_zone.h
@@ -29,18 +29,17 @@
// The number of minutes west of the Greenwich Prime Meridian, NOT including
// Daylight Savings Time adjustments.
//
-// For example: PST/PDT is 480 minutes (28800 seconds, 8 hours).
+// For example: America/Los_Angeles is 480 minutes (28800 seconds, 8 hours).
typedef int SbTimeZone;
// Gets the system's current SbTimeZone in minutes.
SB_EXPORT SbTimeZone SbTimeZoneGetCurrent();
-// Gets a string representation of the current timezone. Note that the string
-// representation can either be standard or daylight saving time. The output
-// can be of the form:
-// 1) A three-letter abbreviation such as "PST" or "PDT" (preferred).
-// 2) A time zone identifier such as "America/Los_Angeles"
-// 3) An un-abbreviated name such as "Pacific Standard Time".
+// Gets a string representation of the current timezone. The format should be
+// in the IANA format https://data.iana.org/time-zones/theory.html#naming .
+// Names normally have the form AREA/LOCATION, where AREA is a continent or
+// ocean, and LOCATION is a specific location within the area.
+// Typical names are 'Africa/Cairo', 'America/New_York', and 'Pacific/Honolulu'.
SB_EXPORT const char* SbTimeZoneGetName();
#ifdef __cplusplus
diff --git a/starboard/tools/abstract_launcher.py b/starboard/tools/abstract_launcher.py
index 9928039..d57589e 100644
--- a/starboard/tools/abstract_launcher.py
+++ b/starboard/tools/abstract_launcher.py
@@ -18,6 +18,7 @@
import abc
import os
import sys
+from enum import IntEnum
from starboard.tools import build
from starboard.tools import paths
@@ -78,7 +79,7 @@
RuntimeError: The platform does not exist, or there is no project root.
"""
- # Creates launcher for provided platform if the platform has a valid port
+ # Creates launcher for provided platform if the platform has a valid port.
launcher_module = _GetLauncherForPlatform(platform_name)
if not launcher_module:
@@ -97,8 +98,51 @@
**kwargs)
+class TargetStatus(IntEnum):
+ """Represents status of the target run and its return code availability."""
+
+ # Target exited normally. Return code is available.
+ OK = 0
+
+ # Target exited normally. Return code is not available.
+ NA = 1
+
+ # Target crashed.
+ CRASH = 2
+
+ # Target not started.
+ NOT_STARTED = 3
+
+ @classmethod
+ def ToString(cls, status):
+ return [
+ "SUCCEEDED", "SUCCEEDED", "FAILED (CRASHED)", "FAILED (NOT STARTED)"
+ ][status]
+
+
class AbstractLauncher(object):
- """Class that specifies all required behavior for Cobalt app launchers."""
+ """
+ Class that specifies all required behavior for Cobalt app launchers.
+ The following is definition of extended interface. Implementation is optional.
+ Functions:
+ - InitDevice: a function to be called before any other performance
+ on the target device.
+ - RebootDevice: a function, used to reboot the target device.
+ - Run2: a function to run a target on device. Must be implemented
+ to support extended interface. The target must have been deployed
+ on the device, if required.
+ Returns:
+ a tuple of ReturnCodeStatus and, target return code if available,
+ else 0.
+ - Deploy: creates a package (if required) and deploys it on the target
+ device.
+ - CheckPackageIsDeployed: a function, which returns True, if the target
+ is deployed on the device, False otherwise. Must be implemented,
+ if Deploy is implemented.
+ - Kill: request the target OS to kill the target process. Must be
+ implemented.
+ - SendStop: sends stop signal to the launcher's executable.
+ """
__metaclass__ = abc.ABCMeta
@@ -143,30 +187,32 @@
self.test_result_xml_path = kwargs.get("test_result_xml_path", None)
+ def HasExtendedInterface(self) -> bool:
+ return hasattr(self, "Run2")
+
@abc.abstractmethod
def Run(self):
- """Runs the launcher's executable.
+ """Runs an underlying application. Supports launching target
+ executable on target device, or target executable directly.
+ Implementation is platform specific.
Must be implemented in subclasses.
Returns:
- The return code from the launcher's executable.
+ The return code from the underlying application, if available,
+ else, 0 in case of normal completion of the underlying application,
+ otherwise 1.
"""
pass
@abc.abstractmethod
- def Kill(self):
- """Kills the launcher. Must be implemented in subclasses."""
- pass
-
- @abc.abstractmethod
def GetDeviceIp(self):
"""Gets the device IP. Must be implemented in subclasses."""
pass
@abc.abstractmethod
def GetDeviceOutputPath(self):
- """Writable path where test targets can output files"""
+ """Writable path where test targets can output files."""
pass
def SupportsSuspendResume(self):
@@ -219,14 +265,6 @@
"""
raise RuntimeError("Freeze not supported for this platform.")
- def SendStop(self):
- """sends stop signal to the launcher's executable.
-
- Raises:
- RuntimeError: Stop signal not supported on platform.
- """
- raise RuntimeError("Stop not supported for this platform.")
-
def SupportsDeepLink(self):
return False
@@ -290,7 +328,7 @@
The default path returned by this method takes the form of:
- "/path/to/out/<platform>_<config>/target_name"
+ "/path/to/out/<platform>_<config>/target_name".
Returns:
The path to an executable target.
@@ -302,7 +340,7 @@
The default path returned by this method takes the form of:
- "/path/to/out/<platform>_<config>/install/target_name"
+ "/path/to/out/<platform>_<config>/install/target_name".
Returns:
The path to an executable target.
diff --git a/starboard/tools/testing/test_filter.py b/starboard/tools/testing/test_filter.py
index 6c2af98..6a787b0 100644
--- a/starboard/tools/testing/test_filter.py
+++ b/starboard/tools/testing/test_filter.py
@@ -19,6 +19,13 @@
FILTER_ALL = 'FILTER_ALL'
DISABLE_TESTING = 'DISABLE_TESTING'
+EVERGREEN_ONLY_TESTS = {
+ 'elf_loader_test': {FILTER_ALL},
+ 'installation_manager_test': {FILTER_ALL},
+ 'reset_evergreen_update_test': {FILTER_ALL},
+ 'slot_management_test': {FILTER_ALL},
+}
+
class TestFilter(object):
"""Container for data used to filter out a unit test.
diff --git a/starboard/tools/testing/test_runner.py b/starboard/tools/testing/test_runner.py
index 8ae6f49..0fb343a 100755
--- a/starboard/tools/testing/test_runner.py
+++ b/starboard/tools/testing/test_runner.py
@@ -29,6 +29,7 @@
from six.moves import cStringIO as StringIO
from starboard.build import clang
from starboard.tools import abstract_launcher
+from starboard.tools.abstract_launcher import TargetStatus
from starboard.tools import build
from starboard.tools import command_line
from starboard.tools import paths
@@ -160,14 +161,33 @@
communicate, and for the main thread to shut them down.
"""
- def __init__(self, launcher):
+ def __init__(self, launcher, skip_init):
self.launcher = launcher
- self.runner_thread = threading.Thread(target=self._Run)
self.return_code_lock = threading.Lock()
self.return_code = 1
+ self.run_test = None
+ self.skip_init = skip_init
def Start(self):
+ if self.launcher.HasExtendedInterface():
+ if not self.skip_init:
+ if hasattr(self.launcher, "InitDevice"):
+ self.launcher.InitDevice()
+ if hasattr(self.launcher, "Deploy"):
+ assert hasattr(self.launcher, "CheckPackageIsDeployed")
+ if abstract_launcher.ARG_NOINSTALL not in self.launcher.launcher_args:
+ self.launcher.Deploy()
+ if not self.launcher.CheckPackageIsDeployed():
+ raise IOError(
+ "The target application is not installed on the device.")
+
+ self.run_test = self.launcher.Run2
+
+ else:
+ self.run_test = lambda: (TargetStatus.OK, self.launcher.Run())
+
+ self.runner_thread = threading.Thread(target=self._Run)
self.runner_thread.start()
def Kill(self):
@@ -183,12 +203,12 @@
def Join(self):
self.runner_thread.join()
- def _Run(self):
- """Runs the launcher, and assigns a return code."""
- return_code = 1
+ def _Run(self) -> None:
+ """Runs the launcher, and assigns a status and a return code."""
+ return_code = TargetStatus.NOT_STARTED, 0
try:
logging.info("Running launcher")
- return_code = self.launcher.Run()
+ return_code = self.run_test()
logging.info("Finished running launcher")
except Exception: # pylint: disable=broad-except
sys.stderr.write(f"Error while running {self.launcher.target_name}:\n")
@@ -259,6 +279,7 @@
self.xml_output_dir = xml_output_dir
self.log_xml_results = log_xml_results
self.threads = []
+ self.is_initialized = False
_EnsureBuildDirectoryExists(self.out_directory)
_VerifyConfig(self._platform_config,
@@ -273,6 +294,7 @@
# If a particular test binary has been provided, configure only that one.
logging.info("Getting test targets")
+
if specified_targets:
self.test_targets = self._GetSpecifiedTestTargets(specified_targets)
else:
@@ -392,11 +414,11 @@
env_variables[test] = test_env
return env_variables
- def _RunTest(self,
- target_name,
- test_name=None,
- shard_index=None,
- shard_count=None):
+ def RunTest(self,
+ target_name,
+ test_name=None,
+ shard_index=None,
+ shard_count=None):
"""Runs a specific target or test and collects the output.
Args:
@@ -497,7 +519,7 @@
logging.info("Launcher initialized")
test_reader = TestLineReader(read_pipe)
- test_launcher = TestLauncher(launcher)
+ test_launcher = TestLauncher(launcher, self.is_initialized)
self.threads.append(test_launcher)
self.threads.append(test_reader)
@@ -524,6 +546,7 @@
# Wait for the launcher to exit then close the write pipe, which will
# cause the reader to exit.
test_launcher.Join()
+ self.is_initialized = True
write_pipe.close()
# Only after closing the write pipe, wait for the reader to exit.
@@ -533,10 +556,10 @@
output = test_reader.GetLines()
self.threads = []
- return self._CollectTestResults(output, target_name,
- test_launcher.GetReturnCode())
+ return (target_name, *self._CollectTestResults(output),
+ *test_launcher.GetReturnCode())
- def _CollectTestResults(self, results, target_name, return_code):
+ def _CollectTestResults(self, results):
"""Collects passing and failing tests for one test binary.
Args:
@@ -570,8 +593,7 @@
# Descriptions of all failed tests appear after this line
failed_tests = self._CollectFailedTests(results[idx + 1:])
- return (target_name, total_count, passed_count, failed_count, failed_tests,
- return_code)
+ return (total_count, passed_count, failed_count, failed_tests)
def _CollectFailedTests(self, lines):
"""Collects the names of all failed tests.
@@ -630,7 +652,8 @@
passed_count = result_set[2]
failed_count = result_set[3]
failed_tests = result_set[4]
- return_code = result_set[5]
+ return_code_status = result_set[5]
+ return_code = result_set[6]
actual_failed_tests = []
flaky_failed_tests = []
filtered_tests = self._GetFilteredTestList(target_name)
@@ -660,7 +683,7 @@
for retry in range(_FLAKY_RETRY_LIMIT):
# Sometimes the returned test "name" includes information about the
# parameter that was passed to it. This needs to be stripped off.
- retry_result = self._RunTest(target_name, test_case.split(",")[0])
+ retry_result = self.RunTest(target_name, test_case.split(",")[0])
print() # Explicit print for empty formatting line.
if retry_result[2] == 1:
flaky_passed_tests.append(test_case)
@@ -676,24 +699,26 @@
else:
logging.info("") # formatting newline.
- test_status = "SUCCEEDED"
+ test_status = TargetStatus.ToString(return_code_status)
all_flaky_tests_succeeded = initial_flaky_failed_count == len(
flaky_passed_tests) and initial_flaky_failed_count != 0
# Always mark as FAILED if we have a non-zero return code, or failing
# test.
- if ((return_code != 0 and not all_flaky_tests_succeeded) or
- actual_failed_count > 0 or flaky_failed_count > 0):
+ if ((return_code_status == TargetStatus.OK and return_code != 0 and
+ not all_flaky_tests_succeeded) or actual_failed_count > 0 or
+ flaky_failed_count > 0):
error = True
- test_status = "FAILED"
failed_test_groups.append(target_name)
# Be specific about the cause of failure if it was caused due to crash
# upon exit. Normal Gtest failures have return_code = 1; test crashes
# yield different return codes (e.g. segfault has return_code = 11).
if (return_code != 1 and actual_failed_count == 0 and
flaky_failed_count == 0):
- test_status = "FAILED (CRASHED)"
+ test_status = "FAILED (ISSUE)"
+ else:
+ test_status = "FAILED"
logging.info("%s: %s.", target_name, test_status)
if return_code != 0 and run_count == 0 and filtered_count == 0:
@@ -808,12 +833,12 @@
if run_action == ShardingTestConfig.RUN_FULL_TEST:
logging.info("SHARD %d RUNS TEST %s (full)", self.shard_index,
test_target)
- results.append(self._RunTest(test_target))
+ results.append(self.RunTest(test_target))
elif run_action == ShardingTestConfig.RUN_PARTIAL_TEST:
logging.info("SHARD %d RUNS TEST %s (%d of %d)", self.shard_index,
test_target, sub_shard_index + 1, sub_shard_count)
results.append(
- self._RunTest(
+ self.RunTest(
test_target,
shard_index=sub_shard_index,
shard_count=sub_shard_count))
@@ -822,7 +847,7 @@
logging.info("SHARD %d SKIP TEST %s", self.shard_index, test_target)
else:
# Run all tests and cases serially. No sharding enabled.
- results.append(self._RunTest(test_target))
+ results.append(self.RunTest(test_target))
return self._ProcessAllTestResults(results)
def GenerateCoverageReport(self):
@@ -957,6 +982,7 @@
launcher_args.append(abstract_launcher.ARG_DRYRUN)
logging.info("Initializing test runner")
+
runner = TestRunner(args.platform, args.config, args.loader_platform,
args.loader_config, args.device_id, args.target_name,
target_params, args.out_directory,
@@ -991,7 +1017,16 @@
return 1
if args.run:
- run_success = runner.RunAllTests()
+ if isinstance(args.target_name, list) and len(args.target_name) == 1:
+ r = runner.RunTest(args.target_name[0])
+ if r[5] == TargetStatus.OK:
+ return r[6]
+ elif r[5] == TargetStatus.NA:
+ return 0
+ else:
+ return 1
+ else:
+ run_success = runner.RunAllTests()
runner.GenerateCoverageReport()
diff --git a/starboard/win/shared/BUILD.gn b/starboard/win/shared/BUILD.gn
index 9501d8f..a99e7e8 100644
--- a/starboard/win/shared/BUILD.gn
+++ b/starboard/win/shared/BUILD.gn
@@ -30,16 +30,18 @@
cflags = [
"/EHsc", # C++ exceptions (required with /ZW)
- ]
-
- cflags += [
"/FU${msvc_path}/lib/x86/store/references/platform.winmd",
"/FU${windows_sdk_path}/References/$wdk_version/Windows.Foundation.FoundationContract/4.0.0.0/Windows.Foundation.FoundationContract.winmd",
"/FU${windows_sdk_path}/References/$wdk_version/Windows.Foundation.UniversalApiContract/14.0.0.0/Windows.Foundation.UniversalApiContract.winmd",
"/FU${windows_sdk_path}/References/$wdk_version/Windows.UI.ViewManagement.ViewManagementViewScalingContract/1.0.0.0/Windows.UI.ViewManagement.ViewManagementViewScalingContract.winmd",
- "/FU${windows_sdk_path}/References/$wdk_version/Windows.Xbox.ApplicationResourcesContract/2.0.0.0/Windows.Xbox.ApplicationResourcesContract.winmd",
- "/FU${windows_sdk_path}/References/$wdk_version/Windows.Xbox.Security.ApplicationSpecificDeviceAuthenticationContract/1.0.0.0/Windows.Xbox.Security.ApplicationSpecificDeviceAuthenticationContract.winmd",
]
+
+ if (is_internal_build) {
+ cflags += [
+ "/FU${windows_sdk_path}/References/$wdk_version/Windows.Xbox.ApplicationResourcesContract/2.0.0.0/Windows.Xbox.ApplicationResourcesContract.winmd",
+ "/FU${windows_sdk_path}/References/$wdk_version/Windows.Xbox.Security.ApplicationSpecificDeviceAuthenticationContract/1.0.0.0/Windows.Xbox.Security.ApplicationSpecificDeviceAuthenticationContract.winmd",
+ ]
+ }
}
static_library("starboard_platform") {
@@ -304,7 +306,6 @@
"//starboard/shared/win32/time_get_now.cc",
"//starboard/shared/win32/time_utils.h",
"//starboard/shared/win32/time_zone_get_current.cc",
- "//starboard/shared/win32/time_zone_get_name.cc",
"//starboard/shared/win32/video_decoder.cc",
"//starboard/shared/win32/video_decoder.h",
"//starboard/shared/win32/wasapi_include.h",
diff --git a/starboard/win/win32/BUILD.gn b/starboard/win/win32/BUILD.gn
index e458622..746db84 100644
--- a/starboard/win/win32/BUILD.gn
+++ b/starboard/win/win32/BUILD.gn
@@ -59,6 +59,7 @@
"//starboard/shared/win32/system_get_used_cpu_memory.cc",
"//starboard/shared/win32/system_raise_platform_error.cc",
"//starboard/shared/win32/system_symbolize.cc",
+ "//starboard/shared/win32/time_zone_get_name.cc",
"//starboard/shared/win32/window_create.cc",
"//starboard/shared/win32/window_destroy.cc",
"//starboard/shared/win32/window_get_platform_handle.cc",
diff --git a/starboard/win/win32/test_filters.py b/starboard/win/win32/test_filters.py
index cf8621f..98be41f 100644
--- a/starboard/win/win32/test_filters.py
+++ b/starboard/win/win32/test_filters.py
@@ -87,6 +87,7 @@
return [test_filter.DISABLE_TESTING]
else:
filters = super().GetTestFilters()
+ _FILTERED_TESTS.update(test_filter.EVERGREEN_ONLY_TESTS)
for target, tests in _FILTERED_TESTS.items():
filters.extend(test_filter.TestFilter(target, test) for test in tests)
if os.environ.get('EXPERIMENTAL_CI', '0') == '1':
diff --git a/starboard/xb1/BUILD.gn b/starboard/xb1/BUILD.gn
index 5a54c16..e4137bc 100644
--- a/starboard/xb1/BUILD.gn
+++ b/starboard/xb1/BUILD.gn
@@ -176,6 +176,10 @@
"//starboard/shared/win32/media_get_max_buffer_capacity.cc",
"//starboard/shared/win32/media_transform.cc",
"//starboard/shared/win32/media_transform.h",
+
+ # TODO (b/304335954): Use uwp implementation for correct IANA name once
+ # daylight savings offset is fixed.
+ "//starboard/shared/win32/time_zone_get_name.cc",
"//starboard/shared/win32/video_decoder.cc",
"//starboard/shared/win32/video_decoder.h",
"//starboard/shared/win32/win32_audio_decoder.cc",
@@ -213,8 +217,8 @@
if (is_internal_build) {
sources += [
"//internal/starboard/shared/uwp/keys.cc",
- "//internal/starboard/xb1/av1_video_decoder.cc",
- "//internal/starboard/xb1/av1_video_decoder.h",
+ "//internal/starboard/xb1/dav1d_video_decoder.cc",
+ "//internal/starboard/xb1/dav1d_video_decoder.h",
"//internal/starboard/xb1/drm_create_system.cc",
"//internal/starboard/xb1/internal_shims.cc",
"//internal/starboard/xb1/media_is_supported.cc",
@@ -234,7 +238,7 @@
"//starboard/shared/widevine:oemcrypto",
"//third_party/internal/ce_cdm/cdm:widevine_cdm_core",
"//third_party/internal/ce_cdm/cdm:widevine_ce_cdm_static",
- "//third_party/internal/libav1_xb1",
+ "//third_party/internal/dav1d_gpu/xb1:dav1d_xb1",
"//third_party/internal/libvpx_xb1",
]
} else {
diff --git a/starboard/xb1/launcher.py b/starboard/xb1/launcher.py
index 9be3550..583aa3b 100644
--- a/starboard/xb1/launcher.py
+++ b/starboard/xb1/launcher.py
@@ -62,12 +62,7 @@
# All other functions are automatically delegated using this function.
def __getattr__(self, fname):
-
- def method(*args):
- f = getattr(self.delegate, fname)
- return f(*args)
-
- return method
+ return getattr(self.delegate, fname)
def GetDeviceIp(self):
"""Gets the device IP. TODO: Implement."""
diff --git a/starboard/xb1/platform_configuration/BUILD.gn b/starboard/xb1/platform_configuration/BUILD.gn
index 68b7862..db2a0b2 100644
--- a/starboard/xb1/platform_configuration/BUILD.gn
+++ b/starboard/xb1/platform_configuration/BUILD.gn
@@ -59,7 +59,9 @@
]
}
- ldflags += [ "/DEBUG:FASTLINK" ]
+ if (is_qa || is_gold || !cobalt_fastbuild) {
+ ldflags += [ "/DEBUG:FASTLINK" ]
+ }
ldflags += [ "/NODEFAULTLIB" ]
arflags += [ "/NODEFAULTLIB" ]
diff --git a/starboard/xb1/shared/gpu_base_video_decoder.cc b/starboard/xb1/shared/gpu_base_video_decoder.cc
index 32749b1..84bab91 100644
--- a/starboard/xb1/shared/gpu_base_video_decoder.cc
+++ b/starboard/xb1/shared/gpu_base_video_decoder.cc
@@ -16,7 +16,9 @@
#include <d3d11_1.h>
#include <wrl/client.h>
+#include <algorithm>
+#include "starboard/once.h"
#include "starboard/shared/uwp/application_uwp.h"
#include "starboard/shared/uwp/async_utils.h"
#include "starboard/shared/uwp/decoder_utils.h"
@@ -53,11 +55,78 @@
constexpr int kMaxNumberOfPendingBuffers = 8;
// Limit the cached presenting images.
constexpr int kNumberOfCachedPresentingImage = 3;
+// The number of frame buffers in decoder
+constexpr int kNumOutputFrameBuffers = 7;
const char kDecoderThreadName[] = "gpu_video_decoder_thread";
-
} // namespace
+class GpuFrameBufferPool {
+ public:
+ HRESULT AllocateFrameBuffers(
+ uint16_t width,
+ uint16_t height,
+ DXGI_FORMAT dxgi_format,
+ Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device,
+ Microsoft::WRL::ComPtr<ID3D12Device> d3d12_device) {
+ HRESULT hr;
+ uint16_t number_of_buffers = kNumOutputFrameBuffers;
+ if (!frame_buffers_.empty()) {
+ auto& buffer = frame_buffers_.front();
+ D3D11_TEXTURE2D_DESC desc;
+ buffer->texture(0)->GetDesc(&desc);
+ if (desc.Format != dxgi_format || buffer->width() < width ||
+ buffer->height() < height ||
+ d3d11_device.Get() != buffer->device11().Get() ||
+ d3d12_device.Get() != buffer->device12().Get()) {
+ frame_buffers_.clear();
+ }
+ }
+ if (frame_buffers_.empty()) {
+ frame_buffers_.reserve(number_of_buffers);
+ while (number_of_buffers--) {
+ GpuVideoDecoderBase::GpuFrameBuffer* gpu_fb =
+ new GpuVideoDecoderBase::GpuFrameBuffer(width, height, dxgi_format,
+ d3d11_device, d3d12_device);
+ hr = gpu_fb->CreateTextures();
+ if (FAILED(hr)) {
+ frame_buffers_.clear();
+ return hr;
+ }
+ frame_buffers_.emplace_back(gpu_fb);
+ }
+ }
+ return S_OK;
+ }
+
+ GpuVideoDecoderBase::GpuFrameBuffer* GetFreeBuffer() {
+ SB_DCHECK(!frame_buffers_.empty());
+ auto iter = std::find_if(
+ frame_buffers_.begin(), frame_buffers_.end(),
+ [](const auto& frame_buffer) { return frame_buffer->HasOneRef(); });
+ if (iter == frame_buffers_.end())
+ return nullptr;
+ else
+ return iter->get();
+ }
+
+ bool CheckIfAllBuffersAreReleased() {
+ for (auto&& frame_buffer : frame_buffers_) {
+ if (!frame_buffer->HasOneRef())
+ return false;
+ }
+ return true;
+ }
+
+ void Clear() { frame_buffers_.clear(); }
+
+ private:
+ std::vector<scoped_refptr<GpuVideoDecoderBase::GpuFrameBuffer>>
+ frame_buffers_;
+};
+
+SB_ONCE_INITIALIZE_FUNCTION(GpuFrameBufferPool, GetGpuFrameBufferPool);
+
class GpuVideoDecoderBase::GPUDecodeTargetPrivate
: public SbDecodeTargetPrivate {
public:
@@ -85,7 +154,6 @@
info.is_opaque = true;
info.width = image->width();
info.height = image->height();
-
GLuint gl_textures_yuv[kNumberOfPlanes] = {};
glGenTextures(kNumberOfPlanes, gl_textures_yuv);
SB_DCHECK(glGetError() == GL_NO_ERROR);
@@ -160,19 +228,109 @@
void* egl_config_;
};
+GpuVideoDecoderBase::GpuFrameBuffer::GpuFrameBuffer(
+ uint16_t width,
+ uint16_t height,
+ DXGI_FORMAT dxgi_format,
+ Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device,
+ Microsoft::WRL::ComPtr<ID3D12Device> d3d12_device)
+ : d3d11_device_(d3d11_device), d3d12_device_(d3d12_device) {
+ SB_DCHECK(d3d11_device_);
+ SB_DCHECK(d3d12_device_);
+
+ texture_desc_.Format = dxgi_format;
+ texture_desc_.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
+ texture_desc_.DepthOrArraySize = 1;
+ texture_desc_.MipLevels = 1;
+ texture_desc_.SampleDesc.Count = 1;
+ texture_desc_.SampleDesc.Quality = 0;
+ texture_desc_.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ texture_desc_.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
+
+ width_ = width;
+ height_ = height;
+}
+
+HRESULT GpuVideoDecoderBase::GpuFrameBuffer::CreateTextures() {
+ const D3D12_HEAP_PROPERTIES kHeapPropertyTypeDefault = {
+ D3D12_HEAP_TYPE_DEFAULT, D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
+ D3D12_MEMORY_POOL_UNKNOWN, 1, 1};
+ HRESULT hr = E_FAIL;
+ for (unsigned int i = 0; i < kNumberOfPlanes; i++) {
+ const int subsampling = i > 0;
+ const int plane_width =
+ (texture_desc_.Format == DXGI_FORMAT_R10G10B10A2_UNORM)
+ ? (((width_ + subsampling) >> subsampling) + 2) / 3
+ : ((width_ + subsampling) >> subsampling);
+ const int plane_height = (height_ + subsampling) >> subsampling;
+
+ // Create interop resources.
+ texture_desc_.Width = plane_width;
+ texture_desc_.Height = plane_height;
+ hr = d3d12_device_->CreateCommittedResource(
+ &kHeapPropertyTypeDefault, D3D12_HEAP_FLAG_SHARED, &texture_desc_,
+ D3D12_RESOURCE_STATE_RENDER_TARGET, 0,
+ IID_PPV_ARGS(&d3d12_resources_[i]));
+ SB_DCHECK(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Lowering the priority of texture reduces the amount of texture
+ // thrashing when the Xbox attempts to transfer textures to faster
+ // memory as it become more reluctant to be moved.
+ Microsoft::WRL::ComPtr<ID3D12Device1> d3d12_device1;
+ if (SUCCEEDED(d3d12_device_.As(&d3d12_device1)) && d3d12_device1) {
+ Microsoft::WRL::ComPtr<ID3D12Pageable> d3d12_pageable;
+ if (SUCCEEDED(d3d12_resources_[i].As(&d3d12_pageable)) &&
+ d3d12_pageable) {
+ D3D12_RESIDENCY_PRIORITY priority = D3D12_RESIDENCY_PRIORITY_LOW;
+ hr = d3d12_device1->SetResidencyPriority(
+ 1, d3d12_pageable.GetAddressOf(), &priority);
+ SB_DCHECK(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+ }
+
+ HANDLE interop_handle = 0;
+ hr = d3d12_device_->CreateSharedHandle(d3d12_resources_[i].Get(), 0,
+ GENERIC_ALL, NULL, &interop_handle);
+ SB_DCHECK(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = d3d11_device_->OpenSharedResource1(interop_handle,
+ IID_PPV_ARGS(&d3d11_textures_[i]));
+ SB_DCHECK(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+ CloseHandle(interop_handle);
+ }
+ return S_OK;
+}
+
GpuVideoDecoderBase::GpuVideoDecoderBase(
SbDecodeTargetGraphicsContextProvider*
decode_target_graphics_context_provider,
const VideoStreamInfo& video_stream_info,
bool is_hdr_video,
+ bool is_10x3_preferred,
const ComPtr<ID3D12Device>& d3d12_device,
+ const ComPtr<ID3D12Heap> d3d12OutputPoolBufferHeap,
void* d3d12_queue)
: decode_target_context_runner_(decode_target_graphics_context_provider),
is_hdr_video_(is_hdr_video),
+ is_10x3_preferred_(is_10x3_preferred),
d3d12_device_(d3d12_device),
- d3d12_queue_(d3d12_queue) {
+ d3d12_queue_(d3d12_queue),
+ d3d12FrameBuffersHeap_(d3d12OutputPoolBufferHeap),
+ frame_buffers_condition_(frame_buffers_mutex_) {
SB_DCHECK(d3d12_device_);
SB_DCHECK(d3d12_queue_);
+ SB_DCHECK(d3d12FrameBuffersHeap_);
egl_display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
EGLint attribute_list[] = {EGL_SURFACE_TYPE, // this must be first
@@ -215,6 +373,7 @@
SB_DCHECK(written_inputs_.empty());
SB_DCHECK(output_queue_.empty());
SB_DCHECK(decoder_behavior_.load() == kDecodingStopped);
+ SB_DCHECK(GetGpuFrameBufferPool()->CheckIfAllBuffersAreReleased());
// All presenting decode targets should be released.
SB_DCHECK(presenting_decode_targets_.empty());
@@ -236,9 +395,18 @@
error_cb_ = error_cb;
}
+size_t GpuVideoDecoderBase::GetPrerollFrameCount() const {
+ // The underlying decoder has its own output queue. We notify the underlying
+ // decoder to preroll frames once we receive first needed frame. Then the
+ // underlying decoder will delay outputs until it has enough prerolled frames.
+ // When we receive the second output frame, the underlying decoder should
+ // already have enough prerolled frames in its own output queue. So, we always
+ // return 2 here.
+ return 2;
+}
+
size_t GpuVideoDecoderBase::GetMaxNumberOfCachedFrames() const {
- return GetMaxNumberOfCachedFramesInternal() -
- number_of_presenting_decode_targets_;
+ return GetMaxNumberOfCachedFramesInternal() + kNumOutputFrameBuffers;
}
void GpuVideoDecoderBase::WriteInputBuffers(const InputBuffers& input_buffers) {
@@ -265,6 +433,7 @@
{
ScopedLock pending_inputs_lock(pending_inputs_mutex_);
pending_inputs_.push_back(input_buffer);
+
needs_more_input = pending_inputs_.size() < kMaxNumberOfPendingBuffers;
}
decoder_behavior_.store(kDecodingFrames);
@@ -309,7 +478,10 @@
decoder_thread_.reset();
}
pending_inputs_.clear();
- written_inputs_.clear();
+ {
+ ScopedLock input_queue_lock(written_inputs_mutex_);
+ written_inputs_.clear();
+ }
// Release all frames after decoder thread is destroyed.
decoder_status_cb_(kReleaseAllFrames, nullptr);
{
@@ -369,30 +541,26 @@
return decoder_thread_->job_queue()->BelongsToCurrentThread();
}
-void GpuVideoDecoderBase::OnOutputRetrieved(
+int GpuVideoDecoderBase::OnOutputRetrieved(
const scoped_refptr<DecodedImage>& image) {
SB_DCHECK(decoder_thread_);
SB_DCHECK(decoder_status_cb_);
SB_DCHECK(image);
if (decoder_behavior_.load() == kResettingDecoder || error_occured_) {
- return;
- }
-
- if (!BelongsToDecoderThread()) {
- decoder_thread_->job_queue()->Schedule(
- std::bind(&GpuVideoDecoderBase::OnOutputRetrieved, this, image));
- return;
+ return 0;
}
SbTime timestamp = image->timestamp();
- const auto iter = FindByTimestamp(written_inputs_, timestamp);
- SB_DCHECK(iter != written_inputs_.cend());
- if (is_hdr_video_) {
- image->AttachColorMetadata((*iter)->video_stream_info().color_metadata);
+ {
+ ScopedLock input_queue_lock(written_inputs_mutex_);
+ const auto iter = FindByTimestamp(written_inputs_, timestamp);
+ SB_DCHECK(iter != written_inputs_.cend());
+ if (is_hdr_video_) {
+ image->AttachColorMetadata((*iter)->video_stream_info().color_metadata);
+ }
+ written_inputs_.erase(iter);
}
- written_inputs_.erase(iter);
-
scoped_refptr<VideoFrameImpl> frame(new VideoFrameImpl(
timestamp, std::bind(&GpuVideoDecoderBase::DeleteVideoFrame, this,
std::placeholders::_1)));
@@ -400,10 +568,21 @@
decoder_behavior_.load() == kEndingStream ? kBufferFull : kNeedMoreInput,
frame);
+ // The underlying decoder relies on the return value of OnOutputRetrieved() to
+ // determine stream preroll status. The underlying decoder will start
+ // prorolling at the first time it receives 1 from OnOutputRetrieved(). In
+ // other words, if OnOutputRetrieved() returns 1, the underlying decoder will
+ // delay next output until it has enough prerolled frames inside the
+ // underlying decoder.
if (!frame->HasOneRef()) {
ScopedLock output_queue_lock(output_queue_mutex_);
output_queue_.push_back(image);
+ if (is_waiting_frame_after_drain_) {
+ is_waiting_frame_after_drain_ = false;
+ return 1;
+ }
}
+ return 0;
}
void GpuVideoDecoderBase::OnDecoderDrained() {
@@ -412,9 +591,7 @@
SB_DCHECK(decoder_behavior_.load() == kEndingStream ||
decoder_behavior_.load() == kResettingDecoder);
- if (decoder_behavior_.load() == kResettingDecoder || error_occured_) {
- return;
- }
+ is_waiting_frame_after_drain_ = true;
if (!BelongsToDecoderThread()) {
decoder_thread_->job_queue()->Schedule(
@@ -422,7 +599,6 @@
return;
}
- SB_DCHECK(written_inputs_.empty());
if (decoder_behavior_.load() == kEndingStream) {
decoder_status_cb_(kBufferFull, VideoFrame::CreateEOSFrame());
}
@@ -445,16 +621,6 @@
}
}
-bool GpuVideoDecoderBase::IsCacheFull() {
- SB_DCHECK(decoder_thread_);
- SB_DCHECK(BelongsToDecoderThread());
-
- ScopedLock output_queue_lock(output_queue_mutex_);
- return written_inputs_.size() + output_queue_.size() +
- number_of_presenting_decode_targets_ >=
- GetMaxNumberOfCachedFramesInternal();
-}
-
void GpuVideoDecoderBase::DecodeOneBuffer() {
SB_DCHECK(decoder_thread_);
SB_DCHECK(BelongsToDecoderThread());
@@ -463,20 +629,28 @@
return;
}
- if (IsCacheFull()) {
- decoder_thread_->job_queue()->Schedule(
- std::bind(&GpuVideoDecoderBase::DecodeOneBuffer, this),
- kSbTimeMillisecond);
- return;
- }
-
+ // Both decoders av1 & vp9 return decoded frames in separate thread,
+ // so there isn't danger of deadlock in DecodeOneBuffer() and there isn't
+ // necessity of IsCacheFull call
+ scoped_refptr<InputBuffer> input = 0;
+ bool needs_more_input = false;
{
ScopedLock pending_inputs_lock(pending_inputs_mutex_);
SB_DCHECK(!pending_inputs_.empty());
- written_inputs_.push_back(pending_inputs_.front());
+ input = pending_inputs_.front();
pending_inputs_.pop_front();
+ if (pending_inputs_.size() < kMaxNumberOfPendingBuffers) {
+ needs_more_input = true;
+ }
}
- DecodeInternal(written_inputs_.back());
+ {
+ ScopedLock input_queue_lock(written_inputs_mutex_);
+ written_inputs_.push_back(input);
+ }
+ if (needs_more_input) {
+ decoder_status_cb_(kNeedMoreInput, nullptr);
+ }
+ DecodeInternal(input);
}
void GpuVideoDecoderBase::DecodeEndOfStream() {
@@ -509,6 +683,9 @@
if (!is_drain_decoder_called_) {
is_drain_decoder_called_ = true;
DrainDecoderInternal();
+ // DrainDecoderInternal is sync command, after it finished, we can be sure
+ // that drain really completed.
+ OnDecoderDrained();
}
}
@@ -558,6 +735,62 @@
number_of_presenting_decode_targets_ = 0;
}
+HRESULT GpuVideoDecoderBase::AllocateFrameBuffers(uint16_t width,
+ uint16_t height) {
+ HRESULT hr = S_OK;
+ DXGI_FORMAT dxgi_format =
+ is_hdr_video_ ? (is_10x3_preferred_ ? DXGI_FORMAT_R10G10B10A2_UNORM
+ : DXGI_FORMAT_R16_UNORM)
+ : DXGI_FORMAT_R8_UNORM;
+ return GetGpuFrameBufferPool()->AllocateFrameBuffers(
+ width, height, dxgi_format, d3d11_device_, d3d12_device_);
+}
+
+void GpuVideoDecoderBase::ReleaseFrameBuffer(GpuFrameBuffer* frame_buffer) {
+ SB_DCHECK(frame_buffer);
+ ScopedLock lock(frame_buffers_mutex_);
+ frame_buffer->Release();
+ SB_DCHECK(frame_buffer->HasOneRef());
+ frame_buffers_condition_.Signal();
+}
+
+void GpuVideoDecoderBase::ClearFrameBuffersPool() {
+ GetGpuFrameBufferPool()->Clear();
+}
+
+GpuVideoDecoderBase::GpuFrameBuffer*
+GpuVideoDecoderBase::GetAvailableFrameBuffer(uint16_t width, uint16_t height) {
+ if (decoder_behavior_.load() == kResettingDecoder) {
+ return nullptr;
+ }
+
+ GpuFrameBuffer* frame_buffer = nullptr;
+ bool is_resetting = false;
+ while (!frame_buffer) {
+ ScopedLock lock(frame_buffers_mutex_);
+ frame_buffer = GetGpuFrameBufferPool()->GetFreeBuffer();
+ // Wait until we get next free frame buffer.
+ if (!frame_buffer) {
+ if (is_resetting) {
+ // We should have enough free frame buffers during resetting. If that
+ // error happens it means that the frames are not released properly by
+ // either GpuVideoDecoderBase or VideoRenderer.
+ SB_NOTREACHED();
+ ReportError(kSbPlayerErrorDecode,
+ "Timed out on waiting for available frame buffer.");
+ return nullptr;
+ }
+ is_resetting = decoder_behavior_.load() == kResettingDecoder;
+ frame_buffers_condition_.WaitTimed(50 * kSbTimeMillisecond);
+ continue;
+ }
+ }
+
+ // Increment the refcount for |frame_buffer| so that its data buffer
+ // persists until ReleaseFrameBuffer is called.
+ frame_buffer->AddRef();
+ return frame_buffer;
+}
} // namespace shared
} // namespace xb1
} // namespace starboard
diff --git a/starboard/xb1/shared/gpu_base_video_decoder.h b/starboard/xb1/shared/gpu_base_video_decoder.h
index e6d2eae..34d4533 100644
--- a/starboard/xb1/shared/gpu_base_video_decoder.h
+++ b/starboard/xb1/shared/gpu_base_video_decoder.h
@@ -51,6 +51,54 @@
~GpuVideoDecoderBase() override;
+ class GpuFrameBuffer : public RefCountedThreadSafe<GpuFrameBuffer> {
+ public:
+ GpuFrameBuffer(uint16_t width,
+ uint16_t height,
+ DXGI_FORMAT dxgi_format,
+ Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device,
+ Microsoft::WRL::ComPtr<ID3D12Device> d3d12_device);
+
+ HRESULT CreateTextures();
+
+ const Microsoft::WRL::ComPtr<ID3D12Resource>& resource(int index) const {
+ SB_DCHECK(index < kNumberOfPlanes);
+ SB_DCHECK(d3d12_resources_[index] != nullptr);
+ return d3d12_resources_[index];
+ }
+
+ const Microsoft::WRL::ComPtr<ID3D11Texture2D>& texture(int index) const {
+ SB_DCHECK(index < kNumberOfPlanes);
+ SB_DCHECK(d3d11_textures_[index] != nullptr);
+ return d3d11_textures_[index];
+ }
+
+ uint16_t width() const { return width_; }
+ uint16_t height() const { return height_; }
+ const Microsoft::WRL::ComPtr<ID3D11Device1>& device11() {
+ return d3d11_device_;
+ }
+ const Microsoft::WRL::ComPtr<ID3D12Device>& device12() {
+ return d3d12_device_;
+ }
+
+ private:
+ uint16_t width_ = 0;
+ uint16_t height_ = 0;
+ D3D12_RESOURCE_DESC texture_desc_ = {0};
+
+ Microsoft::WRL::ComPtr<ID3D12Resource> d3d12_resources_[kNumberOfPlanes];
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_textures_[kNumberOfPlanes];
+
+ const Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device_;
+ const Microsoft::WRL::ComPtr<ID3D12Device> d3d12_device_;
+ };
+ void ReleaseFrameBuffer(GpuFrameBuffer* frame_buffer);
+ int GetWidth() { return frame_width_; }
+ int GetHeight() { return frame_height_; }
+ bool IsHdrVideo() { return is_hdr_video_; }
+ static void ClearFrameBuffersPool();
+
protected:
typedef ::starboard::shared::starboard::media::VideoStreamInfo
VideoStreamInfo;
@@ -116,16 +164,20 @@
kEndingStream
};
- GpuVideoDecoderBase(SbDecodeTargetGraphicsContextProvider*
- decode_target_graphics_context_provider,
- const VideoStreamInfo& video_stream_info,
- bool is_hdr_video,
- const Microsoft::WRL::ComPtr<ID3D12Device>& d3d12_device,
- void* d3d12_queue);
+ GpuVideoDecoderBase(
+ SbDecodeTargetGraphicsContextProvider*
+ decode_target_graphics_context_provider,
+ const VideoStreamInfo& video_stream_info,
+ bool is_hdr_video,
+ bool is_10x3_preferred,
+ const Microsoft::WRL::ComPtr<ID3D12Device>& d3d12_device,
+ const Microsoft::WRL::ComPtr<ID3D12Heap> d3d12OutputPoolBufferHeap,
+ void* d3d12_queue);
// VideoDecoder methods
void Initialize(const DecoderStatusCB& decoder_status_cb,
const ErrorCB& error_cb) final;
+ size_t GetPrerollFrameCount() const final;
SbTime GetPrerollTimeout() const final { return kSbTimeMax; }
size_t GetMaxNumberOfCachedFrames() const override;
@@ -135,19 +187,23 @@
SbDecodeTarget GetCurrentDecodeTarget() final;
// Methods for inherited classes to implement.
- virtual size_t GetMaxNumberOfCachedFramesInternal() const = 0;
virtual void InitializeCodecIfNeededInternal() = 0;
virtual void DecodeInternal(
const scoped_refptr<InputBuffer>& input_buffer) = 0;
virtual void DrainDecoderInternal() = 0;
+ virtual size_t GetMaxNumberOfCachedFramesInternal() const = 0;
bool BelongsToDecoderThread() const;
- void OnOutputRetrieved(const scoped_refptr<DecodedImage>& image);
void OnDecoderDrained();
void ClearCachedImages();
void ReportError(const SbPlayerError error, const std::string& error_message);
+ int OnOutputRetrieved(const scoped_refptr<DecodedImage>& image);
+ HRESULT AllocateFrameBuffers(uint16_t width, uint16_t height);
+ GpuVideoDecoderBase::GpuFrameBuffer* GetAvailableFrameBuffer(uint16_t width,
+ uint16_t height);
const bool is_hdr_video_;
+ const bool is_10x3_preferred_;
int frame_width_;
int frame_height_;
atomic_integral<RetrievingBehavior> decoder_behavior_{kDecodingStopped};
@@ -156,12 +212,16 @@
// These are platform-specific objects required to create and use a codec.
Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device_;
Microsoft::WRL::ComPtr<ID3D12Device> d3d12_device_;
+ Microsoft::WRL::ComPtr<ID3D12Heap>
+ d3d12FrameBuffersHeap_; // output buffers queue memory
void* d3d12_queue_ = nullptr;
+ Mutex frame_buffers_mutex_;
+ ConditionVariable frame_buffers_condition_;
+
private:
class GPUDecodeTargetPrivate;
- bool IsCacheFull();
void DecodeOneBuffer();
void DecodeEndOfStream();
void DrainDecoder();
@@ -183,7 +243,9 @@
// |pending_inputs_| is shared between player main thread and decoder thread.
Mutex pending_inputs_mutex_;
std::deque<scoped_refptr<InputBuffer>> pending_inputs_;
- // |written_inputs_| is only accessed on decoder thread.
+ // |written_inputs_| is shared between decoder thread and underlying decoder
+ // output thread.
+ Mutex written_inputs_mutex_;
std::vector<scoped_refptr<InputBuffer>> written_inputs_;
// |output_queue_| is shared between decoder thread and render thread.
Mutex output_queue_mutex_;
@@ -195,6 +257,7 @@
SbMediaColorMetadata last_presented_color_metadata_ = {};
bool is_drain_decoder_called_ = false;
+ bool is_waiting_frame_after_drain_ = false;
bool needs_hdr_metadata_update_ = true;
};
diff --git a/starboard/xb1/shared/internal_shims.h b/starboard/xb1/shared/internal_shims.h
index 9cd7153..877ed8b 100644
--- a/starboard/xb1/shared/internal_shims.h
+++ b/starboard/xb1/shared/internal_shims.h
@@ -31,7 +31,7 @@
Platform::String ^ GetCertScope();
-void GetSignature(Windows::Storage::Streams::IBuffer ^ message_buffer,
+bool GetSignature(Windows::Storage::Streams::IBuffer ^ message_buffer,
Windows::Storage::Streams::IBuffer ^ *signature);
} // namespace shared
diff --git a/starboard/xb1/shared/internal_stubs.cc b/starboard/xb1/shared/internal_stubs.cc
index a8f6ba5..b243b4b 100644
--- a/starboard/xb1/shared/internal_stubs.cc
+++ b/starboard/xb1/shared/internal_stubs.cc
@@ -33,8 +33,10 @@
Platform::String ^ GetCertScope() { return ""; }
-void GetSignature(Windows::Storage::Streams::IBuffer ^ message_buffer,
- Windows::Storage::Streams::IBuffer ^ *signature) {}
+bool GetSignature(Windows::Storage::Streams::IBuffer ^ message_buffer,
+ Windows::Storage::Streams::IBuffer ^ *signature) {
+ return false;
+}
// clang-format on
} // namespace shared
diff --git a/starboard/xb1/templates/AppxManifest.xml.template b/starboard/xb1/templates/AppxManifest.xml.template
index 85b4a30..5a2d9f2 100644
--- a/starboard/xb1/templates/AppxManifest.xml.template
+++ b/starboard/xb1/templates/AppxManifest.xml.template
@@ -24,10 +24,10 @@
</Properties>
<Dependencies>
<TargetDeviceFamily
- {% if __CONFIG__ == 'gold' %}
- Name="Windows.Xbox"
- {% else %}
+ {% if __CONFIG__ == 'debug' or __CONFIG__ == 'devel' %}
Name="Windows.Universal"
+ {% else %}
+ Name="Windows.Xbox"
{% endif %}
MinVersion="10.0.22000.0"
MaxVersionTested="10.0.22000.0" />
diff --git a/starboard/xb1/test_filters.py b/starboard/xb1/test_filters.py
index e545d5b..6bb45b5 100644
--- a/starboard/xb1/test_filters.py
+++ b/starboard/xb1/test_filters.py
@@ -59,6 +59,7 @@
return [test_filter.DISABLE_TESTING]
filters = super().GetTestFilters()
+ _FILTERED_TESTS.update(test_filter.EVERGREEN_ONLY_TESTS)
for target, tests in _FILTERED_TESTS.items():
filters.extend(test_filter.TestFilter(target, test) for test in tests)
return filters
diff --git a/starboard/xb1/tools/packager.py b/starboard/xb1/tools/packager.py
index 9503f20..a5bcf60 100644
--- a/starboard/xb1/tools/packager.py
+++ b/starboard/xb1/tools/packager.py
@@ -52,7 +52,8 @@
'youtubetv': _INTERNAL_CERT_PATH,
}
_DEFAULT_SDK_BIN_DIR = 'C:\\Program Files (x86)\\Windows Kits\\10\\bin'
-_DEFAULT_WIN_SDK_VERSION = '10.0.22000.0'
+_DEFAULT_WIN_SDK_VERSION = '10.0.22621.0'
+_BACKUP_WIN_SDK_VERSION = '10.0.22000.0'
_SOURCE_SPLASH_SCREEN_SUB_PATH = os.path.join('internal', 'cobalt', 'browser',
'splash_screen')
# The splash screen file referenced in starboard/xb1/shared/configuration.cc
@@ -67,7 +68,7 @@
}
-def _SelectBestPath(os_var_name, path):
+def _SelectBestPath(os_var_name: str, path: str) -> str:
if os_var_name in os.environ:
return os.environ[os_var_name]
if os.path.exists(path):
@@ -86,6 +87,18 @@
return os.path.join(src_dir, _SOURCE_SPLASH_SCREEN_SUB_PATH)
+def GetWinToolsPath() -> str:
+ windows_sdk_bin_dir = _SelectBestPath('WindowsSdkBinPath',
+ _DEFAULT_SDK_BIN_DIR)
+
+ # This check can be removed once it's confirmed our builders are using the new
+ # version of the win sdk.
+ path = os.path.join(windows_sdk_bin_dir, _DEFAULT_WIN_SDK_VERSION, 'x64')
+ if os.path.exists(path):
+ return path
+ return os.path.join(windows_sdk_bin_dir, _BACKUP_WIN_SDK_VERSION, 'x64')
+
+
class Package(package.PackageBase):
"""A class representing an installable UWP Appx package."""
@@ -137,6 +150,15 @@
return
shutil.copy(src_splash_screen_file, splash_screen_dir)
+ def _CopyAppxData(self):
+ appx_data_output_dir = os.path.join(self.appx_folder_location, 'content',
+ 'data')
+ source_dir = os.path.join(self.source_dir, 'appx', 'content', 'data')
+ if not os.path.exists(source_dir):
+ logging.error('Failed to find source content in: %s', source_dir)
+ return
+ shutil.copytree(source_dir, appx_data_output_dir)
+
@classmethod
def SupportedPlatforms(cls):
if platform.system() == 'Windows':
@@ -145,10 +167,7 @@
return []
def __init__(self, publisher, product, **kwargs):
- windows_sdk_bin_dir = _SelectBestPath('WindowsSdkBinPath',
- _DEFAULT_SDK_BIN_DIR)
- self.windows_sdk_host_tools = os.path.join(windows_sdk_bin_dir,
- _DEFAULT_WIN_SDK_VERSION, 'x64')
+ self.windows_sdk_host_tools = GetWinToolsPath()
self.publisher = publisher
self.product = product
super().__init__(**kwargs)
@@ -161,6 +180,11 @@
if self.publisher:
self._UpdateAppxManifestPublisher(publisher)
+ # For YouTubeTV and MainAppBeta move the appx data content to the correct
+ # appx directory.
+ if (self.product in ['youtubetv', 'mainappbeta']):
+ self._CopyAppxData()
+
# Remove any previous splash screen from content.
self._CleanSplashScreenDir()
# Copy the correct splash screen into content.
diff --git a/starboard/xb1/tools/xb1_launcher.py b/starboard/xb1/tools/xb1_launcher.py
index 969a5ae..b83a9e6 100644
--- a/starboard/xb1/tools/xb1_launcher.py
+++ b/starboard/xb1/tools/xb1_launcher.py
@@ -80,6 +80,7 @@
from starboard.shared.win32 import mini_dump_printer
from starboard.tools import abstract_launcher
+from starboard.tools.abstract_launcher import TargetStatus
from starboard.tools import net_args
from starboard.tools import net_log
from starboard.xb1.tools import packager
@@ -88,13 +89,29 @@
_ARGS_DIRECTORY = 'content/data/arguments'
_STARBOARD_ARGUMENTS_FILE = 'starboard_arguments.txt'
_DEFAULT_PACKAGE_NAME = 'GoogleInc.YouTube'
+_STUB_PACKAGE_NAME = 'Microsoft.Title.StubApp'
+_DEBUG_VC_LIBS_PACKAGE_NAME = 'Microsoft.VCLibs.140.00.Debug'
_DEFAULT_APPX_NAME = 'cobalt.appx'
_DEFAULT_STAGING_APP_NAME = 'appx'
+_EXTENSION_SDK_DIR = os.path.realpath(
+ os.path.expandvars('%ProgramFiles(x86)%\\Microsoft SDKs'
+ '\\Windows Kits\\10\\ExtensionSDKs'))
+_DEBUG_VC_LIBS_PATH = os.path.join(_EXTENSION_SDK_DIR, 'Microsoft.VCLibs',
+ '14.0', 'Appx', 'Debug', 'x64',
+ 'Microsoft.VCLibs.x64.Debug.14.00.appx')
_XB1_LOG_FILE_PARAM = 'xb1_log_file'
_XB1_PORT = 11443
_XB1_NET_LOG_PORT = 49353
_XB1_NET_ARG_PORT = 49355
+# Number of times a test will try or retry.
+_TEST_MAX_TRIES = 4
+# Seconds to wait between retries (scales with backoff factor).
+_TEST_RETRY_WAIT = 8
+# Amount to multiply retry time with each failed attempt (i.e. 2 doubles the
+# amount of time to wait between retries).
+_TEST_RETRY_BACKOFF_FACTOR = 2
+
_PROCESS_TIMEOUT = 60 * 5.0
_PROCESS_KILL_TIMEOUT_SECONDS = 5.0
@@ -363,10 +380,12 @@
self._network_api.SetXboxLiveSignedInUserState(users[0]['EmailAddress'],
True)
- def WinAppDeployCmd(self, command):
+ def WinAppDeployCmd(self, command: str):
try:
- out = subprocess.check_output('WinAppDeployCmd ' + command + ' -ip ' +
- self.GetDeviceIp()).decode()
+ exe_path = os.path.join(packager.GetWinToolsPath(), 'WinAppDeployCmd.exe')
+ command_str = f'{exe_path} {command} -ip {self.GetDeviceIp()}'
+ self._LogLn('Running: ' + command_str)
+ out = subprocess.check_output(command_str).decode()
except subprocess.CalledProcessError as e:
self._LogLn(e.output)
raise e
@@ -392,7 +411,9 @@
for package in packages:
try:
package_full_name = package['PackageFullName']
- if package_full_name.find(_DEFAULT_PACKAGE_NAME) != -1:
+ if package_full_name.find(
+ _DEFAULT_PACKAGE_NAME) != -1 or package_full_name.find(
+ _STUB_PACKAGE_NAME) != -1:
if package_full_name not in uninstalled_packages:
self._LogLn('Existing YouTube app found on device. Uninstalling: ' +
package_full_name)
@@ -404,6 +425,9 @@
except subprocess.CalledProcessError as err:
self._LogLn(err.output)
+ def DeleteLooseApps(self):
+ self._network_api.ClearLooseAppFiles()
+
def Deploy(self):
# starboard_arguments.txt is packaged with the appx. It instructs the app
# to wait for the NetArgs thread to send command-line args via the socket.
@@ -415,53 +439,85 @@
raise IOError('Packaged appx not found in package directory. Perhaps '
'package_cobalt script did not complete successfully.')
- existing_package = self.CheckPackageIsDeployed()
+ existing_package = self.CheckPackageIsDeployed(_DEFAULT_PACKAGE_NAME)
if existing_package:
self._LogLn('Existing YouTube app found on device. Uninstalling.')
self.WinAppDeployCmd('uninstall -package ' + existing_package)
+ if not self.CheckPackageIsDeployed(_DEBUG_VC_LIBS_PACKAGE_NAME):
+ self._LogLn('Required dependency missing. Attempting to install.')
+ self.WinAppDeployCmd(f'install -file "{_DEBUG_VC_LIBS_PATH}"')
+
self._LogLn('Deleting temporary files')
self._network_api.ClearTempFiles()
try:
- self._LogLn('Installing appx file ' + appx_package_file)
- self.WinAppDeployCmd('install -file ' + appx_package_file)
+ self.WinAppDeployCmd(f'install -file {appx_package_file}')
except subprocess.CalledProcessError:
# Install exited with non-zero status code, clear everything out, restart,
# and attempt another install.
self._LogLn('Error installing appx. Attempting a clean install...')
self.UninstallSubPackages()
+ self.DeleteLooseApps()
self.RestartDevkit()
- self.WinAppDeployCmd('install -file ' + appx_package_file)
+ self.WinAppDeployCmd(f'install -file {appx_package_file}')
# Cleanup starboard arguments file.
self.InstallStarboardArgument(None)
# Validate that app was installed correctly by checking to make sure
# that the full package name can now be found.
- def CheckPackageIsDeployed(self):
+ def CheckPackageIsDeployed(self, package_name=_DEFAULT_PACKAGE_NAME):
package_list = self.WinAppDeployCmd('list')
- package_index = package_list.find(_DEFAULT_PACKAGE_NAME)
+ package_index = package_list.find(package_name)
if package_index == -1:
return False
return package_list[package_index:].split('\n')[0].strip()
+ def RunTest(self, appx_name: str):
+ self.net_args_thread = None
+ attempt_num = 0
+ retry_wait_s = _TEST_RETRY_WAIT
+ while attempt_num < _TEST_MAX_TRIES:
+ if not self.net_args_thread or not self.net_args_thread.is_alive():
+ # This thread must start before the app executes or else it is possible
+ # the app will hang at _network_api.ExecuteBinary()
+ self.net_args_thread = net_args.NetArgsThread(self.device_id,
+ _XB1_NET_ARG_PORT,
+ self._target_args)
+ if self._network_api.ExecuteBinary(_DEFAULT_PACKAGE_NAME, appx_name):
+ break
+
+ if not self.net_args_thread.ArgsSent():
+ self._LogLn(
+ 'Net Args were not sent to the test! This will likely cause '
+ 'the test to fail!')
+ attempt_num += 1
+ self._LogLn(f'Retry attempt {attempt_num}.')
+ time.sleep(retry_wait_s)
+ retry_wait_s *= _TEST_RETRY_BACKOFF_FACTOR
+ if hasattr(self, 'net_args_thread'):
+ self.net_args_thread.join()
+
+ def InitDevice(self):
+ if not self._network_api.IsInDevMode():
+ raise IOError('\n\n**** Please set the XBOX at ' + self._device_id +
+ ' to dev mode!!!! ****\n')
+ self.SignIn()
+
def Run(self):
# Only upload and install Appx on the first run.
if FirstRun():
if self._do_restart:
self.RestartDevkit()
- if not self._network_api.IsInDevMode():
- raise IOError('\n\n**** Please set the XBOX at ' + self._device_id +
- ' to dev mode!!!! ****\n')
- self.SignIn()
+ self.InitDevice()
if self._do_deploy:
self.Deploy()
else:
self._LogLn('Skipping deploy step.')
- if not self.CheckPackageIsDeployed():
+ if not self.CheckPackageIsDeployed(_DEFAULT_PACKAGE_NAME):
raise IOError('Could not resolve ' + _DEFAULT_PACKAGE_NAME + ' to\n' +
'it\'s full package name after install! This means that' +
'\n the package is not deployed correctly!\n\n')
@@ -470,14 +526,15 @@
self._LogLn('Skipping running step.')
return 0
+ status, _ = self.Run2()
+ if status == TargetStatus.CRASH:
+ return 1
+ else:
+ return 0
+
+ def Run2(self):
try:
self.Kill() # Kill existing running app.
-
- # These threads must start before the app executes or else it is possible
- # the app will hang at _network_api.ExecuteBinary()
- self.net_args_thread = net_args.NetArgsThread(self.device_id,
- _XB1_NET_ARG_PORT,
- self._target_args)
# While binary is running, extract the net log and stream it to
# the output.
self.net_log_thread = net_log.NetLogThread(self.device_id,
@@ -485,7 +542,7 @@
appx_name = ToAppxFriendlyName(self.target_name)
- self._network_api.ExecuteBinary(_DEFAULT_PACKAGE_NAME, appx_name)
+ self.RunTest(appx_name)
while self._network_api.IsBinaryRunning(self.target_name):
self._Log(self.net_log_thread.GetLog())
@@ -511,8 +568,10 @@
self.Kill()
self._LogLn('Finished running...')
- crashed = self._DetectAndHandleAnyCrashes(self.target_name)
- return crashed
+ if self._DetectAndHandleAnyCrashes(self.target_name):
+ return TargetStatus.CRASH, 0
+ else:
+ return TargetStatus.NA, 0
def _DetectAndHandleAnyCrashes(self, target_name):
crashes_detected = False
diff --git a/starboard/xb1/tools/xb1_network_api.py b/starboard/xb1/tools/xb1_network_api.py
index 093218e..fac5872 100644
--- a/starboard/xb1/tools/xb1_network_api.py
+++ b/starboard/xb1/tools/xb1_network_api.py
@@ -48,6 +48,7 @@
_APPX_RELATIVE_PATH = 'appx'
_DEVELOPMENT_FILES = 'DevelopmentFiles'
+_LOOSE_APPS = 'LooseApps'
_LOCAL_APP_DATA = 'LocalAppData'
_LOCAL_CACHE_FOLDERNAME = r'\\LocalCache'
_TEMP_FILE_FOLDERNAME = r'\\WdpTempWebFolder'
@@ -421,7 +422,8 @@
return None
return None
- def ExecuteBinary(self, partial_package_name, app_alias_name):
+ def ExecuteBinary(self, partial_package_name: str,
+ app_alias_name: str) -> bool:
default_relative_name = self._GetDefaultRelativeId(partial_package_name)
if not default_relative_name or not '!' in default_relative_name:
raise IOError('Could not resolve package name "' + partial_package_name +
@@ -432,33 +434,25 @@
appid_64 = base64.b64encode(package_relative_id.encode('UTF-8'))
package_64 = base64.b64encode(default_relative_name.encode('UTF-8'))
- retry_count = 4
- # Time to wait between tries.
- retry_wait_s = 8
try:
- while retry_count > 0:
- self.LogLn('Executing: ' + package_relative_id)
- response = self._DoJsonRequest(
- 'POST',
- _TASKMANAGER_ENDPOINT,
- params={
- 'appid': appid_64,
- 'package': package_64
- },
- raise_on_failure=False)
- if not response or response == requests.codes.OK:
- self.LogLn('Execution successful')
- break
- self.LogLn('Execution not successful: ' + str(response))
- self.LogLn('Retrying with ' + str(retry_count) + ' attempts remaining.')
- time.sleep(retry_wait_s)
- retry_count -= 1
- # Double the wait time until the next attempt.
- retry_wait_s *= 2
+ self.LogLn('Executing: ' + package_relative_id)
+ response = self._DoJsonRequest(
+ 'POST',
+ _TASKMANAGER_ENDPOINT,
+ params={
+ 'appid': appid_64,
+ 'package': package_64
+ },
+ raise_on_failure=False)
+ if not response or response == requests.codes.OK:
+ self.LogLn('Execution successful')
+ return True
+ self.LogLn('Execution not successful: ' + str(response))
except Exception as err:
err_msg = '\n Failed to run:\n ' + package_relative_id + \
'\n because of:\n' + str(err)
raise IOError(err_msg) from err
+ return False
# Given a package name, return all files + directories.
# Throws IOError if the app is locked.
@@ -568,17 +562,23 @@
'path': path
})
- def ClearTempFiles(self):
+ def ClearDevFiles(self, path):
file_listing = self._DoJsonRequest(
'GET',
_GET_FILES_ENDPOINT,
params={
'knownfolderid': _DEVELOPMENT_FILES,
- 'path': _TEMP_FILE_FOLDERNAME
+ 'path': path
})
for file in file_listing['Items']:
self.DeleteFile(_DEVELOPMENT_FILES, _TEMP_FILE_FOLDERNAME, file['Name'])
+ def ClearTempFiles(self):
+ self.ClearDevFiles(_TEMP_FILE_FOLDERNAME)
+
+ def ClearLooseAppFiles(self):
+ self.ClearDevFiles(_LOOSE_APPS)
+
def FindPackage(self, package_name):
all_packages = self.GetInstalledPackages()
diff --git a/testing/android/AndroidManifest.xml b/testing/android/AndroidManifest.xml
index 283ebb9..6faeeb4 100644
--- a/testing/android/AndroidManifest.xml
+++ b/testing/android/AndroidManifest.xml
@@ -10,7 +10,7 @@
android:versionCode="1"
android:versionName="1.0">
- <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="33" />
+ <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="34" />
<application android:label="ChromeNativeTests">
<activity android:name=".ChromeNativeTestActivity"
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 2b729a4..e24189d 100644
--- a/third_party/chromium/media/filters/chunk_demuxer.cc
+++ b/third_party/chromium/media/filters/chunk_demuxer.cc
@@ -235,6 +235,19 @@
return write_head_;
}
+size_t ChunkDemuxerStream::GetStreamMemoryLimit() {
+ DCHECK(stream_);
+ base::AutoLock auto_lock(lock_);
+ return stream_->memory_limit();
+}
+
+void ChunkDemuxerStream::SetStreamMemoryLimitOverride(size_t memory_limit) {
+ DCHECK(stream_);
+ base::AutoLock auto_lock(lock_);
+ stream_->set_memory_limit_override(memory_limit);
+}
+
+
#endif // defined(STARBOARD)
void ChunkDemuxerStream::OnMemoryPressure(
@@ -1110,6 +1123,22 @@
return iter->second[0]->GetWriteHead();
}
+void ChunkDemuxer::SetSourceBufferStreamMemoryLimit(const std::string& id,
+ size_t limit) {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(source_state_map_.find(id) != source_state_map_.end());
+ source_state_map_[id]->SetSourceBufferStreamMemoryLimit(limit);
+}
+
+size_t ChunkDemuxer::GetSourceBufferStreamMemoryLimit(const std::string& id) {
+
+ base::AutoLock auto_lock(lock_);
+ if (source_state_map_.find(id) == source_state_map_.end()) {
+ return 0;
+ }
+ return source_state_map_[id]->GetSourceBufferStreamMemoryLimit();
+}
+
#endif // defined(STARBOARD)
bool ChunkDemuxer::AppendData(const std::string& id,
diff --git a/third_party/chromium/media/filters/chunk_demuxer.h b/third_party/chromium/media/filters/chunk_demuxer.h
index 2e71291..76a01f4 100644
--- a/third_party/chromium/media/filters/chunk_demuxer.h
+++ b/third_party/chromium/media/filters/chunk_demuxer.h
@@ -132,6 +132,8 @@
// DemuxerStream methods.
#if defined(STARBOARD)
std::string mime_type() const override { return mime_type_; }
+ size_t GetStreamMemoryLimit();
+ void SetStreamMemoryLimitOverride(size_t memory_limit);
#endif // defined (STARBOARD)
#if defined(STARBOARD)
@@ -392,6 +394,9 @@
#if defined(STARBOARD)
base::TimeDelta GetWriteHead(const std::string& id) const;
+
+ void SetSourceBufferStreamMemoryLimit(const std::string& guid, size_t limit);
+ size_t GetSourceBufferStreamMemoryLimit(const std::string& guid);
#endif // defined(STARBOARD)
void OnMemoryPressure(
diff --git a/third_party/chromium/media/filters/source_buffer_state.cc b/third_party/chromium/media/filters/source_buffer_state.cc
index 8951bc8..955dcf8 100644
--- a/third_party/chromium/media/filters/source_buffer_state.cc
+++ b/third_party/chromium/media/filters/source_buffer_state.cc
@@ -915,9 +915,41 @@
return success;
}
+#if defined(STARBOARD)
+void SourceBufferState::SetSourceBufferStreamMemoryLimit(size_t limit) {
+ LOG(INFO) << "Setting SourceBuffferStream MemoryLimit Override: " << limit;
+ stream_memory_limit_override_ = limit;
+ SetStreamMemoryLimits();
+}
+
+size_t SourceBufferState::GetSourceBufferStreamMemoryLimit() {
+ // a source buffer can be backed my multiple Demuxer streams, although at
+ // YouTube we usually only have a single one. If we've set an override then
+ // all values will be the same, but we shouldn't assume that, so instead we'll
+ // return the largest here if there are multiple.
+ size_t memory_limit = 0;
+ for (const auto& it : audio_streams_) {
+ memory_limit = std::max(memory_limit, it.second->GetStreamMemoryLimit());
+ }
+ for (const auto& it : video_streams_) {
+ memory_limit = std::max(memory_limit, it.second->GetStreamMemoryLimit());
+ }
+ return memory_limit;
+}
+#endif // defined(STARBOARD)
+
void SourceBufferState::SetStreamMemoryLimits() {
#if defined(STARBOARD)
- // Cobalt doesn't get stream memory limits from the command line.
+ LOG(INFO) << "Custom SourceBuffer memory limit="
+ << stream_memory_limit_override_;
+ if (stream_memory_limit_override_) {
+ for (const auto& it : audio_streams_) {
+ it.second->SetStreamMemoryLimitOverride(stream_memory_limit_override_);
+ }
+ for (const auto& it : video_streams_) {
+ it.second->SetStreamMemoryLimitOverride(stream_memory_limit_override_);
+ }
+ }
#else // defined(STARBOARD)
size_t audio_buf_size_limit =
GetMSEBufferSizeLimitIfExists(switches::kMSEAudioBufferSizeLimitMb);
diff --git a/third_party/chromium/media/filters/source_buffer_state.h b/third_party/chromium/media/filters/source_buffer_state.h
index 31f5bab..62ee4f7 100644
--- a/third_party/chromium/media/filters/source_buffer_state.h
+++ b/third_party/chromium/media/filters/source_buffer_state.h
@@ -159,6 +159,11 @@
void SetParseWarningCallback(SourceBufferParseWarningCB parse_warning_cb);
+#if defined(STARBOARD)
+ void SetSourceBufferStreamMemoryLimit(size_t limit);
+ size_t GetSourceBufferStreamMemoryLimit();
+ size_t stream_memory_limit_override_ = 0;
+#endif // defined(STARBOARD)
private:
// State advances through this list to PARSER_INITIALIZED.
// The intent is to ensure at least one config is received prior to parser
diff --git a/third_party/chromium/media/filters/source_buffer_stream.cc b/third_party/chromium/media/filters/source_buffer_stream.cc
index a1e9e2e..6d6789f 100644
--- a/third_party/chromium/media/filters/source_buffer_stream.cc
+++ b/third_party/chromium/media/filters/source_buffer_stream.cc
@@ -1900,11 +1900,14 @@
video_configs_[append_config_index_] = config;
#if defined(STARBOARD)
- // Dynamically increase |memory_limit_| when video resolution goes up.
- memory_limit_ = std::max(
- memory_limit_,
- GetDemuxerStreamVideoMemoryLimit(Demuxer::DemuxerTypes::kChunkDemuxer,
- &config, mime_type_));
+ // Dynamically increase |memory_limit_| when video resolution goes up as long
+ // as we haven't set a manual override.
+ if (!memory_override_) {
+ memory_limit_ = std::max(
+ memory_limit_,
+ GetDemuxerStreamVideoMemoryLimit(Demuxer::DemuxerTypes::kChunkDemuxer,
+ &config, mime_type_));
+ }
#endif // defined(STARBOARD)
return true;
}
diff --git a/third_party/chromium/media/filters/source_buffer_stream.h b/third_party/chromium/media/filters/source_buffer_stream.h
index 356db45..248b38e 100644
--- a/third_party/chromium/media/filters/source_buffer_stream.h
+++ b/third_party/chromium/media/filters/source_buffer_stream.h
@@ -188,6 +188,16 @@
memory_limit_ = memory_limit;
}
+#if defined(STARBOARD)
+ size_t memory_limit() const {
+ return memory_limit_;
+ }
+ void set_memory_limit_override(size_t memory_limit) {
+ memory_limit_ = memory_limit;
+ memory_override_ = true;
+ }
+#endif // defined (STARBOARD)
+
private:
friend class SourceBufferStreamTest;
@@ -405,6 +415,7 @@
base::TimeDelta GetBufferedDurationForGarbageCollection() const;
const std::string mime_type_;
+ bool memory_override_ = false;
#endif // defined (STARBOARD)
// Used to report log messages that can help the web developer figure out what
diff --git a/third_party/crashpad/build/BUILD.gn b/third_party/crashpad/build/BUILD.gn
index 6e6ccd6..e80f9b5 100644
--- a/third_party/crashpad/build/BUILD.gn
+++ b/third_party/crashpad/build/BUILD.gn
@@ -66,3 +66,12 @@
}
}
}
+
+if (crashpad_is_in_native_target_build) {
+ config("native_target_executable_config") {
+ # This is to undo the "main=StarboardMain" define added for all targets
+ # when final_executable_type == "shared_library", in
+ # starboard/build/config/BUILD.gn, which itself is admittedly a hack.
+ defines = [ "StarboardMain=main" ]
+ }
+}
diff --git a/third_party/crashpad/handler/BUILD.gn b/third_party/crashpad/handler/BUILD.gn
index b8ce718..2b9d2e4 100644
--- a/third_party/crashpad/handler/BUILD.gn
+++ b/third_party/crashpad/handler/BUILD.gn
@@ -169,15 +169,6 @@
}
}
- if (crashpad_is_in_native_target_build) {
- config("crashpad_handler_native_target_config") {
- # This is to undo the "main=StarboardMain" define added for all targets
- # when final_executable_type == "shared_library", in
- # starboard/build/config/BUILD.gn, which itself is admittedly a hack.
- defines = [ "StarboardMain=main" ]
- }
- }
-
crashpad_executable("crashpad_handler") {
if (crashpad_is_in_starboard) {
install_target = !crashpad_is_android
@@ -212,7 +203,7 @@
}
if (crashpad_is_in_native_target_build) {
- configs += [ ":crashpad_handler_native_target_config" ]
+ configs += [ "../build:native_target_executable_config" ]
}
}
}
diff --git a/third_party/crashpad/tools/BUILD.gn b/third_party/crashpad/tools/BUILD.gn
index 402f04a..76d6d28 100644
--- a/third_party/crashpad/tools/BUILD.gn
+++ b/third_party/crashpad/tools/BUILD.gn
@@ -31,9 +31,7 @@
}
}
-# TODO(b/251521595): resolve GN error and enable this target to be built for
-# android.
-if (!crashpad_is_ios && !crashpad_is_android) {
+if (!crashpad_is_ios) {
crashpad_executable("crashpad_database_util") {
check_includes = !crashpad_is_in_starboard && !crashpad_is_in_native_target_build
@@ -53,6 +51,10 @@
"//starboard",
]
}
+
+ if (crashpad_is_in_native_target_build) {
+ configs = [ "../build:native_target_executable_config" ]
+ }
}
if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build) {
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 c623f69..444d9fe 100644
--- a/third_party/devtools/front_end/cobalt/cobalt.js
+++ b/third_party/devtools/front_end/cobalt/cobalt.js
@@ -9,6 +9,7 @@
['Trace', 'console_trace.json'],
['Timed Trace', 'timed_trace.json']
];
+ const timed_trace_durations = ['5', '10', '20', '60'];
super(true, false);
SDK.targetManager.observeTargets(this);
@@ -33,6 +34,16 @@
this.run(`(function() { window.h5vcc.traceEvent.stop();})()`);
console.log("Stopped Trace");
}));
+ traceContainer.appendChild(UI.createLabel('Navigate Timed Trace:'));
+ timed_trace_durations.forEach((duration) => {
+ traceContainer.appendChild(UI.createTextButton(Common.UIString(duration + 's'), event => {
+ console.log("Request Navigate Timed Trace. " + duration);
+ this._cobaltAgent.invoke_sendConsoleCommand({
+ command: 'navigate_timed_trace', message: duration
+ });
+ console.log("Requested Navigate Timed Trace.");
+ }));
+ });
trace_files.forEach((file) => {
traceContainer.appendChild(UI.createTextButton(Common.UIString('Download ' + file[0]), event => {
console.log("Download Trace");
@@ -47,6 +58,32 @@
});
}));
});
+
+ const netLogContainer = this.element.createChild('div', 'netlog-container');
+ netLogContainer.appendChild(UI.createTextButton(Common.UIString('Start NetLog'), event => {
+ console.log("Start NetLog");
+ this.run(`(function() { window.h5vcc.netLog.start();})()`);
+ console.log("Started NetLog");
+ }));
+ netLogContainer.appendChild(UI.createTextButton(Common.UIString('Stop NetLog'), event => {
+ console.log("Stop NetLog");
+ this.run(`(function() { window.h5vcc.netLog.stop();})()`);
+ console.log("Stopped NetLog");
+ }));
+ netLogContainer.appendChild(UI.createTextButton(Common.UIString('Download NetLog'), event => {
+ console.log("Download Trace");
+ this.run(`(function() { return window.h5vcc.netLog.stopAndRead();})()`).then(function (result) {
+ const netlog_file = 'net_log.json';
+ download_element.setAttribute('href', 'data:text/plain;charset=utf-8,' +
+ encodeURIComponent(result.result.value));
+ download_element.setAttribute('download', netlog_file);
+ console.log("Downloaded NetLog");
+ download_element.click();
+ download_element.setAttribute('href', undefined);
+ });
+ }));
+
+
const debugLogContainer = this.element.createChild('div', 'debug-log-container');
debugLogContainer.appendChild(UI.createTextButton(Common.UIString('DebugLog On'), event => {
this._cobaltAgent.invoke_sendConsoleCommand({
@@ -58,6 +95,7 @@
command: 'debug_log', message: 'off'
});
}));
+
const lifecycleContainer = this.element.createChild('div', 'lifecycle-container');
lifecycleContainer.appendChild(UI.createTextButton(Common.UIString('Blur'), event => {
this._cobaltAgent.invoke_sendConsoleCommand({ command: 'blur' });
@@ -77,6 +115,7 @@
lifecycleContainer.appendChild(UI.createTextButton(Common.UIString('Quit'), event => {
this._cobaltAgent.invoke_sendConsoleCommand({ command: 'quit' });
}));
+
const consoleContainer = this.element.createChild('div', 'console-container');
consoleContainer.appendChild(UI.createTextButton(Common.UIString('DebugCommand'), event => {
const outputElement = document.getElementsByClassName('console-output')[0];
diff --git a/third_party/googletest/src/googletest/src/gtest.cc b/third_party/googletest/src/googletest/src/gtest.cc
index 9b84415..2465a48 100644
--- a/third_party/googletest/src/googletest/src/gtest.cc
+++ b/third_party/googletest/src/googletest/src/gtest.cc
@@ -1132,6 +1132,21 @@
}
// A helper class for measuring elapsed times.
+#if GTEST_OS_STARBOARD
+class Timer {
+ public:
+ Timer() : start_(GetTimeInMillis()) {
+ }
+
+ // Return time elapsed in milliseconds since the timer was created.
+ TimeInMillis Elapsed() {
+ return (GetTimeInMillis() - start_);
+ }
+
+ private:
+ TimeInMillis start_;
+};
+#else // GTEST_OS_STARBOARD
class Timer {
public:
Timer() : start_(std::chrono::steady_clock::now()) {}
@@ -1146,6 +1161,7 @@
private:
std::chrono::steady_clock::time_point start_;
};
+#endif // GTEST_OS_STARBOARD
// Returns a timestamp as milliseconds since the epoch. Note this time may jump
// around subject to adjustments by the system, to measure elapsed time use
diff --git a/third_party/musl/arch/aarch64/bits/alltypes.h b/third_party/musl/arch/aarch64/bits/alltypes.h
index 04d8c0b..77ff768 100644
--- a/third_party/musl/arch/aarch64/bits/alltypes.h
+++ b/third_party/musl/arch/aarch64/bits/alltypes.h
@@ -11,10 +11,14 @@
#define __LONG_MAX 0x7fffffffffffffffL
#ifndef __cplusplus
+#if defined(USE_COBALT_CUSTOMIZATIONS)
+typedef __WCHAR_TYPE__ wchar_t;
+#else
#if defined(__NEED_wchar_t) && !defined(__DEFINED_wchar_t)
typedef unsigned wchar_t;
#define __DEFINED_wchar_t
#endif
+#endif // defined(USE_COBALT_CUSTOMIZATIONS)
#endif
#if defined(__NEED_wint_t) && !defined(__DEFINED_wint_t)
diff --git a/third_party/musl/arch/arm/bits/alltypes.h b/third_party/musl/arch/arm/bits/alltypes.h
index 2bc88cd..3993b1e 100644
--- a/third_party/musl/arch/arm/bits/alltypes.h
+++ b/third_party/musl/arch/arm/bits/alltypes.h
@@ -12,10 +12,14 @@
#define __LONG_MAX 0x7fffffffL
#ifndef __cplusplus
+#if defined(USE_COBALT_CUSTOMIZATIONS)
+typedef __WCHAR_TYPE__ wchar_t;
+#else
#if defined(__NEED_wchar_t) && !defined(__DEFINED_wchar_t)
typedef unsigned wchar_t;
#define __DEFINED_wchar_t
#endif
+#endif // defined(USE_COBALT_CUSTOMIZATIONS)
#endif
diff --git a/third_party/musl/arch/x86_64/bits/alltypes.h b/third_party/musl/arch/x86_64/bits/alltypes.h
index b5d6f52..2e3401f 100644
--- a/third_party/musl/arch/x86_64/bits/alltypes.h
+++ b/third_party/musl/arch/x86_64/bits/alltypes.h
@@ -1,16 +1,18 @@
#define _Addr long
#define _Int64 long
#define _Reg long
-
#define __BYTE_ORDER 1234
#define __LONG_MAX 0x7fffffffffffffffL
#ifndef __cplusplus
+#if defined(USE_COBALT_CUSTOMIZATIONS)
+typedef __WCHAR_TYPE__ wchar_t;
+#else
#if defined(__NEED_wchar_t) && !defined(__DEFINED_wchar_t)
typedef int wchar_t;
#define __DEFINED_wchar_t
#endif
-
+#endif // defined(USE_COBALT_CUSTOMIZATIONS)
#endif
#if defined(__FLT_EVAL_METHOD__) && __FLT_EVAL_METHOD__ == 2
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/third_party/v8/src/wasm/wasm-code-manager.cc b/third_party/v8/src/wasm/wasm-code-manager.cc
index 579c094..fd83a60 100644
--- a/third_party/v8/src/wasm/wasm-code-manager.cc
+++ b/third_party/v8/src/wasm/wasm-code-manager.cc
@@ -1123,6 +1123,10 @@
// The caller must hold the {allocation_mutex_}, thus we fail to lock it here.
DCHECK(!allocation_mutex_.TryLock());
+ // Add the code to the surrounding code ref scope, so the returned pointer is
+ // guaranteed to be valid.
+ WasmCodeRefScope::AddRef(code.get());
+
if (!code->IsAnonymous() &&
code->index() >= module_->num_imported_functions) {
DCHECK_LT(code->index(), num_functions());
@@ -1159,17 +1163,21 @@
WasmCodeRefScope::AddRef(prior_code);
// The code is added to the current {WasmCodeRefScope}, hence the ref
// count cannot drop to zero here.
- CHECK(!prior_code->DecRef());
+ prior_code->DecRefOnLiveCode();
}
PatchJumpTablesLocked(slot_idx, code->instruction_start());
+ } else {
+ // The code tables does not hold a reference to the code, hence decrement
+ // the initial ref count of 1. The code was added to the
+ // {WasmCodeRefScope} though, so it cannot die here.
+ code->DecRefOnLiveCode();
}
if (!code->for_debugging() && tiering_state_ == kTieredDown &&
code->tier() == ExecutionTier::kTurbofan) {
liftoff_bailout_count_.fetch_add(1);
}
}
- WasmCodeRefScope::AddRef(code.get());
WasmCode* result = code.get();
owned_code_.emplace(result->instruction_start(), std::move(code));
return result;
diff --git a/third_party/v8/src/wasm/wasm-code-manager.h b/third_party/v8/src/wasm/wasm-code-manager.h
index 26a9030..da8e2cc 100644
--- a/third_party/v8/src/wasm/wasm-code-manager.h
+++ b/third_party/v8/src/wasm/wasm-code-manager.h
@@ -226,6 +226,14 @@
}
}
+ // Decrement the ref count on code that is known to be in use (i.e. the ref
+ // count cannot drop to zero here).
+ void DecRefOnLiveCode() {
+ int old_count = ref_count_.fetch_sub(1, std::memory_order_acq_rel);
+ DCHECK_LE(2, old_count);
+ USE(old_count);
+ }
+
// Decrement the ref count on code that is known to be dead, even though there
// might still be C++ references. Returns whether this drops the last
// reference and the code needs to be freed.
diff --git a/tools/copy_and_filter_out_dir.py b/tools/copy_and_filter_out_dir.py
index b9f08ec..9c398cd 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>
diff --git a/tools/metrics/actions/cobalt/actions.xml b/tools/metrics/actions/cobalt/actions.xml
new file mode 100644
index 0000000..16e1580
--- /dev/null
+++ b/tools/metrics/actions/cobalt/actions.xml
@@ -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.
+-->
+
+<!--
+This file is a comprehensive list of Cobalt user actions along with the owner
+and description for each user action.
+
+For best practices on writing user action descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/actions/README.md
+For details on how to modify this file to add your description, keep reading.
+
+If an action is not user triggered, specify it using not_user_triggered="true"
+as an attribute to the <action> tag.
+
+An example entry looks like:
+
+ <action name="ClickedFooButton">
+ <owner>buttonengineer@google.com</owner>
+ <description>User clicked the Foo button.</description>
+ </action>
+
+Please run ../extract_actions.py to add new actions and pretty-print this file.
+
+If a user action is not being used any more, put an <obsolete> tag under
+the <action> tag with necessary explanation. Don't delete things in this file.
+-->
+
+<actions>
+
+</actions>
diff --git a/tools/metrics/histograms/metadata/cobalt/enums.xml b/tools/metrics/histograms/metadata/cobalt/enums.xml
new file mode 100644
index 0000000..feb372a
--- /dev/null
+++ b/tools/metrics/histograms/metadata/cobalt/enums.xml
@@ -0,0 +1,74 @@
+<!--
+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.
+-->
+
+<!--
+
+This file describes the enumerations referenced by entries in histograms.xml for
+this directory.
+
+For best practices on writing enumerations descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#Enum-Histograms
+-->
+
+<histogram-configuration>
+
+<!-- Enum types -->
+
+<enums>
+
+<enum name="Boolean">
+ <summary>
+ A "True/False" enum for boolean histograms.
+ </summary>
+ <int value="0" label="False"/>
+ <int value="1" label="True"/>
+</enum>
+
+
+<enum name="PipelineStatus">
+ <summary>
+ Possible status values reported by the Media Pipeline
+ </summary>
+ <int value="0" label="PIPELINE_OK"/>
+ <int value="1" label="PIPELINE_ERROR_URL_NOT_FOUND"/>
+ <int value="2" label="PIPELINE_ERROR_NETWORK"/>
+ <int value="3" label="PIPELINE_ERROR_DECODE"/>
+ <int value="5" label="PIPELINE_ERROR_ABORT"/>
+ <int value="6" label="PIPELINE_ERROR_INITIALIZATION_FAILED"/>
+ <int value="7" label="PIPELINE_ERROR_REQUIRED_FILTER_MISSING"/>
+ <int value="8" label="PIPELINE_ERROR_COULD_NOT_RENDER"/>
+ <int value="9" label="PIPELINE_ERROR_READ"/>
+ <int value="10" label="PIPELINE_ERROR_OPERATION_PENDING"/>
+ <int value="11" label="PIPELINE_ERROR_INVALID_STATE"/>
+ <int value="12" label="DEMUXER_ERROR_COULD_NOT_OPEN"/>
+ <int value="13" label="DEMUXER_ERROR_COULD_NOT_PARSE"/>
+ <int value="14" label="DEMUXER_ERROR_NO_SUPPORTED_STREAMS"/>
+ <int value="15" label="DECODER_ERROR_NOT_SUPPORTED"/>
+ <int value="16" label="CHUNK_DEMUXER_ERROR_APPEND_FAILED"/>
+ <int value="17" label="CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR"/>
+ <int value="18" label="CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR"/>
+ <int value="19" label="AUDIO_RENDERER_ERROR"/>
+ <int value="20" label="AUDIO_RENDERER_ERROR_SPLICE_FAILED"/>
+ <int value="21" label="PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED"/>
+ <int value="22" label="DEMUXER_ERROR_DETECTED_HLS"/>
+ <int value="23" label="PIPELINE_ERROR_HARDWARE_CONTEXT_RESET"/>
+ <int value="24" label="PLAYBACK_CAPABILITY_CHANGED"/>
+</enum>
+
+
+</enums>
+
+</histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/cobalt/histograms.xml b/tools/metrics/histograms/metadata/cobalt/histograms.xml
new file mode 100644
index 0000000..7a5547a
--- /dev/null
+++ b/tools/metrics/histograms/metadata/cobalt/histograms.xml
@@ -0,0 +1,169 @@
+<!--
+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.
+-->
+
+<!--
+This file is used to generate a comprehensive list of Cobalt histograms
+along with a detailed description for each histogram. See go/cobalt-telemetry
+for details on how to modify this file.
+
+For best practices on writing histogram descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md
+
+Always run the pretty print utility on this file after editing:
+
+ ./tools/metrics/histograms/pretty_print.py tools/metrics/histograms/metadata/cobalt/histograms.xml
+-->
+
+<histogram-configuration>
+
+<histograms>
+
+<histogram name="Cobalt.DOM.CSS.Link.ParseTimeMicrosPerKB" units="microseconds"
+ expires_after="never">
+<!-- expires-never: Needed for long-term tracking of CSS parse performance. -->
+
+ <owner>joeltine@google.com</owner>
+ <summary>
+ A normalized ratio of time to KB of CSS parsing for link tags. Only logs a
+ sample for links loaded through HTTP/HTTPS and greater than 1KB. See
+ go/cobalt-js-css-parsing-metrics for the full design.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.DOM.CSS.Style.ParseTimeMicrosPerKB"
+ units="microseconds" expires_after="never">
+<!-- expires-never: Needed for long-term tracking of CSS parse performance. -->
+
+ <owner>joeltine@google.com</owner>
+ <summary>
+ A normalized ratio of time to KB of CSS parsing for style tags. Only logs a
+ sample for HTMLStyleElements with CSS greater than 1KB. See
+ go/cobalt-js-css-parsing-metrics for the full design.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.Media.HasEverPlayed" enum="PipelineStatus"
+ expires_after="never">
+<!-- expires-never: Needed for baseline Media pipeline health metric. -->
+
+ <owner>sideboard@google.com</owner>
+ <owner>cobalt-team@google.com</owner>
+ <summary>
+ Status of the media pipeline at the end of its lifecycle for audio-video
+ streams with an unknown video codec.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.Media.PipelineStatus.AudioOnly" enum="PipelineStatus"
+ expires_after="never">
+<!-- expires-never: Needed for baseline Media pipeline health metric. -->
+
+ <owner>sideboard@google.com</owner>
+ <owner>cobalt-team@google.com</owner>
+ <summary>
+ Status of the media pipeline at the end of its lifecycle for audio only
+ streams.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.Media.PipelineStatus.AudioVideo.AV1"
+ enum="PipelineStatus" expires_after="never">
+<!-- expires-never: Needed for baseline Media pipeline health metric. -->
+
+ <owner>sideboard@google.com</owner>
+ <owner>cobalt-team@google.com</owner>
+ <summary>
+ Status of the media pipeline at the end of its lifecycle for audio-video
+ streams with AV1 video codec.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.Media.PipelineStatus.AudioVideo.H264"
+ enum="PipelineStatus" expires_after="never">
+<!-- expires-never: Needed for baseline Media pipeline health metric. -->
+
+ <owner>sideboard@google.com</owner>
+ <owner>cobalt-team@google.com</owner>
+ <summary>
+ Status of the media pipeline at the end of its lifecycle for audio-video
+ streams with H264 video codec.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.Media.PipelineStatus.AudioVideo.Other"
+ enum="PipelineStatus" expires_after="never">
+<!-- expires-never: Needed for baseline Media pipeline health metric. -->
+
+ <owner>sideboard@google.com</owner>
+ <owner>cobalt-team@google.com</owner>
+ <summary>
+ Status of the media pipeline at the end of its lifecycle for audio-video
+ streams with an unknown video codec.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.Media.PipelineStatus.AudioVideo.VP9"
+ enum="PipelineStatus" expires_after="never">
+<!-- expires-never: Needed for baseline Media pipeline health metric. -->
+
+ <owner>sideboard@google.com</owner>
+ <owner>cobalt-team@google.com</owner>
+ <summary>
+ Status of the media pipeline at the end of its lifecycle for audio-video
+ streams with VP9 video codec.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.Media.PipelineStatus.Unsupported" enum="PipelineStatus"
+ expires_after="never">
+<!-- expires-never: Needed for baseline Media pipeline health metric. -->
+
+ <owner>sideboard@google.com</owner>
+ <owner>cobalt-team@google.com</owner>
+ <summary>
+ Status of the media pipeline at the end of its lifecycle for audio-video
+ streams with an unknown video codec.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.Media.PipelineStatus.VideoOnly" enum="PipelineStatus"
+ expires_after="never">
+<!-- expires-never: Needed for baseline Media pipeline health metric. -->
+
+ <owner>sideboard@google.com</owner>
+ <owner>cobalt-team@google.com</owner>
+ <summary>
+ Status of the media pipeline at the end of its lifecycle for video only
+ streams.
+ </summary>
+</histogram>
+
+<histogram name="Cobalt.MediaDevices.MicCreationSucceeded" enum="Boolean"
+ expires_after="never">
+<!-- expires-never: Needed for long-term tracking of device mic support. -->
+
+ <owner>loganmann@google.com</owner>
+ <owner>yt-magma-eng@google.com</owner>
+ <owner>cobalt-team@google.com</owner>
+ <summary>
+ A boolean representing the success or failure of an attempted mic creation
+ event via mediaDevices.getUserMedia().
+ </summary>
+</histogram>
+
+</histograms>
+
+</histogram-configuration>
\ No newline at end of file