Import Cobalt 23.master.0.306649
diff --git a/.codespellignorelines b/.codespellignorelines
index 1a26ffa..e2b2905 100644
--- a/.codespellignorelines
+++ b/.codespellignorelines
@@ -1,2 +1,3 @@
     vp9AllowList.get("Technicolor").add("STING");
   return SkSurface::MakeRenderTarget(gr_context_.get(), SkBudgeted::kNo,
+            texture_infos.push_back(TextureInfo("uv", "ba"));
diff --git a/.gn b/.gn
index 4d67c09..5fb6ec2 100644
--- a/.gn
+++ b/.gn
@@ -14,3 +14,8 @@
 
 # The location of the build configuration file.
 buildconfig = "//starboard/build/config/BUILDCONFIG.gn"
+
+# We have `python3` if we're in a container or on buildbot, so use that.
+if (getenv("IS_DOCKER") == "1" || getenv("BUILDBOT_PROJECT") != "") {
+  script_executable = "python3"
+}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 033e9b1..e80ac24 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -10,6 +10,8 @@
         (
             base|
             build|
+            buildtools|
+            crypto|
             net|
             testing|
             third_party|
@@ -68,17 +70,8 @@
         exclude: |
             (?x)(
                 ^cobalt/bindings/(templates|generated)/|
-                ^starboard/shared/uikit/.*\.h$|
-                tests?\.(cc|h)$
+                ^starboard/shared/uikit/.*\.h$
             )
-    -   id: cpplint_test
-        name: cpplint_test
-        entry: cpplint
-        language: system
-        types: [c++]
-        args: [--verbose=5, --quiet]
-        files: '.*tests?.(cc|h)$'
-        exclude: '^starboard/shared/uikit/.*\.h$'
     -   id: yapf
         name: yapf
         description: Run yapf (the python formatter) in-place on changed files.
@@ -114,10 +107,12 @@
         stages: [push]
         exclude: |
             (?x)^(
+                cobalt/media/|
                 cobalt/layout_tests/testdata/|
                 starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java$
             )
         exclude_types: [markdown]
+        verbose: true
     -   id: check-if-starboard-interface-changed
         name: check if starboard interface changed
         entry: python ./precommit_hooks/warn_that_starboard_interface_changed_wrapper.py
@@ -174,7 +169,7 @@
         types: [text]
         # TODO: Remove docker-compose-windows.yml after internal check evaluates
         # properly on it.
-        exclude: '(^docker-compose-windows.yml|EXCLUDE\.FILES(\.RECURSIVE)?)$'
+        exclude: '(^docker-compose-windows.yml|EXCLUDE\.FILES(\.RECURSIVE)?|codereview\.settings)$'
     -   id: gn-format
         name: GN format
         entry: gn format
diff --git a/BUILD.gn b/BUILD.gn
index fa1f55e..d2d4ccd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -27,7 +27,7 @@
   if (is_qa || is_gold) {
     deps = [
       "//cobalt:default",
-      "//starboard",
+      "//starboard:default",
     ]
   } else {
     deps = [ ":gn_all" ]
diff --git a/base/BUILD.gn b/base/BUILD.gn
index d4c5d7c..c0af895 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -94,10 +94,10 @@
 }
 
 config("base_public_defines") {
-  defines = [
-    "BASE_DONT_ENFORCE_THREAD_NAME_LENGTH",
-    "ENABLE_TEST_DATA",
-  ]
+  defines = [ "BASE_DONT_ENFORCE_THREAD_NAME_LENGTH" ]
+  if (!is_gold) {
+    defines += [ "ENABLE_TEST_DATA" ]
+  }
 }
 
 if (is_win) {
@@ -1107,6 +1107,10 @@
       "debug/proc_maps_linux.h",
       "debug/stack_trace_android.cc",
       "debug/stack_trace_win.cc",
+
+      # No usage found for Cobalt yet and they introduce API leaks.
+      "debug/thread_heap_usage_tracker.cc",
+      "debug/thread_heap_usage_tracker.h",
       "file_descriptor_store.cc",
       "file_descriptor_store.h",
       "file_version_info_mac.h",
@@ -1583,7 +1587,12 @@
   # more robust check for this.
   if (!use_sysroot && (is_android || (is_linux && !is_chromecast)) &&
       host_toolchain != "//build/toolchain/cros:host") {
-    libs += [ "atomic" ]
+    # TODO(b/206642994): see if we can remove this condition. It's added for now
+    # because linking atomic leads to linker errors for evergreen on arm-hardfp,
+    # and it's apparently not really needed (i.e., we can build without it).
+    if (!is_starboard || !sb_is_evergreen) {
+      libs += [ "atomic" ]
+    }
   }
 
   if (!is_starboard && use_allocator_shim) {
@@ -2557,7 +2566,7 @@
   }
 }
 
-if (is_win || is_mac) {
+if (!is_starboard && (is_win || is_mac)) {
   if (current_cpu == "x64") {
     # Must be a shared library so that it can be unloaded during testing.
     shared_library("base_profiler_test_support_library") {
@@ -2568,7 +2577,7 @@
   }
 }
 
-bundle_data("base_unittests_bundle_data") {
+copy("base_unittests_bundle_data") {
   testonly = true
   sources = [
     "//tools/metrics/histograms/enums.xml",
@@ -2594,12 +2603,14 @@
     "test/data/serializer_test.json",
     "test/data/serializer_test_nowhitespace.json",
   ]
-  outputs = [
-    "{{bundle_resources_dir}}/" +
-        "{{source_root_relative_dir}}/{{source_file_part}}",
-  ]
   if (is_starboard) {
     sources -= [ "//tools/metrics/histograms/enums.xml" ]
+    outputs = [ "$sb_static_contents_output_data_dir/test/base/{{source_target_relative}}" ]
+  } else {
+    outputs = [
+      "{{bundle_resources_dir}}/" +
+          "{{source_root_relative_dir}}/{{source_file_part}}",
+    ]
   }
 }
 
@@ -2668,6 +2679,8 @@
     "bit_cast_unittest.cc",
     "bits_unittest.cc",
     "build_time_unittest.cc",
+    # TODO(b/216774170): Fix this test cases, then move to right order.
+    "metrics/persistent_histogram_storage_unittest.cc",
     "callback_helpers_unittest.cc",
     "callback_list_unittest.cc",
     "callback_unittest.cc",
@@ -2790,7 +2803,6 @@
     "metrics/histogram_unittest.cc",
     "metrics/metrics_hashes_unittest.cc",
     "metrics/persistent_histogram_allocator_unittest.cc",
-    "metrics/persistent_histogram_storage_unittest.cc",
     "metrics/persistent_memory_allocator_unittest.cc",
     "metrics/persistent_sample_map_unittest.cc",
     "metrics/sample_map_unittest.cc",
diff --git a/base/DEPS b/base/DEPS
new file mode 100644
index 0000000..208d51a
--- /dev/null
+++ b/base/DEPS
@@ -0,0 +1,17 @@
+include_rules = [
+  "+jni",
+  "+third_party/apple_apsl",
+  "+third_party/ashmem",
+  "+third_party/ced",
+  "+third_party/libxml",
+  "+third_party/lss",
+  "+third_party/modp_b64",
+  "+third_party/tcmalloc",
+
+  # These are implicitly brought in from the root, and we don't want them.
+  "-ipc",
+  "-url",
+
+  # ICU dependendencies must be separate from the rest of base.
+  "-i18n",
+]
diff --git a/base/OWNERS b/base/OWNERS
new file mode 100644
index 0000000..5b6a6b5
--- /dev/null
+++ b/base/OWNERS
@@ -0,0 +1,55 @@
+# About src/base:
+#
+# Chromium is a very mature project, most things that are generally useful are
+# already here, and that things not here aren't generally useful.
+#
+# Base is pulled into many projects. For example, various ChromeOS daemons. So
+# the bar for adding stuff is that it must have demonstrated wide
+# applicability. Prefer to add things closer to where they're used (i.e. "not
+# base"), and pull into base only when needed.  In a project our size,
+# sometimes even duplication is OK and inevitable.
+#
+# Adding a new logging macro DPVELOG_NE is not more clear than just
+# writing the stuff you want to log in a regular logging statement, even
+# if it makes your calling code longer. Just add it to your own code.
+#
+# If the code in question does not need to be used inside base, but will have
+# multiple consumers across the codebase, consider placing it in a new directory
+# under components/ instead.
+
+ajwong@chromium.org
+danakj@chromium.org
+dcheng@chromium.org
+fdoray@chromium.org
+gab@chromium.org
+kylechar@chromium.org
+mark@chromium.org
+thakis@chromium.org
+thestig@chromium.org
+
+# For Bind/Callback:
+per-file bind*=tzik@chromium.org
+per-file callback*=tzik@chromium.org
+
+# For Android-specific changes:
+per-file *android*=file://base/android/OWNERS
+per-file BUILD.gn=file://base/android/OWNERS
+
+# For Fuchsia-specific changes:
+per-file *_fuchsia*=file://build/fuchsia/OWNERS
+
+# For FeatureList API:
+per-file feature_list*=asvitkine@chromium.org
+per-file feature_list*=isherman@chromium.org
+
+# Restricted since rand_util.h also backs the cryptographically secure RNG.
+per-file rand_util*=set noparent
+per-file rand_util*=file://ipc/SECURITY_OWNERS
+
+# For TCMalloc tests:
+per-file security_unittest.cc=jln@chromium.org
+
+# For Value:
+per-file values*=jdoerrie@chromium.org
+
+# COMPONENT: Internals>Core
diff --git a/base/PRESUBMIT.py b/base/PRESUBMIT.py
new file mode 100644
index 0000000..7fc8107
--- /dev/null
+++ b/base/PRESUBMIT.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Chromium presubmit script for src/base.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details on the presubmit API built into depot_tools.
+"""
+
+def _CheckNoInterfacesInBase(input_api, output_api):
+  """Checks to make sure no files in libbase.a have |@interface|."""
+  pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE)
+  files = []
+  for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
+    if (f.LocalPath().startswith('base/') and
+        not "/ios/" in f.LocalPath() and
+        not "/test/" in f.LocalPath() and
+        not f.LocalPath().endswith('_unittest.mm') and
+        not f.LocalPath().endswith('mac/sdk_forward_declarations.h')):
+      contents = input_api.ReadFile(f)
+      if pattern.search(contents):
+        files.append(f)
+
+  if len(files):
+    return [ output_api.PresubmitError(
+        'Objective-C interfaces or categories are forbidden in libbase. ' +
+        'See http://groups.google.com/a/chromium.org/group/chromium-dev/' +
+        'browse_thread/thread/efb28c10435987fd',
+        files) ]
+  return []
+
+
+def _CommonChecks(input_api, output_api):
+  """Checks common to both upload and commit."""
+  results = []
+  results.extend(_CheckNoInterfacesInBase(input_api, output_api))
+  return results
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  return results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  return results
diff --git a/base/allocator/OWNERS b/base/allocator/OWNERS
new file mode 100644
index 0000000..de658d0
--- /dev/null
+++ b/base/allocator/OWNERS
@@ -0,0 +1,4 @@
+primiano@chromium.org
+wfh@chromium.org
+
+# COMPONENT: Internals
diff --git a/base/allocator/partition_allocator/OWNERS b/base/allocator/partition_allocator/OWNERS
new file mode 100644
index 0000000..b0a2a85
--- /dev/null
+++ b/base/allocator/partition_allocator/OWNERS
@@ -0,0 +1,8 @@
+ajwong@chromium.org
+haraken@chromium.org
+palmer@chromium.org
+tsepez@chromium.org
+
+# TEAM: platform-architecture-dev@chromium.org
+#       Also: security-dev@chromium.org
+# COMPONENT: Blink>MemoryAllocator>Partition
diff --git a/base/android/OWNERS b/base/android/OWNERS
new file mode 100644
index 0000000..5c40958
--- /dev/null
+++ b/base/android/OWNERS
@@ -0,0 +1,8 @@
+agrieve@chromium.org
+nyquist@chromium.org
+rmcilroy@chromium.org
+torne@chromium.org
+yfriedman@chromium.org
+
+per-file *.aidl=set noparent
+per-file *.aidl=file://ipc/SECURITY_OWNERS
diff --git a/base/android/java/src/org/chromium/base/process_launcher/OWNERS b/base/android/java/src/org/chromium/base/process_launcher/OWNERS
new file mode 100644
index 0000000..c2edc66
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/process_launcher/OWNERS
@@ -0,0 +1,5 @@
+boliu@chromium.org
+jcivelli@chromium.org
+
+per-file *.aidl=set noparent
+per-file *.aidl=file://ipc/SECURITY_OWNERS
diff --git a/base/android/java/src/org/chromium/base/task/OWNERS b/base/android/java/src/org/chromium/base/task/OWNERS
new file mode 100644
index 0000000..1169d15
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/task/OWNERS
@@ -0,0 +1,2 @@
+alexclarke@chromium.org
+smaier@chromium.org
diff --git a/base/android/jni_generator/PRESUBMIT.py b/base/android/jni_generator/PRESUBMIT.py
new file mode 100644
index 0000000..bc76d5b
--- /dev/null
+++ b/base/android/jni_generator/PRESUBMIT.py
@@ -0,0 +1,37 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Presubmit script for android buildbot.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
+details on the presubmit API built into depot_tools.
+"""
+
+
+def CommonChecks(input_api, output_api):
+  base_android_jni_generator_dir = input_api.PresubmitLocalPath()
+
+  env = dict(input_api.environ)
+  env.update({
+    'PYTHONPATH': base_android_jni_generator_dir,
+    'PYTHONDONTWRITEBYTECODE': '1',
+  })
+
+  return input_api.canned_checks.RunUnitTests(
+      input_api,
+      output_api,
+      unit_tests=[
+        input_api.os_path.join(
+            base_android_jni_generator_dir, 'jni_generator_tests.py')
+      ],
+      env=env,
+  )
+
+
+def CheckChangeOnUpload(input_api, output_api):
+    return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+    return CommonChecks(input_api, output_api)
diff --git a/base/android/linker/DEPS b/base/android/linker/DEPS
new file mode 100644
index 0000000..15c3afb
--- /dev/null
+++ b/base/android/linker/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  # This code cannot depend on anything from base/
+  "-base",
+]
diff --git a/base/android/orderfile/OWNERS b/base/android/orderfile/OWNERS
new file mode 100644
index 0000000..d45b803
--- /dev/null
+++ b/base/android/orderfile/OWNERS
@@ -0,0 +1,3 @@
+lizeb@chromium.org
+mattcary@chromium.org
+pasko@chromium.org
diff --git a/base/containers/OWNERS b/base/containers/OWNERS
new file mode 100644
index 0000000..cc39b28
--- /dev/null
+++ b/base/containers/OWNERS
@@ -0,0 +1,3 @@
+danakj@chromium.org
+dcheng@chromium.org
+vmpstr@chromium.org
diff --git a/base/debug/OWNERS b/base/debug/OWNERS
new file mode 100644
index 0000000..6150257
--- /dev/null
+++ b/base/debug/OWNERS
@@ -0,0 +1,2 @@
+# For activity tracking:
+per-file activity_*=bcwhite@chromium.org
diff --git a/base/fuchsia/OWNERS b/base/fuchsia/OWNERS
new file mode 100644
index 0000000..e7034ea
--- /dev/null
+++ b/base/fuchsia/OWNERS
@@ -0,0 +1 @@
+file://build/fuchsia/OWNERS
diff --git a/base/i18n/OWNERS b/base/i18n/OWNERS
new file mode 100644
index 0000000..d717b8d
--- /dev/null
+++ b/base/i18n/OWNERS
@@ -0,0 +1 @@
+jshin@chromium.org
diff --git a/base/ios/OWNERS b/base/ios/OWNERS
new file mode 100644
index 0000000..bdb59ec
--- /dev/null
+++ b/base/ios/OWNERS
@@ -0,0 +1,3 @@
+eugenebut@chromium.org
+rohitrao@chromium.org
+sdefresne@chromium.org
diff --git a/base/json/OWNERS b/base/json/OWNERS
new file mode 100644
index 0000000..e44d995
--- /dev/null
+++ b/base/json/OWNERS
@@ -0,0 +1 @@
+file://base/SECURITY_OWNERS
diff --git a/base/mac/OWNERS b/base/mac/OWNERS
new file mode 100644
index 0000000..93e90e0
--- /dev/null
+++ b/base/mac/OWNERS
@@ -0,0 +1,8 @@
+mark@chromium.org
+thakis@chromium.org
+
+# sdk_forward_declarations.[h|mm] will likely need to be modified by Cocoa
+# developers in general.
+per-file sdk_forward_declarations.*=file://chrome/browser/ui/cocoa/OWNERS
+
+# COMPONENT: Internals
diff --git a/base/memory/OWNERS b/base/memory/OWNERS
new file mode 100644
index 0000000..9b7cbb1
--- /dev/null
+++ b/base/memory/OWNERS
@@ -0,0 +1,4 @@
+per-file *chromeos*=skuhne@chromium.org
+per-file *chromeos*=oshima@chromium.org
+per-file *shared_memory*=set noparent
+per-file *shared_memory*=file://ipc/SECURITY_OWNERS
diff --git a/base/metrics/OWNERS b/base/metrics/OWNERS
new file mode 100644
index 0000000..4cc69ff
--- /dev/null
+++ b/base/metrics/OWNERS
@@ -0,0 +1,10 @@
+asvitkine@chromium.org
+bcwhite@chromium.org
+gayane@chromium.org
+holte@chromium.org
+isherman@chromium.org
+jwd@chromium.org
+mpearson@chromium.org
+rkaplow@chromium.org
+
+# COMPONENT: Internals>Metrics
diff --git a/base/nix/OWNERS b/base/nix/OWNERS
new file mode 100644
index 0000000..280ba47
--- /dev/null
+++ b/base/nix/OWNERS
@@ -0,0 +1 @@
+thomasanderson@chromium.org
diff --git a/base/numerics/DEPS b/base/numerics/DEPS
new file mode 100644
index 0000000..d95bf13
--- /dev/null
+++ b/base/numerics/DEPS
@@ -0,0 +1,7 @@
+# This is a dependency-free, header-only, library, and it needs to stay that
+# way to facilitate pulling it into various third-party projects. So, this
+# file is here to protect against accidentally introducing dependencies.
+include_rules = [
+  "-base",
+  "+base/numerics",
+]
diff --git a/base/numerics/OWNERS b/base/numerics/OWNERS
new file mode 100644
index 0000000..5493fba
--- /dev/null
+++ b/base/numerics/OWNERS
@@ -0,0 +1,5 @@
+jschuh@chromium.org
+tsepez@chromium.org
+
+
+# COMPONENT: Internals
diff --git a/base/profiler/OWNERS b/base/profiler/OWNERS
new file mode 100644
index 0000000..81ff9fa
--- /dev/null
+++ b/base/profiler/OWNERS
@@ -0,0 +1,5 @@
+# Stack sampling profiler
+per-file native_stack_sampler*=wittman@chromium.org
+per-file stack_sampling_profiler*=wittman@chromium.org
+per-file test_support_library*=wittman@chromium.org
+per-file win32_stack_frame_unwinder*=wittman@chromium.org
diff --git a/base/sampling_heap_profiler/OWNERS b/base/sampling_heap_profiler/OWNERS
new file mode 100644
index 0000000..87c9661
--- /dev/null
+++ b/base/sampling_heap_profiler/OWNERS
@@ -0,0 +1 @@
+alph@chromium.org
diff --git a/base/strings/OWNERS b/base/strings/OWNERS
new file mode 100644
index 0000000..5381872
--- /dev/null
+++ b/base/strings/OWNERS
@@ -0,0 +1,2 @@
+per-file safe_sprintf*=jln@chromium.org
+per-file safe_sprintf*=mdempsky@chromium.org
diff --git a/base/task/OWNERS b/base/task/OWNERS
new file mode 100644
index 0000000..0f3ad5e
--- /dev/null
+++ b/base/task/OWNERS
@@ -0,0 +1,6 @@
+fdoray@chromium.org
+gab@chromium.org
+robliao@chromium.org
+
+# TEAM: scheduler-dev@chromium.org
+# COMPONENT: Internals>TaskScheduler
diff --git a/base/task/sequence_manager/OWNERS b/base/task/sequence_manager/OWNERS
new file mode 100644
index 0000000..ac6eae8
--- /dev/null
+++ b/base/task/sequence_manager/OWNERS
@@ -0,0 +1,6 @@
+altimin@chromium.org
+alexclarke@chromium.org
+skyostil@chromium.org
+
+# TEAM: scheduler-dev@chromium.org
+# Component: Blink>Scheduling
diff --git a/base/task/task_scheduler/OWNERS b/base/task/task_scheduler/OWNERS
new file mode 100644
index 0000000..0f3ad5e
--- /dev/null
+++ b/base/task/task_scheduler/OWNERS
@@ -0,0 +1,6 @@
+fdoray@chromium.org
+gab@chromium.org
+robliao@chromium.org
+
+# TEAM: scheduler-dev@chromium.org
+# COMPONENT: Internals>TaskScheduler
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 756adf0..a4e3b91 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -378,7 +378,7 @@
   }
 }
 
-source_set("run_all_unittests") {
+static_library("run_all_unittests") {
   testonly = true
   sources = [
     "run_all_unittests.cc",
diff --git a/base/test/DEPS b/base/test/DEPS
new file mode 100644
index 0000000..5827c26
--- /dev/null
+++ b/base/test/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/libxml",
+]
diff --git a/base/test/OWNERS b/base/test/OWNERS
new file mode 100644
index 0000000..08d2b4c
--- /dev/null
+++ b/base/test/OWNERS
@@ -0,0 +1,15 @@
+# Metrics-related test utilites:
+per-file *scoped_feature_list*=file://base/metrics/OWNERS
+
+# Tracing test utilities:
+per-file trace_*=file://base/trace_event/OWNERS
+
+#For Windows-specific test utilities:
+per-file *_win*=file://base/win/OWNERS
+
+# For Android-specific changes:
+per-file *android*=file://base/test/android/OWNERS
+per-file BUILD.gn=file://base/test/android/OWNERS
+
+# Linux fontconfig changes
+per-file *fontconfig*=file://base/nix/OWNERS
diff --git a/base/test/android/OWNERS b/base/test/android/OWNERS
new file mode 100644
index 0000000..2b0078b
--- /dev/null
+++ b/base/test/android/OWNERS
@@ -0,0 +1,4 @@
+jbudorick@chromium.org
+file://base/android/OWNERS
+
+# COMPONENT: Test>Android
diff --git a/base/test/android/java/src/org/chromium/base/OWNERS b/base/test/android/java/src/org/chromium/base/OWNERS
new file mode 100644
index 0000000..89442ab
--- /dev/null
+++ b/base/test/android/java/src/org/chromium/base/OWNERS
@@ -0,0 +1,2 @@
+per-file *.aidl=set noparent
+per-file *.aidl=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/base/test/data/file_util/.gitattributes b/base/test/data/file_util/.gitattributes
new file mode 100644
index 0000000..07998b9
--- /dev/null
+++ b/base/test/data/file_util/.gitattributes
@@ -0,0 +1,2 @@
+/blank_line_crlf.txt -text
+/crlf.txt -text
\ No newline at end of file
diff --git a/base/test/ios/OWNERS b/base/test/ios/OWNERS
new file mode 100644
index 0000000..40a68c7
--- /dev/null
+++ b/base/test/ios/OWNERS
@@ -0,0 +1 @@
+rohitrao@chromium.org
diff --git a/base/test/metrics/OWNERS b/base/test/metrics/OWNERS
new file mode 100644
index 0000000..f117f82
--- /dev/null
+++ b/base/test/metrics/OWNERS
@@ -0,0 +1,5 @@
+#TODO(https://crbug.com/846421): Move other metrics test files into here.
+
+file://base/metrics/OWNERS
+
+# COMPONENT: Internals>Metrics
diff --git a/base/third_party/nspr/OWNERS b/base/third_party/nspr/OWNERS
new file mode 100644
index 0000000..20ba660
--- /dev/null
+++ b/base/third_party/nspr/OWNERS
@@ -0,0 +1,2 @@
+rsleevi@chromium.org
+wtc@chromium.org
diff --git a/base/third_party/superfasthash/OWNERS b/base/third_party/superfasthash/OWNERS
new file mode 100644
index 0000000..633cc35
--- /dev/null
+++ b/base/third_party/superfasthash/OWNERS
@@ -0,0 +1 @@
+mgiuca@chromium.org
diff --git a/base/third_party/symbolize/DEPS b/base/third_party/symbolize/DEPS
new file mode 100644
index 0000000..73eab50
--- /dev/null
+++ b/base/third_party/symbolize/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+glog",
+]
diff --git a/base/time/OWNERS b/base/time/OWNERS
new file mode 100644
index 0000000..ff0520a
--- /dev/null
+++ b/base/time/OWNERS
@@ -0,0 +1,3 @@
+miu@chromium.org
+
+# COMPONENT: Internals>Core
diff --git a/base/trace_event/OWNERS b/base/trace_event/OWNERS
new file mode 100644
index 0000000..24a0bd2
--- /dev/null
+++ b/base/trace_event/OWNERS
@@ -0,0 +1,15 @@
+chiniforooshan@chromium.org
+oysteine@chromium.org
+primiano@chromium.org
+per-file trace_event_android.cc=wangxianzhu@chromium.org
+
+# For memory-infra related changes
+ssid@chromium.org
+
+# Emeritus:
+dsinclair@chromium.org
+nduca@chromium.org
+simonhatch@chromium.org
+
+# TEAM: tracing@chromium.org
+# COMPONENT: Speed>Tracing
diff --git a/base/win/OWNERS b/base/win/OWNERS
new file mode 100644
index 0000000..4593b2c
--- /dev/null
+++ b/base/win/OWNERS
@@ -0,0 +1,7 @@
+brucedawson@chromium.org
+grt@chromium.org
+jschuh@chromium.org
+robliao@chromium.org
+scottmg@chromium.org
+
+# COMPONENT: Internals>PlatformIntegration
diff --git a/build/OWNERS b/build/OWNERS
new file mode 100644
index 0000000..405ccdc
--- /dev/null
+++ b/build/OWNERS
@@ -0,0 +1,26 @@
+set noparent
+# NOTE: keep this in sync with lsc-owners-override@chromium.org owners
+agrieve@chromium.org
+brucedawson@chromium.org
+dpranke@google.com
+jochen@chromium.org
+thakis@chromium.org
+thomasanderson@chromium.org
+tikuta@chromium.org
+
+# Clang build config changes:
+hans@chromium.org
+
+# For java build changes:
+wnwen@chromium.org
+
+# NOTE: keep this in sync with lsc-owners-override@chromium.org owners
+
+per-file .gitignore=*
+per-file check_gn_headers_whitelist.txt=*
+per-file mac_toolchain.py=erikchen@chromium.org
+per-file mac_toolchain.py=justincohen@chromium.org
+per-file whitespace_file.txt=*
+per-file OWNERS.status=*
+per-file OWNERS.setnoparent=set noparent
+per-file OWNERS.setnoparent=file://ENG_REVIEW_OWNERS
diff --git a/build/android/OWNERS b/build/android/OWNERS
new file mode 100644
index 0000000..0b64bda
--- /dev/null
+++ b/build/android/OWNERS
@@ -0,0 +1,7 @@
+bjoyce@chromium.org
+jbudorick@chromium.org
+mheikal@chromium.org
+pasko@chromium.org
+skyostil@chromium.org
+tiborg@chromium.org
+wnwen@chromium.org
diff --git a/build/android/PRESUBMIT.py b/build/android/PRESUBMIT.py
new file mode 100644
index 0000000..2cf0602
--- /dev/null
+++ b/build/android/PRESUBMIT.py
@@ -0,0 +1,119 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Presubmit script for android buildbot.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
+details on the presubmit API built into depot_tools.
+"""
+
+
+def CommonChecks(input_api, output_api):
+  build_android_dir = input_api.PresubmitLocalPath()
+
+  def J(*dirs):
+    """Returns a path relative to presubmit directory."""
+    return input_api.os_path.join(build_android_dir, *dirs)
+
+  build_pys = [
+      r'gn/.*\.py$',
+      r'gyp/.*\.py$',
+  ]
+  tests = []
+  # yapf likes formatting the extra_paths_list to be less readable.
+  # yapf: disable
+  tests.extend(
+      input_api.canned_checks.GetPylint(
+          input_api,
+          output_api,
+          pylintrc='pylintrc',
+          files_to_skip=[
+              r'.*_pb2\.py',
+              r'.*list_java_targets\.py',  # crbug.com/1100664
+              r'.*fast_local_dev_server\.py',  # crbug.com/1100664
+          ] + build_pys,
+          extra_paths_list=[
+              J(),
+              J('gyp'),
+              J('buildbot'),
+              J('..', 'util', 'lib', 'common'),
+              J('..', '..', 'third_party', 'catapult', 'common',
+                'py_trace_event'),
+              J('..', '..', 'third_party', 'catapult', 'common', 'py_utils'),
+              J('..', '..', 'third_party', 'catapult', 'devil'),
+              J('..', '..', 'third_party', 'catapult', 'tracing'),
+              J('..', '..', 'third_party', 'depot_tools'),
+              J('..', '..', 'third_party', 'colorama', 'src'),
+              J('..', '..', 'build'),
+          ]))
+  tests.extend(
+      input_api.canned_checks.GetPylint(
+          input_api,
+          output_api,
+          files_to_check=build_pys,
+          files_to_skip=[
+              r'.*_pb2\.py',
+              r'.*_pb2\.py',
+          ],
+          extra_paths_list=[J('gyp'), J('gn')]))
+  # yapf: enable
+
+  # Disabled due to http://crbug.com/410936
+  #output.extend(input_api.canned_checks.RunUnitTestsInDirectory(
+  #input_api, output_api, J('buildbot', 'tests')))
+
+  pylib_test_env = dict(input_api.environ)
+  pylib_test_env.update({
+      'PYTHONPATH': build_android_dir,
+      'PYTHONDONTWRITEBYTECODE': '1',
+  })
+  tests.extend(
+      input_api.canned_checks.GetUnitTests(
+          input_api,
+          output_api,
+          unit_tests=[
+              J('.', 'emma_coverage_stats_test.py'),
+              J('.', 'list_class_verification_failures_test.py'),
+              J('gyp', 'util', 'build_utils_test.py'),
+              J('gyp', 'util', 'manifest_utils_test.py'),
+              J('gyp', 'util', 'md5_check_test.py'),
+              J('gyp', 'util', 'resource_utils_test.py'),
+              J('pylib', 'constants', 'host_paths_unittest.py'),
+              J('pylib', 'gtest', 'gtest_test_instance_test.py'),
+              J('pylib', 'instrumentation',
+                'instrumentation_test_instance_test.py'),
+              J('pylib', 'local', 'device', 'local_device_gtest_run_test.py'),
+              J('pylib', 'local', 'device',
+                'local_device_instrumentation_test_run_test.py'),
+              J('pylib', 'local', 'device', 'local_device_test_run_test.py'),
+              J('pylib', 'local', 'machine',
+                'local_machine_junit_test_run_test.py'),
+              J('pylib', 'output', 'local_output_manager_test.py'),
+              J('pylib', 'output', 'noop_output_manager_test.py'),
+              J('pylib', 'output', 'remote_output_manager_test.py'),
+              J('pylib', 'results', 'json_results_test.py'),
+              J('pylib', 'symbols', 'apk_native_libs_unittest.py'),
+              J('pylib', 'symbols', 'elf_symbolizer_unittest.py'),
+              J('pylib', 'symbols', 'symbol_utils_unittest.py'),
+              J('pylib', 'utils', 'chrome_proxy_utils_test.py'),
+              J('pylib', 'utils', 'decorators_test.py'),
+              J('pylib', 'utils', 'device_dependencies_test.py'),
+              J('pylib', 'utils', 'dexdump_test.py'),
+              J('pylib', 'utils', 'gold_utils_test.py'),
+              J('pylib', 'utils', 'proguard_test.py'),
+              J('pylib', 'utils', 'test_filter_test.py'),
+              J('.', 'convert_dex_profile_tests.py'),
+          ],
+          env=pylib_test_env,
+          run_on_python2=False))
+
+  return input_api.RunTests(tests)
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return CommonChecks(input_api, output_api)
diff --git a/build/android/gradle/OWNERS b/build/android/gradle/OWNERS
new file mode 100644
index 0000000..a0e0826
--- /dev/null
+++ b/build/android/gradle/OWNERS
@@ -0,0 +1,2 @@
+agrieve@chromium.org
+wnwen@chromium.org
diff --git a/build/android/gyp/OWNERS b/build/android/gyp/OWNERS
new file mode 100644
index 0000000..25557e1
--- /dev/null
+++ b/build/android/gyp/OWNERS
@@ -0,0 +1,4 @@
+agrieve@chromium.org
+digit@chromium.org
+smaier@chromium.org
+wnwen@chromium.org
diff --git a/build/android/pylib/gtest/filter/OWNERS b/build/android/pylib/gtest/filter/OWNERS
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/build/android/pylib/gtest/filter/OWNERS
@@ -0,0 +1 @@
+*
diff --git a/build/android/pylib/local/emulator/OWNERS b/build/android/pylib/local/emulator/OWNERS
new file mode 100644
index 0000000..0853590
--- /dev/null
+++ b/build/android/pylib/local/emulator/OWNERS
@@ -0,0 +1,4 @@
+bpastene@chromium.org
+hypan@google.com
+jbudorick@chromium.org
+liaoyuke@chromium.org
diff --git a/build/apple/OWNERS b/build/apple/OWNERS
new file mode 100644
index 0000000..07d900e
--- /dev/null
+++ b/build/apple/OWNERS
@@ -0,0 +1,4 @@
+mark@chromium.org
+rohitrao@chromium.org
+rsesek@chromium.org
+sdefresne@chromium.org
diff --git a/build/args/OWNERS b/build/args/OWNERS
new file mode 100644
index 0000000..d218b6b
--- /dev/null
+++ b/build/args/OWNERS
@@ -0,0 +1 @@
+per-file headless.gn=file://headless/OWNERS
diff --git a/build/chromeos/OWNERS b/build/chromeos/OWNERS
new file mode 100644
index 0000000..e1058c8
--- /dev/null
+++ b/build/chromeos/OWNERS
@@ -0,0 +1 @@
+bpastene@chromium.org
diff --git a/build/chromeos/PRESUBMIT.py b/build/chromeos/PRESUBMIT.py
new file mode 100644
index 0000000..312faf0
--- /dev/null
+++ b/build/chromeos/PRESUBMIT.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Presubmit script for build/chromeos/.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
+details on the presubmit API built into depot_tools.
+"""
+
+
+def CommonChecks(input_api, output_api):
+  results = []
+  results += input_api.canned_checks.RunPylint(
+      input_api, output_api, pylintrc='pylintrc')
+  tests = input_api.canned_checks.GetUnitTestsInDirectory(
+      input_api, output_api, '.', [r'^.+_test\.py$'], run_on_python3=True)
+  results += input_api.RunTests(tests)
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return CommonChecks(input_api, output_api)
diff --git a/build/config/OWNERS b/build/config/OWNERS
new file mode 100644
index 0000000..eeb6706
--- /dev/null
+++ b/build/config/OWNERS
@@ -0,0 +1,5 @@
+dpranke@google.com
+scottmg@chromium.org
+
+per-file ozone.gni=file://ui/ozone/OWNERS
+per-file ozone_extra.gni=file://ui/ozone/OWNERS
diff --git a/build/config/android/OWNERS b/build/config/android/OWNERS
new file mode 100644
index 0000000..a74cfbe
--- /dev/null
+++ b/build/config/android/OWNERS
@@ -0,0 +1 @@
+file://build/android/OWNERS
diff --git a/build/config/apple/OWNERS b/build/config/apple/OWNERS
new file mode 100644
index 0000000..6f3324f
--- /dev/null
+++ b/build/config/apple/OWNERS
@@ -0,0 +1 @@
+file://build/apple/OWNERS
diff --git a/build/config/clang/clang.gni b/build/config/clang/clang.gni
index 5888645..e8f794c 100644
--- a/build/config/clang/clang.gni
+++ b/build/config/clang/clang.gni
@@ -4,7 +4,17 @@
 
 import("//build/toolchain/toolchain.gni")
 
-default_clang_base_path = "//third_party/llvm-build/Release+Asserts"
+if (is_starboard) {
+  import("//starboard/build/toolchain/starboard_toolchains.gni")
+
+  declare_args() {
+    clang_revision = "365097-f7e52fbd-8"
+  }
+
+  default_clang_base_path = "$starboard_toolchains_path/x86_64-linux-gnu-clang-chromium-${clang_revision}"  
+} else {
+  default_clang_base_path = "//third_party/llvm-build/Release+Asserts"
+}
 
 declare_args() {
   # Indicates if the build should use the Chrome-specific plugins for enforcing
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index eafd9e9..22b5a42 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1988,11 +1988,15 @@
 # Shared settings for both "optimize" and "optimize_max" configs.
 # IMPORTANT: On Windows "/O1" and "/O2" must go before the common flags.
 if (is_win) {
-  common_optimize_on_cflags = [
-    "/Ob2",  # Both explicit and auto inlining.
-    "/Oy-",  # Disable omitting frame pointers, must be after /O2.
-    "/Zc:inline",  # Remove unreferenced COMDAT (faster links).
-  ]
+  if (is_starboard) {
+    common_optimize_on_cflags = []
+  } else {
+    common_optimize_on_cflags = [
+      "/Ob2",  # Both explicit and auto inlining.
+      "/Oy-",  # Disable omitting frame pointers, must be after /O2.
+      "/Zc:inline",  # Remove unreferenced COMDAT (faster links).
+    ]
+  }
   if (!is_asan) {
     common_optimize_on_cflags += [
       # Put data in separate COMDATs. This allows the linker
diff --git a/build/config/coverage/OWNERS b/build/config/coverage/OWNERS
new file mode 100644
index 0000000..0fc481f
--- /dev/null
+++ b/build/config/coverage/OWNERS
@@ -0,0 +1,3 @@
+inferno@chromium.org
+liaoyuke@chromium.org
+ochang@chromium.org
diff --git a/build/config/freetype/OWNERS b/build/config/freetype/OWNERS
new file mode 100644
index 0000000..3277f87
--- /dev/null
+++ b/build/config/freetype/OWNERS
@@ -0,0 +1,2 @@
+bungeman@chromium.org
+drott@chromium.org
diff --git a/build/config/fuchsia/OWNERS b/build/config/fuchsia/OWNERS
new file mode 100644
index 0000000..3a1056b
--- /dev/null
+++ b/build/config/fuchsia/OWNERS
@@ -0,0 +1,4 @@
+file://build/fuchsia/OWNERS
+
+per-file *.cmx=set noparent
+per-file *.cmx=file://fuchsia/SECURITY_OWNERS
diff --git a/build/config/fuchsia/test/OWNERS b/build/config/fuchsia/test/OWNERS
new file mode 100644
index 0000000..3be17de
--- /dev/null
+++ b/build/config/fuchsia/test/OWNERS
@@ -0,0 +1,7 @@
+file://build/fuchsia/OWNERS
+
+per-file *.test-cmx=set noparent
+per-file *.test-cmx=ddorwin@chromium.org
+per-file *.test-cmx=wez@chromium.org
+# Please prefer the above when possible.
+per-file *.test-cmx=file://fuchsia/SECURITY_OWNERS
diff --git a/build/config/ios/OWNERS b/build/config/ios/OWNERS
new file mode 100644
index 0000000..6f3324f
--- /dev/null
+++ b/build/config/ios/OWNERS
@@ -0,0 +1 @@
+file://build/apple/OWNERS
diff --git a/build/config/linux/OWNERS b/build/config/linux/OWNERS
new file mode 100644
index 0000000..280ba47
--- /dev/null
+++ b/build/config/linux/OWNERS
@@ -0,0 +1 @@
+thomasanderson@chromium.org
diff --git a/build/config/mac/OWNERS b/build/config/mac/OWNERS
new file mode 100644
index 0000000..6f3324f
--- /dev/null
+++ b/build/config/mac/OWNERS
@@ -0,0 +1 @@
+file://build/apple/OWNERS
diff --git a/build/config/profiling/OWNERS b/build/config/profiling/OWNERS
new file mode 100644
index 0000000..225ce18
--- /dev/null
+++ b/build/config/profiling/OWNERS
@@ -0,0 +1,3 @@
+liaoyuke@chromium.org
+sajjadm@chromium.org
+sebmarchand@chromium.org
diff --git a/build/config/sanitizers/OWNERS b/build/config/sanitizers/OWNERS
new file mode 100644
index 0000000..f6a122b
--- /dev/null
+++ b/build/config/sanitizers/OWNERS
@@ -0,0 +1,3 @@
+inferno@chromium.org
+metzman@chromium.org
+ochang@chromium.org
diff --git a/build/config/win/visual_studio_version.gni b/build/config/win/visual_studio_version.gni
index 48ecc5b..b46c531 100644
--- a/build/config/win/visual_studio_version.gni
+++ b/build/config/win/visual_studio_version.gni
@@ -16,14 +16,24 @@
     wdk_version = "10.0.17763.0"
   }
 
+  if (is_docker_build) {
+    _default_visual_studio_path = "C:/BuildTools"
+  } else {
+    _default_visual_studio_path = "C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional"
+  }
+
   declare_args() {
     # Path to Visual Studio.
-    visual_studio_path = "C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/VC/Tools/MSVC/$visual_studio_version"
+    visual_studio_path = _default_visual_studio_path
 
-    wdk_include_path = "$windows_sdk_path/Include/$wdk_version"
+    wdk_include_path = "$windows_sdk_path/include/$wdk_version"
 
     wdk_lib_path = "$windows_sdk_path/lib/$wdk_version"
   }
+
+  declare_args() {
+    msvc_path = "$visual_studio_path/VC/Tools/MSVC/$visual_studio_version"
+  }
 } else {
   declare_args() {
     # Path to Visual Studio. If empty, the default is used which is to use the
diff --git a/build/fuchsia/OWNERS b/build/fuchsia/OWNERS
new file mode 100644
index 0000000..3dcaf8a
--- /dev/null
+++ b/build/fuchsia/OWNERS
@@ -0,0 +1,8 @@
+ddorwin@chromium.org
+fdegans@chromium.org
+kmarshall@chromium.org
+sergeyu@chromium.org
+wez@chromium.org
+
+per-file linux.sdk.sha1=chromium-autoroll@skia-public.iam.gserviceaccount.com
+per-file mac.sdk.sha1=chromium-autoroll@skia-public.iam.gserviceaccount.com
diff --git a/build/ios/OWNERS b/build/ios/OWNERS
new file mode 100644
index 0000000..6f3324f
--- /dev/null
+++ b/build/ios/OWNERS
@@ -0,0 +1 @@
+file://build/apple/OWNERS
diff --git a/build/lacros/OWNERS b/build/lacros/OWNERS
new file mode 100644
index 0000000..aae4f73
--- /dev/null
+++ b/build/lacros/OWNERS
@@ -0,0 +1,3 @@
+svenzheng@chromium.org
+liaoyuke@chromium.org
+erikchen@chromium.org
diff --git a/build/lacros/PRESUBMIT.py b/build/lacros/PRESUBMIT.py
new file mode 100644
index 0000000..1394a42
--- /dev/null
+++ b/build/lacros/PRESUBMIT.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Presubmit script for changes affecting //build/lacros"""
+
+
+def _CommonChecks(input_api, output_api):
+  tests = input_api.canned_checks.GetUnitTestsInDirectory(
+      input_api, output_api, '.', [r'^.+_test\.py$'])
+  return input_api.RunTests(tests)
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return _CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return _CommonChecks(input_api, output_api)
diff --git a/build/linux/OWNERS b/build/linux/OWNERS
new file mode 100644
index 0000000..8e1cb55
--- /dev/null
+++ b/build/linux/OWNERS
@@ -0,0 +1,3 @@
+mmoss@chromium.org
+thestig@chromium.org
+thomasanderson@chromium.org
diff --git a/build/linux/libncursesw/OWNERS b/build/linux/libncursesw/OWNERS
new file mode 100644
index 0000000..976b955
--- /dev/null
+++ b/build/linux/libncursesw/OWNERS
@@ -0,0 +1 @@
+file://ui/accessibility/OWNERS
diff --git a/build/mac/OWNERS b/build/mac/OWNERS
new file mode 100644
index 0000000..6f3324f
--- /dev/null
+++ b/build/mac/OWNERS
@@ -0,0 +1 @@
+file://build/apple/OWNERS
diff --git a/build/sanitizers/OWNERS b/build/sanitizers/OWNERS
new file mode 100644
index 0000000..b893bc8
--- /dev/null
+++ b/build/sanitizers/OWNERS
@@ -0,0 +1,8 @@
+ochang@chromium.org
+eugenis@chromium.org
+glider@chromium.org
+inferno@chromium.org
+metzman@chromium.org
+rnk@chromium.org
+per-file tsan_suppressions.cc=*
+per-file lsan_suppressions.cc=*
diff --git a/build/skia_gold_common/OWNERS b/build/skia_gold_common/OWNERS
new file mode 100644
index 0000000..428f610
--- /dev/null
+++ b/build/skia_gold_common/OWNERS
@@ -0,0 +1 @@
+bsheedy@chromium.org
diff --git a/build/skia_gold_common/PRESUBMIT.py b/build/skia_gold_common/PRESUBMIT.py
new file mode 100644
index 0000000..41e1bb2
--- /dev/null
+++ b/build/skia_gold_common/PRESUBMIT.py
@@ -0,0 +1,34 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Presubmit script for //build/skia_gold_common/.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details on the presubmit API built into depot_tools.
+"""
+
+
+def CommonChecks(input_api, output_api):
+  output = []
+  build_path = input_api.os_path.join(input_api.PresubmitLocalPath(), '..')
+  skia_gold_env = dict(input_api.environ)
+  skia_gold_env.update({
+      'PYTHONPATH': build_path,
+      'PYTHONDONTWRITEBYTECODE': '1',
+  })
+  output.extend(
+      input_api.canned_checks.RunUnitTestsInDirectory(
+          input_api,
+          output_api,
+          input_api.PresubmitLocalPath(), [r'^.+_unittest\.py$'],
+          env=skia_gold_env))
+  output.extend(input_api.canned_checks.RunPylint(input_api, output_api))
+  return output
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return CommonChecks(input_api, output_api)
diff --git a/build/toolchain/OWNERS b/build/toolchain/OWNERS
new file mode 100644
index 0000000..d7012d3
--- /dev/null
+++ b/build/toolchain/OWNERS
@@ -0,0 +1,5 @@
+dpranke@google.com
+scottmg@chromium.org
+
+# Code Coverage.
+per-file *code_coverage*=liaoyuke@chromium.org
diff --git a/build/toolchain/android/OWNERS b/build/toolchain/android/OWNERS
new file mode 100644
index 0000000..a74cfbe
--- /dev/null
+++ b/build/toolchain/android/OWNERS
@@ -0,0 +1 @@
+file://build/android/OWNERS
diff --git a/build/toolchain/apple/OWNERS b/build/toolchain/apple/OWNERS
new file mode 100644
index 0000000..6f3324f
--- /dev/null
+++ b/build/toolchain/apple/OWNERS
@@ -0,0 +1 @@
+file://build/apple/OWNERS
diff --git a/build/toolchain/cc_wrapper.gni b/build/toolchain/cc_wrapper.gni
index 6d6cfcf..070461f 100644
--- a/build/toolchain/cc_wrapper.gni
+++ b/build/toolchain/cc_wrapper.gni
@@ -35,7 +35,13 @@
 declare_args() {
   # Set to "ccache", "icecc" or "distcc".  Probably doesn't work on windows.
   cc_wrapper = ""
-  if (is_starboard) {
+}
+
+if (is_starboard && cc_wrapper == "") {
+  # TODO(https://crbug.com/gn/273): Use sccache locally as well.
+  if (host_os == "win" && cobalt_fastbuild) {
+    cc_wrapper = "sccache.exe"
+  } else if (host_os != "win") {
     cc_wrapper = "ccache"
   }
 }
diff --git a/build/toolchain/fuchsia/OWNERS b/build/toolchain/fuchsia/OWNERS
new file mode 100644
index 0000000..3f809e8
--- /dev/null
+++ b/build/toolchain/fuchsia/OWNERS
@@ -0,0 +1 @@
+scottmg@chromium.org
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index 42e2b80..b432d20 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -105,6 +105,15 @@
 #      Location of the strip executable. When specified, strip will be run on
 #      all shared libraries and executables as they are built. The pre-stripped
 #      artifacts will be put in lib.unstripped/ and exe.unstripped/.
+#
+# Optional parameters added with the Starboard platform:
+#
+#  - tail_lib_dependencies
+#      If defined, this string will be added to the compilation line after all
+#      other libs are specified.
+#  - using_snarl_linker
+#      Defining this string will ensure static linker flags are passed in a way
+#      that the snarl tool will accept.
 template("gcc_toolchain") {
   toolchain(target_name) {
     assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value")
@@ -266,9 +275,9 @@
       compiler_prefix = "$python_path ${_coverage_wrapper} " + compiler_prefix
     }
 
-    cc = compiler_prefix + invoker.cc
-    cxx = compiler_prefix + invoker.cxx
-    asm = asm_prefix + invoker.cc
+    cc = "$compiler_prefix\"${invoker.cc}\""
+    cxx = "$compiler_prefix\"${invoker.cxx}\""
+    asm = "$asm_prefix\"${invoker.cc}\""
     ar = invoker.ar
     ld = "$goma_ld${invoker.ld}"
     if (defined(invoker.readelf)) {
@@ -346,6 +355,12 @@
     # Object files go in this directory.
     object_subdir = "{{target_out_dir}}/{{label_name}}"
 
+    if (defined(invoker.tail_lib_dependencies)) {
+      tail_lib_dependencies = " " + invoker.tail_lib_dependencies
+    } else {
+      tail_lib_dependencies = ""
+    }
+
     tool("cc") {
       depfile = "{{output}}.d"
       precompiled_header_type = "gcc"
@@ -367,14 +382,27 @@
     tool("asm") {
       # For GCC we can just use the C compiler to compile assembly.
       depfile = "{{output}}.d"
-      command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}}${extra_asmflags} -c {{source}} -o {{output}}"
+
+      # TODO(b/206642994): see if we can remove this condition. It's needed for
+      # now to add cflags for evergreen-arm* platforms but we haven't yet
+      # decided whether cflags should be added here for all platforms.
+      if (is_starboard && sb_is_evergreen && target_cpu == "arm") {
+        command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{cflags}} {{asmflags}}${extra_asmflags} -c {{source}} -o {{output}}"
+      } else {
+        command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}}${extra_asmflags} -c {{source}} -o {{output}}"
+      }
+
       depsformat = "gcc"
       description = "ASM {{output}}"
       outputs = [ "$object_subdir/{{source_name_part}}.o" ]
     }
 
     tool("alink") {
-      if (current_os == "aix") {
+      if (defined(invoker.using_snarl_linker) && invoker.using_snarl_linker) {
+        rspfile = "{{output}}.rsp"
+        rspfile_content = "{{inputs_newline}}"
+        command = "\"$ar\" {{arflags}} rcsD {{output}} @\"$rspfile\""
+      } else if (current_os == "aix") {
         # AIX does not support either -D (deterministic output) or response
         # files.
         command = "$ar -X64 {{arflags}} -r -c -s {{output}} {{inputs}}"
@@ -424,7 +452,14 @@
       # .TOC file, overwrite it, otherwise, don't change it.
       tocfile = sofile + ".TOC"
 
-      link_command = "$ld -shared -Wl,-soname=\"$soname\" {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" @\"$rspfile\""
+      # TODO(b/206642994): see if we can remove this condition. It's needed for
+      # now because we use the ld.lld linker for evergreen-arm* and need to pass
+      # options as `option` instead of `-Wl,option`.
+      if (is_starboard && sb_is_evergreen && target_cpu == "arm") {
+        link_command = "$ld -shared -soname=\"$soname\" {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" @\"$rspfile\""
+      } else {
+        link_command = "$ld -shared -Wl,-soname=\"$soname\" {{ldflags}}${extra_ldflags} -o \"$unstripped_sofile\" @\"$rspfile\""
+      }
 
       # Generate a map file to be used for binary size analysis.
       # Map file adds ~10% to the link time on a z620.
@@ -450,9 +485,14 @@
       command = "$python_path \"$solink_wrapper\" --readelf=\"$readelf\" --nm=\"$nm\" $strip_switch$dwp_switch --sofile=\"$unstripped_sofile\" --tocfile=\"$tocfile\"$map_switch --output=\"$sofile\" -- $link_command"
 
       if (target_cpu == "mipsel" && is_component_build && is_android) {
-        rspfile_content = "-Wl,--start-group -Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}} -Wl,--end-group"
+        rspfile_content = "-Wl,--start-group -Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}} -Wl,--end-group$tail_lib_dependencies"
+        # TODO(b/206642994): see if we can remove this condition. It's needed for
+        # now because we use the ld.lld linker for evergreen-arm* and need to
+        # pass options as `option` instead of `-Wl,option`.
+      } else if (is_starboard && sb_is_evergreen && target_cpu == "arm") {
+        rspfile_content = "--whole-archive {{inputs}} {{solibs}} --no-whole-archive {{libs}}$tail_lib_dependencies"
       } else {
-        rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
+        rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}$tail_lib_dependencies"
       }
 
       description = "SOLINK $sofile"
@@ -520,7 +560,7 @@
         strip_command = "${invoker.strip} -o \"$sofile\" \"$unstripped_sofile\""
         command += " && " + strip_command
       }
-      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
+      rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}$tail_lib_dependencies"
 
       description = "SOLINK_MODULE $sofile"
 
@@ -572,7 +612,7 @@
         start_group_flag = "-Wl,--start-group"
         end_group_flag = "-Wl,--end-group "
       }
-      link_command = "$ld {{ldflags}}${extra_ldflags} -o \"$unstripped_outfile\" $start_group_flag @\"$rspfile\" {{solibs}} $end_group_flag {{libs}}"
+      link_command = "$ld {{ldflags}}${extra_ldflags} {{libs}} -o \"$unstripped_outfile\" $start_group_flag @\"$rspfile\" {{solibs}} $end_group_flag$tail_lib_dependencies"
 
       # Generate a map file to be used for binary size analysis.
       # Map file adds ~10% to the link time on a z620.
diff --git a/build/toolchain/ios/OWNERS b/build/toolchain/ios/OWNERS
new file mode 100644
index 0000000..6f3324f
--- /dev/null
+++ b/build/toolchain/ios/OWNERS
@@ -0,0 +1 @@
+file://build/apple/OWNERS
diff --git a/build/toolchain/mac/OWNERS b/build/toolchain/mac/OWNERS
new file mode 100644
index 0000000..6f3324f
--- /dev/null
+++ b/build/toolchain/mac/OWNERS
@@ -0,0 +1 @@
+file://build/apple/OWNERS
diff --git a/build/toolchain/win/msvc_toolchain.gni b/build/toolchain/win/msvc_toolchain.gni
index 77b60f9..2755047 100644
--- a/build/toolchain/win/msvc_toolchain.gni
+++ b/build/toolchain/win/msvc_toolchain.gni
@@ -78,6 +78,14 @@
     asm = invoker.asm
 
     env_wrapper = "ninja -t msvc -e $env -- "  # Note trailing space.
+
+    # TODO(https://crbug.com/gn/273): Always use sccache for cc and cxx tools.
+    if (cc_wrapper != "") {
+      cl_prefix = "${cc_wrapper} "  # Note trailing space.
+    } else {
+      cl_prefix = env_wrapper
+    }
+
     sys_include_flags = ""
 
     tool("cc") {
@@ -91,7 +99,7 @@
       description = "CC {{output}}"
       outputs = [ "$object_subdir/{{source_name_part}}.obj" ]
 
-      command = "$env_wrapper$cl /nologo /showIncludes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
+      command = "$cl_prefix\"$cl\" /nologo /showIncludes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
     }
 
     tool("cxx") {
@@ -105,7 +113,7 @@
       description = "CXX {{output}}"
       outputs = [ "$object_subdir/{{source_name_part}}.obj" ]
 
-      command = "$env_wrapper$cl /nologo /showIncludes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
+      command = "$cl_prefix\"$cl\" /nologo /showIncludes $sys_include_flags{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\""
     }
 
     tool("asm") {
@@ -114,12 +122,11 @@
       command ="$env_wrapper$asm /nologo /Fo{{output}} /c {{defines}} {{include_dirs}} {{asmflags}} {{source}}"
     }
 
-    linker_wrapper = "ninja -t msvc -e $env -- "  # Note trailing space.
     sys_lib_flags = "${invoker.sys_lib_flags} "  # Note trailing space.
 
     tool("alink") {
       rspfile = "{{output}}.rsp"
-      command = "$linker_wrapper$lib /OUT:{{output}} /nologo ${sys_lib_flags}{{arflags}} @$rspfile"
+      command = "$env_wrapper$lib /OUT:{{output}} /nologo ${sys_lib_flags}{{arflags}} @$rspfile"
       description = "LIB {{output}}"
       outputs = [
         # Ignore {{output_extension}} and always use .lib, there's no reason to
@@ -142,7 +149,7 @@
       rspfile = "${dllname}.rsp"
       pool = "//build/toolchain:link_pool($default_toolchain)"
 
-      command = "$linker_wrapper$link /OUT:$dllname /nologo ${sys_lib_flags}/IMPLIB:$libname /DLL /PDB:$pdbname @$rspfile"
+      command = "$env_wrapper$link /OUT:$dllname /nologo ${sys_lib_flags}/IMPLIB:$libname /DLL /PDB:$pdbname @$rspfile"
 
       default_output_extension = ".dll"
       default_output_dir = "{{root_out_dir}}"
@@ -176,7 +183,7 @@
       rspfile = "${dllname}.rsp"
       pool = "//build/toolchain:link_pool($default_toolchain)"
 
-      command = "$linker_wrapper$link /OUT:$dllname /nologo ${sys_lib_flags}/DLL /PDB:$pdbname @$rspfile"
+      command = "$env_wrapper$link /OUT:$dllname /nologo ${sys_lib_flags}/DLL /PDB:$pdbname @$rspfile"
 
       default_output_extension = ".dll"
       default_output_dir = "{{root_out_dir}}"
@@ -198,7 +205,7 @@
       rspfile = "$exename.rsp"
       pool = "//build/toolchain:link_pool($default_toolchain)"
 
-      command = "$linker_wrapper$link /OUT:$exename /nologo ${sys_lib_flags} /PDB:$pdbname @$rspfile"
+      command = "$env_wrapper$link /OUT:$exename /nologo ${sys_lib_flags} /PDB:$pdbname @$rspfile"
 
       default_output_extension = ".exe"
       default_output_dir = "{{root_out_dir}}"
diff --git a/build/util/PRESUBMIT.py b/build/util/PRESUBMIT.py
new file mode 100644
index 0000000..1e0fc8c
--- /dev/null
+++ b/build/util/PRESUBMIT.py
@@ -0,0 +1,58 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import re
+"""Presubmit for build/util"""
+
+
+def _GetFilesToSkip(input_api):
+  files_to_skip = []
+  affected_files = input_api.change.AffectedFiles()
+  version_script_change = next(
+      (f for f in affected_files
+       if re.search('\\/version\\.py$|\\/version_test\\.py$', f.LocalPath())),
+      None)
+
+  if version_script_change is None:
+    files_to_skip.append('version_test\\.py$')
+
+  android_chrome_version_script_change = next(
+      (f for f in affected_files if re.search(
+          '\\/android_chrome_version\\.py$|'
+          '\\/android_chrome_version_test\\.py$', f.LocalPath())), None)
+
+  if android_chrome_version_script_change is None:
+    files_to_skip.append('android_chrome_version_test\\.py$')
+
+  return files_to_skip
+
+
+def _GetPythonUnitTests(input_api, output_api):
+  # No need to test if files are unchanged
+  files_to_skip = _GetFilesToSkip(input_api)
+
+  return input_api.canned_checks.GetUnitTestsRecursively(
+      input_api,
+      output_api,
+      input_api.PresubmitLocalPath(),
+      files_to_check=['.*_test\\.py$'],
+      files_to_skip=files_to_skip)
+
+
+def CommonChecks(input_api, output_api):
+  """Presubmit checks run on both upload and commit.
+  """
+  checks = []
+  checks.extend(_GetPythonUnitTests(input_api, output_api))
+  return input_api.RunTests(checks, False)
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  """Presubmit checks on CL upload."""
+  return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  """Presubmit checks on commit."""
+  return CommonChecks(input_api, output_api)
diff --git a/build/util/lib/common/PRESUBMIT.py b/build/util/lib/common/PRESUBMIT.py
new file mode 100644
index 0000000..53984fd
--- /dev/null
+++ b/build/util/lib/common/PRESUBMIT.py
@@ -0,0 +1,16 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+def _RunTests(input_api, output_api):
+  return (input_api.canned_checks.RunUnitTestsInDirectory(
+      input_api, output_api, '.', files_to_check=[r'.+_test.py$']))
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return _RunTests(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return _RunTests(input_api, output_api)
diff --git a/buildtools/DEPS b/buildtools/DEPS
new file mode 100644
index 0000000..fde9a08
--- /dev/null
+++ b/buildtools/DEPS
@@ -0,0 +1,25 @@
+use_relative_paths = True
+
+vars = {
+  "chromium_url": "https://chromium.googlesource.com",
+
+  "clang_format_rev": "0653eee0c81ea04715c635dd0885e8096ff6ba6d",   # r302580
+  "libcxx_revision": "3a07dd740be63878167a0ea19fe81869954badd7",    # r303878
+  "libcxxabi_revision": "4072e8fd76febee37f60aeda76d6d9f5e3791daa", # r303806
+  "libunwind_revision": "41f982e5887185b904a456e20dfcd58e6be6cc19", # r306442
+}
+
+deps = {
+  "clang_format/script":
+    Var("chromium_url") + "/chromium/llvm-project/cfe/tools/clang-format.git@" +
+    Var("clang_format_rev"),
+  "third_party/libc++/trunk":
+    Var("chromium_url") + "/chromium/llvm-project/libcxx.git" + "@" +
+    Var("libcxx_revision"),
+  "third_party/libc++abi/trunk":
+    Var("chromium_url") + "/chromium/llvm-project/libcxxabi.git" + "@" +
+    Var("libcxxabi_revision"),
+  "third_party/libunwind/trunk":
+    Var("chromium_url") + "/external/llvm.org/libunwind.git" + "@" +
+    Var("libunwind_revision"),
+}
diff --git a/buildtools/README.txt b/buildtools/README.txt
new file mode 100644
index 0000000..1db97b4
--- /dev/null
+++ b/buildtools/README.txt
@@ -0,0 +1,56 @@
+This repository contains hashes of build tools used by Chromium and related
+projects. The actual binaries are pulled from Google Storage, normally as part
+of a gclient hook.
+
+The repository is separate so that the shared build tools can be shared between
+the various Chromium-related projects without each one needing to maintain
+their own versionining of each binary.
+
+To update the GN binary, run (from the Chromium repo) tools/gn/bin/roll_gn.py
+which will automatically upload the binaries and roll build tools.
+
+________________________________________
+UPDATING AND ROLLING BUILDTOOLS MANUALLY
+
+When you update buildtools, you should roll the new version into the Chromium
+repository right away. Otherwise, the next person who makes a change will end
+up rolling (and testing) your change. If there are any unresolved problems with
+your change, the next person will be blocked.
+
+  - From the buildtools directory, make a branch, edit and upload normally.
+
+  - Get your change reviewed and landed. There are no trybots so landing will
+    be very fast.
+
+  - Get the hash for the commit that commit-bot made. Make a new branch in
+    the Chromium repository and paste the hash into the line in //DEPS
+    labeled "buildtools_revision".
+
+  - You can TBR changes to the DEPS file since the git hashes can't be reviewed
+    in any practical way. Submit that patch to the commit queue.
+
+  - If this roll identifies a problem with your patch, fix it promptly. If you
+    are unable to fix it promptly, it's best to revert your buildtools patch
+    to avoid blocking other people that want to make changes.
+
+________________________
+ADDING BINARIES MANUALLY
+
+One uploads new versions of the tools using the 'gsutil' binary from the
+Google Storage SDK:
+
+  https://developers.google.com/storage/docs/gsutil
+
+There is a checked-in version of gsutil as part of depot_tools.
+
+To initialize gsutil's credentials:
+
+  python ~/depot_tools/third_party/gsutil/gsutil config
+
+  That will give a URL which you should log into with your web browser. For
+  rolling GN, the username should be the one that is on the ACL for the
+  "chromium-gn" bucket (probably your @google.com address). Contact the build
+  team for help getting access if necessary.
+
+  Copy the code back to the command line util. Ignore the project ID (it's OK
+  to just leave blank when prompted).
diff --git a/cobalt/.gitattributes b/cobalt/.gitattributes
new file mode 100644
index 0000000..0f70607
--- /dev/null
+++ b/cobalt/.gitattributes
@@ -0,0 +1,30 @@
+# These files are text and should be normalized (convert crlf > lf).
+*.bat                   text eol=lf
+*.cc                    text eol=lf
+*.cg                    text eol=lf
+*.cpp                   text eol=lf
+*.css                   text eol=lf
+*.gyp                   text eol=lf
+*.gypi                  text eol=lf
+*.h                     text eol=lf
+*.html                  text eol=lf
+*.idl                   text eol=lf
+*.js                    text eol=lf
+*.pump                  text eol=lf
+*.py                    text eol=lf
+*.sublime-project       text eol=lf
+*.sublime-workspace     text eol=lf
+*.template              text eol=lf
+*.txt                   text eol=lf
+*.y                     text eol=lf
+.clang-format           text eol=lf
+codereview.settings     text eol=lf
+gyp_cobalt              text eol=lf
+
+# Images should be treated as binary files.
+*.exe                   binary
+*.jpg                   binary
+*.mp4                   binary
+*.png                   binary
+*.pyc                   binary
+*.ttf                   binary
diff --git a/cobalt/BUILD.gn b/cobalt/BUILD.gn
index 25ae8bc..7fbccdc 100644
--- a/cobalt/BUILD.gn
+++ b/cobalt/BUILD.gn
@@ -16,72 +16,58 @@
   testonly = true
   deps = [
     ":default",
+    "//cobalt/bindings/testing:bindings_test",
+    "//cobalt/browser:browser_test",
+    "//cobalt/dom:dom_test",
+    "//cobalt/dom/testing:dom_testing",
+    "//cobalt/dom_parser:dom_parser_test",
+    "//cobalt/encoding:text_encoding_test",
+    "//cobalt/extension:extension_test",
     "//cobalt/layout:layout_test",
+    "//cobalt/layout_tests",
+    "//cobalt/layout_tests:web_platform_tests",
+    "//cobalt/loader:loader_test",
+    "//cobalt/loader/image/sandbox:image_decoder_sandbox",
+    "//cobalt/media/sandbox:media_sandbox",
+    "//cobalt/media/sandbox:web_media_player_sandbox",
+    "//cobalt/media_capture:media_capture_test",
+    "//cobalt/media_session:media_session_test",
+    "//cobalt/media_stream:media_stream_test",
+    "//cobalt/renderer:renderer_test",
+    "//cobalt/renderer/sandbox:renderer_sandbox",
+    "//cobalt/renderer/sandbox:scaling_text_sandbox",
+    "//cobalt/speech/sandbox:speech_sandbox",
     "//cobalt/web_animations:web_animations_test",
+    "//cobalt/webdriver:webdriver_test",
     "//cobalt/websocket:websocket_test",
     "//cobalt/xhr:xhr_test",
   ]
+
+  if (sb_is_evergreen) {
+    deps += [
+      "//components/update_client:cobalt_slot_management_test",
+      "//components/update_client:update_client_test",
+    ]
+  }
 }
 
 group("default") {
   deps = [
-    "//cobalt/account",
-    "//cobalt/audio",
-    "//cobalt/base",
-    "//cobalt/browser",
-    "//cobalt/css_parser",
-    "//cobalt/dom",
-    "//cobalt/dom_parser",
-    "//cobalt/encoding:text_encoding",
-    "//cobalt/fetch",
-    "//cobalt/h5vcc",
-    "//cobalt/input",
-    "//cobalt/layout",
-    "//cobalt/math",
-    "//cobalt/media",
-    "//cobalt/media_capture",
-    "//cobalt/media_session",
-    "//cobalt/network",
-    "//cobalt/network_bridge",
-    "//cobalt/overlay_info",
-    "//cobalt/render_tree",
+    "//cobalt/browser:cobalt",
+    "//cobalt/browser:cobalt_install",
     "//cobalt/renderer:default_options",
-    "//cobalt/renderer/backend:renderer_backend",
-    "//cobalt/renderer/rasterizer",
-    "//cobalt/renderer/rasterizer/egl/shaders",
-    "//cobalt/renderer/test/png_utils",
-    "//cobalt/script",
-    "//cobalt/script:engine",
     "//cobalt/script:engine_shell",
-    "//cobalt/sso",
-    "//cobalt/storage",
-    "//cobalt/storage:storage_constants",
-    "//cobalt/subtlecrypto",
-    "//cobalt/trace_event",
-    "//cobalt/ui_navigation",
-    "//cobalt/webdriver",
-    "//cobalt/websocket",
-    "//cobalt/xhr",
-    "//content/browser/speech",
-    "//crypto",
-    "//nb",
-    "//third_party/brotli:dec",
     "//third_party/brotli:dec_no_dictionary_data",
-    "//third_party/flac",
-    "//third_party/freetype2",
-    "//third_party/harfbuzz-ng",
-    "//third_party/libpng",
-    "//third_party/libwebp",
-    "//third_party/ots",
-    "//third_party/skia/third_party/skcms",
-    "//third_party/v8",
-    "//third_party/woff2:woff2_dec",
   ]
 
   if (sb_is_evergreen) {
     deps += [
       "//base/util/values:values_util",
+      "//cobalt/updater",
+      "//components/client_update_protocol",
+      "//components/crx_file",
       "//components/prefs",
+      "//components/update_client",
       "//third_party/llvm-project/compiler-rt:compiler_rt",
       "//third_party/llvm-project/libcxx:cxx",
       "//third_party/llvm-project/libcxxabi:cxxabi",
diff --git a/cobalt/audio/BUILD.gn b/cobalt/audio/BUILD.gn
index d24bd06..88bfeb1 100644
--- a/cobalt/audio/BUILD.gn
+++ b/cobalt/audio/BUILD.gn
@@ -74,10 +74,13 @@
   deps = [
     ":audio",
     "//cobalt/dom",
+    "//cobalt/dom/testing:dom_testing",
     "//cobalt/media",
     "//cobalt/script",
     "//cobalt/test:run_all_unittests",
     "//testing/gtest",
   ]
   deps += cobalt_platform_dependencies
+
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/cobalt/base/BUILD.gn b/cobalt/base/BUILD.gn
index 8cae034..302256c 100644
--- a/cobalt/base/BUILD.gn
+++ b/cobalt/base/BUILD.gn
@@ -115,7 +115,6 @@
     "fixed_size_lru_cache_test.cc",
     "token_test.cc",
   ]
-
   deps = [
     ":base",
     "//cobalt/test:run_all_unittests",
@@ -123,4 +122,5 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/cobalt/bindings/bindings_templates.gni b/cobalt/bindings/bindings_templates.gni
index c2d4daf..12f18ad 100644
--- a/cobalt/bindings/bindings_templates.gni
+++ b/cobalt/bindings/bindings_templates.gni
@@ -12,12 +12,16 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//cobalt/bindings/bindings_variables.gni")
 import("//cobalt/bindings/blink_variables.gni")
 import("//cobalt/script/v8c/v8c_variables.gni")
 
+# The allowlist of what extended attributes we support. If an attribute
+# not in this list is encountered, it will cause an error in the
+# pipeline.
+_extended_attributes_file = "//cobalt/bindings/IDLExtendedAttributes.txt"
+
 # Additional files used by the idl compilation scripts.
-bindings_extra_inputs = [
+_bindings_extra_inputs = [
   "//cobalt/bindings/code_generator_cobalt.py",
   "//cobalt/bindings/expression_generator.py",
   "//cobalt/bindings/contexts.py",
@@ -26,11 +30,9 @@
   "//cobalt/bindings/name_conversion.py",
   "//cobalt/bindings/overload_context.py",
 ]
-bindings_extra_inputs += code_generator_template_files
-bindings_extra_inputs += engine_bindings_scripts
-bindings_extra_inputs += idl_lexer_parser_files
-bindings_extra_inputs += idl_cache_files
-bindings_extra_inputs += idl_compiler_files
+_bindings_extra_inputs += engine_bindings_scripts
+_bindings_extra_inputs += idl_lexer_parser_files
+_bindings_extra_inputs += idl_compiler_files
 
 # Template definitions.
 template("idl_compile") {
@@ -38,30 +40,36 @@
     script = "//starboard/build/run_bash.py"
     py_script = engine_idl_compiler
 
-    public_configs = invoker.public_configs
-
     sources = invoker.sources
 
-    deps = invoker.deps
-
-    if (defined(invoker.public_deps)) {
-      public_deps = invoker.public_deps
-    }
+    forward_variables_from(invoker,
+                           [
+                             "testonly",
+                             "deps",
+                             "public_deps",
+                             "public_configs",
+                           ])
 
     inputs = [
-      invoker.interfaces_info,
-      invoker.extended_attributes,
+      _extended_attributes_file,
       invoker.component_info,
+      invoker.interfaces_info,
       py_script,
     ]
-    inputs += invoker.bindings_extra_inputs
+    inputs += _bindings_extra_inputs
 
     generated_file_path =
-        "$generated_source_output_dir/{{source_root_relative_dir}}"
-    generated_file_name = "${generated_bindings_prefix}_{{source_name_part}}"
+        invoker.output_directory + "/{{source_root_relative_dir}}"
+
+    if (defined(invoker.header_prefix)) {
+      header_prefix = invoker.header_prefix + "_"
+    } else {
+      header_prefix = ""
+    }
+
     outputs = [
-      "$generated_file_path/$generated_file_name.h",
-      "$generated_file_path/$generated_file_name.cc",
+      "$generated_file_path/${header_prefix}{{source_name_part}}.h",
+      "$generated_file_path/${generated_bindings_prefix}_{{source_name_part}}.cc",
     ]
 
     args = [
@@ -76,12 +84,49 @@
       "--component-info",
       rebase_path(invoker.component_info),
       "--extended-attributes",
-      rebase_path(invoker.extended_attributes),
+      rebase_path(_extended_attributes_file),
       "{{source}}",
     ]
   }
 }
 
+template("generate_type_conversion") {
+  action(target_name) {
+    script = "//starboard/build/run_bash.py"
+    py_script = engine_conversion_header_generator_script
+
+    deps = invoker.deps
+
+    public_deps = engine_dependencies
+
+    inputs = [
+      py_script,
+      _extended_attributes_file,
+      invoker.global_names_idl_file,
+      "//cobalt/bindings/shared/idl_conditional_macros.h",
+    ]
+    inputs += _bindings_extra_inputs
+    inputs += invoker.inputs
+
+    # Generated header file that contains forward declarations for converting
+    # to/from JS value and generated types.
+    outputs = [ "${invoker.output_dir}/${generated_bindings_prefix}_gen_type_conversion.h" ]
+
+    args = [
+      "python2",
+      rebase_path(py_script, root_build_dir),
+      "--cache-directory",
+      rebase_path(invoker.cache_directory, root_build_dir),
+      "--output-dir",
+      rebase_path(invoker.output_dir, root_build_dir),
+      "--interfaces-info",
+      rebase_path(invoker.interfaces_info, root_build_dir),
+      "--component-info",
+      rebase_path(invoker.component_info, root_build_dir),
+    ]
+  }
+}
+
 template("compute_global_objects") {
   action(target_name) {
     script = "//starboard/build/run_bash.py"
@@ -157,7 +202,7 @@
     inputs = [
       py_script,
       "$bindings_scripts_dir/utilities.py",
-      invoker.extended_attributes,
+      _extended_attributes_file,
       invoker.generated_idl_files,
     ]
     inputs += invoker.idl_files
@@ -189,7 +234,7 @@
       "--component-info-file",
       rebase_path(invoker.component_info_file, root_build_dir),
       "--extended-attributes",
-      rebase_path(invoker.extended_attributes, root_build_dir),
+      rebase_path(_extended_attributes_file, root_build_dir),
       "--dependency-idl-files",
       dependency_idl_files_list,
       "--",
@@ -199,3 +244,101 @@
     ]
   }
 }
+
+template("generate_interfaces_info_overall") {
+  action(target_name) {
+    script = "//starboard/build/run_bash.py"
+    py_script = "$bindings_scripts_dir/compute_interfaces_info_overall.py"
+
+    deps = invoker.deps
+
+    inputs = [
+      py_script,
+      invoker.individual_interfaces_file,
+    ]
+
+    outputs = [ invoker.combined_interfaces_file ]
+
+    args = [
+      "python2",
+      rebase_path(py_script, root_build_dir),
+      "--",
+      rebase_path(invoker.individual_interfaces_file, root_build_dir),
+      rebase_path(invoker.combined_interfaces_file, root_build_dir),
+    ]
+  }
+}
+
+template("cache_lex_tables") {
+  action(target_name) {
+    script = "//starboard/build/run_bash.py"
+    py_script = "$bindings_scripts_dir/blink_idl_parser.py"
+
+    inputs = [ py_script ]
+    inputs += idl_lexer_parser_files
+
+    outputs = [
+      "${invoker.output_dir}/lextab.py",
+      "${invoker.output_dir}/parsetab.pickle",
+    ]
+
+    args = [
+      "python2",
+      rebase_path(py_script, root_build_dir),
+      rebase_path(invoker.output_dir, root_build_dir),
+    ]
+  }
+}
+
+template("cache_templates") {
+  action(target_name) {
+    script = "//starboard/build/run_bash.py"
+    py_script = "//cobalt/bindings/code_generator_cobalt.py"
+
+    inputs = [
+      py_script,
+      "//cobalt/bindings/path_generator.py",
+      "//third_party/jinja2/__init__.py",
+      "//third_party/markupsafe/__init__.py",  # jinja2 dep
+    ]
+
+    # Cobalt's Jinja templates.
+    inputs += engine_template_files
+
+    # Templates that are shared by the code generation for multiple engines.
+    inputs += [
+      "//cobalt/bindings/templates/dictionary.h.template",
+      "//cobalt/bindings/templates/interface-base.cc.template",
+      "//cobalt/bindings/templates/interface-base.h.template",
+      "//cobalt/bindings/templates/callback-interface-base.cc.template",
+      "//cobalt/bindings/templates/callback-interface-base.h.template",
+    ]
+
+    # The filenames are hashes of the template file path.
+    outputs = [
+      "${invoker.output_dir}/__jinja2_0458bc0cdbf0dcc5ede1938c55c599dc57dcae71.cache",
+      "${invoker.output_dir}/__jinja2_303dad9caff7b8c046562177d5460bb9f7b159df.cache",
+      "${invoker.output_dir}/__jinja2_4038c00836c6764d385cf4c46462430f9aaf17be.cache",
+      "${invoker.output_dir}/__jinja2_435670c71610a56f1cef61f2d38133c78acac78f.cache",
+      "${invoker.output_dir}/__jinja2_4a2a64213caba2133a0b71aada741965e14fc1bb.cache",
+      "${invoker.output_dir}/__jinja2_4c7f97150281049711d4ce10a328cc061253b390.cache",
+      "${invoker.output_dir}/__jinja2_5d45ad6b9cacef6d9d179dc9099ec37cf6ee1724.cache",
+      "${invoker.output_dir}/__jinja2_65e7aef6627bae7400833440f6e4ae091c710908.cache",
+      "${invoker.output_dir}/__jinja2_74db451681d577fcb9c39eae42a296cbbcf752c7.cache",
+      "${invoker.output_dir}/__jinja2_8e238dc5f6ff498c4406817387ddb7ece64b45bd.cache",
+      "${invoker.output_dir}/__jinja2_b0197cf1f0bbd6fc7944725aec13712883090f68.cache",
+      "${invoker.output_dir}/__jinja2_d049954a9d5a3dc383d37b7673da54b661ec34b0.cache",
+      "${invoker.output_dir}/__jinja2_d09b22d687fad0727f239c3feb23cadd73ac6bc0.cache",
+      "${invoker.output_dir}/__jinja2_df2da080300d5d4b19c5c7b8b87f6a5521d00c89.cache",
+    ]
+
+    args = [
+      "python2",
+      rebase_path(py_script, root_build_dir),
+      rebase_path("${invoker.output_dir}", root_build_dir),
+      rebase_path("$engine_templates_dir", root_build_dir),
+      rebase_path("${invoker.output_dir}/cached_jinja_templates.stamp",
+                  root_build_dir),
+    ]
+  }
+}
diff --git a/cobalt/bindings/bindings_variables.gni b/cobalt/bindings/bindings_variables.gni
deleted file mode 100644
index 04e9b59..0000000
--- a/cobalt/bindings/bindings_variables.gni
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright 2021 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("//cobalt/browser/browser_bindings_variables.gni")
-import("//cobalt/script/v8c/v8c_variables.gni")
-
-# Migrated from cobalt/bindings/bindings.gypi
-#############################################
-# Blink interface info is calculated in two stages. First at a per-component level
-# (in Blink this is core or modules) and then these are combined. While Cobalt
-# currently does not and may not need to distinguish between components, we adhere to
-# Blink's process for simplicity.
-component_info_pickle = "$bindings_scripts_output_dir/component_info.pickle"
-interfaces_info_individual_pickle =
-    "$bindings_scripts_output_dir/interfaces_info_individual.pickle"
-interfaces_info_combined_pickle =
-    "$bindings_scripts_output_dir/interfaces_info_overall.pickle"
-
-# The allowlist of what extended attributes we support. If an attribute
-# not in this list is encountered, it will cause an error in the
-# pipeline.
-extended_attributes_file = "//cobalt/bindings/IDLExtendedAttributes.txt"
-
-# Base directory into which generated bindings source files will be
-# generated. Directory structure will mirror the directory structure
-# that the .idl files came from.
-generated_source_output_dir = "$bindings_output_dir/source"
-
-# Generated header file that contains forward declarations for converting
-# to/from JS value and generated types.
-generated_type_conversion_header_file = "$generated_source_output_dir/${generated_bindings_prefix}_gen_type_conversion.h"
-
-# Directory containing generated IDL files.
-generated_idls_output_dir = "$bindings_output_dir/idl"
-
-# Templates that are shared by the code generation for multiple engines.
-shared_template_files = [
-  "//cobalt/bindings/templates/dictionary.h.template",
-  "//cobalt/bindings/templates/interface-base.cc.template",
-  "//cobalt/bindings/templates/interface-base.h.template",
-  "//cobalt/bindings/templates/callback-interface-base.cc.template",
-  "//cobalt/bindings/templates/callback-interface-base.h.template",
-]
-
-# Cobalt's Jinja templates.
-code_generator_template_files = engine_template_files + shared_template_files
diff --git a/cobalt/bindings/blink_variables.gni b/cobalt/bindings/blink_variables.gni
index b90a7d5..d19986d 100644
--- a/cobalt/bindings/blink_variables.gni
+++ b/cobalt/bindings/blink_variables.gni
@@ -12,8 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//cobalt/browser/browser_bindings_variables.gni")
-
 # Migrated from third_party/blink/Source/bindings/scripts/scripts.gypi
 ######################################################################
 bindings_scripts_dir = "//third_party/blink/Source/bindings/scripts"
@@ -46,8 +44,6 @@
   "//third_party/blink/Source/bindings/scripts/v8_utilities.py",
 ]
 
-idl_cache_files = [ "$bindings_scripts_output_dir/lextab.py" ]
-
 idl_lexer_parser_files =
     get_path_info([
                     # PLY (Python Lex-Yacc)
diff --git a/cobalt/bindings/testing/BUILD.gn b/cobalt/bindings/testing/BUILD.gn
new file mode 100644
index 0000000..ca902da
--- /dev/null
+++ b/cobalt/bindings/testing/BUILD.gn
@@ -0,0 +1,389 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//cobalt/bindings/bindings_templates.gni")
+import("//cobalt/script/v8c/v8c_variables.gni")
+
+##########################################################
+# Configuration variables for bindings generation scripts.
+##########################################################
+
+_bindings_output_dir = "$root_gen_dir/bindings/testing"
+_bindings_scripts_output_dir = "$_bindings_output_dir/scripts"
+
+# Blink interface info is calculated in two stages. First at a per-component level
+# (in Blink this is core or modules) and then these are combined. While Cobalt
+# currently does not and may not need to distinguish between components, we adhere to
+# Blink's process for simplicity.
+_component_info_pickle = "$_bindings_scripts_output_dir/component_info.pickle"
+_interfaces_info_individual_pickle =
+    "$_bindings_scripts_output_dir/interfaces_info_individual.pickle"
+_interfaces_info_combined_pickle =
+    "$_bindings_scripts_output_dir/interfaces_info_overall.pickle"
+_global_objects_pickle = "$_bindings_scripts_output_dir/GlobalObjects.pickle"
+
+# Base directory into which generated bindings source files will be
+# generated. Directory structure will mirror the directory structure
+# that the .idl files came from.
+_generated_source_output_dir = "$_bindings_output_dir/source"
+
+# Directory containing generated IDL files.
+_generated_idls_output_dir = "$_bindings_output_dir/idl"
+
+###################################################
+# IDL files to be compiled to bindings source code.
+###################################################
+
+# Testing IDL files.
+_source_idl_files = [
+  "anonymous_indexed_getter_interface.idl",
+  "anonymous_named_getter_interface.idl",
+  "anonymous_named_indexed_getter_interface.idl",
+  "arbitrary_interface.idl",
+  "base_interface.idl",
+  "boolean_type_test_interface.idl",
+  "callback_function_interface.idl",
+  "callback_interface_interface.idl",
+  "conditional_interface.idl",
+  "constants_interface.idl",
+  "constructor_interface.idl",
+  "constructor_with_arguments_interface.idl",
+  "convert_simple_object_interface.idl",
+  "derived_getter_setter_interface.idl",
+  "derived_interface.idl",
+  "dictionary_interface.idl",
+  "disabled_interface.idl",
+  "dom_string_test_interface.idl",
+  "enumeration_interface.idl",
+  "exception_object_interface.idl",
+  "exceptions_interface.idl",
+  "extended_idl_attributes_interface.idl",
+  "garbage_collection_test_interface.idl",
+  "global_interface_parent.idl",
+  "indexed_getter_interface.idl",
+  "interface_with_any.idl",
+  "interface_with_any_dictionary.idl",
+  "interface_with_date.idl",
+  "interface_with_unsupported_properties.idl",
+  "named_constructor_interface.idl",
+  "named_getter_interface.idl",
+  "named_indexed_getter_interface.idl",
+  "nested_put_forwards_interface.idl",
+  "no_constructor_interface.idl",
+  "no_interface_object_interface.idl",
+  "nullable_types_test_interface.idl",
+  "numeric_types_test_interface.idl",
+  "object_type_bindings_interface.idl",
+  "operations_test_interface.idl",
+  "promise_interface.idl",
+  "put_forwards_interface.idl",
+  "sequence_user.idl",
+  "single_operation_interface.idl",
+  "static_properties_interface.idl",
+  "stringifier_anonymous_operation_interface.idl",
+  "stringifier_attribute_interface.idl",
+  "stringifier_operation_interface.idl",
+  "target_interface.idl",
+  "union_types_interface.idl",
+  "window.idl",
+]
+
+_generated_header_idl_files = [
+  "derived_dictionary.idl",
+  "dictionary_with_dictionary_member.idl",
+  "test_dictionary.idl",
+  "test_enum.idl",
+]
+
+# Partial interfaces and the right-side of "implements"
+# Code will not get generated for these interfaces; they are used to add
+# functionality to other interfaces.
+_dependency_idl_files = [
+  "implemented_interface.idl",
+  "partial_interface.idl",
+  "interface_with_unsupported_properties_partial.idl",
+]
+
+_unsupported_interface_idl_files = [ "unsupported_interface.idl" ]
+
+#########################
+# Bindings tests targets.
+#########################
+
+static_library("bindings_test_implementation") {
+  testonly = true
+
+  sources = [
+    "constants_interface.cc",
+    "constructor_interface.cc",
+    "exceptions_interface.cc",
+    "garbage_collection_test_interface.cc",
+    "named_constructor_interface.cc",
+    "operations_test_interface.cc",
+    "put_forwards_interface.cc",
+    "static_properties_interface.cc",
+  ]
+
+  deps = [
+    ":generated_types_test_support",
+    "//cobalt/base",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+target(gtest_target_type, "bindings_test") {
+  testonly = true
+
+  sources = [
+    "any_bindings_test.cc",
+    "any_dictionary_bindings_test.cc",
+    "array_buffers_test.cc",
+    "boolean_type_bindings_test.cc",
+    "callback_function_test.cc",
+    "callback_interface_test.cc",
+    "conditional_attribute_test.cc",
+    "constants_bindings_test.cc",
+    "constructor_bindings_test.cc",
+    "convert_simple_object_test.cc",
+    "date_bindings_test.cc",
+    "dependent_interface_test.cc",
+    "dictionary_test.cc",
+    "dom_string_bindings_test.cc",
+    "enumeration_bindings_test.cc",
+    "evaluate_script_test.cc",
+    "exceptions_bindings_test.cc",
+    "extended_attributes_test.cc",
+    "garbage_collection_test.cc",
+    "get_own_property_descriptor.cc",
+    "getter_setter_test.cc",
+    "global_interface_bindings_test.cc",
+    "interface_object_test.cc",
+    "nullable_types_bindings_test.cc",
+    "numeric_type_bindings_test.cc",
+    "object_type_bindings_test.cc",
+    "operations_bindings_test.cc",
+    "optional_arguments_bindings_test.cc",
+    "promise_test.cc",
+    "put_forwards_test.cc",
+    "sequence_bindings_test.cc",
+    "stack_trace_test.cc",
+    "static_properties_bindings_test.cc",
+    "stringifier_bindings_test.cc",
+    "union_type_bindings_test.cc",
+    "unsupported_test.cc",
+    "variadic_arguments_bindings_test.cc",
+  ]
+
+  configs += [ ":enable_conditional_interface" ]
+
+  deps = [
+    ":bindings_test_implementation",
+    ":bindings_test_support",
+    "//cobalt/base",
+    "//cobalt/script",
+    "//cobalt/script/v8c:engine",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+###################
+# Bindings sandbox.
+###################
+
+target(final_executable_type, "bindings_sandbox") {
+  testonly = true
+  sources = [ "bindings_sandbox_main.cc" ]
+  deps = [
+    ":bindings_test_implementation",
+    ":bindings_test_support",
+    "//cobalt/base",
+    "//cobalt/script:engine",
+    "//cobalt/script:standalone_javascript_runner",
+  ]
+}
+
+##############################
+# Bindings generation targets.
+##############################
+
+group("bindings_test_support") {
+  testonly = true
+  public_deps = [
+    ":generated_bindings_sources_test_support",
+    ":generated_types_sources_test_support",
+  ]
+}
+
+config("enable_conditional_interface") {
+  defines = [
+    "ENABLE_CONDITIONAL_INTERFACE",
+    "ENABLE_CONDITIONAL_PROPERTY",
+  ]
+}
+
+config("generated_test_support_sources_includes") {
+  include_dirs = [ _generated_source_output_dir ]
+}
+
+idl_compile("generated_bindings_test_support") {
+  testonly = true
+  sources = _source_idl_files
+
+  cache_directory = _bindings_scripts_output_dir
+  component_info = _component_info_pickle
+  interfaces_info = _interfaces_info_combined_pickle
+  output_directory = _generated_source_output_dir
+  header_prefix = "${generated_bindings_prefix}"
+
+  # TODO(b/211055528): Missing deps from generated sources.
+  deps = [
+    ":cached_jinja_templates_test_support",
+    ":cached_lex_yacc_tables_test_support",
+    ":generated_type_conversion_test_support",
+    ":generated_types_test_support",
+    ":global_constructors_idls_test_support",
+    ":interfaces_info_individual_test_support",
+    ":interfaces_info_overall_test_support",
+    "//cobalt/script:engine",
+  ]
+  public_deps = engine_dependencies + [ "//testing/gmock" ]
+
+  public_configs = [ ":generated_test_support_sources_includes" ]
+}
+
+source_set("generated_bindings_sources_test_support") {
+  testonly = true
+  sources = get_target_outputs(":generated_bindings_test_support")
+  configs += [ ":enable_conditional_interface" ]
+  public_deps = [ ":generated_bindings_test_support" ]
+  deps = [
+    # Ensure that all the files have been generated before trying to compile.
+    ":generated_types_test_support",
+  ]
+}
+
+idl_compile("generated_types_test_support") {
+  sources = _generated_header_idl_files
+
+  cache_directory = _bindings_scripts_output_dir
+  component_info = _component_info_pickle
+  interfaces_info = _interfaces_info_combined_pickle
+  output_directory = _generated_source_output_dir
+
+  # TODO(b/211055528): Missing deps from generated sources.
+  deps = [
+    ":cached_jinja_templates_test_support",
+    ":cached_lex_yacc_tables_test_support",
+    ":global_constructors_idls_test_support",
+    ":interfaces_info_individual_test_support",
+    ":interfaces_info_overall_test_support",
+  ]
+  public_deps = engine_dependencies
+
+  public_configs = [ ":generated_test_support_sources_includes" ]
+}
+
+source_set("generated_types_sources_test_support") {
+  testonly = true
+  sources = get_target_outputs(":generated_types_test_support")
+  public_deps = [ ":generated_types_test_support" ]
+  deps = [
+    # Ensure that all the files have been generated before trying to compile.
+    ":generated_bindings_test_support",
+  ]
+}
+
+generate_type_conversion("generated_type_conversion_test_support") {
+  # TODO(b/211055528): Missing deps from generated sources.
+  deps = [
+    ":cached_jinja_templates_test_support",
+    ":cached_lex_yacc_tables_test_support",
+    ":global_constructors_idls_test_support",
+    ":interfaces_info_overall_test_support",
+  ]
+
+  # Generated IDL file that will define all the constructors that should be
+  # on the Window object.
+  global_names_idl_file =
+      "$_generated_idls_output_dir/testing/window_constructors.idl"
+
+  inputs = [ _interfaces_info_combined_pickle ]
+  inputs += _source_idl_files
+  inputs += _generated_header_idl_files
+
+  cache_directory = _bindings_scripts_output_dir
+  output_dir = _generated_source_output_dir
+  interfaces_info = _interfaces_info_combined_pickle
+  component_info = _component_info_pickle
+}
+
+compute_global_objects("global_objects_test_support") {
+  idl_files = _source_idl_files + _generated_header_idl_files
+
+  global_objects_file = _global_objects_pickle
+}
+
+compute_global_constructors_idls("global_constructors_idls_test_support") {
+  idl_files = _source_idl_files + _unsupported_interface_idl_files
+
+  global_objects_file = _global_objects_pickle
+
+  # Generated IDL file that will define all the constructors that should be
+  # on the Window object.
+  global_names_idl_file =
+      "$_generated_idls_output_dir/testing/window_constructors.idl"
+
+  # Dummy header file which is generated because the idl compiler assumes
+  # there is a header for each IDL.
+  global_constructors_generated_header_file =
+      "$_generated_idls_output_dir/testing/window_constructors.h"
+
+  deps = [ ":global_objects_test_support" ]
+}
+
+compute_interfaces_info_individual("interfaces_info_individual_test_support") {
+  idl_files = _source_idl_files + _generated_header_idl_files +
+              _dependency_idl_files + _unsupported_interface_idl_files
+
+  # Generated IDL file that will define all the constructors that should be
+  # on the Window object.
+  generated_idl_files =
+      "$_generated_idls_output_dir/testing/window_constructors.idl"
+  component_info_file = _component_info_pickle
+  interfaces_info_file = _interfaces_info_individual_pickle
+  cache_directory = _bindings_scripts_output_dir
+  dependency_idl_files = _dependency_idl_files
+
+  deps = [
+    ":cached_lex_yacc_tables_test_support",
+    ":global_constructors_idls_test_support",
+  ]
+}
+
+generate_interfaces_info_overall("interfaces_info_overall_test_support") {
+  individual_interfaces_file = _interfaces_info_individual_pickle
+  combined_interfaces_file = _interfaces_info_combined_pickle
+  deps = [ ":interfaces_info_individual_test_support" ]
+}
+
+cache_lex_tables("cached_lex_yacc_tables_test_support") {
+  output_dir = _bindings_scripts_output_dir
+}
+
+cache_templates("cached_jinja_templates_test_support") {
+  output_dir = _bindings_scripts_output_dir
+}
diff --git a/cobalt/black_box_tests/README.md b/cobalt/black_box_tests/README.md
index 6a696ea..2fd70f4 100644
--- a/cobalt/black_box_tests/README.md
+++ b/cobalt/black_box_tests/README.md
@@ -60,22 +60,22 @@
 work with runner.JSTestsSucceeded() in the python test scripts. Together,
 they allow for test logic to exist in either the python test scripts or
 JavaScript test data.
-e.g. Call OnEndTest() to signal test completion in the JavaScripts,
+e.g. Call OnEndTest() to signal test completion on the JavaScript side,
 JSTestsSucceeded() will react to the signal and return the test status of
-JavaScript test logic; another example is that when python script wants to wait
-for some setup steps on JavaScript, call runner.WaitForJSTestsSetup(). Calling
-setupFinished() in JavaScript whenever ready will unblock the wait.
+JavaScript test logic; another example is that when the python script wants to
+wait for some setup steps on JavaScript, call runner.WaitForJSTestsSetup().
+Calling setupFinished() in JavaScript whenever ready will unblock the wait.
 
 
 ## Test Data
 
-A default local test server will be launcher before any unit test starts to
+A default local test server will be launched before any unit test starts to
 serve the test data in black_box_tests/testdata/. The server's port will be
 passed to the app launcher to fetch test data from.
-Test data can include target web page for Cobalt to open and any additional
-resource(font file, JavaScripts...).
+Test data can include the target web page for Cobalt to open and any additional
+resources (font file, JavaScripts...).
 Tests are free to start their own HTTP servers if the default test server is
-inadequate(e.g. The test is testing that Cobalt handles receipt of a specific
+inadequate (e.g. The test is testing that Cobalt handles receipt of a specific
 HTTP server generated error code properly).
 
 
diff --git a/cobalt/black_box_tests/black_box_tests.py b/cobalt/black_box_tests/black_box_tests.py
index cae01ff..bfb73d8 100644
--- a/cobalt/black_box_tests/black_box_tests.py
+++ b/cobalt/black_box_tests/black_box_tests.py
@@ -65,6 +65,7 @@
     'compression_test',
     'disable_eval_with_csp',
     'persistent_cookie',
+    'soft_mic_platform_service_test',
     'web_debugger',
     'web_platform_tests',
 ]
diff --git a/cobalt/black_box_tests/testdata/soft_mic_platform_service_test.html b/cobalt/black_box_tests/testdata/soft_mic_platform_service_test.html
new file mode 100644
index 0000000..2a451b9
--- /dev/null
+++ b/cobalt/black_box_tests/testdata/soft_mic_platform_service_test.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+
+<html>
+  <head></head>
+  <body>
+    <script src='black_box_js_test_utils.js'></script>
+    <script src='soft_mic_platform_service_test.js'></script>
+  </body>
+</html>
diff --git a/cobalt/black_box_tests/testdata/soft_mic_platform_service_test.js b/cobalt/black_box_tests/testdata/soft_mic_platform_service_test.js
new file mode 100644
index 0000000..39f0947
--- /dev/null
+++ b/cobalt/black_box_tests/testdata/soft_mic_platform_service_test.js
@@ -0,0 +1,296 @@
+// Copyright 2021 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 SOFT_MIC_SERVICE_NAME = "com.google.youtube.tv.SoftMic";
+
+function failTest() {
+  notReached();
+  onEndTest();
+}
+
+/**
+* @param {ArrayBuffer} data to be converted to a String.
+*/
+function ab2str(data) {
+  try {
+    return String.fromCharCode.apply(null, new Uint8Array(data));
+  } catch(error) {
+    console.error(`ab2str() error: ${error}, decoding data: ${data}`);
+  }
+}
+
+/**
+* @param {String} data to be converted to an ArrayBuffer.
+*/
+function str2ab(data) {
+  try {
+    return Uint8Array.from(data.split(''), (s) => {return s.charCodeAt(0)}).buffer;
+  } catch(error) {
+    console.error(`str2ab() error: ${error}, decoding data: ${data}`);
+  }
+}
+
+function bothUndefined(hard_mic, soft_mic) {
+  assertFalse(hard_mic);
+  assertTrue(soft_mic);
+}
+
+function hardMicUndefinedSoftMicTrue(hard_mic, soft_mic) {
+  assertFalse(hard_mic);
+  assertTrue(soft_mic);
+}
+
+function hardMicUndefinedSoftMicFalse(hard_mic, soft_mic) {
+  assertFalse(hard_mic);
+  assertFalse(soft_mic);
+}
+
+function hardMicTrueSoftMicUndefined(hard_mic, soft_mic) {
+  assertTrue(hard_mic);
+  assertTrue(soft_mic);
+}
+
+function hardMicTrueSoftMicTrue(hard_mic, soft_mic) {
+  assertTrue(hard_mic);
+  assertTrue(soft_mic);
+}
+
+function hardMicTrueSoftMicFalse(hard_mic, soft_mic) {
+  assertTrue(hard_mic);
+  assertFalse(soft_mic);
+}
+
+function hardMicFalseSoftMicUndefined(hard_mic, soft_mic) {
+  assertFalse(hard_mic);
+  assertTrue(soft_mic);
+}
+
+function hardMicFalseSoftMicTrue(hard_mic, soft_mic) {
+  assertFalse(hard_mic);
+  assertTrue(soft_mic);
+}
+
+function hardMicFalseSoftMicFalse(hard_mic, soft_mic) {
+  assertFalse(hard_mic);
+  assertFalse(soft_mic);
+}
+
+function micGestureNull(micGesture) {
+  assertFalse(micGesture);
+  assertTrue(micGesture == null);
+}
+
+function micGestureHold(micGesture) {
+  assertTrue(micGesture);
+  assertTrue(micGesture == "HOLD");
+}
+
+function micGestureTap(micGesture) {
+  assertTrue(micGesture);
+  assertTrue(micGesture == "TAP");
+}
+
+/**
+* @param {function} assertCallback
+* @param {boolean} testMicGestureOnly
+*/
+function testService(assertCallback, testMicGestureOnly = false) {
+  var service_send_done = false;
+  var service_response_received = false;
+
+  /**
+  * @param {ArrayBuffer} data
+  */
+  function testResponseIsTrue(data) {
+    try {
+      assertTrue(new Int8Array(data)[0]);
+    } catch (error) {
+      console.log(`Error in testResponseIsTrue: ${error}`);
+      notReached();
+    }
+  }
+
+  /**
+  * @param {ArrayBuffer} data
+  */
+  function receiveCallback(service, data) {
+    var str_response = ab2str(data);
+
+    try {
+      var response = JSON.parse(str_response);
+      var has_hard_mic = response["hasHardMicSupport"];
+      var has_soft_mic = response["hasSoftMicSupport"];
+      var mic_gesture = response["micGesture"];
+
+      console.log(`receiveCallback() response:
+                  has_hard_mic: ${has_hard_mic},
+                  has_soft_mic: ${has_soft_mic},
+                  micGesture: ${mic_gesture}`);
+
+      if (testMicGestureOnly)
+        assertCallback(mic_gesture);
+      else
+        assertCallback(has_hard_mic, has_soft_mic);
+    } catch (error) {
+      console.log(`receiveCallback() error: ${error}`);
+      failTest();
+    }
+
+    service_response_received = true;
+
+    if (service_send_done) {
+      soft_mic_service.close();
+      onEndTest();
+    }
+  }
+
+  if (!H5vccPlatformService) {
+    console.log("H5vccPlatformService is not implemented");
+    onEndTest();
+    return;
+  }
+
+  if (!H5vccPlatformService.has(SOFT_MIC_SERVICE_NAME)) {
+    console.log(`H5vccPlatformService.Has(${SOFT_MIC_SERVICE_NAME}) returned false.`);
+    onEndTest();
+    return;
+  }
+
+  // Open the service and pass the receive_callback.
+  var soft_mic_service = H5vccPlatformService.open(SOFT_MIC_SERVICE_NAME,
+                              receiveCallback);
+
+  if (soft_mic_service === null) {
+    console.log("H5vccPlatformService.open() returned null");
+    failTest();
+    return;
+  }
+
+  // Send "getMicSupport" message and test the sync response here and the async platform
+  // response in the receiveCallback()
+  testResponseIsTrue(soft_mic_service.send(str2ab(JSON.stringify("getMicSupport"))));
+
+  // Test the sync response for "notifySearchActive".
+  testResponseIsTrue(soft_mic_service.send(str2ab(JSON.stringify("notifySearchActive"))));
+
+  // Test the sync response for "notifySearchInactive".
+  testResponseIsTrue(soft_mic_service.send(str2ab(JSON.stringify("notifySearchInactive"))));
+
+  service_send_done = true;
+
+  if (service_response_received) {
+    soft_mic_service.close();
+    onEndTest();
+  }
+}
+
+function testIncorrectRequests() {
+  /**
+  * @param {ArrayBuffer} data
+  */
+  function testResponseIsFalse(data) {
+    try {
+      assertFalse(new Int8Array(data)[0]);
+    } catch (error) {
+      console.log(`Error in testResponseIsFalse: ${error}`);
+      notReached();
+    }
+  }
+
+  if (!H5vccPlatformService) {
+    console.log("H5vccPlatformService is not implemented");
+    onEndTest();
+    return;
+  }
+
+  if (!H5vccPlatformService.has(SOFT_MIC_SERVICE_NAME)) {
+    console.log(`H5vccPlatformService.Has(${SOFT_MIC_SERVICE_NAME}) returned false.`);
+    onEndTest();
+    return;
+  }
+
+  // Open the service and pass the receive_callback.
+  var soft_mic_service = H5vccPlatformService.open(SOFT_MIC_SERVICE_NAME, (service, data) => { });
+
+  if (soft_mic_service === null) {
+    console.log("H5vccPlatformService.open() returned null");
+    failTest();
+    return;
+  }
+
+  // Send "getMicSupport" without JSON.stringify.
+  testResponseIsFalse(soft_mic_service.send(str2ab("getMicSupport")));
+
+  // Send "notifySearchActive" without JSON.stringify.
+  testResponseIsFalse(soft_mic_service.send(str2ab("notifySearchActive")));
+
+  // Send "notifySearchInactive" without JSON.stringify.
+  testResponseIsFalse(soft_mic_service.send(str2ab("notifySearchInactive")));
+
+  // Send "" empty string.
+  testResponseIsFalse(soft_mic_service.send(str2ab("")));
+
+  // Send "foo" invalid message string.
+  testResponseIsFalse(soft_mic_service.send(str2ab("foo")));
+
+  // Complete the test.
+  soft_mic_service.close();
+  onEndTest();
+}
+
+/**
+* @param {KeyboardEvent} event
+*/
+window.onkeydown = function(event) {
+  if (event.shiftKey) {
+    testMicGesture(event)
+    return;
+  }
+
+  if (event.key == 0) {
+    testIncorrectRequests();
+  } else if (event.key == 1) {
+    testService(bothUndefined);
+  } else if (event.key == 2) {
+    testService(hardMicUndefinedSoftMicTrue);
+  } else if (event.key == 3) {
+    testService(hardMicUndefinedSoftMicFalse);
+  } else if (event.key == 4) {
+    testService(hardMicTrueSoftMicUndefined);
+  } else if (event.key == 5) {
+    testService(hardMicTrueSoftMicTrue);
+  } else if (event.key == 6) {
+    testService(hardMicTrueSoftMicFalse);
+  } else if (event.key == 7) {
+    testService(hardMicFalseSoftMicUndefined);
+  } else if (event.key == 8) {
+    testService(hardMicFalseSoftMicTrue);
+  } else if (event.key == 9) {
+    testService(hardMicFalseSoftMicFalse);
+  }
+}
+
+/**
+* @param {KeyboardEvent} event
+*/
+function testMicGesture(event) {
+  if (event.key == 0) {
+    testService(micGestureNull, true);
+  } else if (event.key == 1) {
+    testService(micGestureHold, true);
+  } else if (event.key == 2) {
+    testService(micGestureTap, true);
+  }
+}
diff --git a/cobalt/black_box_tests/tests/soft_mic_platform_service_test.py b/cobalt/black_box_tests/tests/soft_mic_platform_service_test.py
new file mode 100644
index 0000000..d68322a
--- /dev/null
+++ b/cobalt/black_box_tests/tests/soft_mic_platform_service_test.py
@@ -0,0 +1,131 @@
+# Copyright 2021 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
+"""Test SoftMicPlatformService messages match between web app and platform"""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import _env  # pylint: disable=unused-import
+
+from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
+from cobalt.tools.automated_testing import webdriver_utils
+
+keys = webdriver_utils.import_selenium_module('webdriver.common.keys')
+
+
+class SoftMicPlatformServiceTest(black_box_tests.BlackBoxTestCase):
+
+  def test_soft_mic_platform_service(self):
+    with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server:
+      url = server.GetURL(
+          file_name='testdata/soft_mic_platform_service_test.html')
+
+      # The webpage listens for NUMPAD0 through NUMPAD9 at opening
+      # to test hasHardMicSupport and hasSoftMicSupport switch values.
+      with self.CreateCobaltRunner(url=url) as runner:
+        # Press NUMPAD0 to test testIncorrectRequests
+        runner.SendKeys(keys.Keys.NUMPAD0)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(url=url) as runner:
+        # Press NUMPAD1 to test bothUndefined
+        runner.SendKeys(keys.Keys.NUMPAD1)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url, target_params=['--has_soft_mic_support=true']) as runner:
+        # Press NUMPAD2 to test hardMicUndefinedSoftMicTrue
+        runner.SendKeys(keys.Keys.NUMPAD2)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url, target_params=['--has_soft_mic_support=false']) as runner:
+        # Press NUMPAD3 to test hardMicUndefinedSoftMicFalse
+        runner.SendKeys(keys.Keys.NUMPAD3)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url, target_params=['--has_hard_mic_support=true']) as runner:
+        # Press NUMPAD4 to test hardMicTrueSoftMicUndefined
+        runner.SendKeys(keys.Keys.NUMPAD4)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url,
+          target_params=([
+              '--has_hard_mic_support=true', '--has_soft_mic_support=true'
+          ])) as runner:
+        # Press NUMPAD5 to test hardMicTrueSoftMicTrue
+        runner.SendKeys(keys.Keys.NUMPAD5)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url,
+          target_params=([
+              '--has_hard_mic_support=true', '--has_soft_mic_support=false'
+          ])) as runner:
+        # Press NUMPAD6 to test hardMicTrueSoftMicFalse
+        runner.SendKeys(keys.Keys.NUMPAD6)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url, target_params=['--has_hard_mic_support=false']) as runner:
+        # Press NUMPAD7 to test hardMicFalseSoftMicUndefined
+        runner.SendKeys(keys.Keys.NUMPAD7)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url,
+          target_params=([
+              '--has_hard_mic_support=false', '--has_soft_mic_support=true'
+          ])) as runner:
+        # Press NUMPAD8 to test hardMicFalseSoftMicTrue
+        runner.SendKeys(keys.Keys.NUMPAD8)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url,
+          target_params=([
+              '--has_hard_mic_support=false', '--has_soft_mic_support=false'
+          ])) as runner:
+        # Press NUMPAD9 to test hardMicFalseSoftMicFalse
+        runner.SendKeys(keys.Keys.NUMPAD9)
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      # The webpage listens for NUMPAD0 through NUMPAD9 at opening with SHIFT
+      # to test micGesture tap and hold switch values.
+      with self.CreateCobaltRunner(url=url) as runner:
+        # Press SHIFT, NUMPAD0 to test micGestureNull
+        runner.SendKeys([keys.Keys.SHIFT, keys.Keys.NUMPAD0])
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url, target_params=['--mic_gesture=foo']) as runner:
+        # Press SHIFT, NUMPAD0 to test micGestureNull
+        runner.SendKeys([keys.Keys.SHIFT, keys.Keys.NUMPAD0])
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url, target_params=['--mic_gesture=hold']) as runner:
+        # Press SHIFT, NUMPAD1 to test micGestureHold
+        runner.SendKeys([keys.Keys.SHIFT, keys.Keys.NUMPAD1])
+        self.assertTrue(runner.JSTestsSucceeded())
+
+      with self.CreateCobaltRunner(
+          url=url, target_params=['--mic_gesture=tap']) as runner:
+        # Press SHIFT, NUMPAD2 to test micGestureTap
+        runner.SendKeys([keys.Keys.SHIFT, keys.Keys.NUMPAD2])
+        self.assertTrue(runner.JSTestsSucceeded())
diff --git a/cobalt/browser/BUILD.gn b/cobalt/browser/BUILD.gn
index cd760a5..6c9ae83 100644
--- a/cobalt/browser/BUILD.gn
+++ b/cobalt/browser/BUILD.gn
@@ -13,8 +13,34 @@
 # limitations under the License.
 
 import("//cobalt/bindings/bindings_templates.gni")
-import("//cobalt/browser/browser_bindings_variables.gni")
 import("//cobalt/browser/idl_files.gni")
+import("//cobalt/script/v8c/v8c_variables.gni")
+
+##########################################################
+# Configuration variables for bindings generation scripts.
+##########################################################
+
+_bindings_output_dir = "$root_gen_dir/bindings/browser"
+_bindings_scripts_output_dir = "$_bindings_output_dir/scripts"
+
+# Blink interface info is calculated in two stages. First at a per-component level
+# (in Blink this is core or modules) and then these are combined. While Cobalt
+# currently does not and may not need to distinguish between components, we adhere to
+# Blink's process for simplicity.
+_component_info_pickle = "$_bindings_scripts_output_dir/component_info.pickle"
+_interfaces_info_individual_pickle =
+    "$_bindings_scripts_output_dir/interfaces_info_individual.pickle"
+_interfaces_info_combined_pickle =
+    "$_bindings_scripts_output_dir/interfaces_info_overall.pickle"
+_global_objects_pickle = "$_bindings_scripts_output_dir/GlobalObjects.pickle"
+
+# Base directory into which generated bindings source files will be
+# generated. Directory structure will mirror the directory structure
+# that the .idl files came from.
+_generated_source_output_dir = "$_bindings_output_dir/source"
+
+# Directory containing generated IDL files.
+_generated_idls_output_dir = "$_bindings_output_dir/idl"
 
 target(final_executable_type, "cobalt") {
   sources = [ "main.cc" ]
@@ -24,18 +50,40 @@
     "//cobalt/base",
     "//net",
   ]
+  content_deps = [
+    "//cobalt/dom:licenses",
+    "//cobalt/network:copy_ssl_certificates",
+    "//cobalt/speech:speech_testdata",
+    "//cobalt/webdriver:copy_webdriver_data",
+    "//third_party/icu:icudata",
+  ]
+  if (cobalt_font_package == "empty") {
+    content_deps += [ "//cobalt/content/fonts:copy_font_data" ]
+  } else {
+    content_deps += [
+      "//cobalt/content/fonts:copy_fonts",
+      "//cobalt/content/fonts:fonts_xml",
+    ]
+  }
+  if (!is_gold) {
+    content_deps += [ "//cobalt/debug:copy_backend_web_files" ]
+  }
 }
 
+##############################
+# Bindings generation targets.
+##############################
+
 config("bindings_includes") {
-  include_dirs = [ generated_source_output_dir ]
+  include_dirs = [ _generated_source_output_dir ]
 }
 
 source_set("browser_switches") {
+  has_pedantic_warnings = true
   sources = [
     "switches.cc",
     "switches.h",
   ]
-
   public_deps = [ "//starboard:starboard_headers_only" ]
 }
 
@@ -117,6 +165,7 @@
     "//cobalt/ui_navigation",
     "//cobalt/webdriver",
     "//cobalt/websocket",
+    "//cobalt/worker",
     "//cobalt/xhr",
     "//crypto",
     "//nb",
@@ -142,13 +191,49 @@
   }
 
   if (sb_is_evergreen) {
-    # TODO(b/206642994): Migrate //cobalt/updater
-    # deps += [ "//cobalt/updater" ]
+    deps += [ "//cobalt/updater" ]
   } else {
     deps += cobalt_platform_dependencies
   }
 }
 
+target(gtest_target_type, "browser_test") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [
+    "device_authentication_test.cc",
+    "memory_settings/auto_mem_settings_test.cc",
+    "memory_settings/auto_mem_test.cc",
+    "memory_settings/calculations_test.cc",
+    "memory_settings/memory_settings_test.cc",
+    "memory_settings/pretty_print_test.cc",
+    "memory_settings/table_printer_test.cc",
+    "memory_settings/test_common.h",
+    "memory_tracker/tool/tool_impl_test.cc",
+    "memory_tracker/tool/util_test.cc",
+    "user_agent_string_test.cc",
+  ]
+
+  deps = [
+    ":browser",
+    ":browser_switches",
+    "//cobalt/base",
+    "//cobalt/browser/memory_settings:browser_memory_settings",
+    "//cobalt/browser/memory_tracker:memory_tracker_tool",
+    "//cobalt/dom",
+    "//cobalt/loader",
+    "//cobalt/math",
+    "//cobalt/network",
+    "//cobalt/speech",
+    "//cobalt/storage",
+    "//cobalt/test:run_all_unittests",
+    "//nb",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
 group("bindings") {
   public_configs = [ ":bindings_includes" ]
 
@@ -163,11 +248,11 @@
 idl_compile("generated_bindings") {
   sources = source_idl_files
 
-  cache_directory = bindings_scripts_output_dir
-  component_info = component_info_pickle
-  extended_attributes = extended_attributes_file
-  interfaces_info = interfaces_info_combined_pickle
-  output_directory = generated_source_output_dir
+  cache_directory = _bindings_scripts_output_dir
+  component_info = _component_info_pickle
+  interfaces_info = _interfaces_info_combined_pickle
+  output_directory = _generated_source_output_dir
+  header_prefix = "${generated_bindings_prefix}"
 
   # TODO(b/211055528): Missing deps from generated sources.
   deps = [
@@ -191,17 +276,17 @@
   deps = [
     # Ensure that all the files have been generated before trying to compile.
     ":generated_types",
+    "//cobalt/webdriver",
   ]
 }
 
 idl_compile("generated_types") {
   sources = generated_header_idl_files
 
-  cache_directory = bindings_scripts_output_dir
-  component_info = component_info_pickle
-  extended_attributes = extended_attributes_file
-  interfaces_info = interfaces_info_combined_pickle
-  output_directory = generated_source_output_dir
+  cache_directory = _bindings_scripts_output_dir
+  component_info = _component_info_pickle
+  interfaces_info = _interfaces_info_combined_pickle
+  output_directory = _generated_source_output_dir
 
   # TODO(b/211055528): Missing deps from generated sources.
   deps = [
@@ -225,10 +310,7 @@
   ]
 }
 
-action("generated_type_conversion") {
-  script = "//starboard/build/run_bash.py"
-  py_script = engine_conversion_header_generator_script
-
+generate_type_conversion("generated_type_conversion") {
   # TODO(b/211055528): Missing deps from generated sources.
   deps = [
     ":cached_jinja_templates",
@@ -237,59 +319,41 @@
     ":interfaces_info_overall",
   ]
 
-  public_deps = engine_dependencies
-
   # Generated IDL file that will define all the constructors that should be
   # on the Window object.
   global_names_idl_file =
-      "$generated_idls_output_dir/dom/window_constructors.idl"
-  inputs = [
-    py_script,
-    interfaces_info_combined_pickle,
-    extended_attributes_file,
-    global_names_idl_file,
-    "//cobalt/bindings/shared/idl_conditional_macros.h",
-  ]
-  inputs += bindings_extra_inputs
+      "$_generated_idls_output_dir/dom/window_constructors.idl"
+
+  inputs = [ _interfaces_info_combined_pickle ]
   inputs += source_idl_files
   inputs += generated_header_idl_files
 
-  outputs = [ generated_type_conversion_header_file ]
-
-  args = [
-    "python2",
-    rebase_path(py_script, root_build_dir),
-    "--cache-directory",
-    rebase_path(bindings_scripts_output_dir, root_build_dir),
-    "--output-dir",
-    rebase_path(generated_source_output_dir, root_build_dir),
-    "--interfaces-info",
-    rebase_path(interfaces_info_combined_pickle, root_build_dir),
-    "--component-info",
-    rebase_path(component_info_pickle, root_build_dir),
-  ]
+  cache_directory = _bindings_scripts_output_dir
+  output_dir = _generated_source_output_dir
+  interfaces_info = _interfaces_info_combined_pickle
+  component_info = _component_info_pickle
 }
 
 compute_global_objects("global_objects") {
   idl_files = source_idl_files + generated_header_idl_files
 
-  global_objects_file = global_objects_pickle
+  global_objects_file = _global_objects_pickle
 }
 
 compute_global_constructors_idls("global_constructors_idls") {
   idl_files = source_idl_files
 
-  global_objects_file = global_objects_pickle
+  global_objects_file = _global_objects_pickle
 
   # Generated IDL file that will define all the constructors that should be
   # on the Window object.
   global_names_idl_file =
-      "$generated_idls_output_dir/dom/window_constructors.idl"
+      "$_generated_idls_output_dir/dom/window_constructors.idl"
 
   # Dummy header file which is generated because the idl compiler assumes
   # there is a header for each IDL.
   global_constructors_generated_header_file =
-      "$generated_idls_output_dir/dom/window_constructors.h"
+      "$_generated_idls_output_dir/dom/window_constructors.h"
 
   deps = [ ":global_objects" ]
 }
@@ -300,11 +364,11 @@
 
   # Generated IDL file that will define all the constructors that should be
   # on the Window object.
-  generated_idl_files = "$generated_idls_output_dir/dom/window_constructors.idl"
-  component_info_file = component_info_pickle
-  interfaces_info_file = interfaces_info_individual_pickle
-  cache_directory = bindings_scripts_output_dir
-  extended_attributes = extended_attributes_file
+  generated_idl_files =
+      "$_generated_idls_output_dir/dom/window_constructors.idl"
+  component_info_file = _component_info_pickle
+  interfaces_info_file = _interfaces_info_individual_pickle
+  cache_directory = _bindings_scripts_output_dir
 
   deps = [
     ":cached_lex_yacc_tables",
@@ -312,69 +376,16 @@
   ]
 }
 
-action("interfaces_info_overall") {
-  script = "//starboard/build/run_bash.py"
-  py_script = "$bindings_scripts_dir/compute_interfaces_info_overall.py"
-
+generate_interfaces_info_overall("interfaces_info_overall") {
+  individual_interfaces_file = _interfaces_info_individual_pickle
+  combined_interfaces_file = _interfaces_info_combined_pickle
   deps = [ ":interfaces_info_individual" ]
-
-  inputs = [
-    py_script,
-    interfaces_info_individual_pickle,
-  ]
-
-  outputs = [ interfaces_info_combined_pickle ]
-
-  args = [
-    "python2",
-    rebase_path(py_script, root_build_dir),
-    "--",
-    rebase_path(interfaces_info_individual_pickle, root_build_dir),
-    rebase_path(interfaces_info_combined_pickle, root_build_dir),
-  ]
 }
 
-action("cached_lex_yacc_tables") {
-  script = "//starboard/build/run_bash.py"
-  py_script = "$bindings_scripts_dir/blink_idl_parser.py"
-
-  inputs = [ py_script ]
-  inputs += idl_lexer_parser_files
-
-  outputs = [
-    "$bindings_scripts_output_dir/lextab.py",
-    "$bindings_scripts_output_dir/parsetab.pickle",
-  ]
-
-  args = [
-    "python2",
-    rebase_path(py_script, root_build_dir),
-    rebase_path(bindings_scripts_output_dir, root_build_dir),
-  ]
+cache_lex_tables("cached_lex_yacc_tables") {
+  output_dir = _bindings_scripts_output_dir
 }
 
-action("cached_jinja_templates") {
-  script = "//starboard/build/run_bash.py"
-  py_script = "//cobalt/bindings/code_generator_cobalt.py"
-
-  inputs = [
-    py_script,
-    "//cobalt/bindings/path_generator.py",
-    "//third_party/jinja2/__init__.py",
-    "//third_party/markupsafe/__init__.py",  # jinja2 dep
-  ]
-  inputs += code_generator_template_files
-
-  # TODO: Figure out a way to list the actual outputs instead
-  # Dummy to track dependency
-  outputs = [ "$bindings_scripts_output_dir/cached_jinja_templates.stamp" ]
-
-  args = [
-    "python2",
-    rebase_path(py_script, root_build_dir),
-    rebase_path("$bindings_scripts_output_dir", root_build_dir),
-    rebase_path("$engine_templates_dir", root_build_dir),
-    rebase_path("$bindings_scripts_output_dir/cached_jinja_templates.stamp",
-                root_build_dir),
-  ]
+cache_templates("cached_jinja_templates") {
+  output_dir = _bindings_scripts_output_dir
 }
diff --git a/cobalt/browser/browser.gyp b/cobalt/browser/browser.gyp
index 9c7c7f5..6091c14 100644
--- a/cobalt/browser/browser.gyp
+++ b/cobalt/browser/browser.gyp
@@ -134,6 +134,7 @@
         '<(DEPTH)/cobalt/ui_navigation/ui_navigation.gyp:ui_navigation',
         '<(DEPTH)/cobalt/webdriver/webdriver.gyp:webdriver',
         '<(DEPTH)/cobalt/websocket/websocket.gyp:websocket',
+        '<(DEPTH)/cobalt/worker/worker.gyp:*',
         '<(DEPTH)/cobalt/xhr/xhr.gyp:xhr',
         '<(DEPTH)/net/net.gyp:net',
         '<(DEPTH)/nb/nb.gyp:nb',
diff --git a/cobalt/browser/browser_bindings_gen.gyp b/cobalt/browser/browser_bindings_gen.gyp
index bce6391..fc7c2d7 100644
--- a/cobalt/browser/browser_bindings_gen.gyp
+++ b/cobalt/browser/browser_bindings_gen.gyp
@@ -237,6 +237,8 @@
         '../websocket/close_event.idl',
         '../websocket/web_socket.idl',
 
+        '../worker/service_worker_container.idl',
+
         '../xhr/xml_http_request.idl',
         '../xhr/xml_http_request_event_target.idl',
         '../xhr/xml_http_request_upload.idl',
@@ -376,6 +378,7 @@
         '../dom/window_timers.idl',
         '../media_capture/navigator.idl',
         '../media_session/navigator_media_session.idl',
+        '../worker/navigator.idl',
     ],
 
     'conditions': [
diff --git a/cobalt/browser/browser_bindings_variables.gni b/cobalt/browser/browser_bindings_variables.gni
deleted file mode 100644
index b5b7768..0000000
--- a/cobalt/browser/browser_bindings_variables.gni
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2021 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.
-
-# Migrated from cobalt/browser/browser_bindings_gen.gyp
-#######################################################
-bindings_output_dir = "$root_gen_dir/bindings/browser"
-bindings_scripts_output_dir = "$bindings_output_dir/scripts"
-
-global_objects_pickle = "$bindings_scripts_output_dir/GlobalObjects.pickle"
diff --git a/cobalt/browser/browser_module.cc b/cobalt/browser/browser_module.cc
index f749090..faf23cf 100644
--- a/cobalt/browser/browser_module.cc
+++ b/cobalt/browser/browser_module.cc
@@ -1766,22 +1766,31 @@
 
   ResetResources();
 
+  // Suspend media module and update system window and resource provider.
+  if (media_module_) {
+    DCHECK(system_window_);
+    window_size_ = system_window_->GetWindowSize();
+#if SB_API_VERSION >= 13
+    // This needs to be done before destroying the renderer module as it
+    // may use the renderer module to release assets during the update.
+    media_module_->UpdateSystemWindowAndResourceProvider(NULL,
+                                                         GetResourceProvider());
+#endif  // SB_API_VERSION >= 13
+  }
+
   if (renderer_module_) {
     // Destroy the renderer module into so that it releases all its graphical
     // resources.
     DestroyRendererModule();
   }
 
-  if (media_module_) {
-    DCHECK(system_window_);
-    window_size_ = system_window_->GetWindowSize();
 #if SB_API_VERSION >= 13
+  // Reset system window after renderer module destroyed.
+  if (media_module_) {
     input_device_manager_.reset();
     system_window_.reset();
-    media_module_->UpdateSystemWindowAndResourceProvider(NULL,
-                                                         GetResourceProvider());
-#endif  // SB_API_VERSION >= 13
   }
+#endif  // SB_API_VERSION >= 13
 }
 
 void BrowserModule::FreezeInternal(SbTimeMonotonic timestamp) {
diff --git a/cobalt/browser/browser_module.h b/cobalt/browser/browser_module.h
index cec410e..dc79f84 100644
--- a/cobalt/browser/browser_module.h
+++ b/cobalt/browser/browser_module.h
@@ -73,11 +73,11 @@
 #if defined(ENABLE_DEBUGGER)
 #include "cobalt/browser/debug_console.h"
 #include "cobalt/browser/lifecycle_console_commands.h"
-#include "cobalt/debug/backend/debug_dispatcher.h"
+#include "cobalt/debug/backend/debug_dispatcher.h"  // nogncheck
 #include "cobalt/debug/backend/debugger_state.h"
 #include "cobalt/debug/console/command_manager.h"
-#include "cobalt/debug/debug_client.h"
-#endif  // ENABLE_DEBUGGER
+#include "cobalt/debug/debug_client.h"  // nogncheck
+#endif                                  // ENABLE_DEBUGGER
 
 #if SB_IS(EVERGREEN)
 #include "cobalt/updater/updater_module.h"
@@ -234,6 +234,7 @@
                                              SbTimeMonotonic timestamp);
   // Pass the deeplink timestamp from Starboard.
   void SetDeepLinkTimestamp(SbTimeMonotonic timestamp);
+
  private:
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
   static void CoreDumpHandler(void* browser_module_as_void);
diff --git a/cobalt/browser/idl_files.gni b/cobalt/browser/idl_files.gni
index 5a3203a..d86cff1 100644
--- a/cobalt/browser/idl_files.gni
+++ b/cobalt/browser/idl_files.gni
@@ -228,6 +228,8 @@
   "//cobalt/websocket/close_event.idl",
   "//cobalt/websocket/web_socket.idl",
 
+  "//cobalt/worker/service_worker_container.idl",
+
   "//cobalt/xhr/xml_http_request.idl",
   "//cobalt/xhr/xml_http_request_event_target.idl",
   "//cobalt/xhr/xml_http_request_upload.idl",
@@ -373,4 +375,5 @@
   "//cobalt/dom/window_timers.idl",
   "//cobalt/media_capture/navigator.idl",
   "//cobalt/media_session/navigator_media_session.idl",
+  "//cobalt/worker/navigator.idl",
 ]
diff --git a/cobalt/browser/memory_settings/BUILD.gn b/cobalt/browser/memory_settings/BUILD.gn
index a99e9c2..83bcad6 100644
--- a/cobalt/browser/memory_settings/BUILD.gn
+++ b/cobalt/browser/memory_settings/BUILD.gn
@@ -13,6 +13,8 @@
 # limitations under the License.
 
 static_library("browser_memory_settings") {
+  has_pedantic_warnings = true
+
   sources = [
     "auto_mem.cc",
     "auto_mem.h",
diff --git a/cobalt/browser/memory_tracker/BUILD.gn b/cobalt/browser/memory_tracker/BUILD.gn
index 4131d62..f2ecb49 100644
--- a/cobalt/browser/memory_tracker/BUILD.gn
+++ b/cobalt/browser/memory_tracker/BUILD.gn
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 static_library("memory_tracker_tool") {
+  has_pedantic_warnings = true
   sources = [
     "tool.cc",
     "tool.h",
diff --git a/cobalt/build/all.gyp b/cobalt/build/all.gyp
index b514602..080b70a 100644
--- a/cobalt/build/all.gyp
+++ b/cobalt/build/all.gyp
@@ -83,6 +83,7 @@
         '<(DEPTH)/cobalt/webdriver/webdriver.gyp:*',
         '<(DEPTH)/cobalt/webdriver/webdriver_test.gyp:*',
         '<(DEPTH)/cobalt/websocket/websocket.gyp:*',
+        '<(DEPTH)/cobalt/worker/worker.gyp:*',
         '<(DEPTH)/cobalt/xhr/xhr.gyp:*',
         '<(DEPTH)/crypto/crypto.gyp:crypto_unittests_deploy',
         '<(DEPTH)/third_party/boringssl/boringssl_tool.gyp:*',
diff --git a/cobalt/build/cobalt_configuration.gypi b/cobalt/build/cobalt_configuration.gypi
index 26085f3..d75f498 100644
--- a/cobalt/build/cobalt_configuration.gypi
+++ b/cobalt/build/cobalt_configuration.gypi
@@ -504,6 +504,8 @@
     # further reduced on systems with extremely low memory.
     'cobalt_media_source_garbage_collection_duration_threshold_in_seconds%': -1,
 
+    # TODO(b/212641065): Evaluate if any flags in defines_debug, defines_devel
+    # defines_qa need to be added to GN.
     'defines_debug': [
       'ALLOCATOR_STATS_TRACKING',
       'COBALT_BOX_DUMP_ENABLED',
diff --git a/cobalt/build/gn.py b/cobalt/build/gn.py
new file mode 100644
index 0000000..93af0bb
--- /dev/null
+++ b/cobalt/build/gn.py
@@ -0,0 +1,105 @@
+# Copyright 2021 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.
+"""Thin wrapper for building Starboard platforms with GN."""
+
+import argparse
+import os
+import shutil
+import subprocess
+import sys
+from pathlib import Path
+
+_REPOSITORY_ROOT = os.path.abspath(
+    os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
+sys.path.append(_REPOSITORY_ROOT)
+from starboard.build.platforms import PLATFORMS  # pylint:disable=wrong-import-position
+
+_BUILD_TYPES = ['debug', 'devel', 'qa', 'gold']
+
+
+def main(out_directory: str, platform: str, build_type: str,
+         overwrite_args: bool, check_dependencies: bool):
+  platform_path = PLATFORMS[platform]
+  dst_args_gn_file = os.path.join(out_directory, 'args.gn')
+  src_args_gn_file = os.path.join(platform_path, 'args.gn')
+
+  Path(out_directory).mkdir(parents=True, exist_ok=True)
+
+  if overwrite_args or not os.path.exists(dst_args_gn_file):
+    shutil.copy(src_args_gn_file, dst_args_gn_file)
+
+    with open(dst_args_gn_file, 'a') as f:
+      f.write(f'build_type = "{build_type}"\n')
+  extra_args = []
+  if check_dependencies:
+    extra_args += ['--check']
+  gn_command = ['gn', 'gen', out_directory] + extra_args
+  print(' '.join(gn_command))
+  subprocess.check_call(gn_command)
+
+
+if __name__ == '__main__':
+  parser = argparse.ArgumentParser()
+
+  builds_directory_group = parser.add_mutually_exclusive_group()
+  builds_directory_group.add_argument(
+      'out_directory',
+      type=str,
+      nargs='?',
+      help='Path to the directory to build in.')
+  builds_directory_group.add_argument(
+      '-b',
+      '--builds_directory',
+      type=str,
+      help='Path to the directory a named build folder, <platform>_<config>/, '
+      'will be created in.')
+
+  parser.add_argument(
+      '-p',
+      '--platform',
+      required=True,
+      choices=list(PLATFORMS),
+      help='The platform to build.')
+  parser.add_argument(
+      '-c',
+      '-C',
+      '--build_type',
+      default='devel',
+      choices=_BUILD_TYPES,
+      help='The build_type (configuration) to build with.')
+  parser.add_argument(
+      '--overwrite_args',
+      default=False,
+      action='store_true',
+      help='Whether or not to overwrite an existing args.gn file if one exists '
+      'in the out directory. In general, if the file exists, you should run '
+      '`gn args <out_directory>` to edit it instead.')
+  parser.add_argument(
+      '--check',
+      default=False,
+      action='store_true',
+      help='Whether or not to generate the ninja files with the gn --check '
+      'option.')
+  args = parser.parse_args()
+
+  if args.out_directory:
+    builds_out_directory = args.out_directory
+  else:
+    builds_directory = os.getenv('COBALT_BUILDS_DIRECTORY',
+                                 args.builds_directory or 'out')
+    builds_out_directory = os.path.join(builds_directory,
+                                        f'{args.platform}_{args.build_type}')
+
+  main(builds_out_directory, args.platform, args.build_type,
+       args.overwrite_args, args.check)
diff --git a/cobalt/content/fonts/BUILD.gn b/cobalt/content/fonts/BUILD.gn
index 8b99258..899c2df 100644
--- a/cobalt/content/fonts/BUILD.gn
+++ b/cobalt/content/fonts/BUILD.gn
@@ -16,12 +16,14 @@
 
 if (cobalt_font_package == "empty") {
   copy("copy_font_data") {
+    install_content = true
     sources = [ "$source_font_config_dir/fonts.xml" ]
     outputs =
         [ "$sb_static_contents_output_data_dir/fonts/{{source_file_part}}" ]
   }
 } else {
   action("fonts_xml") {
+    install_content = true
     script = "scripts/filter_fonts.py"
     font_xml = "$source_font_config_dir/fonts.xml"
     sources = [ font_xml ]
@@ -35,6 +37,7 @@
   }
 
   copy("copy_fonts") {
+    install_content = true
     if (copy_font_files) {
       fonts = exec_script("scripts/filter_fonts.py",
                           [
diff --git a/cobalt/content/fonts/config/android/fonts.xml b/cobalt/content/fonts/config/android/fonts.xml
index d26ab07..1d10ac1 100644
--- a/cobalt/content/fonts/config/android/fonts.xml
+++ b/cobalt/content/fonts/config/android/fonts.xml
@@ -170,7 +170,7 @@
         <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
     </family>
-    <family>
+    <family lang="und-Thai">
         <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
     </family>
diff --git a/cobalt/content/ssl/certs/4a6481c9.0 b/cobalt/content/ssl/certs/4a6481c9.0
deleted file mode 100644
index 6f0f8db..0000000
--- a/cobalt/content/ssl/certs/4a6481c9.0
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
-A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
-Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
-MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
-A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
-v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
-eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
-tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
-C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
-zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
-mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
-V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
-bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
-3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
-J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
-291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
-ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
-AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
-TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
------END CERTIFICATE-----
diff --git a/cobalt/content/ssl/certs/76cb8f92.0 b/cobalt/content/ssl/certs/76cb8f92.0
deleted file mode 100644
index edbeb27..0000000
--- a/cobalt/content/ssl/certs/76cb8f92.0
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG
-A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh
-bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE
-ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS
-b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5
-7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS
-J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y
-HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP
-t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz
-FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY
-XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw
-hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js
-MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA
-A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj
-Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx
-XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o
-omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc
-A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
-WL1WMRJOEcgh4LMRkWXbtKaIOM5V
------END CERTIFICATE-----
diff --git a/cobalt/css_parser/BUILD.gn b/cobalt/css_parser/BUILD.gn
index 08114c0..93c7458 100644
--- a/cobalt/css_parser/BUILD.gn
+++ b/cobalt/css_parser/BUILD.gn
@@ -22,7 +22,7 @@
   script = "//starboard/build/run_bash.py"
 
   # Define the platform specific Bison binary.
-  if (is_win) {
+  if (host_os == "win") {
     bison_executable = "win_bison"
   } else {
     bison_executable = "bison"
diff --git a/cobalt/debug/BUILD.gn b/cobalt/debug/BUILD.gn
index 0f97f00..567027e 100644
--- a/cobalt/debug/BUILD.gn
+++ b/cobalt/debug/BUILD.gn
@@ -93,6 +93,8 @@
 }
 
 copy("copy_backend_web_files") {
+  install_content = true
+
   sources = [
     "backend/content/css_agent.js",
     "backend/content/dom_agent.js",
diff --git a/cobalt/demos/content/BUILD.gn b/cobalt/demos/content/BUILD.gn
new file mode 100644
index 0000000..e87713e
--- /dev/null
+++ b/cobalt/demos/content/BUILD.gn
@@ -0,0 +1,216 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+copy("demos_testdata") {
+  sources = [
+    "animations-demo/index.html",
+    "animations-demo/layer_fern.css",
+    "animations-demo/layer_fern.js",
+    "animations-demo/layer_intro.css",
+    "animations-demo/layer_intro.js",
+    "animations-demo/layer_sun.css",
+    "animations-demo/layer_sun.js",
+    "background-mode-demo/background-mode-demo.html",
+    "background-mode-demo/background-mode-demo.js",
+    "cobalt-oxide/cobalt-oxide.css",
+    "cobalt-oxide/cobalt-oxide.html",
+    "cobalt-oxide/cobalt-oxide.js",
+    "color-transitions-demo/color-transitions-demo.html",
+    "crash-demo/crash-demo.html",
+    "deep-link-demo/deep-link-demo.html",
+    "deviceorientation-demo/deviceorientation-demo.html",
+    "disable-jit/index.html",
+    "dom-gc-demo/dom-gc-demo.html",
+    "dual-playback-demo/bear.mp4",
+    "dual-playback-demo/dual-playback-demo.html",
+    "eme-demo/eme-demo.html",
+    "eme-demo/eme-demo.js",
+    "focus-demo/focus-demo.html",
+    "hybrid-navigation/hybrid-navigation-grid.html",
+    "lottie-player-demo/lottie-player-demo.html",
+    "lottie-player-demo/lottie-player-raw-json-demo.html",
+    "lottie-player-demo/white_material_wave_loading.json",
+    "material-design-spinner-demo/index.html",
+    "media-capture/media-devices-test.html",
+    "media-element-demo/.gitignore",
+    "media-element-demo/README.md",
+    "media-element-demo/legacy/key-systems.html",
+    "media-element-demo/legacy/key-systems.js",
+    "media-element-demo/package-lock.json",
+    "media-element-demo/package.json",
+    "media-element-demo/public/assets/vp9_720p.webm",
+    "media-element-demo/public/index.html",
+    "media-element-demo/public/styles/app.css",
+    "media-element-demo/src/components/component.ts",
+    "media-element-demo/src/components/download_buffer_info.ts",
+    "media-element-demo/src/components/error_logger.ts",
+    "media-element-demo/src/components/player.ts",
+    "media-element-demo/src/components/router.ts",
+    "media-element-demo/src/components/source_buffer_info.ts",
+    "media-element-demo/src/components/video_info.ts",
+    "media-element-demo/src/components/watch.ts",
+    "media-element-demo/src/index.ts",
+    "media-element-demo/src/utils/download_buffer.ts",
+    "media-element-demo/src/utils/downloader.ts",
+    "media-element-demo/src/utils/enums.ts",
+    "media-element-demo/src/utils/limited_source_buffer.ts",
+    "media-element-demo/src/utils/media.ts",
+    "media-element-demo/src/utils/observable.ts",
+    "media-element-demo/src/utils/shared_values.ts",
+    "media-element-demo/tsconfig.json",
+    "media-element-demo/webpack.config.js",
+    "media-query/media-query-test.html",
+    "mtm-demo/mtm.html",
+    "mtm-demo/normal.html",
+    "opacity-transitions-demo/opacity-transitions-demo.html",
+    "page-visibility-demo/page-visibility-demo.html",
+    "performance-api-demo/performance-lifecycle-timing-demo.html",
+    "performance-api-demo/performance-resource-timing-demo.html",
+    "performance-api-demo/resources/square.png",
+    "pointer-events-demo/pointer-events-demo.html",
+    "screen_diagonal/screen_diagonal.html",
+    "script-debugger-test/script-debugger-test.html",
+    "script-tag-demo/increment-and-print-i.js",
+    "script-tag-demo/script-tag-demo.html",
+    "selector-tester/selector-tester.html",
+    "simple-xhr/simple-xhr.html",
+    "simple-xhr/simple-xhr.js",
+    "smooth-animations-demo/index.html",
+    "smooth-key-scroll/index.html",
+    "specificity-demo/specificity-demo.html",
+    "speech-synthesis-demo/index.html",
+    "splash_screen/beforeunload.html",
+    "splash_screen/block_render_tree_html_display_none.html",
+    "splash_screen/link_splash_screen.html",
+    "splash_screen/link_splash_screen_network.html",
+    "splash_screen/redirect_server.py",
+    "splash_screen/redirected.html",
+    "splash_screen/render_postponed.html",
+    "system-caption-settings/index.html",
+    "timer-demo/timer-demo.html",
+    "transitions-demo/transitions-demo.html",
+    "transparent-animated-webp-demo/bottleflip_loader.webp",
+    "transparent-animated-webp-demo/index.html",
+    "transparent-animated-webp-demo/loading-spinner-opaque.webp",
+    "transparent-animated-webp-demo/webp-animated-semitransparent4.webp",
+    "unload-demo/unload-demo.html",
+    "user-agent-client-hints-demo/user-agent-client-hints-demo.html",
+    "web-audio-demo/web-audio-demo.html",
+  ]
+
+  if (is_internal_build) {
+    sources += [
+      "javascript-fuzzer/index.html",
+      "kabuki/runtime-dump.html",
+      "media-element-demo/public/assets/ac3.mp4",
+      "media-element-demo/public/assets/dash-audio.mp4",
+      "media-element-demo/public/assets/dash-video-1080p.mp4",
+      "media-element-demo/public/assets/dash-video-240p.mp4",
+      "media-element-demo/public/assets/eac3.mp4",
+      "media-element-demo/public/assets/hvc1_480p.mp4",
+      "media-element-demo/public/assets/hvc1_480p_720p.mp4",
+      "media-element-demo/public/assets/hvc1_720p.mp4",
+      "media-element-demo/public/assets/hvc1_hdr_480p.mp4",
+      "media-element-demo/public/assets/progressive.mp4",
+      "mtm-demo/README.txt",
+      "mtm-demo/progressive.mp4",
+      "performance-spike/assets/Roboto-Regular.ttf",
+      "performance-spike/assets/banner.jpg",
+      "performance-spike/assets/banner1080.jpg",
+      "performance-spike/assets/banner1080baked.jpg",
+      "performance-spike/assets/banner1080withLinearGradient.jpg",
+      "performance-spike/assets/banner720.jpg",
+      "performance-spike/assets/banner720baked.jpg",
+      "performance-spike/assets/icons.ttf",
+      "performance-spike/assets/profile-alecmce.jpg",
+      "performance-spike/css/default.css",
+      "performance-spike/css/icons-content.css",
+      "performance-spike/css/icons.css",
+      "performance-spike/di/injector.js",
+      "performance-spike/di/mapping.js",
+      "performance-spike/di/resolver.js",
+      "performance-spike/index.html",
+      "performance-spike/namespace.js",
+      "performance-spike/runtime-dump.html",
+      "performance-spike/spike/anim/_config.js",
+      "performance-spike/spike/anim/alignment.js",
+      "performance-spike/spike/anim/centering.js",
+      "performance-spike/spike/anim/cssanimations/_config.js",
+      "performance-spike/spike/anim/cssanimations/animationbuilder.js",
+      "performance-spike/spike/anim/cssanimations/centering.js",
+      "performance-spike/spike/anim/cssanimations/rows.js",
+      "performance-spike/spike/anim/csstransitions/_config.js",
+      "performance-spike/spike/anim/csstransitions/centering.js",
+      "performance-spike/spike/anim/csstransitions/rows.js",
+      "performance-spike/spike/anim/csstransitions/transitionbuilder.js",
+      "performance-spike/spike/anim/rows.js",
+      "performance-spike/spike/anim/transformer/_config.js",
+      "performance-spike/spike/anim/transformer/centering.js",
+      "performance-spike/spike/anim/transformer/ease.js",
+      "performance-spike/spike/anim/transformer/rows.js",
+      "performance-spike/spike/anim/transformer/transformer.js",
+      "performance-spike/spike/anim/tweens/_config.js",
+      "performance-spike/spike/anim/tweens/centering.js",
+      "performance-spike/spike/anim/tweens/rows.js",
+      "performance-spike/spike/anim/tweens/tweens.js",
+      "performance-spike/spike/behavior/_config.js",
+      "performance-spike/spike/behavior/body.js",
+      "performance-spike/spike/behavior/buttons.js",
+      "performance-spike/spike/behavior/column.js",
+      "performance-spike/spike/behavior/content.js",
+      "performance-spike/spike/behavior/cssfocused.js",
+      "performance-spike/spike/behavior/factory.js",
+      "performance-spike/spike/behavior/header.js",
+      "performance-spike/spike/behavior/headerbuttons.js",
+      "performance-spike/spike/behavior/item.js",
+      "performance-spike/spike/behavior/main.js",
+      "performance-spike/spike/behavior/menu.js",
+      "performance-spike/spike/behavior/navigate.js",
+      "performance-spike/spike/behavior/row.js",
+      "performance-spike/spike/core/_config.js",
+      "performance-spike/spike/core/environment.js",
+      "performance-spike/spike/core/experiments.js",
+      "performance-spike/spike/core/fps.js",
+      "performance-spike/spike/core/location.js",
+      "performance-spike/spike/core/math.js",
+      "performance-spike/spike/core/rollingmean.js",
+      "performance-spike/spike/core/styles.js",
+      "performance-spike/spike/core/throttlefactory.js",
+      "performance-spike/spike/core/ticker.js",
+      "performance-spike/spike/ctrl/_config.js",
+      "performance-spike/spike/ctrl/behaviors.js",
+      "performance-spike/spike/ctrl/factory.js",
+      "performance-spike/spike/ctrl/focuser.js",
+      "performance-spike/spike/ctrl/hbox.js",
+      "performance-spike/spike/ctrl/keyhandler.js",
+      "performance-spike/spike/ctrl/leaf.js",
+      "performance-spike/spike/ctrl/vbox.js",
+      "performance-spike/spike/data/_config.js",
+      "performance-spike/spike/data/channel_crash_course.js",
+      "performance-spike/spike/data/menu.js",
+      "performance-spike/spike/data/menu_content.js",
+      "performance-spike/spike/data/update.js",
+      "performance-spike/spike/factory/_config.js",
+      "performance-spike/spike/factory/itemfactory.js",
+      "performance-spike/spike/factory/menufactory.js",
+      "performance-spike/spike/factory/rowfactory.js",
+      "performance-spike/spike/main.js",
+      "text-encoding-workaround/text-encoding-workaround.html",
+    ]
+  }
+
+  outputs = [
+    "$sb_static_contents_output_data_dir/test/demos/{{source_target_relative}}",
+  ]
+}
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/0.5.html b/cobalt/demos/content/mse-eme-conformance-tests/0.5.html
deleted file mode 100644
index 26d8d0e..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/0.5.html
+++ /dev/null
@@ -1,6299 +0,0 @@
-<!DOCTYPE html>
-<!--
-Copyright 2014 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.
--->
-<html>
-  <head>
-    <title>Legacy Media Source and Encrypted Media Conformance Tests (2013)</title>
-    <link rel="stylesheet" href="style-20150612143746.css" type="text/css"></link>
-    <script type="text/javascript">
-function Alert(msg) {
-  console.log(msg);
-  throw msg;
-}
-
-// util-20150612143746.js begin
-(function() {
-
-if (!Function.prototype.bind) {
-  Function.prototype.bind = function(oThis) {
-    if (typeof this !== 'function') {
-      throw new TypeError('What is trying to be bound is not a function');
-    }
-
-    var aArgs = Array.prototype.slice.call(arguments, 1);
-    var fToBind = this;
-    var fNOP = function() {};
-    var fBound = function() {
-      return fToBind.apply(
-          this instanceof fNOP && oThis ? this : oThis,
-          aArgs.concat(Array.prototype.slice.call(arguments)));
-    };
-
-    fNOP.prototype = this.prototype;
-    fBound.prototype = new fNOP();
-
-    return fBound;
-  };
-}
-
-var util = {};
-
-util.createElement = function(tag, id, class_, innerHTML) {
-  var element = document.createElement(tag);
-  if (id != null)
-    element.id = id;
-  if (innerHTML != null)
-    element.innerHTML = innerHTML;
-  if (class_ != null)
-    element.classList.add(class_);
-  return element;
-};
-
-util.getClosestElement = function(refElement) {
-  if (arguments.length === 1)
-    return null;
-
-  var bestElement = arguments[1];
-  var bestDistance =
-      Math.abs((bestElement.offsetLeft + bestElement.offsetWidth / 2) -
-               (refElement.offsetLeft + refElement.offsetWidth / 2));
-  for (var i = 2; i < arguments.length; ++i) {
-    var currElement = arguments[i];
-    var currDistance =
-        Math.abs((currElement.offsetLeft + currElement.offsetWidth / 2) -
-                 (refElement.offsetLeft + refElement.offsetWidth / 2));
-    if (currDistance < bestDistance) {
-      bestDistance = currDistance;
-      bestElement = currElement;
-    }
-  }
-
-  return bestElement;
-};
-
-util.fireEvent = function(obj, eventName) {
-  if (document.createEvent) {
-    var event = document.createEvent('MouseEvents');
-    event.initEvent(eventName, true, false);
-    obj.dispatchEvent(event);
-  } else if (document.createEventObject) {
-    obj.fireEvent('on' + eventName);
-  }
-};
-
-util.getElementWidth = function(element) {
-  var style = window.getComputedStyle(element);
-  var width = 0;
-
-  if (!isNaN(parseInt(style.width))) width += parseInt(style.width);
-  if (!isNaN(parseInt(style.marginLeft))) width += parseInt(style.marginLeft);
-  if (!isNaN(parseInt(style.marginRight))) width += parseInt(style.marginRight);
-
-  return width;
-};
-
-util.isValidArgument = function(arg) {
-  return typeof(arg) != 'undefined' && arg != null;
-};
-
-util.MakeCapitalName = function(name) {
-  name = name.substr(0, 1).toUpperCase() + name.substr(1);
-  var offset = 0;
-  for (;;) {
-    var space = name.indexOf(' ', offset);
-    if (space === -1)
-      break;
-    name = name.substr(0, space + 1) +
-        name.substr(space + 1, 1).toUpperCase() + name.substr(space + 2);
-    offset = space + 1;
-  }
-  return name;
-};
-
-util.Round = function(value, digits) {
-  return Math.round(value * Math.pow(10, digits)) / Math.pow(10, digits);
-};
-
-util.SizeToText = function(size, unitType) {
-  var unit = 'B';
-  if (!!unitType && (unitType == 'B' || unitType == 'b')) {
-    unit = unitType;
-  }
-  if (size >= 1024 * 1024) {
-    size /= 1024 * 1024;
-    unit = 'M';
-  } else if (size >= 1024) {
-    size /= 1024;
-    unit = 'K';
-  }
-  if ((size - Math.floor(size)) * 10 <
-      Math.floor(size))
-    size = Math.floor(size);
-  else
-    size = util.Round(size, 3);
-  return size + unit;
-};
-
-util.formatStatus = function(status) {
-  if (typeof status === 'undefined')
-    return 'undefined';
-  else if (typeof status === 'string')
-    return '"' + status + '"';
-  else if (typeof status === 'number')
-    return status.toString();
-  else if (typeof status === 'boolean')
-    return status ? 'true' : 'false';
-  throw 'unknown status type';
-};
-
-util.getAttr = function(obj, attr) {
-  attr = attr.split('.');
-  if (!obj || attr.length === 0)
-    return undefined;
-  while (attr.length) {
-    if (!obj)
-      return undefined;
-    obj = obj[attr.shift()];
-  }
-  return obj;
-};
-
-util.resize = function(str, newLength, fillValue) {
-  if (typeof str != 'string')
-    throw 'Only string is supported';
-  if (str.length > newLength) {
-    str.substr(0, newLength);
-  } else {
-    while (str.length < newLength)
-      str += fillValue;
-  }
-
-  return str;
-};
-
-window.util = util;
-
-})();
-// util-20150612143746.js end
-
-// streamDef-20150612143746.js begin
-function getStreamDef(index) {
-  var d = {};
-  index = index || 0;
-
-  var streamDefinitions = [
-    {
-      AudioType: 'audio/mp4; codecs="mp4a.40.2"',
-      VideoType: 'video/mp4; codecs="avc1.640028"',
-      AudioTiny: ['media/car-20120827-8b.mp4', 717502, 181.62],
-      AudioNormal: ['media/car-20120827-8c.mp4', 2884572, 181.58, {
-          200000: 12.42}],
-      AudioNormalAdv: ['media/car-20120827-8c.mp4', 2884572, 181.58, {
-          200000: 12.42}],
-      AudioHuge: ['media/car-20120827-8d.mp4', 5789853, 181.58, {
-          'appendAudioOffset': 17.42}],
-      VideoTiny: ['media/car-20120827-85.mp4', 6015001, 181.44, {
-          'videoChangeRate': 11.47}],
-      VideoNormal: ['media/car-20120827-86.mp4', 15593225, 181.44, {
-          'mediaSourceDuration': Infinity}],
-      VideoHuge: ['media/car-20120827-89.mp4', 95286345, 181.44],
-      AudioTinyClearKey: ['media/car_cenc-20120827-8b.mp4', 783470, 181.62],
-      AudioNormalClearKey: ['media/car_cenc-20120827-8c.mp4', 3013084, 181.58],
-      AudioHugeClearKey: ['media/car_cenc-20120827-8d.mp4', 5918365, 181.58],
-      VideoTinyClearKey: ['media/car_cenc-20120827-85.mp4', 6217017, 181.44],
-      VideoNormalClearKey: ['media/car_cenc-20120827-86.mp4', 15795193, 181.44],
-      VideoHugeClearKey: ['media/car_cenc-20120827-89.mp4', 95488313, 181.44],
-      VideoStreamYTCenc: ['media/oops_cenc-20121114-145-no-clear-start.mp4', 39980507, 242.71],
-      VideoTinyStreamYTCenc: ['media/oops_cenc-20121114-145-143.mp4', 7229257, 30.03],
-      VideoSmallStreamYTCenc: ['media/oops_cenc-20121114-143-no-clear-start.mp4', 12045546, 242.71],
-      Audio1MB: ['media/car-audio-1MB-trunc.mp4', 1048576, 65.875],
-      Video1MB: ['media/test-video-1MB.mp4', 1053406, 1.04],
-      ProgressiveLow: ['media/car_20130125_18.mp4', 15477531, 181.55],
-      ProgressiveNormal: ['media/car_20130125_22.mp4', 55163609, 181.55],
-      ProgressiveHigh: [],
-    }, {
-      AudioType: 'audio/mp4; codecs="mp4a.40.2"',
-      VideoType: 'video/webm; codecs="vp9"',
-      AudioTiny: ['media/car-20120827-8b.mp4', 717502, 181.62],
-      AudioNormal: ['media/car-20120827-8c.mp4', 2884572, 181.58, {
-          200000: 12.42}],
-      AudioNormalAdv: ['media/car-20120827-8c.mp4', 2884572, 181.58, {
-          200000: 12.42}],
-      AudioHuge: ['media/car-20120827-8d.mp4', 5789853, 181.58, {
-          'appendAudioOffset': 17.42}],
-      VideoTiny: ['media/feelings_vp9-20130806-242.webm', 4478156, 135.46, {
-          'videoChangeRate': 15.35}],
-      VideoNormal: ['media/feelings_vp9-20130806-243.webm', 7902885, 135.46, {
-          'mediaSourceDuration': 135.469}],
-      VideoHuge: ['media/feelings_vp9-20130806-247.webm', 27757852, 135.46],
-      AudioTinyClearKey: ['media/car_cenc-20120827-8b.mp4', 783470, 181.62],
-      AudioNormalClearKey: ['media/car_cenc-20120827-8c.mp4', 3013084, 181.58],
-      AudioHugeClearKey: ['media/car_cenc-20120827-8d.mp4', 5918365, 181.58],
-      VideoTinyClearKey: [],
-      VideoNormalClearKey: [],
-      VideoHugeClearKey: [],
-      VideoStreamYTCenc: [],
-      VideoTinyStreamYTCenc: [],
-      VideoSmallStreamYTCenc: [],
-      Audio1MB: ['media/car-audio-1MB-trunc.mp4', 1048576, 65.875],
-      Video1MB: ['media/vp9-video-1mb.webm', 1103716, 1.00],
-      ProgressiveLow: [],
-      ProgressiveNormal: [],
-      ProgressiveHigh: [],
-    }
-  ];
-
-  d.AudioType = streamDefinitions[index]['AudioType'];
-  d.VideoType = streamDefinitions[index]['VideoType'];
-
-  var CreateAudioDef = function(src, size, duration, customMap) {
-    return {name: 'audio', type: d.AudioType, size: size, src: src,
-        duration: duration, bps: Math.floor(size / duration),
-        customMap: customMap};
-  };
-
-  var CreateVideoDef = function(src, size, duration, customMap) {
-    return {name: 'video', type: d.VideoType, size: size, src: src,
-        duration: duration, bps: Math.floor(size / duration),
-        customMap: customMap};
-  };
-
-  d.AudioTiny = CreateAudioDef.apply(this, streamDefinitions[index]['AudioTiny']);
-  d.AudioNormal = CreateAudioDef.apply(this, streamDefinitions[index]['AudioNormal']);
-  d.AudioNormalAdv = CreateAudioDef.apply(this, streamDefinitions[index]['AudioNormalAdv']);
-  d.AudioHuge = CreateAudioDef.apply(this,streamDefinitions[index]['AudioHuge']);
-
-  d.VideoTiny = CreateVideoDef.apply(this, streamDefinitions[index]['VideoTiny']);
-  d.VideoNormal = CreateVideoDef.apply(this, streamDefinitions[index]['VideoNormal']);
-  d.VideoHuge = CreateVideoDef.apply(this, streamDefinitions[index]['VideoHuge']);
-
-  d.AudioTinyClearKey = CreateAudioDef.apply(this, streamDefinitions[index]['AudioTinyClearKey']);
-  d.AudioNormalClearKey = CreateAudioDef.apply(this, streamDefinitions[index]['AudioNormalClearKey']);
-  d.AudioHugeClearKey = CreateAudioDef.apply(this, streamDefinitions[index]['AudioHugeClearKey']);
-
-  d.VideoTinyClearKey = CreateVideoDef.apply(this, streamDefinitions[index]['VideoTinyClearKey']);
-  d.VideoNormalClearKey = CreateVideoDef.apply(this, streamDefinitions[index]['VideoNormalClearKey']);
-  d.VideoHugeClearKey = CreateVideoDef.apply(this, streamDefinitions[index]['VideoHugeClearKey']);
-
-  d.VideoStreamYTCenc = CreateVideoDef.apply(this, streamDefinitions[index]['VideoStreamYTCenc']);
-  d.VideoTinyStreamYTCenc = CreateVideoDef.apply(this, streamDefinitions[index]['VideoTinyStreamYTCenc']);
-  d.VideoSmallStreamYTCenc = CreateVideoDef.apply(this, streamDefinitions[index]['VideoSmallStreamYTCenc']);
-
-  d.Audio1MB = CreateAudioDef.apply(this, streamDefinitions[index]['Audio1MB']);
-  d.Video1MB = CreateVideoDef.apply(this, streamDefinitions[index]['Video1MB']);
-
-  d.ProgressiveLow = CreateVideoDef.apply(this, streamDefinitions[index]['ProgressiveLow']);
-  d.ProgressiveNormal = CreateVideoDef.apply(this, streamDefinitions[index]['ProgressiveNormal']);
-  d.ProgressiveHigh = CreateVideoDef.apply(this, streamDefinitions[index]['ProgressiveHigh']);
-
-  d.isWebM = function() {
-    return index === 1;
-  }
-
-  return d;
-}
-
-var StreamDef = getStreamDef();
-
-function UpdateStreamDef(index) {
-  StreamDef = getStreamDef(index);
-}
-
-// streamDef-20150612143746.js end
-
-// focusManager-20150612143746.js begin
-
-(function() {
-
-var INFINITY = 100000;
-var CLOSE = 50;
-var MAX_FUDGE = INFINITY;
-var DIRECTION_WEIGHT = 0.5;
-
-var LEFT = new Pair(-1, 0);
-var UP = new Pair(0, -1);
-var RIGHT = new Pair(1, 0);
-var DOWN = new Pair(0, 1);
-
-function Pair(x, y) {
-  this.x = x;
-  this.y = y;
-
-  this.add = function(operand) {
-    return new Pair(this.x + operand.x, this.y + operand.y);
-  };
-
-  this.sub = function(operand) {
-    return new Pair(this.x - operand.x, this.y - operand.y);
-  };
-
-  this.dot = function(operand) {
-    return this.x * operand.x + this.y * operand.y;
-  };
-
-  this.dotRelative = function(ref, operand) {
-    return this.x * (operand.x - ref.x) + this.y * (operand.y - ref.y);
-  };
-
-  this.distTo = function(operand) {
-    return Math.sqrt(this.x * operand.x + this.y * operand.y);
-  };
-
-  this.distTo2 = function(operand) {
-    return this.x * operand.x + this.y * operand.y;
-  };
-
-  this.cross = function(operand) {
-    return this.x * operand.y - this.y * operand.x;
-  };
-}
-
-function Rect(left, top, width, height) {
-  this.left = left;
-  this.top = top;
-  this.width = width;
-  this.height = height;
-  this.right = this.left + this.width;
-  this.bottom = this.top + this.height;
-
-  var rangeDist = function(start, end, startRef, endRef) {
-    if (start < startRef) {
-      if (end < startRef)
-        return startRef - end;
-      return 0;
-    }
-    if (start <= endRef)
-      return 0;
-    return start - endRef;
-  };
-
-  this.valid = function() {
-    return this.width !== 0 && this.height !== 0;
-  };
-
-  this.inside = function(x, y) {
-    // Technically speaking, this is not correct. However, this works out for
-    // our usage.
-    return x >= this.left && x < this.left + this.width &&
-        y >= this.top && y < this.top + this.height;
-  };
-
-  this.intersect = function(that) {
-    return this.inside(that.left, that.top) ||
-        this.inside(that.right, that.top) ||
-        this.inside(that.left, that.bottom) ||
-        this.inside(that.right, that.bottom) ||
-        that.inside(this.left, this.top) ||
-        that.inside(this.right, this.top) ||
-        that.inside(this.left, this.bottom) ||
-        that.inside(this.right, this.bottom);
-  };
-
-  this.intersectComplete = function(that) {
-    var centerXThat = (that.left + that.right) * 0.5;
-    var centerYThat = (that.top + that.bottom) * 0.5;
-    var halfThatWidth = that.width * 0.5;
-    var halfThatHeight = that.height * 0.5;
-    var expandedRect = new Rect(
-        this.left - halfThatWidth, this.top - halfThatHeight,
-        this.width + that.width, this.height + that.height);
-    return expandedRect.inside(centerXThat, centerYThat);
-  };
-
-  this.distanceSquared = function(ref, dir) {
-    var x, y;
-    if (dir.x === -1) {
-      x = Math.max((ref.left - this.right) * DIRECTION_WEIGHT, 0);
-      y = rangeDist(this.top, this.bottom, ref.top, ref.bottom);
-    } else if (dir.x === 1) {
-      x = Math.max((this.left - ref.right) * DIRECTION_WEIGHT, 0);
-      y = rangeDist(this.top, this.bottom, ref.top, ref.bottom);
-    } else if (dir.y === -1) {
-      x = rangeDist(this.left, this.right, ref.left, ref.right);
-      y = Math.max((ref.top - this.bottom) * DIRECTION_WEIGHT, 0);
-    } else {
-      x = rangeDist(this.left, this.right, ref.left, ref.right);
-      y = Math.max((this.top - ref.bottom) * DIRECTION_WEIGHT, 0);
-    }
-
-    return x * x + y * y;
-  };
-
-  this.generateSideSliver = function(dir) {
-    var left, right, top, bottom;
-
-    if (dir === LEFT) {
-      left = this.left - CLOSE;
-      right = this.left - 1;
-      top = (this.top + this.bottom) * 0.5;
-      bottom = top;
-    } else if (dir === RIGHT) {
-      left = this.right + 1;
-      right = this.right + CLOSE;
-      top = (this.top + this.bottom) * 0.5;
-      bottom = top;
-    } else if (dir === UP) {
-      left = (this.left + this.right) * 0.5;
-      right = (this.left + this.right) * 0.5;
-      top = this.top - CLOSE;
-      bottom = this.top - 1;
-    } else {
-      left = (this.left + this.right) * 0.5;
-      right = (this.left + this.right) * 0.5;
-      top = this.bottom + 1;
-      bottom = this.bottom + CLOSE;
-    }
-
-    return new Rect(left, top, right - left, bottom - top);
-  };
-
-  // Generates a rectangle to check if there are any other rectangles strictly
-  // to one side (defined by dir) of 'this'.
-  this.generateSideRect = function(dir, fudge) {
-    if (!fudge) {
-      return this.generateSideSliver(dir);
-    }
-
-    var left, right, top, bottom;
-
-    if (dir === LEFT) {
-      left = -INFINITY;
-      right = this.left - 1;
-      top = this.top - fudge;
-      bottom = this.bottom + fudge;
-    } else if (dir === RIGHT) {
-      left = this.right + 1;
-      right = INFINITY;
-      top = this.top - fudge;
-      bottom = this.bottom + fudge;
-    } else if (dir === UP) {
-      left = this.left - fudge;
-      right = this.right + fudge;
-      top = -INFINITY;
-      bottom = this.top - 1;
-    } else {
-      left = this.left - fudge;
-      right = this.right + fudge;
-      top = this.bottom + 1;
-      bottom = INFINITY;
-    }
-
-    return new Rect(left, top, right - left, bottom - top);
-  };
-
-  this.toSideOf = function(target, dir) {
-    var testX = [0, this.right - this.center.x, this.left - this.center.x];
-    var testY = [0, this.bottom - this.center.y, this.top - this.center.y];
-
-    var testLineSegRel0 = new Pair(
-        testX[dir.x - (dir.x != 0)], testY[dir.y - (dir.y != 0)]);
-    var testLineSegRel1 = new Pair(
-        testY[dir.x + (dir.x != 0)], testY[dir.y + (dir.y != 0)]);
-
-    return dir.cross(testLineSegRel0) * dir.cross(testLineSegRel1) <= 0 &&
-        this.intersect(target);
-  };
-
-  this.toString = function() {
-    return '(' + this.left + ', ' + this.top + ', ' + this.right + ', ' +
-        this.bottom + ')';
-  };
-};
-
-function createRect(element) {
-  var offsetLeft = element.offsetLeft;
-  var offsetTop = element.offsetTop;
-  var e = element.offsetParent;
-  while (e && e !== document.body) {
-    offsetLeft += e.offsetLeft;
-    offsetTop += e.offsetTop;
-    e = e.offsetParent;
-  }
-  return new Rect(offsetLeft, offsetTop,
-                  element.offsetWidth, element.offsetHeight);
-};
-
-function FocusManager() {
-  var elements = [];
-  var handlers = [];
-
-  var pickElement_ = function(currElem, dir, fudge) {
-    var rect = createRect(currElem);
-    var rectSide = rect.generateSideRect(dir, fudge);
-    var bestDistanceSquared = INFINITY * INFINITY;
-    var bestElement = null;
-
-    for (var i = 0; i < elements.length; ++i) {
-      if (elements[i] !== currElem) {
-        var r = createRect(elements[i]);
-
-        if (r.valid() && r.intersectComplete(rectSide)) {
-          var distanceSquared = r.distanceSquared(rect, dir);
-          if (!bestElement || distanceSquared < bestDistanceSquared) {
-            bestElement = elements[i];
-            bestDistanceSquared = distanceSquared;
-          }
-        }
-      }
-    }
-
-    return bestElement;
-  };
-
-  var pickElement = function(currElem, dir) {
-    return pickElement_(currElem, dir) ||
-        pickElement_(currElem, dir, 2) ||
-        pickElement_(currElem, dir, MAX_FUDGE);
-  };
-
-  var onkeydown = function(e) {
-    if (elements.indexOf(e.target) !== -1) {
-      var dir;
-      if (e.keyCode === 37) {  // left
-        dir = LEFT;
-      } else if (e.keyCode === 38) {  // up
-        dir = UP;
-      } else if (e.keyCode === 39) {  // right
-        dir = RIGHT;
-      } else if (e.keyCode === 40) {  // down
-        dir = DOWN;
-      } else {
-        return true;
-      }
-      var element = pickElement(e.target, dir);
-      if (element) {
-        element.focus();
-        e.stopPropagation();
-        e.preventDefault();
-      }
-    }
-
-    return true;
-  };
-
-  this.add = function(element) {
-    if (elements.indexOf(element) === -1) {
-      elements.push(element);
-      handlers.push(element.addEventListener('keydown', onkeydown));
-    }
-  };
-};
-
-window.addEventListener('load', function() {
-  var focusManager = new FocusManager;
-  var elements = document.getElementsByClassName('focusable');
-  for (var i = 0; i < elements.length; ++i)
-    focusManager.add(elements[i]);
-
-  /*var links = document.getElementsByTagName('A');
-  for (var i = 0; i < links.length; ++i)
-    focusManager.add(links[i]);*/
-});
-
-})();
-
-// focusManager-20150612143746.js end
-
-// logger-20150612143746.js begin
-
-(function() {
-
-var Logger = function(log) {
-  this.throwError = true;
-  this.log = log;
-  this.assert = function(cond, msg) {
-    if (!cond) {
-      this.log('Assert failed: ' + msg);
-      try {
-        var x = y.z.u.v.w;
-      } catch (e) {
-        this.log(e.stack);
-      }
-      if (this.throwError) throw 'Assert: ' + msg;
-    }
-  };
-
-  this.check = function(condition, passMsg, failMsg) {
-    if (condition)
-      this.log(passMsg);
-    else
-      this.assert(false, failMsg);
-  };
-
-  this.checkEq = function(x, y, name) {
-    var result = (x == y) ||
-        (typeof(x) === 'number' && typeof(y) === 'number' &&
-         isNaN(x) && isNaN(y));
-    this.check(result, 'checkEq passed: ' + name + ' is (' + x + ').',
-               name + ' is (' + x + ') which should be (' + y + ')');
-  };
-
-  this.checkNE = function(x, y, name) {
-    var result = (x != y) &&
-        !((typeof(x) === 'number' && typeof(y) === 'number' &&
-           isNaN(x) && isNaN(y)));
-    this.check(result, 'checkNE passed: ' + name + ' is (' + x + ').',
-               name + ' is (' + x + ') which shouldn\'t.');
-  };
-};
-
-window.createLogger = function(log) {
-  return new Logger(log || console.log.bind(console));
-};
-
-})();
-
-// logger-20150612143746.js end
-
-// js/harness/xhr-20150612143746.js begin
-
-(function() {
-
-var BYPASS_CACHE = false;
-
-// Hook the onload event for request that is finished successfully
-var Request = function(manager, logger, file, onload, postLength,
-                       start, length) {
-  var self = this;
-
-  this.open = function() {
-    this.xhr = new XMLHttpRequest();
-
-    this.onload = onload;
-    this.type = util.isValidArgument(postLength) ? 'POST' : 'GET';
-
-    this.xhr.open(this.type,
-                  file + (BYPASS_CACHE ? '?' + (new Date()).getTime() : ''));
-    this.xhr.responseType = 'arraybuffer';
-
-    this.startTime = new Date().getTime();
-    this.lastUpdate = this.startTime;
-
-    if (start != null && length != null)
-      this.xhr.setRequestHeader(
-          'Range', 'bytes=' + start + '-' + (start + length - 1));
-
-    this.xhr.addEventListener('error', function(e) {
-      if (self.xhr.status === 404)
-        Alert('Failed to find "' + file +
-              '" with error 404. Is it on the server?');
-      manager.requestFinished(self);
-      logger.log('XHR error with code', self.xhr.status);
-      self.open();
-      self.send();
-    });
-
-    this.xhr.addEventListener('timeout', function(e) {
-      manager.requestFinished(self);
-      logger.log('XHR timeout');
-      self.open();
-      self.send();
-    });
-
-    this.xhr.addEventListener('load', function(e) {
-      manager.requestFinished(self);
-      return self.onload(e);
-    });
-
-    this.xhr.addEventListener('progress', function onProgress(e) {
-      if (e.lengthComputable && (e.loaded === e.total)) {
-        self.xhr.removeEventListener('progress', onProgress);
-      }
-      self.lastUpdate = new Date().getTime();
-    });
-  };
-
-  this.getRawResponse = function() {
-    if (this.xhr.status === 404)
-      Alert('Failed to find "' + file +
-            '" with error 404. Is it on the server?');
-    logger.assert(this.xhr.status >= 200 && this.xhr.status < 300,
-                  'XHR bad status: ' + this.xhr.status);
-    return this.xhr.response;
-  };
-
-  this.getResponseData = function() {
-    if (this.xhr.status === 404)
-      Alert('Failed to find "' + file +
-            '" with error 404. Is it on the server?');
-    logger.assert(this.xhr.status >= 200 && this.xhr.status < 300,
-                  'XHR bad status: ' + this.xhr.status);
-    var result = new Uint8Array(this.xhr.response);
-    if (length != null) {
-      var rangeHeader = this.xhr.getResponseHeader('Content-Range');
-      var lengthHeader = this.xhr.getResponseHeader('Content-Length');
-      if (!rangeHeader && lengthHeader) {
-        logger.assert(length <= lengthHeader,
-                      'Length of response is smaller than request');
-        result = result.subarray(start, start + length);
-        logger.checkEq(result.length, length, 'XHR length');
-        return result;
-      }
-      logger.checkEq(result.length, length, 'XHR length');
-    }
-    return result;
-  };
-
-  this.send = function(postData) {
-    manager.addRequest(this);
-    if (postData) {
-      logger.checkEq(this.type, 'POST', 'XHR requestType');
-      this.xhr.send(postData);
-    } else {
-      logger.checkEq(this.type, 'GET', 'XHR requestType');
-      this.xhr.send();
-    }
-  };
-
-  this.abort = function() {
-    this.xhr.abort();
-  };
-
-  this.open();
-};
-
-var XHRManager = function(logger) {
-  var requests = [];
-  this.totalRequestDuration = 0;
-
-  this.addRequest = function(request) {
-    logger.checkEq(requests.indexOf(request), -1, 'request index');
-    requests.push(request);
-  };
-
-  this.requestFinished = function(request) {
-    var currentTime = new Date().getTime();
-    this.totalRequestDuration += currentTime - request.startTime;
-    logger.checkNE(requests.indexOf(request), -1, 'request index');
-    requests.splice(requests.indexOf(request), 1);
-  };
-
-  this.abortAll = function() {
-    for (var i = 0; i < requests.length; ++i)
-      requests[i].abort();
-    requests = [];
-  };
-
-  this.createRequest = function(file, onload, start, length) {
-    return new Request(this, logger, file, onload, null, start, length);
-  };
-
-  this.createPostRequest = function(file, onload, postLength, start, length) {
-    return new Request(this, logger, file, onload, postLength, start, length);
-  };
-
-  this.hasActiveRequests = function() {
-    if (requests.length > 0) {
-      return true;
-    }
-    return false;
-  }
-
-  this.getLastUpdate = function() {
-    if (requests.length == 0) {
-      return null;
-    }
-
-    var latestUpdate = 0;
-    for (var i in requests) {
-      latestUpdate = Math.max(requests[i].lastUpdate, latestUpdate);
-    }
-    return latestUpdate;
-  };
-};
-
-window.createXHRManager = function(logger) {
-  return new XHRManager(logger);
-};
-
-})();
-
-// js/harness/xhr-20150612143746.js end
-
-// js/harness/timeout-20150612143746.js begin
-
-(function() {
-
-var TimeoutManager = function(logger) {
-  var timers = [];
-  var intervals = [];
-
-  var getUniqueItem = function(container) {
-    var id = 0;
-    while (typeof(container[id]) != 'undefined')
-      ++id;
-    container[id] = {id: id};
-    return container[id];
-  };
-
-  var timeoutHandler = function(id) {
-    if (timers[id]) {
-      var func = timers[id].func;
-      delete timers[id];
-      func();
-    }
-  };
-
-  var intervalHandler = function(id) {
-    var func = intervals[id].func;
-    func();
-  };
-
-  this.setTimeout = function(func, timeout) {
-    var timer = getUniqueItem(timers);
-    timer.func = func;
-    var id = window.setTimeout(timeoutHandler.bind(this, timer.id), timeout);
-  };
-
-  this.setInterval = function(func, timeout) {
-    var interval = getUniqueItem(intervals);
-    interval.func = func;
-    interval.id = window.setInterval(intervalHandler, timeout, interval.id);
-  };
-
-  this.clearAll = function() {
-    for (var id = 0; id < timers.length; ++id)
-      if (typeof(timers[id]) != 'undefined')
-        window.clearTimeout(timers[id].id);
-    timers = [];
-
-    for (var id = 0; id < intervals.length; ++id)
-      if (typeof(intervals[id]) != 'undefined')
-        window.clearInterval(intervals[id].id);
-    intervals = [];
-  };
-};
-
-window.createTimeoutManager = function(logger) {
-  return new TimeoutManager(logger);
-};
-
-})();
-
-// js/harness/timeout-20150612143746.js end
-
-// js/harness/testView-20150612143746.js begin
-var TestView = (function() {
-
-var createElement = util.createElement;
-
-var createAnchor = function(text, id) {
-  return util.createElement('span', id, 'rightmargin20', text);
-};
-
-var createOption = function(text, value) {
-  var option = document.createElement('option');
-  option.text = text;
-  option.value = value;
-  return option;
-};
-
-function TestView(mseSpec) {
-  this.mseSpec = mseSpec;
-  this.testList = null;
-
-  var selectors = [];
-  var switches = [];
-  var commands = [];
-  var testSuites = [];
-  var links = [];
-
-  this.addSelector = function(text, optionTexts, values, callback) {
-    optionTexts = optionTexts instanceof Array ? optionTexts : [optionTexts];
-    values = values instanceof Array ? values : [values];
-
-    if (optionTexts.length !== values.length)
-      throw "text length and value length don't match!";
-
-    selectors.push({
-      'text': text,
-      'optionTexts': optionTexts,
-      'values': values,
-      'cb': callback
-    })
-  };
-
-  this.addSwitch = function(text, id) {
-    switches.push({text: text, id: id});
-  };
-
-  this.addCommand = function(text, id, title, onclick) {
-    commands.push({text: text, id: id, title: title, onclick: onclick});
-  };
-
-  this.addTestSuite = function(text, href) {
-    testSuites.push({text: text, href: href});
-  };
-
-  this.addTestSuites = function(testTypes) {
-    var isTestTypeSupported = function(testType) {
-      var supported = testTypes[testType].supported;
-      if (typeof supported === 'string' && supported == 'all') {
-        return true;
-      } else if (typeof supported === 'object') {
-        for (var index in supported) {
-          if (supported[index] == mseSpec) {
-            return true;
-          }
-        }
-      }
-      return false;
-    };
-
-    for (var testType in testTypes) {
-      if (testType !== currentTestType && isTestTypeSupported(testType)) {
-        this.addTestSuite(testTypes[testType].name, '?test_type=' + testType);
-      }
-    }
-  }
-
-  this.addLink = function(text, href) {
-    links.push({text: text, href: href});
-  };
-
-  this.generate = function() {
-    var heading = '[' + this.mseSpec + '] ' +
-        testTypes[currentTestType].heading + ' (v 20150612143746-4K5xqupUzgiRyTYP)';
-    document.title = testTypes[currentTestType].title;
-    document.body.appendChild(createElement('div', 'title', null, heading));
-    document.body.appendChild(createElement('div', 'info'));
-    document.body.appendChild(createElement('div', 'usage'));
-    document.body.appendChild(createElement('div', 'testview'));
-
-    var div = document.getElementById(this.divId);
-    div.innerHTML = '';
-    div.appendChild(createElement('div', 'testsuites', 'container'));
-    div.appendChild(createElement('div', 'switches', 'container'));
-    div.appendChild(createElement('div', 'controls', 'container'));
-
-    var testContainer = createElement('div', null, 'container');
-    testContainer.appendChild(createElement('div', 'testlist', 'box-left'));
-    testContainer.appendChild(createElement('div', 'testarea'));
-    div.appendChild(testContainer);
-
-    var outputArea = createElement('div', 'outputarea');
-    var textArea = createElement('div', 'output');
-    textArea.rows = 10;
-    textArea.cols = 80;
-    outputArea.appendChild(textArea);
-    div.appendChild(outputArea);
-
-    var switchDiv = document.getElementById('switches');
-    for (var i = 0; i < switches.length; ++i) {
-      var id = switches[i].id;
-      switchDiv.appendChild(document.createTextNode(switches[i].text));
-      switchDiv.appendChild(createAnchor(window[id] ? 'on' : 'off', id));
-      switchDiv.lastChild.href = 'javascript:;';
-      switchDiv.lastChild.onclick = (function(id) {
-        return function(e) {
-          var wasOff = e.target.innerHTML === 'off';
-          e.target.innerHTML = wasOff ? 'on' : 'off';
-          window[id] = wasOff;
-        };
-      })(id);
-    }
-    for (var i = 0; i < selectors.length; ++i) {
-      switchDiv.appendChild(document.createTextNode(selectors[i].text));
-      var select = document.createElement('select');
-      for (var j = 0; j < selectors[i].optionTexts.length; ++j) {
-        select.appendChild(createOption(selectors[i].optionTexts[j],
-            selectors[i].values[j]));
-      }
-      select.onchange = selectors[i].cb;
-      switchDiv.appendChild(select);
-    }
-
-    switchDiv.appendChild(
-        createElement('span', 'finish-count', null, '0 tests finished'));
-
-    var controlsDiv = document.getElementById('controls');
-    for (var i = 0; i < commands.length; ++i) {
-      controlsDiv.appendChild(createAnchor(commands[i].text, commands[i].id));
-      controlsDiv.lastChild.href = 'javascript:;';
-      controlsDiv.lastChild.onclick = commands[i].onclick;
-      controlsDiv.lastChild.title = commands[i].title;
-    }
-
-    for (var i = 0; i < links.length; ++i) {
-      controlsDiv.appendChild(createAnchor(links[i].text));
-      controlsDiv.lastChild.href = links[i].href;
-    }
-
-    var testSuitesDiv = document.getElementById('testsuites');
-    for (var i = 0; i < testSuites.length; ++i) {
-      testSuitesDiv.appendChild(createAnchor(testSuites[i].text));
-      testSuitesDiv.lastChild.href = testSuites[i].href;
-    }
-
-    this.testList.generate(document.getElementById('testlist'));
-  };
-
-  this.addTest = function(desc) {
-    return this.testList.addTest(desc);
-  };
-
-  this.anySelected = function() {
-    return this.testList.anySelected();
-  };
-};
-
-return {
-  create: function(mseSpec) {
-    return new TestView(mseSpec);
-  }};
-
-})();
-
-// js/harness/testView-20150612143746.js end
-
-// fullTestList-20150612143746.js begin
-
-(function() {
-
-var createElement = util.createElement;
-
-function Test(desc, fields) {
-  var INDEX = 0;
-  var STATUS = INDEX + 1;
-  var DESC = STATUS + 1;
-  var FIELD = DESC + 1;
-  this.index = desc.index;
-  this.id = 'test-row' + this.index;
-  this.desc = desc;
-  this.steps = [];
-
-  this.createElement = function(element) {
-    element.id = this.id;
-    element.appendChild(createElement('td', null, 'index',
-                                      this.index + 1 + '.'));
-    element.appendChild(
-        createElement('td', null, 'status',
-                      '<input type="checkbox" checked="yes"></input> >'));
-    element.appendChild(createElement('td', null, 'desc'));
-
-    for (var field = 0; field < fields.length; ++field)
-      element.appendChild(createElement('td', null, 'state', 0));
-
-    var link = createElement('span', null, null, desc.desc);
-    link.href = 'javascript:;';
-    link.onclick = desc.onclick;
-    link.title = desc.title;
-    element.childNodes[DESC].appendChild(link);
-    var explanationPoint = createElement('span', null, 'desc-expl-point', '?');
-    var explanation = createElement(
-        'span', null, 'desc-expl-popup', desc.title);
-    explanationPoint.appendChild(explanation);
-    element.childNodes[DESC].appendChild(explanationPoint);
-  };
-
-  this.addStep = function(name) {
-    var tr = createElement('tr');
-    tr.appendChild(createElement('td', null, 'small'));
-    tr.appendChild(createElement('td', null, 'small'));
-    tr.appendChild(createElement('td', null, 'small',
-                                 this.steps.length + 1 + '. ' + name));
-    for (var field = 0; field < fields.length; ++field)
-      tr.appendChild(createElement('td', null, 'small', 0));
-
-    var element = document.getElementById(this.id);
-    if (this.steps.length !== 0)
-      element = this.steps[this.steps.length - 1];
-    if (element.nextSibling)
-      element.parentNode.insertBefore(tr, element.nextSibling);
-    else
-      element.parentNode.appendChild(tr);
-    this.steps.push(tr);
-  };
-
-  this.updateStatus = function() {
-    var element = document.getElementById(this.id);
-    element.childNodes[STATUS].className =
-        this.desc.running ? 'status_current' : 'status';
-    for (var field = 0; field < fields.length; ++field)
-      element.childNodes[FIELD + field].innerHTML =
-          this.desc[fields[field].replace(' ', '_')];
-  };
-
-  this.selected = function() {
-    var element = document.getElementById(this.id);
-    return element.childNodes[STATUS].childNodes[0].checked;
-  };
-
-  this.select = function() {
-    var element = document.getElementById(this.id);
-    element.childNodes[STATUS].childNodes[0].checked = true;
-  };
-
-  this.deselect = function() {
-    var element = document.getElementById(this.id);
-    element.childNodes[STATUS].childNodes[0].checked = false;
-  };
-}
-
-function TestList(fields) {
-  var tableId = 'test-list-table';
-  var headId = tableId + '-head';
-  var bodyId = tableId + '-body';
-  var tests = [];
-
-  if (!fields || !fields.length)
-    throw 'No test fields';
-
-  this.addColumnHeader = function(class_, text) {
-    var head = document.getElementById(headId);
-    var th = createElement('th', null, class_, text);
-    th.scope = 'col';
-    head.appendChild(th);
-  };
-
-  this.addTest = function(desc) {
-    var test = new Test(desc, fields);
-    tests.push(test);
-    return test;
-  };
-
-  this.generate = function(div) {
-    var table = document.createElement('table');
-    table.id = tableId;
-    div.appendChild(table);
-
-    var thead = createElement('thead');
-    table.classList.add('test-table');
-    table.innerHTML = '';
-    var head = createElement('tr');
-    var body = createElement('tbody');
-
-    head.id = headId;
-    body.id = bodyId;
-    thead.appendChild(head);
-    table.appendChild(thead);
-    table.appendChild(body);
-
-    this.addColumnHeader('index');
-    this.addColumnHeader('status');
-    this.addColumnHeader('desc', 'Test');
-
-    for (var i = 0; i < fields.length; ++i)
-      this.addColumnHeader('state', util.MakeCapitalName(fields[i]));
-
-    for (var i = 0; i < tests.length; ++i) {
-      var tr = createElement('tr');
-      body.appendChild(tr);
-      tests[i].createElement(tr);
-      tests[i].updateStatus();
-    }
-  };
-
-  this.getTest = function(index) {
-    return tests[index];
-  };
-
-  this.anySelected = function() {
-    for (var i = 0; i < tests.length; ++i)
-      if (tests[i].selected())
-        return true;
-    return false;
-  };
-
-  this.selectAll = function() {
-    for (var i = 0; i < tests.length; ++i)
-      tests[i].select();
-  };
-
-  this.deselectAll = function() {
-    for (var i = 0; i < tests.length; ++i)
-      tests[i].deselect();
-  };
-};
-
-window.createFullTestList = function(fields) {
-  return new TestList(fields);
-};
-
-})();
-// fullTestList-20150612143746.js end
-
-// js/harness/fullTestView-20150612143746.js begin
-
-var fullTestView = (function() {
-
-function FullTestView(fields) {
-  var self = this;
-  this.divId = 'testview';
-  this.testCount = 0;
-
-  this.initialize = function() {
-    this.testList = createFullTestList(fields);
-
-    this.addSwitch('Loop: ', 'loop');
-    this.addSwitch('Stop on failure: ', 'stoponfailure');
-    this.addSwitch('Log: ', 'logging');
-    this.addSwitch('WebM/VP9 (2015/tip only): ', 'enablewebm');
-
-    this.addCommand('Select All', 'select-all', 'Select all tests.',
-                    this.testList.selectAll.bind(this.testList));
-    this.addCommand('Deselect All', 'deselect-all', 'Deselect all tests.',
-                    this.testList.deselectAll.bind(this.testList));
-    this.addCommand('Run Selected', 'run-selected',
-                    'Run all selected tests in order.',
-                    function(e) {
-                      if (self.onrunselected)
-                        self.onrunselected.call(self, e);
-                    });
-
-    this.addLink('Links', 'links.html');
-    this.addLink('Instructions', 'instructions.html');
-    this.addLink('Changelog', 'main.html');
-    this.addLink('Download', 'download-20150612143746.tar.gz');
-
-    this.addTestSuites(testTypes);
-  };
-
-  this.addTest = function(desc) {
-    return this.testList.addTest(desc);
-  };
-
-  this.generate = function() {
-    FullTestView.prototype.generate.call(this);
-    // document.getElementById('run-selected').focus();
-  };
-
-  this.getTest = function(index) {
-    return this.testList.getTest(index);
-  };
-
-  this.finishedOneTest = function() {
-    ++this.testCount;
-    document.getElementById('finish-count').innerHTML =
-        this.testCount === 1 ? this.testCount + ' test finished' :
-                              this.testCount + ' tests finished';
-  };
-
-  this.anySelected = function() {
-    return this.testList.anySelected();
-  };
-
-  this.initialize();
-};
-
-//FullTestView.prototype = TestView.create();
-//FullTestView.prototype.constructor = FullTestView;
-
-return {
-  create: function(mseSpec, fields) {
-    FullTestView.prototype = TestView.create(mseSpec);
-    FullTestView.prototype.constructor = FullTestView;
-    return new FullTestView(fields);
-  }
-};
-
-})();
-
-// js/harness/fullTestView-20150612143746.js end
-
-// js/harness/compactTestList-20150612143746.js begin
-
-(function() {
-
-var ITEM_IN_COLUMN = 25;  // Test item count in a column
-var CATEGORY_SPACE = 1;  // Row between the end of the last category and the
-                         // beginning of the next category
-var MIN_ROW_AT_THE_BOTTOM = 2;  // If at the bottom of the table and the row
-                                // count is less than this, start a new column.
-
-var createElement = util.createElement;
-
-function Category(categoryName) {
-  this.setElement = function(nameCell, statusCell) {
-    nameCell.className = 'cell-category';
-    nameCell.innerText = categoryName;
-  };
-
-  this.setDoubleElement = function(cellElem) {
-    cellElem.className = 'cell-category';
-    cellElem.setAttribute('colspan', 2);
-    cellElem.innerText = categoryName;
-  };
-}
-
-function Test(desc, style) {
-  var self = this;
-  this.index = desc.index;
-  this.nameId = 'test-item-name-' + this.index;
-  this.statusId = 'test-item-status-' + this.index;
-  this.desc = desc;
-  this.steps = [];
-  this.style = style;
-
-  this.createElement = function(name, status) {
-    name.id = this.nameId;
-    status.id = this.statusId;
-    var link = createElement('span', null, null,
-                             this.index + 1 + '. ' + this.desc.desc);
-    link.href = 'javascript:;';
-    link.onclick = desc.onclick;
-    link.title = desc.title;
-    name.appendChild(link);
-    this.updateStatus(status);
-  };
-
-  this.updateStatus = function(status) {
-    var text = this.desc.status;
-    var failureStatus = '';
-    if (text && text.length > 5) text = '';
-    status = status ? status : document.getElementById(this.statusId);
-
-    if (this.style === 'extra compact') {
-      failureStatus = this.desc.mandatory ? 'test-status-fail' :
-          'test-status-optional-fail';
-      if (this.desc.running) {
-        status.className = 'test-status-running';
-      } else if (this.desc.failures) {
-        status.className = failureStatus;
-      } else if (this.desc.timeouts) {
-        status.className = failureStatus;
-      } else if (this.desc.passes) {
-        status.className = 'test-status-pass';
-      } else {
-        status.className = 'test-status-none';
-      }
-    } else {
-      failureStatus = this.desc.mandatory ? 'cell-status-fail' :
-          'cell-status-normal';
-      if (this.desc.running) {
-        status.innerHTML = '&nbsp;...&nbsp;';
-        status.className = 'cell-status-running';
-      } else if (this.desc.failures) {
-        status.innerHTML = text || '&nbsp;Fail&nbsp;';
-        status.className = failureStatus;
-      } else if (this.desc.timeouts) {
-        status.innerHTML = text || '&nbsp;Fail&nbsp;';
-        status.className = failureStatus;
-      } else if (this.desc.passes) {
-        status.innerHTML = text || '&nbsp;Pass&nbsp;';
-        status.className = 'cell-status-pass';
-      } else {
-        status.innerHTML = ' ';
-        status.className = 'cell-status-normal';
-      }
-    }
-  };
-
-  this.selected = function() {
-    return true;
-  };
-
-  this.getElement = function() {
-    return document.getElementById(this.nameId).childNodes[0];
-  };
-}
-
-function TestList(style) {
-  var self = this;
-  var tests = [];
-
-  var SINGLE_WIDTH_CELL = 1;
-  var DOUBLE_WIDTH_CELL = 2;
-
-  this.style = style || '';
-
-  // returns array [row, column]
-  var getTableDimension = function() {
-    var lastCategory = '';
-    var cells = 0;
-    var rowLeft;
-
-    for (var i = 0; i < tests.length; ++i) {
-      if (lastCategory !== tests[i].desc.category) {
-        rowLeft = ITEM_IN_COLUMN - cells % ITEM_IN_COLUMN;
-        if (rowLeft < MIN_ROW_AT_THE_BOTTOM)
-          cells += rowLeft;
-        if (cells % ITEM_IN_COLUMN !== 0)
-          cells += CATEGORY_SPACE;
-        cells++;
-        lastCategory = tests[i].desc.category;
-      } else if (cells % ITEM_IN_COLUMN === 0) {
-        cells++;  // category (continued)
-      }
-      cells++;
-    }
-
-    return [Math.min(cells, ITEM_IN_COLUMN),
-            Math.floor((cells + ITEM_IN_COLUMN - 1) / ITEM_IN_COLUMN)];
-  };
-
-  var createExtraCompactTable = function(div, table) {
-    var lastCategory = null;
-    var totalCells = 0;
-    var totalTests = 0;
-    var rowsRemaining = 0;
-    var layoutColumnSpan = [];
-    var rows = [];
-    var j = 0;
-
-    var createEmptyCells = function(row) {
-      if (table.childNodes.length <= row) {
-        table.appendChild(createElement('tr'));
-      }
-
-      var tr = table.childNodes[row];
-      var elems = [
-        createElement('td', null, 'test-status-none'),
-        createElement('td', null, 'cell-name', '&nbsp;')
-      ];
-      tr.appendChild(elems[0]);
-      tr.appendChild(elems[1]);
-      return elems;
-    };
-
-    var createTestCells = function(testIndex, row, test) {
-      var cells = createEmptyCells(row);
-      tests[testIndex].createElement(cells[1], cells[0]);
-    };
-
-    var createCategoryCell = function(row, categoryName) {
-      if (table.childNodes.length <= row) {
-        table.appendChild(createElement('tr'));
-      }
-
-      var tr = table.childNodes[row];
-      var elem = createElement('td', null, 'cell-name', '&nbsp;');
-      tr.appendChild(elem);
-
-      (new Category(categoryName)).setDoubleElement(elem);
-    };
-
-    for (var i = 0; i < tests.length; ++i) {
-      var currCategory = tests[i].desc.category;
-
-      if (lastCategory !== currCategory) {
-        rowsRemaining = ITEM_IN_COLUMN - totalCells % ITEM_IN_COLUMN;
-
-        if (rowsRemaining < MIN_ROW_AT_THE_BOTTOM) {
-          // Add a row for heading.
-          for (j = 0; j < rowsRemaining; ++j) {
-            createEmptyCells(totalCells % ITEM_IN_COLUMN);
-            totalCells += 1;
-          }
-        }
-
-        if (totalCells % ITEM_IN_COLUMN !== 0) {
-          // Add a row for extra space before heading, if in middle of column.
-          for (j = 0; j < CATEGORY_SPACE; ++j) {
-            createEmptyCells(totalCells % ITEM_IN_COLUMN);
-            totalCells += 1;
-          }
-        }
-
-        lastCategory = currCategory;
-        createCategoryCell(totalCells % ITEM_IN_COLUMN, lastCategory);
-        totalCells++;
-      } else if (totalCells % ITEM_IN_COLUMN === 0) {
-        // category (continued)
-        createCategoryCell(totalCells % ITEM_IN_COLUMN, lastCategory);
-        totalCells++;
-      }
-
-      createTestCells(totalTests, totalCells % ITEM_IN_COLUMN, lastCategory);
-      totalCells++;
-      totalTests++;
-    }
-
-    div.innerHTML = '';
-    div.appendChild(table);
-  };
-
-  this.addTest = function(desc) {
-    var test = new Test(desc, this.style);
-    tests.push(test);
-    return test;
-  };
-
-  this.generate = function(div) {
-    var table = createElement('div', null, 'compact-list');
-    var tr;
-    var dim = getTableDimension();
-    var lastCategory = '';
-    var row;
-    var column;
-
-    if (self.style === 'extra compact') {
-      createExtraCompactTable(div, table);
-    } else {
-      for (row = 0; row < dim[0]; ++row) {
-        tr = createElement('div');
-        table.appendChild(tr);
-        for (column = 0; column < dim[1]; ++column) {
-          tr.appendChild(createElement('span', null, 'cell-name', '&nbsp;'));
-          tr.appendChild(createElement('span', null, 'cell-divider'));
-          tr.appendChild(createElement('span', null, 'cell-status-normal'));
-        }
-      }
-
-      div.innerHTML = '';
-      div.appendChild(table);
-
-      row = column = 0;
-
-      for (var i = 0; i < tests.length; ++i) {
-        if (lastCategory !== tests[i].desc.category) {
-          if (ITEM_IN_COLUMN - row <= MIN_ROW_AT_THE_BOTTOM) {
-            row = 0;
-            column++;
-          }
-
-          if (row % ITEM_IN_COLUMN !== 0)
-            row += CATEGORY_SPACE;
-
-          lastCategory = tests[i].desc.category;
-          (new Category(lastCategory)).setElement(
-              table.childNodes[row].childNodes[column * 3],
-              table.childNodes[row].childNodes[column * 3 + 2]);
-          row++;
-        } else if (row === 0) {
-          (new Category(lastCategory)).setElement(
-              table.childNodes[row].childNodes[column * 3],
-              table.childNodes[row].childNodes[column * 3 + 2]);
-          row++;
-        }
-
-        tests[i].createElement(
-            table.childNodes[row].childNodes[column * 3],
-            table.childNodes[row].childNodes[column * 3 + 2]);
-        row++;
-
-        if (row === ITEM_IN_COLUMN) {
-          row = 0;
-          column++;
-        }
-      }
-    }
-  };
-
-  this.getTest = function(index) {
-    return tests[index];
-  };
-
-  this.anySelected = function() {
-    return tests.length !== 0;
-  };
-};
-
-window.createCompactTestList = function(style) {
-  return new TestList(style);
-};
-
-})();
-
-// js/harness/compactTestList-20150612143746.js end
-
-// js/harness/compactTestView-20150612143746.js begin
-
-var compactTestView = (function() {
-
-function CompactTestView(fields, style) {
-  var self = this;
-  this.divId = 'testview';
-  this.testCount = 0;
-
-  this.initialize = function() {
-    this.testList = createCompactTestList(style);
-
-    this.addSwitch('Loop: ', 'loop');
-    this.addSwitch('Stop on failure: ', 'stoponfailure');
-    this.addSwitch('Log: ', 'logging');
-    this.addSwitch('WebM/VP9 (2015/tip only): ', 'enablewebm');
-
-    this.addCommand('Run All', 'run-selected', 'Run all tests in order.',
-        function(e) {
-      if (self.onrunselected)
-        self.onrunselected.call(self, e);
-    });
-
-    this.addLink('Links', 'links.html');
-    this.addLink('Instructions', 'instructions.html');
-    this.addLink('Changelog', 'main.html');
-    this.addLink('Download', 'download-20150612143746.tar.gz');
-
-    this.addTestSuites(testTypes);
-  };
-
-  this.addTest = function(desc) {
-    return this.testList.addTest(desc);
-  };
-
-  this.generate = function() {
-    CompactTestView.prototype.generate.call(this);
-    // document.getElementById('run-selected').focus();
-
-    var USAGE = 'Use &uarr;&darr;&rarr;&larr; to move around, ' +
-        'use ENTER to select.';
-    document.getElementById('usage').innerHTML = USAGE;
-    // document.getElementById('run-selected').focus();
-  };
-
-  this.getTest = function(index) {
-    return this.testList.getTest(index);
-  };
-
-  this.finishedOneTest = function() {
-    ++this.testCount;
-    document.getElementById('finish-count').innerHTML =
-        this.testCount === 1 ? this.testCount + ' test finished' :
-                               this.testCount + ' tests finished';
-  };
-
-  this.anySelected = function() {
-    return this.testList.anySelected();
-  };
-
-  this.initialize();
-};
-
-CompactTestView.prototype = TestView.create();
-CompactTestView.prototype.constructor = CompactTestView;
-
-return {
-  create: function(mseSpec, fields, style) {
-    CompactTestView.prototype = TestView.create(mseSpec);
-    CompactTestView.prototype.constructor = CompactTestView;
-    return new CompactTestView(fields, style);
-  }
-};
-
-})();
-
-// js/harness/compactTestView-20150612143746.js end
-
-// js/lib/mse/2013/mediaSourcePortability-20150612143746.js begin
-
-/*
- * without Webkit prefix and MediaSource ver 0.5 with or without Webkit prefix.
- */
-function setupMsePortability() {
-  var dlog = function() {
-    var forward = window.dlog || console.log.bind(console);
-    forward.apply(this, arguments);
-  };
-
-  // Check if we have MSE 0.6 WITHOUT webkit prefix
-  if (window.MediaSource) {
-    window.MediaSource.prototype.version = 'MSE-live';
-    return;
-  }
-
-  // Check if we have MSE 0.6 WITH webkit prefix
-  if (window.WebKitMediaSource) {
-    window.MediaSource = window.WebKitMediaSource;
-    window.MediaSource.prototype.version = 'MSE-live-webkit';
-
-    var cou = window.URL.createObjectURL;
-    var creatingURL = false;
-    window.URL.createObjectURL = function(obj) {
-      if (!creatingURL) {
-        creatingURL = true;
-        var url = window.URL.createObjectURL(obj);
-        creatingURL = false;
-        return url;
-      }
-      return cou.call(this, obj);
-    };
-
-    var ael = window.MediaSource.prototype.addEventListener;
-    window.MediaSource.prototype.addEventListener = function(
-        type, listener, useCaptures) {
-      var re = /^source(open|close|ended)$/;
-      var match = re.exec(type);
-      if (match) {
-        ael.call(this, 'webkit' + type, listener, useCaptures);
-      } else {
-        ael.call(this, type, listener, useCaptures);
-      }
-    };
-
-    return;
-  }
-
-  var v = document.createElement('video');
-
-  // Do we have any forms of MSE 0.5?
-  // NOTE: We will only support MSE 0.5 with webkit prefix.
-  if (!v.webkitSourceAddId)
-    return;
-
-  function MediaSource() {
-    this.sourceBuffers = [];
-    this.activeSourceBuffers = this.sourceBuffers;
-    this.readyState = 'closed';
-
-    this.msWrapperVideo = null;
-    this.msWrapperDuration = NaN;
-    this.msWrapperSourceIdCount = 1;
-    this.msWrapperHandlers = {};
-    this.msWrapperAppended = false;
-
-    this.isWrapper = true;
-  }
-
-  MediaSource.prototype.version = 'MSE-v0.5-wrapped-webkit';
-  var missingFeature = 'Missing:';
-  if (!v.webkitSourceSetDuration)
-    missingFeature += ' webkitSourceSetDuration';
-  if (!v.webkitSourceTimestampOffset)
-    missingFeature += ' webkitSourceTimestampOffset';
-  if (missingFeature !== 'Missing:')
-    MediaSource.prototype.version += '(' + missingFeature + ')';
-
-  MediaSource.prototype.msWrapperHandler = function(name, evt) {
-    var handlers = this.msWrapperHandlers[name] || [];
-    dlog(4, 'In msWrapperHandler');
-    if (name === 'close') {
-      this.readyState = 'closed';
-      this.msWrapperDuration = NaN;
-    } else {
-      this.readyState = name;
-    }
-    for (var i = 0; i < handlers.length; i++) {
-      handlers[i].call(evt, evt);
-    }
-  };
-
-  MediaSource.prototype.attachTo = function(video) {
-    dlog(4, 'In msWrapperAttach');
-    var names = ['open', 'close', 'ended'];
-    for (var i = 0; i < names.length; i++) {
-      var h = this.msWrapperHandler.bind(this, names[i]);
-      video.addEventListener('webkitsource' + names[i], h);
-    }
-    this.msWrapperVideo = video;
-    var self = this;
-    video.addEventListener('durationchange', function() {
-      LOG(video.duration);
-      self.msWrapperDuration = video.duration;
-    });
-    video.src = video.webkitMediaSourceURL;
-  };
-
-  MediaSource.prototype.addSourceBuffer = function(type) {
-    if (!this.msWrapperVideo) throw 'Unattached';
-    var id = '' + this.msWrapperSourceIdCount;
-    this.msWrapperSourceIdCount += 1;
-    this.msWrapperVideo.webkitSourceAddId(id, type);
-
-    var buf = new SourceBuffer(this.msWrapperVideo, id);
-    this.sourceBuffers.push(buf);
-    return buf;
-  };
-
-  MediaSource.prototype.removeSourceBuffer = function(buf) {
-    for (var i = 0; i < this.sourceBuffers.length; ++i) {
-      if (buf === this.sourceBuffers[i]) {
-        this.msWrapperVideo.webkitSourceRemoveId(buf.msWrapperSourceId);
-        delete this.sourceBuffers.splice(i, 1)[0];
-        break;
-      }
-    }
-  };
-
-  MediaSource.prototype.endOfStream = function(opt_error) {
-    var v = this.msWrapperVideo;
-
-    // TODO: are these prefixed in M21?
-    var err;
-    if (opt_error === 'network') {
-      err = v.EOS_NETWORK_ERR;
-    } else if (opt_error === 'decode') {
-      err = v.EOS_DECODE_ERR;
-    } else if (!opt_error) {
-      err = v.EOS_NO_ERROR;
-    } else {
-      throw 'Unrecognized endOfStream error type: ' + opt_error;
-    }
-
-    v.webkitSourceEndOfStream(err);
-  };
-
-  // The 'setDuration' method of the media element is an extension to the
-  // MSE-v0.5 spec, which will be implemented on some devices.
-  // Calling this method is defined to have the same semantics as setting
-  // the duration property of the Media Source object in the current spec.
-  // Getting it is undefined, although clearly here we just return the last
-  // value that we set.
-  Object.defineProperty(MediaSource.prototype, 'duration', {
-    get: function() {
-      if (this.readyState === 'closed')
-        return NaN;
-      return this.msWrapperDuration;
-    },
-    set: function(duration) {
-      this.msWrapperDuration = duration;
-      if (this.msWrapperVideo.webkitSourceSetDuration) {
-        this.msWrapperVideo.webkitSourceSetDuration(duration);
-      } else {
-        dlog(1, 'webkitSourceSetDuration() missing (ignored)');
-      }
-    }
-  });
-
-  MediaSource.prototype.addEventListener = function(name, handler) {
-    var re = /^source(open|close|ended)$/;
-    var match = re.exec(name);
-    if (match && match[1]) {
-      name = match[1];
-      var l = this.msWrapperHandlers[name] || [];
-      l.push(handler);
-      this.msWrapperHandlers[name] = l;
-    } else {
-      throw 'Unrecognized event name: ' + name;
-    }
-  };
-
-  function SourceBuffer(video, id) {
-    this.msWrapperVideo = video;
-    this.msWrapperSourceId = id;
-    this.msWrapperTimestampOffset = 0;
-  }
-
-  function FakeSourceBufferedRanges() {
-    this.length = 0;
-  }
-
-  SourceBuffer.prototype.msWrapperGetBuffered = function() {
-    dlog(4, 'In msWrapperGetBuffered');
-
-    // Chrome 22 doesn't like calling sourceBuffered() before initialization
-    // segment gets appended.
-    if (!this.msWrapperAppended) return new FakeSourceBufferedRanges();
-
-    var v = this.msWrapperVideo;
-    var id = this.msWrapperSourceId;
-    return v.webkitSourceBuffered(id);
-  };
-
-  SourceBuffer.prototype.append = function(bytes) {
-    dlog(4, 'In append');
-    var v = this.msWrapperVideo;
-    var id = this.msWrapperSourceId;
-    v.webkitSourceAppend(id, bytes);
-    this.msWrapperAppended = true;
-  };
-
-  SourceBuffer.prototype.abort = function() {
-    dlog(4, 'In abort');
-    var v = this.msWrapperVideo;
-    var id = this.msWrapperSourceId;
-    v.webkitSourceAbort(id);
-  };
-
-  // The 'setTimestampOffset' method of the media element is an extension to the
-  // MSE-v0.5 spec, which will be implemented on some devices.
-  // Calling this method is defined to have the same semantics as setting the
-  // timestampOffset property of the Media Source object in the current spec.
-  Object.defineProperty(SourceBuffer.prototype, 'timestampOffset', {
-    get: function() { return this.msWrapperTimestampOffset; },
-    set: function(o) {
-      this.msWrapperTimestampOffset = o;
-      if (this.msWrapperVideo.webkitSourceTimestampOffset) {
-        this.msWrapperVideo.webkitSourceTimestampOffset(
-            this.msWrapperSourceId, o);
-      } else {
-        dlog(1, 'webkitSourceTimestampOffset() missing (ignored)');
-      }
-    }
-  });
-
-  Object.defineProperty(SourceBuffer.prototype, 'buffered', {
-    get: SourceBuffer.prototype.msWrapperGetBuffered
-  });
-
-  window.MediaSource = MediaSource;
-}
-
-// js/lib/mse/2013/mediaSourcePortability-20150612143746.js end
-
-// js/lib/eme/encryptedMediaPortability-20150612143746.js begin
-/* The code tries to wrapper the EME versions with or without webkit prefix */
-
-
-function prefixedAttributeName(obj, suffix, opt_preprefix) {
-  suffix = suffix.toLowerCase();
-  if (opt_preprefix === undefined) {
-    opt_preprefix = '';
-  }
-  for (var attr in obj) {
-    var lattr = attr.toLowerCase();
-    if (lattr.indexOf(opt_preprefix) == 0 &&
-        lattr.indexOf(suffix, lattr.length - suffix.length) != -1) {
-      return attr;
-    }
-  }
-  return null;
-}
-
-function normalizeAttribute(obj, suffix, opt_preprefix) {
-  if (opt_preprefix === undefined) {
-    opt_preprefix = '';
-  }
-
-  var attr = prefixedAttributeName(obj, suffix, opt_preprefix);
-  if (attr) {
-    obj[opt_preprefix + suffix] = obj[attr];
-  }
-}
-
-function stringToArray(s) {
-  var array = new Uint8Array(new ArrayBuffer(s.length));
-  for (var i = 0; i < s.length; i++) {
-    array[i] = s.charCodeAt(i);
-  }
-  return array;
-}
-
-
-function arrayToString(a) {
-  return String.fromCharCode.apply(String, a);
-}
-
-function parsePlayReadyKeyMessage(message) {
-  // message is UTF-16LE, let's throw out every second element.
-  var message_ascii = new Array();
-  for (var i = 0; i < message.length; i += 2) {
-    message_ascii.push(message[i]);
-  }
-
-  var str = String.fromCharCode.apply(String, message_ascii);
-  var doc = (new DOMParser()).parseFromString(str, 'text/xml');
-  return stringToArray(
-      atob(doc.childNodes[0].childNodes[0].childNodes[0].childNodes[0].data));
-}
-
-/**
- * Extracts the BMFF Clear Key ID from the init data of the segment.
- * @param {ArrayBuffer} initData Init data for the media segment.
- * @return {ArrayBuffer} Returns the BMFF ClearKey ID if found, otherwise the
- *     initData itself.
- */
-function extractBMFFClearKeyID(initData) {
-  // Accessing the Uint8Array's underlying ArrayBuffer is impossible, so we
-  // copy it to a new one for parsing.
-  var abuf = new ArrayBuffer(initData.length);
-  var view = new Uint8Array(abuf);
-  view.set(initData);
-
-  var dv = new DataView(abuf);
-  var pos = 0;
-  while (pos < abuf.byteLength) {
-    var box_size = dv.getUint32(pos, false);
-    var type = dv.getUint32(pos + 4, false);
-
-    if (type != 0x70737368)
-      throw 'Box type ' + type.toString(16) + ' not equal to "pssh"';
-
-    // Scan for Clear Key header
-    if ((dv.getUint32(pos + 12, false) == 0x58147ec8) &&
-        (dv.getUint32(pos + 16, false) == 0x04234659) &&
-        (dv.getUint32(pos + 20, false) == 0x92e6f52c) &&
-        (dv.getUint32(pos + 24, false) == 0x5ce8c3cc)) {
-      var size = dv.getUint32(pos + 28, false);
-      if (size != 16) throw 'Unexpected KID size ' + size;
-      return new Uint8Array(abuf.slice(pos + 32, pos + 32 + size));
-    }
-
-    // Failing that, scan for Widevine protobuf header
-    if ((dv.getUint32(pos + 12, false) == 0xedef8ba9) &&
-        (dv.getUint32(pos + 16, false) == 0x79d64ace) &&
-        (dv.getUint32(pos + 20, false) == 0xa3c827dc) &&
-        (dv.getUint32(pos + 24, false) == 0xd51d21ed)) {
-      return new Uint8Array(abuf.slice(pos + 36, pos + 52));
-    }
-    pos += box_size;
-  }
-  // Couldn't find it, give up hope.
-  return initData;
-}
-
-function base64_encode(arr) {
-  var b64dict = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-  var b64pad = "=";
-
-  var i;
-  var hexStr = "";
-  for (i = 0; i < arr.length; i++) {
-    var hex = arr[i].toString(16);
-    hexStr += hex.length == 1 ? "0" + hex : hex;
-  }
-
-  var dest = "";
-  var c;
-  for (i = 0; i+3 <= hexStr.length; i+=3) {
-    c = parseInt(hexStr.substring(i, i+3), 16);
-    dest += b64dict.charAt(c >> 6) + b64dict.charAt(c & 63);
-  }
-  if (i+1 == hexStr.length) {
-    c = parseInt(hexStr.substring(i, i+1), 16);
-    dest += b64dict.charAt(c << 2);
-  } else if (i+2 == hexStr.length) {
-    c = parseInt(hexStr.substring(i, i+2), 16);
-    dest += b64dict.charAt(c >> 2) + b64dict.charAt((c & 3) << 4);
-  }
-  
-  while ((dest.length & 3) > 0) {
-    dest += b64pad;
-  }
-
-  return dest;
-}
-
-function b64tob64url(s) {
-  var b64urlStr = removePadding(s);
-  b64urlStr = b64urlStr.replace(/\+/g, "-");
-  b64urlStr = b64urlStr.replace(/\//g, "_");
-  return b64urlStr;
-}
-
-function removePadding(s) {
-  return s.replace(/\=/g, "");
-}
-
-function base64NoPadding_encode(arr) {
-  return removePadding(base64_encode(arr));
-}
-
-function base64url_encode(arr) {
-  return b64tob64url(base64_encode(arr));
-}
-
-(function() {
-  var dlog = function() {
-    var forward = window.dlog || console.log.bind(console);
-    forward.apply(this, arguments);
-  };
-
-  var proto = window.HTMLMediaElement.prototype;
-
-  if (proto.addKey || proto.setMediaKeys) {
-    return;  // Non-prefix version, needn't wrap.
-  }
-
-  if (!proto.webkitAddKey) {
-    dlog(1, 'EME is not available');
-    return;  // EME is not available at all.
-  }
-
-  proto.generateKeyRequest = function(keySystem, initData) {
-    return proto.webkitGenerateKeyRequest.call(this, keySystem, initData);
-  };
-
-  proto.addKey = function(keySystem, key, initData, sessionId) {
-    return proto.webkitAddKey.call(this, keySystem, key, initData, sessionId);
-  };
-
-  proto.cancelKeyRequest = function(keySystem, sessionId) {
-    return proto.webkitCancelKeyRequest.call(this, keySystem, sessionId);
-  };
-
-  var ael = proto.addEventListener;
-  var eventWrapper = function(listener, e) {
-    listener.call(this, e);
-  };
-
-  proto.addEventListener = function(type, listener, useCaptures) {
-    var re = /^(keyadded|keyerror|keymessage|needkey)$/;
-    var match = re.exec(type);
-    if (match) {
-      ael.call(this, 'webkit' + type, eventWrapper.bind(this, listener),
-               useCaptures);
-    } else {
-      ael.call(this, type, listener, useCaptures);
-    }
-  };
-})();
-
-// js/lib/eme/encryptedMediaPortability-20150612143746.js end
-
-// js/harness/test-20150612143746.js begin
-
-
-var BYPASS_CACHE = false;
-var XHR_TIMEOUT_LIMIT = 5000;
-
-(function() {
-
-window.testTypes = {
-  'conformance-test': {
-    name: 'MSE Conformance Tests',
-    supported: 'all',
-    title: 'Media Source and Encrypted Media Conformance Tests',
-    heading: 'MSE Conformance Tests'
-  },
-  'encryptedmedia-test': {
-    name: 'EME Conformance Tests',
-    supported: ['2015', 'tip'],
-    title: 'Encrypted Media Extensions Conformance Tests',
-    heading: 'EME Conformance Tests'
-   },
-  'performance-test': {
-    name: 'MSE Performance Tests',
-    supported: 'all',
-    title: 'Media Source and Encrypted Media Conformance Tests',
-    heading: 'MSE Performance Tests'
-  },
-  'endurance-test': {
-    name: 'MSE Endurance Tests',
-    supported: 'all',
-    title: 'Media Source and Encrypted Media Conformance Tests',
-    heading: 'MSE Endurance Tests'
-  },
-  'progressive-test': {
-    name: 'Progressive Tests',
-    supported: 'all',
-    title: 'HTML Media Element Conformance Tests',
-    heading: 'HTML Media Element Conformance Tests'
-  }
-};
-
-window.kDefaultTestType = 'conformance-test';
-
-window.currentTestType = null;
-window.recycleVideoTag = true;
-
-if (!window.LOG) {
-  window.LOG = console.log.bind(console);
-}
-
-var TestBase = {};
-
-TestBase.onsourceopen = function() {
-  this.log('default onsourceopen()');
-};
-
-TestBase.start = function(runner, video) {
-  this.log('Test started');
-};
-
-TestBase.teardown = function(mseSpec) {
-  if (this.video != null) {
-    this.video.removeAllEventListeners();
-    this.video.pause();
-    if (mseSpec && mseSpec !== '0.5') // For backwards compatibility.
-      window.URL.revokeObjectURL(this.video.src);
-    this.video.src = '';
-    if (recycleVideoTag)
-      this.video.parentNode.removeChild(this.video);
-  }
-  this.ms = null;
-  this.video = null;
-  this.runner = null;
-};
-
-TestBase.log = function() {
-  var args = Array.prototype.slice.call(arguments, 0);
-  args.splice(0, 0, this.desc + ': ');
-  LOG.apply(this, args);
-};
-
-TestBase.dump = function() {
-  if (this.video) {
-    this.log('video.currentTime =', this.video.currentTime);
-    this.log('video.readyState =', this.video.readyState);
-    this.log('video.networkState =', this.video.networkState);
-  }
-  if (this.ms) {
-    this.log('ms.sb count =', this.ms.sourceBuffers.length);
-    for (var i = 0; i < this.ms.sourceBuffers.length; ++i) {
-      if (this.ms.sourceBuffers[i].buffered) {
-        var buffered = this.ms.sourceBuffers[i].buffered;
-        this.log('sb' + i + '.buffered.length', buffered.length);
-        for (var j = 0; j < buffered.length; ++j) {
-          this.log('  ' + j + ': (' + buffered.start(j) + ', ' +
-                   buffered.end(j) + ')');
-        }
-      } else {
-        this.log('sb', i, 'invalid buffered range');
-      }
-    }
-  }
-};
-
-TestBase.timeout = 30000;
-
-window.createTest = function(name) {
-  var t = function() {};
-  t.prototype.__proto__ = TestBase;
-  t.prototype.desc = name;
-  t.prototype.running = false;
-  t.prototype.category = '';
-  t.prototype.mandatory = true;
-
-  return t;
-};
-
-window.createMSTest = function(name) {
-  var t = createTest(name);
-  t.prototype.start = function(runner, video) {
-    this.ms = new MediaSource();
-    this.ms.addEventListener('sourceopen', this.onsourceopen.bind(this));
-    if (this.ms.isWrapper)
-      this.ms.attachTo(video);
-    else
-      this.video.src = window.URL.createObjectURL(this.ms);
-    this.log('MS test started');
-  };
-  return t;
-};
-
-var ConformanceTestRunner = function(testSuite, testsMask, mseSpec) {
-  this.testView = null;
-  this.currentTest = null;
-  this.currentTestIdx = 0;
-  this.assertThrowsError = true;
-  this.XHRManager = createXHRManager(createLogger(this.log.bind(this)));
-  this.timeouts = createTimeoutManager(createLogger(this.log.bind(this)));
-  this.lastResult = 'pass';
-  this.mseSpec = mseSpec || '0.5';
-
-  if (testsMask) {
-    this.testList = [];
-    testsMask = util.resize(testsMask, testSuite.tests.length,
-                            testsMask.substr(-1));
-    for (var i = 0; i < testSuite.tests.length; ++i)
-      if (testsMask[i] === '1')
-        this.testList.push(testSuite.tests[i]);
-  } else {
-    this.testList = testSuite.tests;
-  }
-  this.fields = testSuite.fields;
-  this.info = testSuite.info;
-  this.viewType = testSuite.viewType;
-};
-
-ConformanceTestRunner.prototype.log = function() {
-  var args = Array.prototype.slice.call(arguments, 0);
-  args.splice(0, 0, 'ConformanceTestRunner: ');
-  LOG.apply(this, args);
-};
-
-ConformanceTestRunner.prototype.assert = function(cond, msg) {
-  if (!cond) {
-    ++this.testList[this.currentTestIdx].prototype.failures;
-    this.updateStatus();
-    this.error('Assert failed: ' + msg, false);
-  }
-};
-
-ConformanceTestRunner.prototype.checkException = function(testFunc, exceptionCode) {
-  try {
-    testFunc();
-    this.fail('Expect exception ' + exceptionCode);
-  } catch (err) {
-    this.checkEq(err.code, exceptionCode, 'Exception');
-  }
-};
-
-ConformanceTestRunner.prototype.check = function(condition, passMsg, failMsg) {
-  if (condition)
-    this.log(passMsg);
-  else
-    this.assert(false, failMsg);
-};
-
-ConformanceTestRunner.prototype.checkType = function(x, y, name) {
-  var t = typeof(x);
-  var result = t === y;
-  this.check(result, 'checkType passed: type of ' + name + ' is (' + t + ').',
-             'Type of ' + name + ' is (' + t + ') which should be (' + y + ')');
-};
-
-ConformanceTestRunner.prototype.checkEq = function(x, y, name) {
-  var result = (x == y) ||
-      (typeof(x) === 'number' && typeof(y) === 'number' &&
-       isNaN(x) && isNaN(y));
-  this.check(result, 'checkEq passed: ' + name + ' is (' + x + ').',
-             name + ' is (' + x + ') which should be (' + y + ')');
-};
-
-ConformanceTestRunner.prototype.checkNE = function(x, y, name) {
-  var result = (x != y) &&
-      !(typeof(x) === 'number' && typeof(y) === 'number' &&
-        isNaN(x) && isNaN(y));
-  this.check(result, 'checkNE passed: ' + name + ' is (' + x + ').',
-             name + ' is (' + x + ') which shouldn\'t.');
-};
-
-ConformanceTestRunner.prototype.checkApproxEq = function(x, y, name) {
-  var diff = Math.abs(x - y);
-  var eps = 0.5;
-  this.check(diff < eps, 'checkApproxEq passed: ' + name + ' is (' + x + ').',
-             name + ' is (' + x + ') which should between [' +
-                (y - eps) + ', ' + (y + eps) + ']');
-};
-
-ConformanceTestRunner.prototype.checkGr = function(x, y, name) {
-  this.check(x > y, 'checkGr passed: ' + name + ' is (' + x + ').',
-             name + ' is (' + x +
-                 ') which should be greater than (' + y + ')');
-};
-
-ConformanceTestRunner.prototype.checkGE = function(x, y, name) {
-  this.check(x >= y, 'checkGE passed: ' + name + ' is (' + x + ').',
-             name + ' is (' + x +
-                 ') which should be greater than or equal to (' + y + ')');
-};
-
-ConformanceTestRunner.prototype.checkLE = function(x, y, name) {
-  this.check(x <= y, 'checkLE passed: ' + name + ' is (' + x + ').',
-             name + ' is (' + x +
-                 ') which should be less than or equal to (' + y + ')');
-};
-
-ConformanceTestRunner.prototype.getControlContainer = function() {
-  // Override this function to anchor one to the DOM.
-  return document.createElement('div');
-};
-
-ConformanceTestRunner.prototype.getNewVideoTag = function() {
-  // Override this function to anchor one to the DOM.
-  return document.createElement('video');
-};
-
-ConformanceTestRunner.prototype.getOutputArea = function() {
-  // Override this function to anchor one to the DOM.
-  return document.createElement('div');
-};
-
-ConformanceTestRunner.prototype.updateStatus = function() {
-  this.testView.getTest(this.currentTestIdx).updateStatus();
-};
-
-ConformanceTestRunner.prototype.initialize = function() {
-  var self = this;
-  if (this.viewType === 'extra compact')
-    this.testView = compactTestView.create(this.mseSpec, this.fields,
-                                           this.viewType);
-  else if (this.viewType === 'compact')
-    this.testView = compactTestView.create(this.mseSpec, this.fields);
-  else
-    this.testView = fullTestView.create(this.mseSpec, this.fields);
-
-  this.testView.onrunselected = function() {
-    self.startTest(0, self.testList.length);
-  };
-
-  for (var i = 0; i < this.testList.length; ++i) {
-    this.testList[i].prototype.onclick = this.startTest.bind(this, i, 1);
-    this.testView.addTest(this.testList[i].prototype);
-  }
-
-  this.testView.generate(this.mseSpec);
-
-  document.getElementById('info').innerText = this.info;
-  this.log('Media Source and Encrypted Media Conformance Tests ' +
-           '(version 20150612143746-4K5xqupUzgiRyTYP)');
-
-  this.longestTimeRatio = -1;
-  this.longestTest = null;
-};
-
-ConformanceTestRunner.prototype.onfinished = function() {
-  this.log('Finished!');
-  if (this.longestTest && this.longestTimeRatio > 0) {
-    this.log('Longest test is ' + this.longestTest + ', it takes ' +
-             this.longestTimeRatio + ' of its timeout.');
-  }
-
-  var keepRunning = (!stoponfailure || this.lastResult === 'pass') &&
-      loop && (this.testView.anySelected() || this.numOfTestToRun === 1);
-  if (keepRunning) {
-    this.testToRun = this.numOfTestToRun;
-    this.currentTestIdx = this.startIndex;
-    this.startNextTest();
-  } else {
-    this.lastResult = 'pass';
-    this.getNewVideoTag();
-    this.log('All tests are completed');
-  }
-
-  this.sendTestReport(GetTestResults());
-};
-
-ConformanceTestRunner.prototype.sendTestReport = function(results) {
-  if (this.clientName) {
-    var xhr = new XMLHttpRequest();
-    var resultsURL = 'http://qual-e.appspot.com/api?command=save_result';
-    resultsURL += '&source=mse_eme_conformance';
-    resultsURL += '&testid=' + this.clientName + '_' + this.runStartTime;
-    xhr.open('POST', resultsURL);
-    xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
-    xhr.send(JSON.stringify(results));
-  }
-};
-
-ConformanceTestRunner.prototype.startTest = function(startIndex, numOfTestToRun) {
-  if (!this.currentTest) {
-    this.startIndex = startIndex;
-    this.numOfTestToRun = numOfTestToRun;
-    this.testToRun = numOfTestToRun;
-    this.currentTestIdx = startIndex;
-    this.startNextTest();
-    this.runStartTime = Date.now();
-  }
-};
-
-ConformanceTestRunner.prototype.startNextTest = function() {
-  UpdateStreamDef(Number(enablewebm));
-  if (this.numOfTestToRun != 1) {
-    while (this.testToRun > 0 &&
-           !this.testView.getTest(this.currentTestIdx).selected()) {
-      this.testToRun--;
-      this.currentTestIdx++;
-    }
-  }
-
-  if (this.testToRun <= 0 || (stoponfailure && this.lastResult != 'pass')) {
-    this.onfinished();
-    return;
-  }
-
-  this.currentTest = new this.testList[this.currentTestIdx];
-
-  this.log('**** Starting test ' + (this.currentTest.index + 1) + ':' +
-           this.currentTest.desc + ' with timeout ' +
-           this.currentTest.timeout);
-  this.timeouts.setTimeout(this.timeout.bind(this), this.currentTest.timeout);
-
-  this.testList[this.currentTestIdx].prototype.testName = this.currentTest.desc;
-  this.testList[this.currentTestIdx].prototype.running = true;
-
-  this.updateStatus();
-
-  this.startTime = Date.now();
-  this.currentTest.runner = this;
-  this.currentTest.video = this.getNewVideoTag();
-
-  var addEventListener = this.currentTest.video.addEventListener;
-  this.currentTest.video.eventsAdded = [];
-  this.currentTest.video.addEventListener =
-      function(type, listener, useCapture) {
-        addEventListener.call(this, type, listener, useCapture);
-        this.eventsAdded.push([type, listener]);
-      };
-  this.currentTest.video.removeAllEventListeners = function() {
-    for (var i = 0; i < this.eventsAdded.length; ++i) {
-      this.removeEventListener(this.eventsAdded[i][0],
-                               this.eventsAdded[i][1]);
-    }
-  };
-
-  this.currentTest.start(this, this.currentTest.video);
-};
-
-ConformanceTestRunner.prototype.succeed = function() {
-  this.lastResult = 'pass';
-  ++this.testList[this.currentTestIdx].prototype.passes;
-  this.updateStatus();
-  this.log('**** Test ' + this.currentTest.desc + ' succeeded.');
-  this.teardownCurrentTest(false);
-};
-
-ConformanceTestRunner.prototype.error = function(msg, isTimeout) {
-  this.lastResult = isTimeout ? 'timeout' : 'failure';
-  var test = this.currentTest;
-  this.log(msg);
-  var stack = '';
-
-  try {
-    test.dump();
-  } catch (e) {
-  }
-
-  try {
-    var x = y.z.u.v.w;
-  } catch (e) {
-    if (e && e.stack)
-    {
-      this.log(e.stack);
-      stack = e.stack;
-    }
-  }
-
-  this.testList[this.currentTestIdx].prototype.lastError = {
-    message: msg,
-    callStack: stack
-  };
-
-  this.teardownCurrentTest(isTimeout);
-  if (this.assertThrowsError) throw msg;
-};
-
-ConformanceTestRunner.prototype.fail = function(msg) {
-  ++this.testList[this.currentTestIdx].prototype.failures;
-  this.updateStatus();
-  this.error('Test ' + this.currentTest.desc + ' FAILED: ' + msg, false);
-};
-
-ConformanceTestRunner.prototype.timeout = function() {
-  var isTestTimedOut = false;
-  var currentTime = new Date().getTime();
-  var testTime = currentTime - this.startTime;
-
-  // Wait longer if we have still running xhr requests.
-  // Don't consider data transfer time as part of the timeout.
-  if (this.XHRManager) {
-    if (this.XHRManager.hasActiveRequests()) {
-      var timeSinceLastUpdate = currentTime - this.XHRManager.getLastUpdate();
-      if (timeSinceLastUpdate < XHR_TIMEOUT_LIMIT) {
-        this.timeouts.setTimeout(this.timeout.bind(this), 1000);
-      } else {
-        isTestTimedOut = true;
-      }
-    } else if (this.XHRManager.totalRequestDuration > 0) {
-      var testTimeLimit = this.currentTest.timeout + this.XHRManager.totalRequestDuration;
-      if (testTime < testTimeLimit) {
-        this.timeouts.setTimeout(this.timeout.bind(this),
-                                 this.XHRManager.totalRequestDuration);
-      } else {
-        isTestTimedOut = true;
-      }
-    }
-  } else {
-    isTestTimedOut = true;
-  }
-
-  if (isTestTimedOut) {
-    ++this.testList[this.currentTestIdx].prototype.timeouts;
-    this.updateStatus();
-    this.error('Test ' + this.currentTest.desc + ' TIMED OUT!', true);
-  }
-};
-
-ConformanceTestRunner.prototype.teardownCurrentTest = function(isTimeout) {
-  if (!isTimeout) {
-    var time = Date.now() - this.startTime;
-    var ratio = time / this.currentTest.timeout;
-    if (ratio >= this.longestTimeRatio) {
-      this.longestTimeRatio = ratio;
-      this.longestTest = this.currentTest.desc;
-      this.log('New longest test ' + this.currentTest.desc +
-               ' with timeout ' + this.currentTest.timeout + ' takes ' + time);
-    }
-  }
-
-  this.testList[this.currentTestIdx].prototype.running = false;
-  this.updateStatus();
-
-  this.timeouts.clearAll();
-  this.XHRManager.abortAll();
-  this.testView.finishedOneTest();
-  this.currentTest.teardown(this.mseSpec);
-  if (this.currentTest.ms &&
-      !this.currentTest.ms.isWrapper &&
-      this.currentTest &&
-      this.currentTest.video &&
-      this.currentTest.video.src) {
-    if (this.mseSpec !== '0.5')
-      window.URL.revokeObjectURL(this.currentTest.video.src);
-    this.currentTest.video.src = '';
-  }
-  this.currentTest = null;
-  this.testToRun--;
-  this.currentTestIdx++;
-  window.setTimeout(this.startNextTest.bind(this), 1);
-};
-
-window.TestBase = TestBase;
-window.ConformanceTestRunner = ConformanceTestRunner;
-
-window.GetTestResults = function(testStartId, testEndId) {
-  testStartId = testStartId || 0;
-  testEndId = testEndId || window.globalRunner.testList.length;
-  var results = [];
-
-  for (var i = testStartId; i < testEndId; ++i) {
-    if (window.globalRunner.testList[i]) {
-      var newResult = {
-        id: i,
-        name: window.globalRunner.testList[i].prototype.testName,
-        passed: false,
-        passes: 0,
-        error: ''
-      };
-
-      if (window.globalRunner.testList[i].prototype.passes > 0) {
-        newResult.passed = true;
-        newResult.passes = window.globalRunner.testList[i].prototype.passes;
-      }
-      else {
-        newResult.passed = false;
-        newResult.error = window.globalRunner.testList[i].prototype.lastError;
-      }
-      results.push(newResult);
-    }
-  }
-
-  return results;
-};
-
-
-window.createSimpleTest = function() {
-  window.ms = new MediaSource;
-  ms.addEventListener('sourceopen', function() {
-    window.vsrc = ms.addSourceBuffer(StreamDef.VideoType);
-    window.asrc = ms.addSourceBuffer(StreamDef.AudioType);
-    console.log('Objects has been created:\n' +
-                'They are video, ms, logger, XHMManager, timeouts, ' +
-                'vchain, vsrc, achain, asrc');
-  });
-  window.video = document.createElement('video');
-  window.logger = createLogger();
-  window.XHRManager = createXHRManager(logger);
-  window.timeouts = createTimeoutManager(logger);
-  video.src = window.URL.createObjectURL(ms);
-  window.vchain = new ResetInit(new FileSource(
-      'media/car-20120827-85.mp4', XHRManager, timeouts));
-  window.achain = new ResetInit(new FileSource(
-      'media/car-20120827-8b.mp4', XHRManager, timeouts));
-};
-
-})();
-
-// js/harness/test-20150612143746.js end
-
-// js/lib/mse/2013/msutil-20150612143746.js begin
-
-(function() {
-
-var DLOG_LEVEL = 3;
-
-// Log a debug message. Only logs if the given level is less than the current
-// value of the global variable DLOG_LEVEL.
-window.dlog = function(level) {
-  if (typeof(level) !== 'number')
-    throw 'level has to be an non-negative integer!';
-  // Comment this to prevent debug output
-  if (arguments.length > 1 && level <= DLOG_LEVEL) {
-    var args = [];
-    for (var i = 1; i < arguments.length; ++i)
-      args.push(arguments[i]);
-    if (window.LOG)
-      window.LOG.apply(null, args);
-    else
-      console.log(args);
-  }
-};
-
-var ensureUID = (function() {
-  var uid = 0;
-
-  return function(sb) {
-    if (!sb.uid) sb.uid = uid++;
-  };
-})();
-
-var elementInBody = function(element) {
-  while (element && element !== document.body)
-    element = element.parentNode;
-  return Boolean(element);
-};
-
-// A version of 'SourceBuffer.append()' that automatically handles EOS
-// (indicated by the 'null' values. Returns true if append succeeded,
-// false if EOS.
-window.safeAppend = function(sb, buf) {
-  ensureUID(sb);
-
-  if (!buf)
-    dlog(2, 'EOS appended to ' + sb.uid);
-  else
-    sb.append(buf);
-
-  return Boolean(buf);
-};
-
-// Convert a 4-byte array into a signed 32-bit int.
-function btoi(data, offset) {
-  offset = offset || 0;
-  var result = data[offset] >>> 0;
-  result = (result << 8) + (data[offset + 1] >>> 0);
-  result = (result << 8) + (data[offset + 2] >>> 0);
-  result = (result << 8) + (data[offset + 3] >>> 0);
-  return result;
-}
-
-// Convert a 4-byte array into a fourcc.
-function btofourcc(data, offset) {
-  offset = offset || 0;
-  return String.fromCharCode(data[offset], data[offset + 1],
-                             data[offset + 2], data[offset + 3]);
-}
-
-// Convert a signed 32-bit int into a 4-byte array.
-function itob(value) {
-  return [value >>> 24, (value >>> 16) & 0xff, (value >>> 8) & 0xff,
-         value & 0xff];
-}
-
-// Return the offset of sidx box
-function getSIDXOffset(data) {
-  var length = data.length;
-  var pos = 0;
-
-  while (pos + 8 <= length) {
-    var size = [];
-
-    for (var i = 0; i < 4; ++i)
-      size.push(data[pos + i]);
-
-    size = btoi(size);
-    if (size < 8) throw 'Unexpectedly small size';
-    if (pos + size >= data.length) break;
-
-    if (btofourcc(data, pos + 4) === 'sidx')
-      return pos;
-
-    pos += size;
-  }
-
-  throw 'Cannot find sidx box in first ' + data.length + ' bytes of file';
-}
-
-// Given a buffer contains the first 32k of a file, return a list of tables
-// containing 'time', 'duration', 'offset', and 'size' properties for each
-// subsegment.
-function parseSIDX(data) {
-  var sidxStartBytes = getSIDXOffset(data);
-  var currPos = sidxStartBytes;
-
-  function read(bytes) {
-    if (currPos + bytes > data.length) throw 'sidx box is incomplete.';
-    var result = [];
-    for (var i = 0; i < bytes; ++i) result.push(data[currPos + i]);
-    currPos += bytes;
-    return result;
-  }
-
-  var size = btoi(read(4));
-  var sidxEnd = sidxStartBytes + size;
-  var boxType = read(4);
-  boxType = btofourcc(boxType);
-  if (boxType !== 'sidx') throw 'Unrecognized box type ' + boxType;
-
-  var verFlags = btoi(read(4));
-  var refId = read(4);
-  var timescale = btoi(read(4));
-
-  var earliestPts, offset;
-  if (verFlags === 0) {
-    earliestPts = btoi(read(4));
-    offset = btoi(read(4));
-  } else {
-    dlog(2, 'Warning: may be truncating sidx values');
-    read(4);
-    earliestPts = btoi(read(4));
-    read(4);
-    offset = btoi(read(4));
-  }
-  offset = offset + sidxEnd;
-
-  var count = btoi(read(4));
-  var time = earliestPts;
-
-  var res = [];
-  for (var i = 0; i < count; ++i) {
-    var size = btoi(read(4));
-    var duration = btoi(read(4));
-    var sapStuff = read(4);
-    res.push({
-      time: time / timescale,
-      duration: duration / timescale,
-      offset: offset,
-      size: size
-    });
-    time = time + duration;
-    offset = offset + size;
-  }
-  if (currPos !== sidxEnd) throw 'Bad end point' + currPos + sidxEnd;
-  return res;
-}
-
-// Given a BufferedRange object, find the one that contains the given time
-// 't'. Returns the end time of the buffered range. If a suitable buffered
-// range is not found, returns 'null'.
-function findBufferedRangeEndForTime(sb, t) {
-  var buf = sb.buffered;
-  ensureUID(sb);
-  for (var i = 0; i < buf.length; ++i) {
-    var s = buf.start(i), e = buf.end(i);
-    dlog(4, 'findBuf: uid=' + sb.uid + ' index=' + i + ' time=' + t +
-         ' start=' + s + ' end=' + e);
-    if (t >= s && t <= e)
-      return e;
-  }
-
-  return null;
-}
-
-// This part defines the... source, for the, erm... media. But it's not the
-// Media Source. No. No way.
-//
-// Let's call it "source chain" instead.
-//
-// At the end of a source chain is a file source. File sources implement the
-// following methods:
-//
-//  init(t, cb): Gets the (cached) initialization segment buffer for t.
-//  Current position is not affected. If cb is null, it will return the init
-//  segment, otherwise it will call cb with the asynchronously received init
-//  segment. If will throw is init segment is not ready and cb is null.
-//
-//  seek(t): Sets the maximum time of the next segment to be appended. Will
-//  likely round down to the nearest segment start time. (To reset a source
-//  after EOF, seek to 0.)
-//
-//  pull(cb): Call the cb with the next media segment.
-//  return value of EOS('null') indicates that the chain has been exhausted.
-//
-// Most source chain elements will return entire media segments, and many will
-// expect incoming data to begin on a media segment boundary. Those elements
-// that either do not require this property, or return output that doesn't
-// follow it, will be noted.
-//
-// All source chain elements will forward messages that are not handled to the
-// upstream element until they reach the file source.
-
-// Produces a FileSource table.
-window.FileSource = function(path, xhrManager, timeoutManager,
-                             startIndex, endIndex) {
-  this.path = path;
-  this.startIndex = startIndex;
-  this.endIndex = endIndex;
-  this.segs = null;
-  this.segIdx = 0;
-  this.initBuf = null;
-
-  this.init = function(t, cb) {
-    if (!cb) {
-      if (!this.initBuf)
-        throw 'Calling init synchronusly when the init seg is not ready';
-      return this.initBuf;
-    }
-    self = this;
-    if (this.initBuf) {
-      timeoutManager.setTimeout(cb.bind(this, this.initBuf), 1);
-    } else {
-      var self = this;
-      var xhr = xhrManager.createRequest(this.path, function(e) {
-        self.segs = parseSIDX(this.getResponseData());
-
-        self.startIndex = self.startIndex || 0;
-        self.endIndex = self.endIndex || self.segs.length - 1;
-        self.endIndex = Math.min(self.endIndex, self.segs.length - 1);
-        self.startIndex = Math.min(self.startIndex, self.endIndex);
-        self.segIdx = self.startIndex;
-
-        xhr = xhrManager.createRequest(self.path, function(e) {
-          self.initBuf = this.getResponseData();
-          cb.call(self, self.initBuf);
-        }, 0, self.segs[0].offset);
-        xhr.send();
-      }, 0, 32 * 1024);
-      xhr.send();
-    }
-  };
-
-  this.seek = function(t, sb) {
-    if (!this.initBuf)
-      throw 'Seek must be called after init';
-
-    if (sb)
-      sb.abort();
-    else if (t !== 0)
-      throw 'You can only seek to the beginning without providing a sb';
-
-    t += this.segs[this.startIndex].time;
-    var i = this.startIndex;
-    while (i <= this.endIndex && this.segs[i].time <= t)
-      ++i;
-    this.segIdx = i - 1;
-    dlog(2, 'Seeking to segment index=' + this.segIdx + ' time=' + t +
-         ' start=' + this.segs[this.segIdx].time +
-         ' length=' + this.segs[this.segIdx].duration);
-  };
-
-  this.pull = function(cb) {
-    if (this.segIdx > this.endIndex) {
-      timeoutManager.setTimeout(cb.bind(this, null), 1);
-      return;
-    }
-    var seg = this.segs[this.segIdx];
-    ++this.segIdx;
-    var self = this;
-    var xhr = xhrManager.createRequest(this.path, function(e) {
-      cb.call(self, this.getResponseData());
-    }, seg.offset, seg.size);
-    xhr.send();
-  };
-  this.duration = function() {
-    var last = this.segs[this.segs.length - 1];
-    return last.time + last.duration;
-  };
-  this.currSegDuration = function() {
-    if (!this.segs || !this.segs[this.segIdx])
-      return 0;
-    return this.segs[this.segIdx].duration;
-  };
-};
-
-function attachChain(downstream, upstream) {
-  downstream.upstream = upstream;
-  downstream.init = function(t, cb) {
-    return upstream.init(t, cb);
-  };
-  downstream.seek = function(t, sb) {
-    return upstream.seek(t, sb);
-  };
-  downstream.pull = function(cb) {
-    return upstream.pull(cb);
-  };
-  downstream.duration = function() {
-    return upstream.duration();
-  };
-  downstream.currSegDuration = function() {
-    return upstream.currSegDuration();
-  };
-}
-
-window.ResetInit = function(upstream) {
-  this.initSent = false;
-  attachChain(this, upstream);
-
-  this.init = function(t, cb) {
-    this.initSent = true;
-    return this.upstream.init(t, cb);
-  };
-
-  this.seek = function(t, sb) {
-    this.initSent = false;
-    return this.upstream.seek(t, sb);
-  };
-
-  this.pull = function(cb) {
-    if (!this.initSent) {
-      this.initSent = true;
-      this.upstream.init(0, function(initSeg) {
-        cb(initSeg);
-      });
-      return;
-    }
-    var self = this;
-    this.upstream.pull(function(rsp) {
-      if (!rsp)
-        self.initSent = false;
-      cb(rsp);
-    });
-  };
-};
-
-// This function _blindly_ parses the mdhd header in the segment to find the
-// timescale. It doesn't take any box hierarchy into account.
-function parseTimeScale(data) {
-  for (var i = 0; i < data.length - 3; ++i) {
-    if (btofourcc(data, i) !== 'mdhd')
-      continue;
-    var off = i + 16;
-    if (data[i + 4] != 0)
-      off = i + 28;
-
-    return btoi(data, off);
-  }
-
-  throw 'Failed to find mdhd box in the segment provided';
-}
-
-function replaceTFDT(data, tfdt) {
-  for (var i = 0; i < data.length - 3; ++i) {
-    if (btofourcc(data, i) !== 'tfdt')
-      continue;
-    tfdt = itob(tfdt);  // convert it into array
-    var off = i + 8;
-    if (data[i + 4] === 0) {
-      data[off] = tfdt[0];
-      data[off + 1] = tfdt[1];
-      data[off + 2] = tfdt[2];
-      data[off + 3] = tfdt[3];
-    } else {
-      data[off] = 0;
-      data[off + 1] = 0;
-      data[off + 2] = 0;
-      data[off + 3] = 0;
-      data[off + 4] = tfdt[0];
-      data[off + 5] = tfdt[1];
-      data[off + 6] = tfdt[2];
-      data[off + 7] = tfdt[3];
-    }
-
-    return true;
-  }
-  // the init segment doesn't have tfdt box.
-  return false;
-}
-
-// It will repeat a normal stream to turn it into an infinite stream.
-// This type of stream cannot be seeked.
-window.InfiniteStream = function(upstream) {
-  this.upstream = upstream;
-  this.timescale = null;
-  this.elapsed = 0;
-  attachChain(this, upstream);
-
-  this.seek = function(t, sb) {
-    throw 'InfiniteStream cannot be seeked';
-  };
-
-  this.pull = function(cb) {
-    var self = this;
-    var currSegDuration = self.upstream.currSegDuration();
-    function onPull(buf) {
-      if (!buf) {
-        self.upstream.seek(0, null);
-        self.upstream.pull(onPull);
-        return;
-      }
-      if (!self.timescale) {
-        var initBuf = self.upstream.init(0);
-        self.timescale = parseTimeScale(initBuf);
-      }
-      var tfdt = Math.floor(self.timescale * self.elapsed);
-      if (tfdt === 1) tfdt = 0;
-      dlog(3, 'TFDT: time=' + self.elapsed + ' timescale=' + self.timescale +
-           ' tfdt=' + tfdt);
-      if (replaceTFDT(buf, tfdt))
-        self.elapsed = self.elapsed + currSegDuration;
-      cb(buf);
-    }
-    this.upstream.pull(onPull);
-  };
-
-  return this;
-};
-
-// Pull 'len' bytes from upstream chain element 'elem'. 'cache'
-// is a temporary buffer of bytes left over from the last pull.
-//
-// This function will send exactly 0 or 1 pull messages upstream. If 'len' is
-// greater than the number of bytes in the combined values of 'cache' and the
-// pulled buffer, it will be capped to the available bytes. This avoids a
-// number of nasty edge cases.
-//
-// Returns 'rsp, newCache'. 'newCache' should be passed as 'cache' to the
-// next invocation.
-function pullBytes(elem, len, cache, cb) {
-  if (!cache) {
-    // Always return EOS if cache is EOS, the caller should call seek before
-    // reusing the source chain.
-    cb(cache, null);
-    return;
-  }
-
-  if (len <= cache.length) {
-    var buf = cache.subarray(0, len);
-    cache = cache.subarray(len);
-    cb(buf, cache);
-    return;
-  }
-
-  elem.pull(function(buf) {
-    if (!buf) {  // EOS
-      cb(cache, buf);
-      return;
-    }
-    var newCache = new Uint8Array(new ArrayBuffer(cache.length + buf.length));
-    newCache.set(cache);
-    newCache.set(buf, cache.length);
-    cache = newCache;
-
-    if (cache.length <= len) {
-      cb(cache, new Uint8Array(new ArrayBuffer(0)));
-    } else {
-      buf = cache.subarray(0, len);
-      cache = cache.subarray(len);
-      cb(buf, cache);
-    }
-  });
-}
-
-window.FixedAppendSize = function(upstream, size) {
-  this.cache = new Uint8Array(new ArrayBuffer(0));
-  attachChain(this, upstream);
-  this.appendSize = function() {
-    return size || 512 * 1024;
-  };
-  this.seek = function(t, sb) {
-    this.cache = new Uint8Array(new ArrayBuffer(0));
-    return this.upstream.seek(t, sb);
-  };
-  this.pull = function(cb) {
-    var len = this.appendSize();
-    var self = this;
-    pullBytes(this.upstream, len, this.cache, function(buf, cache) {
-      self.cache = cache;
-      cb(buf);
-    });
-  };
-};
-
-window.RandomAppendSize = function(upstream, min, max) {
-  FixedAppendSize.apply(this, arguments);
-  this.appendSize = function() {
-    min = min || 100;
-    max = max || 10000;
-    return Math.floor(Math.random() * (max - min + 1) + min);
-  };
-};
-
-window.RandomAppendSize.prototype = new window.FixedAppendSize;
-window.RandomAppendSize.prototype.constructor = window.RandomAppendSize;
-
-// This function appends the init segment to media source
-window.appendInit = function(mp, sb, chain, t, cb) {
-  chain.init(t, function(initSeg) {
-    sb.append(initSeg);
-    cb();
-  });
-};
-
-// This is a simple append loop. It pulls data from 'chain' and appends it to
-// 'sb' until the end of the buffered range contains time 't'.
-// It starts from the current playback location.
-window.appendUntil = function(timeoutManager, mp, sb, chain, t, cb) {
-  if (!elementInBody(mp)) {
-    cb();
-      return;
-  }
-
-  var started = sb.buffered.length !== 0;
-  var current = mp.currentTime;
-  var bufferedEnd = findBufferedRangeEndForTime(sb, current);
-
-  if (bufferedEnd) {
-    bufferedEnd = bufferedEnd + 0.1;
-  } else {
-    bufferedEnd = 0;
-    if (started) {
-      chain.seek(0, sb);
-    }
-  }
-
-  (function loop(buffer) {
-    if (!elementInBody(mp)) {
-      cb();
-      return;
-    }
-    if (buffer) {
-      if (!safeAppend(sb, buffer)) {
-        cb();
-        return;
-      }
-      bufferedEnd = findBufferedRangeEndForTime(sb, bufferedEnd);
-      if (bufferedEnd) {
-        bufferedEnd = bufferedEnd + 0.1;
-      } else {
-        bufferedEnd = 0;
-      }
-      timeoutManager.setTimeout(loop, 0);
-    } else {
-      if (t >= bufferedEnd && !mp.error)
-        chain.pull(loop);
-      else
-        cb();
-    }
-  })();
-};
-
-// This is a simple append loop. It pulls data from 'chain' and appends it to
-// 'sb' until the end of the buffered range that contains time 't' is at
-// least 'gap' seconds beyond 't'. If 't' is not currently in a buffered
-// range, it first seeks to a time before 't' and appends until 't' is
-// covered.
-window.appendAt = function(timeoutManager, mp, sb, chain, t, gap, cb) {
-  if (!elementInBody(mp)) {
-    cb();
-    return;
-  }
-
-  gap = gap || 3;
-
-  var bufferedEnd = findBufferedRangeEndForTime(sb, t);
-
-  (function loop(buffer) {
-    if (!elementInBody(mp)) {
-      cb();
-      return;
-    }
-    if (buffer) {
-      if (!safeAppend(sb, buffer))
-        return;
-      bufferedEnd = findBufferedRangeEndForTime(sb, t);
-      timeoutManager.setTimeout(loop, 0);
-    } else {
-      if (t + gap >= (bufferedEnd || 0) && !mp.error) {
-        chain.pull(loop);
-      } else {
-        cb();
-      }
-    }
-  })();
-};
-
-// Append data from chains 'f1' and 'f2' to source buffers 's1' and 's2',
-// maintaining 'lead' seconds of time between current playback time and end of
-// current buffered range. Continue to do this until the current playback time
-// reaches 'endTime'.
-// It supports play one stream, where 's2' and 'f2' are null.
-//
-// 'lead' may be small or negative, which usually triggers some interesting
-// fireworks with regard to the network buffer level state machine.
-//
-// TODO: catch transition to HAVE_CURRENT_DATA or lower and append enough to
-// resume in that case
-window.playThrough = function(timeoutManager, mp, lead, endTime, s1, f1, s2,
-                              f2, cb) {
-  var yieldTime = 0.03;
-
-  function loop() {
-    if (!elementInBody(mp))
-      return;
-    if (mp.currentTime <= endTime && !mp.error)
-      timeoutManager.setTimeout(playThrough.bind(
-          null, timeoutManager, mp, lead, endTime, s1, f1, s2, f2, cb),
-          yieldTime * 1000);
-    else
-      cb();
-  };
-  appendAt(timeoutManager, mp, s1, f1, mp.currentTime, yieldTime + lead,
-           function() {
-             if (s2)
-               appendAt(timeoutManager, mp, s2, f2, mp.currentTime,
-                        yieldTime + lead, loop);
-             else
-               loop();
-           });
-};
-
-window.waitUntil = function(timeouts, media, target, cb) {
-  var initTime = media.currentTime;
-  var lastTime = lastTime;
-  var check = function() {
-    if (media.currentTime === initTime) {
-      timeouts.setTimeout(check, 500);
-    } else if (media.currentTime === lastTime || media.currentTime > target) {
-      cb();
-    } else {
-      lastTime = media.currentTime;
-      timeouts.setTimeout(check, 500);
-    }
-  };
-
-  timeouts.setTimeout(check, 500);
-};
-
-window.callAfterLoadedMetaData = function(media, testFunc) {
-  var onLoadedMetadata = function() {
-    LOG('onLoadedMetadata called');
-    media.removeEventListener('loadedmetadata', onLoadedMetadata);
-    testFunc();
-  };
-
-  if (media.readyState >= media.HAVE_METADATA) {
-    LOG('onLoadedMetadata bypassed');
-    testFunc();
-  } else {
-    media.addEventListener('loadedmetadata', onLoadedMetadata);
-  }
-};
-
-})();
-
-// js/lib/mse/2013/msutil-20150612143746.js end
-
-// js/tests/2013/conformanceTest-20150612143746.js begin
-
-var ConformanceTest = function() {
-
-var tests = [];
-var info = 'No MSE Support!';
-if (window.MediaSource)
-  info = 'MSE Version: ' + MediaSource.prototype.version;
-info += ' / Default Timeout: ' + TestBase.timeout + 'ms';
-
-var fields = ['passes', 'failures', 'timeouts'];
-
-var createConformanceTest = function(name) {
-  var t = createMSTest(name);
-  t.prototype.index = tests.length;
-  t.prototype.passes = 0;
-  t.prototype.failures = 0;
-  t.prototype.timeouts = 0;
-  tests.push(t);
-  return t;
-};
-
-
-var testPresence = createConformanceTest('Presence');
-testPresence.prototype.title = 'Test if MediaSource object is present.';
-testPresence.prototype.start = function(runner, video) {
-  if (!window.MediaSource)
-    return runner.fail('No MediaSource object available.');
-
-  var ms = new MediaSource();
-  if (!ms)
-    return runner.fail('Found MediaSource but could not create one');
-
-  if (ms.version)
-    this.log('Media source version reported as ' + ms.version);
-  else
-    this.log('No media source version reported');
-
-  runner.succeed();
-};
-testPresence.prototype.teardown = function() {};
-
-
-var testAttach = createConformanceTest('Attach');
-testAttach.prototype.timeout = 2000;
-testAttach.prototype.title =
-    'Test if MediaSource object can be attached to video.';
-testAttach.prototype.start = function(runner, video) {
-  this.ms = new MediaSource();
-  this.ms.addEventListener('sourceopen', function() {
-    runner.succeed();
-  });
-  if (this.ms.isWrapper)
-    this.ms.attachTo(video);
-  else
-    video.src = window.URL.createObjectURL(this.ms);
-  video.load();
-};
-testAttach.prototype.teardown = function() {};
-
-
-var testAddSourceBuffer = createConformanceTest('addSourceBuffer');
-testAddSourceBuffer.prototype.title =
-    'Test if we can add source buffer';
-testAddSourceBuffer.prototype.onsourceopen = function() {
-  this.runner.checkEq(this.ms.sourceBuffers.length, 0, 'Source buffer number');
-  this.ms.addSourceBuffer(StreamDef.AudioType);
-  this.runner.checkEq(this.ms.sourceBuffers.length, 1, 'Source buffer number');
-  this.ms.addSourceBuffer(StreamDef.VideoType);
-  this.runner.checkEq(this.ms.sourceBuffers.length, 2, 'Source buffer number');
-  this.runner.succeed();
-};
-
-
-var testSupportedFormats = createConformanceTest('SupportedFormats');
-testSupportedFormats.prototype.title =
-    'Test if we support mp4 video (video/mp4; codecs="avc1.640008") and ' +
-    'audio (audio/mp4; codecs="mp4a.40.5") formats, or webm video' +
-    '(video/webm; codecs="vorbis,vp9") and audio (audio/webm; codecs="vorbis").';
-testSupportedFormats.prototype.onsourceopen = function() {
-  try {
-      this.log('Trying format ' + StreamDef.AudioType);
-      var src = this.ms.addSourceBuffer(StreamDef.AudioType);
-      this.log('Trying format ' + StreamDef.VideoType);
-      var src = this.ms.addSourceBuffer(StreamDef.VideoType);
-  } catch (e) {
-      return this.runner.fail(e);
-  }
-  this.runner.succeed();
-};
-
-
-var testAddSourceBufferException = createConformanceTest('AddSBException');
-testAddSourceBufferException.prototype.title =
-    'Test if add incorrect source buffer type will fire the correct ' +
-    'exceptions.';
-testAddSourceBufferException.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var self = this;
-  runner.checkException(function() {
-    self.ms.addSourceBuffer('^^^');
-  }, DOMException.NOT_SUPPORTED_ERR);
-  if (this.ms.isWrapper) {
-    runner.checkException(function() {
-      var video = document.createElement('video');
-      video.webkitSourceAddId('id', StreamDef.AudioType);
-    }, DOMException.INVALID_STATE_ERR);
-  } else {
-    runner.checkException(function() {
-      var ms = new MediaSource;
-      ms.addSourceBuffer(StreamDef.AudioType);
-    }, DOMException.INVALID_STATE_ERR);
-  }
-  runner.succeed();
-};
-
-
-var createInitialMediaStateTest = function(state, value, check) {
-  var test = createConformanceTest('InitialMedia' +
-                                   util.MakeCapitalName(state));
-
-  check = typeof(check) === 'undefined' ? 'checkEq' : check;
-  test.prototype.title = 'Test if the state ' + state +
-      ' is correct when onsourceopen is called';
-  test.prototype.onsourceopen = function() {
-    this.runner[check](this.video[state], value, state);
-    this.runner.succeed();
-  };
-};
-
-createInitialMediaStateTest('duration', NaN);
-createInitialMediaStateTest('videoWidth', 0);
-createInitialMediaStateTest('videoHeight', 0);
-createInitialMediaStateTest('readyState', HTMLMediaElement.HAVE_NOTHING);
-createInitialMediaStateTest('src', '', 'checkNE');
-createInitialMediaStateTest('currentSrc', '', 'checkNE');
-
-
-var createInitialMSStateTest = function(state, value, check) {
-  var test = createConformanceTest('InitialMS' + util.MakeCapitalName(state));
-
-  check = typeof(check) === 'undefined' ? 'checkEq' : check;
-  test.prototype.title = 'Test if the state ' + state +
-      ' is correct when onsourceopen is called';
-  test.prototype.onsourceopen = function() {
-    this.runner[check](this.ms[state], value, state);
-    this.runner.succeed();
-  };
-};
-
-createInitialMSStateTest('duration', NaN);
-createInitialMSStateTest('readyState', 'open');
-
-
-var createAppendTest = function(stream) {
-  var test = createConformanceTest('Append' +
-                                   util.MakeCapitalName(stream.name));
-  test.prototype.title = 'Test if we can append a whole ' + stream.name +
-      ' file whose size is 1MB.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var sb = this.ms.addSourceBuffer(stream.type);
-    var xhr = runner.XHRManager.createRequest(stream.src,
-      function(e) {
-        sb.append(xhr.getResponseData());
-        runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
-        runner.checkEq(sb.buffered.start(0), 0, 'Range start');
-        runner.checkApproxEq(sb.buffered.end(0), stream.duration, 'Range end');
-        runner.succeed();
-      });
-    xhr.send();
-  };
-};
-
-createAppendTest(StreamDef.Audio1MB);
-createAppendTest(StreamDef.Video1MB);
-
-
-var createAbortTest = function(stream) {
-  var test = createConformanceTest('Abort' + util.MakeCapitalName(stream.name));
-  test.prototype.title = 'Test if we can abort the current segment.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var sb = this.ms.addSourceBuffer(stream.type);
-    var xhr = runner.XHRManager.createRequest(stream.src,
-      function(e) {
-        sb.append(xhr.getResponseData());
-        sb.abort();
-        sb.append(xhr.getResponseData());
-        runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
-        runner.checkEq(sb.buffered.start(0), 0, 'Range start');
-        runner.checkGr(sb.buffered.end(0), 0, 'Range end');
-        runner.succeed();
-      }, 0, 200000);
-    xhr.send();
-  };
-};
-
-createAbortTest(StreamDef.Audio1MB);
-createAbortTest(StreamDef.Video1MB);
-
-
-var createTimestampOffsetTest = function(stream) {
-  var test = createConformanceTest('TimestampOffset' +
-                            util.MakeCapitalName(stream.name));
-  test.prototype.title = 'Test if we can set timestamp offset.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var sb = this.ms.addSourceBuffer(stream.type);
-    var xhr = runner.XHRManager.createRequest(stream.src,
-      function(e) {
-        sb.timestampOffset = 5;
-        sb.append(xhr.getResponseData());
-        runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
-        runner.checkEq(sb.buffered.start(0), 5, 'Range start');
-        runner.checkApproxEq(sb.buffered.end(0), stream.duration + 5,
-                             'Range end');
-        runner.succeed();
-      });
-    xhr.send();
-  };
-};
-
-createTimestampOffsetTest(StreamDef.Audio1MB);
-createTimestampOffsetTest(StreamDef.Video1MB);
-
-
-var testDuration = createConformanceTest('Duration');
-testDuration.prototype.title =
-    'Test if we can set duration.';
-testDuration.prototype.onsourceopen = function() {
-  this.ms.duration = 10;
-  this.runner.checkEq(this.ms.duration, 10, 'ms.duration');
-  this.runner.succeed();
-};
-
-
-var testSourceRemove = createConformanceTest('SourceRemove');
-testSourceRemove.prototype.title =
-    'Test if we can add/remove source buffer and do it for more than once';
-testSourceRemove.prototype.onsourceopen = function() {
-  var sb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  this.ms.removeSourceBuffer(sb);
-  this.runner.checkEq(this.ms.sourceBuffers.length, 0, 'Source buffer number');
-  this.ms.addSourceBuffer(StreamDef.AudioType);
-  this.runner.checkEq(this.ms.sourceBuffers.length, 1, 'Source buffer number');
-  for (var i = 0; i < 10; ++i) {
-    try {
-      sb = this.ms.addSourceBuffer(StreamDef.VideoType);
-      this.runner.checkEq(this.ms.sourceBuffers.length, 2,
-                          'Source buffer number');
-      this.ms.removeSourceBuffer(sb);
-      this.runner.checkEq(this.ms.sourceBuffers.length, 1,
-                          'Source buffer number');
-    } catch (e) {
-      return this.runner.fail(e);
-    }
-  }
-  this.runner.succeed();
-};
-
-
-var createDurationAfterAppendTest = function(type, stream) {
-  var test = createConformanceTest('DurationAfterAppend' +
-                                   util.MakeCapitalName(type));
-  test.prototype.title = 'Test if the duration expands after appending data.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var ms = this.ms;
-    var sb = ms.addSourceBuffer(stream.type);
-    var self = this;
-    var ondurationchange = function() {
-      self.log('ondurationchange called');
-      media.removeEventListener('durationchange', ondurationchange);
-      runner.checkApproxEq(ms.duration, sb.buffered.end(0), 'ms.duration');
-      runner.succeed();
-    };
-    var xhr = runner.XHRManager.createRequest(stream.src,
-      function(e) {
-        var data = xhr.getResponseData();
-        sb.append(data);
-        sb.abort();
-        ms.duration = sb.buffered.end(0) / 2;
-        media.addEventListener('durationchange', ondurationchange);
-        sb.append(data);
-      });
-    xhr.send();
-  };
-};
-
-createDurationAfterAppendTest('audio', StreamDef.Audio1MB);
-createDurationAfterAppendTest('video', StreamDef.Video1MB);
-
-
-var createPausedTest = function(type, stream) {
-  var test = createConformanceTest('PausedStateWith' +
-                                   util.MakeCapitalName(type));
-  test.prototype.title = 'Test if the paused state is correct before or ' +
-      ' after appending data.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var ms = this.ms;
-    var sb = ms.addSourceBuffer(stream.type);
-
-    runner.checkEq(media.paused, true, 'media.paused');
-
-    var xhr = runner.XHRManager.createRequest(stream.src,
-      function(e) {
-        runner.checkEq(media.paused, true, 'media.paused');
-        sb.append(xhr.getResponseData());
-        runner.checkEq(media.paused, true, 'media.paused');
-        runner.succeed();
-      });
-    xhr.send();
-  };
-};
-
-createPausedTest('audio', StreamDef.Audio1MB);
-createPausedTest('video', StreamDef.Video1MB);
-
-
-var createMediaElementEventsTest = function() {
-  var test = createConformanceTest('MediaElementEvents');
-  test.prototype.title = 'Test if the events on MediaSource are correct.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var ms = this.ms;
-    var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-    var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-    var lastState = 'open';
-    var self = this;
-    var videoXhr = runner.XHRManager.createRequest(StreamDef.Video1MB.src,
-      function(e) {
-        self.log('onload called');
-        videoSb.append(videoXhr.getResponseData());
-        videoSb.abort();
-        ms.duration = 1;
-        ms.endOfStream();
-        media.play();
-      });
-    var audioXhr = runner.XHRManager.createRequest(StreamDef.Audio1MB.src,
-      function(e) {
-        self.log('onload called');
-        audioSb.append(audioXhr.getResponseData());
-        audioSb.abort();
-        videoXhr.send();
-      });
-
-    media.addEventListener('ended', function() {
-      self.log('onended called');
-      runner.succeed();
-    });
-
-    audioXhr.send();
-  };
-};
-
-createMediaElementEventsTest();
-
-
-var createMediaSourceEventsTest = function() {
-  var test = createConformanceTest('MediaSourceEvents');
-  test.prototype.title = 'Test if the events on MediaSource are correct.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var ms = this.ms;
-    var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-    var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-    var lastState = 'open';
-    var self = this;
-    var videoXhr = runner.XHRManager.createRequest(StreamDef.Video1MB.src,
-      function(e) {
-        self.log('onload called');
-        videoSb.append(videoXhr.getResponseData());
-        videoSb.abort();
-        ms.endOfStream();
-      });
-    var audioXhr = runner.XHRManager.createRequest(StreamDef.Audio1MB.src,
-      function(e) {
-        self.log('onload called');
-        audioSb.append(audioXhr.getResponseData());
-        audioSb.abort();
-        videoXhr.send();
-      });
-
-    ms.addEventListener('sourceclose', function() {
-      self.log('onsourceclose called');
-      runner.checkEq(lastState, 'ended', 'The previous state');
-      runner.succeed();
-    });
-
-    ms.addEventListener('sourceended', function() {
-      self.log('onsourceended called');
-      runner.checkEq(lastState, 'open', 'The previous state');
-      lastState = 'ended';
-      media.removeAttribute('src');
-      media.load();
-    });
-
-    audioXhr.send();
-  };
-};
-
-createMediaSourceEventsTest();
-
-
-var testBufferSize = createConformanceTest('VideoBufferSize');
-testBufferSize.prototype.title = 'Determines video buffer sizes by ' +
-    'appending incrementally until discard occurs, and tests that it meets ' +
-    'the minimum requirements for streaming.';
-testBufferSize.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var sb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var self = this;
-  var xhr = runner.XHRManager.createRequest('media/test-video-1MB.mp4',
-    function(e) {
-      // The test clip has a bitrate which is nearly exactly 1MB/sec, and
-      // lasts 1s. We start appending it repeatedly until we get eviction.
-      var expectedTime = 0;
-      while (true) {
-        sb.append(xhr.getResponseData());
-        runner.checkEq(sb.buffered.start(0), 0, 'Range start');
-        if (expectedTime > sb.buffered.end(0) + 0.1) break;
-        expectedTime++;
-        sb.timestampOffset = expectedTime;
-      }
-      var MIN_SIZE = 12;
-      runner.checkGE(expectedTime, MIN_SIZE, 'Estimated source buffer size');
-      runner.succeed();
-    });
-  xhr.send();
-};
-
-
-var testSourceChain = createConformanceTest('SourceChain');
-testSourceChain.prototype.title =
-    'Test if Source Chain works properly. Source Chain is a stack of ' +
-    'classes that help with common tasks like appending init segment or ' +
-    'append data in random size.';
-testSourceChain.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoChain = new RandomAppendSize(new ResetInit(
-      new FileSource('media/car-20120827-85.mp4', runner.XHRManager,
-                     runner.timeouts)));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioChain = new FixedAppendSize(new ResetInit(
-      new FileSource('media/car-20120827-8b.mp4', runner.XHRManager,
-                     runner.timeouts)));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-
-  appendUntil(runner.timeouts, media, videoSb, videoChain, 5, function() {
-    appendUntil(runner.timeouts, media, audioSb, audioChain, 5, function() {
-      media.play();
-      playThrough(
-          runner.timeouts, media, 1, 2,
-          videoSb, videoChain, audioSb, audioChain,
-          function() {
-        runner.checkGE(media.currentTime, 2, 'currentTime');
-        runner.succeed();
-      });
-    });
-  });
-};
-
-
-var testVideoDimension = createConformanceTest('VideoDimension');
-testVideoDimension.prototype.title =
-    'Test if the readyState transition is correct.';
-testVideoDimension.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoChain = new ResetInit(new FixedAppendSize(
-      new FileSource('media/car-20120827-86.mp4', runner.XHRManager,
-                     runner.timeouts), 65536));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var self = this;
-
-  runner.checkEq(media.videoWidth, 0, 'video width');
-  runner.checkEq(media.videoHeight, 0, 'video height');
-
-  media.addEventListener('loadedmetadata', function(e) {
-    self.log('loadedmetadata called');
-    runner.checkEq(media.videoWidth, 640, 'video width');
-    runner.checkEq(media.videoHeight, 360, 'video height');
-    runner.succeed();
-  });
-
-  runner.checkEq(media.readyState, media.HAVE_NOTHING, 'readyState');
-  appendInit(media, videoSb, videoChain, 0, function() {});
-};
-
-
-var testPlaybackState = createConformanceTest('PlaybackState');
-testPlaybackState.prototype.title =
-    'Test if the playback state transition is correct.';
-testPlaybackState.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoChain = new ResetInit(new FixedAppendSize(
-      new FileSource('media/car-20120827-86.mp4', runner.XHRManager,
-                     runner.timeouts), 65536));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioChain = new ResetInit(new FixedAppendSize(
-      new FileSource('media/car-20120827-8b.mp4', runner.XHRManager,
-                     runner.timeouts), 65536));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var self = this;
-
-  media.play();
-  runner.checkEq(media.currentTime, 0, 'media.currentTime');
-  media.pause();
-  runner.checkEq(media.currentTime, 0, 'media.currentTime');
-
-  appendInit(media, audioSb, audioChain, 0, function() {});
-  appendInit(media, videoSb, videoChain, 0, function() {});
-  callAfterLoadedMetaData(media, function() {
-    media.play();
-    runner.checkEq(media.currentTime, 0, 'media.currentTime');
-    media.pause();
-    runner.checkEq(media.currentTime, 0, 'media.currentTime');
-    media.play();
-    appendUntil(runner.timeouts, media, audioSb, audioChain, 5, function() {
-      appendUntil(runner.timeouts, media, videoSb, videoChain, 5, function() {
-        playThrough(runner.timeouts, media, 1, 2, audioSb, audioChain,
-                    videoSb, videoChain, function() {
-          var time = media.currentTime;
-          media.pause();
-          runner.checkApproxEq(media.currentTime, time, 'media.currentTime');
-          runner.succeed();
-        });
-      });
-    });
-  });
-};
-
-
-var testStartPlayWithoutData = createConformanceTest('StartPlayWithoutData');
-testStartPlayWithoutData.prototype.title =
-    'Test if we can start play before feeding any data. The play should ' +
-    'start automatically after data is appended';
-testStartPlayWithoutData.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoChain = new ResetInit(
-      new FileSource('media/car-20120827-86.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioChain = new ResetInit(
-      new FileSource('media/car-20120827-8d.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-
-  media.play();
-  appendUntil(runner.timeouts, media, videoSb, videoChain, 1, function() {
-    appendUntil(runner.timeouts, media, audioSb, audioChain, 1, function() {
-      playThrough(
-          runner.timeouts, media, 1, 2,
-          videoSb, videoChain, audioSb, audioChain,
-          function() {
-        runner.checkGE(media.currentTime, 2, 'currentTime');
-        runner.succeed();
-      });
-    });
-  });
-};
-
-
-var testPlayPartialSegment = createConformanceTest('PlayPartialSegment');
-testPlayPartialSegment.prototype.title =
-    'Test if we can play a partially appended video segment.';
-testPlayPartialSegment.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var video = this.video;
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var videoXhr = runner.XHRManager.createRequest('media/car-20120827-85.mp4',
-    function(e) {
-      videoSb.append(this.getResponseData());
-      video.addEventListener('timeupdate', function(e) {
-        if (!video.paused && video.currentTime >= 2) {
-          runner.succeed();
-        }
-      });
-      video.play();
-    }, 0, 1500000);
-  var audioXhr = runner.XHRManager.createRequest('media/car-20120827-8b.mp4',
-    function(e) {
-      audioSb.append(this.getResponseData());
-      videoXhr.send();
-    }, 0, 500000);
-  audioXhr.send();
-};
-
-
-var testIncrementalAudio = createConformanceTest('IncrementalAudio');
-testIncrementalAudio.prototype.title =
-    'Test if we can append audio not in the unit of segment.';
-testIncrementalAudio.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var sb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var xhr = runner.XHRManager.createRequest('media/car-20120827-8c.mp4',
-    function(e) {
-      sb.append(xhr.getResponseData());
-      runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
-      runner.checkEq(sb.buffered.start(0), 0, 'Range start');
-      runner.checkApproxEq(sb.buffered.end(0), 12.42, 'Range end');
-      runner.succeed();
-    }, 0, 200000);
-  xhr.send();
-};
-
-
-var testAppendAudioOffset = createConformanceTest('AppendAudioOffset');
-testAppendAudioOffset.prototype.title =
-    'Test if we can append audio data with an explicit offset.';
-testAppendAudioOffset.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var video = this.video;
-  var sb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var xhr = runner.XHRManager.createRequest('media/car-20120827-8c.mp4',
-    function(e) {
-      sb.timestampOffset = 5;
-      sb.append(this.getResponseData());
-      xhr2.send();
-    }, 0, 200000);
-  var xhr2 = runner.XHRManager.createRequest('media/car-20120827-8d.mp4',
-    function(e) {
-      sb.abort();
-      sb.timestampOffset = 0;
-      sb.append(this.getResponseData());
-      runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
-      runner.checkEq(sb.buffered.start(0), 0, 'Range start');
-      runner.checkApproxEq(sb.buffered.end(0), 17.42, 'Range end');
-      runner.succeed();
-    }, 0, 200000);
-  xhr.send();
-};
-
-
-var testVideoChangeRate = createConformanceTest('VideoChangeRate');
-testVideoChangeRate.prototype.title =
-    'Test if we can change the format of video on the fly.';
-testVideoChangeRate.prototype.onsourceopen = function() {
-  var self = this;
-  var runner = this.runner;
-  var video = this.video;
-  var sb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var xhr = runner.XHRManager.createRequest('media/car-20120827-86.mp4',
-    function(e) {
-      sb.timestampOffset = 5;
-      sb.append(this.getResponseData());
-      xhr2.send();
-    }, 0, 200000);
-  var xhr2 = runner.XHRManager.createRequest('media/car-20120827-85.mp4',
-    function(e) {
-      sb.abort();
-      sb.timestampOffset = 0;
-      sb.append(this.getResponseData());
-      runner.checkEq(sb.buffered.length, 1, 'Source buffer number');
-      runner.checkEq(sb.buffered.start(0), 0, 'Range start');
-      runner.checkApproxEq(sb.buffered.end(0), 11.47, 'Range end');
-      callAfterLoadedMetaData(video, function() {
-        video.currentTime = 3;
-        video.addEventListener('seeked', function(e) {
-          self.log('seeked called');
-          video.addEventListener('timeupdate', function(e) {
-            self.log('timeupdate called with ' + video.currentTime);
-            if (!video.paused && video.currentTime >= 2) {
-              runner.succeed();
-            }
-          });
-        });
-      });
-      video.play();
-    }, 0, 400000);
-  this.ms.duration = 100000000;  // Ensure that we can seek to any position.
-  xhr.send();
-};
-
-
-var createAppendMultipleInitTest = function(type, stream) {
-  var test = createConformanceTest('AppendMultipleInit' +
-                                   util.MakeCapitalName(type));
-  test.prototype.title = 'Test if we can append multiple init segments.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var chain = new FileSource(stream.src, runner.XHRManager, runner.timeouts);
-    var src = this.ms.addSourceBuffer(stream.type);
-    var init;
-
-    chain.init(0, function(buf) {
-      init = buf;
-      chain.pull(function(buf) {
-        for (var i = 0; i < 10; ++i)
-          src.append(init);
-        src.append(buf);
-        src.abort();
-        var end = src.buffered.end(0);
-        for (var i = 0; i < 10; ++i)
-          src.append(init);
-        runner.checkEq(src.buffered.end(0), end, 'Range end');
-        runner.succeed();
-      });
-    });
-  };
-};
-
-createAppendMultipleInitTest('audio', StreamDef.Audio1MB);
-createAppendMultipleInitTest('video', StreamDef.Video1MB);
-
-
-var testAppendOutOfOrder = createConformanceTest('AppendOutOfOrder');
-testAppendOutOfOrder.prototype.title =
-    'Test if we can append segments out of order. This is valid according' +
-    ' to MSE v0.6 section 2.3.';
-testAppendOutOfOrder.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var audioChain = new FileSource('media/car-20120827-8c.mp4',
-                                   runner.XHRManager,
-                                   runner.timeouts);
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var bufs = [];
-
-  audioChain.init(0, function(buf) {
-    bufs.push(buf);
-    audioChain.pull(function(buf) {
-      bufs.push(buf);
-      audioChain.pull(function(buf) {
-        bufs.push(buf);
-        audioChain.pull(function(buf) {
-          bufs.push(buf);
-          audioChain.pull(function(buf) {
-            bufs.push(buf);
-            audioSb.append(bufs[0]);
-            runner.checkEq(audioSb.buffered.length, 0, 'Source buffer number');
-            audioSb.append(bufs[2]);
-            runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
-            runner.checkGr(audioSb.buffered.start(0), 0, 'Range start');
-            audioSb.append(bufs[1]);
-            runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
-            runner.checkEq(audioSb.buffered.start(0), 0, 'Range start');
-            audioSb.append(bufs[4]);
-            runner.checkEq(audioSb.buffered.length, 2, 'Source buffer number');
-            runner.checkEq(audioSb.buffered.start(0), 0, 'Range start');
-            audioSb.append(bufs[3]);
-            runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
-            runner.checkEq(audioSb.buffered.start(0), 0, 'Range start');
-            runner.succeed();
-          });
-        });
-      });
-    });
-  });
-};
-
-
-var testBufferedRange = createConformanceTest('BufferedRange');
-testBufferedRange.prototype.title =
-    'Test if SourceBuffer.buffered get updated correctly after feeding data.';
-testBufferedRange.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoChain = new ResetInit(
-      new FileSource('media/car-20120827-86.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioChain = new ResetInit(
-      new FileSource('media/car-20120827-8c.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-
-  runner.checkEq(videoSb.buffered.length, 0, 'Source buffer number');
-  runner.checkEq(audioSb.buffered.length, 0, 'Source buffer number');
-  appendInit(media, videoSb, videoChain, 0, function() {
-    appendInit(media, audioSb, audioChain, 0, function() {
-      runner.checkEq(videoSb.buffered.length, 0, 'Source buffer number');
-      runner.checkEq(audioSb.buffered.length, 0, 'Source buffer number');
-      appendUntil(runner.timeouts, media, videoSb, videoChain, 5, function() {
-        runner.checkEq(videoSb.buffered.length, 1, 'Source buffer number');
-        runner.checkEq(videoSb.buffered.start(0), 0, 'Source buffer number');
-        runner.checkGE(videoSb.buffered.end(0), 5, 'Range end');
-        appendUntil(runner.timeouts, media, audioSb, audioChain, 5, function() {
-          runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
-          runner.checkEq(audioSb.buffered.start(0), 0, 'Source buffer number');
-          runner.checkGE(audioSb.buffered.end(0), 5, 'Range end');
-          runner.succeed();
-        });
-      });
-    });
-  });
-};
-
-
-var testMediaSourceDuration = createConformanceTest('MediaSourceDuration');
-testMediaSourceDuration.prototype.title =
-    'Test if the duration on MediaSource can be set and got sucessfully.';
-testMediaSourceDuration.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var ms = this.ms;
-  var videoChain = new ResetInit(
-      new FileSource('media/car-20120827-86.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var self = this;
-  var onsourceclose = function() {
-    self.log('onsourceclose called');
-    runner.assert(isNaN(ms.duration));
-    runner.succeed();
-  };
-
-  runner.assert(isNaN(media.duration), 'Initial media duration not NaN');
-  media.play();
-  appendInit(media, videoSb, videoChain, 0, function() {
-    appendUntil(runner.timeouts, media, videoSb, videoChain, 10, function() {
-      runner.checkEq(ms.duration, Infinity, 'ms.duration');
-      ms.duration = 5;
-      runner.checkEq(ms.duration, 5, 'ms.duration');
-      runner.checkEq(media.duration, 5, 'media.duration');
-      runner.checkLE(videoSb.buffered.end(0), 5.1, 'Range end');
-      videoSb.abort();
-      videoChain.seek(0);
-      appendInit(media, videoSb, videoChain, 0, function() {
-        appendUntil(runner.timeouts, media,
-            videoSb, videoChain, 10, function() {
-          runner.checkApproxEq(ms.duration, 10, 'ms.duration');
-          ms.duration = 5;
-          var duration = videoSb.buffered.end(0);
-          ms.endOfStream();
-          runner.checkEq(ms.duration, duration, 'ms.duration');
-          media.play();
-          ms.addEventListener('sourceended', function() {
-            runner.checkEq(ms.duration, duration, 'ms.duration');
-            runner.checkEq(media.duration, duration, 'media.duration');
-            ms.addEventListener('sourceclose', onsourceclose);
-            media.removeAttribute('src');
-            media.load();
-          });
-        });
-      });
-    });
-  });
-};
-
-
-var testAudioWithOverlap = createConformanceTest('AudioWithOverlap');
-testAudioWithOverlap.prototype.title =
-    'Test if audio data with overlap will be merged into one range.';
-testAudioWithOverlap.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var audioChain = new ResetInit(
-      new FileSource('media/car-20120827-8c.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var GAP = 0.1;
-
-  appendInit(media, audioSb, audioChain, 0, function() {
-    audioChain.pull(function(buf) {
-      runner.assert(safeAppend(audioSb, buf), 'safeAppend failed');
-      runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
-      var segmentDuration = audioSb.buffered.end(0);
-      audioSb.timestampOffset = segmentDuration - GAP;
-      audioChain.seek(0);
-      audioChain.pull(function(buf) {
-        runner.assert(safeAppend(audioSb, buf), 'safeAppend failed');
-        audioChain.pull(function(buf) {
-          runner.assert(safeAppend(audioSb, buf), 'safeAppend failed');
-          runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
-          runner.checkApproxEq(audioSb.buffered.end(0),
-                               segmentDuration * 2 - GAP, 'Range end');
-          runner.succeed();
-        });
-      });
-    });
-  });
-};
-
-
-var testAudioWithSmallGap = createConformanceTest('AudioWithSmallGap');
-testAudioWithSmallGap.prototype.title =
-    'Test if audio data with a gap smaller than an audio frame size ' +
-    'will be merged into one buffered range.';
-testAudioWithSmallGap.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var audioChain = new ResetInit(
-      new FileSource('media/car-20120827-8c.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var GAP = 0.01;  // The audio frame size of this file is 0.0232
-
-  appendInit(media, audioSb, audioChain, 0, function() {
-    audioChain.pull(function(buf) {
-      runner.assert(safeAppend(audioSb, buf), 'safeAppend failed');
-      runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
-      var segmentDuration = audioSb.buffered.end(0);
-      audioSb.timestampOffset = segmentDuration + GAP;
-      audioChain.seek(0);
-      audioChain.pull(function(buf) {
-        runner.assert(safeAppend(audioSb, buf, 'safeAppend failed'));
-        audioChain.pull(function(buf) {
-          runner.assert(safeAppend(audioSb, buf), 'safeAppend failed');
-          runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
-          runner.checkApproxEq(audioSb.buffered.end(0),
-                               segmentDuration * 2 + GAP, 'Range end');
-          runner.succeed();
-        });
-      });
-    });
-  });
-};
-
-
-var testAudioWithLargeGap = createConformanceTest('AudioWithLargeGap');
-testAudioWithLargeGap.prototype.title =
-    'Test if audio data with a gap larger than an audio frame size ' +
-    'will not be merged into one buffered range.';
-testAudioWithLargeGap.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var audioChain = new ResetInit(
-      new FileSource('media/car-20120827-8c.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var GAP = 0.03;  // The audio frame size of this file is 0.0232
-
-  appendInit(media, audioSb, audioChain, 0, function() {
-    audioChain.pull(function(buf) {
-      runner.assert(safeAppend(audioSb, buf), 'safeAppend failed');
-      runner.checkEq(audioSb.buffered.length, 1, 'Source buffer number');
-      var segmentDuration = audioSb.buffered.end(0);
-      audioSb.timestampOffset = segmentDuration + GAP;
-      audioChain.seek(0);
-      audioChain.pull(function(buf) {
-        runner.assert(safeAppend(audioSb, buf), 'safeAppend failed');
-        audioChain.pull(function(buf) {
-          runner.assert(safeAppend(audioSb, buf), 'safeAppend failed');
-          runner.checkEq(audioSb.buffered.length, 2, 'Source buffer number');
-          runner.succeed();
-        });
-      });
-    });
-  });
-};
-
-
-var testCanPlayClearKey = createConformanceTest('CanPlayClearKey');
-testCanPlayClearKey.prototype.title =
-    'Test if canPlay return is correct for clear key.';
-testCanPlayClearKey.prototype.onsourceopen = function() {
-  var video = this.video;
-  this.runner.assert(
-      video.canPlayType(
-          StreamDef.VideoType, 'org.w3.clearkey') === 'probably' ||
-      video.canPlayType(
-          StreamDef.VideoType, 'webkit-org.w3.clearkey') === 'probably',
-      "canPlay doesn't support video and clearkey properly");
-  this.runner.assert(
-      video.canPlayType(
-          StreamDef.AudioType, 'org.w3.clearkey') === 'probably' ||
-      video.canPlayType(
-          StreamDef.AudioType, 'webkit-org.w3.clearkey') === 'probably',
-      "canPlay doesn't support audio and clearkey properly");
-  this.runner.succeed();
-};
-
-
-var testCanPlayPlayReady = createConformanceTest('CanPlayPlayReady');
-testCanPlayPlayReady.prototype.title =
-    'Test if canPlay return is correct for PlayReady.';
-testCanPlayPlayReady.prototype.onsourceopen = function() {
-  var video = this.video;
-  this.runner.checkEq(
-      video.canPlayType(StreamDef.VideoType, 'com.youtube.playready'),
-                        'probably', 'canPlayType result');
-  this.runner.checkEq(
-      video.canPlayType(StreamDef.AudioType, 'com.youtube.playready'),
-                        'probably', 'canPlayType result');
-  this.runner.succeed();
-};
-
-
-var testCannotPlayWidevine = createConformanceTest('CannotPlayWidevine');
-testCannotPlayWidevine.prototype.title =
-    'Test if canPlay return is correct for Widevine.';
-testCannotPlayWidevine.prototype.onsourceopen = function() {
-  var video = this.video;
-  this.runner.checkEq(
-      video.canPlayType(StreamDef.VideoType, 'com.widevine.alpha'), '',
-      'canPlayType result');
-  this.runner.checkEq(
-      video.canPlayType(StreamDef.AudioType, 'com.widevine.alpha'), '',
-      'canPlayType result');
-  this.runner.succeed();
-};
-
-
-var testWebM = createConformanceTest('WebMHandling');
-testWebM.prototype.title = 'Ensure that WebM is either supported or ' +
-    'that attempting to add a WebM SourceBuffer results in an error.';
-testWebM.prototype.onsourceopen = function() {
-  var mime = 'video/webm; codecs="vorbis,vp8"';
-  var runner = this.runner;
-  try {
-    this.log('Add sourceBuffer typed webm');
-    var webmSb = this.ms.addSourceBuffer(mime);
-  } catch (e) {
-    runner.checkEq(e.code, DOMException.NOT_SUPPORTED_ERR,
-                          'exception code');
-    this.log('Add sourceBuffer typed webm to closed MediaSource');
-    try {
-      (new MediaSource).addSourceBuffer(mime);
-    } catch (e) {
-      LOG("WebM with mime '" + mime + "' not supported. (This is okay.)");
-      runner.succeed();
-      return;
-    }
-    runner.fail('Add sourceBuffer typed webm to closed MediaSource hasn\'t' +
-                ' thrown any exception.');
-    return;
-  }
-  var xhr = runner.XHRManager.createRequest('media/test.webm',
-    function(e) {
-      try {
-        webmSb.append(xhr.getResponseData());
-      } catch (e) {
-        LOG('WebM support claimed but error on appending data!');
-        runner.fail();
-        return;
-      }
-      runner.checkEq(webmSb.buffered.length, 1, 'buffered.length');
-      runner.checkApproxEq(webmSb.buffered.end(0), 6.04, 'buffered.end(0)');
-      runner.succeed();
-    });
-  xhr.send();
-};
-
-
-var testClearKeyAudio = createConformanceTest('ClearKeyAudio');
-testClearKeyAudio.prototype.title =
-    'Test if we can play audio encrypted with ClearKey encryption.';
-testClearKeyAudio.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoChain = new ResetInit(
-      new FileSource('media/car-20120827-86.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioChain = new ResetInit(
-      new FileSource('media/car_cenc-20120827-8c.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-
-  media.addEventListener('needkey', function(e) {
-    e.target.generateKeyRequest('org.w3.clearkey', e.initData);
-  });
-
-  media.addEventListener('keymessage', function(e) {
-    var key = new Uint8Array([
-        0x1a, 0x8a, 0x20, 0x95, 0xe4, 0xde, 0xb2, 0xd2,
-        0x9e, 0xc8, 0x16, 0xac, 0x7b, 0xae, 0x20, 0x82]);
-    var keyId = new Uint8Array([
-        0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
-        0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e]);
-    e.target.addKey('org.w3.clearkey', key, keyId, e.sessionId);
-  });
-
-  appendUntil(runner.timeouts, media, videoSb, videoChain, 5, function() {
-    appendUntil(runner.timeouts, media, audioSb, audioChain, 5, function() {
-      media.play();
-      playThrough(
-          runner.timeouts, media, 10, 5,
-          videoSb, videoChain, audioSb, audioChain, function() {
-        runner.checkGE(media.currentTime, 5, 'currentTime');
-        runner.succeed();
-      });
-    });
-  });
-};
-
-
-var testClearKeyVideo = createConformanceTest('ClearKeyVideo');
-testClearKeyVideo.prototype.title =
-    'Test if we can play video encrypted with ClearKey encryption.';
-testClearKeyVideo.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoChain = new ResetInit(
-      new FileSource('media/car_cenc-20120827-86.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioChain = new ResetInit(
-      new FileSource('media/car-20120827-8c.mp4', runner.XHRManager,
-                     runner.timeouts));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-
-  media.addEventListener('needkey', function(e) {
-    e.target.generateKeyRequest('org.w3.clearkey', e.initData);
-  });
-
-  media.addEventListener('keymessage', function(e) {
-    var key = new Uint8Array([
-        0x1a, 0x8a, 0x20, 0x95, 0xe4, 0xde, 0xb2, 0xd2,
-        0x9e, 0xc8, 0x16, 0xac, 0x7b, 0xae, 0x20, 0x82]);
-    var keyId = new Uint8Array([
-        0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
-        0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e]);
-    e.target.addKey('org.w3.clearkey', key, keyId, e.sessionId);
-  });
-
-  appendUntil(runner.timeouts, media, videoSb, videoChain, 5, function() {
-    appendUntil(runner.timeouts, media, audioSb, audioChain, 5, function() {
-      media.play();
-      playThrough(
-          runner.timeouts, media, 10, 5,
-          videoSb, videoChain, audioSb, audioChain, function() {
-        runner.checkGE(media.currentTime, 5, 'currentTime');
-        runner.succeed();
-      });
-    });
-  });
-};
-
-
-var testSeekTimeUpdate = createConformanceTest('SeekTimeUpdate');
-testSeekTimeUpdate.prototype.title =
-  'Timeupdate event fired with correct currentTime after seeking.';
-testSeekTimeUpdate.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var lastTime = 0;
-  var updateCount = 0;
-  var xhr = runner.XHRManager.createRequest('media/car-20120827-86.mp4',
-      function() {
-    videoSb.append(xhr.getResponseData());
-    var xhr2 = runner.XHRManager.createRequest('media/car-20120827-8c.mp4',
-        function() {
-      audioSb.append(xhr2.getResponseData());
-      callAfterLoadedMetaData(media, function() {
-        media.addEventListener('timeupdate', function(e) {
-          if (!media.paused) {
-            ++updateCount;
-            runner.checkGE(media.currentTime, lastTime,
-                           'media.currentTime');
-            if (updateCount > 3) {
-              updateCount = 0;
-              lastTime += 10;
-              if (lastTime >= 35)
-                runner.succeed();
-              else
-                media.currentTime = lastTime + 6;
-            }
-          }
-        });
-        media.play();
-      });
-    }, 0, 1000000);
-    xhr2.send();
-    }, 0, 5000000);
-  this.ms.duration = 100000000;  // Ensure that we can seek to any position.
-  xhr.send();
-};
-
-
-var testSourceSeek = createConformanceTest('Seek');
-testSourceSeek.prototype.title = 'Test if we can seek during playing. It' +
-    ' also tests if the implementation properly supports seek operation' +
-    ' fired immediately after another seek that hasn\'t been completed.';
-testSourceSeek.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoChain = new ResetInit(new FileSource(
-      'media/car-20120827-86.mp4', runner.XHRManager, runner.timeouts));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioChain = new ResetInit(new FileSource(
-      'media/car-20120827-8c.mp4', runner.XHRManager, runner.timeouts));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var self = this;
-
-  this.ms.duration = 100000000;  // Ensure that we can seek to any position.
-
-  appendUntil(runner.timeouts, media, videoSb, videoChain, 20, function() {
-    appendUntil(runner.timeouts, media, audioSb, audioChain, 20, function() {
-      self.log('Seek to 17s');
-      callAfterLoadedMetaData(media, function() {
-        media.currentTime = 17;
-        media.play();
-        playThrough(
-            runner.timeouts, media, 10, 19,
-            videoSb, videoChain, audioSb, audioChain, function() {
-          runner.checkGE(media.currentTime, 19, 'currentTime');
-          self.log('Seek to 28s');
-          media.currentTime = 53;
-          media.currentTime = 58;
-          playThrough(
-              runner.timeouts, media, 10, 60,
-              videoSb, videoChain, audioSb, audioChain, function() {
-            runner.checkGE(media.currentTime, 60, 'currentTime');
-            self.log('Seek to 7s');
-            media.currentTime = 0;
-            media.currentTime = 7;
-            videoChain.seek(7, videoSb);
-            audioChain.seek(7, audioSb);
-            playThrough(runner.timeouts, media, 10, 9, videoSb, videoChain,
-                audioSb, audioChain, function() {
-              runner.checkGE(media.currentTime, 9, 'currentTime');
-              runner.succeed();
-            });
-          });
-        });
-      });
-    });
-  });
-};
-
-
-var testBufUnbufSeek = createConformanceTest('BufUnbufSeek');
-testBufUnbufSeek.prototype.title = 'Seek into and out of a buffered region.';
-testBufUnbufSeek.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var xhr = runner.XHRManager.createRequest('media/car-20120827-86.mp4',
-      function() {
-    videoSb.append(xhr.getResponseData());
-    var xhr2 = runner.XHRManager.createRequest('media/car-20120827-8c.mp4',
-        function() {
-      audioSb.append(xhr2.getResponseData());
-      callAfterLoadedMetaData(media, function() {
-        var N = 30;
-        function loop(i) {
-          if (i > N) {
-            media.currentTime = 1.005;
-            media.addEventListener('timeupdate', function(e) {
-              if (!media.paused && media.currentTime > 3)
-                runner.succeed();
-            });
-            return;
-          }
-          // bored of shitty test scripts now => test scripts get shittier
-          media.currentTime = (i++ % 2) * 1.0e6 + 1;
-          runner.timeouts.setTimeout(loop.bind(null, i), 50);
-        }
-        media.play();
-        media.addEventListener('play', loop.bind(null, 0));
-      });
-    }, 0, 100000);
-    xhr2.send();
-  }, 0, 1000000);
-  this.ms.duration = 100000000;  // Ensure that we can seek to any position.
-  xhr.send();
-};
-
-
-var createDelayedTest = function(delayed, nonDelayed) {
-  var test = createConformanceTest('Delayed' +
-                                   util.MakeCapitalName(delayed.name));
-  test.prototype.title = 'Test if we can play properly when there' +
-    ' is not enough ' + delayed.name + ' data. The play should resume once ' +
-    delayed.name + ' data is appended.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var chain = new FixedAppendSize(new ResetInit(
-        new FileSource(nonDelayed.src, runner.XHRManager, runner.timeouts)),
-                       65536);
-    var src = this.ms.addSourceBuffer(nonDelayed.type);
-    var delayedChain = new FixedAppendSize(new ResetInit(
-        new FileSource(delayed.src, runner.XHRManager, runner.timeouts)),
-                       65536);
-    var delayedSrc = this.ms.addSourceBuffer(delayed.type);
-    var self = this;
-    var ontimeupdate = function(e) {
-      if (!media.paused) {
-        var end = delayedSrc.buffered.end(0);
-        runner.checkLE(media.currentTime, end + 1.0, 'media.currentTime');
-      }
-    };
-    appendUntil(runner.timeouts, media, src, chain, 15, function() {
-      appendUntil(runner.timeouts, media, delayedSrc, delayedChain, 8,
-                  function() {
-        var end = delayedSrc.buffered.end(0);
-        self.log('Start play when there is only ' + end + ' seconds of ' +
-                 delayed.name + ' data.');
-        media.play();
-        media.addEventListener('timeupdate', ontimeupdate);
-        waitUntil(runner.timeouts, media, delayedSrc.buffered.end(0) + 3,
-            function() {
-          runner.checkLE(media.currentTime, end + 1.0, 'media.currentTime');
-          runner.checkGr(media.currentTime, end - 1.0, 'media.currentTime');
-          runner.succeed();
-        });
-      });
-    });
-  };
-};
-
-createDelayedTest(StreamDef.AudioNormal, StreamDef.VideoNormal);
-createDelayedTest(StreamDef.VideoNormal, StreamDef.AudioNormal);
-
-
-var testXHRUint8Array = createConformanceTest('XHRUint8Array');
-testXHRUint8Array.prototype.title = 'Ensure that XHR can send an Uint8Array';
-testXHRUint8Array.prototype.timeout = 10000;
-testXHRUint8Array.prototype.start = function(runner, video) {
-  var s = 'XHR DATA';
-  var buf = new ArrayBuffer(s.length);
-  var view = new Uint8Array(buf);
-  for (var i = 0; i < s.length; i++) {
-    view[i] = s.charCodeAt(i);
-  }
-
-  var xhr = runner.XHRManager.createPostRequest(
-    'https://drmproxy.appspot.com/echo',
-    function(e) {
-      runner.checkEq(String.fromCharCode.apply(null, xhr.getResponseData()),
-                     s, 'XHR response');
-      runner.succeed();
-    },
-    view.length);
-  xhr.send(view);
-};
-
-
-var testXHRAbort = createConformanceTest('XHRAbort');
-testXHRAbort.prototype.title = 'Ensure that XHR aborts actually abort by ' +
-    'issuing an absurd number of them and then aborting all but one.';
-testXHRAbort.prototype.start = function(runner, video) {
-  var N = 100;
-  var startTime = Date.now();
-  var lastAbortTime;
-  function startXHR(i) {
-    var xhr = runner.XHRManager.createRequest(
-        'media/car-20120827-85.mp4?x=' + Date.now() + '.' + i,
-        function() {
-      if (i >= N) {
-        xhr.getResponseData();  // This will verify status internally.
-        runner.succeed();
-      }
-    });
-    if (i < N) {
-      runner.timeouts.setTimeout(xhr.abort.bind(xhr), 10);
-      runner.timeouts.setTimeout(startXHR.bind(null, i + 1), 1);
-      lastAbortTime = Date.now();
-    }
-    xhr.send();
-  };
-  startXHR(0);
-};
-
-
-var testXHROpenState = createConformanceTest('XHROpenState');
-testXHROpenState.prototype.title = 'Ensure XMLHttpRequest.open does not ' +
-    'reset XMLHttpRequest.responseType';
-testXHROpenState.prototype.start = function(runner, video) {
-  var xhr = new XMLHttpRequest;
-  // It should not be an error to set responseType before calling open
-  xhr.responseType = 'arraybuffer';
-  xhr.open('GET', 'http://google.com', true);
-  runner.checkEq(xhr.responseType, 'arraybuffer', 'XHR responseType');
-  runner.succeed();
-};
-
-
-var testFrameGaps = createConformanceTest('FrameGaps');
-testFrameGaps.prototype.title = 'Test media with frame durations of 24FPS ' +
-    'but segment timing corresponding to 23.976FPS';
-testFrameGaps.prototype.filename = 'media/nq-frames24-tfdt23.mp4';
-testFrameGaps.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var videoChain = new FixedAppendSize(new ResetInit(
-      new FileSource(this.filename, runner.XHRManager,
-                     runner.timeouts)));
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioChain = new FixedAppendSize(new ResetInit(
-      new FileSource('media/car-20120827-8c.mp4', runner.XHRManager,
-                     runner.timeouts)));
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  media.play();
-  playThrough(runner.timeouts, media, 5, 18, videoSb, videoChain,
-              audioSb, audioChain, runner.succeed.bind(runner));
-};
-
-
-var testFrameOverlaps = createConformanceTest('FrameOverlaps');
-testFrameOverlaps.prototype.title = 'Test media with frame durations of ' +
-    '23.976FPS but segment timing corresponding to 24FPS';
-testFrameOverlaps.prototype.filename = 'media/nq-frames23-tfdt24.mp4';
-testFrameOverlaps.prototype.onsourceopen = testFrameGaps.prototype.onsourceopen;
-
-
-var testAAC51 = createConformanceTest('AAC51');
-testAAC51.prototype.title = 'Test 5.1-channel AAC';
-testAAC51.prototype.audioFilename = 'media/sintel-trunc.mp4';
-testAAC51.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var media = this.video;
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var xhr = runner.XHRManager.createRequest(this.audioFilename,
-    function(e) {
-      audioSb.append(xhr.getResponseData());
-      var xhr2 = runner.XHRManager.createRequest('media/car-20120827-86.mp4',
-        function(e) {
-          videoSb.append(xhr2.getResponseData());
-          media.play();
-          media.addEventListener('timeupdate', function(e) {
-            if (!media.paused && media.currentTime > 2)
-              runner.succeed();
-          });
-        }, 0, 3000000);
-      xhr2.send();
-    });
-  xhr.send();
-};
-
-
-var testEventTimestamp = createConformanceTest('EventTimestamp');
-testEventTimestamp.prototype.title = 'Test Event Timestamp is relative to ' +
-    'the epoch';
-testEventTimestamp.prototype.onsourceopen = function() {
-  var runner = this.runner;
-  var video = this.video;
-  var videoSb = this.ms.addSourceBuffer(StreamDef.VideoType);
-  var audioSb = this.ms.addSourceBuffer(StreamDef.AudioType);
-  var last = Date.now();
-  runner.checkGr(last, 1360000000000, 'Date.now()');
-
-  var audioXhr = runner.XHRManager.createRequest('media/car-20120827-8b.mp4',
-    function(e) {
-      audioSb.append(this.getResponseData());
-      video.addEventListener('timeupdate', function(e) {
-        runner.checkGE(e.timeStamp, last, 'event.timeStamp');
-        last = e.timeStamp;
-        if (!video.paused && video.currentTime >= 2) {
-          runner.succeed();
-        }
-      });
-      video.play();
-    }, 0, 500000);
-
-  var videoXhr = runner.XHRManager.createRequest('media/car-20120827-85.mp4',
-    function(e) {
-      videoSb.append(this.getResponseData());
-      audioXhr.send();
-    }, 0, 1500000);
-  videoXhr.send();
-};
-
-
-var testDualKey = createConformanceTest('[OPTIONAL/NEW]DualKey');
-testDualKey.prototype.title = 'Tests multiple video keys';
-testDualKey.prototype.start = function(runner, video) {
-  var ms = new MediaSource();
-  var testEmeHandler = new EMEHandler();
-
-  var firstLicense = null;
-  var licenseTestPass = false;
-  testEmeHandler['_onLoad'] = testEmeHandler['onLoad'];
-  testEmeHandler['onLoad'] = function(initData, session, e) {
-    try {
-      testEmeHandler._onLoad(initData, session, e);
-    } catch (exp) {
-      if (firstLicense)
-        runner.fail('Adding second key failed. Perhaps the system does not ' +
-                    'support more than one video key?');
-      else
-        runner.fail('Failed to add first key.');
-    }
-
-    var licenseString = arrayToString(
-        new Uint8Array(e.target.response)).split('\r\n').pop();
-    if (!firstLicense)
-      firstLicense = licenseString;
-    else if (firstLicense !== licenseString)
-      licenseTestPass = true;
-    else
-      runner.fail('Somehow, the same key was used. This is a failure of the ' +
-                  'test video selection.');
-  };
-
-  testEmeHandler.init(video);
-
-  var kFlavorMap = {
-    playready: 'http://dash-mse-test.appspot.com/api/drm/playready?' +
-               'drm_system=playready&source=YOUTUBE&' +
-               'video_id=03681262dc412c06&ip=0.0.0.0&ipbits=0&' +
-               'expire=19000000000&' +
-               'sparams=ip,ipbits,expire,drm_system,source,video_id&' +
-               'signature=3BB038322E72D0B027F7233A733CD67D518AF675.' +
-               '2B7C39053DA46498D23F3BCB87596EF8FD8B1669&key=test_key1',
-    clearkey: 'http://dash-mse-test.appspot.com/api/drm/clearkey?' +
-              'drm_system=clearkey&source=YOUTUBE&video_id=03681262dc412c06&' +
-              'ip=0.0.0.0&ipbits=0&expire=19000000000&' +
-              'sparams=ip,ipbits,expire,drm_system,source,video_id&' +
-              'signature=065297462DF2ACB0EFC28506C5BA5E2E509864D3.' +
-              '1FEC674BBB2420DE6B0C7FE3ECD8740C58A43420&key=test_key1'
-  };
-
-  var kFlavorFiles = {
-    playready: [
-      'media/oops_cenc-20121114-145-no-clear-start.mp4',
-      'media/oops_cenc-20121114-145-143.mp4'],
-    clearkey: [
-      'media/oops_cenc-20121114-145-no-clear-start.mp4',
-      'media/oops_cenc-20121114-143-no-clear-start.mp4']
-  };
-
-  var keySystem = 'clearkey';
-  var keySystemQuery = /keysystem=([^&]*)/.exec(document.location.search);
-  if (keySystemQuery && kFlavorMap[keySystemQuery[1]]) {
-    keySystem = keySystemQuery[1];
-  }
-  try {
-    testEmeHandler.setFlavor(kFlavorMap, keySystem);
-  } catch (e) {
-    runner.fail('Browser does not support the requested key system: ' +
-                keySystem);
-    return;
-  }
-
-  function onError(e) {
-    runner.fail('Error reported in TestClearKeyNeedKey');
-  }
-
-  // Open two sources. When the second source finishes, it should also call
-  // onLoad above. onLoad will then check if the two keys are dissimilar.
-  function onSourceOpen(e) {
-    var sb = ms.addSourceBuffer('video/mp4; codecs="avc1.640028"');
-
-    var firstFile = new ResetInit(new FileSource(
-      kFlavorFiles[keySystem][0],
-      runner.XHRManager, runner.timeouts));
-
-    appendUntil(runner.timeouts, video, sb, firstFile, 5, function() {
-      sb.abort();
-
-      var secondFile = new ResetInit(new FileSource(
-        kFlavorFiles[keySystem][1],
-        runner.XHRManager, runner.timeouts));
-
-      appendInit(video, sb, secondFile, 0, function() {
-        sb.timestampOffset = video.buffered.end(0);
-        appendAt(runner.timeouts, video, sb, secondFile, 5, 5, function() {
-          video.play();
-        });
-      });
-    });
-
-    video.addEventListener('timeupdate', function onTimeUpdate() {
-      if (video.currentTime >= 10 - 1) {
-        video.removeEventListener('timeupdate', onTimeUpdate);
-        runner.succeed();
-      }
-    });
-  }
-
-  ms.addEventListener('sourceopen', onSourceOpen);
-  ms.addEventListener('webkitsourceopen', onSourceOpen);
-  video.addEventListener('error', onError);
-  video.src = window.URL.createObjectURL(ms);
-  video.load();
-};
-testDualKey.prototype.teardown = function() {};
-
-
-return {tests: tests, info: info, fields: fields, viewType: 'compact'};
-
-};
-
-// js/tests/2013/conformanceTest-20150612143746.js end
-
-// js/tests/2013/enduranceTest-20150612143746.js begin
-var EnduranceTest = function() {
-
-var tests = [];
-var info = 'Please use these tests to check for resource leaks or ' +
-    'accumulating issues.';
-var fields = ['elapsed'];
-
-var createEnduranceTest = function(name) {
-  var t = createMSTest(name);
-  t.prototype.index = tests.length;
-  t.prototype.elapsed = 0;
-  t.prototype.timeout = 2147483647;
-  tests.push(t);
-  return t;
-};
-
-var enableProgressUpdate = function(test, runner, media) {
-  test.prototype.elapsed = 0;
-  runner.updateStatus();
-
-  runner.timeouts.setInterval(function() {
-    test.prototype.elapsed = util.Round(media.currentTime, 3);
-    runner.updateStatus();
-  }, 1000);
-};
-
-var createOneShotTest = function(stream) {
-  var test = createEnduranceTest(util.MakeCapitalName(stream.name) + 'OneShot');
-  test.prototype.title = 'XHR and Play media once.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var sb = this.ms.addSourceBuffer(stream.type);
-
-    enableProgressUpdate(test, runner, media);
-
-    var xhr = runner.XHRManager.createRequest(stream.src,
-      function(e) {
-        sb.append(xhr.getResponseData());
-        var end = util.Round(sb.buffered.end(0), 2);
-        media.addEventListener('timeupdate', function(e) {
-          if (!media.paused && media.currentTime > end - 1) {
-            media.pause();
-            runner.succeed();
-          }
-        });
-        media.play();
-      });
-    xhr.send();
-  };
-};
-
-createOneShotTest(StreamDef.AudioNormal);
-createOneShotTest(StreamDef.VideoNormal);
-
-
-var createInfiniteLoopTest = function(stream) {
-  var test = createEnduranceTest('Infinite' +
-                                   util.MakeCapitalName(stream.name) + 'Loop');
-  test.prototype.title = 'Play in an infinite loop, good way to see if ' +
-      'there is any resource leak.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var chain = new InfiniteStream(new ResetInit(
-        new FileSource(stream.src, runner.XHRManager, runner.timeouts)));
-    var src = this.ms.addSourceBuffer(stream.type);
-
-    enableProgressUpdate(test, runner, media);
-
-    appendUntil(runner.timeouts, media, src, chain, 1, function() {
-      media.play();
-      playThrough(
-          runner.timeouts, media, 20, Infinity, src, chain, null, null,
-          function() {}
-      );
-    });
-  };
-};
-
-createInfiniteLoopTest(StreamDef.AudioNormal);
-createInfiniteLoopTest(StreamDef.VideoNormal);
-
-
-var createInfiniteAVLoopTest = function(audio, video, desc) {
-  var test = createEnduranceTest('InfiniteAVLoop' + desc);
-  test.prototype.times = 'n/a';
-  test.prototype.length = 'n/a';
-  test.prototype.title =
-    'Play in an infinite loop, good way to see if there is any resource leak.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var timeouts = runner.timeouts;
-    var media = this.video;
-    var video_chain = new InfiniteStream(new ResetInit(
-        new FileSource(video.src, runner.XHRManager, runner.timeouts)));
-    var video_src = this.ms.addSourceBuffer(StreamDef.VideoType);
-    var audio_chain = new InfiniteStream(new ResetInit(
-        new FileSource(audio.src, runner.XHRManager, runner.timeouts)));
-    var audio_src = this.ms.addSourceBuffer(StreamDef.AudioType);
-
-    enableProgressUpdate(test, runner, media);
-
-    media.addEventListener('needkey', function(e) {
-      e.target.generateKeyRequest('org.w3.clearkey', e.initData);
-    });
-
-    media.addEventListener('keymessage', function(e) {
-      var key = new Uint8Array([
-          0x1a, 0x8a, 0x20, 0x95, 0xe4, 0xde, 0xb2, 0xd2,
-          0x9e, 0xc8, 0x16, 0xac, 0x7b, 0xae, 0x20, 0x82]);
-      var key_id = new Uint8Array([
-          0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
-          0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e]);
-      e.target.addKey('org.w3.clearkey', key, key_id, e.sessionId);
-    });
-    appendUntil(timeouts, media, video_src, video_chain, 1, function() {
-      appendUntil(timeouts, media, audio_src, audio_chain, 1, function() {
-        media.play();
-        playThrough(
-            timeouts, media, 5, Infinity, video_src, video_chain,
-            audio_src, audio_chain, function() {}
-        );
-      });
-    });
-  };
-};
-
-createInfiniteAVLoopTest(StreamDef.AudioTiny, StreamDef.VideoTiny, 'Tiny');
-createInfiniteAVLoopTest(StreamDef.AudioNormal, StreamDef.VideoNormal,
-                         'Normal');
-createInfiniteAVLoopTest(StreamDef.AudioHuge, StreamDef.VideoHuge, 'Huge');
-
-createInfiniteAVLoopTest(StreamDef.AudioTinyClearKey,
-                         StreamDef.VideoTinyClearKey, 'TinyWithClearKey');
-createInfiniteAVLoopTest(StreamDef.AudioNormalClearKey,
-                         StreamDef.VideoNormalClearKey, 'NormalWithClearKey');
-createInfiniteAVLoopTest(StreamDef.AudioHugeClearKey,
-                         StreamDef.VideoHugeClearKey, 'HugeWithClearKey');
-
-var createSourceAbortTest = function(stream) {
-  var test = createEnduranceTest('Source Abort Test');
-  test.prototype.title = 'Source Abort Test.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var chain = new ResetInit(new FileSource(stream.src, runner.XHRManager,
-                                             runner.timeouts));
-    var src = this.ms.addSourceBuffer(stream.type);
-
-    test.prototype.times = 0;
-    test.prototype.min = 0;
-    test.prototype.max = 0;
-    test.prototype.average = 0;
-    runner.updateStatus();
-
-    var segs = [];
-    var i = 0;
-    var j = 0;
-    var k = 0;
-
-    function doTest() {
-      src.append(segs[0]);
-      if (i < segs[1].length) {
-        if (j < segs[2].length) {
-          if (k < segs[3].length) {
-            src.append(segs[1].subarray(0, i));
-            src.abort();
-            src.append(segs[2].subarray(0, j));
-            src.abort();
-            src.append(segs[3].subarray(0, k));
-            src.abort();
-            test.prototype.elapsed++;
-            runner.updateStatus();
-            k++;
-            if (k == segs[3].length) {
-              k = 0;
-              j++;
-              if (j == segs[2].length) {
-                j = 0;
-                i++;
-                if (i == segs[1].length) {
-                  runner.succeed();
-                  return;
-                }
-              }
-            }
-            runner.timeouts.setTimeout(doTest, 0);
-          }
-        }
-      }
-    }
-
-    chain.pull(function(data) {
-      segs.push(data);
-      chain.pull(function(data) {
-        segs.push(data);
-        chain.pull(function(data) {
-          segs.push(data);
-          chain.pull(function(data) {
-            segs.push(data);
-            doTest();
-          });
-        });
-      });
-    });
-  };
-};
-
-createSourceAbortTest(StreamDef.VideoHuge);
-
-/*
-var createInfiniteLoopYTCencTest = function(stream, keysystem, desc) {
-  var test = createEnduranceTest(
-      'Infinite' + util.MakeCapitalName(stream.name) + 'LoopWith' + desc);
-  test.prototype.times = 'âˆÅ¾';
-  test.prototype.length = 'âˆÅ¾';
-  test.prototype.title =
-    'Play in an infinite loop, good way to see if there is any resource leak.';
-  var extractBMFFClearKeyID = function(initData) {
-    // Accessing the Uint8Array's underlying ArrayBuffer is impossible, so we
-    // copy it to a new one for parsing.
-    var abuf = new ArrayBuffer(initData.length);
-    var view = new Uint8Array(abuf);
-    view.set(initData);
-
-    var dv = new DataView(abuf);
-    var pos = 0;
-    while (pos < abuf.byteLength) {
-      var box_size = dv.getUint32(pos, false);
-      var type = dv.getUint32(pos + 4, false);
-
-      if (type !== 0x70737368)
-        throw 'Box type ' + type.toString(16) + ' not equal to "pssh"';
-
-      if ((dv.getUint32(pos + 12, false) === 0x58147ec8) &&
-          (dv.getUint32(pos + 16, false) === 0x04234659) &&
-          (dv.getUint32(pos + 20, false) === 0x92e6f52c) &&
-          (dv.getUint32(pos + 24, false) === 0x5ce8c3cc)) {
-        var size = dv.getUint32(pos + 28, false);
-        if (size !== 16) throw 'Unexpected KID size ' + size;
-        return new Uint8Array(abuf.slice(pos + 32, pos + 32 + size));
-      }
-      pos += box_size;
-    }
-    // Couldn't find it, give up hope.
-    return initData;
-  };
-
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var timeouts = runner.timeouts;
-    var media = this.video;
-    var chain = new InfiniteStream(new ResetInit(
-        new FileSource(stream.src, runner.XHRManager, runner.timeouts)));
-    var src = this.ms.addSourceBuffer(stream.type);
-    var self = this;
-
-    media.addEventListener('needkey', function(e) {
-      if (keysystem.indexOf('clearkey') !== -1) {
-        self.initData = extractBMFFClearKeyID(e.initData);
-        console.log(e.initData);
-        console.log(self.initData);
-      } else {
-        self.initData = e.initData;
-      }
-      e.target.generateKeyRequest(keysystem, self.initData);
-    });
-
-    media.addEventListener('keymessage', function(e) {
-      var xhr = runner.XHRManager.createPostRequest(
-          // TODO: make this universal
-          'http://dash-mse-test.appspot.com/api/drm/clearkey?' +
-          'source=YOUTUBE&video_id=03681262dc412c06',
-          function() {
-        e.target.addKey('org.w3.clearkey', xhr.getResponseData(),
-                        self.initData, e.sessionId);
-      }, e.message.length);
-      xhr.send(e.message);
-    });
-
-    appendUntil(timeouts, media, src, chain, 1, function() {
-      media.play();
-      playThrough(
-          timeouts, media, 5, Infinity, src, chain, null, null, function() {}
-      );
-    });
-  };
-};
-
-createInfiniteLoopYTCencTest(StreamDef.VideoNormalYTCenc,
-                             'webkit-org.w3.clearkey', 'ClearKey');
-createInfiniteLoopYTCencTest(StreamDef.VideoNormalYTCenc,
-                             'com.youtube.playready', 'PlayReady');
-*/
-
-return {tests: tests, info: info, fields: fields, viewType: 'full'};
-
-};
-
-// js/tests/2013/enduranceTest-20150612143746.js end
-
-// js/tests/2013/performanceTest-20150612143746.js begin
-
-var PerformanceTest = function() {
-
-var tests = [];
-var info = 'These tests can evaluate the quality of the implementation.';
-var fields = ['times', 'min', 'max', 'average', 'baseline PC',
-    'baseline device'];
-
-function Profiler() {
-  var start = Date.now();
-  var last = Date.now();
-  var times = 0;
-
-  this.min = Infinity;
-  this.max = -Infinity;
-  this.average = 0;
-
-  this.tick = function() {
-    var curr = Date.now();
-    var elapsed = (curr - last) / 1000.;
-    last = curr;
-    ++times;
-    if (elapsed > this.max) this.max = elapsed;
-    if (elapsed < this.min) this.min = elapsed;
-    this.average = (curr - start) / times / 1000.;
-  };
-};
-
-var createPerformanceTest = function(name) {
-  var t = createMSTest(name);
-  t.prototype.index = tests.length;
-  t.prototype.times = 0;
-  t.prototype.min = 0;
-  t.prototype.max = 0;
-  t.prototype.average = 0;
-  t.prototype.baseline_PC = 'N/A';
-  t.prototype.baseline_device = 'N/A';
-  t.prototype.timeout = 2147483647;
-  tests.push(t);
-  return t;
-};
-
-
-var createCreateUint8ArrayTest = function(size, times, refPC, refDevice) {
-  var test = createPerformanceTest(
-      'create Uint8Array in ' + util.SizeToText(size));
-  test.prototype.baseline_PC = refPC;
-  test.prototype.baseline_device = refDevice;
-  test.prototype.title = 'Measure Uint8Array creation performance.';
-  test.prototype.start = function(runner, video) {
-    var profiler = new Profiler;
-    test.prototype.times = 0;
-    var array;
-    for (var i = 0; i < times; ++i) {
-      array = new Uint8Array(new ArrayBuffer(size));
-      array = new Uint8Array(array);
-      profiler.tick();
-      ++test.prototype.times;
-      test.prototype.min = profiler.min;
-      test.prototype.max = profiler.max;
-      test.prototype.average = util.Round(profiler.average, 3);
-      runner.updateStatus();
-    }
-    runner.succeed();
-  };
-};
-
-createCreateUint8ArrayTest(1024 * 1024, 1, 0.001, 0.002);
-
-
-var createXHRRequestTest = function(size, times) {
-  var test = createPerformanceTest('XHR Request in ' + util.SizeToText(size));
-  test.prototype.title = 'Measure XHR request performance.';
-  test.prototype.start = function(runner, video) {
-    var startTime = Date.now();
-    var profiler = new Profiler;
-    test.prototype.times = 0;
-    function startXHR(i) {
-      var xhr = runner.XHRManager.createRequest(
-          'media/car-20120827-85.mp4?x=' + Date.now() + '.' + i,
-          function() {
-            xhr.getResponseData();
-            profiler.tick();
-            ++test.prototype.times;
-            test.prototype.min = profiler.min;
-            test.prototype.max = profiler.max;
-            test.prototype.average = util.Round(profiler.average, 3);
-            runner.updateStatus();
-            if (i < times)
-              runner.timeouts.setTimeout(startXHR.bind(null, i + 1), 10);
-            else
-              runner.succeed();
-          }, 0, size);
-      xhr.send();
-    };
-    startXHR(1);
-  };
-};
-
-createXHRRequestTest(4096, 32);
-createXHRRequestTest(1024 * 1024, 16);
-createXHRRequestTest(4 * 1024 * 1024, 16);
-
-
-var createXHRAbortTest = function(size, times, refPC, refDevice) {
-  var test = createPerformanceTest('Abort XHR Request in ' +
-                                   util.SizeToText(size));
-  test.prototype.baseline_PC = refPC;
-  test.prototype.baseline_device = refDevice;
-  test.prototype.title = 'Measure how fast to abort XHR request.';
-  test.prototype.start = function(runner, video) {
-    var startTime = Date.now();
-    var profiler = new Profiler;
-    test.prototype.times = 0;
-    function startXHR(i) {
-      var xhr = runner.XHRManager.createRequest(
-          'media/car-20120827-85.mp4?x=' + Date.now() + '.' + i,
-          function() {});
-      xhr.send();
-      runner.timeouts.setTimeout(function() {
-        xhr.abort();
-        profiler.tick();
-        ++test.prototype.times;
-        test.prototype.min = profiler.min;
-        test.prototype.max = profiler.max;
-        test.prototype.average = util.Round(profiler.average, 3);
-        runner.updateStatus();
-        if (i < times)
-          startXHR(i + 1);
-        else
-          runner.succeed();
-      }, 0, size);
-    };
-    startXHR(1);
-  };
-};
-
-createXHRAbortTest(4096, 64, 0.098, 0.125);
-createXHRAbortTest(1024 * 1024, 64, 0.116, 0.14);
-createXHRAbortTest(4 * 1024 * 1024, 64, 0.126, 0.15);
-
-
-var createAppendTest = function(stream, size, times, refPC, refDevice) {
-  var test = createPerformanceTest('Append ' + util.SizeToText(size) +
-                                   ' to ' + stream.name + ' source buffer');
-  test.prototype.baseline_PC = refPC;
-  test.prototype.baseline_device = refDevice;
-  test.prototype.title = 'Measure source buffer append performance.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var sb = this.ms.addSourceBuffer(stream.type);
-    var xhr = runner.XHRManager.createRequest(stream.src,
-      function(e) {
-        var profiler = new Profiler;
-        var responseData = xhr.getResponseData();
-        test.prototype.times = 0;
-        for (var i = 0; i < times; ++i) {
-          sb.append(responseData);
-          sb.abort();
-          sb.timestampOffset = sb.buffered.end(sb.buffered.length - 1);
-          profiler.tick();
-          ++test.prototype.times;
-          test.prototype.min = profiler.min;
-          test.prototype.max = profiler.max;
-          test.prototype.average = util.Round(profiler.average, 3);
-          runner.updateStatus();
-        }
-        runner.succeed();
-      }, 0, size);
-    xhr.send();
-  };
-};
-
-createAppendTest(StreamDef.AudioNormal, 16384, 1024, 0.002, 0.12);
-createAppendTest(StreamDef.AudioNormal, 2 * 1024 * 1024, 128, 0.098, 0.19);
-createAppendTest(StreamDef.VideoNormal, 16384, 1024, 0.002, 0.1);
-createAppendTest(StreamDef.VideoNormal, 4 * 1024 * 1024, 64, 0.015, 0.15);
-
-
-var createSeekAccuracyTest = function(stream, size, times, step) {
-  var test = createPerformanceTest('Video Seek Accuracy Test');
-  test.prototype.baseline_PC = 0;
-  test.prototype.baseline_device = 0;
-  test.prototype.title = 'Measure video seeking accuracy.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var sb = this.ms.addSourceBuffer(stream.type);
-    var seekTime = 0;
-    var minimumTimeAfterSeek = Infinity;
-    var totalDiff = 0;
-    var xhr = runner.XHRManager.createRequest(stream.src,
-      function(e) {
-        test.prototype.times = 0;
-        test.prototype.min = Infinity;
-        test.prototype.max = 0;
-        sb.append(xhr.getResponseData());
-        sb.abort();
-        media.addEventListener('timeupdate', function(e) {
-          if (media.currentTime < minimumTimeAfterSeek)
-            minimumTimeAfterSeek = media.currentTime;
-        });
-        media.addEventListener('seeked', function(e) {
-          if (media.currentTime < minimumTimeAfterSeek)
-            minimumTimeAfterSeek = media.currentTime;
-          var diff = minimumTimeAfterSeek - seekTime;
-          totalDiff += diff;
-          ++test.prototype.times;
-          if (diff < test.prototype.min) test.prototype.min = diff;
-          if (diff > test.prototype.max) test.prototype.max = diff;
-          test.prototype.average =
-            util.Round(totalDiff / test.prototype.times, 3);
-          seekTime += step;
-          minimumTimeAfterSeek = Infinity;
-          runner.updateStatus();
-          if (seekTime < times)
-            media.currentTime = seekTime;
-          else
-            runner.succeed();
-        });
-        callAfterLoadedMetaData(media, function() {
-          media.play();
-          media.currentTime = seekTime;
-        });
-      }, 0, size);
-    xhr.send();
-  };
-};
-
-createSeekAccuracyTest(StreamDef.VideoNormal, 12 * 1024 * 1024, 100, 1);
-
-
-var createSeekBackwardsTest = function(audio, video) {
-  var test = createPerformanceTest('Seek Backwards Test');
-  test.prototype.baseline_PC = 0;
-  test.prototype.baseline_device = 0;
-  test.prototype.title = 'Measure seeking accuracy while seeking backwards.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var audio_chain = new ResetInit(
-        new FileSource(audio.src, runner.XHRManager, runner.timeouts));
-    var video_chain = new ResetInit(
-        new FileSource(video.src, runner.XHRManager, runner.timeouts));
-    var audio_src = this.ms.addSourceBuffer(audio.type);
-    var video_src = this.ms.addSourceBuffer(video.type);
-    var seekTime = video.duration - 5;
-    var minimumTimeAfterSeek = Infinity;
-    var totalDiff = 0;
-    var doingSeek = false;
-
-    test.prototype.times = 0;
-    test.prototype.min = 0;
-    test.prototype.max = 0;
-    runner.updateStatus();
-
-    var ontimeupdate = function() {
-      media.removeEventListener('timeupdate', ontimeupdate);
-      if (seekTime > 5) {
-        seekTime -= 1;
-        doSeek();
-      } else {
-        runner.succeed();
-      }
-    };
-
-    var onseeked = function() {
-      media.removeEventListener('seeked', onseeked);
-      media.addEventListener('timeupdate', ontimeupdate);
-    };
-
-    var doSeek = function() {
-      if (doingSeek) {
-        runner.timeouts.setTimeout(doSeek, 100);
-        return;
-      }
-      doingSeek = true;
-      media.addEventListener('seeked', onseeked);
-      audio_chain.seek(Math.max(seekTime, 0), audio_src);
-      video_chain.seek(seekTime, video_src);
-      media.currentTime = seekTime;
-
-      audio_chain.pull(function(data) {
-        audio_src.append(data);
-        audio_chain.pull(function(data) {
-          audio_src.append(data);
-          video_chain.pull(function(data) {
-            video_src.append(data);
-            video_chain.pull(function(data) {
-              video_src.append(data);
-              video_chain.pull(function(data) {
-                video_src.append(data);
-                doingSeek = false;
-              });
-            });
-          });
-        });
-      });
-    };
-
-    this.ms.duration = 100000000;  // Ensure that we can seek to any position.
-    audio_chain.init(0, function(data) {
-      audio_src.append(data);
-      video_chain.init(0, function(data) {
-        video_src.append(data);
-        media.play();
-        callAfterLoadedMetaData(media, doSeek);
-      });
-    });
-  };
-};
-
-createSeekBackwardsTest(StreamDef.AudioNormal, StreamDef.VideoNormal);
-
-
-var createBufferSizeTest = function(stream, refPC, refDevice) {
-  var test = createPerformanceTest(
-      'Buffer Size for ' + stream.name + ' in ' +
-      util.SizeToText(stream.bps) + ' bps');
-  test.prototype.baseline_PC = refPC;
-  test.prototype.baseline_device = refDevice;
-  test.prototype.title = 'Determines buffer sizes for different stream ' +
-      'types and qualites.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var sb = this.ms.addSourceBuffer(stream.type);
-    function startXHR() {
-      var size = Math.min(stream.size, 1024 * 1024);
-      var xhr = runner.XHRManager.createRequest(
-          stream.src,
-          function() {
-            var buf = xhr.getResponseData();
-            while (true) {
-              var old_end = sb.buffered.length ? sb.buffered.end(0) : 0;
-              sb.timestampOffset = old_end;
-              sb.append(buf);
-              sb.abort();
-              var new_end = sb.buffered.length ? sb.buffered.end(0) : 0;
-              test.prototype.min = Math.floor(new_end);
-              test.prototype.max = Math.floor(new_end);
-              test.prototype.average = Math.floor(new_end);
-              runner.updateStatus();
-              if (new_end <= old_end && new_end !== 0)
-                break;
-            }
-            runner.succeed();
-          }, 0, size);
-      xhr.send();
-    };
-    startXHR();
-  };
-};
-
-createBufferSizeTest(StreamDef.AudioTiny, 3147, 512);
-createBufferSizeTest(StreamDef.AudioNormal, 786, 128);
-createBufferSizeTest(StreamDef.AudioHuge, 393, 64);
-
-createBufferSizeTest(StreamDef.VideoTiny, 4610, 784);
-createBufferSizeTest(StreamDef.VideoNormal, 1062, 182);
-createBufferSizeTest(StreamDef.VideoHuge, 281, 47);
-
-
-var createPrerollSizeTest = function(stream, refPC, refDevice) {
-  var test = createPerformanceTest(
-      'Preroll Size for ' + stream.name + ' in ' +
-      util.SizeToText(stream.bps) + ' bps');
-  test.prototype.baseline_PC = refPC;
-  test.prototype.baseline_device = refDevice;
-  test.prototype.title = 'Determines preroll sizes for different stream ' +
-      'types and qualites.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var sb = this.ms.addSourceBuffer(stream.type);
-    var end = 0;
-
-    test.prototype.times = 0;
-    test.prototype.min = 0;
-    test.prototype.max = 0;
-    test.prototype.average = 0;
-    runner.updateStatus();
-
-    function timeupdate(e) {
-      if (this.currentTime) {
-        runner.succeed();
-      }
-    };
-
-    function append(buf) {
-      var size = buf.length;
-      while (buf.length) {
-        var appendSize = Math.min(1, buf.length);
-        sb.append(buf.subarray(0, appendSize));
-        buf = buf.subarray(appendSize);
-        ++test.prototype.times;
-        if (sb.buffered.length && sb.buffered.end(0) - end > 0.1) {
-          end = sb.buffered.end(0);
-          break;
-        }
-      }
-
-      test.prototype.min = util.Round(end, 3);
-      test.prototype.max = util.Round(end, 3);
-      test.prototype.average = util.Round(end, 3);
-      runner.updateStatus();
-      runner.timeouts.setTimeout(append.bind(null, buf), 500);
-    };
-
-    function startXHR() {
-      var size = Math.min(stream.size, 5 * 1024 * 1024);
-      var xhr = runner.XHRManager.createRequest(
-          stream.src,
-          function() {
-            var buf = new Uint8Array(new ArrayBuffer(size));
-            buf.set(xhr.getResponseData());
-            append(buf);
-          }, 0, size);
-      xhr.send();
-    };
-
-    this.video.addEventListener('timeupdate', timeupdate);
-    this.video.play();
-    startXHR();
-  };
-};
-
-createPrerollSizeTest(StreamDef.AudioTiny, 1.486, 0.557);
-createPrerollSizeTest(StreamDef.AudioNormal, 0.418, 0.209);
-createPrerollSizeTest(StreamDef.AudioHuge, 0.418, 0.209);
-
-createPrerollSizeTest(StreamDef.VideoTiny, 0.25, 0.751);
-createPrerollSizeTest(StreamDef.VideoNormal, 0.25, 0.667);
-createPrerollSizeTest(StreamDef.VideoHuge, 0.25, 0.584);
-
-
-var createSizeToPauseTest = function(stream, refPC, refDevice) {
-  var test = createPerformanceTest(
-      'Buffer Size Before Pausing ' + stream.name + ' in ' +
-      util.SizeToText(stream.bps) + ' bps');
-  test.prototype.baseline_PC = refPC;
-  test.prototype.baseline_device = refDevice;
-  test.prototype.title = 'Determines preroll sizes for different stream ' +
-      'types and qualites.';
-  test.prototype.onsourceopen = function() {
-    var runner = this.runner;
-    var media = this.video;
-    var chain = new ResetInit(new FileSource(stream.src, runner.XHRManager,
-                                             runner.timeouts));
-    var src = this.ms.addSourceBuffer(stream.type);
-
-    test.prototype.times = 0;
-    test.prototype.min = 0;
-    test.prototype.max = 0;
-    test.prototype.average = 0;
-    runner.updateStatus();
-
-    appendUntil(runner.timeouts, media, src, chain, 10, function() {
-      function timeupdate(e) {
-        if (this.currentTime) {
-          runner.timeouts.setTimeout(function() {
-            var gap = src.buffered.end(0) - media.currentTime;
-            gap = util.Round(gap, 3);
-            test.prototype.times = 1;
-            test.prototype.min = gap;
-            test.prototype.max = gap;
-            test.prototype.average = gap;
-            runner.updateStatus();
-            runner.succeed();
-          }, (src.buffered.end(0) + 3) * 1000);
-        }
-      };
-      media.addEventListener('timeupdate', timeupdate);
-      media.play();
-    });
-  };
-};
-
-createSizeToPauseTest(StreamDef.AudioTiny, 0, 0.094);
-createSizeToPauseTest(StreamDef.AudioNormal, 0, 0.047);
-createSizeToPauseTest(StreamDef.AudioHuge, 0, 0.047);
-
-createSizeToPauseTest(StreamDef.VideoTiny, 0.083, 0.043);
-createSizeToPauseTest(StreamDef.VideoNormal, 0.125, 0.084);
-createSizeToPauseTest(StreamDef.VideoHuge, 0.083, 0.043);
-
-return {tests: tests, info: info, fields: fields, viewType: 'full'};
-
-};
-
-// js/tests/2013/performanceTest-20150612143746.js end
-
-// js/tests/progressiveTest-20150612143746.js begin
-
-var ProgressiveTest = function() {
-
-var tests = [];
-var info = 'Default Timeout: ' + TestBase.timeout + 'ms';
-
-var fields = ['passes', 'failures', 'timeouts'];
-
-var createProgressiveTest = function(category, name, mandatory) {
-  var t = createTest(name);
-  t.prototype.category = category;
-  t.prototype.index = tests.length;
-  t.prototype.passes = 0;
-  t.prototype.failures = 0;
-  t.prototype.timeouts = 0;
-  t.prototype.mandatory = true;
-  if (typeof mandatory == 'boolean' && !mandatory)
-    t.prototype.mandatory = false;
-  tests.push(t);
-  return t;
-};
-
-
-var createInitialMediaStateTest = function(state, value, check) {
-  var test = createProgressiveTest('state before initial', state);
-
-  check = typeof(check) === 'undefined' ? 'checkEq' : check;
-  test.prototype.title = 'Test if the state ' + state +
-      ' is correct when media element is just created';
-  test.prototype.start = function(runner, video) {
-    test.prototype.status = util.formatStatus(util.getAttr(video, state));
-    runner[check](util.getAttr(video, state), value, state);
-    runner.succeed();
-  };
-};
-
-createInitialMediaStateTest('src', '');  // can actually be undefined
-createInitialMediaStateTest('currentSrc', '');
-createInitialMediaStateTest('defaultPlaybackRate', 1);
-createInitialMediaStateTest('playbackRate', 1);
-createInitialMediaStateTest('duration', NaN);
-createInitialMediaStateTest('paused', true);
-createInitialMediaStateTest('seeking', false);
-createInitialMediaStateTest('ended', false);
-createInitialMediaStateTest('videoWidth', 0);
-createInitialMediaStateTest('videoHeight', 0);
-createInitialMediaStateTest('buffered.length', 0);
-createInitialMediaStateTest('played.length', 0);
-createInitialMediaStateTest('seekable.length', 0);
-createInitialMediaStateTest('networkState', HTMLMediaElement.NETWORK_EMPTY);
-createInitialMediaStateTest('readyState', HTMLMediaElement.HAVE_NOTHING);
-
-
-var createMediaStateAfterSrcAssignedTest = function(state, value, check) {
-  var test = createProgressiveTest('state after src assigned', state);
-
-  check = typeof(check) === 'undefined' ? 'checkEq' : check;
-  test.prototype.title = 'Test if the state ' + state +
-      ' is correct when media element is a src has been assigned';
-  test.prototype.start = function(runner, video) {
-    video.src = StreamDef.ProgressiveLow.src;
-    test.prototype.status = util.formatStatus(util.getAttr(video, state));
-    runner[check](util.getAttr(video, state), value, state);
-    runner.succeed();
-  };
-};
-
-createMediaStateAfterSrcAssignedTest('networkState',
-                                     HTMLMediaElement.NETWORK_NO_SOURCE);
-createMediaStateAfterSrcAssignedTest('readyState',
-                                     HTMLMediaElement.HAVE_NOTHING);
-createMediaStateAfterSrcAssignedTest('src', '', 'checkNE');
-
-
-var createMediaStateInLoadStart = function(state, value, check) {
-  var test = createProgressiveTest('state in loadstart', state);
-
-  check = typeof(check) === 'undefined' ? 'checkEq' : check;
-  test.prototype.title = 'Test if the state ' + state +
-      ' is correct when media element is a src has been assigned';
-  test.prototype.start = function(runner, video) {
-    video.addEventListener('loadstart', function() {
-      test.prototype.status = util.formatStatus(util.getAttr(video, state));
-      runner[check](util.getAttr(video, state), value, state);
-      runner.succeed();
-    });
-    video.src = StreamDef.ProgressiveLow.src;
-  };
-};
-
-createMediaStateInLoadStart('networkState', HTMLMediaElement.NETWORK_LOADING);
-createMediaStateInLoadStart('readyState', HTMLMediaElement.HAVE_NOTHING);
-createMediaStateInLoadStart('currentSrc', '', 'checkNE');
-
-
-var createProgressTest = function() {
-  var test = createProgressiveTest('event', 'onprogress');
-
-  test.prototype.title = 'Test if there is progress event.';
-  test.prototype.start = function(runner, video) {
-    var self = this;
-    video.src = StreamDef.ProgressiveLow.src + '?' + Date.now();
-    video.addEventListener('progress', function() {
-      self.log('onprogress called');
-      runner.succeed();
-    });
-  };
-};
-
-createProgressTest();
-
-
-var createTimeUpdateTest = function() {
-  var test = createProgressiveTest('event', 'ontimeupdate');
-
-  test.prototype.title = 'Test if there is timeupdate event.';
-  test.prototype.start = function(runner, video) {
-    var self = this;
-    video.src = StreamDef.ProgressiveLow.src;
-    video.addEventListener('timeupdate', function() {
-      self.log('ontimeupdate called');
-      runner.succeed();
-    });
-    video.play();
-  };
-};
-
-createTimeUpdateTest();
-
-
-var createCanPlayTest = function() {
-  var test = createProgressiveTest('event', 'canplay');
-
-  test.prototype.title = 'Test if there is canplay event.';
-  test.prototype.start = function(runner, video) {
-    var self = this;
-    video.src = StreamDef.ProgressiveLow.src;
-    video.addEventListener('canplay', function() {
-      self.log('canplay called');
-      runner.succeed();
-    });
-  };
-};
-
-createCanPlayTest();
-
-
-var createAutoPlayTest = function() {
-  var test = createProgressiveTest('control', 'autoplay');
-
-  test.prototype.title = 'Test if autoplay works';
-  test.prototype.start = function(runner, video) {
-    var self = this;
-    video.autoplay = true;
-    video.src = StreamDef.ProgressiveLow.src;
-    video.addEventListener('timeupdate', function() {
-      self.log('ontimeupdate called');
-      runner.succeed();
-    });
-  };
-};
-
-createAutoPlayTest();
-
-
-var createNetworkStateTest = function() {
-  var test = createProgressiveTest('state', 'networkState', false);
-
-  test.prototype.title = 'Test if the network state is correct';
-  test.prototype.start = function(runner, video) {
-    var self = this;
-    video.src = StreamDef.ProgressiveLow.src;
-    video.addEventListener('suspend', function() {
-      self.log('onsuspend called');
-      runner.checkEq(video.networkState, HTMLMediaElement.NETWORK_IDLE,
-                     'networkState');
-      runner.succeed();
-    });
-  };
-};
-
-createNetworkStateTest();
-
-
-var createOnLoadedMetadataTest = function() {
-  var test = createProgressiveTest('event', 'onloadedmetadata');
-
-  test.prototype.title = 'Test if the onloadedmetadata is called correctly';
-  test.prototype.start = function(runner, video) {
-    video.addEventListener('loadedmetadata', function() {
-      runner.succeed();
-    });
-    video.src = 'getvideo.py';
-  };
-};
-
-
-// getvideo.py is not supported by AppEngine.
-// createOnLoadedMetadataTest();
-
-
-var createPlayingWithoutDataPaused = function() {
-  var test = createProgressiveTest('play without data', 'paused',
-                                   false);
-
-  test.prototype.title = 'Test if we can play without any data';
-  test.prototype.start = function(runner, video) {
-    video.src = 'hang.py';
-    video.play();
-    test.prototype.status = util.formatStatus(video.paused);
-    runner.checkEq(video.paused, false, 'video.paused');
-    runner.succeed();
-  };
-};
-
-createPlayingWithoutDataPaused();
-
-
-var createPlayingWithoutDataWaiting = function() {
-  var test = createProgressiveTest('play without data', 'onwaiting',
-                                   false);
-
-  test.prototype.title = 'Test if we can play without any data';
-  test.prototype.start = function(runner, video) {
-    video.addEventListener('waiting', function() {
-      runner.checkEq(video.currentTime, 0, 'video.currentTime');
-      runner.succeed();
-    });
-    video.src = 'hang.py';
-    video.play();
-  };
-};
-
-createPlayingWithoutDataWaiting();
-
-
-var createTimeUpdateMaxGranularity = function(suffix, playbackRatio) {
-  var test = createProgressiveTest(
-      'timeupdate', 'max granularity' + suffix, false);
-
-  test.prototype.title = 'Test the time update granularity.';
-  test.prototype.start = function(runner, video) {
-    var maxGranularity = 0;
-    var times = 0;
-    var last = 0;
-    video.addEventListener('suspend', function() {
-      video.playbackRate = playbackRatio;
-      video.play();
-      video.addEventListener('timeupdate', function() {
-        if (times !== 0) {
-          var interval = Date.now() - last;
-          if (interval > maxGranularity)
-            maxGranularity = interval;
-        }
-        if (times === 50) {
-          maxGranularity = maxGranularity / 1000.0;
-          test.prototype.status = util.Round(maxGranularity, 2);
-          runner.checkLE(maxGranularity, 0.26, 'maxGranularity');
-          runner.succeed();
-        }
-        last = Date.now();
-        ++times;
-      });
-    });
-    video.src = StreamDef.ProgressiveLow.src;
-  };
-};
-
-createTimeUpdateMaxGranularity('', 1.0);
-createTimeUpdateMaxGranularity(' slow motion', 0.2);
-createTimeUpdateMaxGranularity(' fast motion', 2.0);
-
-
-var createTimeUpdateMinGranularity = function(suffix, playbackRatio) {
-  var test = createProgressiveTest(
-      'timeupdate', 'min granularity' + suffix, false);
-
-  test.prototype.title = 'Test the time update granularity.';
-  test.prototype.start = function(runner, video) {
-    var minGranularity = Infinity;
-    var times = 0;
-    var last = 0;
-    video.addEventListener('suspend', function() {
-      video.playbackRate = playbackRatio;
-      video.play();
-      video.addEventListener('timeupdate', function() {
-        if (times !== 0) {
-          var interval = Date.now() - last;
-          if (interval > 1 && interval < minGranularity)
-            minGranularity = interval;
-        }
-        if (times === 50) {
-          minGranularity = minGranularity / 1000.0;
-          test.prototype.status = util.Round(minGranularity, 2);
-          runner.checkGE(minGranularity, 0.015, 'minGranularity');
-          runner.succeed();
-        }
-        last = Date.now();
-        ++times;
-      });
-    });
-    video.src = StreamDef.ProgressiveLow.src;
-  };
-};
-
-createTimeUpdateMinGranularity('', 1.0);
-createTimeUpdateMinGranularity(' slow motion', 0.2);
-createTimeUpdateMinGranularity(' fast motion', 2.0);
-
-
-var createTimeUpdateAccuracy = function() {
-  var test = createProgressiveTest('timeupdate', 'accuracy', false);
-
-  test.prototype.title = 'Test the time update granularity.';
-  test.prototype.start = function(runner, video) {
-    var maxTimeDiff = 0;
-    var baseTimeDiff = 0;
-    var times = 0;
-    video.addEventListener('suspend', function() {
-      video.play();
-      video.addEventListener('timeupdate', function() {
-        if (times === 0) {
-          baseTimeDiff = Date.now() / 1000.0 - video.currentTime;
-        } else {
-          var timeDiff = Date.now() / 1000.0 - video.currentTime;
-          maxTimeDiff = Math.max(Math.abs(timeDiff - baseTimeDiff),
-                                 maxTimeDiff);
-        }
-
-        if (times > 500 || video.currentTime > 10) {
-          test.prototype.status = util.Round(maxTimeDiff, 2);
-          runner.checkLE(maxTimeDiff, 0.5, 'maxTimeDiff');
-          runner.succeed();
-        }
-        ++times;
-      });
-    });
-    video.src = StreamDef.ProgressiveLow.src;
-  };
-};
-createTimeUpdateAccuracy();
-
-
-var createTimeUpdateProgressing = function() {
-  var test = createProgressiveTest('timeupdate', 'progressing', false);
-
-  test.prototype.title = 'Test if the time updates progress.';
-  test.prototype.start = function(runner, video) {
-    var last = 0;
-    var times = 0;
-    video.addEventListener('timeupdate', function() {
-      if (times === 0) {
-        last = video.currentTime;
-      } else {
-        runner.checkGE(video.currentTime, last, 'video.currentTime');
-        last = video.currentTime;
-      }
-
-      if (video.currentTime > 10) {
-        test.prototype.status = util.Round(video.currentTime, 2);
-        runner.succeed();
-      }
-      ++times;
-    });
-    video.src = StreamDef.ProgressiveLow.src;
-    video.play();
-  };
-};
-
-createTimeUpdateProgressing();
-
-
-var createTimeUpdateProgressingWithInitialSeek = function() {
-  var test = createProgressiveTest(
-      'timeupdate', 'progressing after seek', false);
-
-  test.prototype.title = 'Test if the time updates progress.';
-  test.prototype.start = function(runner, video) {
-    var last = 0;
-    var times = 0;
-    video.addEventListener('canplay', function() {
-      if (times == 0) {
-        video.currentTime = 0.001;
-        video.play();
-        video.addEventListener('timeupdate', function() {
-          if (times === 0) {
-            last = video.currentTime;
-          } else {
-            runner.checkGE(video.currentTime, last, 'video.currentTime');
-            last = video.currentTime;
-          }
-
-          if (video.currentTime > 10) {
-            test.prototype.status = util.Round(video.currentTime, 2);
-            runner.succeed();
-          }
-          ++times;
-        });
-      }
-    });
-    video.src = StreamDef.ProgressiveLow.src;
-  };
-};
-
-createTimeUpdateProgressingWithInitialSeek();
-
-
-var createTimeUpdateProgressingWithDurationCheck = function() {
-  var test = createProgressiveTest(
-      'timeupdate', 'duration on timeupdate', true);
-
-  test.prototype.title = 'Test if the duration is non-negative when time ' +
-      'updates.';
-  test.prototype.start = function(runner, video) {
-    video.addEventListener('timeupdate', function() {
-      runner.checkGE(video.duration, 0, 'video.duration');
-      if (video.currentTime > 1) {
-        runner.succeed();
-      }
-    });
-    video.src = StreamDef.ProgressiveLow.src;
-    video.play();
-  };
-};
-
-createTimeUpdateProgressingWithDurationCheck();
-
-return {tests: tests, info: info, fields: fields, viewType: 'compact'};
-
-};
-
-// js/tests/progressiveTest-20150612143746.js end
-
-// js/harness/main-20150612143746.js begin
-(function() {
-
-var timestamp;
-var command;
-var viewType;
-var timeout;
-var testsMask;
-
-var loadTests = function(testType) {
-  currentTestType = testType;
-
-  // We have to make it compatible to the legacy url format.
-  var testName = testType.substr(0, testType.indexOf('-'));
-  testName = util.MakeCapitalName(testName) + 'Test';
-  console.log(currentTestType);
-  console.log(testName);
-  return window[testName]();
-};
-
-var parseParam = function(param, defaultValue) {
-  var regex = new RegExp('(\\?|\\&)' + param + '=([-,\\w]+)', 'g');
-  var value = regex.exec(document.URL);
-  return value ? value[2] : defaultValue;
-};
-
-var parseParams = function() {
-  var testType = parseParam('test_type', kDefaultTestType);
-
-  if (!testTypes[testType]) {
-    Alert('Cannot find test type ' + testType);
-    throw 'Cannot find test type ' + testType;
-  }
-
-  timestamp = parseParam('timestamp');
-  // if (!timestamp) return;
-
-  command = parseParam('command');
-  viewType = parseParam('view_type');
-  TestBase.timeout = parseParam('timeout', TestBase.timeout);
-
-  var disableLog = parseParam('disable_log', 'false');
-  window.logging = disableLog !== 'true';
-  var loop = parseParam('loop', 'false');
-  window.loop = loop === 'true';
-  var stoponfailure = parseParam('stoponfailure', 'false');
-  window.stoponfailure = stoponfailure === 'true';
-  var enablewebm = parseParam('enablewebm', 'false');
-  window.enablewebm = enablewebm === 'true';
-
-  var tests = parseParam('tests');
-  var exclude = parseParam('exclude');
-
-  if (tests) {
-    testsMask = '';
-    tests = tests.split(',').map(function(x) {return parseInt(x);}).
-        sort(function(a, b) {return a - b;});
-    for (var i = 0; i < tests.length; ++i) {
-      var index = tests[i] * 1 - 1;
-      if (index < 0)
-        continue;
-      testsMask = util.resize(testsMask, index, '0');
-      testsMask += '1';
-    }
-    testsMask += '0';
-  } else if (exclude) {
-    exclude = exclude.split(',').map(function(x) {return parseInt(x);}).
-        sort(function(a, b) {return a - b;});
-    testsMask = '';
-    for (var i = 0; i < exclude.length; ++i) {
-      var index = exclude[i] * 1 - 1;
-      if (index < 0)
-        continue;
-      testsMask = util.resize(testsMask, index, '1');
-      testsMask += '0';
-    }
-    testsMask += '1';
-  } else {
-    testsMask = parseParam('tests_mask');
-    if (!testsMask)
-      testsMask = '1';
-  }
-
-  var testSuite = loadTests(testType);
-  if (viewType)
-    testSuite.viewType = viewType;
-  return testSuite;
-};
-
-window.globalRunner = null;
-
-var startRunner = function(testSuite, mseSpec) {
-  var id = 0;
-  var runner = new ConformanceTestRunner(testSuite, testsMask, mseSpec);
-
-  // Expose the runner so outside/injected scripts can read it.
-  window.globalRunner = runner;
-
-  runner.getNewVideoTag = function() {
-    var testarea = document.getElementById('testarea');
-    var vid = 'v' + id;
-    if (recycleVideoTag)
-      ++id;
-    if (!document.getElementById(vid)) {
-      testarea.innerHTML = '';
-      testarea.appendChild(util.createElement('video', vid, 'box-right'));
-      document.getElementById(vid).controls = true;
-    }
-    return document.getElementById(vid);
-  };
-
-  runner.getControlContainer = function() {
-    return document.getElementById('control');
-  };
-
-  window.LOG = function() {
-    if (!window.logging)
-      return;
-    var output = document.getElementById('output');
-    var text = '';
-
-    for (var i = 0; i < arguments.length; ++i)
-      text += arguments[i].toString() + ' ';
-
-    console.log(text);
-    output.value = text + '\n' + output.value;
-  };
-  runner.initialize();
-  if (command === 'run')
-    runner.startTest(0, runner.testList.length);
-};
-
-window.startMseTest = function(mseSpec) {
-  setupMsePortability(mseSpec);
-
-  var testSuite = parseParams();
-  if (!timestamp) {
-/*    if (!/\?/.test(document.URL))
-      window.location = document.URL + '?timestamp=' + (new Date()).getTime();
-    else
-      window.location = document.URL + '&timestamp=' + (new Date()).getTime();
-    return;*/
-  }
-  startRunner(testSuite, mseSpec);
-};
-
-})();
-
-// js/harness/main-20150612143746.js end
-    </script>
-  </head>
-  <body>
-    <script type="text/javascript">
-      window.setTimeout(function() { startMseTest(); }, 1);
-    </script>
-  </body>
-</html>
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/README.txt b/cobalt/demos/content/mse-eme-conformance-tests/README.txt
deleted file mode 100644
index 36fe3ee..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/README.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-The content of this folder will not work as local files as the media files
-have to be served in a web server in order to make it work with XMLHttpRequest.
-The content of this folder will be synced to
-/
-and it can be accessed via the following url:
-https://storage.googleapis.com/yt-dash-mse-eme-test/0.5.html
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/.gitignore b/cobalt/demos/content/mse-eme-conformance-tests/media/.gitignore
deleted file mode 100644
index cc2ac4e..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Ignore raw media files as they are not tracked inside git repository.  They
-# are synced via gclient hook "mse_eme_conformance_tests".
-*.mp4
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-85.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-85.mp4.sha1
deleted file mode 100644
index 0b7d073..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-85.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-525f4499f58ddd838c3290caccc8a9526dd3ce3b
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-86.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-86.mp4.sha1
deleted file mode 100644
index a3ed38f..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-86.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0dccf2486fa7d35503c574b31a0c9377aea2501b
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8b.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8b.mp4.sha1
deleted file mode 100644
index e89e1e8..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8b.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-6dbf8f08a19b54fb03e314d06514875ae7e935fd
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8c.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8c.mp4.sha1
deleted file mode 100644
index 96d89c6..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8c.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-2d0fc9b7d1db36c94eadad0c231bdae7a11b0d0d
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8d.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8d.mp4.sha1
deleted file mode 100644
index b92e419..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car-20120827-8d.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e3d0abec59fc5b136faffcd052e351c16c147922
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4.sha1
deleted file mode 100644
index 920fd78..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car-audio-1MB-trunc.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-1b24172d028d213e7d9133db3b52de52028736c0
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car_20130125_18.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car_20130125_18.mp4.sha1
deleted file mode 100644
index 9ab541e..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car_20130125_18.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-24fc78ce49cafb91b58fa60c35fe22a1d5b2cd2b
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4.sha1
deleted file mode 100644
index e957caf..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-85.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-978ef66d6cdf83a2474567d90402d25bf44b1582
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4.sha1
deleted file mode 100644
index 509d199..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8b.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f7c06a431837c2d1c323e04697369a8af0f797a9
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4.sha1
deleted file mode 100644
index ae579ac..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8c.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-edc4888006af3d734cfc803976380f15b7c55d08
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4.sha1
deleted file mode 100644
index fe20489..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/car_cenc-20120827-8d.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e26b56453f20e03cf114b9cba27c9c52a36bb451
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4.sha1
deleted file mode 100644
index 77df480..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames23-tfdt24.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0d35423701c8c864cf54af36182e517f68a783ec
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4.sha1
deleted file mode 100644
index 1124439..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/nq-frames24-tfdt23.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a146da2c2d755cca39f1df4cecdc7afd4e3323f0
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4.sha1
deleted file mode 100644
index c01deb9..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/oops_cenc-20121114-145-143.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-422d426dd9ec7367054f7c84a75a4b54b7562693
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/sintel-trunc.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/sintel-trunc.mp4.sha1
deleted file mode 100644
index 0a539dd..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/sintel-trunc.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-18b46d6d3b6b7815aa262d3868d5a33c5e8c6207
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/media/test-video-1MB.mp4.sha1 b/cobalt/demos/content/mse-eme-conformance-tests/media/test-video-1MB.mp4.sha1
deleted file mode 100644
index 8a99c26..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/media/test-video-1MB.mp4.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d0389c8f3342147a515d8ea29ec7ceacf8a60a97
\ No newline at end of file
diff --git a/cobalt/demos/content/mse-eme-conformance-tests/style-20150612143746.css b/cobalt/demos/content/mse-eme-conformance-tests/style-20150612143746.css
deleted file mode 100644
index 31c3b52..0000000
--- a/cobalt/demos/content/mse-eme-conformance-tests/style-20150612143746.css
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
-Copyright 2014 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.
-*/
-
-body {
-  background: #fff;
-  font-size: 10px;
-  margin: 30px;
-  width: 1280px;
-}
-
-textarea { color: #369; }
-
-h2 {
-  margin: 10px 0 0 0;
-}
-
-h3 {
-  margin: 10px 0 0 0;
-}
-
-.test-table .index { width: 30px; text-align: right; }
-.test-table .status {
-  width: 20px;
-  color: #fff;
-  text-align: right;
-}
-
-.test-table .status_current {
-  width: 20px;
-  text-align: right;
-}
-
-.test-table .desc { width: 200px; }
-.test-table .state { width: 30px; text-align: center; }
-.test-table .failstate { width: 30px; text-align: center; }
-
-.test-table {
-  width: 380px;
-  text-align: left;
-  margin: 0px;
-  display: inline-block;
-}
-
-.test-table th {
-  font-weight: bold;
-  color: #039;
-  padding: 10px 8px 4px;
-}
-
-.test-table td {
-  font-size: 9px;
-  color: #669;
-  padding: 9px 8px 0;
-}
-
-.test-table td.failstate {
-  font-size: 9px;
-  color: #f00;
-  padding: 9px 8px 0;
-}
-
-.test-table td.small {
-  font-size: 6px;
-  color: #0a0;
-  padding: 9px 8px 0;
-}
-
-.compact-list .cell-category {
-  font-weight: bold;
-  color: #039;
-}
-
-.compact-list .cell-divider {
-  width: 5px;
-}
-
-.compact-list .cell-status-normal {
-  text-align: center;
-}
-
-.compact-list .cell-status-running {
-  text-align: center;
-}
-
-.compact-list .cell-status-fail {
-  background-color: #800;
-  color: #FFF;
-  text-align: center;
-}
-
-.compact-list .cell-status-pass {
-  background-color: #080;
-  color: #FFF;
-  text-align: center;
-}
-
-.compact-list .test-status-none {
-  width: 5px;
-}
-
-.compact-list .test-status-running {
-  width: 5px;
-  background-color: #880;
-}
-
-.compact-list .test-status-fail {
-  width: 5px;
-  background-color: #800;
-}
-
-.compact-list .test-status-pass {
-  width: 5px;
-  background-color: #080;
-}
-
-.compact-list .test-status-optional-fail {
-  width: 3px;
-  padding: 0px;
-}
-
-ul {
-  padding-left: 20px;
-  margin-top: 5px;
-}
-
-div.container {
-  width:100%;
-  margin:5px 0 5px 0;
-  padding:5px 0 5px 0;
-  overflow:hidden;
-}
-
-div.container_hidden {
-  width:100%;
-  margin:10px 0 10px 0;
-  overflow:hidden;
-  display:none;
-}
-
-.box-left {
-  width:740px;
-}
-
-.box-right {
-  width:420px;
-}
-
-.desc-expl-popup {
-  position: absolute;
-  padding: 0.5em;
-  z-index: 100;
-  display: none;
-}
-
-span.nowrap {
-  display: inline-block;
-  margin: 0px 10px 0px 0px;
-  width: 200px;
-}
-
-span.code {
-}
-
-.rightmargin20 {
-  margin-left: 0px;
-  margin-right: 20px;
-}
-
-.rightmargin20new {
-  margin-left: 0px;
-  margin-right: 20px;
-  color: #f00;
-  font-weight: bold;
-}
-
-a.title_link {
-  color: #000;
-}
diff --git a/cobalt/demos/content/soft-mic-platform-service-demo/soft_mic_platform_service_demo.html b/cobalt/demos/content/soft-mic-platform-service-demo/soft_mic_platform_service_demo.html
new file mode 100644
index 0000000..31bc511
--- /dev/null
+++ b/cobalt/demos/content/soft-mic-platform-service-demo/soft_mic_platform_service_demo.html
@@ -0,0 +1,136 @@
+<!--
+This is a light weighted demo page used to verify SoftMicPlatformService.
+Start a http server by running this python3 command in the directory
+cobalt/demos/content/soft-mic-platform-service/:
+python3 -m http.server 8000
+Then run in Cobalt using this command:
+out/linux-x64x11_debug/cobalt --url=http://localhost:8000/soft_mic_platform_service_demo.html
+-->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<body>
+  <script>
+    'use strict';
+    /**
+    * @param {ArrayBuffer} data to be converted to a String.
+    */
+    function ab2str(data) {
+      try {
+        return String.fromCharCode.apply(null, new Uint8Array(data));
+      } catch(error) {
+        console.error(`ab2str() error: ${error}, decoding data: ${data}`);
+      }
+    }
+
+    /**
+    * @param {String} data to be converted to an ArrayBuffer.
+    */
+    function str2ab(data) {
+      try {
+        return Uint8Array.from(data.split(''), (s) => {return s.charCodeAt(0)}).buffer;
+      } catch(error) {
+        console.error(`str2ab() error: ${error}, decoding data: ${data}`);
+      }
+    }
+
+    async function testSoftMicPlatformService() {
+      // Set to true once the service.send() calls are complete.
+      var service_send_done = false;
+
+      // These default boolean values represent the default assumption for
+      // platforms that do not implement the extension.
+      var has_soft_mic = true;
+      var has_hard_mic = false;
+
+      if (!H5vccPlatformService) {
+        // H5vccPlatformService is not implemented. Fallback to current Soft Mic
+        // implementation.
+        console.error("H5vccPlatformService is not implemented");
+        return;
+      }
+
+      var SOFT_MIC_SERVICE_NAME = "com.google.youtube.tv.SoftMic";
+
+      if (!H5vccPlatformService.has(SOFT_MIC_SERVICE_NAME)) {
+        // SOFT_MIC_SERVICE_NAME is not implemented. Fallback to current
+        // Soft Mic implementation.
+        console.error(`H5vccPlatformService.Has(${SOFT_MIC_SERVICE_NAME}) returned false.`);
+        return;
+      }
+
+      /**
+      * @param {ArrayBuffer} data
+      */
+      function receiveCallback(service, data) {
+        var str_response = ab2str(data);
+
+        try {
+          var response = JSON.parse(str_response);
+          has_hard_mic = response["hasHardMicSupport"];
+          has_soft_mic = response["hasSoftMicSupport"];
+          var mic_gesture = response["micGesture"];
+          console.log(`receiveCallback() response:\n
+                       has_hard_mic: ${has_hard_mic}\n
+                       has_soft_mic: ${has_soft_mic}\n
+                       micGesture: ${mic_gesture}`);
+
+          // It is now safe to close the platform service.
+          if (service_send_done)
+            soft_mic_service.close();
+        } catch (error) {
+          console.error(`receiveCallback() error: ${error}, str_response: ${str_response}`);
+        }
+      }
+
+      // Open the service and pass the receive_callback.
+      var soft_mic_service = H5vccPlatformService.open(SOFT_MIC_SERVICE_NAME,
+                                  receiveCallback);
+
+      // Async web app message for "getMicSupport".
+      var get_mic_support_sync_response = soft_mic_service.send(str2ab(JSON.stringify("getMicSupport")));
+      try {
+        if (new Int8Array(get_mic_support_sync_response)[0])
+          console.log("getMicSupport send() platform response success.");
+        else
+          console.log("getMicSupport send() platform response failure.");
+      } catch (error) {
+        console.log(`Error in response from platform for getMicSupport: ${error}`);
+      }
+
+      // Test notifySearchActive send() and response from platform.
+      var notify_search_active_message = str2ab(JSON.stringify("notifySearchActive"));
+      var notify_search_active_response = soft_mic_service.send(notify_search_active_message);
+      try {
+        if (new Int8Array(notify_search_active_response)[0])
+          console.log("notifySearchActive send() platform response success.");
+        else
+          console.log("notifySearchActive send() platform response failure.");
+      } catch (error) {
+        console.log(`Error in response from platform for notifySearchActive: ${error}`);
+      }
+
+      // Test notifySearchInactive send() and response from platform.
+      var notify_search_inactive_message = str2ab(JSON.stringify("notifySearchInactive"));
+      var notify_search_inactive_response = soft_mic_service.send(notify_search_inactive_message);
+      try {
+        if (new Int8Array(notify_search_inactive_response)[0])
+          console.log("notifySearchInactive send() platform response success.");
+        else
+          console.log("notifySearchInactive send() platform response failure.");
+      } catch (error) {
+        console.log(`Error in response from platform for notifySearchInactive: ${error}`);
+      }
+
+      service_send_done = true;
+
+      // Close the service after a timeout. This is in case there is an error on
+      // the platform and a response is not received in the receiveCallback().
+      var TIME_BEFORE_CLOSE = 10000;
+      await new Promise(r => setTimeout(r, TIME_BEFORE_CLOSE));
+      soft_mic_service.close();
+    }
+
+    testSoftMicPlatformService();
+
+  </script>
+</body>
diff --git a/cobalt/dom/BUILD.gn b/cobalt/dom/BUILD.gn
index 04e1ce0..c594de0 100644
--- a/cobalt/dom/BUILD.gn
+++ b/cobalt/dom/BUILD.gn
@@ -358,6 +358,7 @@
     "//cobalt/system_window",
     "//cobalt/ui_navigation",
     "//cobalt/web_animations",
+    "//cobalt/worker",
     "//crypto",
     "//nb",
     "//net",
@@ -381,9 +382,97 @@
 }
 
 copy("licenses") {
+  install_content = true
   license_path =
       "licenses/platform/$cobalt_licenses_platform/licenses_cobalt.txt"
   sources = [ "$static_contents_source_dir/$license_path" ]
   outputs =
       [ "$sb_static_contents_output_data_dir/licenses/licenses_cobalt.txt" ]
 }
+
+target(gtest_target_type, "dom_test") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [
+    "application_lifecycle_state_test.cc",
+    "blob_test.cc",
+    "comment_test.cc",
+    "crypto_test.cc",
+    "csp_delegate_test.cc",
+    "custom_event_test.cc",
+    "document_test.cc",
+    "document_type_test.cc",
+    "dom_implementation_test.cc",
+    "dom_parser_test.cc",
+    "dom_rect_list_test.cc",
+    "dom_string_map_test.cc",
+    "dom_token_list_test.cc",
+    "element_test.cc",
+    "error_event_test.cc",
+    "event_queue_test.cc",
+    "event_target_test.cc",
+    "event_test.cc",
+    "font_cache_test.cc",
+    "html_element_factory_test.cc",
+    "html_element_test.cc",
+    "html_link_element_test.cc",
+    "intersection_observer_test.cc",
+    "keyboard_event_test.cc",
+    "local_storage_database_test.cc",
+    "location_test.cc",
+    "media_query_list_test.cc",
+    "mutation_observer_test.cc",
+    "named_node_map_test.cc",
+    "navigator_licenses_test.cc",
+    "node_dispatch_event_test.cc",
+    "node_list_live_test.cc",
+    "node_list_test.cc",
+    "node_test.cc",
+    "on_screen_keyboard_test.cc",
+    "performance_observer_test.cc",
+    "performance_test.cc",
+    "rule_matching_test.cc",
+    "screen_test.cc",
+    "serializer_test.cc",
+    "storage_area_test.cc",
+    "text_test.cc",
+    "time_ranges_test.cc",
+    "url_utils_test.cc",
+    "user_agent_data_test.cc",
+    "window_test.cc",
+    "window_timers_test.cc",
+    "xml_document_test.cc",
+  ]
+
+  deps = [
+    ":dom",
+    ":dom_exception",
+    "//cobalt/base",
+    "//cobalt/browser:browser",
+    "//cobalt/browser:generated_bindings",
+    "//cobalt/browser:generated_types",
+    "//cobalt/csp",
+    "//cobalt/css_parser",
+    "//cobalt/cssom",
+    "//cobalt/cssom:cssom_test",
+    "//cobalt/dom/testing:dom_testing",
+    "//cobalt/dom_parser",
+    "//cobalt/h5vcc",
+    "//cobalt/loader",
+    "//cobalt/media_session",
+    "//cobalt/network_bridge",
+    "//cobalt/network_bridge",
+    "//cobalt/render_tree",
+    "//cobalt/script",
+    "//cobalt/script/v8c:engine",
+    "//cobalt/storage",
+    "//cobalt/storage/store:memory_store",
+    "//cobalt/test:run_all_unittests",
+    "//nb",
+    "//net:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//url",
+  ]
+}
diff --git a/cobalt/dom/dom.gyp b/cobalt/dom/dom.gyp
index 4039e2c..c02e614 100644
--- a/cobalt/dom/dom.gyp
+++ b/cobalt/dom/dom.gyp
@@ -365,6 +365,7 @@
         '<(DEPTH)/cobalt/system_window/system_window.gyp:system_window',
         '<(DEPTH)/cobalt/ui_navigation/ui_navigation.gyp:ui_navigation',
         '<(DEPTH)/cobalt/web_animations/web_animations.gyp:web_animations',
+        '<(DEPTH)/cobalt/worker/worker.gyp:worker',
         '<(DEPTH)/nb/nb.gyp:nb',
         '<(DEPTH)/net/net.gyp:net',
         '<(DEPTH)/third_party/icu/icu.gyp:icuuc',
diff --git a/cobalt/dom/eme/media_key_session.cc b/cobalt/dom/eme/media_key_session.cc
index 71a1894..3eca031 100644
--- a/cobalt/dom/eme/media_key_session.cc
+++ b/cobalt/dom/eme/media_key_session.cc
@@ -17,7 +17,7 @@
 #include <memory>
 #include <type_traits>
 
-#include "base/polymorphic_downcast.h"
+#include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/dom_exception.h"
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/eme/eme_helpers.h"
diff --git a/cobalt/dom/eme/media_key_status_map.cc b/cobalt/dom/eme/media_key_status_map.cc
index bbaa8db..88b21c2 100644
--- a/cobalt/dom/eme/media_key_status_map.cc
+++ b/cobalt/dom/eme/media_key_status_map.cc
@@ -15,7 +15,7 @@
 #include "cobalt/dom/eme/media_key_status_map.h"
 
 #include "base/logging.h"
-#include "base/polymorphic_downcast.h"
+#include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/script/array_buffer.h"
 #include "cobalt/script/array_buffer_view.h"
diff --git a/cobalt/dom/navigator.cc b/cobalt/dom/navigator.cc
index 55fbf20..6e1e73d 100644
--- a/cobalt/dom/navigator.cc
+++ b/cobalt/dom/navigator.cc
@@ -25,6 +25,7 @@
 #include "cobalt/media_capture/media_devices.h"
 #include "cobalt/media_session/media_session_client.h"
 #include "cobalt/script/script_value_factory.h"
+#include "cobalt/worker/service_worker_container.h"
 #include "starboard/configuration_constants.h"
 #include "starboard/file.h"
 #include "starboard/media.h"
@@ -156,6 +157,7 @@
       plugins_(new PluginArray()),
       media_devices_(
           new media_capture::MediaDevices(settings, script_value_factory)),
+      service_worker_(new worker::ServiceWorkerContainer(script_value_factory)),
       system_caption_settings_(captions),
       script_value_factory_(script_value_factory) {}
 
@@ -234,6 +236,10 @@
   return media_devices_;
 }
 
+scoped_refptr<worker::ServiceWorkerContainer> Navigator::service_worker() {
+  return service_worker_;
+}
+
 const scoped_refptr<MimeTypeArray>& Navigator::mime_types() const {
   return mime_types_;
 }
diff --git a/cobalt/dom/navigator.h b/cobalt/dom/navigator.h
index 29e68cd..3fdb68e 100644
--- a/cobalt/dom/navigator.h
+++ b/cobalt/dom/navigator.h
@@ -31,6 +31,7 @@
 #include "cobalt/script/script_value_factory.h"
 #include "cobalt/script/sequence.h"
 #include "cobalt/script/wrappable.h"
+#include "cobalt/worker/service_worker_container.h"
 
 namespace cobalt {
 namespace dom {
@@ -71,6 +72,9 @@
   // Web API: MediaDevices
   scoped_refptr<media_capture::MediaDevices> media_devices();
 
+  // Web API: ServiceWorker
+  scoped_refptr<worker::ServiceWorkerContainer> service_worker();
+
   const scoped_refptr<MimeTypeArray>& mime_types() const;
   const scoped_refptr<PluginArray>& plugins() const;
 
@@ -135,6 +139,7 @@
   scoped_refptr<PluginArray> plugins_;
   scoped_refptr<cobalt::media_session::MediaSession> media_session_;
   scoped_refptr<cobalt::media_capture::MediaDevices> media_devices_;
+  scoped_refptr<cobalt::worker::ServiceWorkerContainer> service_worker_;
   scoped_refptr<cobalt::dom::captions::SystemCaptionSettings>
       system_caption_settings_;
   script::ScriptValueFactory* script_value_factory_;
diff --git a/cobalt/dom/testing/BUILD.gn b/cobalt/dom/testing/BUILD.gn
new file mode 100644
index 0000000..6cc8596
--- /dev/null
+++ b/cobalt/dom/testing/BUILD.gn
@@ -0,0 +1,49 @@
+# Copyright 2021 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.
+
+static_library("dom_testing") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [
+    "gtest_workarounds.h",
+    "html_collection_testing.h",
+    "mock_event_listener.h",
+    "mock_layout_boxes.h",
+    "stub_css_parser.cc",
+    "stub_css_parser.h",
+    "stub_environment_settings.h",
+    "stub_script_runner.cc",
+    "stub_script_runner.h",
+    "stub_window.h",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//cobalt/base",
+    "//cobalt/browser",
+    "//cobalt/browser:bindings",
+    "//cobalt/css_parser",
+    "//cobalt/cssom",
+    "//cobalt/dom",
+    "//cobalt/dom:dom_exception",
+    "//cobalt/dom_parser",
+    "//cobalt/loader",
+    "//cobalt/script",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//url",
+  ]
+}
diff --git a/cobalt/dom_parser/BUILD.gn b/cobalt/dom_parser/BUILD.gn
index f4f4c20..e4e4256 100644
--- a/cobalt/dom_parser/BUILD.gn
+++ b/cobalt/dom_parser/BUILD.gn
@@ -40,3 +40,23 @@
     "//third_party/protobuf:protobuf_lite",
   ]
 }
+
+target(gtest_target_type, "dom_parser_test") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [
+    "html_decoder_test.cc",
+    "xml_decoder_test.cc",
+  ]
+
+  deps = [
+    ":dom_parser",
+    "//cobalt/dom",
+    "//cobalt/dom/testing:dom_testing",
+    "//cobalt/loader",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/cobalt/encoding/BUILD.gn b/cobalt/encoding/BUILD.gn
index adb6e89..708a621 100644
--- a/cobalt/encoding/BUILD.gn
+++ b/cobalt/encoding/BUILD.gn
@@ -30,3 +30,24 @@
     "//third_party/icu:icuuc",
   ]
 }
+
+target(gtest_target_type, "text_encoding_test") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [
+    "text_decoder_test.cc",
+    "text_encoder_test.cc",
+  ]
+
+  deps = [
+    ":text_encoding",
+    "//cobalt/base",
+    "//cobalt/dom",
+    "//cobalt/dom/testing:dom_testing",
+    "//cobalt/script",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/cobalt/encoding/text_decoder.cc b/cobalt/encoding/text_decoder.cc
index b6abd56..1118c0f 100644
--- a/cobalt/encoding/text_decoder.cc
+++ b/cobalt/encoding/text_decoder.cc
@@ -143,7 +143,7 @@
   if (!do_not_flush_) {
     bom_seen_ = false;
   }
-  if (!ignore_bom_ && !bom_seen_) {
+  if (!ignore_bom_ && !bom_seen_ && length) {
     bom_seen_ = true;
     if (!RemoveBOM(start, length, exception_state)) {
       return;
diff --git a/cobalt/encoding/text_decoder_test.cc b/cobalt/encoding/text_decoder_test.cc
index 282a8d7..956a277 100644
--- a/cobalt/encoding/text_decoder_test.cc
+++ b/cobalt/encoding/text_decoder_test.cc
@@ -89,6 +89,7 @@
       .Times(0);
   scoped_refptr<TextDecoder> text_decoder_ = new TextDecoder(&exception_state_);
   std::vector<std::pair<std::vector<uint8>, std::string>> tests = {
+      {{}, ""},
       {{72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33},
        "Hello world!"},
       {{72,  101, 106, 33, 32, 208, 159, 209, 128, 208, 184, 208, 178, 208,
diff --git a/cobalt/extension/BUILD.gn b/cobalt/extension/BUILD.gn
new file mode 100644
index 0000000..2e1723b
--- /dev/null
+++ b/cobalt/extension/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2021 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.
+
+target(gtest_target_type, "extension_test") {
+  testonly = true
+  has_pedantic_warnings = true
+  sources = [ "extension_test.cc" ]
+  deps = [
+    "//cobalt/base",
+    "//cobalt/test:run_all_unittests",
+    "//starboard",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+  if (sb_is_evergreen) {
+    deps += cobalt_platform_dependencies
+  }
+  content_deps = [ "//third_party/icu:icudata" ]
+}
diff --git a/cobalt/extension/extension_test.cc b/cobalt/extension/extension_test.cc
index 43b4925..b7c0602 100644
--- a/cobalt/extension/extension_test.cc
+++ b/cobalt/extension/extension_test.cc
@@ -18,6 +18,7 @@
 #include "cobalt/extension/crash_handler.h"
 #include "cobalt/extension/cwrappers.h"
 #include "cobalt/extension/font.h"
+#include "cobalt/extension/free_space.h"
 #include "cobalt/extension/graphics.h"
 #include "cobalt/extension/installation_manager.h"
 #include "cobalt/extension/javascript_cache.h"
@@ -349,5 +350,24 @@
       << "Extension struct should be a singleton";
 }
 
+TEST(ExtensionTest, FreeSpace) {
+  typedef CobaltExtensionFreeSpaceApi ExtensionApi;
+  const char* kExtensionName = kCobaltExtensionFreeSpaceName;
+
+  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->MeasureFreeSpace, nullptr);
+
+  const ExtensionApi* second_extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
+  EXPECT_EQ(second_extension_api, extension_api)
+      << "Extension struct should be a singleton";
+}
 }  // namespace extension
 }  // namespace cobalt
diff --git a/cobalt/extension/free_space.h b/cobalt/extension/free_space.h
new file mode 100644
index 0000000..53d466b
--- /dev/null
+++ b/cobalt/extension/free_space.h
@@ -0,0 +1,50 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#ifndef COBALT_EXTENSION_FREE_SPACE_H_
+#define COBALT_EXTENSION_FREE_SPACE_H_
+
+#include <stdint.h>
+
+#include "starboard/system.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define kCobaltExtensionFreeSpaceName "dev.cobalt.extension.FreeSpace"
+
+typedef struct CobaltExtensionFreeSpaceApi {
+  // Name should be the string |kCobaltExtensionFreeSpaceName|.
+  // 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.
+
+  // Returns the free space in bytes for the provided |system_path_id|.
+  // If there is no implementation for the that |system_path_id| or
+  // if there was an error -1 is returned.
+  int64_t (*MeasureFreeSpace)(SbSystemPathId system_path_id);
+} CobaltExtensionFreeSpaceApi;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // COBALT_EXTENSION_FREE_SPACE_H_
diff --git a/cobalt/h5vcc/BUILD.gn b/cobalt/h5vcc/BUILD.gn
index b914711..96de253 100644
--- a/cobalt/h5vcc/BUILD.gn
+++ b/cobalt/h5vcc/BUILD.gn
@@ -111,7 +111,6 @@
       "h5vcc_updater.cc",
       "h5vcc_updater.h",
     ]
-    # TODO(b/211447021): Migrate //cobalt/updater
-    # deps += [ "//cobalt/updater" ]
+    deps += [ "//cobalt/updater" ]
   }
 }
diff --git a/cobalt/h5vcc/h5vcc.cc b/cobalt/h5vcc/h5vcc.cc
index 196c708..e1e9e04 100644
--- a/cobalt/h5vcc/h5vcc.cc
+++ b/cobalt/h5vcc/h5vcc.cc
@@ -28,6 +28,9 @@
   runtime_ = new H5vccRuntime(settings.event_dispatcher);
   settings_ =
       new H5vccSettings(settings.media_module, settings.network_module,
+#if SB_IS(EVERGREEN)
+                        settings.updater_module,
+#endif
                         settings.user_agent_data, settings.global_environment);
 #if defined(COBALT_ENABLE_SSO)
   sso_ = new H5vccSso();
diff --git a/cobalt/h5vcc/h5vcc_settings.cc b/cobalt/h5vcc/h5vcc_settings.cc
index 2607bed..83bf08b 100644
--- a/cobalt/h5vcc/h5vcc_settings.cc
+++ b/cobalt/h5vcc/h5vcc_settings.cc
@@ -21,18 +21,29 @@
 
 H5vccSettings::H5vccSettings(media::MediaModule* media_module,
                              cobalt::network::NetworkModule* network_module,
+#if SB_IS(EVERGREEN)
+                             cobalt::updater::UpdaterModule* updater_module,
+#endif
                              dom::NavigatorUAData* user_agent_data,
                              script::GlobalEnvironment* global_environment)
     : media_module_(media_module),
       network_module_(network_module),
+#if SB_IS(EVERGREEN)
+      updater_module_(updater_module),
+#endif
       user_agent_data_(user_agent_data),
-      global_environment_(global_environment) {}
+      global_environment_(global_environment) {
+}
 
 bool H5vccSettings::Set(const std::string& name, int32 value) const {
   const char kMediaPrefix[] = "Media.";
   const char kNavigatorUAData[] = "NavigatorUAData";
   const char kQUIC[] = "QUIC";
 
+#if SB_IS(EVERGREEN)
+  const char kUpdaterMinFreeSpaceBytes[] = "Updater.MinFreeSpaceBytes";
+#endif
+
   if (name.compare(kMediaPrefix) == 0) {
     return media_module_ ? media_module_->SetConfiguration(name, value) : false;
   }
@@ -51,6 +62,12 @@
     }
   }
 
+#if SB_IS(EVERGREEN)
+  if (name.compare(kUpdaterMinFreeSpaceBytes) == 0) {
+    updater_module_->SetMinFreeSpaceBytes(value);
+    return true;
+  }
+#endif
   return false;
 }
 
diff --git a/cobalt/h5vcc/h5vcc_settings.h b/cobalt/h5vcc/h5vcc_settings.h
index ec4d4c1..086a524 100644
--- a/cobalt/h5vcc/h5vcc_settings.h
+++ b/cobalt/h5vcc/h5vcc_settings.h
@@ -23,6 +23,10 @@
 #include "cobalt/script/global_environment.h"
 #include "cobalt/script/wrappable.h"
 
+#if SB_IS(EVERGREEN)
+#include "cobalt/updater/updater_module.h"
+#endif
+
 namespace cobalt {
 namespace h5vcc {
 
@@ -33,6 +37,9 @@
  public:
   explicit H5vccSettings(media::MediaModule* media_module,
                          cobalt::network::NetworkModule* network_module,
+#if SB_IS(EVERGREEN)
+                         cobalt::updater::UpdaterModule* updater_module,
+#endif
                          dom::NavigatorUAData* user_agent_data,
                          script::GlobalEnvironment* global_environment);
 
@@ -46,6 +53,9 @@
  private:
   media::MediaModule* media_module_;
   cobalt::network::NetworkModule* network_module_ = nullptr;
+#if SB_IS(EVERGREEN)
+  cobalt::updater::UpdaterModule* updater_module_ = nullptr;
+#endif
   dom::NavigatorUAData* user_agent_data_;
   script::GlobalEnvironment* global_environment_;
 
diff --git a/cobalt/h5vcc/h5vcc_trace_event.cc b/cobalt/h5vcc/h5vcc_trace_event.cc
index 5e27555..a7c509e 100644
--- a/cobalt/h5vcc/h5vcc_trace_event.cc
+++ b/cobalt/h5vcc/h5vcc_trace_event.cc
@@ -14,6 +14,8 @@
 
 #include "cobalt/h5vcc/h5vcc_trace_event.h"
 
+#include "base/files/file_util.h"
+
 namespace cobalt {
 namespace h5vcc {
 
@@ -29,17 +31,31 @@
   } else {
     base::FilePath output_filepath(
         output_filename.empty() ? kOutputTraceFilename : output_filename);
+    last_absolute_path_.clear();
     trace_to_file_.reset(new trace_event::ScopedTraceToFile(output_filepath));
   }
 }
 
 void H5vccTraceEvent::Stop() {
   if (trace_to_file_) {
+    last_absolute_path_ = trace_to_file_->absolute_output_path();
     trace_to_file_.reset();
   } else {
     DLOG(WARNING) << "H5vccTraceEvent is already stopped.";
   }
 }
 
+std::string H5vccTraceEvent::Read() {
+  if (trace_to_file_) {
+    Stop();
+  }
+  std::string trace;
+  if (!last_absolute_path_.empty()) {
+    ReadFileToString(last_absolute_path_, &trace);
+  }
+  return trace;
+}
+
+
 }  // namespace h5vcc
 }  // namespace cobalt
diff --git a/cobalt/h5vcc/h5vcc_trace_event.h b/cobalt/h5vcc/h5vcc_trace_event.h
index c1f2f8f..9621448 100644
--- a/cobalt/h5vcc/h5vcc_trace_event.h
+++ b/cobalt/h5vcc/h5vcc_trace_event.h
@@ -51,6 +51,7 @@
 
   void Start(const std::string& output_filename);
   void Stop();
+  std::string Read();
 
   TRACE_EVENT0_FOR_EACH(DEFINE_H5VCC_TRACE_EVENT0)
   TRACE_EVENT1_FOR_EACH(DEFINE_H5VCC_TRACE_EVENT1)
@@ -62,6 +63,8 @@
   // While initialized, it means that a trace is on-going.
   std::unique_ptr<trace_event::ScopedTraceToFile> trace_to_file_;
 
+  base::FilePath last_absolute_path_;
+
   DISALLOW_COPY_AND_ASSIGN(H5vccTraceEvent);
 };
 
diff --git a/cobalt/h5vcc/h5vcc_trace_event.idl b/cobalt/h5vcc/h5vcc_trace_event.idl
index 9fca122..3e1a23c 100644
--- a/cobalt/h5vcc/h5vcc_trace_event.idl
+++ b/cobalt/h5vcc/h5vcc_trace_event.idl
@@ -19,6 +19,7 @@
   // will be used.
   void start(optional DOMString output_filename = "");
   void stop();
+  DOMString read();
 
   void traceBegin(DOMString category, DOMString name);
   void traceEnd(DOMString category, DOMString name);
diff --git a/cobalt/layout_tests/BUILD.gn b/cobalt/layout_tests/BUILD.gn
new file mode 100644
index 0000000..01eb9e0
--- /dev/null
+++ b/cobalt/layout_tests/BUILD.gn
@@ -0,0 +1,87 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+static_library("layout_test_utils") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [
+    "layout_snapshot.cc",
+    "layout_snapshot.h",
+    "test_parser.cc",
+    "test_parser.h",
+    "test_utils.cc",
+    "test_utils.h",
+    "web_platform_test_parser.cc",
+    "web_platform_test_parser.h",
+  ]
+
+  deps = [
+    "//base/test:test_support",
+    "//cobalt/base",
+    "//cobalt/browser",
+    "//cobalt/cssom",
+    "//cobalt/layout_tests/testdata:layout_copy_test_data",
+    "//cobalt/math",
+    "//cobalt/network",
+    "//cobalt/render_tree",
+    "//cobalt/script",
+    "//net",
+    "//starboard:starboard_headers_only",
+    "//url",
+  ]
+}
+
+target(gtest_target_type, "layout_tests") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [ "layout_tests.cc" ]
+
+  deps = [
+    ":layout_test_utils",
+    "//cobalt/base",
+    "//cobalt/browser",
+    "//cobalt/cssom",
+    "//cobalt/math",
+    "//cobalt/render_tree:animations",
+    "//cobalt/renderer:render_tree_pixel_tester",
+    "//cobalt/renderer/backend:renderer_backend",
+    "//cobalt/script",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gtest",
+    "//url",
+  ]
+}
+
+target(gtest_target_type, "web_platform_tests") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [ "web_platform_tests.cc" ]
+
+  deps = [
+    ":layout_test_utils",
+    "//cobalt/base",
+    "//cobalt/browser",
+    "//cobalt/cssom",
+    "//cobalt/math",
+    "//cobalt/media",
+    "//cobalt/network",
+    "//cobalt/render_tree",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gtest",
+    "//url",
+  ]
+}
diff --git a/cobalt/layout_tests/testdata/BUILD.gn b/cobalt/layout_tests/testdata/BUILD.gn
new file mode 100644
index 0000000..1c8dbad
--- /dev/null
+++ b/cobalt/layout_tests/testdata/BUILD.gn
@@ -0,0 +1,1779 @@
+# Copyright 2021 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.
+
+copy("layout_copy_test_data") {
+  sources = [
+    "animation-timing/5-animation-frame-callback-is-called-before-layout-occurs-expected.png",
+    "animation-timing/5-animation-frame-callback-is-called-before-layout-occurs.html",
+    "animation-timing/layout_tests.txt",
+    "bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines-expected.png",
+    "bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines.html",
+    "bidi/containing-block-should-not-inherit-directionality-from-nested-block-box-expected.png",
+    "bidi/containing-block-should-not-inherit-directionality-from-nested-block-box.html",
+    "bidi/directional-stack-should-be-restored-following-nested-paragraph-expected.png",
+    "bidi/directional-stack-should-be-restored-following-nested-paragraph.html",
+    "bidi/inline-blocks-should-have-a-neutral-direction-within-containing-paragraph-and-open-nested-paragraph-expected.png",
+    "bidi/inline-blocks-should-have-a-neutral-direction-within-containing-paragraph-and-open-nested-paragraph.html",
+    "bidi/inline-blocks-should-not-impact-directionality-of-containing-block-expected.png",
+    "bidi/inline-blocks-should-not-impact-directionality-of-containing-block.html",
+    "bidi/inline-container-blocks-should-not-impact-directionality-of-containing-block-expected.png",
+    "bidi/inline-container-blocks-should-not-impact-directionality-of-containing-block.html",
+    "bidi/inline-container-boxes-should-continue-active-paragraph-expected.png",
+    "bidi/inline-container-boxes-should-continue-active-paragraph.html",
+    "bidi/layout_tests.txt",
+    "bidi/line-boxes-should-use-containing-block-direction-for-inline-base-direction-expected.png",
+    "bidi/line-boxes-should-use-containing-block-direction-for-inline-base-direction.html",
+    "bidi/mirror-characters-should-be-reversed-in-rtl-expected.png",
+    "bidi/mirror-characters-should-be-reversed-in-rtl.html",
+    "bidi/nested-block-boxes-should-close-active-paragraph-expected.png",
+    "bidi/nested-block-boxes-should-close-active-paragraph.html",
+    "bidi/nested-block-boxes-should-inherit-directionality-from-containing-block-expected.png",
+    "bidi/nested-block-boxes-should-inherit-directionality-from-containing-block.html",
+    "bidi/numbers-should-have-weak-directionality-1-expected.png",
+    "bidi/numbers-should-have-weak-directionality-1.html",
+    "bidi/numbers-should-have-weak-directionality-2-expected.png",
+    "bidi/numbers-should-have-weak-directionality-2.html",
+    "bidi/numbers-should-have-weak-directionality-3-expected.png",
+    "bidi/numbers-should-have-weak-directionality-3.html",
+    "bidi/paragraph-should-maintain-directional-stack-with-nested-inline-container-blocks-expected.png",
+    "bidi/paragraph-should-maintain-directional-stack-with-nested-inline-container-blocks.html",
+    "bidi/whitespace-should-be-handled-properly-with-bidirectional-text-expected.png",
+    "bidi/whitespace-should-be-handled-properly-with-bidirectional-text.html",
+    "cluster-fuzz/fuzz-222-expected.png",
+    "cluster-fuzz/fuzz-222.html",
+    "cluster-fuzz/layout_tests.txt",
+    "cobalt-pixel/aliasing-solid-borders-expected.png",
+    "cobalt-pixel/aliasing-solid-borders.html",
+    "cobalt-pixel/aliasing-solid-color-expected.png",
+    "cobalt-pixel/aliasing-solid-color.html",
+    "cobalt-pixel/aliasing-texture-expected.png",
+    "cobalt-pixel/aliasing-texture.html",
+    "cobalt-pixel/layout_tests.txt",
+    "cobalt/100-dynamically-created-nested-elements-expected.png",
+    "cobalt/100-dynamically-created-nested-elements.html",
+    "cobalt/100-nested-elements-expected.png",
+    "cobalt/100-nested-elements.html",
+    "cobalt/README.txt",
+    "cobalt/block-and-inline-block-display-expected.png",
+    "cobalt/block-and-inline-block-display.html",
+    "cobalt/btoa-with-null-char-expected.png",
+    "cobalt/btoa-with-null-char.html",
+    "cobalt/changing-css-text-triggers-layout-expected.png",
+    "cobalt/changing-css-text-triggers-layout.html",
+    "cobalt/cobalt-oxide-expected.png",
+    "cobalt/console-trace-should-not-crash-expected.png",
+    "cobalt/console-trace-should-not-crash.html",
+    "cobalt/console-trace-should-not-crash.js",
+    "cobalt/div-with-border-of-non-integer-size-expected.png",
+    "cobalt/div-with-border-of-non-integer-size.html",
+    "cobalt/divs-with-background-color-and-text-expected.png",
+    "cobalt/divs-with-background-color-and-text.html",
+    "cobalt/fixed-width-divs-with-background-color-expected.png",
+    "cobalt/fixed-width-divs-with-background-color.html",
+    "cobalt/font-weight-expected.png",
+    "cobalt/font-weight.html",
+    "cobalt/image-cleanup-expected.png",
+    "cobalt/image-cleanup.html",
+    "cobalt/image-from-blob-expected.png",
+    "cobalt/image-from-blob.html",
+    "cobalt/image-onerror-expected.png",
+    "cobalt/image-onerror.html",
+    "cobalt/inline-box-with-overflow-words-expected.png",
+    "cobalt/inline-box-with-overflow-words.html",
+    "cobalt/inline-style-allowed-while-cloning-objects-expected.png",
+    "cobalt/inline-style-allowed-while-cloning-objects.html",
+    "cobalt/interface-object-types-are-correct-expected.png",
+    "cobalt/interface-object-types-are-correct.html",
+    "cobalt/layout_tests.txt",
+    "cobalt/onload_event_fired_even_though_link_file_does_not_exist-expected.png",
+    "cobalt/onload_event_fired_even_though_link_file_does_not_exist.html",
+    "cobalt/performance-spike-header-buttons.html",
+    "cobalt/platform-object-user-properties-survive-gc-expected.png",
+    "cobalt/platform-object-user-properties-survive-gc.html",
+    "cobalt/positioned-boxes-with-same-z-index-are-processed-in-insertion-order-expected.png",
+    "cobalt/positioned-boxes-with-same-z-index-are-processed-in-insertion-order.html",
+    "cobalt/relative-font-size-expected.png",
+    "cobalt/relative-font-size.html",
+    "cobalt/repro-27290784-expected.png",
+    "cobalt/repro-27290784.html",
+    "cobalt/screenshot-expected.png",
+    "cobalt/screenshot-with-animation-expected.png",
+    "cobalt/screenshot-with-animation.html",
+    "cobalt/screenshot.html",
+    "cobalt/simple-transform-stacked-expected.png",
+    "cobalt/simple-transform-stacked.html",
+    "cobalt/simple-transform-text-expected.png",
+    "cobalt/simple-transform-text.html",
+    "cobalt/support/MEgalopolisExtra.woff2",
+    "cobalt/support/tcu-font.woff",
+    "cobalt/transform-translate-with-em-units-expected.png",
+    "cobalt/transform-translate-with-em-units.html",
+    "cobalt/transform-with-background-color-expected.png",
+    "cobalt/transform-with-background-color.html",
+    "cobalt/url-utils-interfaces-expected.png",
+    "cobalt/url-utils-interfaces.html",
+    "cobalt/user-agent-style-sheet-display-expected.png",
+    "cobalt/user-agent-style-sheet-display.html",
+    "cobalt/user-agent-test-expected.png",
+    "cobalt/user-agent-test.html",
+    "cobalt/window-onerror-expected.png",
+    "cobalt/window-onerror.html",
+    "cobalt/woff2-decoding-expected.png",
+    "cobalt/woff2-decoding.html",
+    "csp/img-src-expected.png",
+    "csp/img-src.html",
+    "csp/layout_tests.txt",
+    "css-2-1/10-1-absolute-elements-inherit-style-from-direct-parent-expected.png",
+    "css-2-1/10-1-absolute-elements-inherit-style-from-direct-parent.html",
+    "css-2-1/10-1-absolute-positioned-elements-are-positioned-relative-to-parent-element-flow-expected.png",
+    "css-2-1/10-1-absolute-positioned-elements-are-positioned-relative-to-parent-element-flow.html",
+    "css-2-1/10-1-absolute-positioned-elements-are-positioned-relative-to-parent-inline-box-expected.png",
+    "css-2-1/10-1-absolute-positioned-elements-are-positioned-relative-to-parent-inline-box.html",
+    "css-2-1/10-1-absolute-positioned-elements-compute-used-values-from-containing-block-expected.png",
+    "css-2-1/10-1-absolute-positioned-elements-compute-used-values-from-containing-block.html",
+    "css-2-1/10-1-absolute-positioned-elements-container-block-is-absolute-positioned-ancestor-expected.png",
+    "css-2-1/10-1-absolute-positioned-elements-container-block-is-absolute-positioned-ancestor.html",
+    "css-2-1/10-1-absolute-positioned-elements-do-not-effect-containing-block-size-expected.png",
+    "css-2-1/10-1-absolute-positioned-elements-do-not-effect-containing-block-size.html",
+    "css-2-1/10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements-expected.png",
+    "css-2-1/10-1-containing-block-above-stacking-context-should-be-padding-edge-for-absolute-positioned-elements.html",
+    "css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-absolutely-positioned-elements-expected.png",
+    "css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-absolutely-positioned-elements.html",
+    "css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements-expected.png",
+    "css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-fixed-positioned-elements.html",
+    "css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-percentage-of-absolutely-positioned-elements-expected.png",
+    "css-2-1/10-1-containing-block-should-be-ancestor-padding-edge-for-percentage-of-absolutely-positioned-elements.html",
+    "css-2-1/10-1-non-positioned-stacking-context-above-containing-block-should-apply-proper-offset-to-children-expected.png",
+    "css-2-1/10-1-non-positioned-stacking-context-above-containing-block-should-apply-proper-offset-to-children.html",
+    "css-2-1/10-1-positioned-stacking-context-above-fixed-containing-block-should-apply-proper-offset-to-children-expected.png",
+    "css-2-1/10-1-positioned-stacking-context-above-fixed-containing-block-should-apply-proper-offset-to-children.html",
+    "css-2-1/10-3-1-auto-margin-should-become-zero-in-inline-non-replaced-elements-expected.png",
+    "css-2-1/10-3-1-auto-margin-should-become-zero-in-inline-non-replaced-elements.html",
+    "css-2-1/10-3-1-width-should-not-apply-to-inline-non-replaced-elements-expected.png",
+    "css-2-1/10-3-1-width-should-not-apply-to-inline-non-replaced-elements.html",
+    "css-2-1/10-3-2-auto-margin-should-become-zero-in-inline-replaced-elements-expected.png",
+    "css-2-1/10-3-2-auto-margin-should-become-zero-in-inline-replaced-elements.html",
+    "css-2-1/10-3-2-replaced-box-width-expected.png",
+    "css-2-1/10-3-2-replaced-box-width.html",
+    "css-2-1/10-3-3-auto-margin-left-and-right-should-center-element-horizontally-expected.png",
+    "css-2-1/10-3-3-auto-margin-left-and-right-should-center-element-horizontally.html",
+    "css-2-1/10-3-3-auto-width-should-zero-other-autos-expected.png",
+    "css-2-1/10-3-3-auto-width-should-zero-other-autos.html",
+    "css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-expected.png",
+    "css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-rtl-expected.png",
+    "css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained-rtl.html",
+    "css-2-1/10-3-3-correct-margin-is-ignored-if-overconstrained.html",
+    "css-2-1/10-3-3-margin-auto-should-be-treated-as-zero-if-sum-is-greater-than-containing-block-width-expected.png",
+    "css-2-1/10-3-3-margin-auto-should-be-treated-as-zero-if-sum-is-greater-than-containing-block-width.html",
+    "css-2-1/10-3-3-one-auto-should-follow-from-equality-expected.png",
+    "css-2-1/10-3-3-one-auto-should-follow-from-equality.html",
+    "css-2-1/10-3-3-overconstrained-should-respect-direction-expected.png",
+    "css-2-1/10-3-3-overconstrained-should-respect-direction.html",
+    "css-2-1/10-3-4-block-level-replaced-box-margins-should-be-calculated-as-for-non-replaced-box-expected.png",
+    "css-2-1/10-3-4-block-level-replaced-box-margins-should-be-calculated-as-for-non-replaced-box.html",
+    "css-2-1/10-3-4-block-level-replaced-box-width-should-be-calculated-as-for-inline-replaced-box-expected.png",
+    "css-2-1/10-3-4-block-level-replaced-box-width-should-be-calculated-as-for-inline-replaced-box.html",
+    "css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit-expected.png",
+    "css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit-rtl-expected.png",
+    "css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit-rtl.html",
+    "css-2-1/10-3-7-absolute-element-children-should-shrink-to-fit.html",
+    "css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-expected.png",
+    "css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-rtl-expected.png",
+    "css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit-rtl.html",
+    "css-2-1/10-3-7-absolute-element-children-with-br-elements-should-shrink-to-fit.html",
+    "css-2-1/10-3-7-absolute-position-elements-solve-for-height-when-top-and-bottom-are-specified-expected.png",
+    "css-2-1/10-3-7-absolute-position-elements-solve-for-height-when-top-and-bottom-are-specified.html",
+    "css-2-1/10-3-7-absolute-position-elements-solve-for-width-when-left-and-right-are-specified-expected.png",
+    "css-2-1/10-3-7-absolute-position-elements-solve-for-width-when-left-and-right-are-specified.html",
+    "css-2-1/10-3-7-blockification-should-not-affect-static-position-expected.png",
+    "css-2-1/10-3-7-blockification-should-not-affect-static-position-rtl-expected.png",
+    "css-2-1/10-3-7-blockification-should-not-affect-static-position-rtl.html",
+    "css-2-1/10-3-7-blockification-should-not-affect-static-position.html",
+    "css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-expected.png",
+    "css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-rtl-expected.png",
+    "css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block-rtl.html",
+    "css-2-1/10-3-7-left-and-top-position-absolute-elements-relative-to-their-containing-block.html",
+    "css-2-1/10-3-7-left-and-width-and-right-are-auto-expected.png",
+    "css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element-expected.png",
+    "css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element-rtl-expected.png",
+    "css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element-rtl.html",
+    "css-2-1/10-3-7-left-and-width-and-right-are-auto-in-inline-element.html",
+    "css-2-1/10-3-7-left-and-width-and-right-are-auto-rtl-expected.png",
+    "css-2-1/10-3-7-left-and-width-and-right-are-auto-rtl.html",
+    "css-2-1/10-3-7-left-and-width-and-right-are-auto.html",
+    "css-2-1/10-3-7-multiple-absolute-positioned-elements-share-same-static-position-expected.png",
+    "css-2-1/10-3-7-multiple-absolute-positioned-elements-share-same-static-position.html",
+    "css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto-expected.png",
+    "css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto-rtl-expected.png",
+    "css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto-rtl.html",
+    "css-2-1/10-3-7-none-of-left-and-width-and-right-is-auto.html",
+    "css-2-1/10-3-7-position-absolute-should-apply-rules-that-dictate-shrink-to-fit.html",
+    "css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto-expected.png",
+    "css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto-rtl-expected.png",
+    "css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto-rtl.html",
+    "css-2-1/10-3-7-some-of-left-and-width-and-right-are-auto.html",
+    "css-2-1/10-3-7-static-position-for-block-level-elements-expected.png",
+    "css-2-1/10-3-7-static-position-for-block-level-elements-rtl-expected.png",
+    "css-2-1/10-3-7-static-position-for-block-level-elements-rtl.html",
+    "css-2-1/10-3-7-static-position-for-block-level-elements.html",
+    "css-2-1/10-3-9-auto-margin-should-become-zero-in-inline-block-non-replaced-elements-expected.png",
+    "css-2-1/10-3-9-auto-margin-should-become-zero-in-inline-block-non-replaced-elements.html",
+    "css-2-1/10-3-9-child-width-should-be-calculated-after-width-of-inline-block-parent-expected.png",
+    "css-2-1/10-3-9-child-width-should-be-calculated-after-width-of-inline-block-parent.html",
+    "css-2-1/10-3-9-inline-block-non-replaced-and-br-elements-with-auto-width-should-shrink-to-fit-expected.png",
+    "css-2-1/10-3-9-inline-block-non-replaced-and-br-elements-with-auto-width-should-shrink-to-fit.html",
+    "css-2-1/10-3-9-inline-block-non-replaced-elements-with-auto-width-should-shrink-to-fit-expected.png",
+    "css-2-1/10-3-9-inline-block-non-replaced-elements-with-auto-width-should-shrink-to-fit.html",
+    "css-2-1/10-3-9-shrink-to-fit-width-should-not-depend-on-text-align-expected.png",
+    "css-2-1/10-3-9-shrink-to-fit-width-should-not-depend-on-text-align.html",
+    "css-2-1/10-4-min-height-and-max-height-limit-box-size-expected.png",
+    "css-2-1/10-4-min-height-and-max-height-limit-box-size.html",
+    "css-2-1/10-4-min-height-and-max-height-percentage-should-refer-containing-block-height-expected.png",
+    "css-2-1/10-4-min-height-and-max-height-percentage-should-refer-containing-block-height.html",
+    "css-2-1/10-4-min-width-and-max-width-constraints-for-replaced_elements-expected.png",
+    "css-2-1/10-4-min-width-and-max-width-constraints-for-replaced_elements.html",
+    "css-2-1/10-4-min-width-and-max-width-limit-box-size-expected.png",
+    "css-2-1/10-4-min-width-and-max-width-limit-box-size.html",
+    "css-2-1/10-4-min-width-and-max-width-percentage-should-refer-containing-block-width-expected.png",
+    "css-2-1/10-4-min-width-and-max-width-percentage-should-refer-containing-block-width.html",
+    "css-2-1/10-5-percentage-of-auto-height-on-absolute-element-should-compute-to-percentage-expected.png",
+    "css-2-1/10-5-percentage-of-auto-height-on-absolute-element-should-compute-to-percentage.html",
+    "css-2-1/10-5-percentage-of-auto-height-should-compute-to-auto-expected.png",
+    "css-2-1/10-5-percentage-of-auto-height-should-compute-to-auto.html",
+    "css-2-1/10-5-percentage-of-height-should-work-properly-with-top-and-bottom-expected.png",
+    "css-2-1/10-5-percentage-of-height-should-work-properly-with-top-and-bottom.html",
+    "css-2-1/10-6-1-content-height-of-inline-boxes-matches-font-size-expected.png",
+    "css-2-1/10-6-1-content-height-of-inline-boxes-matches-font-size.html",
+    "css-2-1/10-6-2-replaced-box-height-expected.png",
+    "css-2-1/10-6-2-replaced-box-height.html",
+    "css-2-1/10-8-1-inline-box-with-non-normal-line-height-should-use-first-available-font-for-font-metrics-expected.png",
+    "css-2-1/10-8-1-inline-box-with-non-normal-line-height-should-use-first-available-font-for-font-metrics.html",
+    "css-2-1/10-8-1-inline-box-with-normal-line-height-should-combine-all-used-fonts-in-font-metrics-expected.png",
+    "css-2-1/10-8-1-inline-box-with-normal-line-height-should-combine-all-used-fonts-in-font-metrics.html",
+    "css-2-1/10-8-1-unitless-line-height-expected.png",
+    "css-2-1/10-8-1-unitless-line-height.html",
+    "css-2-1/10-8-1-vertical-align-baseline-after-middle-expected.png",
+    "css-2-1/10-8-1-vertical-align-baseline-after-middle-text-expected.png",
+    "css-2-1/10-8-1-vertical-align-baseline-after-middle-text.html",
+    "css-2-1/10-8-1-vertical-align-baseline-after-middle.html",
+    "css-2-1/10-8-1-vertical-align-expected.png",
+    "css-2-1/10-8-1-vertical-align-fixed-size-boxes-and-text-expected.png",
+    "css-2-1/10-8-1-vertical-align-fixed-size-boxes-and-text.html",
+    "css-2-1/10-8-1-vertical-align-larger-font-middle-after-baseline-expected.png",
+    "css-2-1/10-8-1-vertical-align-larger-font-middle-after-baseline.html",
+    "css-2-1/10-8-1-vertical-align-middle-top-baseline-simple-expected.png",
+    "css-2-1/10-8-1-vertical-align-middle-top-baseline-simple.html",
+    "css-2-1/10-8-1-vertical-align-potential-baseline-vs-bottom-confusion-expected.png",
+    "css-2-1/10-8-1-vertical-align-potential-baseline-vs-bottom-confusion.html",
+    "css-2-1/10-8-1-vertical-align.html",
+    "css-2-1/10-8-baseline-of-inline-block-should-be-baseline-of-last-line-box-expected.png",
+    "css-2-1/10-8-baseline-of-inline-block-should-be-baseline-of-last-line-box.html",
+    "css-2-1/10-8-large-elements-can-increase-the-distance-between-succesive-baselines-expected.png",
+    "css-2-1/10-8-large-elements-can-increase-the-distance-between-succesive-baselines.html",
+    "css-2-1/10-8-line-height-and-font-size-and-borders-in-nested-inline-boxes-do-no-affect-alignment-expected.png",
+    "css-2-1/10-8-line-height-and-font-size-and-borders-in-nested-inline-boxes-do-no-affect-alignment.html",
+    "css-2-1/10-8-line-height-should-be-used-for-vertical-align-with-inline-non-replaced-boxes-expected.png",
+    "css-2-1/10-8-line-height-should-be-used-for-vertical-align-with-inline-non-replaced-boxes.html",
+    "css-2-1/10-8-line-height-should-not-affect-relative-vertical-placement-of-child-boxes-expected.png",
+    "css-2-1/10-8-line-height-should-not-affect-relative-vertical-placement-of-child-boxes.html",
+    "css-2-1/10-8-line-height-should-specify-minimum-height-of-line-boxes-expected.png",
+    "css-2-1/10-8-line-height-should-specify-minimum-height-of-line-boxes.html",
+    "css-2-1/10-8-margin-box-should-be-used-for-vertical-align-with-most-boxes-expected.png",
+    "css-2-1/10-8-margin-box-should-be-used-for-vertical-align-with-most-boxes.html",
+    "css-2-1/10-8-vertical-align-top-and-bottom-affect-height-and-baseline-expected.png",
+    "css-2-1/10-8-vertical-align-top-and-bottom-affect-height-and-baseline.html",
+    "css-2-1/11-1-1-overflow-auto-div-with-position-absolute-children-expected.png",
+    "css-2-1/11-1-1-overflow-auto-div-with-position-absolute-children.html",
+    "css-2-1/11-1-1-overflow-auto-expected.png",
+    "css-2-1/11-1-1-overflow-auto-from-non-positioned-containing-block-should-affect-relative-positioned-child-expected.png",
+    "css-2-1/11-1-1-overflow-auto-from-non-positioned-containing-block-should-affect-relative-positioned-child.html",
+    "css-2-1/11-1-1-overflow-auto-transform-expected.png",
+    "css-2-1/11-1-1-overflow-auto-transform.html",
+    "css-2-1/11-1-1-overflow-auto.html",
+    "css-2-1/11-1-1-overflow-from-non-positioned-containing-block-should-affect-relative-positioned-child-expected.png",
+    "css-2-1/11-1-1-overflow-from-non-positioned-containing-block-should-affect-relative-positioned-child.html",
+    "css-2-1/11-1-1-overflow-hidden-and-opacity-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-and-opacity.html",
+    "css-2-1/11-1-1-overflow-hidden-and-position-absolute-div-with-position-absolute-children-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-and-position-absolute-div-with-position-absolute-children.html",
+    "css-2-1/11-1-1-overflow-hidden-applied-to-elements-with-display-set-to-block-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-applied-to-elements-with-display-set-to-block.html",
+    "css-2-1/11-1-1-overflow-hidden-applied-to-elements-with-display-set-to-inline-block-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-applied-to-elements-with-display-set-to-inline-block.html",
+    "css-2-1/11-1-1-overflow-hidden-applied-to-elements-with-display-set-to-inline-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-applied-to-elements-with-display-set-to-inline.html",
+    "css-2-1/11-1-1-overflow-hidden-div-with-position-absolute-children-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-div-with-position-absolute-children.html",
+    "css-2-1/11-1-1-overflow-hidden-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-masks-padding-box-only-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-masks-padding-box-only.html",
+    "css-2-1/11-1-1-overflow-hidden-transform-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-transform.html",
+    "css-2-1/11-1-1-overflow-hidden-translate-expected.png",
+    "css-2-1/11-1-1-overflow-hidden-translate.html",
+    "css-2-1/11-1-1-overflow-hidden.html",
+    "css-2-1/11-1-1-overflow-scroll-absolute-positioned-elements-positioned-scroller-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-absolute-positioned-elements-positioned-scroller.html",
+    "css-2-1/11-1-1-overflow-scroll-absolute-positioned-elements-unpositioned-scroller-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-absolute-positioned-elements-unpositioned-scroller.html",
+    "css-2-1/11-1-1-overflow-scroll-container-scrolled-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-container-scrolled-rtl-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-container-scrolled-rtl.html",
+    "css-2-1/11-1-1-overflow-scroll-container-scrolled.html",
+    "css-2-1/11-1-1-overflow-scroll-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-fixed-positioned-elements-transformed-scroller-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-fixed-positioned-elements-transformed-scroller.html",
+    "css-2-1/11-1-1-overflow-scroll-fixed-positioned-elements-untransformed-scroller-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-fixed-positioned-elements-untransformed-scroller.html",
+    "css-2-1/11-1-1-overflow-scroll-should-affect-descendants-with-z-index-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-should-affect-descendants-with-z-index.html",
+    "css-2-1/11-1-1-overflow-scroll-transform-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-transform.html",
+    "css-2-1/11-1-1-overflow-scroll-unpositioned-elements-unpositioned-scroller-expected.png",
+    "css-2-1/11-1-1-overflow-scroll-unpositioned-elements-unpositioned-scroller.html",
+    "css-2-1/11-1-1-overflow-scroll.html",
+    "css-2-1/11-1-1-overflow-should-affect-descendants-with-z-index-expected.png",
+    "css-2-1/11-1-1-overflow-should-affect-descendants-with-z-index.html",
+    "css-2-1/11-1-1-overflow-should-not-affect-descendants-contained-in-another-block-expected.png",
+    "css-2-1/11-1-1-overflow-should-not-affect-descendants-contained-in-another-block.html",
+    "css-2-1/11-1-1-overflow-visible-expected.png",
+    "css-2-1/11-1-1-overflow-visible.html",
+    "css-2-1/11-1-absolutely-positioned-children-of-overflow-hidden-expected.png",
+    "css-2-1/11-1-absolutely-positioned-children-of-overflow-hidden-to-left-expected.png",
+    "css-2-1/11-1-absolutely-positioned-children-of-overflow-hidden-to-left.html",
+    "css-2-1/11-1-absolutely-positioned-children-of-overflow-hidden-to-right-expected.png",
+    "css-2-1/11-1-absolutely-positioned-children-of-overflow-hidden-to-right.html",
+    "css-2-1/11-1-absolutely-positioned-children-of-overflow-hidden.html",
+    "css-2-1/11-1-absolutely-positioned-children-of-overflow-scroll-expected.png",
+    "css-2-1/11-1-absolutely-positioned-children-of-overflow-scroll.html",
+    "css-2-1/11-2-visibility-hidden-renders-generated-box-invisible-expected.png",
+    "css-2-1/11-2-visibility-hidden-renders-generated-box-invisible.html",
+    "css-2-1/11-2-visibility-hidden-should-be-overridable-expected.png",
+    "css-2-1/11-2-visibility-hidden-should-be-overridable.html",
+    "css-2-1/11-2-visibility-hidden-still-affects-layout-expected.png",
+    "css-2-1/11-2-visibility-hidden-still-affects-layout.html",
+    "css-2-1/12-1-after-pseudoelement-expected.png",
+    "css-2-1/12-1-after-pseudoelement-simple-expected.png",
+    "css-2-1/12-1-after-pseudoelement-simple.html",
+    "css-2-1/12-1-after-pseudoelement.html",
+    "css-2-1/12-1-before-pseudoelement-does-not-inherit-inline-style-expected.png",
+    "css-2-1/12-1-before-pseudoelement-does-not-inherit-inline-style.html",
+    "css-2-1/12-1-before-pseudoelement-expected.png",
+    "css-2-1/12-1-before-pseudoelement-responds-to-style-change-expected.png",
+    "css-2-1/12-1-before-pseudoelement-responds-to-style-change.html",
+    "css-2-1/12-1-before-pseudoelement.html",
+    "css-2-1/16-2-text-align-can-be-left-center-right-expected.png",
+    "css-2-1/16-2-text-align-can-be-left-center-right.html",
+    "css-2-1/16-2-text-align-should-not-apply-when-child-boxes-overflow-expected.png",
+    "css-2-1/16-2-text-align-should-not-apply-when-child-boxes-overflow.html",
+    "css-2-1/18-4-outline-animation-expected.png",
+    "css-2-1/18-4-outline-animation.html",
+    "css-2-1/18-4-outline-expected.png",
+    "css-2-1/18-4-outline-overflow-hidden-expected.png",
+    "css-2-1/18-4-outline-overflow-hidden.html",
+    "css-2-1/18-4-outline.html",
+    "css-2-1/8-1-margin-should-be-transparent-expected.png",
+    "css-2-1/8-1-margin-should-be-transparent.html",
+    "css-2-1/8-3-1-box-with-cropped-overflow-should-form-collapsed-margin-with-parent-but-not-child-expected.png",
+    "css-2-1/8-3-1-box-with-cropped-overflow-should-form-collapsed-margin-with-parent-but-not-child.html",
+    "css-2-1/8-3-1-box-with-in-flow-child-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-box-with-in-flow-child-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-box-with-inline-child-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-box-with-inline-child-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-box-with-min-height-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-box-with-min-height-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-box-with-no-in-flow-or-inline-children-should-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-box-with-no-in-flow-or-inline-children-should-form-collapsed-margin.html",
+    "css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-bottom-margin-expected.png",
+    "css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-bottom-margin.html",
+    "css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-top-but-not-bottom-margin-expected.png",
+    "css-2-1/8-3-1-empty-box-margin-collapses-itself-then-collapses-with-parent-top-but-not-bottom-margin.html",
+    "css-2-1/8-3-1-in-flow-siblings-separated-by-absolute-box-should-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-in-flow-siblings-separated-by-absolute-box-should-form-collapsed-margin.html",
+    "css-2-1/8-3-1-in-flow-siblings-separated-by-inline-box-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-in-flow-siblings-separated-by-inline-box-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-in-flow-siblings-should-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-in-flow-siblings-should-form-collapsed-margin.html",
+    "css-2-1/8-3-1-inline-level-boxes-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-inline-level-boxes-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-border-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-border-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-padding-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-separated-by-padding-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-first-in-flow-child-should-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-absolute-box-should-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-border-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-border-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-inline-box-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-padding-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-separated-by-padding-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-and-last-in-flow-child-should-form-collapsed-margin.html",
+    "css-2-1/8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-parent-with-non-auto-height-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-1-statically-positioned-absolute-box-should-not-form-collapsed-margin-expected.png",
+    "css-2-1/8-3-1-statically-positioned-absolute-box-should-not-form-collapsed-margin.html",
+    "css-2-1/8-3-margin-percentage-should-refer-containing-block-width-expected.png",
+    "css-2-1/8-3-margin-percentage-should-refer-containing-block-width.html",
+    "css-2-1/8-3-negative-margins-should-be-allowed-expected.png",
+    "css-2-1/8-3-negative-margins-should-be-allowed.html",
+    "css-2-1/8-3-negative_margins-should-be-allowed-to-produce_negative-box-widths-expected.png",
+    "css-2-1/8-3-negative_margins-should-be-allowed-to-produce_negative-box-widths.html",
+    "css-2-1/8-3-vertical-margins-should-not-apply-to-non-replaced-inline-elements-expected.png",
+    "css-2-1/8-3-vertical-margins-should-not-apply-to-non-replaced-inline-elements.html",
+    "css-2-1/8-4-padding-color-should-be-specified-by-background-expected.png",
+    "css-2-1/8-4-padding-color-should-be-specified-by-background.html",
+    "css-2-1/8-4-padding-image-should-be-specified-by-background-expected.png",
+    "css-2-1/8-4-padding-image-should-be-specified-by-background.html",
+    "css-2-1/8-4-padding-percentage-should-refer-containing-block-width-expected.png",
+    "css-2-1/8-4-padding-percentage-should-refer-containing-block-width.html",
+    "css-2-1/9-2-1-1-anonymous-block-boxes-should-be-ignored-when-resolving-percentages-expected.png",
+    "css-2-1/9-2-1-1-anonymous-block-boxes-should-be-ignored-when-resolving-percentages.html",
+    "css-2-1/9-2-1-1-inline-level-boxes-should-be-broken-around-block-level-box-expected.png",
+    "css-2-1/9-2-1-1-inline-level-boxes-should-be-broken-around-block-level-box.html",
+    "css-2-1/9-2-1-1-inline-level-boxes-should-be-wrapped-in-anonymous-block-boxes-in-block-formatting-context-expected.png",
+    "css-2-1/9-2-1-1-inline-level-boxes-should-be-wrapped-in-anonymous-block-boxes-in-block-formatting-context.html",
+    "css-2-1/9-2-1-block-level-boxes-should-participate-in-block-formatting-context-expected.png",
+    "css-2-1/9-2-1-block-level-boxes-should-participate-in-block-formatting-context.html",
+    "css-2-1/9-2-1-descendant-boxes-should-be-nested-in-ascendant-box-expected.png",
+    "css-2-1/9-2-1-descendant-boxes-should-be-nested-in-ascendant-box.html",
+    "css-2-1/9-2-2-inline-level-boxes-should-participate-in-inline-formatting-context-expected.png",
+    "css-2-1/9-2-2-inline-level-boxes-should-participate-in-inline-formatting-context.html",
+    "css-2-1/9-2-4-display-none-on-br-element-should-not-generate-line-break-expected.png",
+    "css-2-1/9-2-4-display-none-on-br-element-should-not-generate-line-break.html",
+    "css-2-1/9-2-4-display-none-should-not-be-overridable-expected.png",
+    "css-2-1/9-2-4-display-none-should-not-be-overridable.html",
+    "css-2-1/9-2-4-display-none-should-not-generate-box-expected.png",
+    "css-2-1/9-2-4-display-none-should-not-generate-box.html",
+    "css-2-1/9-3-1-fixed-position-elements-are-contained-by-transformed-elements-expected.png",
+    "css-2-1/9-3-1-fixed-position-elements-are-contained-by-transformed-elements.html",
+    "css-2-1/9-3-1-fixed-position-elements-are-contained-by-viewport-despite-absolute-containing-block-expected.png",
+    "css-2-1/9-3-1-fixed-position-elements-are-contained-by-viewport-despite-absolute-containing-block.html",
+    "css-2-1/9-3-1-fixed-position-elements-compute-em-units-from-parent-expected.png",
+    "css-2-1/9-3-1-fixed-position-elements-compute-em-units-from-parent.html",
+    "css-2-1/9-3-1-fixed-position-elements-follow-normal-stacking-context-rules-expected.png",
+    "css-2-1/9-3-1-fixed-position-elements-follow-normal-stacking-context-rules.html",
+    "css-2-1/9-3-1-fixed-position-elements-resolve-percentages-relative-to-viewport-expected.png",
+    "css-2-1/9-3-1-fixed-position-elements-resolve-percentages-relative-to-viewport.html",
+    "css-2-1/9-3-1-fixed-position-elements-right-and-bottom-position-relative-to-viewport-expected.png",
+    "css-2-1/9-3-1-fixed-position-elements-right-and-bottom-position-relative-to-viewport.html",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-bottom-length-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-bottom-length.html",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-bottom-percentage-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-bottom-percentage.html",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-left-length-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-left-length.html",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-left-percentage-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-left-percentage.html",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-right-length-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-right-length.html",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-right-percentage-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-right-percentage.html",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-top-length-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-top-length.html",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-top-percentage-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-can-be-positioned-with-top-percentage.html",
+    "css-2-1/9-3-2-absolute-position-elements-with-implicit-width-can-be-positioned-with-bottom-length-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-with-implicit-width-can-be-positioned-with-bottom-length.html",
+    "css-2-1/9-3-2-absolute-position-elements-with-implicit-width-can-be-positioned-with-right-length-expected.png",
+    "css-2-1/9-3-2-absolute-position-elements-with-implicit-width-can-be-positioned-with-right-length.html",
+    "css-2-1/9-4-1-vertical-margins-between-adjacent-block-level-boxes-in-a-block-formatting-context-collapse-expected.png",
+    "css-2-1/9-4-1-vertical-margins-between-adjacent-block-level-boxes-in-a-block-formatting-context-collapse.html",
+    "css-2-1/9-4-2-collapsed-elements-should-reduce-available-width-of-line-box-expected.png",
+    "css-2-1/9-4-2-collapsed-elements-should-reduce-available-width-of-line-box.html",
+    "css-2-1/9-4-2-inline-boxes-should-split-at-br-elements-expected.png",
+    "css-2-1/9-4-2-inline-boxes-should-split-at-br-elements.html",
+    "css-2-1/9-4-2-long-inline-boxes-should-be-split-expected.png",
+    "css-2-1/9-4-2-long-inline-boxes-should-be-split.html",
+    "css-2-1/9-4-2-margin-should-not-overflow-line-box-expected.png",
+    "css-2-1/9-4-2-margin-should-not-overflow-line-box.html",
+    "css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs-expected.png",
+    "css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-bidi-split-occurs.html",
+    "css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2-expected.png",
+    "css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-2.html",
+    "css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs-expected.png",
+    "css-2-1/9-4-2-margins-borders-paddings-should-have-no-visual-effect-when-split-occurs.html",
+    "css-2-1/9-4-2-multiple-inline-boxes-exceeding-width-of-line-box-should-be-split-expected.png",
+    "css-2-1/9-4-2-multiple-inline-boxes-exceeding-width-of-line-box-should-be-split.html",
+    "css-2-1/9-4-2-nested-inline-boxes-should-split-at-br-elements-expected.png",
+    "css-2-1/9-4-2-nested-inline-boxes-should-split-at-br-elements.html",
+    "css-2-1/9-4-2-splitting-of-boxes-can-affect-containing-box-of-children-expected.png",
+    "css-2-1/9-4-2-splitting-of-boxes-can-affect-containing-box-of-children.html",
+    "css-2-1/9-4-2-splitting-of-boxes-can-affect-stacking-context-of-children-expected.png",
+    "css-2-1/9-4-2-splitting-of-boxes-can-affect-stacking-context-of-children.html",
+    "css-2-1/9-4-2-splitting-of-relatively-positioned-box-does-not-affect-stacking-context-of-siblings-expected.png",
+    "css-2-1/9-4-2-splitting-of-relatively-positioned-box-does-not-affect-stacking-context-of-siblings.html",
+    "css-2-1/9-4-2-unsplittable-inline-box-should-overflow-expected.png",
+    "css-2-1/9-4-2-unsplittable-inline-box-should-overflow.html",
+    "css-2-1/9-4-3-relative-positioned-element-percentages-resolved-from-parent-expected.png",
+    "css-2-1/9-4-3-relative-positioned-element-percentages-resolved-from-parent.html",
+    "css-2-1/9-4-3-relative-positioned-elements-are-offset-from-inline-box-ancestor-expected.png",
+    "css-2-1/9-4-3-relative-positioned-elements-are-offset-from-inline-box-ancestor.html",
+    "css-2-1/9-4-3-relative-positioned-elements-are-offset-from-normal-flow-expected.png",
+    "css-2-1/9-4-3-relative-positioned-elements-are-offset-from-normal-flow-via-right-and-bottom-expected.png",
+    "css-2-1/9-4-3-relative-positioned-elements-are-offset-from-normal-flow-via-right-and-bottom.html",
+    "css-2-1/9-4-3-relative-positioned-elements-are-offset-from-normal-flow.html",
+    "css-2-1/9-4-3-relative-positioned-elements-participate-in-stacking-context-expected.png",
+    "css-2-1/9-4-3-relative-positioned-elements-participate-in-stacking-context.html",
+    "css-2-1/9-4-3-relative-positioned-elements-use-left-and-top-versus-right-and-bottom-expected.png",
+    "css-2-1/9-4-3-relative-positioned-elements-use-left-and-top-versus-right-and-bottom-rtl-expected.png",
+    "css-2-1/9-4-3-relative-positioned-elements-use-left-and-top-versus-right-and-bottom-rtl.html",
+    "css-2-1/9-4-3-relative-positioned-elements-use-left-and-top-versus-right-and-bottom.html",
+    "css-2-1/9-4-3-relative-positioned-elements-with-inline-block-display-are-offset-from-normal-flow-expected.png",
+    "css-2-1/9-4-3-relative-positioned-elements-with-inline-block-display-are-offset-from-normal-flow.html",
+    "css-2-1/9-4-3-relative-positioned-elements-with-inline-display-are-offset-from-normal-flow-expected.png",
+    "css-2-1/9-4-3-relative-positioned-elements-with-inline-display-are-offset-from-normal-flow.html",
+    "css-2-1/9-7-display-should-resolve-to-block-if-position-is-absolute-expected.png",
+    "css-2-1/9-7-display-should-resolve-to-block-if-position-is-absolute.html",
+    "css-2-1/9-9-1-absolute-positioned-elements-should-render-on-top-of-static-elements-expected.png",
+    "css-2-1/9-9-1-absolute-positioned-elements-should-render-on-top-of-static-elements.html",
+    "css-2-1/9-9-1-containing-block-may-be-farther-than-stacking-context-expected.png",
+    "css-2-1/9-9-1-containing-block-may-be-farther-than-stacking-context.html",
+    "css-2-1/9-9-1-elements-with-transform-should-appear-over-normal-flow-elements-expected.png",
+    "css-2-1/9-9-1-elements-with-transform-should-appear-over-normal-flow-elements.html",
+    "css-2-1/9-9-1-fixed-position-containing-block-should-form-stacking-context-expected.png",
+    "css-2-1/9-9-1-fixed-position-containing-block-should-form-stacking-context.html",
+    "css-2-1/9-9-1-fixed-position-element-should-not-appear-on-top-of-later-siblings-expected.png",
+    "css-2-1/9-9-1-fixed-position-element-should-not-appear-on-top-of-later-siblings.html",
+    "css-2-1/9-9-1-nearest-ancestor-stacking-context-should-contain-element-expected.png",
+    "css-2-1/9-9-1-nearest-ancestor-stacking-context-should-contain-element.html",
+    "css-2-1/9-9-1-negative-z-indices-expected.png",
+    "css-2-1/9-9-1-negative-z-indices.html",
+    "css-2-1/9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context-expected.png",
+    "css-2-1/9-9-1-relative-positioned-element-should-be-included-in-containing-stacking-context.html",
+    "css-2-1/9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling-expected.png",
+    "css-2-1/9-9-1-relative-positioned-element-should-not-appear-on-top-of-later-sibling.html",
+    "css-2-1/9-9-1-simple-positive-z-indices-expected.png",
+    "css-2-1/9-9-1-simple-positive-z-indices.html",
+    "css-2-1/9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees-expected.png",
+    "css-2-1/9-9-1-stacking-contexts-and-containing-blocks-in-separate-subtrees.html",
+    "css-2-1/9-9-1-stacking-contexts-and-containing-blocks-with-transforms-expected.png",
+    "css-2-1/9-9-1-stacking-contexts-and-containing-blocks-with-transforms.html",
+    "css-2-1/9-9-1-stacking-contexts-differ-from-containing-blocks-expected.png",
+    "css-2-1/9-9-1-stacking-contexts-differ-from-containing-blocks.html",
+    "css-2-1/9-9-1-stacking-contexts-should-take-into-account-intermediate-containing-blocks-expected.png",
+    "css-2-1/9-9-1-stacking-contexts-should-take-into-account-intermediate-containing-blocks.html",
+    "css-2-1/9-9-1-z-index-should-only-be-applied-to-positioned-elements-expected.png",
+    "css-2-1/9-9-1-z-index-should-only-be-applied-to-positioned-elements.html",
+    "css-2-1/DISABLED-10-3-2-auto-width-should-resolve-to-intrinsic-width.html",
+    "css-2-1/DISABLED-10-3-7-absolute-elements-do-not-break-inline-boxes.html",
+    "css-2-1/DISABLED-10-3-7-inline-absolute-elements-do-not-break-inline-boxes-but-appear-at-inline-position.html",
+    "css-2-1/DISABLED-10-3-7-inline-box-should-not-split-on-absolutely-positioned-child.html",
+    "css-2-1/DISABLED-10-8-1-inline-box-without-glyphs-should-contain-strut.html",
+    "css-2-1/DISABLED-10-8-1-vertical-align-notext.html",
+    "css-2-1/DISABLED-10-8-line-height-can-be-modified-by-non-ancestor-non-sibling-boxes.html",
+    "css-2-1/DISABLED-12-1-after-pseudoelement-linebreak.html",
+    "css-2-1/DISABLED-8-3-1-box-without-in-flow-children-should-form-collapsed-margin.html",
+    "css-2-1/DISABLED-9-2-1-1-inline-level-box-border-should-be-split-around-block-level-box.html",
+    "css-2-1/DISABLED-9-3-2-bottom-should-work-with-positive-and-negative-offsets.html",
+    "css-2-1/DISABLED-9-3-2-left-should-work-with-positive-and-negative-offsets.html",
+    "css-2-1/DISABLED-9-3-2-right-should-work-with-positive-and-negative-offsets.html",
+    "css-2-1/DISABLED-9-3-2-top-should-work-with-positive-and-negative-offsets.html",
+    "css-2-1/DISABLED-9-4-2-horizontal-margins-borders-paddings-should-be-respected-in-inline-formatting-context.html",
+    "css-2-1/DISABLED-9-4-2-splitting-of-boxes-may-not-affect-containing-box-of-positioned-children.html",
+    "css-2-1/DISABLED-9-4-3-relative-positioned-inline-level-elements-participate-in-stacking-context.html",
+    "css-2-1/DISABLED-9-9-1-background-and-borders-should-be-painted-before-children-with-negative-z-index.html",
+    "css-2-1/cobalt.png",
+    "css-2-1/layout_tests.txt",
+    "css-text-3/2-1-letters-should-all-appear-in-uppercase-with-text-transform-uppercase-expected.png",
+    "css-text-3/2-1-letters-should-all-appear-in-uppercase-with-text-transform-uppercase.html",
+    "css-text-3/3-bidirectional-content-should-overflow-the-line-with-white-space-nowrap-expected.png",
+    "css-text-3/3-bidirectional-content-should-overflow-the-line-with-white-space-nowrap.html",
+    "css-text-3/3-br-elements-within-white-space-nowrap-block-should-generate-new-lines-expected.png",
+    "css-text-3/3-br-elements-within-white-space-nowrap-block-should-generate-new-lines.html",
+    "css-text-3/3-br-elements-within-white-space-pre-block-should-generate-new-lines-expected.png",
+    "css-text-3/3-br-elements-within-white-space-pre-block-should-generate-new-lines.html",
+    "css-text-3/3-br-elements-within-white-space-pre-line-block-should-generate-new-lines-expected.png",
+    "css-text-3/3-br-elements-within-white-space-pre-line-block-should-generate-new-lines.html",
+    "css-text-3/3-br-elements-within-white-space-pre-wrap-block-should-generate-new-lines-expected.png",
+    "css-text-3/3-br-elements-within-white-space-pre-wrap-block-should-generate-new-lines.html",
+    "css-text-3/3-content-should-overflow-the-line-with-white-space-nowrap-expected.png",
+    "css-text-3/3-content-should-overflow-the-line-with-white-space-nowrap.html",
+    "css-text-3/3-content-should-overflow-the-line-with-white-space-pre-expected.png",
+    "css-text-3/3-content-should-overflow-the-line-with-white-space-pre.html",
+    "css-text-3/3-content-should-wrap-the-line-with-white-space-pre-line-expected.png",
+    "css-text-3/3-content-should-wrap-the-line-with-white-space-pre-line.html",
+    "css-text-3/3-content-should-wrap-the-line-with-white-space-pre-wrap-expected.png",
+    "css-text-3/3-content-should-wrap-the-line-with-white-space-pre-wrap.html",
+    "css-text-3/3-lines-and-spaces-should-be-collapsed-with-white-space-nowrap-containing-block-expected.png",
+    "css-text-3/3-lines-and-spaces-should-be-collapsed-with-white-space-nowrap-containing-block.html",
+    "css-text-3/3-lines-and-spaces-should-be-collapsed-with-white-space-nowrap-expected.png",
+    "css-text-3/3-lines-and-spaces-should-be-collapsed-with-white-space-nowrap.html",
+    "css-text-3/3-lines-and-spaces-should-be-retained-with-white-space-pre-containing-block-expected.png",
+    "css-text-3/3-lines-and-spaces-should-be-retained-with-white-space-pre-containing-block.html",
+    "css-text-3/3-lines-and-spaces-should-be-retained-with-white-space-pre-expected.png",
+    "css-text-3/3-lines-and-spaces-should-be-retained-with-white-space-pre-wrap-containing-block-expected.png",
+    "css-text-3/3-lines-and-spaces-should-be-retained-with-white-space-pre-wrap-containing-block.html",
+    "css-text-3/3-lines-and-spaces-should-be-retained-with-white-space-pre-wrap-expected.png",
+    "css-text-3/3-lines-and-spaces-should-be-retained-with-white-space-pre-wrap.html",
+    "css-text-3/3-lines-and-spaces-should-be-retained-with-white-space-pre.html",
+    "css-text-3/3-lines-should-be-retained-and-spaces-collapsed-with-white-space-pre-line-containing-block-expected.png",
+    "css-text-3/3-lines-should-be-retained-and-spaces-collapsed-with-white-space-pre-line-containing-block.html",
+    "css-text-3/3-lines-should-be-retained-and-spaces-collapsed-with-white-space-pre-line-expected.png",
+    "css-text-3/3-lines-should-be-retained-and-spaces-collapsed-with-white-space-pre-line.html",
+    "css-text-3/3-multiple-boxes-with-a-mixture-of-white-space-nowrap-and-normal-should-wrap-properly-expected.png",
+    "css-text-3/3-multiple-boxes-with-a-mixture-of-white-space-nowrap-and-normal-should-wrap-properly.html",
+    "css-text-3/3-multiple-boxes-with-a-mixture-of-white-space-pre-and-normal-should-wrap-properly-expected.png",
+    "css-text-3/3-multiple-boxes-with-a-mixture-of-white-space-pre-and-normal-should-wrap-properly.html",
+    "css-text-3/3-multiple-boxes-within-white-space-nowrap-block-should-not-wrap-expected.png",
+    "css-text-3/3-multiple-boxes-within-white-space-nowrap-block-should-not-wrap.html",
+    "css-text-3/3-multiple-boxes-within-white-space-pre-block-should-not-wrap-expected.png",
+    "css-text-3/3-multiple-boxes-within-white-space-pre-block-should-not-wrap.html",
+    "css-text-3/3-multiple-boxes-within-white-space-pre-line-block-should-wrap-expected.png",
+    "css-text-3/3-multiple-boxes-within-white-space-pre-line-block-should-wrap.html",
+    "css-text-3/3-multiple-boxes-within-white-space-pre-wrap-block-should-wrap-expected.png",
+    "css-text-3/3-multiple-boxes-within-white-space-pre-wrap-block-should-wrap.html",
+    "css-text-3/3-non-space-ending-white-space-pre-box-should-obey-wrapping-rules-expected.png",
+    "css-text-3/3-non-space-ending-white-space-pre-box-should-obey-wrapping-rules.html",
+    "css-text-3/3-non-space-preceding-white-space-pre-box-should-not-be-wrappable-expected.png",
+    "css-text-3/3-non-space-preceding-white-space-pre-box-should-not-be-wrappable.html",
+    "css-text-3/3-space-ending-white-space-pre-box-should-be-treated-as-non-breaking-space-expected.png",
+    "css-text-3/3-space-ending-white-space-pre-box-should-be-treated-as-non-breaking-space.html",
+    "css-text-3/3-space-preceding-white-space-pre-box-should-be-wrappable-expected.png",
+    "css-text-3/3-space-preceding-white-space-pre-box-should-be-wrappable.html",
+    "css-text-3/4-1-1-space-following-collapsible-space-in-another-box-should-be-collapsed-expected.png",
+    "css-text-3/4-1-1-space-following-collapsible-space-in-another-box-should-be-collapsed.html",
+    "css-text-3/4-1-1-space-preceding-br-element-should-not-be-collapsed-expected.png",
+    "css-text-3/4-1-1-space-preceding-br-element-should-not-be-collapsed.html",
+    "css-text-3/4-1-3-spaces-at-beginning-and-end-of-line-should-be-collapsed-expected.png",
+    "css-text-3/4-1-3-spaces-at-beginning-and-end-of-line-should-be-collapsed.html",
+    "css-text-3/4-1-empty-inline-block-should-be-treated-as-non-empty-text-expected.png",
+    "css-text-3/4-1-empty-inline-block-should-be-treated-as-non-empty-text.html",
+    "css-text-3/5-1-inline-blocks-should-be-treated-as-object-replacement-characters-for-line-wrapping-expected.png",
+    "css-text-3/5-1-inline-blocks-should-be-treated-as-object-replacement-characters-for-line-wrapping.html",
+    "css-text-3/5-1-replaced-elements-should-be-treated-as-object-replacement-characters-for-line-wrapping-expected.png",
+    "css-text-3/5-1-replaced-elements-should-be-treated-as-object-replacement-characters-for-line-wrapping.html",
+    "css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping-expected.png",
+    "css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping-rtl-expected.png",
+    "css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping-rtl.html",
+    "css-text-3/5-absolute-boxes-in-span-should-not-impact-line-wrapping.html",
+    "css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity-expected.png",
+    "css-text-3/5-collapsible-leading-white-space-should-not-prevent-soft-wrap-opportunity.html",
+    "css-text-3/5-collapsible-trailing-white-space-should-not-prevent-soft-wrap-opportunity-expected.png",
+    "css-text-3/5-collapsible-trailing-white-space-should-not-prevent-soft-wrap-opportunity.html",
+    "css-text-3/5-collapsible-trailing-white-space-that-overflows-line-prior-to-collapse-should-not-cause-wrap-expected.png",
+    "css-text-3/5-collapsible-trailing-white-space-that-overflows-line-prior-to-collapse-should-not-cause-wrap.html",
+    "css-text-3/5-element-edge-should-not-introduce-soft-wrap-opportunity-expected.png",
+    "css-text-3/5-element-edge-should-not-introduce-soft-wrap-opportunity.html",
+    "css-text-3/5-end-edge-but-not-start-edge-of-non-zero-padding-span-should-justify-line-existence-expected.png",
+    "css-text-3/5-end-edge-but-not-start-edge-of-non-zero-padding-span-should-justify-line-existence.html",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-first-breakable-location-on-first-box-overflow-break-word-expected.png",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-first-breakable-location-on-first-box-overflow-break-word.html",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-first-breakable-location-on-first-box-overflow-normal-expected.png",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-first-breakable-location-on-first-box-overflow-normal.html",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-first-breakable-location-on-second-box-overflow-break-word-expected.png",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-first-breakable-location-on-second-box-overflow-break-word.html",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-first-breakable-location-on-second-box-overflow-normal-expected.png",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-first-breakable-location-on-second-box-overflow-normal.html",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-last-breakable-location-within-width-to-prevent-overflow-expected.png",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-last-breakable-location-within-width-to-prevent-overflow.html",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-last-soft-wrap-location-within-width-expected.png",
+    "css-text-3/5-multiple-boxes-in-span-should-wrap-at-last-soft-wrap-location-within-width.html",
+    "css-text-3/5-multiple-boxes-should-wrap-at-first-breakable-location-on-first-box-overflow-break-word-expected.png",
+    "css-text-3/5-multiple-boxes-should-wrap-at-first-breakable-location-on-first-box-overflow-break-word.html",
+    "css-text-3/5-multiple-boxes-should-wrap-at-first-breakable-location-on-first-box-overflow-normal-expected.png",
+    "css-text-3/5-multiple-boxes-should-wrap-at-first-breakable-location-on-first-box-overflow-normal.html",
+    "css-text-3/5-multiple-boxes-should-wrap-at-first-breakable-location-on-second-box-overflow-break-word-expected.png",
+    "css-text-3/5-multiple-boxes-should-wrap-at-first-breakable-location-on-second-box-overflow-break-word.html",
+    "css-text-3/5-multiple-boxes-should-wrap-at-first-breakable-location-on-second-box-overflow-normal-expected.png",
+    "css-text-3/5-multiple-boxes-should-wrap-at-first-breakable-location-on-second-box-overflow-normal.html",
+    "css-text-3/5-multiple-boxes-should-wrap-at-last-breakable-location-within-width-to-prevent-overflow-expected.png",
+    "css-text-3/5-multiple-boxes-should-wrap-at-last-breakable-location-within-width-to-prevent-overflow.html",
+    "css-text-3/5-multiple-boxes-should-wrap-at-last-soft-wrap-location-within-width-expected.png",
+    "css-text-3/5-multiple-boxes-should-wrap-at-last-soft-wrap-location-within-width.html",
+    "css-text-3/5-span-with-overflowing-padding-should-attempt-to-wrap-line-before-expected.png",
+    "css-text-3/5-span-with-overflowing-padding-should-attempt-to-wrap-line-before.html",
+    "css-text-3/5-span-within-span-with-overflowing-padding-should-attempt-to-wrap-line-before-expected.png",
+    "css-text-3/5-span-within-span-with-overflowing-padding-should-attempt-to-wrap-line-before.html",
+    "css-text-3/5-unwrappable-collapsible-leading-white-space-should-not-count-against-available-width-expected.png",
+    "css-text-3/5-unwrappable-collapsible-leading-white-space-should-not-count-against-available-width.html",
+    "css-text-3/5-wrappable-collapsible-leading-white-space-should-not-count-against-available-width-expected.png",
+    "css-text-3/5-wrappable-collapsible-leading-white-space-should-not-count-against-available-width.html",
+    "css-text-3/6-2-gracefully-handle-unbreakable-overflow-word-with-overflow-wrap-break-word-expected.png",
+    "css-text-3/6-2-gracefully-handle-unbreakable-overflow-word-with-overflow-wrap-break-word.html",
+    "css-text-3/6-2-words-should-be-breakable-with-overflow-wrap-break-word-if-first-on-line-expected.png",
+    "css-text-3/6-2-words-should-be-breakable-with-overflow-wrap-break-word-if-first-on-line.html",
+    "css-text-3/6-2-words-should-be-breakable-with-word-wrap-break-word-if-first-on-line-expected.png",
+    "css-text-3/6-2-words-should-be-breakable-with-word-wrap-break-word-if-first-on-line.html",
+    "css-text-3/6-2-words-should-not-be-breakable-with-overflow-wrap-break-word-if-not-first-on-line-expected.png",
+    "css-text-3/6-2-words-should-not-be-breakable-with-overflow-wrap-break-word-if-not-first-on-line.html",
+    "css-text-3/6-2-words-should-not-be-breakable-with-word-wrap-break-word-if-not-first-on-line-expected.png",
+    "css-text-3/6-2-words-should-not-be-breakable-with-word-wrap-break-word-if-not-first-on-line.html",
+    "css-text-3/7-1-text-align-end-value-should-align-with-end-edge-of-line-box-expected.png",
+    "css-text-3/7-1-text-align-end-value-should-align-with-end-edge-of-line-box.html",
+    "css-text-3/7-1-text-align-should-not-double-shift-contents-of-inline-container-box-expected.png",
+    "css-text-3/7-1-text-align-should-not-double-shift-contents-of-inline-container-box.html",
+    "css-text-3/7-1-text-align-should-not-impact-spacing-between-boxes-expected.png",
+    "css-text-3/7-1-text-align-should-not-impact-spacing-between-boxes.html",
+    "css-text-3/7-1-text-align-start-value-should-align-with-start-edge-of-line-box-expected.png",
+    "css-text-3/7-1-text-align-start-value-should-align-with-start-edge-of-line-box.html",
+    "css-text-3/7-1-text-align-values-left-center-right-should-not-base-alignment-on-line-box-direction-expected.png",
+    "css-text-3/7-1-text-align-values-left-center-right-should-not-base-alignment-on-line-box-direction.html",
+    "css-text-3/9-1-negative-text-indent-should-indent-first-line-in-paragraph-to-the-left-expected.png",
+    "css-text-3/9-1-negative-text-indent-should-indent-first-line-in-paragraph-to-the-left.html",
+    "css-text-3/9-1-negative-text-indent-should-indent-first-line-in-rtl-paragraph-to-the-right-expected.png",
+    "css-text-3/9-1-negative-text-indent-should-indent-first-line-in-rtl-paragraph-to-the-right.html",
+    "css-text-3/9-1-negative-text-indent-should-indent-nested-inline-block-to-the-left-expected.png",
+    "css-text-3/9-1-negative-text-indent-should-indent-nested-inline-block-to-the-left.html",
+    "css-text-3/9-1-negative-text-indent-should-indent-nested-inline-rtl-block-to-the-right-expected.png",
+    "css-text-3/9-1-negative-text-indent-should-indent-nested-inline-rtl-block-to-the-right.html",
+    "css-text-3/9-1-text-indent-should-indent-first-line-in-paragraph-expected.png",
+    "css-text-3/9-1-text-indent-should-indent-first-line-in-paragraph.html",
+    "css-text-3/9-1-text-indent-should-indent-first-line-in-rtl-paragraph-on-right-expected.png",
+    "css-text-3/9-1-text-indent-should-indent-first-line-in-rtl-paragraph-on-right.html",
+    "css-text-3/9-1-text-indent-should-indent-nested-inline-block-expected.png",
+    "css-text-3/9-1-text-indent-should-indent-nested-inline-block.html",
+    "css-text-3/9-1-text-indent-should-indent-nested-inline-rtl-block-expected.png",
+    "css-text-3/9-1-text-indent-should-indent-nested-inline-rtl-block.html",
+    "css-text-3/9-1-text-indent-should-not-indent-nested-span-expected.png",
+    "css-text-3/9-1-text-indent-should-not-indent-nested-span.html",
+    "css-text-3/layout_tests.txt",
+    "css-transforms/15-1-matrix-transform-function-expected.png",
+    "css-transforms/15-1-matrix-transform-function-with-transform-origin-expected.png",
+    "css-transforms/15-1-matrix-transform-function-with-transform-origin.html",
+    "css-transforms/15-1-matrix-transform-function.html",
+    "css-transforms/15-1-rotate-transform-function-expected.png",
+    "css-transforms/15-1-rotate-transform-function-with-transform-origin-expected.png",
+    "css-transforms/15-1-rotate-transform-function-with-transform-origin.html",
+    "css-transforms/15-1-rotate-transform-function.html",
+    "css-transforms/15-1-scale-transform-function-expected.png",
+    "css-transforms/15-1-scale-transform-function-with-transform-origin-expected.png",
+    "css-transforms/15-1-scale-transform-function-with-transform-origin.html",
+    "css-transforms/15-1-scale-transform-function.html",
+    "css-transforms/4-bounding-box-should-be-border-box-expected.png",
+    "css-transforms/4-bounding-box-should-be-border-box.html",
+    "css-transforms/7-replaced-boxes-transform-expected.png",
+    "css-transforms/7-replaced-boxes-transform.html",
+    "css-transforms/7-transform-should-establish-stacking-context-expected.png",
+    "css-transforms/7-transform-should-establish-stacking-context.html",
+    "css-transforms/7-translation-transform-supports-percentages-expected.png",
+    "css-transforms/7-translation-transform-supports-percentages.html",
+    "css-transforms/layout_tests.txt",
+    "css3-animations/3-animations-during-display-none-waits-expected.png",
+    "css3-animations/3-animations-during-display-none-waits-from-ancestor-expected.png",
+    "css3-animations/3-animations-during-display-none-waits-from-ancestor.html",
+    "css3-animations/3-animations-during-display-none-waits.html",
+    "css3-animations/3-animations-reset-on-display-change-not-computed-style-update-expected.png",
+    "css3-animations/3-animations-reset-on-display-change-not-computed-style-update.html",
+    "css3-animations/3-animations-restart-after-display-none-expected.png",
+    "css3-animations/3-animations-restart-after-display-none-from-ancestor-expected.png",
+    "css3-animations/3-animations-restart-after-display-none-from-ancestor.html",
+    "css3-animations/3-animations-restart-after-display-none.html",
+    "css3-animations/3-simple-animation-expected.png",
+    "css3-animations/3-simple-animation.html",
+    "css3-animations/canceled_animation_should_not_get_animationend_event-expected.png",
+    "css3-animations/canceled_animation_should_not_get_animationend_event.html",
+    "css3-animations/layout_tests.txt",
+    "css3-animations/removed_animation_from_other_animationend_event_handler_should_still_get_animationend_event-expected.png",
+    "css3-animations/removed_animation_from_other_animationend_event_handler_should_still_get_animationend_event.html",
+    "css3-background/14-2-1-background-positioning-area-is-smaller-than-image-size-expected.png",
+    "css3-background/14-2-1-background-positioning-area-is-smaller-than-image-size.html",
+    "css3-background/14-2-1-background-with-color-expected.png",
+    "css3-background/14-2-1-background-with-color-image-expected.png",
+    "css3-background/14-2-1-background-with-color-image-position-expected.png",
+    "css3-background/14-2-1-background-with-color-image-position-repeat-expected.png",
+    "css3-background/14-2-1-background-with-color-image-position-repeat.html",
+    "css3-background/14-2-1-background-with-color-image-position.html",
+    "css3-background/14-2-1-background-with-color-image-repeat-expected.png",
+    "css3-background/14-2-1-background-with-color-image-repeat-position-expected.png",
+    "css3-background/14-2-1-background-with-color-image-repeat-position.html",
+    "css3-background/14-2-1-background-with-color-image-repeat.html",
+    "css3-background/14-2-1-background-with-color-image.html",
+    "css3-background/14-2-1-background-with-color-position-expected.png",
+    "css3-background/14-2-1-background-with-color-position-image-expected.png",
+    "css3-background/14-2-1-background-with-color-position-image.html",
+    "css3-background/14-2-1-background-with-color-position-repeat-expected.png",
+    "css3-background/14-2-1-background-with-color-position-repeat.html",
+    "css3-background/14-2-1-background-with-color-position.html",
+    "css3-background/14-2-1-background-with-color-repeat-expected.png",
+    "css3-background/14-2-1-background-with-color-repeat-image-expected.png",
+    "css3-background/14-2-1-background-with-color-repeat-image-position-expected.png",
+    "css3-background/14-2-1-background-with-color-repeat-image-position.html",
+    "css3-background/14-2-1-background-with-color-repeat-image.html",
+    "css3-background/14-2-1-background-with-color-repeat-position-expected.png",
+    "css3-background/14-2-1-background-with-color-repeat-position-image-expected.png",
+    "css3-background/14-2-1-background-with-color-repeat-position-image.html",
+    "css3-background/14-2-1-background-with-color-repeat-position.html",
+    "css3-background/14-2-1-background-with-color-repeat.html",
+    "css3-background/14-2-1-background-with-color.html",
+    "css3-background/14-2-1-background-with-image-color-expected.png",
+    "css3-background/14-2-1-background-with-image-color-position-expected.png",
+    "css3-background/14-2-1-background-with-image-color-position-repeat-expected.png",
+    "css3-background/14-2-1-background-with-image-color-position-repeat.html",
+    "css3-background/14-2-1-background-with-image-color-position.html",
+    "css3-background/14-2-1-background-with-image-color-repeat-expected.png",
+    "css3-background/14-2-1-background-with-image-color-repeat-position-expected.png",
+    "css3-background/14-2-1-background-with-image-color-repeat-position.html",
+    "css3-background/14-2-1-background-with-image-color-repeat.html",
+    "css3-background/14-2-1-background-with-image-color.html",
+    "css3-background/14-2-1-background-with-image-position-color-expected.png",
+    "css3-background/14-2-1-background-with-image-position-color-repeat-expected.png",
+    "css3-background/14-2-1-background-with-image-position-color-repeat.html",
+    "css3-background/14-2-1-background-with-image-position-color.html",
+    "css3-background/14-2-1-background-with-image-position-expected.png",
+    "css3-background/14-2-1-background-with-image-position-repeat-color-expected.png",
+    "css3-background/14-2-1-background-with-image-position-repeat-color.html",
+    "css3-background/14-2-1-background-with-image-position-repeat-expected.png",
+    "css3-background/14-2-1-background-with-image-position-repeat.html",
+    "css3-background/14-2-1-background-with-image-position.html",
+    "css3-background/14-2-1-background-with-image-repeat-color-expected.png",
+    "css3-background/14-2-1-background-with-image-repeat-color-position-expected.png",
+    "css3-background/14-2-1-background-with-image-repeat-color-position.html",
+    "css3-background/14-2-1-background-with-image-repeat-color.html",
+    "css3-background/14-2-1-background-with-image-repeat-position-color-expected.png",
+    "css3-background/14-2-1-background-with-image-repeat-position-color.html",
+    "css3-background/14-2-1-background-with-image-repeat-position-expected.png",
+    "css3-background/14-2-1-background-with-image-repeat-position.html",
+    "css3-background/14-2-1-background-with-opaque-image-color-expected.png",
+    "css3-background/14-2-1-background-with-opaque-image-color.html",
+    "css3-background/14-2-1-background-with-opaque-image-transform-expected.png",
+    "css3-background/14-2-1-background-with-opaque-image-transform.html",
+    "css3-background/14-2-1-background-with-position-color-expected.png",
+    "css3-background/14-2-1-background-with-position-color-image-expected.png",
+    "css3-background/14-2-1-background-with-position-color-image-repeat-expected.png",
+    "css3-background/14-2-1-background-with-position-color-image-repeat.html",
+    "css3-background/14-2-1-background-with-position-color-image.html",
+    "css3-background/14-2-1-background-with-position-color-repeat-expected.png",
+    "css3-background/14-2-1-background-with-position-color-repeat-image-expected.png",
+    "css3-background/14-2-1-background-with-position-color-repeat-image.html",
+    "css3-background/14-2-1-background-with-position-color-repeat.html",
+    "css3-background/14-2-1-background-with-position-color.html",
+    "css3-background/14-2-1-background-with-position-image-color-expected.png",
+    "css3-background/14-2-1-background-with-position-image-color-repeat-expected.png",
+    "css3-background/14-2-1-background-with-position-image-color-repeat.html",
+    "css3-background/14-2-1-background-with-position-image-color.html",
+    "css3-background/14-2-1-background-with-position-image-expected.png",
+    "css3-background/14-2-1-background-with-position-image-repeat-color-expected.png",
+    "css3-background/14-2-1-background-with-position-image-repeat-color.html",
+    "css3-background/14-2-1-background-with-position-image-repeat-expected.png",
+    "css3-background/14-2-1-background-with-position-image-repeat.html",
+    "css3-background/14-2-1-background-with-position-image.html",
+    "css3-background/14-2-1-background-with-position-repeat-color-expected.png",
+    "css3-background/14-2-1-background-with-position-repeat-color-image-expected.png",
+    "css3-background/14-2-1-background-with-position-repeat-color-image.html",
+    "css3-background/14-2-1-background-with-position-repeat-color.html",
+    "css3-background/14-2-1-background-with-position-repeat-expected.png",
+    "css3-background/14-2-1-background-with-position-repeat-image-color-expected.png",
+    "css3-background/14-2-1-background-with-position-repeat-image-color.html",
+    "css3-background/14-2-1-background-with-position-repeat-image-expected.png",
+    "css3-background/14-2-1-background-with-position-repeat-image.html",
+    "css3-background/14-2-1-background-with-position-repeat.html",
+    "css3-background/14-2-1-background-with-repeat-color-expected.png",
+    "css3-background/14-2-1-background-with-repeat-color-image-expected.png",
+    "css3-background/14-2-1-background-with-repeat-color-image-position-expected.png",
+    "css3-background/14-2-1-background-with-repeat-color-image-position.html",
+    "css3-background/14-2-1-background-with-repeat-color-image.html",
+    "css3-background/14-2-1-background-with-repeat-color-position-expected.png",
+    "css3-background/14-2-1-background-with-repeat-color-position-image-expected.png",
+    "css3-background/14-2-1-background-with-repeat-color-position-image.html",
+    "css3-background/14-2-1-background-with-repeat-color-position.html",
+    "css3-background/14-2-1-background-with-repeat-color.html",
+    "css3-background/14-2-1-background-with-repeat-image-color-expected.png",
+    "css3-background/14-2-1-background-with-repeat-image-color-position-expected.png",
+    "css3-background/14-2-1-background-with-repeat-image-color-position.html",
+    "css3-background/14-2-1-background-with-repeat-image-color.html",
+    "css3-background/14-2-1-background-with-repeat-image-expected.png",
+    "css3-background/14-2-1-background-with-repeat-image-position-color-expected.png",
+    "css3-background/14-2-1-background-with-repeat-image-position-color.html",
+    "css3-background/14-2-1-background-with-repeat-image-position-expected.png",
+    "css3-background/14-2-1-background-with-repeat-image-position.html",
+    "css3-background/14-2-1-background-with-repeat-image.html",
+    "css3-background/14-2-1-background-with-repeat-position-color-expected.png",
+    "css3-background/14-2-1-background-with-repeat-position-color-image-expected.png",
+    "css3-background/14-2-1-background-with-repeat-position-color-image.html",
+    "css3-background/14-2-1-background-with-repeat-position-color.html",
+    "css3-background/14-2-1-background-with-repeat-position-expected.png",
+    "css3-background/14-2-1-background-with-repeat-position-image-color-expected.png",
+    "css3-background/14-2-1-background-with-repeat-position-image-color.html",
+    "css3-background/14-2-1-background-with-repeat-position-image-expected.png",
+    "css3-background/14-2-1-background-with-repeat-position-image.html",
+    "css3-background/14-2-1-background-with-repeat-position.html",
+    "css3-background/14-2-1-multiple-layers-background-image-expected.png",
+    "css3-background/14-2-1-multiple-layers-background-image.html",
+    "css3-background/3-10-background-none-declares-initial-value-for-background-color-expected.png",
+    "css3-background/3-10-background-none-declares-initial-value-for-background-color.html",
+    "css3-background/3-11-2-body-background-can-be-set-to-transparent-later-expected.png",
+    "css3-background/3-11-2-body-background-can-be-set-to-transparent-later.html",
+    "css3-background/3-11-2-propagate-computed-value-of-background-from-body-to-root-expected.png",
+    "css3-background/3-11-2-propagate-computed-value-of-background-from-body-to-root.html",
+    "css3-background/3-11-2-propagate-computed-value-of-background-from-html-to-root-expected.png",
+    "css3-background/3-11-2-propagate-computed-value-of-background-from-html-to-root.html",
+    "css3-background/4-0-border-1-individual-edge-expected.png",
+    "css3-background/4-0-border-1-individual-edge-with-rounded-corners-expected.png",
+    "css3-background/4-0-border-1-individual-edge-with-rounded-corners.html",
+    "css3-background/4-0-border-1-individual-edge.html",
+    "css3-background/4-0-border-2-individual-edges-expected.png",
+    "css3-background/4-0-border-2-individual-edges-with-rounded-corners-expected.png",
+    "css3-background/4-0-border-2-individual-edges-with-rounded-corners.html",
+    "css3-background/4-0-border-2-individual-edges.html",
+    "css3-background/4-0-border-color-with-solid-border-style-and-background-color-expected.png",
+    "css3-background/4-0-border-color-with-solid-border-style-and-background-color.html",
+    "css3-background/4-0-border-color-with-solid-border-style-expected.png",
+    "css3-background/4-0-border-color-with-solid-border-style.html",
+    "css3-background/4-0-border-set-using-border-color-and-border-style-expected.png",
+    "css3-background/4-0-border-set-using-border-color-and-border-style.html",
+    "css3-background/4-0-border-set-using-border-color-border-width-and-border-style-expected.png",
+    "css3-background/4-0-border-set-using-border-color-border-width-and-border-style.html",
+    "css3-background/4-0-border-set-using-border-width-and-border-style-expected.png",
+    "css3-background/4-0-border-set-using-border-width-and-border-style.html",
+    "css3-background/4-0-border-set-using-border-width-border-color-and-border-style-with-empty-content-expected.png",
+    "css3-background/4-0-border-set-using-border-width-border-color-and-border-style-with-empty-content.html",
+    "css3-background/4-0-border-style-hidden-with-only-background-color-expected.png",
+    "css3-background/4-0-border-style-hidden-with-only-background-color.html",
+    "css3-background/4-0-border-width-with-solid-border-style-expected.png",
+    "css3-background/4-0-border-width-with-solid-border-style.html",
+    "css3-background/4-0-different-border-styles-with-different-border-widths-expected.png",
+    "css3-background/4-0-different-border-styles-with-different-border-widths.html",
+    "css3-background/4-0-different-border-widths-with-solid-border-style-expected.png",
+    "css3-background/4-0-different-border-widths-with-solid-border-style.html",
+    "css3-background/4-1-border-color-2-values-with-rounded-corners-expected.png",
+    "css3-background/4-1-border-color-2-values-with-rounded-corners.html",
+    "css3-background/4-1-border-color-3-values-with-rounded-corners-expected.png",
+    "css3-background/4-1-border-color-3-values-with-rounded-corners.html",
+    "css3-background/4-1-border-color-4-values-with-different-rounded-corners-and-different-border-widths-expected.png",
+    "css3-background/4-1-border-color-4-values-with-different-rounded-corners-and-different-border-widths.html",
+    "css3-background/4-1-border-color-4-values-with-different-rounded-corners-expected.png",
+    "css3-background/4-1-border-color-4-values-with-different-rounded-corners.html",
+    "css3-background/4-1-border-color-4-values-with-rounded-corners-and-different-border-widths-expected.png",
+    "css3-background/4-1-border-color-4-values-with-rounded-corners-and-different-border-widths.html",
+    "css3-background/4-1-border-color-4-values-with-rounded-corners-expected.png",
+    "css3-background/4-1-border-color-4-values-with-rounded-corners.html",
+    "css3-background/4-3-border-width-2-values-with-rounded-corners-expected.png",
+    "css3-background/4-3-border-width-2-values-with-rounded-corners.html",
+    "css3-background/4-3-border-width-3-values-with-rounded-corners-expected.png",
+    "css3-background/4-3-border-width-3-values-with-rounded-corners.html",
+    "css3-background/4-3-border-width-4-values-with-different-rounded-corners-expected.png",
+    "css3-background/4-3-border-width-4-values-with-different-rounded-corners.html",
+    "css3-background/4-3-border-width-4-values-with-rounded-corners-expected.png",
+    "css3-background/4-3-border-width-4-values-with-rounded-corners.html",
+    "css3-background/5-0-border-radius-2-values-with-background-color-and-border-and-inset-shadow.html",
+    "css3-background/5-0-border-radius-2-values-with-background-color-and-border-and-outset-shadow.html",
+    "css3-background/5-0-border-radius-2-values-with-background-color-and-border.html",
+    "css3-background/5-0-border-radius-2-values-with-background-color-and-inset-shadow.html",
+    "css3-background/5-0-border-radius-2-values-with-background-color-and-outset-shadow.html",
+    "css3-background/5-0-border-radius-2-values-with-background-color-expected.png",
+    "css3-background/5-0-border-radius-2-values-with-background-color.html",
+    "css3-background/5-0-border-radius-2-values-with-border-only-expected.png",
+    "css3-background/5-0-border-radius-2-values-with-border-only.html",
+    "css3-background/5-0-border-radius-3-values-with-background-color-and-border-and-inset-shadow.html",
+    "css3-background/5-0-border-radius-3-values-with-background-color-and-border-and-outset-shadow.html",
+    "css3-background/5-0-border-radius-3-values-with-background-color-and-border.html",
+    "css3-background/5-0-border-radius-3-values-with-background-color-and-inset-shadow.html",
+    "css3-background/5-0-border-radius-3-values-with-background-color-and-outset-shadow.html",
+    "css3-background/5-0-border-radius-3-values-with-background-color-expected.png",
+    "css3-background/5-0-border-radius-3-values-with-background-color.html",
+    "css3-background/5-0-border-radius-3-values-with-border-only-expected.png",
+    "css3-background/5-0-border-radius-3-values-with-border-only.html",
+    "css3-background/5-0-border-radius-4-values-with-background-color-and-border-and-inset-shadow.html",
+    "css3-background/5-0-border-radius-4-values-with-background-color-and-border-and-outset-shadow.html",
+    "css3-background/5-0-border-radius-4-values-with-background-color-and-border.html",
+    "css3-background/5-0-border-radius-4-values-with-background-color-and-inset-shadow.html",
+    "css3-background/5-0-border-radius-4-values-with-background-color-and-outset-shadow.html",
+    "css3-background/5-0-border-radius-4-values-with-background-color-expected.png",
+    "css3-background/5-0-border-radius-4-values-with-background-color.html",
+    "css3-background/5-0-border-radius-4-values-with-border-only-expected.png",
+    "css3-background/5-0-border-radius-4-values-with-border-only.html",
+    "css3-background/5-0-border-radius-circle-with-background-color-and-border-expected.png",
+    "css3-background/5-0-border-radius-circle-with-background-color-and-border.html",
+    "css3-background/5-0-border-radius-circle-with-background-image-and-border-expected.png",
+    "css3-background/5-0-border-radius-circle-with-background-image-and-border.html",
+    "css3-background/5-0-border-radius-circle-with-irrational-out-of-bounds-radius-expected.png",
+    "css3-background/5-0-border-radius-circle-with-irrational-out-of-bounds-radius.html",
+    "css3-background/5-0-border-radius-circle-with-out-of-bounds-radius-expected.png",
+    "css3-background/5-0-border-radius-circle-with-out-of-bounds-radius.html",
+    "css3-background/5-0-border-radius-with-background-color-and-border-expected.png",
+    "css3-background/5-0-border-radius-with-background-color-and-border.html",
+    "css3-background/5-0-border-radius-with-background-color-expected.png",
+    "css3-background/5-0-border-radius-with-background-color.html",
+    "css3-background/5-0-border-radius-with-background-image-and-border-expected.png",
+    "css3-background/5-0-border-radius-with-background-image-and-border.html",
+    "css3-background/5-0-border-radius-with-background-image-expected.png",
+    "css3-background/5-0-border-radius-with-background-image.html",
+    "css3-background/5-0-border-radius-with-border-only-expected.png",
+    "css3-background/5-0-border-radius-with-border-only.html",
+    "css3-background/5-0-border-radius-with-zero-value-and-background-color-and-border-and-inset-shadow.html",
+    "css3-background/5-0-border-radius-with-zero-value-and-background-color-and-border-and-outset-shadow.html",
+    "css3-background/5-0-border-radius-with-zero-value-and-background-color-and-border.html",
+    "css3-background/5-0-border-radius-with-zero-value-and-background-color-and-inset-shadow.html",
+    "css3-background/5-0-border-radius-with-zero-value-and-background-color-and-outset-shadow.html",
+    "css3-background/5-0-border-radius-with-zero-value-and-background-color-expected.png",
+    "css3-background/5-0-border-radius-with-zero-value-and-background-color.html",
+    "css3-background/5-0-border-radius-with-zero-value-and-border-only-expected.png",
+    "css3-background/5-0-border-radius-with-zero-value-and-border-only.html",
+    "css3-background/7-1-1-box-shadow-with-inset-and-blur-and-spread-expected.png",
+    "css3-background/7-1-1-box-shadow-with-inset-and-blur-and-spread.html",
+    "css3-background/7-1-1-box-shadow-with-inset-expected.png",
+    "css3-background/7-1-1-box-shadow-with-inset.html",
+    "css3-background/7-1-1-box-shadow-with-spread-expected.png",
+    "css3-background/7-1-1-box-shadow-with-spread.html",
+    "css3-background/7-1-1-inset-box-shadow-applies-to-padding-box-expected.png",
+    "css3-background/7-1-1-inset-box-shadow-applies-to-padding-box.html",
+    "css3-background/7-1-1-outset-box-shadow-applies-to-border-box-expected.png",
+    "css3-background/7-1-1-outset-box-shadow-applies-to-border-box.html",
+    "css3-background/7-1-2-box-shadow-with-blur-expected.png",
+    "css3-background/7-1-2-box-shadow-with-blur.html",
+    "css3-background/7-1-box-shadow-appears-even-with-overflow-hidden-expected.png",
+    "css3-background/7-1-box-shadow-appears-even-with-overflow-hidden.html",
+    "css3-background/7-1-box-shadow-circle-with-inset-negative-spread-blur-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-circle-with-inset-negative-spread-blur-and-rounded-corners.html",
+    "css3-background/7-1-box-shadow-circle-with-inset-spread-blur-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-circle-with-inset-spread-blur-and-rounded-corners.html",
+    "css3-background/7-1-box-shadow-circle-with-negative-spread-blur-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-circle-with-negative-spread-blur-and-rounded-corners.html",
+    "css3-background/7-1-box-shadow-circle-with-spread-blur-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-circle-with-spread-blur-and-rounded-corners.html",
+    "css3-background/7-1-box-shadow-offset-top-left-expected.png",
+    "css3-background/7-1-box-shadow-offset-top-left.html",
+    "css3-background/7-1-box-shadow-with-inset-and-outset-blur-expected.png",
+    "css3-background/7-1-box-shadow-with-inset-and-outset-blur.html",
+    "css3-background/7-1-box-shadow-with-inset-negative-spread-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-with-inset-negative-spread-and-rounded-corners.html",
+    "css3-background/7-1-box-shadow-with-inset-spread-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-with-inset-spread-and-rounded-corners.html",
+    "css3-background/7-1-box-shadow-with-inset-spread-blur-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-with-inset-spread-blur-and-rounded-corners.html",
+    "css3-background/7-1-box-shadow-with-negative-spread-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-with-negative-spread-and-rounded-corners.html",
+    "css3-background/7-1-box-shadow-with-spread-and-blur-expected.png",
+    "css3-background/7-1-box-shadow-with-spread-and-blur.html",
+    "css3-background/7-1-box-shadow-with-spread-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-with-spread-and-rounded-corners.html",
+    "css3-background/7-1-box-shadow-with-spread-blur-and-rounded-corners-expected.png",
+    "css3-background/7-1-box-shadow-with-spread-blur-and-rounded-corners.html",
+    "css3-background/7-1-circle-box-shadow-offset-top-left-expected.png",
+    "css3-background/7-1-circle-box-shadow-offset-top-left.html",
+    "css3-background/7-1-circle-box-shadow-with-inset-expected.png",
+    "css3-background/7-1-circle-box-shadow-with-inset.html",
+    "css3-background/7-1-circle-box-shadow-with-spread-expected.png",
+    "css3-background/7-1-circle-box-shadow-with-spread.html",
+    "css3-background/7-1-simple-black-box-shadow-expected.png",
+    "css3-background/7-1-simple-black-box-shadow.html",
+    "css3-background/7-1-simple-red-box-shadow-expected.png",
+    "css3-background/7-1-simple-red-box-shadow.html",
+    "css3-background/cobalt.png",
+    "css3-background/cobalt_logo.jpg",
+    "css3-background/cobalt_opaque.jpg",
+    "css3-background/layout_tests.txt",
+    "css3-background/legend-sprite.png",
+    "css3-background/test.png",
+    "css3-color/3-2-nested-opacity-compounds-expected.png",
+    "css3-color/3-2-nested-opacity-compounds.html",
+    "css3-color/3-2-non-positioned-element-with-opacity-forms-stacking-context-expected.png",
+    "css3-color/3-2-non-positioned-element-with-opacity-forms-stacking-context.html",
+    "css3-color/3-2-non-positioned-element-with-opacity-impacts-positioned-descendants-expected.png",
+    "css3-color/3-2-non-positioned-element-with-opacity-impacts-positioned-descendants.html",
+    "css3-color/3-2-opacity-applies-to-children-expected.png",
+    "css3-color/3-2-opacity-applies-to-children.html",
+    "css3-color/3-2-opacity-applies-to-text-expected.png",
+    "css3-color/3-2-opacity-applies-to-text.html",
+    "css3-color/3-2-opacity-does-apply-to-background-expected.png",
+    "css3-color/3-2-opacity-does-apply-to-background.html",
+    "css3-color/layout_tests.txt",
+    "css3-conditional/7-4-cssmediarule-expected.png",
+    "css3-conditional/7-4-cssmediarule.html",
+    "css3-conditional/layout_tests.txt",
+    "css3-flexbox/absolutely-positioned-children-expected.png",
+    "css3-flexbox/absolutely-positioned-children.html",
+    "css3-flexbox/combined-baseline-expected.png",
+    "css3-flexbox/combined-baseline.html",
+    "css3-flexbox/combined-container-sizing-edge-cases-expected.png",
+    "css3-flexbox/combined-container-sizing-edge-cases.html",
+    "css3-flexbox/combined-order-and-multiline-expected.png",
+    "css3-flexbox/combined-order-and-multiline.html",
+    "css3-flexbox/combined-positioning-tests-expected.png",
+    "css3-flexbox/combined-positioning-tests.html",
+    "css3-flexbox/combined-shrinking-and-justify-content-expected.png",
+    "css3-flexbox/combined-shrinking-and-justify-content.html",
+    "css3-flexbox/combined-with-baselines-percentages-expected.png",
+    "css3-flexbox/combined-with-baselines-percentages.html",
+    "css3-flexbox/content-based-minimum-size-expected.png",
+    "css3-flexbox/content-based-minimum-size.html",
+    "css3-flexbox/csswg_flex-001-expected.png",
+    "css3-flexbox/csswg_flex-001.html",
+    "css3-flexbox/csswg_flex-002-expected.png",
+    "css3-flexbox/csswg_flex-002.html",
+    "css3-flexbox/csswg_flex-003-expected.png",
+    "css3-flexbox/csswg_flex-003.html",
+    "css3-flexbox/csswg_flex-004-expected.png",
+    "css3-flexbox/csswg_flex-004.html",
+    "css3-flexbox/csswg_flex-basis-001-expected.png",
+    "css3-flexbox/csswg_flex-basis-001.html",
+    "css3-flexbox/csswg_flex-basis-002-expected.png",
+    "css3-flexbox/csswg_flex-basis-002.html",
+    "css3-flexbox/csswg_flex-basis-003-expected.png",
+    "css3-flexbox/csswg_flex-basis-003.html",
+    "css3-flexbox/csswg_flex-basis-004-expected.png",
+    "css3-flexbox/csswg_flex-basis-004.html",
+    "css3-flexbox/csswg_flex-basis-005-expected.png",
+    "css3-flexbox/csswg_flex-basis-005.html",
+    "css3-flexbox/csswg_flex-basis-006-expected.png",
+    "css3-flexbox/csswg_flex-basis-006.html",
+    "css3-flexbox/csswg_flex-basis-007-expected.png",
+    "css3-flexbox/csswg_flex-basis-007.html",
+    "css3-flexbox/csswg_flex-basis-008-expected.png",
+    "css3-flexbox/csswg_flex-basis-008.html",
+    "css3-flexbox/csswg_flex-basis-010-expected.png",
+    "css3-flexbox/csswg_flex-basis-010.html",
+    "css3-flexbox/csswg_flex-grow-001-expected.png",
+    "css3-flexbox/csswg_flex-grow-001.html",
+    "css3-flexbox/csswg_flex-grow-002-expected.png",
+    "css3-flexbox/csswg_flex-grow-002.html",
+    "css3-flexbox/csswg_flex-grow-003-expected.png",
+    "css3-flexbox/csswg_flex-grow-003.html",
+    "css3-flexbox/csswg_flex-grow-004-expected.png",
+    "css3-flexbox/csswg_flex-grow-004.html",
+    "css3-flexbox/csswg_flex-grow-005-expected.png",
+    "css3-flexbox/csswg_flex-grow-005.html",
+    "css3-flexbox/csswg_flex-grow-006-expected.png",
+    "css3-flexbox/csswg_flex-grow-006.html",
+    "css3-flexbox/csswg_flex-grow-007-expected.png",
+    "css3-flexbox/csswg_flex-grow-007.html",
+    "css3-flexbox/csswg_flex-shrink-001-expected.png",
+    "css3-flexbox/csswg_flex-shrink-001.html",
+    "css3-flexbox/csswg_flex-shrink-002-expected.png",
+    "css3-flexbox/csswg_flex-shrink-002.html",
+    "css3-flexbox/csswg_flex-shrink-003-expected.png",
+    "css3-flexbox/csswg_flex-shrink-003.html",
+    "css3-flexbox/csswg_flex-shrink-004-expected.png",
+    "css3-flexbox/csswg_flex-shrink-004.html",
+    "css3-flexbox/csswg_flex-shrink-005-expected.png",
+    "css3-flexbox/csswg_flex-shrink-005.html",
+    "css3-flexbox/csswg_flex-shrink-006-expected.png",
+    "css3-flexbox/csswg_flex-shrink-006.html",
+    "css3-flexbox/csswg_flex-shrink-007-expected.png",
+    "css3-flexbox/csswg_flex-shrink-007.html",
+    "css3-flexbox/csswg_flex-shrink-008-expected.png",
+    "css3-flexbox/csswg_flex-shrink-008.html",
+    "css3-flexbox/csswg_flexbox-flex-basis-content-001a-expected.png",
+    "css3-flexbox/csswg_flexbox-flex-basis-content-001a.html",
+    "css3-flexbox/csswg_flexbox-flex-basis-content-001b-expected.png",
+    "css3-flexbox/csswg_flexbox-flex-basis-content-001b.html",
+    "css3-flexbox/csswg_flexbox-flex-basis-content-002a-expected.png",
+    "css3-flexbox/csswg_flexbox-flex-basis-content-002a.html",
+    "css3-flexbox/csswg_flexbox-flex-basis-content-002b-expected.png",
+    "css3-flexbox/csswg_flexbox-flex-basis-content-002b.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0-0-unitless-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0-0-unitless.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-0.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0-N-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0-N-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-N-unitless-basis-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0-N-unitless-basis.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-N.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-Npercent-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0-Npercent-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0-Npercent-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-Npercent.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0-auto-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0-auto-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-0-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-0.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-0-unitless-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-0-unitless.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-0.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-1-unitless-basis-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-1-unitless-basis.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-N-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-N-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-N-unitless-basis-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-N-unitless-basis.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-N.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-Npercent-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-Npercent-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-Npercent-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-Npercent.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-auto-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1-auto-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-0-1-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-1.html",
+    "css3-flexbox/csswg_flexbox_flex-0-N-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-N-0-unitless-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-N-0-unitless.html",
+    "css3-flexbox/csswg_flexbox_flex-0-N-0.html",
+    "css3-flexbox/csswg_flexbox_flex-0-N-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-N-N-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-N-N-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-0-N-N.html",
+    "css3-flexbox/csswg_flexbox_flex-0-N-Npercent-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-N-Npercent-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-N-Npercent-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-0-N-Npercent.html",
+    "css3-flexbox/csswg_flexbox_flex-0-N-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-N-auto-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-N-auto-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-0-N-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-0-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-N.html",
+    "css3-flexbox/csswg_flexbox_flex-0-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-0-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-1-0-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-0-0-unitless-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-0-0-unitless.html",
+    "css3-flexbox/csswg_flexbox_flex-1-0-0.html",
+    "css3-flexbox/csswg_flexbox_flex-1-0-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-0-N-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-0-N-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-1-0-N.html",
+    "css3-flexbox/csswg_flexbox_flex-1-0-Npercent-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-0-Npercent-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-0-Npercent-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-1-0-Npercent.html",
+    "css3-flexbox/csswg_flexbox_flex-1-0-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-0-auto-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-0-auto-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-1-0-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-1-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-0.html",
+    "css3-flexbox/csswg_flexbox_flex-1-1-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-1-0-unitless-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-1-0-unitless.html",
+    "css3-flexbox/csswg_flexbox_flex-1-1-0.html",
+    "css3-flexbox/csswg_flexbox_flex-1-1-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-1-N-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-1-N-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-1-1-N.html",
+    "css3-flexbox/csswg_flexbox_flex-1-1-Npercent-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-1-Npercent-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-1-Npercent-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-1-1-Npercent.html",
+    "css3-flexbox/csswg_flexbox_flex-1-1-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-1-auto-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-1-auto-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-1-1-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-1-1-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-1.html",
+    "css3-flexbox/csswg_flexbox_flex-1-N-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-N-0-unitless-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-N-0-unitless.html",
+    "css3-flexbox/csswg_flexbox_flex-1-N-0.html",
+    "css3-flexbox/csswg_flexbox_flex-1-N-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-N-N-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-N-N-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-1-N-N.html",
+    "css3-flexbox/csswg_flexbox_flex-1-N-Npercent-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-N-Npercent-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-N-Npercent-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-1-N-Npercent.html",
+    "css3-flexbox/csswg_flexbox_flex-1-N-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-N-auto-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-N-auto-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-1-N-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-1-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-1-N.html",
+    "css3-flexbox/csswg_flexbox_flex-N-0-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-0-0-unitless-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-0-0-unitless.html",
+    "css3-flexbox/csswg_flexbox_flex-N-0-0.html",
+    "css3-flexbox/csswg_flexbox_flex-N-0-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-0-N-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-0-N-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-N-0-N.html",
+    "css3-flexbox/csswg_flexbox_flex-N-0-Npercent-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-0-Npercent-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-0-Npercent-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-N-0-Npercent.html",
+    "css3-flexbox/csswg_flexbox_flex-N-0-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-0-auto-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-0-auto-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-N-0-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-N-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-0.html",
+    "css3-flexbox/csswg_flexbox_flex-N-1-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-1-0-unitless-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-1-0-unitless.html",
+    "css3-flexbox/csswg_flexbox_flex-N-1-0.html",
+    "css3-flexbox/csswg_flexbox_flex-N-1-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-1-N-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-1-N-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-N-1-N.html",
+    "css3-flexbox/csswg_flexbox_flex-N-1-Npercent-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-1-Npercent-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-1-Npercent-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-N-1-Npercent.html",
+    "css3-flexbox/csswg_flexbox_flex-N-1-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-1-auto-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-1-auto-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-N-1-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-N-1-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-1.html",
+    "css3-flexbox/csswg_flexbox_flex-N-N-0-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-N-0-unitless-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-N-0-unitless.html",
+    "css3-flexbox/csswg_flexbox_flex-N-N-0.html",
+    "css3-flexbox/csswg_flexbox_flex-N-N-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-N-N-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-N-N-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-N-N-N.html",
+    "css3-flexbox/csswg_flexbox_flex-N-N-Npercent-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-N-Npercent-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-N-Npercent-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-N-N-Npercent.html",
+    "css3-flexbox/csswg_flexbox_flex-N-N-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-N-auto-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-N-auto-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-N-N-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-N-N-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-N-N.html",
+    "css3-flexbox/csswg_flexbox_flex-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-basis-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-basis-shrink-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-basis-shrink.html",
+    "css3-flexbox/csswg_flexbox_flex-basis.html",
+    "css3-flexbox/csswg_flexbox_flex-initial-2-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-initial-2.html",
+    "css3-flexbox/csswg_flexbox_flex-initial-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-initial.html",
+    "css3-flexbox/csswg_flexbox_flex-natural-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-natural-mixed-basis-auto-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-natural-mixed-basis-auto.html",
+    "css3-flexbox/csswg_flexbox_flex-natural-mixed-basis-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-natural-mixed-basis.html",
+    "css3-flexbox/csswg_flexbox_flex-natural-variable-auto-basis-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-natural-variable-auto-basis.html",
+    "css3-flexbox/csswg_flexbox_flex-natural-variable-zero-basis-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-natural-variable-zero-basis.html",
+    "css3-flexbox/csswg_flexbox_flex-natural.html",
+    "css3-flexbox/csswg_flexbox_flex-none-expected.png",
+    "css3-flexbox/csswg_flexbox_flex-none.html",
+    "css3-flexbox/empty_container_baseline-expected.png",
+    "css3-flexbox/empty_container_baseline.html",
+    "css3-flexbox/flex-container-auto-margins-expected.png",
+    "css3-flexbox/flex-container-auto-margins.html",
+    "css3-flexbox/flex-items-flexibility-expected.png",
+    "css3-flexbox/flex-items-flexibility.html",
+    "css3-flexbox/layout_tests.txt",
+    "css3-flexbox/positioned-containers-expected.png",
+    "css3-flexbox/positioned-containers.html",
+    "css3-fonts/4-2-font-face-font-family-hides-system-font-family-expected.png",
+    "css3-fonts/4-2-font-face-font-family-hides-system-font-family.html",
+    "css3-fonts/4-3-src-local-can-match-font-postscript-name-expected.png",
+    "css3-fonts/4-3-src-local-can-match-font-postscript-name.html",
+    "css3-fonts/4-3-src-local-can-match-full-font-name-expected.png",
+    "css3-fonts/4-3-src-local-can-match-full-font-name.html",
+    "css3-fonts/4-3-use-first-available-local-font-face-expected.png",
+    "css3-fonts/4-3-use-first-available-local-font-face.html",
+    "css3-fonts/4-3-use-next-font-family-if-font-face-sources-unavailable-expected.png",
+    "css3-fonts/4-3-use-next-font-family-if-font-face-sources-unavailable.html",
+    "css3-fonts/4-4-use-correct-style-in-font-face-set-expected.png",
+    "css3-fonts/4-4-use-correct-style-in-font-face-set.html",
+    "css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range-expected.png",
+    "css3-fonts/4-5-prefer-later-face-with-close-style-over-earlier-face-with-unicode-range.html",
+    "css3-fonts/4-5-use-correct-font-with-unicode-range-expected.png",
+    "css3-fonts/4-5-use-correct-font-with-unicode-range.html",
+    "css3-fonts/5-2-use-correct-style-in-font-family-expected.png",
+    "css3-fonts/5-2-use-correct-style-in-font-family.html",
+    "css3-fonts/5-2-use-first-available-listed-font-family-expected.png",
+    "css3-fonts/5-2-use-first-available-listed-font-family.html",
+    "css3-fonts/5-2-use-numerical-font-weights-in-family-face-matching-expected.png",
+    "css3-fonts/5-2-use-numerical-font-weights-in-family-face-matching.html",
+    "css3-fonts/5-2-use-specified-font-family-if-available-expected.png",
+    "css3-fonts/5-2-use-specified-font-family-if-available.html",
+    "css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found-expected.png",
+    "css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found.html",
+    "css3-fonts/color-emojis-should-render-properly-expected.png",
+    "css3-fonts/color-emojis-should-render-properly.html",
+    "css3-fonts/layout_tests.txt",
+    "css3-fonts/synthetic-bolding-should-not-occur-on-bold-font-expected.png",
+    "css3-fonts/synthetic-bolding-should-not-occur-on-bold-font.html",
+    "css3-fonts/synthetic-bolding-should-occur-on-non-bold-font-expected.png",
+    "css3-fonts/synthetic-bolding-should-occur-on-non-bold-font.html",
+    "css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs-expected.png",
+    "css3-fonts/text-bounds-should-be-calculated-properly-when-containing-zero-height-font-runs.html",
+    "css3-images/4-1-2-corner-to-corner-linear-gradient-expected.png",
+    "css3-images/4-1-2-corner-to-corner-linear-gradient.html",
+    "css3-images/4-1-2-gradient-in-middle-expected.png",
+    "css3-images/4-1-2-gradient-in-middle.html",
+    "css3-images/4-1-2-linear-gradient-with-same-stop-positions-expected.png",
+    "css3-images/4-1-2-linear-gradient-with-same-stop-positions.html",
+    "css3-images/4-1-2-linear-gradient-with-transparency-expected.png",
+    "css3-images/4-1-2-linear-gradient-with-transparency.html",
+    "css3-images/4-1-2-multistop-one-dimensional-expected.png",
+    "css3-images/4-1-2-multistop-one-dimensional-gradient-expected.png",
+    "css3-images/4-1-2-multistop-one-dimensional-gradient.html",
+    "css3-images/4-1-2-simple-yellow-to-blue-arbitrary-angle-linear-gradient-expected.png",
+    "css3-images/4-1-2-simple-yellow-to-blue-arbitrary-angle-linear-gradient.html",
+    "css3-images/4-1-2-simple-yellow-to-blue-vertical-linear-gradient-expected.png",
+    "css3-images/4-1-2-simple-yellow-to-blue-vertical-linear-gradient.html",
+    "css3-images/4-1-2-three-color-linear-gradient-expected.png",
+    "css3-images/4-1-2-three-color-linear-gradient.html",
+    "css3-images/4-1-2-to-bottom-linear-gradient-expected.png",
+    "css3-images/4-1-2-to-bottom-linear-gradient.html",
+    "css3-images/4-1-2-to-left-linear-gradient-expected.png",
+    "css3-images/4-1-2-to-left-linear-gradient.html",
+    "css3-images/4-1-2-to-right-linear-gradient-expected.png",
+    "css3-images/4-1-2-to-right-linear-gradient.html",
+    "css3-images/4-1-2-to-top-linear-gradient-expected.png",
+    "css3-images/4-1-2-to-top-linear-gradient.html",
+    "css3-images/4-2-4-closest-side-radial-gradient-expected.png",
+    "css3-images/4-2-4-closest-side-radial-gradient.html",
+    "css3-images/4-2-4-radial-gradient-non-centered-expected.png",
+    "css3-images/4-2-4-radial-gradient-non-centered.html",
+    "css3-images/4-2-4-radial-gradient-with-transparency-expected.png",
+    "css3-images/4-2-4-radial-gradient-with-transparency.html",
+    "css3-images/4-2-4-simple-radial-gradients-expected.png",
+    "css3-images/4-2-4-simple-radial-gradients.html",
+    "css3-images/layout_tests.txt",
+    "css3-mediaqueries/bug_style.css",
+    "css3-mediaqueries/layout_tests.txt",
+    "css3-mediaqueries/media_query_rule_precedence_200-expected.png",
+    "css3-mediaqueries/media_query_rule_precedence_200.html",
+    "css3-mediaqueries/media_query_rule_precedence_300-expected.png",
+    "css3-mediaqueries/media_query_rule_precedence_300.html",
+    "css3-mediaqueries/media_query_rule_precedence_400-expected.png",
+    "css3-mediaqueries/media_query_rule_precedence_400.html",
+    "css3-text-decor/4-red-text-shadow-expected.png",
+    "css3-text-decor/4-red-text-shadow.html",
+    "css3-text-decor/4-simple-text-shadow-expected.png",
+    "css3-text-decor/4-simple-text-shadow.html",
+    "css3-text-decor/4-text-shadow-appears-when-text-color-is-transparent-expected.png",
+    "css3-text-decor/4-text-shadow-appears-when-text-color-is-transparent.html",
+    "css3-text-decor/4-text-shadow-glow-expected.png",
+    "css3-text-decor/4-text-shadow-glow.html",
+    "css3-text-decor/4-text-shadow-multiple-layers-expected.png",
+    "css3-text-decor/4-text-shadow-multiple-layers.html",
+    "css3-text-decor/4-text-shadow-multiple-overlapping-blurred-layers-expected.png",
+    "css3-text-decor/4-text-shadow-multiple-overlapping-blurred-layers.html",
+    "css3-text-decor/4-text-shadow-multiple-overlapping-layers-expected.png",
+    "css3-text-decor/4-text-shadow-multiple-overlapping-layers.html",
+    "css3-text-decor/4-text-shadow-with-blur-expected.png",
+    "css3-text-decor/4-text-shadow-with-blur.html",
+    "css3-text-decor/4-text-shadow-with-large-blur-expected.png",
+    "css3-text-decor/4-text-shadow-with-large-blur.html",
+    "css3-text-decor/layout_tests.txt",
+    "css3-transitions/2-transition-ends-on-display-none-expected.png",
+    "css3-transitions/2-transition-ends-on-display-none-for-pseudoelement-expected.png",
+    "css3-transitions/2-transition-ends-on-display-none-for-pseudoelement.html",
+    "css3-transitions/2-transition-ends-on-display-none-from-ancestor-expected.png",
+    "css3-transitions/2-transition-ends-on-display-none-from-ancestor.html",
+    "css3-transitions/2-transition-ends-on-display-none.html",
+    "css3-transitions/2-transition-for-inheritable-property-from-ancestor-should-occur-expected.png",
+    "css3-transitions/2-transition-for-inheritable-property-from-ancestor-should-occur.html",
+    "css3-transitions/2-transition-for-not-inheritable-property-from-ancestor-should-not-occur-expected.png",
+    "css3-transitions/2-transition-for-not-inheritable-property-from-ancestor-should-not-occur.html",
+    "css3-transitions/2-transition-start-during-display-none-does-not-transition-expected.png",
+    "css3-transitions/2-transition-start-during-display-none-does-not-transition-from-ancestor-expected.png",
+    "css3-transitions/2-transition-start-during-display-none-does-not-transition-from-ancestor.html",
+    "css3-transitions/2-transition-start-during-display-none-does-not-transition.html",
+    "css3-transitions/2-transition-started-on-display-none-pseudoelement-does-not-dcheck-expected.png",
+    "css3-transitions/2-transition-started-on-display-none-pseudoelement-does-not-dcheck.html",
+    "css3-transitions/3-transition-not-started-on-reattach-expected.png",
+    "css3-transitions/3-transition-not-started-on-reattach.html",
+    "css3-transitions/5-multiple-transitions-should-fire-in-correct-order-expected.png",
+    "css3-transitions/5-multiple-transitions-should-fire-in-correct-order.html",
+    "css3-transitions/5-simple-transition-expected.png",
+    "css3-transitions/5-simple-transition-for-pseudoelement-expected.png",
+    "css3-transitions/5-simple-transition-for-pseudoelement.html",
+    "css3-transitions/5-simple-transition.html",
+    "css3-transitions/canceled_transition_property_should_not_get_transitionend_event-expected.png",
+    "css3-transitions/canceled_transition_property_should_not_get_transitionend_event.html",
+    "css3-transitions/layout_tests.txt",
+    "css3-transitions/modified_transition_property_from_other_transitionend_event_handler_should_still_get_transitionend_event-expected.png",
+    "css3-transitions/modified_transition_property_from_other_transitionend_event_handler_should_still_get_transitionend_event.html",
+    "css3-transitions/removed_transition_property_from_other_transitionend_event_handler_should_still_get_transitionend_event-expected.png",
+    "css3-transitions/removed_transition_property_from_other_transitionend_event_handler_should_still_get_transitionend_event.html",
+    "css3-ui/5-2-ellipsis-should-clip-when-it-overflows-expected.png",
+    "css3-ui/5-2-ellipsis-should-clip-when-it-overflows.html",
+    "css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows-expected.png",
+    "css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows.html",
+    "css3-ui/5-2-ellipsis-should-ellipsize-overflowing-atomic-inline-element-after-text-expected.png",
+    "css3-ui/5-2-ellipsis-should-ellipsize-overflowing-atomic-inline-element-after-text.html",
+    "css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word-expected.png",
+    "css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word.html",
+    "css3-ui/5-2-ellipsis-should-ellipsize-overflowing-second-atomic-inline-element-on-line-expected.png",
+    "css3-ui/5-2-ellipsis-should-ellipsize-overflowing-second-atomic-inline-element-on-line.html",
+    "css3-ui/5-2-ellipsis-should-ellipsize-second-atomic-inline-element-with-overflowing-end-margin-at-start-edge-expected.png",
+    "css3-ui/5-2-ellipsis-should-ellipsize-second-atomic-inline-element-with-overflowing-end-margin-at-start-edge.html",
+    "css3-ui/5-2-ellipsis-should-ellipsize-second-atomic-inline-element-with-overflowing-start-margin-at-start-edge-expected.png",
+    "css3-ui/5-2-ellipsis-should-ellipsize-second-atomic-inline-element-with-overflowing-start-margin-at-start-edge.html",
+    "css3-ui/5-2-ellipsis-should-ellipsize-span-with-overflowing-end-margin-at-end-line-edge-expected.png",
+    "css3-ui/5-2-ellipsis-should-ellipsize-span-with-overflowing-end-margin-at-end-line-edge.html",
+    "css3-ui/5-2-ellipsis-should-ellipsize-span-with-overflowing-start-margin-at-end-line-edge-expected.png",
+    "css3-ui/5-2-ellipsis-should-ellipsize-span-with-overflowing-start-margin-at-end-line-edge.html",
+    "css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix-expected.png",
+    "css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix.html",
+    "css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation-expected.png",
+    "css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation.html",
+    "css3-ui/5-2-ellipsis-should-not-display-ellipsis-when-overflow-is-visible-expected.png",
+    "css3-ui/5-2-ellipsis-should-not-display-ellipsis-when-overflow-is-visible.html",
+    "css3-ui/5-2-ellipsis-should-not-ellipsize-first-atomic-inline-element-on-line-expected.png",
+    "css3-ui/5-2-ellipsis-should-not-ellipsize-first-atomic-inline-element-on-line.html",
+    "css3-ui/5-2-ellipsis-should-not-ellipsize-first-atomic-inline-element-with-overflowing-end-margin-expected.png",
+    "css3-ui/5-2-ellipsis-should-not-ellipsize-first-atomic-inline-element-with-overflowing-end-margin.html",
+    "css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line-expected.png",
+    "css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line.html",
+    "css3-ui/5-2-ellipsis-should-not-place-ellipsis-before-first-character-is-encountered-on-line-expected.png",
+    "css3-ui/5-2-ellipsis-should-not-place-ellipsis-before-first-character-is-encountered-on-line.html",
+    "css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins-expected.png",
+    "css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins.html",
+    "css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span-expected.png",
+    "css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span.html",
+    "css3-ui/5-2-ellipsis-should-use-style-of-containing-block-expected.png",
+    "css3-ui/5-2-ellipsis-should-use-style-of-containing-block.html",
+    "css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis-expected.png",
+    "css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis.html",
+    "css3-ui/5-2-text-overflow-clip-should-not-display-ellipsis-on-overflow-expected.png",
+    "css3-ui/5-2-text-overflow-clip-should-not-display-ellipsis-on-overflow.html",
+    "css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden-expected.png",
+    "css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden.html",
+    "css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited-expected.png",
+    "css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited.html",
+    "css3-ui/layout_tests.txt",
+    "css3-values/4-2-numbers-can-be-represented-in-scientific-notation-expected.png",
+    "css3-values/4-2-numbers-can-be-represented-in-scientific-notation.html",
+    "css3-values/5-1-1-em-should-be-relative-to-parent-expected.png",
+    "css3-values/5-1-1-em-should-be-relative-to-parent.html",
+    "css3-values/5-1-1-rem-should-be-relative-to-root-expected.png",
+    "css3-values/5-1-1-rem-should-be-relative-to-root.html",
+    "css3-values/5-1-1-vw-vh-should-be-relative-to-viewport-size-expected.png",
+    "css3-values/5-1-1-vw-vh-should-be-relative-to-viewport-size.html",
+    "css3-values/layout_tests.txt",
+    "cssom-view/6-scrolling-area-absolute-positioned-children-expected.png",
+    "cssom-view/6-scrolling-area-absolute-positioned-children-rtl-expected.png",
+    "cssom-view/6-scrolling-area-absolute-positioned-children-rtl.html",
+    "cssom-view/6-scrolling-area-absolute-positioned-children.html",
+    "cssom-view/6-scrolling-area-absolute-positioned-grandchildren-expected.png",
+    "cssom-view/6-scrolling-area-absolute-positioned-grandchildren-rtl-expected.png",
+    "cssom-view/6-scrolling-area-absolute-positioned-grandchildren-rtl.html",
+    "cssom-view/6-scrolling-area-absolute-positioned-grandchildren.html",
+    "cssom-view/6-scrolling-area-fix-positioned-children-excluded-expected.png",
+    "cssom-view/6-scrolling-area-fix-positioned-children-excluded.html",
+    "cssom-view/6-scrolling-area-fix-positioned-children-included-expected.png",
+    "cssom-view/6-scrolling-area-fix-positioned-children-included.html",
+    "cssom-view/6-scrolling-area-not-visible-grandchildren-excluded-expected.png",
+    "cssom-view/6-scrolling-area-not-visible-grandchildren-excluded.html",
+    "cssom-view/6-scrolling-area-scaled-content-expected.png",
+    "cssom-view/6-scrolling-area-scaled-content.html",
+    "cssom-view/6-scrolling-area-visible-and-scaled-grandchildren-included-expected.png",
+    "cssom-view/6-scrolling-area-visible-and-scaled-grandchildren-included.html",
+    "cssom-view/extensions_to_the_element_interface_client_top_left_width_height-expected.png",
+    "cssom-view/extensions_to_the_element_interface_client_top_left_width_height.html",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_box_splitting-expected.png",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_box_splitting.html",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_nested_transforms-expected.png",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_nested_transforms.html",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_scale_transform-expected.png",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_scale_transform.html",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_scroll-expected.png",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_scroll.html",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_scroll_rtl-expected.png",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_scroll_rtl.html",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_translate_transform-expected.png",
+    "cssom-view/extensions_to_the_element_interface_get_bounding_client_rect_with_translate_transform.html",
+    "cssom-view/extensions_to_the_html_element_interface_offset_top_left_width_height-expected.png",
+    "cssom-view/extensions_to_the_html_element_interface_offset_top_left_width_height.html",
+    "cssom-view/extensions_to_the_html_element_interface_offset_width_height_with_box_splitting-expected.png",
+    "cssom-view/extensions_to_the_html_element_interface_offset_width_height_with_box_splitting.html",
+    "cssom-view/layout_tests.txt",
+    "dom/2-7-event-target-should-be-constructible-expected.png",
+    "dom/2-7-event-target-should-be-constructible.html",
+    "dom/layout_tests.txt",
+    "incremental-layout/DISABLED-size-related-style-change-should-trigger-size-update-of-absolute-positioned-siblings.html",
+    "incremental-layout/added_inline_child_of_inline_box_gets_rendered-expected.png",
+    "incremental-layout/added_inline_child_of_inline_box_gets_rendered.html",
+    "incremental-layout/child-being-moved-to-new-parent-should-trigger-inherited-style-update-expected.png",
+    "incremental-layout/child-being-moved-to-new-parent-should-trigger-inherited-style-update.html",
+    "incremental-layout/computed_style_change_gets_rendered-expected.png",
+    "incremental-layout/computed_style_change_gets_rendered.html",
+    "incremental-layout/cross_references_style_change_should_trigger_containing_block_update-expected.png",
+    "incremental-layout/cross_references_style_change_should_trigger_containing_block_update.html",
+    "incremental-layout/cross_references_style_change_should_trigger_stacking_context_update-expected.png",
+    "incremental-layout/cross_references_style_change_should_trigger_stacking_context_update.html",
+    "incremental-layout/dir_attribute_change_gets_rendered-expected.png",
+    "incremental-layout/dir_attribute_change_gets_rendered.html",
+    "incremental-layout/layout_tests.txt",
+    "incremental-layout/removed_inline_child_of_inline_box_does_not_get_rendered-expected.png",
+    "incremental-layout/removed_inline_child_of_inline_box_does_not_get_rendered.html",
+    "incremental-layout/size-related-style-change-should-trigger-size-update-of-absolute-positioned-children-expected.png",
+    "incremental-layout/size-related-style-change-should-trigger-size-update-of-absolute-positioned-children.html",
+    "incremental-layout/size-related-style-change-should-trigger-size-update-of-absolute-positioned-descendants-expected.png",
+    "incremental-layout/size-related-style-change-should-trigger-size-update-of-absolute-positioned-descendants.html",
+    "incremental-layout/size-related-style-change-should-trigger-size-update-of-static-descendants-expected.png",
+    "incremental-layout/size-related-style-change-should-trigger-size-update-of-static-descendants.html",
+    "incremental-layout/size-related-style-change-should-trigger-size-update-of-static-siblings-expected.png",
+    "incremental-layout/size-related-style-change-should-trigger-size-update-of-static-siblings.html",
+    "incremental-layout/text-node-text-content-mutation-expected.png",
+    "incremental-layout/text-node-text-content-mutation.html",
+    "incremental-layout/transform-style-change-should-trigger-size-update-expected.png",
+    "incremental-layout/transform-style-change-should-trigger-size-update.html",
+    "intersection-observer/containing-block-has-rotate-transform-expected.png",
+    "intersection-observer/containing-block-has-rotate-transform.html",
+    "intersection-observer/containing-block-undergoes-transition-expected.png",
+    "intersection-observer/containing-block-undergoes-transition.html",
+    "intersection-observer/element-in-containing-block-chain-has-overflow-clip-with-padding-and-border-expected.png",
+    "intersection-observer/element-in-containing-block-chain-has-overflow-clip-with-padding-and-border.html",
+    "intersection-observer/element-in-containing-block-chain-has-overflow-clip-without-padding-or-border-expected.png",
+    "intersection-observer/element-in-containing-block-chain-has-overflow-clip-without-padding-or-border.html",
+    "intersection-observer/intersection-ratio-is-nonzero-but-is-intersecting-is-false-expected.png",
+    "intersection-observer/intersection-ratio-is-nonzero-but-is-intersecting-is-false.html",
+    "intersection-observer/layout_tests.txt",
+    "intersection-observer/multiple-observers-with-different-roots-and-targets-expected.png",
+    "intersection-observer/multiple-observers-with-different-roots-and-targets.html",
+    "intersection-observer/no-intersection-when-root-is-not-in-containing-block-chain-of-target-expected.png",
+    "intersection-observer/no-intersection-when-root-is-not-in-containing-block-chain-of-target.html",
+    "intersection-observer/observers-should-update-when-elements-move-with-threshold-expected.png",
+    "intersection-observer/observers-should-update-when-elements-move-with-threshold.html",
+    "intersection-observer/observers-should-update-when-elements-move-without-threshold-expected.png",
+    "intersection-observer/observers-should-update-when-elements-move-without-threshold.html",
+    "intersection-observer/previous-threshold-index-and-is-intersecting-fields-should-be-updated-expected.png",
+    "intersection-observer/previous-threshold-index-and-is-intersecting-fields-should-be-updated.html",
+    "intersection-observer/root-has-nonzero-padding-and-border-and-overflow-clip-expected.png",
+    "intersection-observer/root-has-nonzero-padding-and-border-and-overflow-clip.html",
+    "intersection-observer/root-has-nonzero-padding-and-border-expected.png",
+    "intersection-observer/root-has-nonzero-padding-and-border.html",
+    "intersection-observer/root-has-nonzero-root-margin-expected.png",
+    "intersection-observer/root-has-nonzero-root-margin.html",
+    "intersection-observer/root-intersects-with-multiple-targets-expected.png",
+    "intersection-observer/root-intersects-with-multiple-targets.html",
+    "intersection-observer/root-is-viewport-and-target-is-partially-visible-expected.png",
+    "intersection-observer/root-is-viewport-and-target-is-partially-visible.html",
+    "intersection-observer/target-has-nonzero-padding-and-border-expected.png",
+    "intersection-observer/target-has-nonzero-padding-and-border.html",
+    "intersection-observer/target-undergoes-transition-expected.png",
+    "intersection-observer/target-undergoes-transition.html",
+    "intersection-observer/target-with-nonzero-area-is-edge-adjacent-to-root-expected.png",
+    "intersection-observer/target-with-nonzero-area-is-edge-adjacent-to-root.html",
+    "intersection-observer/target-with-zero-area-is-edge-adjacent-to-root-expected.png",
+    "intersection-observer/target-with-zero-area-is-edge-adjacent-to-root.html",
+    "intersection-observer/unobserved-targets-do-not-get-included-in-next-update-expected.png",
+    "intersection-observer/unobserved-targets-do-not-get-included-in-next-update.html",
+    "intersection-observer/unobserving-elements-without-calling-observe-should-not-crash-expected.png",
+    "intersection-observer/unobserving-elements-without-calling-observe-should-not-crash.html",
+    "lottie-player/layout_tests.txt",
+    "lottie-player/lottie-background-attribute-expected.png",
+    "lottie-player/lottie-background-attribute.html",
+    "lottie-player/lottie-playback-events-expected.png",
+    "lottie-player/lottie-playback-events.html",
+    "text-shaping/combining-character-sequences-should-be-handled-properly-expected.png",
+    "text-shaping/combining-character-sequences-should-be-handled-properly.html",
+    "text-shaping/layout_tests.txt",
+    "the-dir-attribute/layout_tests.txt",
+    "the-dir-attribute/the-dir-attribute-001-expected.png",
+    "the-dir-attribute/the-dir-attribute-001.html",
+    "the-dir-attribute/the-dir-attribute-002-expected.png",
+    "the-dir-attribute/the-dir-attribute-002.html",
+    "the-dir-attribute/the-dir-attribute-003.html",
+    "the-dir-attribute/the-dir-attribute-004.html",
+    "the-dir-attribute/the-dir-attribute-005.html",
+    "the-dir-attribute/the-dir-attribute-006.html",
+    "the-dir-attribute/the-dir-attribute-007-expected.png",
+    "the-dir-attribute/the-dir-attribute-007.html",
+    "the-dir-attribute/the-dir-attribute-008-expected.png",
+    "the-dir-attribute/the-dir-attribute-008.html",
+    "the-dir-attribute/the-dir-attribute-009-expected.png",
+    "the-dir-attribute/the-dir-attribute-009.html",
+    "the-dir-attribute/the-dir-attribute-010-expected.png",
+    "the-dir-attribute/the-dir-attribute-010.html",
+    "the-dir-attribute/the-dir-attribute-011-expected.png",
+    "the-dir-attribute/the-dir-attribute-011.html",
+    "the-dir-attribute/the-dir-attribute-012-expected.png",
+    "the-dir-attribute/the-dir-attribute-012.html",
+    "the-dir-attribute/the-dir-attribute-046-expected.png",
+    "the-dir-attribute/the-dir-attribute-046.html",
+    "the-dir-attribute/the-dir-attribute-047-expected.png",
+    "the-dir-attribute/the-dir-attribute-047.html",
+    "the-dir-attribute/the-dir-attribute-048-expected.png",
+    "the-dir-attribute/the-dir-attribute-048.html",
+    "the-dir-attribute/the-dir-attribute-049-expected.png",
+    "the-dir-attribute/the-dir-attribute-049.html",
+    "the-dir-attribute/the-dir-attribute-050-expected.png",
+    "the-dir-attribute/the-dir-attribute-050.html",
+    "the-dir-attribute/the-dir-attribute-051.html",
+    "the-dir-attribute/the-dir-attribute-052.html",
+    "the-dir-attribute/the-dir-attribute-053-expected.png",
+    "the-dir-attribute/the-dir-attribute-053.html",
+    "the-dir-attribute/the-dir-attribute-054-expected.png",
+    "the-dir-attribute/the-dir-attribute-054.html",
+    "the-dir-attribute/the-dir-attribute-055-expected.png",
+    "the-dir-attribute/the-dir-attribute-055.html",
+    "the-dir-attribute/the-dir-attribute-056-expected.png",
+    "the-dir-attribute/the-dir-attribute-056.html",
+    "the-dir-attribute/the-dir-attribute-057-expected.png",
+    "the-dir-attribute/the-dir-attribute-057.html",
+    "the-dir-attribute/the-dir-attribute-058-expected.png",
+    "the-dir-attribute/the-dir-attribute-058.html",
+    "the-dir-attribute/the-dir-attribute-059-expected.png",
+    "the-dir-attribute/the-dir-attribute-059.html",
+    "the-dir-attribute/the-dir-attribute-060-expected.png",
+    "the-dir-attribute/the-dir-attribute-060.html",
+    "the-dir-attribute/the-dir-attribute-061-expected.png",
+    "the-dir-attribute/the-dir-attribute-061.html",
+    "the-dir-attribute/the-dir-attribute-062-expected.png",
+    "the-dir-attribute/the-dir-attribute-062.html",
+    "the-dir-attribute/the-dir-attribute-063.html",
+    "the-dir-attribute/the-dir-attribute-064.html",
+    "the-dir-attribute/the-dir-attribute-065-expected.png",
+    "the-dir-attribute/the-dir-attribute-065.html",
+    "the-dir-attribute/the-dir-attribute-066-expected.png",
+    "the-dir-attribute/the-dir-attribute-066.html",
+    "the-dir-attribute/the-dir-attribute-067-expected.png",
+    "the-dir-attribute/the-dir-attribute-067.html",
+    "the-dir-attribute/the-dir-attribute-068-expected.png",
+    "the-dir-attribute/the-dir-attribute-068.html",
+    "the-dir-attribute/the-dir-attribute-069-expected.png",
+    "the-dir-attribute/the-dir-attribute-069.html",
+    "the-dir-attribute/the-dir-attribute-070-expected.png",
+    "the-dir-attribute/the-dir-attribute-070.html",
+    "the-dir-attribute/the-dir-attribute-071-expected.png",
+    "the-dir-attribute/the-dir-attribute-071.html",
+    "the-dir-attribute/writing-modes-principal-flow-body-propagation-expected.png",
+    "the-dir-attribute/writing-modes-principal-flow-body-propagation.html",
+    "the-dir-attribute/writing-modes-principal-flow-root-propagation-expected.png",
+    "the-dir-attribute/writing-modes-principal-flow-root-propagation.html",
+    "web-platform-tests/WebCryptoAPI/web_platform_tests.txt",
+    "web-platform-tests/WebIDL/web_platform_tests.txt",
+    "web-platform-tests/XMLHttpRequest/web_platform_tests.txt",
+    "web-platform-tests/cobalt_special/web_platform_tests.txt",
+    "web-platform-tests/content-security-policy/web_platform_tests.txt",
+    "web-platform-tests/cors/web_platform_tests.txt",
+    "web-platform-tests/dom/web_platform_tests.txt",
+    "web-platform-tests/encoding/web_platform_tests.txt",
+    "web-platform-tests/fetch/web_platform_tests.txt",
+    "web-platform-tests/html/web_platform_tests.txt",
+    "web-platform-tests/intersection-observer/web_platform_tests.txt",
+    "web-platform-tests/mediasession/web_platform_tests.txt",
+    "web-platform-tests/performance-timeline/web_platform_tests.txt",
+    "web-platform-tests/resource-timing/web_platform_tests.txt",
+    "web-platform-tests/streams/web_platform_tests.txt",
+    "web-platform-tests/websockets/web_platform_tests.txt",
+    "webappapis/6-1-5-2-onload-event-fires-on-window-expected.png",
+    "webappapis/6-1-5-2-onload-event-fires-on-window-when-set-via-attribute-expected.png",
+    "webappapis/6-1-5-2-onload-event-fires-on-window-when-set-via-attribute.html",
+    "webappapis/6-1-5-2-onload-event-fires-on-window.html",
+    "webappapis/layout_tests.txt",
+    "webp/layout_tests.txt",
+    "webp/static-webp-background-image-expected.png",
+    "webp/static-webp-background-image.html",
+    "webp/static-webp-image.webp",
+  ]
+
+  subdir = "cobalt/layout_tests"
+
+  outputs = [ "$sb_static_contents_output_data_dir/test/$subdir/{{source_target_relative}}" ]
+}
diff --git a/cobalt/loader/BUILD.gn b/cobalt/loader/BUILD.gn
index a94ee44..2892cc9 100644
--- a/cobalt/loader/BUILD.gn
+++ b/cobalt/loader/BUILD.gn
@@ -168,3 +168,96 @@
     rebase_path("embedded_resources", root_build_dir),
   ]
 }
+
+target(gtest_target_type, "loader_test") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [
+    "blob_fetcher_test.cc",
+    "fetcher_factory_test.cc",
+    "fetcher_test.h",
+    "file_fetcher_test.cc",
+    "font/typeface_decoder_test.cc",
+    "image/image_decoder_test.cc",
+    "image/image_decoder_unit_test.cc",
+    "loader_test.cc",
+    "mesh/mesh_decoder_test.cc",
+    "text_decoder_test.cc",
+  ]
+
+  configs -= [ "//starboard/build/config:size" ]
+  configs += [ "//starboard/build/config:speed" ]
+
+  deps = [
+    ":copy_loader_test_data",
+    ":loader",
+    "//cobalt/base:base",
+    "//cobalt/dom",
+    "//cobalt/dom_parser",
+    "//cobalt/math:math",
+    "//cobalt/render_tree",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/ots:ots",
+  ]
+
+  deps += cobalt_platform_dependencies
+}
+
+copy("copy_loader_test_data") {
+  sources = [
+    "testdata/baseline_jpeg.jpg",
+    "testdata/empty.txt",
+    "testdata/icons.ttf",
+    "testdata/icons.woff",
+    "testdata/icons.woff2",
+    "testdata/interlaced_png.png",
+    "testdata/non_interlaced_png.png",
+    "testdata/performance-spike.html",
+    "testdata/progressive_jpeg.jpg",
+    "testdata/projection.box",
+    "testdata/sandbox01.jpg",
+    "testdata/sandbox01.png",
+    "testdata/sandbox01.webp",
+    "testdata/sandbox02.jpg",
+    "testdata/sandbox02.png",
+    "testdata/sandbox02.webp",
+    "testdata/sandbox03.jpg",
+    "testdata/sandbox03.png",
+    "testdata/sandbox03.webp",
+    "testdata/sandbox04.jpg",
+    "testdata/sandbox04.webp",
+    "testdata/sandbox05.jpg",
+    "testdata/sandbox05.webp",
+    "testdata/sandbox06.jpg",
+    "testdata/sandbox06.webp",
+    "testdata/sandbox07.jpg",
+    "testdata/sandbox07.webp",
+    "testdata/sandbox08.jpg",
+    "testdata/sandbox08.webp",
+    "testdata/sandbox09.jpg",
+    "testdata/sandbox09.webp",
+    "testdata/sandbox10.jpg",
+    "testdata/sandbox10.webp",
+    "testdata/sandbox11.jpg",
+    "testdata/sandbox11.webp",
+    "testdata/sandbox12.jpg",
+    "testdata/sandbox12.webp",
+    "testdata/sandbox13.jpg",
+    "testdata/sandbox14.jpg",
+    "testdata/sandbox14.webp",
+    "testdata/sandbox15.jpg",
+    "testdata/sandbox15.webp",
+    "testdata/sandbox16.jpg",
+    "testdata/sandbox16.webp",
+    "testdata/sandbox17.jpg",
+    "testdata/sandbox18.jpg",
+    "testdata/vsauce_sm.webp",
+    "testdata/webp_image.webp",
+  ]
+
+  file_path = "{{source_root_relative_dir}}/{{source_file_part}}"
+  outputs = [ "$sb_static_contents_output_data_dir/test/$file_path" ]
+}
diff --git a/cobalt/loader/image/sandbox/BUILD.gn b/cobalt/loader/image/sandbox/BUILD.gn
new file mode 100644
index 0000000..adf6ab1
--- /dev/null
+++ b/cobalt/loader/image/sandbox/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a sample sandbox application for experimenting with the Cobalt
+# ImageDecoder.
+
+# This target will build a sandbox application that allows for easy
+# experimentation with the ImageDecoder on any platform.
+target(final_executable_type, "image_decoder_sandbox") {
+  sources = [ "image_decoder_sandbox.cc" ]
+
+  deps = [
+    "//cobalt/base",
+    "//cobalt/loader",
+    "//cobalt/loader:copy_loader_test_data",
+    "//cobalt/math",
+    "//cobalt/renderer",
+    "//cobalt/system_window",
+    "//cobalt/trace_event",
+    "//url",
+  ]
+}
diff --git a/cobalt/math/BUILD.gn b/cobalt/math/BUILD.gn
index 59c4494..48c2943 100644
--- a/cobalt/math/BUILD.gn
+++ b/cobalt/math/BUILD.gn
@@ -100,4 +100,5 @@
     "//cobalt/test:run_all_unittests",
     "//testing/gtest",
   ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/cobalt/media/BUILD.gn b/cobalt/media/BUILD.gn
index 2194a4d..5428451 100644
--- a/cobalt/media/BUILD.gn
+++ b/cobalt/media/BUILD.gn
@@ -24,10 +24,6 @@
   defines = [ "MEDIA_IMPLEMENTATION" ]
 }
 
-config("media_config_public") {
-  include_dirs = [ ".." ]
-}
-
 component("media") {
   sources = [
     "base/audio_bus.cc",
@@ -232,8 +228,6 @@
     ":media_config",
   ]
 
-  public_configs = [ ":media_config_public" ]
-
   deps = [
     "//base",
     "//cobalt/base",
diff --git a/cobalt/media/OWNERS b/cobalt/media/OWNERS
new file mode 100644
index 0000000..8a329a4
--- /dev/null
+++ b/cobalt/media/OWNERS
@@ -0,0 +1,24 @@
+# NOTE: Do not use these owners when you're in a subdirectory that has
+# OWNERS file. For example:
+# - cast
+# - midi
+# - ozone
+# - capture/{content,video}
+# Instead prefer the OWNERS in the subdirectory as they will be more familiar,
+# and to load balance. Only use OWNERS in this file for these subdirectories
+# when doing refactorings and general cleanups.
+
+chcunningham@chromium.org
+dalecurtis@chromium.org
+ddorwin@chromium.org
+hubbe@chromium.org
+jrummell@chromium.org
+liberato@chromium.org
+sandersd@chromium.org
+watk@chromium.org
+wolenetz@chromium.org
+xhwang@chromium.org
+
+per-file *.isolate=maruel@chromium.org
+per-file *.isolate=tandrii@chromium.org
+per-file *.isolate=vadimsh@chromium.org
diff --git a/cobalt/media/base/encryption_pattern.cc b/cobalt/media/base/encryption_pattern.cc
index 20608ca..4349bc0 100644
--- a/cobalt/media/base/encryption_pattern.cc
+++ b/cobalt/media/base/encryption_pattern.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/base/encryption_pattern.h"
+#include "cobalt/media/base/encryption_pattern.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/base/playback_statistics.cc b/cobalt/media/base/playback_statistics.cc
index ca6263d..ff49d71 100644
--- a/cobalt/media/base/playback_statistics.cc
+++ b/cobalt/media/base/playback_statistics.cc
@@ -37,7 +37,7 @@
 volatile SbAtomic32 s_max_video_height = 0;
 volatile SbAtomic32 s_last_working_codec = kUnknownVideoCodec;
 
-int64_t RoundValues(int64_t value) {
+int64_t RoundValue(int64_t value) {
   if (value < 10) {
     return value;
   }
@@ -225,10 +225,10 @@
       (current_video_config.is_encrypted() ? "Y" : "N"), video_width_.value(),
       video_height_.value(), SbAtomicNoBarrier_Load(&s_active_instances),
       SbAtomicNoBarrier_Load(&s_max_active_instances),
-      RoundValues(SbAtomicNoBarrier_Load(&s_av1_played)),
-      RoundValues(SbAtomicNoBarrier_Load(&s_h264_played)),
-      RoundValues(SbAtomicNoBarrier_Load(&s_hevc_played)),
-      RoundValues(SbAtomicNoBarrier_Load(&s_vp9_played)),
+      RoundValue(SbAtomicNoBarrier_Load(&s_av1_played)),
+      RoundValue(SbAtomicNoBarrier_Load(&s_h264_played)),
+      RoundValue(SbAtomicNoBarrier_Load(&s_hevc_played)),
+      RoundValue(SbAtomicNoBarrier_Load(&s_vp9_played)),
       SbAtomicNoBarrier_Load(&s_min_video_width),
       SbAtomicNoBarrier_Load(&s_min_video_height),
       SbAtomicNoBarrier_Load(&s_max_video_width),
@@ -238,16 +238,16 @@
           .c_str(),
       ValToString(seek_time_).c_str(),
       is_first_audio_buffer_written_
-          ? RoundValues(first_written_audio_timestamp_.value().InSeconds())
+          ? RoundValue(first_written_audio_timestamp_.value().InSeconds())
           : -1,
       is_first_video_buffer_written_
-          ? RoundValues(first_written_video_timestamp_.value().InSeconds())
+          ? RoundValue(first_written_video_timestamp_.value().InSeconds())
           : -1,
       is_first_audio_buffer_written_
-          ? RoundValues(last_written_audio_timestamp_.value().InSeconds())
+          ? RoundValue(last_written_audio_timestamp_.value().InSeconds())
           : -1,
       is_first_video_buffer_written_
-          ? RoundValues(last_written_video_timestamp_.value().InSeconds())
+          ? RoundValue(last_written_video_timestamp_.value().InSeconds())
           : -1);
 }
 
diff --git a/cobalt/media/base/simd/convert_rgb_to_yuv.h b/cobalt/media/base/simd/convert_rgb_to_yuv.h
index a180549..7eaa2b1 100644
--- a/cobalt/media/base/simd/convert_rgb_to_yuv.h
+++ b/cobalt/media/base/simd/convert_rgb_to_yuv.h
@@ -5,7 +5,7 @@
 #ifndef COBALT_MEDIA_BASE_SIMD_CONVERT_RGB_TO_YUV_H_
 #define COBALT_MEDIA_BASE_SIMD_CONVERT_RGB_TO_YUV_H_
 
-#include "media/base/yuv_convert.h"
+#include "cobalt/media/base/yuv_convert.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/base/simd/convert_rgb_to_yuv_c.cc b/cobalt/media/base/simd/convert_rgb_to_yuv_c.cc
index 70f28e1..752e57b 100644
--- a/cobalt/media/base/simd/convert_rgb_to_yuv_c.cc
+++ b/cobalt/media/base/simd/convert_rgb_to_yuv_c.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "build/build_config.h"
-#include "media/base/simd/convert_rgb_to_yuv.h"
+#include "cobalt/media/base/simd/convert_rgb_to_yuv.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/base/simd/convert_rgb_to_yuv_sse2.cc b/cobalt/media/base/simd/convert_rgb_to_yuv_sse2.cc
index 32fb4b7..43f0365 100644
--- a/cobalt/media/base/simd/convert_rgb_to_yuv_sse2.cc
+++ b/cobalt/media/base/simd/convert_rgb_to_yuv_sse2.cc
@@ -3,13 +3,15 @@
 // found in the LICENSE file.
 
 #include "build/build_config.h"
-#include "media/base/simd/convert_rgb_to_yuv.h"
+#include "cobalt/media/base/simd/convert_rgb_to_yuv.h"
 
 #if defined(COMPILER_MSVC)
 #include <intrin.h>
 #else
+// clang-format off
 #include <mmintrin.h>
 #include <emmintrin.h>
+// clang-format on
 
 #include "starboard/types.h"
 #endif
diff --git a/cobalt/media/base/simd/convert_rgb_to_yuv_ssse3.cc b/cobalt/media/base/simd/convert_rgb_to_yuv_ssse3.cc
index dbb27c6..0e025a5 100644
--- a/cobalt/media/base/simd/convert_rgb_to_yuv_ssse3.cc
+++ b/cobalt/media/base/simd/convert_rgb_to_yuv_ssse3.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/base/simd/convert_rgb_to_yuv.h"
+#include "cobalt/media/base/simd/convert_rgb_to_yuv.h"
 
 #include "build/build_config.h"
-#include "media/base/simd/convert_rgb_to_yuv_ssse3.h"
+#include "cobalt/media/base/simd/convert_rgb_to_yuv_ssse3.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/base/simd/convert_rgb_to_yuv_unittest.cc b/cobalt/media/base/simd/convert_rgb_to_yuv_unittest.cc
index 23063ec..b218f00 100644
--- a/cobalt/media/base/simd/convert_rgb_to_yuv_unittest.cc
+++ b/cobalt/media/base/simd/convert_rgb_to_yuv_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/base/simd/convert_rgb_to_yuv.h"
+#include "cobalt/media/base/simd/convert_rgb_to_yuv.h"
 
 #include <algorithm>
 #include <memory>
diff --git a/cobalt/media/base/simd/convert_yuv_to_rgb.h b/cobalt/media/base/simd/convert_yuv_to_rgb.h
index dbb47bc..08fcd58 100644
--- a/cobalt/media/base/simd/convert_yuv_to_rgb.h
+++ b/cobalt/media/base/simd/convert_yuv_to_rgb.h
@@ -5,7 +5,7 @@
 #ifndef COBALT_MEDIA_BASE_SIMD_CONVERT_YUV_TO_RGB_H_
 #define COBALT_MEDIA_BASE_SIMD_CONVERT_YUV_TO_RGB_H_
 
-#include "media/base/yuv_convert.h"
+#include "cobalt/media/base/yuv_convert.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/base/simd/convert_yuv_to_rgb_c.cc b/cobalt/media/base/simd/convert_yuv_to_rgb_c.cc
index 6025492..bac0ba3 100644
--- a/cobalt/media/base/simd/convert_yuv_to_rgb_c.cc
+++ b/cobalt/media/base/simd/convert_yuv_to_rgb_c.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "build/build_config.h"
-#include "media/base/simd/convert_yuv_to_rgb.h"
+#include "cobalt/media/base/simd/convert_yuv_to_rgb.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/base/simd/convert_yuv_to_rgb_x86.cc b/cobalt/media/base/simd/convert_yuv_to_rgb_x86.cc
index 72cfec3..ffa75b7 100644
--- a/cobalt/media/base/simd/convert_yuv_to_rgb_x86.cc
+++ b/cobalt/media/base/simd/convert_yuv_to_rgb_x86.cc
@@ -8,8 +8,8 @@
 #include <mmintrin.h>
 #endif
 
-#include "media/base/simd/convert_yuv_to_rgb.h"
-#include "media/base/yuv_convert.h"
+#include "cobalt/media/base/simd/convert_yuv_to_rgb.h"
+#include "cobalt/media/base/yuv_convert.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/base/simd/filter_yuv.h b/cobalt/media/base/simd/filter_yuv.h
index ec0a770..7dc3104 100644
--- a/cobalt/media/base/simd/filter_yuv.h
+++ b/cobalt/media/base/simd/filter_yuv.h
@@ -5,7 +5,7 @@
 #ifndef COBALT_MEDIA_BASE_SIMD_FILTER_YUV_H_
 #define COBALT_MEDIA_BASE_SIMD_FILTER_YUV_H_
 
-#include "media/base/media_export.h"
+#include "cobalt/media/base/media_export.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/base/simd/filter_yuv_c.cc b/cobalt/media/base/simd/filter_yuv_c.cc
index 7e50d4e..e83ec00 100644
--- a/cobalt/media/base/simd/filter_yuv_c.cc
+++ b/cobalt/media/base/simd/filter_yuv_c.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/base/simd/filter_yuv.h"
+#include "cobalt/media/base/simd/filter_yuv.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/base/simd/filter_yuv_sse2.cc b/cobalt/media/base/simd/filter_yuv_sse2.cc
index 9fc24c1..e11a9e0 100644
--- a/cobalt/media/base/simd/filter_yuv_sse2.cc
+++ b/cobalt/media/base/simd/filter_yuv_sse2.cc
@@ -9,7 +9,7 @@
 #include <mmintrin.h>
 #endif
 
-#include "media/base/simd/filter_yuv.h"
+#include "cobalt/media/base/simd/filter_yuv.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/decoder_buffer_allocator.cc b/cobalt/media/decoder_buffer_allocator.cc
index 9515da1..76ba23c 100644
--- a/cobalt/media/decoder_buffer_allocator.cc
+++ b/cobalt/media/decoder_buffer_allocator.cc
@@ -60,13 +60,8 @@
   // We cannot call SbMediaGetMaxBufferCapacity because |video_codec_| is not
   // set yet. Use 0 (unbounded) until |video_codec_| is updated in
   // UpdateVideoConfig().
-  int max_capacity = 0;
-  reuse_allocator_.reset(new nb::BidirectionalFitReuseAllocator(
-      &fallback_allocator_, initial_capacity_, kSmallAllocationThreshold,
-      allocation_unit_, max_capacity));
-  DLOG(INFO) << "Allocated " << initial_capacity_
-             << " bytes for media buffer pool as its initial buffer, with max"
-             << " capacity set to " << max_capacity;
+  starboard::ScopedLock scoped_lock(mutex_);
+  CreateReuseAllocator(0);
 }
 
 DecoderBufferAllocator::~DecoderBufferAllocator() {
@@ -84,6 +79,36 @@
   }
 }
 
+void DecoderBufferAllocator::Suspend() {
+  if (!using_memory_pool_ || is_memory_pool_allocated_on_demand_) {
+    return;
+  }
+
+  TRACK_MEMORY_SCOPE("Media");
+
+  starboard::ScopedLock scoped_lock(mutex_);
+
+  if (reuse_allocator_ && reuse_allocator_->GetAllocated() == 0) {
+    DLOG(INFO) << "Freed " << reuse_allocator_->GetCapacity()
+               << " bytes of media buffer pool `on suspend`.";
+    reuse_allocator_.reset();
+  }
+}
+
+void DecoderBufferAllocator::Resume() {
+  if (!using_memory_pool_ || is_memory_pool_allocated_on_demand_) {
+    return;
+  }
+
+  TRACK_MEMORY_SCOPE("Media");
+
+  starboard::ScopedLock scoped_lock(mutex_);
+
+  if (!reuse_allocator_) {
+    CreateReuseAllocator(0);
+  }
+}
+
 DecoderBuffer::Allocator::Allocations DecoderBufferAllocator::Allocate(
     size_t size, size_t alignment, intptr_t context) {
   TRACK_MEMORY_SCOPE("Media");
@@ -106,12 +131,7 @@
       max_capacity = SbMediaGetMaxBufferCapacity(
           video_codec_, resolution_width_, resolution_height_, bits_per_pixel_);
     }
-    reuse_allocator_.reset(new nb::BidirectionalFitReuseAllocator(
-        &fallback_allocator_, initial_capacity_, kSmallAllocationThreshold,
-        allocation_unit_, max_capacity));
-    DLOG(INFO) << "Allocated " << initial_capacity_
-               << " bytes for media buffer pool, with max capacity set to "
-               << max_capacity;
+    CreateReuseAllocator(max_capacity);
   }
 
   void* p = reuse_allocator_->Allocate(size, alignment);
@@ -217,6 +237,17 @@
                                            resolution_height_, bits_per_pixel_);
 }
 
+void DecoderBufferAllocator::CreateReuseAllocator(int max_capacity) {
+  mutex_.DCheckAcquired();
+
+  reuse_allocator_.reset(new nb::BidirectionalFitReuseAllocator(
+      &fallback_allocator_, initial_capacity_, kSmallAllocationThreshold,
+      allocation_unit_, max_capacity));
+  DLOG(INFO) << "Allocated " << initial_capacity_
+             << " bytes for media buffer pool, with max capacity set to "
+             << max_capacity;
+}
+
 bool DecoderBufferAllocator::UpdateAllocationRecord() const {
 #if !defined(COBALT_BUILD_TYPE_GOLD)
   // This code is not quite multi-thread safe but is safe enough for tracking
diff --git a/cobalt/media/decoder_buffer_allocator.h b/cobalt/media/decoder_buffer_allocator.h
index e78e6af..67e5352 100644
--- a/cobalt/media/decoder_buffer_allocator.h
+++ b/cobalt/media/decoder_buffer_allocator.h
@@ -37,6 +37,9 @@
   DecoderBufferAllocator();
   ~DecoderBufferAllocator() override;
 
+  void Suspend();
+  void Resume();
+
   Allocations Allocate(size_t size, size_t alignment,
                        intptr_t context) override;
   void Free(Allocations allocations) override;
@@ -46,6 +49,8 @@
   size_t GetMaximumMemoryCapacity() const override;
 
  private:
+  void CreateReuseAllocator(int max_capacity);
+
   // Update the Allocation record, and return false if allocation exceeds the
   // max buffer capacity, or true otherwise.
   bool UpdateAllocationRecord() const;
diff --git a/cobalt/media/formats/mp2t/es_adapter_video.cc b/cobalt/media/formats/mp2t/es_adapter_video.cc
index 7b3e64b..92a72f3 100644
--- a/cobalt/media/formats/mp2t/es_adapter_video.cc
+++ b/cobalt/media/formats/mp2t/es_adapter_video.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/es_adapter_video.h"
+#include "cobalt/media/formats/mp2t/es_adapter_video.h"
 
-#include "media/base/timestamp_constants.h"
-#include "media/base/video_decoder_config.h"
-#include "media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/base/timestamp_constants.h"
+#include "cobalt/media/base/video_decoder_config.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/formats/mp2t/es_adapter_video.h b/cobalt/media/formats/mp2t/es_adapter_video.h
index bdb23ee..e8e5ddd 100644
--- a/cobalt/media/formats/mp2t/es_adapter_video.h
+++ b/cobalt/media/formats/mp2t/es_adapter_video.h
@@ -13,8 +13,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
-#include "media/base/media_export.h"
-#include "media/base/stream_parser_buffer.h"
+#include "cobalt/media/base/media_export.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/formats/mp2t/es_adapter_video_unittest.cc b/cobalt/media/formats/mp2t/es_adapter_video_unittest.cc
index 81951e0..65e5882 100644
--- a/cobalt/media/formats/mp2t/es_adapter_video_unittest.cc
+++ b/cobalt/media/formats/mp2t/es_adapter_video_unittest.cc
@@ -11,11 +11,11 @@
 #include "base/macros.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
-#include "media/base/media_util.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/base/timestamp_constants.h"
-#include "media/base/video_decoder_config.h"
-#include "media/formats/mp2t/es_adapter_video.h"
+#include "cobalt/media/base/media_util.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/base/timestamp_constants.h"
+#include "cobalt/media/base/video_decoder_config.h"
+#include "cobalt/media/formats/mp2t/es_adapter_video.h"
 #include "starboard/types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/cobalt/media/formats/mp2t/es_parser.cc b/cobalt/media/formats/mp2t/es_parser.cc
index 502c148..d20c634 100644
--- a/cobalt/media/formats/mp2t/es_parser.cc
+++ b/cobalt/media/formats/mp2t/es_parser.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/es_parser.h"
+#include "cobalt/media/formats/mp2t/es_parser.h"
 
-#include "media/base/timestamp_constants.h"
-#include "media/formats/common/offset_byte_queue.h"
+#include "cobalt/media/base/timestamp_constants.h"
+#include "cobalt/media/formats/common/offset_byte_queue.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/es_parser.h b/cobalt/media/formats/mp2t/es_parser.h
index da35fa7..6b2a989 100644
--- a/cobalt/media/formats/mp2t/es_parser.h
+++ b/cobalt/media/formats/mp2t/es_parser.h
@@ -13,8 +13,8 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
-#include "media/base/media_export.h"
-#include "media/base/stream_parser_buffer.h"
+#include "cobalt/media/base/media_export.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/formats/mp2t/es_parser_adts.cc b/cobalt/media/formats/mp2t/es_parser_adts.cc
index db1d3d3..6773610 100644
--- a/cobalt/media/formats/mp2t/es_parser_adts.cc
+++ b/cobalt/media/formats/mp2t/es_parser_adts.cc
@@ -2,22 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/es_parser_adts.h"
+#include "cobalt/media/formats/mp2t/es_parser_adts.h"
 
 #include <algorithm>
 #include <vector>
 
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
-#include "media/base/audio_timestamp_helper.h"
-#include "media/base/bit_reader.h"
-#include "media/base/channel_layout.h"
-#include "media/base/media_util.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/base/timestamp_constants.h"
-#include "media/formats/common/offset_byte_queue.h"
-#include "media/formats/mp2t/mp2t_common.h"
-#include "media/formats/mpeg/adts_constants.h"
+#include "cobalt/media/base/audio_timestamp_helper.h"
+#include "cobalt/media/base/bit_reader.h"
+#include "cobalt/media/base/channel_layout.h"
+#include "cobalt/media/base/media_util.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/base/timestamp_constants.h"
+#include "cobalt/media/formats/common/offset_byte_queue.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/formats/mpeg/adts_constants.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/formats/mp2t/es_parser_adts.h b/cobalt/media/formats/mp2t/es_parser_adts.h
index f52e1dc..7630ff7 100644
--- a/cobalt/media/formats/mp2t/es_parser_adts.h
+++ b/cobalt/media/formats/mp2t/es_parser_adts.h
@@ -13,10 +13,10 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "media/base/audio_decoder_config.h"
-#include "media/base/media_export.h"
-#include "media/formats/mp2t/es_parser.h"
-#include "media/formats/mpeg/adts_stream_parser.h"
+#include "cobalt/media/base/audio_decoder_config.h"
+#include "cobalt/media/base/media_export.h"
+#include "cobalt/media/formats/mp2t/es_parser.h"
+#include "cobalt/media/formats/mpeg/adts_stream_parser.h"
 #include "starboard/types.h"
 
 namespace cobalt {
@@ -26,6 +26,7 @@
 class OffsetByteQueue;
 class StreamParserBuffer;
 }  // namespace media
+}  // namespace cobalt
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/es_parser_adts_unittest.cc b/cobalt/media/formats/mp2t/es_parser_adts_unittest.cc
index 4c86a32..92b63fc 100644
--- a/cobalt/media/formats/mp2t/es_parser_adts_unittest.cc
+++ b/cobalt/media/formats/mp2t/es_parser_adts_unittest.cc
@@ -8,9 +8,9 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/formats/mp2t/es_parser_adts.h"
-#include "media/formats/mp2t/es_parser_test_base.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/formats/mp2t/es_parser_adts.h"
+#include "cobalt/media/formats/mp2t/es_parser_test_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
diff --git a/cobalt/media/formats/mp2t/es_parser_h264.cc b/cobalt/media/formats/mp2t/es_parser_h264.cc
index ede1283..35e534a 100644
--- a/cobalt/media/formats/mp2t/es_parser_h264.cc
+++ b/cobalt/media/formats/mp2t/es_parser_h264.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/es_parser_h264.h"
+#include "cobalt/media/formats/mp2t/es_parser_h264.h"
 
 #include <limits>
 
@@ -11,14 +11,14 @@
 #include "base/optional.h"
 #include "cobalt/math/geometry/rect.h"
 #include "cobalt/math/geometry/size.h"
-#include "media/base/encryption_scheme.h"
-#include "media/base/media_util.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/base/timestamp_constants.h"
-#include "media/base/video_frame.h"
-#include "media/filters/h264_parser.h"
-#include "media/formats/common/offset_byte_queue.h"
-#include "media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/base/encryption_scheme.h"
+#include "cobalt/media/base/media_util.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/base/timestamp_constants.h"
+#include "cobalt/media/base/video_frame.h"
+#include "cobalt/media/filters/h264_parser.h"
+#include "cobalt/media/formats/common/offset_byte_queue.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/es_parser_h264.h b/cobalt/media/formats/mp2t/es_parser_h264.h
index fa4219a..84a2262 100644
--- a/cobalt/media/formats/mp2t/es_parser_h264.h
+++ b/cobalt/media/formats/mp2t/es_parser_h264.h
@@ -12,10 +12,10 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "media/base/media_export.h"
-#include "media/base/video_decoder_config.h"
-#include "media/formats/mp2t/es_adapter_video.h"
-#include "media/formats/mp2t/es_parser.h"
+#include "cobalt/media/base/media_export.h"
+#include "cobalt/media/base/video_decoder_config.h"
+#include "cobalt/media/formats/mp2t/es_adapter_video.h"
+#include "cobalt/media/formats/mp2t/es_parser.h"
 #include "starboard/types.h"
 
 namespace cobalt {
@@ -25,6 +25,7 @@
 struct H264SPS;
 class OffsetByteQueue;
 }  // namespace media
+}  // namespace cobalt
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/es_parser_h264_unittest.cc b/cobalt/media/formats/mp2t/es_parser_h264_unittest.cc
index c1149d0..5e237f3 100644
--- a/cobalt/media/formats/mp2t/es_parser_h264_unittest.cc
+++ b/cobalt/media/formats/mp2t/es_parser_h264_unittest.cc
@@ -12,10 +12,10 @@
 #include "base/macros.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/filters/h264_parser.h"
-#include "media/formats/mp2t/es_parser_h264.h"
-#include "media/formats/mp2t/es_parser_test_base.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/filters/h264_parser.h"
+#include "cobalt/media/formats/mp2t/es_parser_h264.h"
+#include "cobalt/media/formats/mp2t/es_parser_test_base.h"
 #include "starboard/memory.h"
 #include "starboard/types.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -122,7 +122,7 @@
     offset += sizeof(aud);
 
     memcpy(&stream_with_aud[offset], &stream_[access_units_[k].offset],
-                 access_units_[k].size);
+           access_units_[k].size);
     offset += access_units_[k].size;
   }
 
diff --git a/cobalt/media/formats/mp2t/es_parser_mpeg1audio.cc b/cobalt/media/formats/mp2t/es_parser_mpeg1audio.cc
index a97a0e2..5b47aa9 100644
--- a/cobalt/media/formats/mp2t/es_parser_mpeg1audio.cc
+++ b/cobalt/media/formats/mp2t/es_parser_mpeg1audio.cc
@@ -2,22 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/es_parser_mpeg1audio.h"
+#include "cobalt/media/formats/mp2t/es_parser_mpeg1audio.h"
 
 #include <vector>
 
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
-#include "media/base/audio_timestamp_helper.h"
-#include "media/base/bit_reader.h"
-#include "media/base/channel_layout.h"
-#include "media/base/media_util.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/base/timestamp_constants.h"
-#include "media/formats/common/offset_byte_queue.h"
-#include "media/formats/mp2t/mp2t_common.h"
-#include "media/formats/mpeg/mpeg1_audio_stream_parser.h"
+#include "cobalt/media/base/audio_timestamp_helper.h"
+#include "cobalt/media/base/bit_reader.h"
+#include "cobalt/media/base/channel_layout.h"
+#include "cobalt/media/base/media_util.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/base/timestamp_constants.h"
+#include "cobalt/media/formats/common/offset_byte_queue.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/formats/mpeg/mpeg1_audio_stream_parser.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/es_parser_mpeg1audio.h b/cobalt/media/formats/mp2t/es_parser_mpeg1audio.h
index 4966b46..5ac0343 100644
--- a/cobalt/media/formats/mp2t/es_parser_mpeg1audio.h
+++ b/cobalt/media/formats/mp2t/es_parser_mpeg1audio.h
@@ -13,10 +13,10 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "media/base/audio_decoder_config.h"
-#include "media/base/media_export.h"
-#include "media/base/media_log.h"
-#include "media/formats/mp2t/es_parser.h"
+#include "cobalt/media/base/audio_decoder_config.h"
+#include "cobalt/media/base/media_export.h"
+#include "cobalt/media/base/media_log.h"
+#include "cobalt/media/formats/mp2t/es_parser.h"
 #include "starboard/types.h"
 
 namespace cobalt {
@@ -26,6 +26,7 @@
 class OffsetByteQueue;
 class StreamParserBuffer;
 }  // namespace media
+}  // namespace cobalt
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/es_parser_mpeg1audio_unittest.cc b/cobalt/media/formats/mp2t/es_parser_mpeg1audio_unittest.cc
index 13aa58a..a92c318 100644
--- a/cobalt/media/formats/mp2t/es_parser_mpeg1audio_unittest.cc
+++ b/cobalt/media/formats/mp2t/es_parser_mpeg1audio_unittest.cc
@@ -8,10 +8,10 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "media/base/media_log.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/formats/mp2t/es_parser_mpeg1audio.h"
-#include "media/formats/mp2t/es_parser_test_base.h"
+#include "cobalt/media/base/media_log.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/formats/mp2t/es_parser_mpeg1audio.h"
+#include "cobalt/media/formats/mp2t/es_parser_test_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
diff --git a/cobalt/media/formats/mp2t/es_parser_test_base.cc b/cobalt/media/formats/mp2t/es_parser_test_base.cc
index 7315320..fe73bac 100644
--- a/cobalt/media/formats/mp2t/es_parser_test_base.cc
+++ b/cobalt/media/formats/mp2t/es_parser_test_base.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/es_parser_test_base.h"
+#include "cobalt/media/formats/mp2t/es_parser_test_base.h"
 
 #include "base/files/memory_mapped_file.h"
 #include "base/logging.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/base/test_data_util.h"
-#include "media/base/timestamp_constants.h"
-#include "media/formats/mp2t/es_parser.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/base/test_data_util.h"
+#include "cobalt/media/base/timestamp_constants.h"
+#include "cobalt/media/formats/mp2t/es_parser.h"
 #include "starboard/memory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/cobalt/media/formats/mp2t/mp2t_stream_parser.cc b/cobalt/media/formats/mp2t/mp2t_stream_parser.cc
index 09513a4..f9f0e4f 100644
--- a/cobalt/media/formats/mp2t/mp2t_stream_parser.cc
+++ b/cobalt/media/formats/mp2t/mp2t_stream_parser.cc
@@ -2,27 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/mp2t_stream_parser.h"
+#include "cobalt/media/formats/mp2t/mp2t_stream_parser.h"
 
 #include <memory>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "media/base/media_tracks.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/base/text_track_config.h"
-#include "media/base/timestamp_constants.h"
-#include "media/formats/mp2t/es_parser.h"
-#include "media/formats/mp2t/es_parser_adts.h"
-#include "media/formats/mp2t/es_parser_h264.h"
-#include "media/formats/mp2t/es_parser_mpeg1audio.h"
-#include "media/formats/mp2t/mp2t_common.h"
-#include "media/formats/mp2t/ts_packet.h"
-#include "media/formats/mp2t/ts_section.h"
-#include "media/formats/mp2t/ts_section_pat.h"
-#include "media/formats/mp2t/ts_section_pes.h"
-#include "media/formats/mp2t/ts_section_pmt.h"
+#include "cobalt/media/base/media_tracks.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/base/text_track_config.h"
+#include "cobalt/media/base/timestamp_constants.h"
+#include "cobalt/media/formats/mp2t/es_parser.h"
+#include "cobalt/media/formats/mp2t/es_parser_adts.h"
+#include "cobalt/media/formats/mp2t/es_parser_h264.h"
+#include "cobalt/media/formats/mp2t/es_parser_mpeg1audio.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/formats/mp2t/ts_packet.h"
+#include "cobalt/media/formats/mp2t/ts_section.h"
+#include "cobalt/media/formats/mp2t/ts_section_pat.h"
+#include "cobalt/media/formats/mp2t/ts_section_pes.h"
+#include "cobalt/media/formats/mp2t/ts_section_pmt.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/mp2t_stream_parser.h b/cobalt/media/formats/mp2t/mp2t_stream_parser.h
index 6554bee..e37a8a3 100644
--- a/cobalt/media/formats/mp2t/mp2t_stream_parser.h
+++ b/cobalt/media/formats/mp2t/mp2t_stream_parser.h
@@ -11,12 +11,12 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "media/base/audio_decoder_config.h"
-#include "media/base/byte_queue.h"
-#include "media/base/media_export.h"
-#include "media/base/stream_parser.h"
-#include "media/base/video_decoder_config.h"
-#include "media/formats/mp2t/timestamp_unroller.h"
+#include "cobalt/media/base/audio_decoder_config.h"
+#include "cobalt/media/base/byte_queue.h"
+#include "cobalt/media/base/media_export.h"
+#include "cobalt/media/base/stream_parser.h"
+#include "cobalt/media/base/video_decoder_config.h"
+#include "cobalt/media/formats/mp2t/timestamp_unroller.h"
 #include "starboard/types.h"
 
 namespace cobalt {
@@ -64,7 +64,7 @@
   // Callback invoked to register a PES pid.
   // Possible values for |stream_type| are defined in:
   // ISO-13818.1 / ITU H.222 Table 2.34 "Stream type assignments".
-  // |pes_pid| is part of the Program Map Table refered by |pmt_pid|.
+  // |pes_pid| is part of the Program Map Table referred by |pmt_pid|.
   void RegisterPes(int pmt_pid, int pes_pid, int stream_type);
 
   // Since the StreamParser interface allows only one audio & video streams,
diff --git a/cobalt/media/formats/mp2t/mp2t_stream_parser_unittest.cc b/cobalt/media/formats/mp2t/mp2t_stream_parser_unittest.cc
index a273cc4..8bf38a6 100644
--- a/cobalt/media/formats/mp2t/mp2t_stream_parser_unittest.cc
+++ b/cobalt/media/formats/mp2t/mp2t_stream_parser_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/mp2t_stream_parser.h"
+#include "cobalt/media/formats/mp2t/mp2t_stream_parser.h"
 
 #include <algorithm>
 #include <memory>
@@ -14,15 +14,15 @@
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
-#include "media/base/audio_decoder_config.h"
-#include "media/base/decoder_buffer.h"
-#include "media/base/media_log.h"
-#include "media/base/media_track.h"
-#include "media/base/media_tracks.h"
-#include "media/base/stream_parser_buffer.h"
-#include "media/base/test_data_util.h"
-#include "media/base/text_track_config.h"
-#include "media/base/video_decoder_config.h"
+#include "cobalt/media/base/audio_decoder_config.h"
+#include "cobalt/media/base/decoder_buffer.h"
+#include "cobalt/media/base/media_log.h"
+#include "cobalt/media/base/media_track.h"
+#include "cobalt/media/base/media_tracks.h"
+#include "cobalt/media/base/stream_parser_buffer.h"
+#include "cobalt/media/base/test_data_util.h"
+#include "cobalt/media/base/text_track_config.h"
+#include "cobalt/media/base/video_decoder_config.h"
 #include "starboard/types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/cobalt/media/formats/mp2t/timestamp_unroller.cc b/cobalt/media/formats/mp2t/timestamp_unroller.cc
index b88c6a6..3ff20f8 100644
--- a/cobalt/media/formats/mp2t/timestamp_unroller.cc
+++ b/cobalt/media/formats/mp2t/timestamp_unroller.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/timestamp_unroller.h"
+#include "cobalt/media/formats/mp2t/timestamp_unroller.h"
 
 #include "base/logging.h"
 
diff --git a/cobalt/media/formats/mp2t/timestamp_unroller.h b/cobalt/media/formats/mp2t/timestamp_unroller.h
index 0ea65b5..28e171b 100644
--- a/cobalt/media/formats/mp2t/timestamp_unroller.h
+++ b/cobalt/media/formats/mp2t/timestamp_unroller.h
@@ -6,7 +6,7 @@
 #define COBALT_MEDIA_FORMATS_MP2T_TIMESTAMP_UNROLLER_H_
 
 #include "base/macros.h"
-#include "media/base/media_export.h"
+#include "cobalt/media/base/media_export.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/formats/mp2t/timestamp_unroller_unittest.cc b/cobalt/media/formats/mp2t/timestamp_unroller_unittest.cc
index 7da57bc..10e68d5 100644
--- a/cobalt/media/formats/mp2t/timestamp_unroller_unittest.cc
+++ b/cobalt/media/formats/mp2t/timestamp_unroller_unittest.cc
@@ -7,7 +7,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/test/perf_test_suite.h"
-#include "media/formats/mp2t/timestamp_unroller.h"
+#include "cobalt/media/formats/mp2t/timestamp_unroller.h"
 #include "starboard/types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/cobalt/media/formats/mp2t/ts_packet.cc b/cobalt/media/formats/mp2t/ts_packet.cc
index 098aad9..94bf3b3 100644
--- a/cobalt/media/formats/mp2t/ts_packet.cc
+++ b/cobalt/media/formats/mp2t/ts_packet.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/ts_packet.h"
+#include "cobalt/media/formats/mp2t/ts_packet.h"
 
 #include <memory>
 
-#include "media/base/bit_reader.h"
-#include "media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/base/bit_reader.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/ts_section_pat.cc b/cobalt/media/formats/mp2t/ts_section_pat.cc
index 76f4e62..1aa3449 100644
--- a/cobalt/media/formats/mp2t/ts_section_pat.cc
+++ b/cobalt/media/formats/mp2t/ts_section_pat.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/ts_section_pat.h"
+#include "cobalt/media/formats/mp2t/ts_section_pat.h"
 
 #include <vector>
 
 #include "base/logging.h"
-#include "media/base/bit_reader.h"
-#include "media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/base/bit_reader.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/ts_section_pat.h b/cobalt/media/formats/mp2t/ts_section_pat.h
index 0b102e2..64cca26 100644
--- a/cobalt/media/formats/mp2t/ts_section_pat.h
+++ b/cobalt/media/formats/mp2t/ts_section_pat.h
@@ -8,7 +8,7 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "media/formats/mp2t/ts_section_psi.h"
+#include "cobalt/media/formats/mp2t/ts_section_psi.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/ts_section_pes.cc b/cobalt/media/formats/mp2t/ts_section_pes.cc
index 6cb2312..a381a3b 100644
--- a/cobalt/media/formats/mp2t/ts_section_pes.cc
+++ b/cobalt/media/formats/mp2t/ts_section_pes.cc
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/ts_section_pes.h"
+#include "cobalt/media/formats/mp2t/ts_section_pes.h"
 
 #include <memory>
 
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
-#include "media/base/bit_reader.h"
-#include "media/base/timestamp_constants.h"
-#include "media/formats/mp2t/es_parser.h"
-#include "media/formats/mp2t/mp2t_common.h"
-#include "media/formats/mp2t/timestamp_unroller.h"
+#include "cobalt/media/base/bit_reader.h"
+#include "cobalt/media/base/timestamp_constants.h"
+#include "cobalt/media/formats/mp2t/es_parser.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/formats/mp2t/timestamp_unroller.h"
 
 static const int kPesStartCode = 0x000001;
 
diff --git a/cobalt/media/formats/mp2t/ts_section_pes.h b/cobalt/media/formats/mp2t/ts_section_pes.h
index 553d520..675d774 100644
--- a/cobalt/media/formats/mp2t/ts_section_pes.h
+++ b/cobalt/media/formats/mp2t/ts_section_pes.h
@@ -9,8 +9,8 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "media/base/byte_queue.h"
-#include "media/formats/mp2t/ts_section.h"
+#include "cobalt/media/base/byte_queue.h"
+#include "cobalt/media/formats/mp2t/ts_section.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/formats/mp2t/ts_section_pmt.cc b/cobalt/media/formats/mp2t/ts_section_pmt.cc
index 98fe82b..071b7a6 100644
--- a/cobalt/media/formats/mp2t/ts_section_pmt.cc
+++ b/cobalt/media/formats/mp2t/ts_section_pmt.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/ts_section_pmt.h"
+#include "cobalt/media/formats/mp2t/ts_section_pmt.h"
 
 #include <map>
 #include <utility>
 
 #include "base/logging.h"
-#include "media/base/bit_reader.h"
-#include "media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/base/bit_reader.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
 
 namespace cobalt {
 namespace media {
@@ -71,7 +71,7 @@
   RCHECK(program_info_length < 1024);
 
   // Read the program info descriptor.
-  // TODO(damienv): check wether any of the descriptors could be useful.
+  // TODO(damienv): check whether any of the descriptors could be useful.
   // Defined in section 2.6 of ISO-13818.
   RCHECK(bit_reader->SkipBits(8 * program_info_length));
 
@@ -97,7 +97,7 @@
     pid_map.insert(std::pair<int, int>(pid_es, stream_type));
 
     // Read the ES info descriptors.
-    // TODO(damienv): check wether any of the descriptors could be useful.
+    // TODO(damienv): check whether any of the descriptors could be useful.
     // Defined in section 2.6 of ISO-13818.
     RCHECK(bit_reader->SkipBits(8 * es_info_length));
   }
diff --git a/cobalt/media/formats/mp2t/ts_section_pmt.h b/cobalt/media/formats/mp2t/ts_section_pmt.h
index f6ed1d9..5362da5 100644
--- a/cobalt/media/formats/mp2t/ts_section_pmt.h
+++ b/cobalt/media/formats/mp2t/ts_section_pmt.h
@@ -8,7 +8,7 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "media/formats/mp2t/ts_section_psi.h"
+#include "cobalt/media/formats/mp2t/ts_section_psi.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/formats/mp2t/ts_section_psi.cc b/cobalt/media/formats/mp2t/ts_section_psi.cc
index fb0b3ba..0f809ae 100644
--- a/cobalt/media/formats/mp2t/ts_section_psi.cc
+++ b/cobalt/media/formats/mp2t/ts_section_psi.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/formats/mp2t/ts_section_psi.h"
+#include "cobalt/media/formats/mp2t/ts_section_psi.h"
 
 #include <algorithm>
 
 #include "base/logging.h"
-#include "media/base/bit_reader.h"
-#include "media/formats/mp2t/mp2t_common.h"
+#include "cobalt/media/base/bit_reader.h"
+#include "cobalt/media/formats/mp2t/mp2t_common.h"
 
 static bool IsCrcValid(const uint8_t* buf, int size) {
   uint32_t crc = 0xffffffffu;
diff --git a/cobalt/media/formats/mp2t/ts_section_psi.h b/cobalt/media/formats/mp2t/ts_section_psi.h
index 69ebb93..b840dd2 100644
--- a/cobalt/media/formats/mp2t/ts_section_psi.h
+++ b/cobalt/media/formats/mp2t/ts_section_psi.h
@@ -7,8 +7,8 @@
 
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "media/base/byte_queue.h"
-#include "media/formats/mp2t/ts_section.h"
+#include "cobalt/media/base/byte_queue.h"
+#include "cobalt/media/formats/mp2t/ts_section.h"
 #include "starboard/types.h"
 
 namespace cobalt {
diff --git a/cobalt/media/media_module.cc b/cobalt/media/media_module.cc
index 3f6671d..8d4e195 100644
--- a/cobalt/media/media_module.cc
+++ b/cobalt/media/media_module.cc
@@ -203,6 +203,8 @@
     }
   }
 
+  decoder_buffer_allocator_.Suspend();
+
   resource_provider_ = NULL;
 }
 
@@ -216,6 +218,8 @@
     window = system_window_->GetSbWindow();
   }
 
+  decoder_buffer_allocator_.Resume();
+
   for (Players::iterator iter = players_.begin(); iter != players_.end();
        ++iter) {
     DCHECK(!iter->second);
diff --git a/cobalt/media/progressive/demuxer_fuzzer.cc b/cobalt/media/progressive/demuxer_fuzzer.cc
index ebd2766..d7be774 100644
--- a/cobalt/media/progressive/demuxer_fuzzer.cc
+++ b/cobalt/media/progressive/demuxer_fuzzer.cc
@@ -20,12 +20,12 @@
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "cobalt/base/wrap_main.h"
+#include "cobalt/media/base/bind_to_loop.h"
+#include "cobalt/media/base/pipeline_status.h"
+#include "cobalt/media/progressive/progressive_demuxer.h"
 #include "cobalt/media/sandbox/fuzzer_app.h"
 #include "cobalt/media/sandbox/in_memory_data_source.h"
 #include "cobalt/media/sandbox/media_sandbox.h"
-#include "media/base/bind_to_loop.h"
-#include "media/base/pipeline_status.h"
-#include "media/progressive/progressive_demuxer.h"
 
 namespace cobalt {
 namespace media {
@@ -69,22 +69,16 @@
 
  private:
   // DataSourceHost methods (parent class of DemuxerHost)
-  void SetTotalBytes(int64 total_bytes) override {
-  }
+  void SetTotalBytes(int64 total_bytes) override {}
 
-  void AddBufferedByteRange(int64 start, int64 end) override {
-  }
+  void AddBufferedByteRange(int64 start, int64 end) override {}
 
   void AddBufferedTimeRange(base::TimeDelta start,
-                            base::TimeDelta end) override {
-  }
+                            base::TimeDelta end) override {}
 
   // DemuxerHost methods
-  void SetDuration(base::TimeDelta duration) override {
-  }
-  void OnDemuxerError(PipelineStatus error) override {
-    error_occurred_ = true;
-  }
+  void SetDuration(base::TimeDelta duration) override {}
+  void OnDemuxerError(PipelineStatus error) override { error_occurred_ = true; }
 
   void InitializeCB(PipelineStatus status) {
     DCHECK(!error_occurred_);
diff --git a/cobalt/media/progressive/mock_data_source_reader.h b/cobalt/media/progressive/mock_data_source_reader.h
index cf3ddab..1ec8ba7 100644
--- a/cobalt/media/progressive/mock_data_source_reader.h
+++ b/cobalt/media/progressive/mock_data_source_reader.h
@@ -17,7 +17,7 @@
 #ifndef COBALT_MEDIA_PROGRESSIVE_MOCK_DATA_SOURCE_READER_H_
 #define COBALT_MEDIA_PROGRESSIVE_MOCK_DATA_SOURCE_READER_H_
 
-#include "media/progressive/data_source_reader.h"
+#include "cobalt/media/progressive/data_source_reader.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace cobalt {
diff --git a/cobalt/media/sandbox/BUILD.gn b/cobalt/media/sandbox/BUILD.gn
new file mode 100644
index 0000000..8ff9b34
--- /dev/null
+++ b/cobalt/media/sandbox/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a sample sandbox application for experimenting with the Cobalt
+# media/renderer interface.
+
+target(final_executable_type, "media_sandbox") {
+  sources = [ "media2_sandbox.cc" ]
+
+  deps = [
+    "//cobalt/base",
+    "//cobalt/math",
+    "//cobalt/media",
+    "//starboard",
+  ]
+
+  content_deps = [ "//third_party/icu:icudata" ]
+}
+
+target(final_executable_type, "web_media_player_sandbox") {
+  sources = [
+    "format_guesstimator.cc",
+    "format_guesstimator.h",
+    "media_sandbox.cc",
+    "media_sandbox.h",
+    "web_media_player_helper.cc",
+    "web_media_player_helper.h",
+    "web_media_player_sandbox.cc",
+  ]
+
+  deps = [
+    "//cobalt/base",
+
+    # Use test data from demos to avoid keeping two copies of video files.
+    "//cobalt/demos/content:demos_testdata",
+    "//cobalt/loader",
+    "//cobalt/math",
+    "//cobalt/media",
+    "//cobalt/network",
+    "//cobalt/render_tree",
+    "//cobalt/render_tree:animations",
+    "//cobalt/renderer",
+    "//cobalt/storage",
+    "//cobalt/system_window",
+    "//cobalt/trace_event",
+    "//starboard",
+    "//url",
+  ]
+
+  if (!sb_is_evergreen) {
+    deps += cobalt_platform_dependencies
+  }
+}
diff --git a/cobalt/media/sandbox/demuxer_helper.cc b/cobalt/media/sandbox/demuxer_helper.cc
index c5b2991..6828de6 100644
--- a/cobalt/media/sandbox/demuxer_helper.cc
+++ b/cobalt/media/sandbox/demuxer_helper.cc
@@ -19,12 +19,12 @@
 #include "base/bind_helpers.h"
 #include "base/callback_helpers.h"
 #include "base/logging.h"
+#include "cobalt/media/base/audio_decoder_config.h"
+#include "cobalt/media/base/bind_to_loop.h"
+#include "cobalt/media/base/decoder_buffer.h"
+#include "cobalt/media/base/video_decoder_config.h"
 #include "cobalt/media/fetcher_buffered_data_source.h"
-#include "media/base/audio_decoder_config.h"
-#include "media/base/bind_to_loop.h"
-#include "media/base/decoder_buffer.h"
-#include "media/base/video_decoder_config.h"
-#include "media/progressive/progressive_demuxer.h"
+#include "cobalt/media/progressive/progressive_demuxer.h"
 
 namespace cobalt {
 namespace media {
@@ -212,19 +212,14 @@
 class DemuxerHelper::DemuxerHostStub : public ::media::DemuxerHost {
  private:
   // DataSourceHost methods
-  void SetTotalBytes(int64 total_bytes) override {
-  }
-  void AddBufferedByteRange(int64 start, int64 end) override {
-  }
+  void SetTotalBytes(int64 total_bytes) override {}
+  void AddBufferedByteRange(int64 start, int64 end) override {}
   void AddBufferedTimeRange(base::TimeDelta start,
-                            base::TimeDelta end) override {
-  }
+                            base::TimeDelta end) override {}
 
   // DemuxerHost methods
-  void SetDuration(base::TimeDelta duration) override {
-  }
-  void OnDemuxerError(::media::PipelineStatus error) override {
-  }
+  void SetDuration(base::TimeDelta duration) override {}
+  void OnDemuxerError(::media::PipelineStatus error) override {}
 };
 
 DemuxerHelper::DemuxerHelper(
diff --git a/cobalt/media/sandbox/demuxer_helper.h b/cobalt/media/sandbox/demuxer_helper.h
index 69e100b..83051d8 100644
--- a/cobalt/media/sandbox/demuxer_helper.h
+++ b/cobalt/media/sandbox/demuxer_helper.h
@@ -19,7 +19,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "cobalt/loader/fetcher_factory.h"
-#include "media/base/demuxer.h"
+#include "cobalt/media/base/demuxer.h"
 #include "url/gurl.h"
 
 namespace cobalt {
diff --git a/cobalt/media/sandbox/in_memory_data_source.h b/cobalt/media/sandbox/in_memory_data_source.h
index fe39f57..ba22db7 100644
--- a/cobalt/media/sandbox/in_memory_data_source.h
+++ b/cobalt/media/sandbox/in_memory_data_source.h
@@ -18,7 +18,7 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
-#include "media/base/data_source.h"
+#include "cobalt/media/base/data_source.h"
 
 namespace cobalt {
 namespace media {
diff --git a/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc b/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc
index 1030a81..5a45130 100644
--- a/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc
+++ b/cobalt/media/sandbox/raw_video_decoder_fuzzer.cc
@@ -21,11 +21,11 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "cobalt/base/wrap_main.h"
+#include "cobalt/media/base/bind_to_loop.h"
 #include "cobalt/media/sandbox/fuzzer_app.h"
 #include "cobalt/media/sandbox/media_sandbox.h"
 #include "cobalt/media/sandbox/media_source_demuxer.h"
 #include "cobalt/media/sandbox/zzuf_fuzzer.h"
-#include "media/base/bind_to_loop.h"
 #include "starboard/memory.h"
 
 namespace cobalt {
@@ -66,8 +66,8 @@
       current_au_buffer_ =
           ::media::ShellBufferFactory::Instance()->AllocateBufferNow(
               desc.size, desc.is_keyframe);
-      memcpy(current_au_buffer_->GetWritableData(),
-                   &au_data_[0] + desc.offset, desc.size);
+      memcpy(current_au_buffer_->GetWritableData(), &au_data_[0] + desc.offset,
+             desc.size);
       ++au_index_;
     } else if (!current_au_buffer_->IsEndOfStream()) {
       current_au_buffer_ =
diff --git a/cobalt/media_capture/BUILD.gn b/cobalt/media_capture/BUILD.gn
index f20b541..5334dbc 100644
--- a/cobalt/media_capture/BUILD.gn
+++ b/cobalt/media_capture/BUILD.gn
@@ -52,3 +52,26 @@
     "//starboard",
   ]
 }
+
+target(gtest_target_type, "media_capture_test") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [
+    "get_user_media_test.cc",
+    "media_recorder_test.cc",
+  ]
+
+  deps = [
+    ":media_capture",
+    "//cobalt/dom",
+    "//cobalt/dom:dom_exception",
+    "//cobalt/dom/testing:dom_testing",
+    "//cobalt/media_stream",
+    "//cobalt/media_stream:media_stream_test_headers",
+    "//cobalt/script",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/cobalt/media_integration_tests/_env.py b/cobalt/media_integration_tests/_env.py
index a9d83bf..a289cbb 100644
--- a/cobalt/media_integration_tests/_env.py
+++ b/cobalt/media_integration_tests/_env.py
@@ -15,12 +15,12 @@
 #
 """Ask the parent directory to load the project environment."""
 
-from imp import load_source
+from imp import load_source  # pylint: disable=deprecated-module
 from os import path
 import sys
 
 _ENV = path.abspath(path.join(path.dirname(__file__), path.pardir, '_env.py'))
 if not path.exists(_ENV):
-  print '%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV)
+  print('%s: Can\'t find repo root.\nMissing parent: %s' % (__file__, _ENV))
   sys.exit(1)
 load_source('', _ENV)
diff --git a/cobalt/media_session/BUILD.gn b/cobalt/media_session/BUILD.gn
index 56f00c9..2ef1cea 100644
--- a/cobalt/media_session/BUILD.gn
+++ b/cobalt/media_session/BUILD.gn
@@ -39,3 +39,19 @@
     "//starboard:starboard_headers_only",
   ]
 }
+
+target(gtest_target_type, "media_session_test") {
+  testonly = true
+
+  sources = [ "media_session_test.cc" ]
+
+  deps = [
+    ":media_session",
+    "//cobalt/base",
+    "//cobalt/browser",
+    "//cobalt/script",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/cobalt/media_stream/BUILD.gn b/cobalt/media_stream/BUILD.gn
index 934eec4..b10e1fc 100644
--- a/cobalt/media_stream/BUILD.gn
+++ b/cobalt/media_stream/BUILD.gn
@@ -50,3 +50,44 @@
     "//starboard",
   ]
 }
+
+target(gtest_target_type, "media_stream_test") {
+  testonly = true
+  has_pedantic_warnings = true
+
+  sources = [
+    "audio_parameters_test.cc",
+    "media_stream_audio_source_test.cc",
+    "media_stream_audio_track_test.cc",
+    "media_stream_test.cc",
+  ]
+
+  deps = [
+    ":media_stream",
+    ":media_stream_test_headers",
+    "//cobalt/dom/testing:dom_testing",
+    "//cobalt/media",
+    "//cobalt/script",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+
+  deps += cobalt_platform_dependencies
+}
+
+source_set("media_stream_test_headers") {
+  testonly = true
+
+  sources = [
+    "testing/mock_media_stream_audio_sink.h",
+    "testing/mock_media_stream_audio_source.h",
+    "testing/mock_media_stream_audio_track.h",
+  ]
+
+  deps = [
+    ":media_stream",
+    "//cobalt/script",
+    "//testing/gmock",
+  ]
+}
diff --git a/cobalt/network/BUILD.gn b/cobalt/network/BUILD.gn
index 44bccfc..4658ad4 100644
--- a/cobalt/network/BUILD.gn
+++ b/cobalt/network/BUILD.gn
@@ -81,6 +81,7 @@
 }
 
 copy("copy_ssl_certificates") {
+  install_content = true
   sources = [
     "//cobalt/content/ssl/certs/002c0b4f.0",
     "//cobalt/content/ssl/certs/02265526.0",
@@ -119,7 +120,6 @@
     "//cobalt/content/ssl/certs/406c9bb1.0",
     "//cobalt/content/ssl/certs/4304c5e5.0",
     "//cobalt/content/ssl/certs/48bec511.0",
-    "//cobalt/content/ssl/certs/4a6481c9.0",
     "//cobalt/content/ssl/certs/4b718d9b.0",
     "//cobalt/content/ssl/certs/4bfab552.0",
     "//cobalt/content/ssl/certs/4f316efb.0",
@@ -143,7 +143,6 @@
     "//cobalt/content/ssl/certs/706f604c.0",
     "//cobalt/content/ssl/certs/749e9e03.0",
     "//cobalt/content/ssl/certs/75d1b2ed.0",
-    "//cobalt/content/ssl/certs/76cb8f92.0",
     "//cobalt/content/ssl/certs/76faf6c0.0",
     "//cobalt/content/ssl/certs/7719f463.0",
     "//cobalt/content/ssl/certs/773e07ad.0",
@@ -213,12 +212,11 @@
     "//cobalt/content/ssl/certs/feffd413.0",
     "//cobalt/content/ssl/certs/ff34af3f.0",
   ]
-
   outputs =
       [ "$sb_static_contents_output_data_dir/ssl/certs/{{source_file_part}}" ]
 }
 
-target(gtest_target_type, "network_base_test") {
+target(gtest_target_type, "network_test") {
   testonly = true
   has_pedantic_warnings = true
 
diff --git a/cobalt/network/persistent_cookie_store_test.cc b/cobalt/network/persistent_cookie_store_test.cc
index 7f3325e..a05cde9 100644
--- a/cobalt/network/persistent_cookie_store_test.cc
+++ b/cobalt/network/persistent_cookie_store_test.cc
@@ -15,6 +15,7 @@
 #include "cobalt/network/persistent_cookie_store.h"
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/files/file_path.h"
diff --git a/cobalt/overlay_info/BUILD.gn b/cobalt/overlay_info/BUILD.gn
index d2209fe..b445d56 100644
--- a/cobalt/overlay_info/BUILD.gn
+++ b/cobalt/overlay_info/BUILD.gn
@@ -44,4 +44,5 @@
     "//cobalt/test:run_all_unittests",
     "//testing/gtest",
   ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/cobalt/render_tree/image.h b/cobalt/render_tree/image.h
index 357052f..668f4f0 100644
--- a/cobalt/render_tree/image.h
+++ b/cobalt/render_tree/image.h
@@ -137,6 +137,9 @@
   // A YUV image where each channel, Y, U and V, is stored as a separate
   // single-channel 10bit unnormalized image plane.
   kMultiPlaneImageFormatYUV3Plane10BitBT2020,
+  // A compacted YUV image where Y, U and V, are stored in 32-bit and each
+  // 32-bit represents 3 10-bit pixels with 2 bits of gap.
+  kMultiPlaneImageFormatYUV3Plane10BitCompactedBT2020,
 };
 
 // Like the ImageDataDescriptor object, a MultiPlaneImageDataDescriptor
diff --git a/cobalt/renderer/BUILD.gn b/cobalt/renderer/BUILD.gn
index c834841..8cf5f18 100644
--- a/cobalt/renderer/BUILD.gn
+++ b/cobalt/renderer/BUILD.gn
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//cobalt/renderer/rasterizer/skia/skia/skia_next.gni")
+
 source_set("renderer_headers_only") {
   sources = [
     "egl_and_gles.h",
@@ -84,6 +86,10 @@
   configs -= [ "//starboard/build/config:size" ]
   configs += [ "//starboard/build/config:speed" ]
 
+  if (use_skia_next) {
+    include_dirs = [ skia_include_dir ]
+  }
+
   deps = [
     ":renderer",
     "//cobalt/base",
@@ -97,6 +103,449 @@
   ]
 }
 
+target(gtest_target_type, "renderer_test") {
+  testonly = true
+
+  sources = [
+    "animations_test.cc",
+    "pipeline_test.cc",
+    "rasterizer/lottie_coverage_pixel_test.cc",
+    "rasterizer/pixel_test.cc",
+    "rasterizer/pixel_test_fixture.cc",
+    "rasterizer/pixel_test_fixture.h",
+    "rasterizer/stress_test.cc",
+    "resource_provider_test.cc",
+    "smoothed_value_test.cc",
+    "submission_queue_test.cc",
+  ]
+
+  configs -= [ "//starboard/build/config:size" ]
+  configs += [ "//starboard/build/config:speed" ]
+
+  deps = [
+    ":render_tree_pixel_tester",
+    ":renderer",
+    ":renderer_copy_lottie_test_data",
+    ":renderer_copy_test_data",
+    ":renderer_download_lottie_test_data",
+    ":renderer_headers_only",
+    "//base:i18n",
+    "//cobalt/base",
+    "//cobalt/loader",
+    "//cobalt/math",
+    "//cobalt/render_tree",
+    "//cobalt/render_tree:animations",
+    "//cobalt/renderer/backend:renderer_backend",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/icu:icuuc",
+  ]
+}
+
+copy("renderer_copy_test_data") {
+  sources = [
+    "rasterizer/testdata/2xSpeedLottieAnimationTest-expected.png",
+    "rasterizer/testdata/AlmostCircleViaRoundedCorners-expected.png",
+    "rasterizer/testdata/Area1Image-expected.png",
+    "rasterizer/testdata/Area1Opacity-expected.png",
+    "rasterizer/testdata/BeginningOfPlayingLottieAnimationTest-expected.png",
+    "rasterizer/testdata/BlueFillRectOnEntireSurface-expected.png",
+    "rasterizer/testdata/BlueFillRectOnTopLeftQuarterOfSurface-expected.png",
+    "rasterizer/testdata/BlueRoundedCornersRectangularSubPixelBorder-expected.png",
+    "rasterizer/testdata/BounceModeLottieAnimationTest-expected.png",
+    "rasterizer/testdata/BoxShadowBigCircleWithInset25pxSpread50pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowBigEllipseWithInset25pxSpread50pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowBlur100pxCentered-expected.png",
+    "rasterizer/testdata/BoxShadowBlur1PxCentered-expected.png",
+    "rasterizer/testdata/BoxShadowBlur2PxCentered-expected.png",
+    "rasterizer/testdata/BoxShadowBlur3PxCentered-expected.png",
+    "rasterizer/testdata/BoxShadowBlur4PxCentered-expected.png",
+    "rasterizer/testdata/BoxShadowBlur5PxCentered-expected.png",
+    "rasterizer/testdata/BoxShadowBlur6PxCentered-expected.png",
+    "rasterizer/testdata/BoxShadowBlur8PxCentered-expected.png",
+    "rasterizer/testdata/BoxShadowBlurBottomLeft-expected.png",
+    "rasterizer/testdata/BoxShadowBlurBottomRight-expected.png",
+    "rasterizer/testdata/BoxShadowBlurCentered-expected.png",
+    "rasterizer/testdata/BoxShadowBlurCenteredOffscreenBottomRight-expected.png",
+    "rasterizer/testdata/BoxShadowBlurCenteredOffscreenTopLeft-expected.png",
+    "rasterizer/testdata/BoxShadowBlurTopLeft-expected.png",
+    "rasterizer/testdata/BoxShadowBlurTopRight-expected.png",
+    "rasterizer/testdata/BoxShadowCircleBottomRight-expected.png",
+    "rasterizer/testdata/BoxShadowCircleSpread-expected.png",
+    "rasterizer/testdata/BoxShadowCircleWithInset25pxSpread1pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowCircleWithInset25pxSpread1pxBlurRoundedCornersAndNoOffset-expected.png",
+    "rasterizer/testdata/BoxShadowCircleWithInset25pxSpread50pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowCircleWithInset25pxSpread8pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowCircleWithInset25pxSpreadAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowCircleWithOutset25pxSpread1pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowCircleWithOutset25pxSpread50pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowCircleWithOutset25pxSpread8pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowCircleWithOutset25pxSpreadAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowEllipseWithInset25pxSpread1pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowEllipseWithInset25pxSpread50pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowEllipseWithInset25pxSpread8pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowEllipseWithInset25pxSpreadAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowEllipseWithOutset25pxSpread1pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowEllipseWithOutset25pxSpread50pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowEllipseWithOutset25pxSpread8pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowEllipseWithOutset25pxSpreadAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowInsetCircleBottomRight-expected.png",
+    "rasterizer/testdata/BoxShadowInsetCircleSpread-expected.png",
+    "rasterizer/testdata/BoxShadowInsetUnderTransparentCircleBottomRightBlueBackground-expected.png",
+    "rasterizer/testdata/BoxShadowUnderTransparentCircleBottomRightBlueBackground-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpread1pxBlurAndIsometricRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpread1pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpread50pxBlurAndIsometricRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpread50pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpread8pxBlurAndIsometricRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpread8pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpreadAndBlur-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpreadAndIsometricRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpreadAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset25pxSpreadAndSameRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset5pxSpread25pxBlurAndDifferentRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset5pxSpread25pxBlurAndSameRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset5pxSpread5pxBlurAndSameRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset5pxSpreadAndDifferentRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInset5pxSpreadAndSameRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInsetNeg10pxSpreadAnd10pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithInsetNeg10pxSpreadAnd2pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset25pxSpread1pxBlurAndIsometricRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset25pxSpread1pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset25pxSpread50pxBlurAndIsometricRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset25pxSpread50pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset25pxSpread8pxBlurAndIsometricRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset25pxSpread8pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset25pxSpreadAndIsometricRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset25pxSpreadAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset25pxSpreadAndSameRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset5pxSpread25pxBlurAndDifferentRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset5pxSpread25pxBlurAndSameRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset5pxSpread5pxBlurAndSameRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset5pxSpreadAndDifferentRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutset5pxSpreadAndSameRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutsetNeg10pxSpreadAnd10pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithOutsetNeg10pxSpreadAnd2pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/BoxShadowWithSpread-expected.png",
+    "rasterizer/testdata/ChildrenOfCompositionThatStartsOffscreenAppear-expected.png",
+    "rasterizer/testdata/CircleViaRoundedCorners-expected.png",
+    "rasterizer/testdata/CircularSubPixelBorder-expected.png",
+    "rasterizer/testdata/CircularThickBorder-expected.png",
+    "rasterizer/testdata/CircularViewportOverCascadeOfRects-expected.png",
+    "rasterizer/testdata/CircularViewportOverImage-expected.png",
+    "rasterizer/testdata/CircularViewportOverWrappingImage-expected.png",
+    "rasterizer/testdata/CircularViewportOverZoomedInImage-expected.png",
+    "rasterizer/testdata/ClearRectNodeTest-expected.png",
+    "rasterizer/testdata/ColoredDropShadowBlurredText-expected.png",
+    "rasterizer/testdata/ColoredDropShadowText-expected.png",
+    "rasterizer/testdata/CompositionOfCascadedRectsOfDifferentColors-expected.png",
+    "rasterizer/testdata/CompositionOfSingleSolidColorRectWithAnisoScale-expected.png",
+    "rasterizer/testdata/CompositionOfSingleSolidColorRectWithIsoScale-expected.png",
+    "rasterizer/testdata/CompositionOfSingleSolidColorRectWithNoTransform-expected.png",
+    "rasterizer/testdata/CompositionOfSingleSolidColorRectWithRotation-expected.png",
+    "rasterizer/testdata/CompositionOfSingleSolidColorRectWithTranslation-expected.png",
+    "rasterizer/testdata/CompositionOfSingleSolidColorRectWithTranslationRotationAndAnisoScale-expected.png",
+    "rasterizer/testdata/ConcurrentTrimPathsLottieAnimationTest-expected.png",
+    "rasterizer/testdata/DebugAnimatedWebPFrame-expected.png",
+    "rasterizer/testdata/DownwardPointingTriangle-expected.png",
+    "rasterizer/testdata/DrawNullImage-expected.png",
+    "rasterizer/testdata/DrawNullImageInRoundedFilter-expected.png",
+    "rasterizer/testdata/DrawOffscreenImage-expected.png",
+    "rasterizer/testdata/DrawOffscreenYUVImage-expected.png",
+    "rasterizer/testdata/DropShadowBlurred0Point1PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurred1Px8PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurred1PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurred20PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurred2PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurred3PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurred4PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurred5PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurred6PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurred8PxText-expected.png",
+    "rasterizer/testdata/DropShadowBlurredText-expected.png",
+    "rasterizer/testdata/DropShadowText-expected.png",
+    "rasterizer/testdata/EllipticalSubPixelBorder-expected.png",
+    "rasterizer/testdata/EllipticalThickBorder-expected.png",
+    "rasterizer/testdata/EllipticalViewportOverCascadeOfRects-expected.png",
+    "rasterizer/testdata/EllipticalViewportOverCascadeOfRectsWithOpacity-expected.png",
+    "rasterizer/testdata/EllipticalViewportOverCompositionOfImages-expected.png",
+    "rasterizer/testdata/EllipticalViewportOverImage-expected.png",
+    "rasterizer/testdata/EllipticalViewportOverWrappingImage-expected.png",
+    "rasterizer/testdata/EllipticalViewportOverZoomedInImage-expected.png",
+    "rasterizer/testdata/EmptyRectWith4DifferentRoundedCornersAndEdgeColorsAndEdgeWidthsBorder-expected.png",
+    "rasterizer/testdata/EmptyRectWith4DifferentRoundedCornersAndEdgeColorsBorder-expected.png",
+    "rasterizer/testdata/EmptyRectWith4DifferentRoundedCornersAndEdgeWidthsBorder-expected.png",
+    "rasterizer/testdata/EmptyRectWithRedBorderOnTopLeftOfSurface-expected.png",
+    "rasterizer/testdata/EmptyRectWithRoundedCornersAnd4DifferentEdgeColorsAndEdgeWidthsBorder-expected.png",
+    "rasterizer/testdata/EmptyRectWithRoundedCornersAnd4DifferentEdgeColorsBorder-expected.png",
+    "rasterizer/testdata/EmptyRectWithRoundedCornersAnd4DifferentEdgeWidthsBorder-expected.png",
+    "rasterizer/testdata/EndOfPlayingLottieAnimationTest-expected.png",
+    "rasterizer/testdata/FilterBlurred0Point1PxText-expected.png",
+    "rasterizer/testdata/FilterBlurred100PxText-expected.png",
+    "rasterizer/testdata/FilterBlurred1PxText-expected.png",
+    "rasterizer/testdata/FilterBlurred20PxText-expected.png",
+    "rasterizer/testdata/FilterBlurred2PxText-expected.png",
+    "rasterizer/testdata/FilterBlurred3PxText-expected.png",
+    "rasterizer/testdata/FilterBlurred4PxText-expected.png",
+    "rasterizer/testdata/FilterBlurred5PxText-expected.png",
+    "rasterizer/testdata/FilterBlurred6PxText-expected.png",
+    "rasterizer/testdata/FilterBlurred8PxText-expected.png",
+    "rasterizer/testdata/FractionallyPositionedViewportsRenderCircularImages-expected.png",
+    "rasterizer/testdata/FractionallyPositionedViewportsRenderOpacityCircle-expected.png",
+    "rasterizer/testdata/GreenFillRectOnEntireSurface-expected.png",
+    "rasterizer/testdata/GreenFillRectOnTopLeftQuarterOfSurface-expected.png",
+    "rasterizer/testdata/GreyBoxShadowBottomLeft-expected.png",
+    "rasterizer/testdata/GreyBoxShadowBottomRight-expected.png",
+    "rasterizer/testdata/GreyBoxShadowTopLeft-expected.png",
+    "rasterizer/testdata/GreyBoxShadowTopRight-expected.png",
+    "rasterizer/testdata/Height1Image-expected.png",
+    "rasterizer/testdata/Height1Opacity-expected.png",
+    "rasterizer/testdata/HorizontalEllipseGradient2Stops-expected.png",
+    "rasterizer/testdata/HorizontalEllipseGradient3Stops-expected.png",
+    "rasterizer/testdata/HorizontalEllipseGradient5Stops-expected.png",
+    "rasterizer/testdata/ImageEdgeNoWrap-expected.png",
+    "rasterizer/testdata/ImageEdgeNoWrapWithPixelCentersOffset-expected.png",
+    "rasterizer/testdata/ImageEdgeNoWrapWithPixelCentersOffsetAndRotatedTexture-expected.png",
+    "rasterizer/testdata/ImageNodeLocalTransformAndExternalTransform-expected.png",
+    "rasterizer/testdata/ImageNodeLocalTransformOfImageSmallerThanSurface-expected.png",
+    "rasterizer/testdata/ImageNodeLocalTransformRotationAndScale-expected.png",
+    "rasterizer/testdata/ImageNodeLocalTransformScaleAndTranslation-expected.png",
+    "rasterizer/testdata/ImageNodeLocalTransformTranslation-expected.png",
+    "rasterizer/testdata/ImageOfBlackTransparentGridOverlappingSolidRectPremultipliedAlpha-expected.png",
+    "rasterizer/testdata/ImageOfBlackTransparentGridOverlappingSolidRectUsingUnpremultipliedAlpha-expected.png",
+    "rasterizer/testdata/ImageOfWhiteTransparentGridOverlappingSolidRectPremultipliedAlpha-expected.png",
+    "rasterizer/testdata/ImageOfWhiteTransparentGridOverlappingSolidRectUnpremultipliedAlpha-expected.png",
+    "rasterizer/testdata/ImagesAreLinearlyInterpolated-expected.png",
+    "rasterizer/testdata/LargeEllipticalViewportOverImage-expected.png",
+    "rasterizer/testdata/LinearGradient2Stops315Degrees-expected.png",
+    "rasterizer/testdata/LinearGradient2Stops45Degrees-expected.png",
+    "rasterizer/testdata/LinearGradient2StopsLeftRight-expected.png",
+    "rasterizer/testdata/LinearGradient2StopsTopBottom-expected.png",
+    "rasterizer/testdata/LinearGradient3Stops210DegreesInset-expected.png",
+    "rasterizer/testdata/LinearGradient3Stops30DegreesInset-expected.png",
+    "rasterizer/testdata/LinearGradient3StopsLeftRightInset-expected.png",
+    "rasterizer/testdata/LinearGradient3StopsTopBottomInset-expected.png",
+    "rasterizer/testdata/LinearGradient5Stops150DegreesOutset-expected.png",
+    "rasterizer/testdata/LinearGradient5Stops330DegreesOutset-expected.png",
+    "rasterizer/testdata/LinearGradient5StopsLeftRightOutset-expected.png",
+    "rasterizer/testdata/LinearGradient5StopsTopBottomOutset-expected.png",
+    "rasterizer/testdata/LinearGradientWithTransparencyOnWhiteBackground-expected.png",
+    "rasterizer/testdata/LoopingLottieAnimationTest-expected.png",
+    "rasterizer/testdata/LottiePreserveAspectRatioTooNarrowAnimationTest-expected.png",
+    "rasterizer/testdata/LottiePreserveAspectRatioTooShortAnimationTest-expected.png",
+    "rasterizer/testdata/LottieScaledWideAnimationTest-expected.png",
+    "rasterizer/testdata/MapToMeshI420Test-expected.png",
+    "rasterizer/testdata/MapToMeshNV12Test-expected.png",
+    "rasterizer/testdata/MapToMeshRGBTest-expected.png",
+    "rasterizer/testdata/MapToMeshUYVYTest-expected.png",
+    "rasterizer/testdata/MiddleOfPlayingLottieAnimationTest-expected.png",
+    "rasterizer/testdata/MultipleColoredDropShadowBlurredText-expected.png",
+    "rasterizer/testdata/MultipleColoredDropShadowText-expected.png",
+    "rasterizer/testdata/NotLoopingLottieAnimationTest-expected.png",
+    "rasterizer/testdata/OpacityFilterOnCompositionOfThreeRectsTest-expected.png",
+    "rasterizer/testdata/OpacityFilterOnImageNodeTest-expected.png",
+    "rasterizer/testdata/OpacityFilterOnRectNodeTest-expected.png",
+    "rasterizer/testdata/OpacityFilterOnRotatedRectNodeTest-expected.png",
+    "rasterizer/testdata/OpacityFilterOnVeryLargeRectNodeTest-expected.png",
+    "rasterizer/testdata/OpacityFilterWithinOpacityFilter-expected.png",
+    "rasterizer/testdata/OpacityOnRectAndEllipseMaskedImage-expected.png",
+    "rasterizer/testdata/OvalViaRoundedCorners-expected.png",
+    "rasterizer/testdata/OverLoopLimitCountLottieAnimationTest-expected.png",
+    "rasterizer/testdata/PausedLottieAnimationTest-expected.png",
+    "rasterizer/testdata/PunchThroughVideoNodePunchesThroughSetBoundsCBReturnsFalse-expected.png",
+    "rasterizer/testdata/PunchThroughVideoNodePunchesThroughSetBoundsCBReturnsTrue-expected.png",
+    "rasterizer/testdata/RadialGradient2Stops-expected.png",
+    "rasterizer/testdata/RadialGradient3Stops-expected.png",
+    "rasterizer/testdata/RadialGradient5Stops-expected.png",
+    "rasterizer/testdata/RadialGradientWithTransparencyOnWhiteBackground-expected.png",
+    "rasterizer/testdata/RectDrawOrder-expected.png",
+    "rasterizer/testdata/RectNodeContainsBorderWithRotation-expected.png",
+    "rasterizer/testdata/RectNodeContainsBorderWithScale-expected.png",
+    "rasterizer/testdata/RectNodeContainsBorderWithTranslation-expected.png",
+    "rasterizer/testdata/RectNodeContainsBorderWithTranslationRotationAndScale-expected.png",
+    "rasterizer/testdata/RectNodeContainsSkinnyBorderWithTranslation-expected.png",
+    "rasterizer/testdata/RectWithRoundedCornersOnSolidColor-expected.png",
+    "rasterizer/testdata/RedFillRectOnEntireSurface-expected.png",
+    "rasterizer/testdata/RedFillRectOnTopLeftQuarterOfSurface-expected.png",
+    "rasterizer/testdata/RedRectWith2DifferentRadiusForEachCornerOnTopLeftOfSurface-expected.png",
+    "rasterizer/testdata/RedRectWith4DifferentBlueBordersOnTopLeftOfSurface-expected.png",
+    "rasterizer/testdata/RedRectWith4DifferentColorAndWidthBorders-expected.png",
+    "rasterizer/testdata/RedRectWith4DifferentColorBorders-expected.png",
+    "rasterizer/testdata/RedRectWithBlueBorderOnTopLeftOfSurface-expected.png",
+    "rasterizer/testdata/RedRectWithDifferentRoundedCornersOnTopLeftOfSurface-expected.png",
+    "rasterizer/testdata/RedTextIn500PtFont-expected.png",
+    "rasterizer/testdata/RedTextOnBlueIn40PtFont-expected.png",
+    "rasterizer/testdata/ReverseDirectionLottieAnimationTest-expected.png",
+    "rasterizer/testdata/RotatedOvalViaRoundedCorners-expected.png",
+    "rasterizer/testdata/RotatedRoundedCornersViewportOverImage-expected.png",
+    "rasterizer/testdata/RotatedTextInScaledRoundedCorners-expected.png",
+    "rasterizer/testdata/RotatedThenScaledRectWithDifferentRoundedCorners-expected.png",
+    "rasterizer/testdata/RoundedCornersDifferentCornersDifferentThicknessSolidBrush-expected.png",
+    "rasterizer/testdata/RoundedCornersDifferentViewportOverCascadedRects-expected.png",
+    "rasterizer/testdata/RoundedCornersDifferentViewportOverImage-expected.png",
+    "rasterizer/testdata/RoundedCornersEachDifferentThickBorder-expected.png",
+    "rasterizer/testdata/RoundedCornersEachDifferentThickBorderSolidBrush-expected.png",
+    "rasterizer/testdata/RoundedCornersRectangularSubPixelBorder-expected.png",
+    "rasterizer/testdata/RoundedCornersSubPixelBorder-expected.png",
+    "rasterizer/testdata/RoundedCornersThickBlueBorder-expected.png",
+    "rasterizer/testdata/RoundedCornersThickBorder-expected.png",
+    "rasterizer/testdata/RoundedCornersViewportOverCascadeOfImages-expected.png",
+    "rasterizer/testdata/RoundedCornersViewportOverCascadedRects-expected.png",
+    "rasterizer/testdata/RoundedCornersViewportOverImage-expected.png",
+    "rasterizer/testdata/RoundedCornersViewportOverTranslatedImage-expected.png",
+    "rasterizer/testdata/RoundedCornersViewportOverWrappingImage-expected.png",
+    "rasterizer/testdata/RoundedCornersViewportOverZoomedInImage-expected.png",
+    "rasterizer/testdata/ScaledBoxShadowEllipseWithOutset25pxSpread3pxBlurAndRoundedCorners-expected.png",
+    "rasterizer/testdata/ScaledBoxShadowEllipseWithOutset5pxSpreadAndRoundedCorners-expected.png",
+    "rasterizer/testdata/ScaledBoxShadowWithSpreadAndBlurCentered-expected.png",
+    "rasterizer/testdata/ScaledSingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners-expected.png",
+    "rasterizer/testdata/ScaledThenRotatedRectWithDifferentRoundedCorners-expected.png",
+    "rasterizer/testdata/ScaledThenRotatedRoundedCornersViewportOverImage-expected.png",
+    "rasterizer/testdata/ScalingUpAnOpacityFilterTextDoesNotPixellate-expected.png",
+    "rasterizer/testdata/SeekFrameLottieAnimationTest-expected.png",
+    "rasterizer/testdata/SeekPercentStringLottieAnimationTest-expected.png",
+    "rasterizer/testdata/ShearedText-expected.png",
+    "rasterizer/testdata/SimpleText40PtFontWithCharacterLowerThanBaseline-expected.png",
+    "rasterizer/testdata/SimpleTextIn20PtFont-expected.png",
+    "rasterizer/testdata/SimpleTextIn40PtFont-expected.png",
+    "rasterizer/testdata/SimpleTextIn500PtFont-expected.png",
+    "rasterizer/testdata/SimpleTextIn80PtFont-expected.png",
+    "rasterizer/testdata/SimpleTextInRed40PtChineseFont-expected.png",
+    "rasterizer/testdata/SimpleTextInRed40PtFont-expected.png",
+    "rasterizer/testdata/SimpleTextInRed40PtThaiFont-expected.png",
+    "rasterizer/testdata/SingleRGBAImageLargerThanRenderTarget-expected.png",
+    "rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaque-expected.png",
+    "rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners-expected.png",
+    "rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCornersOnSolidColor-expected.png",
+    "rasterizer/testdata/SingleRGBAImageWithEnlargedDestRect-expected.png",
+    "rasterizer/testdata/SingleRGBAImageWithReflection-expected.png",
+    "rasterizer/testdata/SingleRGBAImageWithSameSizeAsRenderTarget-expected.png",
+    "rasterizer/testdata/SingleRGBAImageWithShrunkenDestRect-expected.png",
+    "rasterizer/testdata/SquishedEllipticalThickBorder-expected.png",
+    "rasterizer/testdata/StoppedLottieAnimationTest-expected.png",
+    "rasterizer/testdata/StretchedRoundedCornersViewportOverCascadedRects-expected.png",
+    "rasterizer/testdata/TextNodesScaleDownSmoothly-expected.png",
+    "rasterizer/testdata/ThreePlaneYUVImageSupport-expected.png",
+    "rasterizer/testdata/ThreePlaneYUVImageWithDestSizeDifferentFromImage-expected.png",
+    "rasterizer/testdata/ThreePlaneYUVImageWithReflection-expected.png",
+    "rasterizer/testdata/ThreePlaneYUVImageWithTransform-expected.png",
+    "rasterizer/testdata/ToggleLoopingOffLottieAnimationTest-expected.png",
+    "rasterizer/testdata/ToggleLoopingOnLottieAnimationTest-expected.png",
+    "rasterizer/testdata/TogglePlayFromPausedLottieAnimationTest-expected.png",
+    "rasterizer/testdata/TogglePlayFromPlayingLottieAnimationTest-expected.png",
+    "rasterizer/testdata/TogglePlayFromStoppedLottieAnimationTest-expected.png",
+    "rasterizer/testdata/TooManyGlyphs-expected.png",
+    "rasterizer/testdata/TranslatedRightCircularViewportOverImage-expected.png",
+    "rasterizer/testdata/TransparencyLottieAnimationTest-expected.png",
+    "rasterizer/testdata/TransparentBlackTextOnRedIn40PtFont-expected.png",
+    "rasterizer/testdata/TransparentBoxShadowBlurOnGreenBackgroundCentered-expected.png",
+    "rasterizer/testdata/TransparentBoxShadowOnGreenBackgroundBottomRight-expected.png",
+    "rasterizer/testdata/TransparentRectOverlappingSolidRect-expected.png",
+    "rasterizer/testdata/TwoPlaneYUVImageSupport-expected.png",
+    "rasterizer/testdata/TwoPlaneYUVImageWithDestSizeDifferentFromImage-expected.png",
+    "rasterizer/testdata/TwoPlaneYUVImageWithTransform-expected.png",
+    "rasterizer/testdata/UnderLoopLimitCountLottieAnimationTest-expected.png",
+    "rasterizer/testdata/VerticalEllipseGradient2Stops-expected.png",
+    "rasterizer/testdata/VerticalEllipseGradient3Stops-expected.png",
+    "rasterizer/testdata/VerticalEllipseGradient5Stops-expected.png",
+    "rasterizer/testdata/VeryLargeOpacityFilterDoesNotOccupyVeryMuchMemory-expected.png",
+    "rasterizer/testdata/ViewportFilterAndOpacityFilterOnTextNodeTest-expected.png",
+    "rasterizer/testdata/ViewportFilterOnCompositionOfThreeRectsTest-expected.png",
+    "rasterizer/testdata/ViewportFilterOnRotatedRectNodeTest-expected.png",
+    "rasterizer/testdata/ViewportFilterOnTextNodeTest-expected.png",
+    "rasterizer/testdata/ViewportFilterWithTransformOnTextNodeTest-expected.png",
+    "rasterizer/testdata/ViewportFilterWithTranslateAndScaleOnRectNodeTest-expected.png",
+    "rasterizer/testdata/WhiteTextOnBlackIn40PtFont-expected.png",
+    "rasterizer/testdata/Width1Image-expected.png",
+    "rasterizer/testdata/Width1Opacity-expected.png",
+    "rasterizer/testdata/YUV2PlaneImagesAreLinearlyInterpolated-expected.png",
+    "rasterizer/testdata/YUV3PlaneImagesAreLinearlyInterpolated-expected.png",
+    "rasterizer/testdata/YUV422UYVYImageScaledAndTranslated-expected.png",
+    "rasterizer/testdata/YUV422UYVYImageScaledUpSupport-expected.png",
+    "rasterizer/testdata/YUV422UYVYImageSupport-expected.png",
+    "rasterizer/testdata/ZoomedInImagesDoNotWrapInterpolated-expected.png",
+    "rasterizer/testdata/hunter_gone_too_deep.json",
+    "rasterizer/testdata/loading-spinner-opaque.webp",
+    "rasterizer/testdata/white_material_wave_loading.json",
+    "rasterizer/testdata/ytk_ink_logo_rotate.json",
+  ]
+  file_path = "{{source_root_relative_dir}}/{{source_file_part}}"
+  outputs = [ "$sb_static_contents_output_data_dir/test/$file_path" ]
+}
+
+_lottie_resource_path = "rasterizer/testdata/lottie_coverage"
+
+copy("renderer_copy_lottie_test_data") {
+  # TODO(b/211909342): List the individual files that are to be copied.
+  sources = [ _lottie_resource_path ]
+  deps = [ ":renderer_download_lottie_test_data" ]
+  file_path = "{{source_root_relative_dir}}/{{source_file_part}}"
+  outputs = [ "$sb_static_contents_output_data_dir/test/$file_path" ]
+}
+
+action("renderer_download_lottie_test_data") {
+  script = "//tools/download_from_gcs.py"
+
+  inputs = [
+    "$_lottie_resource_path/finger_print-expected.png.sha1",
+    "$_lottie_resource_path/finger_print.json.sha1",
+    "$_lottie_resource_path/gesture_go_back-expected.png.sha1",
+    "$_lottie_resource_path/gesture_go_back.json.sha1",
+    "$_lottie_resource_path/gesture_go_home-expected.png.sha1",
+    "$_lottie_resource_path/gesture_go_home.json.sha1",
+    "$_lottie_resource_path/heart_preloader-expected.png.sha1",
+    "$_lottie_resource_path/heart_preloader.json.sha1",
+    "$_lottie_resource_path/ripple_loading_animation-expected.png.sha1",
+    "$_lottie_resource_path/ripple_loading_animation.json.sha1",
+    "$_lottie_resource_path/skottie-3d-2planes-expected.png.sha1",
+    "$_lottie_resource_path/skottie-3d-2planes.json.sha1",
+    "$_lottie_resource_path/skottie-effects-tranform-expected.png.sha1",
+    "$_lottie_resource_path/skottie-effects-tranform.json.sha1",
+    "$_lottie_resource_path/skottie-fill-effect-expected.png.sha1",
+    "$_lottie_resource_path/skottie-fill-effect.json.sha1",
+    "$_lottie_resource_path/skottie-gradient-opacity-expected.png.sha1",
+    "$_lottie_resource_path/skottie-gradient-opacity.json.sha1",
+    "$_lottie_resource_path/skottie-gradient-ramp-expected.png.sha1",
+    "$_lottie_resource_path/skottie-gradient-ramp.json.sha1",
+    "$_lottie_resource_path/skottie-linear-wipe-effect-expected.png.sha1",
+    "$_lottie_resource_path/skottie-linear-wipe-effect.json.sha1",
+    "$_lottie_resource_path/skottie-luma-matte-expected.png.sha1",
+    "$_lottie_resource_path/skottie-luma-matte.json.sha1",
+    "$_lottie_resource_path/skottie-mask-feather-expected.png.sha1",
+    "$_lottie_resource_path/skottie-mask-feather.json.sha1",
+    "$_lottie_resource_path/skottie-matte-blendmode-expected.png.sha1",
+    "$_lottie_resource_path/skottie-matte-blendmode.json.sha1",
+    "$_lottie_resource_path/skottie-motiontile-effect-phase-expected.png.sha1",
+    "$_lottie_resource_path/skottie-motiontile-effect-phase.json.sha1",
+    "$_lottie_resource_path/skottie-shift-channels-effect-expected.png.sha1",
+    "$_lottie_resource_path/skottie-shift-channels-effect.json.sha1",
+    "$_lottie_resource_path/skottie-tritone-effect-expected.png.sha1",
+    "$_lottie_resource_path/skottie-tritone-effect.json.sha1",
+    "$_lottie_resource_path/skottie-venetianblinds-effect-expected.png.sha1",
+    "$_lottie_resource_path/skottie-venetianblinds-effect.json.sha1",
+    "$_lottie_resource_path/white_material_wave_loading-expected.png.sha1",
+    "$_lottie_resource_path/white_material_wave_loading.json.sha1",
+  ]
+
+  # TODO(b/211909342): This script downloads files to the source tree. GN only
+  # allows outputs under the output folder so they can't be listed here.
+  # For now, use a placeholder file as GN requires an action to have outputs.
+  outputs = [ "$target_out_dir/lottie_download.stamp" ]
+  sha_dir = rebase_path(_lottie_resource_path, root_build_dir)
+  args = [
+    "--bucket",
+    "lottie-coverage-testdata",
+    "--sha1",
+    sha_dir,
+    "--output",
+    sha_dir,
+    "--stamp_file",
+    rebase_path(outputs[0], root_build_dir),
+  ]
+}
+
 static_library("default_options") {
   sources = [ "//cobalt/renderer/get_default_rasterizer_for_platform.cc" ]
 
diff --git a/cobalt/renderer/backend/BUILD.gn b/cobalt/renderer/backend/BUILD.gn
index b591a41..92f235b 100644
--- a/cobalt/renderer/backend/BUILD.gn
+++ b/cobalt/renderer/backend/BUILD.gn
@@ -30,14 +30,13 @@
 target(gtest_target_type, "graphics_system_test") {
   testonly = true
   sources = [ "graphics_system_test.cc" ]
-
   deps = [
     ":renderer_backend",
-    "//base/test:run_all_unittests",
-    "//base/test:test_support",
     "//cobalt/base",
     "//cobalt/system_window",
+    "//cobalt/test:run_all_unittests",
     "//testing/gmock",
     "//testing/gtest",
   ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/cobalt/renderer/backend/egl/graphics_context.cc b/cobalt/renderer/backend/egl/graphics_context.cc
index ebc2d9d..480ac6d 100644
--- a/cobalt/renderer/backend/egl/graphics_context.cc
+++ b/cobalt/renderer/backend/egl/graphics_context.cc
@@ -17,6 +17,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <utility>
 
 #include "cobalt/renderer/backend/egl/graphics_context.h"
 
@@ -96,6 +97,10 @@
   return base::polymorphic_downcast<GraphicsSystemEGL*>(system());
 }
 
+const GraphicsSystemEGL* GraphicsContextEGL::system_egl() const {
+  return base::polymorphic_downcast<GraphicsSystemEGL*>(system());
+}
+
 bool GraphicsContextEGL::ComputeReadPixelsNeedVerticalFlip() {
   // This computation is expensive, so it is cached the first time that it is
   // computed. Simply return the value if it is already cached.
@@ -204,10 +209,10 @@
   GL_CALL(glCompileShader(blit_fragment_shader_));
   GL_CALL(glAttachShader(blit_program_, blit_fragment_shader_));
 
-  GL_CALL(glBindAttribLocation(
-      blit_program_, kBlitPositionAttribute, "a_position"));
-  GL_CALL(glBindAttribLocation(
-      blit_program_, kBlitTexcoordAttribute, "a_tex_coord"));
+  GL_CALL(glBindAttribLocation(blit_program_, kBlitPositionAttribute,
+                               "a_position"));
+  GL_CALL(glBindAttribLocation(blit_program_, kBlitTexcoordAttribute,
+                               "a_tex_coord"));
 
   GL_CALL(glLinkProgram(blit_program_));
 
@@ -286,8 +291,7 @@
 }
 
 void GraphicsContextEGL::MakeCurrentWithSurface(RenderTargetEGL* surface) {
-  DCHECK_NE(EGL_NO_SURFACE, surface) <<
-      "Use ReleaseCurrentContext().";
+  DCHECK_NE(EGL_NO_SURFACE, surface) << "Use ReleaseCurrentContext().";
 
   // In some EGL implementations, like Angle, the first time we make current on
   // a thread can result in global allocations being made that are never freed.
@@ -353,8 +357,8 @@
 
 void GraphicsContextEGL::ReleaseCurrentContext() {
   GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
-  EGL_CALL(eglMakeCurrent(
-      display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
+  EGL_CALL(
+      eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
 
   current_surface_ = NULL;
   is_current_ = false;
@@ -380,8 +384,8 @@
 
 scoped_refptr<RenderTarget> GraphicsContextEGL::CreateOffscreenRenderTarget(
     const math::Size& dimensions) {
-  scoped_refptr<RenderTarget> render_target(new PBufferRenderTargetEGL(
-      display_, config_, dimensions));
+  scoped_refptr<RenderTarget> render_target(
+      new PBufferRenderTargetEGL(display_, config_, dimensions));
 
   if (render_target->CreationError()) {
     return scoped_refptr<RenderTarget>();
@@ -412,8 +416,7 @@
   int half_height = height / 2;
   for (int row = 0; row < half_height; ++row) {
     uint8_t* top_row = pixels + row * pitch_in_bytes;
-    uint8_t* bottom_row =
-        pixels + (height - 1 - row) * pitch_in_bytes;
+    uint8_t* bottom_row = pixels + (height - 1 - row) * pitch_in_bytes;
     for (int i = 0; i < pitch_in_bytes; ++i) {
       std::swap(top_row[i], bottom_row[i]);
     }
@@ -496,6 +499,10 @@
   GL_CALL(glFinish());
 }
 
+math::Size GraphicsContextEGL::GetWindowSize() const {
+  return system_egl()->GetWindowSize();
+}
+
 void GraphicsContextEGL::Blit(GLuint texture, int x, int y, int width,
                               int height) {
   // Render a texture to the specified output rectangle on the render target.
diff --git a/cobalt/renderer/backend/egl/graphics_context.h b/cobalt/renderer/backend/egl/graphics_context.h
index 19013dc..7c9e7c6 100644
--- a/cobalt/renderer/backend/egl/graphics_context.h
+++ b/cobalt/renderer/backend/egl/graphics_context.h
@@ -42,6 +42,7 @@
   ~GraphicsContextEGL() override;
 
   GraphicsSystemEGL* system_egl();
+  const GraphicsSystemEGL* system_egl() const;
 
   EGLContext GetContext() { return context_; }
 
@@ -66,6 +67,8 @@
 
   void Finish() override;
 
+  math::Size GetWindowSize() const;
+
   // Helper class to allow one to create a RAII object that will acquire the
   // current context upon construction and release it upon destruction.
   class ScopedMakeCurrent {
diff --git a/cobalt/renderer/backend/egl/graphics_system.cc b/cobalt/renderer/backend/egl/graphics_system.cc
index 8418244..d2642a5 100644
--- a/cobalt/renderer/backend/egl/graphics_system.cc
+++ b/cobalt/renderer/backend/egl/graphics_system.cc
@@ -16,6 +16,7 @@
 #if SB_API_VERSION >= 12 || SB_HAS(GLES2)
 
 #include <memory>
+#include <utility>
 
 #include "cobalt/renderer/backend/egl/graphics_system.h"
 
@@ -31,7 +32,7 @@
 #include "cobalt/renderer/backend/egl/texture.h"
 #include "cobalt/renderer/backend/egl/utils.h"
 #if defined(ENABLE_GLIMP_TRACING)
-#include "glimp/tracing/tracing.h"
+#include "glimp/tracing/tracing.h"  // nogncheck
 #endif
 
 #include "cobalt/renderer/egl_and_gles.h"
@@ -283,6 +284,10 @@
 #endif
 }
 
+math::Size GraphicsSystemEGL::GetWindowSize() const {
+  return system_window_ ? system_window_->GetWindowSize() : math::Size();
+}
+
 }  // namespace backend
 }  // namespace renderer
 }  // namespace cobalt
diff --git a/cobalt/renderer/backend/egl/graphics_system.h b/cobalt/renderer/backend/egl/graphics_system.h
index 3ed462d..4920900 100644
--- a/cobalt/renderer/backend/egl/graphics_system.h
+++ b/cobalt/renderer/backend/egl/graphics_system.h
@@ -15,6 +15,8 @@
 #ifndef COBALT_RENDERER_BACKEND_EGL_GRAPHICS_SYSTEM_H_
 #define COBALT_RENDERER_BACKEND_EGL_GRAPHICS_SYSTEM_H_
 
+#include <memory>
+
 #include "base/optional.h"
 #include "cobalt/renderer/backend/egl/resource_context.h"
 #include "cobalt/renderer/backend/egl/texture_data.h"
@@ -45,6 +47,8 @@
   std::unique_ptr<RawTextureMemoryEGL> AllocateRawTextureMemory(
       size_t size_in_bytes, size_t alignment);
 
+  math::Size GetWindowSize() const;
+
  private:
   EGLDisplay display_;
   EGLConfig config_;
diff --git a/cobalt/renderer/backend/graphics_context.cc b/cobalt/renderer/backend/graphics_context.cc
index 1d3d0f6..3455980 100644
--- a/cobalt/renderer/backend/graphics_context.cc
+++ b/cobalt/renderer/backend/graphics_context.cc
@@ -63,11 +63,10 @@
       graphics_context ? graphics_context->graphics_extension_ : nullptr;
 #if SB_API_VERSION >= 12
 #if defined(ENABLE_MAP_TO_MESH)
-#error \
-    "ENABLE_MAP_TO_MESH is deprecated after Starboard version 12, use \
-the Cobalt graphics extension function IsMapToMeshEnabled() instead."
+#error "ENABLE_MAP_TO_MESH is deprecated after Starboard version 12, use "
+  "the Cobalt graphics extension function IsMapToMeshEnabled() instead."
 #endif  // defined(ENABLE_MAP_TO_MESH)
-  if (graphics_ext && graphics_ext->version >= 3) {
+      if (graphics_ext && graphics_ext->version >= 3) {
     return graphics_ext->IsMapToMeshEnabled();
   }
 
diff --git a/cobalt/renderer/rasterizer/egl/BUILD.gn b/cobalt/renderer/rasterizer/egl/BUILD.gn
index 51363e6..ce17947 100644
--- a/cobalt/renderer/rasterizer/egl/BUILD.gn
+++ b/cobalt/renderer/rasterizer/egl/BUILD.gn
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//cobalt/renderer/rasterizer/skia/skia/skia_next.gni")
+
 static_library("software_rasterizer") {
   sources = [
     "software_rasterizer.cc",
@@ -23,6 +25,10 @@
   configs -= [ "//starboard/build/config:size" ]
   configs += [ "//starboard/build/config:speed" ]
 
+  if (use_skia_next) {
+    include_dirs = [ skia_include_dir ]
+  }
+
   deps = [
     "//base",
     "//cobalt/math",
@@ -87,6 +93,10 @@
   configs -= [ "//starboard/build/config:size" ]
   configs += [ "//starboard/build/config:speed" ]
 
+  if (use_skia_next) {
+    include_dirs = [ skia_include_dir ]
+  }
+
   deps = [
     ":software_rasterizer",
     "//base",
diff --git a/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc b/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
index 076aaf8..a1d3f9b 100644
--- a/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
+++ b/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.cc
@@ -100,6 +100,10 @@
     64 * 1.1678f, 64 * 2.1479f,  0.0f,          -1.1469f,
     0.0f,         0.0f,          0.0f,          1.0f};
 
+const float k10BitBT2020ColorMatrixCompacted[16] = {
+    1.1678f, 0.0f,    1.6835f, -0.9147f, 1.1678f, -0.1878f, -0.6522f, 0.347f,
+    1.1678f, 2.1479f, 0.0f,    -1.1469f, 0.0f,    0.0f,     0.0f,     1.0f};
+
 const float* GetColorMatrixForImageType(
     TexturedMeshRenderer::Image::Type type) {
   switch (type) {
@@ -115,6 +119,9 @@
     case TexturedMeshRenderer::Image::YUV_UYVY_422_BT709: {
       return kBT709ColorMatrix;
     } break;
+    case TexturedMeshRenderer::Image::YUV_3PLANE_10BIT_COMPACT_BT2020: {
+      return k10BitBT2020ColorMatrixCompacted;
+    } break;
     case TexturedMeshRenderer::Image::YUV_3PLANE_10BIT_BT2020: {
       return k10BitBT2020ColorMatrix;
     } break;
@@ -177,6 +184,25 @@
                          scale_translate_vector));
   }
 
+  if (image.type == Image::YUV_3PLANE_10BIT_COMPACT_BT2020) {
+    math::Size viewport_size = graphics_context_->GetWindowSize();
+    GL_CALL(glUniform2f(blit_program.viewport_size_uniform,
+                        viewport_size.width(), viewport_size.height()));
+
+    SB_DCHECK(image.num_textures() >= 1);
+    math::Size texture_size = image.textures[0].texture->GetSize();
+    GL_CALL(glUniform2f(
+        blit_program.viewport_to_texture_size_ratio_uniform,
+        viewport_size.width() / static_cast<float>(texture_size.width()),
+        viewport_size.height() / static_cast<float>(texture_size.height())));
+
+    // The pixel shader requires the actual frame size of the first plane
+    // for calculations.
+    size_t subtexture_width = (texture_size.width() + 2) / 3;
+    GL_CALL(glUniform2f(blit_program.subtexture_size_uniform, subtexture_width,
+                        texture_size.height()));
+  }
+
   GL_CALL(glDrawArrays(mode, 0, num_vertices));
 
   for (int i = 0; i < image.num_textures(); ++i) {
@@ -472,6 +498,71 @@
   return CompileShader(blit_fragment_shader_source);
 }
 
+uint32 TexturedMeshRenderer::CreateYUVCompactedTexturesFragmentShader(
+    uint32 texture_target) {
+  SamplerInfo sampler_info = GetSamplerInfo(texture_target);
+
+  std::string blit_fragment_shader_source = sampler_info.preamble;
+
+  // The fragment shader below performs YUV->RGB transform for
+  // compacted 10-bit yuv420 textures
+  blit_fragment_shader_source +=
+      "precision mediump float;"
+      "varying vec2 v_tex_coord_y;"
+      "varying vec2 v_tex_coord_u;"
+      "varying vec2 v_tex_coord_v;"
+      "uniform sampler2D texture_y;"
+      "uniform sampler2D texture_u;"
+      "uniform sampler2D texture_v;"
+      "uniform vec2 viewport_size;"
+      "uniform vec2 viewport_to_texture_ratio;"
+      "uniform vec2 texture_size;"
+      "uniform mat4 to_rgb_color_matrix;"
+      // In a compacted YUV image each channel, Y, U and V, is stored as a
+      // separate single-channel image plane. Its pixels are stored in 32-bit
+      // and each represents 3 10-bit pixels with 2 bits of gap.
+      // In order to extract compacted pixels, the horizontal position at the
+      // compacted texture is calculated as:
+      // index = compacted_position % 3;
+      // decompacted_position = compacted_position / 3;
+      // pixel = CompactedTexture.Sample(Sampler, decompacted_position)[index];
+      "void main() {"
+      // Take into account display size to texture size ratio for Y component.
+      "vec2 DTid = vec2(floor(v_tex_coord_y * viewport_size));"
+      "vec2 compact_pos_Y = vec2((DTid + 0.5) / viewport_to_texture_ratio);"
+      // Calculate the position of 10-bit pixel for Y.
+      "vec2 decompact_pos_Y;"
+      "decompact_pos_Y.x = (floor(compact_pos_Y.x / 3.0) + 0.5) / "
+      "texture_size.x;"
+      "decompact_pos_Y.y = compact_pos_Y.y / texture_size.y;"
+      // Calculate the index of 10-bit pixel for Y.
+      "int index_Y = int(mod(floor(compact_pos_Y.x), 3.0));"
+      // Extract Y component.
+      "float Y_component = texture2D(texture_y, decompact_pos_Y)[index_Y];"
+      // For yuv420 U and V textures have dimensions twice less than Y.
+      "DTid = vec2(DTid / 2.0);"
+      "vec2 texture_size_UV = vec2(texture_size / 2.0);"
+      "vec2 compact_pos_UV = vec2((DTid + 0.5) / viewport_to_texture_ratio);"
+      // Calculate the position of 10-bit pixels for U and V.
+      "vec2 decompact_pos_UV;"
+      "decompact_pos_UV.x = (floor(compact_pos_UV.x / 3.0) + 0.5) / "
+      "texture_size_UV.x;"
+      "decompact_pos_UV.y = (floor(compact_pos_UV.y) + 0.5) / "
+      "texture_size_UV.y;"
+      // Calculate the index of 10-bit pixels for U and V.
+      "int index_UV = int(mod(floor(compact_pos_UV), 3.0));"
+      // Extract U and V components.
+      "float U_component = texture2D(texture_u, decompact_pos_UV)[index_UV];"
+      "float V_component =  texture2D(texture_v, decompact_pos_UV)[index_UV];"
+      // Perform the YUV->RGB transform and output the color value.
+      "vec4 untransformed_color = vec4(Y_component, U_component, V_component, "
+      "1.0);"
+      "gl_FragColor = untransformed_color * to_rgb_color_matrix;"
+      "}";
+
+  return CompileShader(blit_fragment_shader_source);
+}
+
 // static
 TexturedMeshRenderer::ProgramInfo TexturedMeshRenderer::MakeBlitProgram(
     const float* color_matrix, const std::vector<TextureInfo>& textures,
@@ -530,7 +621,20 @@
         glGetUniformLocation(result.gl_program_id, "to_rgb_color_matrix"));
     GL_CALL(glUniformMatrix4fv(to_rgb_color_matrix_uniform, 1, GL_FALSE,
                                color_matrix));
+    if (color_matrix == k10BitBT2020ColorMatrixCompacted) {
+      result.viewport_size_uniform = GL_CALL_SIMPLE(
+          glGetUniformLocation(result.gl_program_id, "viewport_size"));
+      DCHECK_EQ(GL_NO_ERROR, GL_CALL_SIMPLE(glGetError()));
+      result.viewport_to_texture_size_ratio_uniform =
+          GL_CALL_SIMPLE(glGetUniformLocation(result.gl_program_id,
+                                              "viewport_to_texture_ratio"));
+      DCHECK_EQ(GL_NO_ERROR, GL_CALL_SIMPLE(glGetError()));
+      result.subtexture_size_uniform = GL_CALL_SIMPLE(
+          glGetUniformLocation(result.gl_program_id, "texture_size"));
+      DCHECK_EQ(GL_NO_ERROR, GL_CALL_SIMPLE(glGetError()));
+    }
   }
+
   GL_CALL(glUseProgram(0));
 
   GL_CALL(glDeleteShader(blit_fragment_shader));
@@ -599,7 +703,8 @@
       } break;
       case Image::YUV_3PLANE_BT601_FULL_RANGE:
       case Image::YUV_3PLANE_BT709:
-      case Image::YUV_3PLANE_10BIT_BT2020: {
+      case Image::YUV_3PLANE_10BIT_BT2020:
+      case Image::YUV_3PLANE_10BIT_COMPACT_BT2020: {
         std::vector<TextureInfo> texture_infos;
 #if defined(GL_RED_EXT)
         if (image.textures[0].texture->GetFormat() == GL_RED_EXT) {
@@ -622,9 +727,15 @@
         texture_infos.push_back(TextureInfo("u", "a"));
         texture_infos.push_back(TextureInfo("v", "a"));
 #endif  // defined(GL_RED_EXT)
-        result = MakeBlitProgram(
-            color_matrix, texture_infos,
-            CreateFragmentShader(texture_target, texture_infos, color_matrix));
+        uint32 shader_program;
+        if (type == Image::YUV_3PLANE_10BIT_COMPACT_BT2020) {
+          shader_program =
+              CreateYUVCompactedTexturesFragmentShader(texture_target);
+        } else {
+          shader_program =
+              CreateFragmentShader(texture_target, texture_infos, color_matrix);
+        }
+        result = MakeBlitProgram(color_matrix, texture_infos, shader_program);
       } break;
       case Image::YUV_UYVY_422_BT709: {
         std::vector<TextureInfo> texture_infos;
diff --git a/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h b/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h
index e607bd7..2faac78 100644
--- a/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h
+++ b/cobalt/renderer/rasterizer/egl/textured_mesh_renderer.h
@@ -59,6 +59,10 @@
       // YUV BT2020 image where the Y, U and V components are all on different
       // 10bit unnormalized textures.
       YUV_3PLANE_10BIT_BT2020,
+      // YUV BT2020 image where the Y, U and V components are all on different
+      // textures. Its pixels are stored in 32 bits, and each pixel represents
+      // three 10-bit pixels with two bits of gap.
+      YUV_3PLANE_10BIT_COMPACT_BT2020,
       // 1 texture is used that contains RGBA pixels.
       RGBA,
       // 1 texture plane is used where Y is sampled twice for each UV sample
@@ -79,6 +83,7 @@
         case YUV_3PLANE_BT601_FULL_RANGE:
         case YUV_3PLANE_BT709:
         case YUV_3PLANE_10BIT_BT2020:
+        case YUV_3PLANE_10BIT_COMPACT_BT2020:
           return 3;
         case RGBA:
         case YUV_UYVY_422_BT709:
@@ -121,6 +126,9 @@
     int32 texture_uniforms[3];
     int32 texture_size_uniforms[3];
     uint32 gl_program_id;
+    int32 viewport_size_uniform;
+    int32 viewport_to_texture_size_ratio_uniform;
+    int32 subtexture_size_uniform;
   };
   // We key each program off of their GL texture type and image type.
   typedef std::tuple<uint32, Image::Type, base::Optional<int32> > CacheKey;
@@ -142,6 +150,9 @@
   // of applying bilinear filtering within a texel between the two Y values.
   static uint32 CreateUYVYFragmentShader(uint32 texture_target,
                                          int32 texture_wrap_s);
+  // YUV compacted textures need a special fragment shader to handle compacted
+  // pixels. Compacted textures support YUV420 only.
+  static uint32 CreateYUVCompactedTexturesFragmentShader(uint32 texture_target);
 
   backend::GraphicsContextEGL* graphics_context_;
 
diff --git a/cobalt/renderer/rasterizer/skia/BUILD.gn b/cobalt/renderer/rasterizer/skia/BUILD.gn
index 11146df..abb46ad 100644
--- a/cobalt/renderer/rasterizer/skia/BUILD.gn
+++ b/cobalt/renderer/rasterizer/skia/BUILD.gn
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//cobalt/renderer/rasterizer/skia/skia/skia_next.gni")
+
 static_library("common") {
   visibility = [ ":*" ]
 
@@ -42,6 +44,10 @@
   configs -= [ "//starboard/build/config:size" ]
   configs += [ "//starboard/build/config:speed" ]
 
+  if (use_skia_next) {
+    include_dirs = [ skia_include_dir ]
+  }
+
   public_deps = [
     "//base",
     "//cobalt/base",
@@ -78,6 +84,10 @@
   configs -= [ "//starboard/build/config:size" ]
   configs += [ "//starboard/build/config:speed" ]
 
+  if (use_skia_next) {
+    include_dirs = [ skia_include_dir ]
+  }
+
   deps = [
     ":common",
     "//cobalt/renderer:renderer_headers_only",
@@ -101,5 +111,9 @@
   configs -= [ "//starboard/build/config:size" ]
   configs += [ "//starboard/build/config:speed" ]
 
+  if (use_skia_next) {
+    include_dirs = [ skia_include_dir ]
+  }
+
   deps = [ ":common" ]
 }
diff --git a/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index 2c08712..db0c69c 100644
--- a/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -328,6 +328,18 @@
   return result;
 }
 
+inline void SetImageTextures(egl::TexturedMeshRenderer::Image& result,
+                             unsigned int textures_num,
+                             const math::Matrix3F& local_transform,
+                             HardwareMultiPlaneImage* hardware_image,
+                             render_tree::StereoMode stereo_mode) {
+  for (auto i = 0; i < textures_num; i++) {
+    result.textures[i] = GetTextureFromHardwareFrontendImage(
+        local_transform, hardware_image->GetHardwareFrontendImage(i).get(),
+        stereo_mode);
+  }
+}
+
 egl::TexturedMeshRenderer::Image SkiaImageToTexturedMeshRendererImage(
     const math::Matrix3F& local_transform, Image* image,
     render_tree::StereoMode stereo_mode) {
@@ -357,48 +369,25 @@
         render_tree::kMultiPlaneImageFormatYUV3PlaneBT601FullRange) {
       result.type =
           egl::TexturedMeshRenderer::Image::YUV_3PLANE_BT601_FULL_RANGE;
-      result.textures[0] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(0).get(),
-          stereo_mode);
-      result.textures[1] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(1).get(),
-          stereo_mode);
-      result.textures[2] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(2).get(),
-          stereo_mode);
+      SetImageTextures(result, 3, local_transform, hardware_image, stereo_mode);
     } else if (hardware_image->GetFormat() ==
                render_tree::kMultiPlaneImageFormatYUV2PlaneBT709) {
       result.type = egl::TexturedMeshRenderer::Image::YUV_2PLANE_BT709;
-      result.textures[0] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(0).get(),
-          stereo_mode);
-      result.textures[1] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(1).get(),
-          stereo_mode);
+      SetImageTextures(result, 2, local_transform, hardware_image, stereo_mode);
     } else if (hardware_image->GetFormat() ==
                render_tree::kMultiPlaneImageFormatYUV3PlaneBT709) {
       result.type = egl::TexturedMeshRenderer::Image::YUV_3PLANE_BT709;
-      result.textures[0] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(0).get(),
-          stereo_mode);
-      result.textures[1] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(1).get(),
-          stereo_mode);
-      result.textures[2] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(2).get(),
-          stereo_mode);
+      SetImageTextures(result, 3, local_transform, hardware_image, stereo_mode);
     } else if (hardware_image->GetFormat() ==
                render_tree::kMultiPlaneImageFormatYUV3Plane10BitBT2020) {
       result.type = egl::TexturedMeshRenderer::Image::YUV_3PLANE_10BIT_BT2020;
-      result.textures[0] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(0).get(),
-          stereo_mode);
-      result.textures[1] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(1).get(),
-          stereo_mode);
-      result.textures[2] = GetTextureFromHardwareFrontendImage(
-          local_transform, hardware_image->GetHardwareFrontendImage(2).get(),
-          stereo_mode);
+      SetImageTextures(result, 3, local_transform, hardware_image, stereo_mode);
+    } else if (hardware_image->GetFormat() ==
+               render_tree::
+                   kMultiPlaneImageFormatYUV3Plane10BitCompactedBT2020) {
+      result.type =
+          egl::TexturedMeshRenderer::Image::YUV_3PLANE_10BIT_COMPACT_BT2020;
+      SetImageTextures(result, 3, local_transform, hardware_image, stereo_mode);
     }
   } else {
     NOTREACHED();
diff --git a/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc b/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
index f3295a7..0ccee82 100644
--- a/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
+++ b/cobalt/renderer/rasterizer/skia/hardware_resource_provider.cc
@@ -19,6 +19,7 @@
 #include "cobalt/renderer/rasterizer/skia/hardware_resource_provider.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/synchronization/waitable_event.h"
 #include "base/trace_event/trace_event.h"
@@ -205,6 +206,7 @@
       }
     } break;
     case kSbDecodeTargetFormat3Plane10BitYUVI420:
+    case kSbDecodeTargetFormat3Plane10BitYUVI420Compact:
     case kSbDecodeTargetFormat3PlaneYUVI420: {
       DCHECK_LT(plane, 3);
 #if defined(GL_RED_EXT)
@@ -233,6 +235,9 @@
     case kSbDecodeTargetFormat3Plane10BitYUVI420: {
       return render_tree::kMultiPlaneImageFormatYUV3Plane10BitBT2020;
     } break;
+    case kSbDecodeTargetFormat3Plane10BitYUVI420Compact: {
+      return render_tree::kMultiPlaneImageFormatYUV3Plane10BitCompactedBT2020;
+    }
     default: { NOTREACHED(); }
   }
   return render_tree::kMultiPlaneImageFormatYUV2PlaneBT709;
diff --git a/cobalt/renderer/rasterizer/skia/skia/BUILD.gn b/cobalt/renderer/rasterizer/skia/skia/BUILD.gn
index 6fac75a..b43ec9f 100644
--- a/cobalt/renderer/rasterizer/skia/skia/BUILD.gn
+++ b/cobalt/renderer/rasterizer/skia/skia/BUILD.gn
@@ -12,26 +12,27 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//cobalt/renderer/rasterizer/skia/skia/skia_next.gni")
 import("//cobalt/renderer/rasterizer/skia/skia/skia_sources.gni")
 import("//cobalt/renderer/rasterizer/skia/skia/skia_template.gni")
 
 # skia_opts_none and skia_opts targets contain the platform-specific optimizations for Skia
 config("skia_opts_none") {
   include_dirs = [
-    "//third_party/skia/include/core",
-    "//third_party/skia/include/effects",
-    "//third_party/skia/src/core",
-    "//third_party/skia/src/utils",
+    "//third_party/$skia_subdir/include/core",
+    "//third_party/$skia_subdir/include/effects",
+    "//third_party/$skia_subdir/src/core",
+    "//third_party/$skia_subdir/src/utils",
   ]
 }
 
 config("skia_opts") {
   include_dirs = [
-    "//third_party/skia/include/core",
-    "//third_party/skia/include/effects",
-    "//third_party/skia/src/core",
-    "//third_party/skia/src/opts",
-    "//third_party/skia/src/utils",
+    "//third_party/$skia_subdir/include/core",
+    "//third_party/$skia_subdir/include/effects",
+    "//third_party/$skia_subdir/src/core",
+    "//third_party/$skia_subdir/src/opts",
+    "//third_party/$skia_subdir/src/utils",
   ]
 }
 
@@ -41,6 +42,11 @@
 skia_common("skia_library_no_asan") {
   check_includes = false
   sources = skia_effects_imagefilter_sources_no_asan
+  if (use_skia_next) {
+    include_dirs = [ skia_include_dir ]
+    defines = [ "USE_SKIA_NEXT" ]
+    cflags_cc = [ "-Wno-c++17-extensions" ]
+  }
 
   # TODO(b/207398024): If this is a target we need to build, then we'll
   # need to rework the configuration framework to be able to remove this
@@ -58,21 +64,26 @@
 config("skia_library_config_public") {
   include_dirs = [
     #temporary until we can hide SkFontHost
-    "//third_party/skia",
-    "//third_party/skia/src/core",
-    "//third_party/skia/src/utils",
-    "//third_party/skia/include/core",
-    "//third_party/skia/include/effects",
-    "//third_party/skia/include/gpu",
-    "//third_party/skia/include/lazy",
-    "//third_party/skia/include/pathops",
-    "//third_party/skia/include/pipe",
-    "//third_party/skia/include/ports",
-    "//third_party/skia/include/private",
-    "//third_party/skia/include/utils",
+    "//third_party/$skia_subdir",
+    "//third_party/$skia_subdir/src/core",
+    "//third_party/$skia_subdir/src/utils",
+    "//third_party/$skia_subdir/include/core",
+    "//third_party/$skia_subdir/include/effects",
+    "//third_party/$skia_subdir/include/gpu",
+    "//third_party/$skia_subdir/include/lazy",
+    "//third_party/$skia_subdir/include/pathops",
+    "//third_party/$skia_subdir/include/pipe",
+    "//third_party/$skia_subdir/include/ports",
+    "//third_party/$skia_subdir/include/private",
+    "//third_party/$skia_subdir/include/utils",
   ]
 
   defines = [ "SK_BUILD_NO_OPTS" ]
+
+  if (use_skia_next) {
+    defines += [ "USE_SKIA_NEXT" ]
+    cflags_cc = [ "-Wno-c++17-extensions" ]
+  }
 }
 
 skia_common("skia_library") {
@@ -89,9 +100,9 @@
     ":skia_library_no_asan",
     "//base",
     "//starboard/common",
+    "//third_party/$skia_subdir/third_party/skcms",
     "//third_party/freetype2",
     "//third_party/libpng",
-    "//third_party/skia/third_party/skcms",
     "//third_party/zlib",
   ]
 }
@@ -131,6 +142,10 @@
 
   include_dirs = [ target_gen_dir ]
 
+  if (use_skia_next) {
+    include_dirs += [ skia_include_dir ]
+  }
+
   deps = [
     "//base",
     "//cobalt/base",
diff --git a/cobalt/renderer/rasterizer/skia/skia/skia_next.gni b/cobalt/renderer/rasterizer/skia/skia/skia_next.gni
new file mode 100644
index 0000000..7c61125
--- /dev/null
+++ b/cobalt/renderer/rasterizer/skia/skia/skia_next.gni
@@ -0,0 +1,25 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  use_skia_next = false
+}
+
+skia_subdir = "skia"
+skia_include_dir = ""
+
+if (use_skia_next) {
+  skia_subdir = "skia_next/third_party/skia"
+  skia_include_dir = "//third_party/skia_next"
+}
diff --git a/cobalt/renderer/rasterizer/skia/skia/skia_sources.gni b/cobalt/renderer/rasterizer/skia/skia/skia_sources.gni
index 40b3a97..03a9310 100644
--- a/cobalt/renderer/rasterizer/skia/skia/skia_sources.gni
+++ b/cobalt/renderer/rasterizer/skia/skia/skia_sources.gni
@@ -12,46 +12,52 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//third_party/skia/gn/core.gni")
-import("//third_party/skia/gn/effects.gni")
-import("//third_party/skia/gn/effects_imagefilters.gni")
-import("//third_party/skia/gn/gpu.gni")
-import("//third_party/skia/gn/utils.gni")
-import("//third_party/skia/modules/skottie/skottie.gni")
-import("//third_party/skia/modules/sksg/sksg.gni")
-import("//third_party/skia/modules/skshaper/skshaper.gni")
+import("//cobalt/renderer/rasterizer/skia/skia/skia_next.gni")
+import("//third_party/$skia_subdir/gn/core.gni")
+import("//third_party/$skia_subdir/gn/effects.gni")
+import("//third_party/$skia_subdir/gn/effects_imagefilters.gni")
+import("//third_party/$skia_subdir/gn/gpu.gni")
+import("//third_party/$skia_subdir/gn/utils.gni")
+import("//third_party/$skia_subdir/modules/skottie/skottie.gni")
+import("//third_party/$skia_subdir/modules/sksg/sksg.gni")
+import("//third_party/$skia_subdir/modules/skshaper/skshaper.gni")
 
 combined_sources = []
 
 shared_sources = [
-  "//third_party/skia/src/codec/SkBmpBaseCodec.cpp",
-  "//third_party/skia/src/codec/SkBmpCodec.cpp",
-  "//third_party/skia/src/codec/SkBmpMaskCodec.cpp",
-  "//third_party/skia/src/codec/SkBmpRLECodec.cpp",
-  "//third_party/skia/src/codec/SkBmpStandardCodec.cpp",
-  "//third_party/skia/src/codec/SkCodec.cpp",
-  "//third_party/skia/src/codec/SkCodecImageGenerator.cpp",
-  "//third_party/skia/src/codec/SkColorTable.cpp",
-  "//third_party/skia/src/codec/SkGifCodec.cpp",
-  "//third_party/skia/src/codec/SkMaskSwizzler.cpp",
-  "//third_party/skia/src/codec/SkMasks.cpp",
-  "//third_party/skia/src/codec/SkSampledCodec.cpp",
-  "//third_party/skia/src/codec/SkSampler.cpp",
-  "//third_party/skia/src/codec/SkStreamBuffer.cpp",
-  "//third_party/skia/src/codec/SkSwizzler.cpp",
-  "//third_party/skia/src/codec/SkWbmpCodec.cpp",
-  "//third_party/skia/src/images/SkImageEncoder.cpp",
-  "//third_party/skia/src/ports/SkDiscardableMemory_none.cpp",
-  "//third_party/skia/src/ports/SkFontHost_FreeType.cpp",
-  "//third_party/skia/src/ports/SkFontHost_FreeType_common.cpp",
-  "//third_party/skia/src/ports/SkFontHost_FreeType_common.h",
-  "//third_party/skia/src/ports/SkGlobalInitialization_default.cpp",
-  "//third_party/skia/src/ports/SkImageGenerator_skia.cpp",
-  "//third_party/skia/src/sfnt/SkOTTable_name.cpp",
-  "//third_party/skia/src/sfnt/SkOTUtils.cpp",
-  "//third_party/skia/third_party/gif/SkGifImageReader.cpp",
+  "//third_party/$skia_subdir/src/codec/SkBmpBaseCodec.cpp",
+  "//third_party/$skia_subdir/src/codec/SkBmpCodec.cpp",
+  "//third_party/$skia_subdir/src/codec/SkBmpMaskCodec.cpp",
+  "//third_party/$skia_subdir/src/codec/SkBmpRLECodec.cpp",
+  "//third_party/$skia_subdir/src/codec/SkBmpStandardCodec.cpp",
+  "//third_party/$skia_subdir/src/codec/SkCodec.cpp",
+  "//third_party/$skia_subdir/src/codec/SkCodecImageGenerator.cpp",
+  "//third_party/$skia_subdir/src/codec/SkColorTable.cpp",
+  "//third_party/$skia_subdir/src/codec/SkMaskSwizzler.cpp",
+  "//third_party/$skia_subdir/src/codec/SkMasks.cpp",
+  "//third_party/$skia_subdir/src/codec/SkSampledCodec.cpp",
+  "//third_party/$skia_subdir/src/codec/SkSampler.cpp",
+  "//third_party/$skia_subdir/src/codec/SkStreamBuffer.cpp",
+  "//third_party/$skia_subdir/src/codec/SkSwizzler.cpp",
+  "//third_party/$skia_subdir/src/codec/SkWbmpCodec.cpp",
+  "//third_party/$skia_subdir/src/images/SkImageEncoder.cpp",
+  "//third_party/$skia_subdir/src/ports/SkDiscardableMemory_none.cpp",
+  "//third_party/$skia_subdir/src/ports/SkFontHost_FreeType.cpp",
+  "//third_party/$skia_subdir/src/ports/SkFontHost_FreeType_common.cpp",
+  "//third_party/$skia_subdir/src/ports/SkFontHost_FreeType_common.h",
+  "//third_party/$skia_subdir/src/ports/SkGlobalInitialization_default.cpp",
+  "//third_party/$skia_subdir/src/ports/SkImageGenerator_skia.cpp",
+  "//third_party/$skia_subdir/src/sfnt/SkOTTable_name.cpp",
+  "//third_party/$skia_subdir/src/sfnt/SkOTUtils.cpp",
 ]
 
+if (!use_skia_next) {
+  shared_sources += [
+    "//third_party/$skia_subdir/src/codec/SkGifCodec.cpp",
+    "//third_party/$skia_subdir/third_party/gif/SkGifImageReader.cpp",
+  ]
+}
+
 # from "core.gni"
 combined_sources += shared_sources
 combined_sources += skia_core_sources
@@ -70,68 +76,95 @@
 # Exclude all unused skia files
 combined_sources -= [
   # codec
-  "//third_party/skia/src/codec/SkSampledCodec.cpp",
+  "//third_party/$skia_subdir/src/codec/SkSampledCodec.cpp",
 
   # core
-  "//third_party/skia/src/core/SkMultiPictureDraw.cpp",
-  "//third_party/skia/src/core/SkTime.cpp",
-
-  # gpu
-  "//third_party/skia/src/gpu/gl/GrGLMakeNativeInterface_none.cpp",
+  "//third_party/$skia_subdir/src/core/SkTime.cpp",
 
   # utils bitmap
-  "//third_party/skia/src/utils/SkCamera.cpp",
-  "//third_party/skia/src/utils/SkCanvasStack.h",
-  "//third_party/skia/src/utils/SkFloatUtils.h",
-  "//third_party/skia/src/utils/SkFrontBufferedStream.cpp",
-  "//third_party/skia/src/utils/SkInterpolator.cpp",
-  "//third_party/skia/src/utils/SkParseColor.cpp",
-  "//third_party/skia/src/utils/SkParsePath.cpp",
-  "//third_party/skia/src/utils/SkThreadUtils_pthread.cpp",
-  "//third_party/skia/src/utils/SkThreadUtils_win.cpp",
+  "//third_party/$skia_subdir/src/utils/SkCamera.cpp",
+  "//third_party/$skia_subdir/src/utils/SkCanvasStack.h",
+  "//third_party/$skia_subdir/src/utils/SkFloatUtils.h",
+  "//third_party/$skia_subdir/src/utils/SkParseColor.cpp",
+  "//third_party/$skia_subdir/src/utils/SkParsePath.cpp",
+  "//third_party/$skia_subdir/src/utils/SkThreadUtils_pthread.cpp",
+  "//third_party/$skia_subdir/src/utils/SkThreadUtils_win.cpp",
 
   # mac
-  "//third_party/skia/src/utils/mac/SkCreateCGImageRef.cpp",
+  "//third_party/$skia_subdir/src/utils/mac/SkCreateCGImageRef.cpp",
 
   # windows
-  "//third_party/skia/src/utils/win/SkAutoCoInitialize.cpp",
-  "//third_party/skia/src/utils/win/SkAutoCoInitialize.h",
-  "//third_party/skia/src/utils/win/SkDWrite.cpp",
-  "//third_party/skia/src/utils/win/SkDWrite.h",
-  "//third_party/skia/src/utils/win/SkDWriteFontFileStream.cpp",
-  "//third_party/skia/src/utils/win/SkDWriteFontFileStream.h",
-  "//third_party/skia/src/utils/win/SkDWriteGeometrySink.cpp",
-  "//third_party/skia/src/utils/win/SkDWriteGeometrySink.h",
-  "//third_party/skia/src/utils/win/SkHRESULT.cpp",
-  "//third_party/skia/src/utils/win/SkHRESULT.h",
-  "//third_party/skia/src/utils/win/SkIStream.cpp",
-  "//third_party/skia/src/utils/win/SkIStream.h",
-  "//third_party/skia/src/utils/win/SkTScopedComPtr.h",
-  "//third_party/skia/src/utils/win/SkWGL.h",
-  "//third_party/skia/src/utils/win/SkWGL_win.cpp",
+  "//third_party/$skia_subdir/src/utils/win/SkAutoCoInitialize.cpp",
+  "//third_party/$skia_subdir/src/utils/win/SkAutoCoInitialize.h",
+  "//third_party/$skia_subdir/src/utils/win/SkDWrite.cpp",
+  "//third_party/$skia_subdir/src/utils/win/SkDWrite.h",
+  "//third_party/$skia_subdir/src/utils/win/SkDWriteFontFileStream.cpp",
+  "//third_party/$skia_subdir/src/utils/win/SkDWriteFontFileStream.h",
+  "//third_party/$skia_subdir/src/utils/win/SkDWriteGeometrySink.cpp",
+  "//third_party/$skia_subdir/src/utils/win/SkDWriteGeometrySink.h",
+  "//third_party/$skia_subdir/src/utils/win/SkHRESULT.cpp",
+  "//third_party/$skia_subdir/src/utils/win/SkHRESULT.h",
+  "//third_party/$skia_subdir/src/utils/win/SkIStream.cpp",
+  "//third_party/$skia_subdir/src/utils/win/SkIStream.h",
+  "//third_party/$skia_subdir/src/utils/win/SkTScopedComPtr.h",
+  "//third_party/$skia_subdir/src/utils/win/SkWGL.h",
+  "//third_party/$skia_subdir/src/utils/win/SkWGL_win.cpp",
 ]
 
-sksl_sources = [
-  "//third_party/skia/src/sksl/SkSLASTNode.cpp",
-  "//third_party/skia/src/sksl/SkSLByteCode.cpp",
-  "//third_party/skia/src/sksl/SkSLCFGGenerator.cpp",
-  "//third_party/skia/src/sksl/SkSLCPPCodeGenerator.cpp",
-  "//third_party/skia/src/sksl/SkSLCPPUniformCTypes.cpp",
-  "//third_party/skia/src/sksl/SkSLCompiler.cpp",
-  "//third_party/skia/src/sksl/SkSLGLSLCodeGenerator.cpp",
-  "//third_party/skia/src/sksl/SkSLHCodeGenerator.cpp",
-  "//third_party/skia/src/sksl/SkSLIRGenerator.cpp",
-  "//third_party/skia/src/sksl/SkSLLexer.cpp",
-  "//third_party/skia/src/sksl/SkSLMetalCodeGenerator.cpp",
-  "//third_party/skia/src/sksl/SkSLOutputStream.cpp",
-  "//third_party/skia/src/sksl/SkSLParser.cpp",
-  "//third_party/skia/src/sksl/SkSLPipelineStageCodeGenerator.cpp",
-  "//third_party/skia/src/sksl/SkSLSPIRVCodeGenerator.cpp",
-  "//third_party/skia/src/sksl/SkSLSectionAndParameterHelper.cpp",
-  "//third_party/skia/src/sksl/SkSLString.cpp",
-  "//third_party/skia/src/sksl/SkSLUtil.cpp",
-  "//third_party/skia/src/sksl/ir/SkSLSetting.cpp",
-  "//third_party/skia/src/sksl/ir/SkSLSymbolTable.cpp",
-  "//third_party/skia/src/sksl/ir/SkSLType.cpp",
-  "//third_party/skia/src/sksl/ir/SkSLVariableReference.cpp",
-]
+# Exclude unused skia files which only exist in m79.
+if (!use_skia_next) {
+  combined_sources -= [
+    # core
+    "//third_party/$skia_subdir/src/core/SkMultiPictureDraw.cpp",
+
+    # gpu
+    "//third_party/$skia_subdir/src/gpu/gl/GrGLMakeNativeInterface_none.cpp",
+
+    # utils bitmap
+    "//third_party/$skia_subdir/src/utils/SkFrontBufferedStream.cpp",
+    "//third_party/$skia_subdir/src/utils/SkInterpolator.cpp",
+  ]
+}
+
+if (use_skia_next) {
+  sksl_sources = [
+    "//third_party/$skia_subdir/src/sksl/SkSLCompiler.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLLexer.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLOutputStream.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLString.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLUtil.cpp",
+    "//third_party/$skia_subdir/src/sksl/ir/SkSLSetting.cpp",
+    "//third_party/$skia_subdir/src/sksl/ir/SkSLSymbolTable.cpp",
+    "//third_party/$skia_subdir/src/sksl/ir/SkSLType.cpp",
+    "//third_party/$skia_subdir/src/sksl/ir/SkSLVariableReference.cpp",
+  ]
+} else {
+  sksl_sources = [
+    "//third_party/$skia_subdir/src/sksl/SkSLASTNode.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLByteCode.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLCFGGenerator.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLCPPCodeGenerator.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLCPPUniformCTypes.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLCompiler.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLGLSLCodeGenerator.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLHCodeGenerator.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLIRGenerator.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLLexer.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLMetalCodeGenerator.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLOutputStream.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLParser.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLPipelineStageCodeGenerator.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLSPIRVCodeGenerator.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLSectionAndParameterHelper.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLString.cpp",
+    "//third_party/$skia_subdir/src/sksl/SkSLUtil.cpp",
+    "//third_party/$skia_subdir/src/sksl/ir/SkSLSetting.cpp",
+    "//third_party/$skia_subdir/src/sksl/ir/SkSLSymbolTable.cpp",
+    "//third_party/$skia_subdir/src/sksl/ir/SkSLType.cpp",
+    "//third_party/$skia_subdir/src/sksl/ir/SkSLVariableReference.cpp",
+  ]
+
+  # Exclude unused skia sksl files
+  sksl_sources -=
+      [ "//third_party/$skia_subdir/src/sksl/SkSLMetalCodeGenerator.cpp" ]
+}
diff --git a/cobalt/renderer/rasterizer/skia/skia/skia_template.gni b/cobalt/renderer/rasterizer/skia/skia/skia_template.gni
index 516cb32..7ffe944 100644
--- a/cobalt/renderer/rasterizer/skia/skia/skia_template.gni
+++ b/cobalt/renderer/rasterizer/skia/skia/skia_template.gni
@@ -15,6 +15,8 @@
 # This file handles the removal of platform-specific files from the
 # Skia build.
 
+import("//cobalt/renderer/rasterizer/skia/skia/skia_next.gni")
+
 template("skia_common") {
   # This list will contain all defines that also need to be exported to
   # dependent components.
@@ -57,12 +59,16 @@
     forward_variables_from(invoker, "*", [ "configs" ])
 
     if (defined(include_dirs)) {
-      include_dirs += [ "//third_party/skia" ]
+      include_dirs += [ "//third_party/$skia_subdir" ]
     } else {
-      include_dirs = [ "//third_party/skia" ]
+      include_dirs = [ "//third_party/$skia_subdir" ]
     }
 
-    defines = [
+    if (!defined(defines)) {
+      defines = []
+    }
+
+    defines += [
       # skia uses static initializers to initialize the serialization logic
       # of its "pictures" library. This is currently not used in Cobalt; if
       # it ever gets used the processes that use it need to call
diff --git a/cobalt/renderer/sandbox/BUILD.gn b/cobalt/renderer/sandbox/BUILD.gn
new file mode 100644
index 0000000..ba175a0
--- /dev/null
+++ b/cobalt/renderer/sandbox/BUILD.gn
@@ -0,0 +1,50 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a sample sandbox application for experimenting with the Cobalt
+# render tree/renderer interface.
+
+# This target will build a sandbox application that allows for easy
+# experimentation with the renderer interface on any platform.  This can
+# also be useful for visually inspecting the output that the Cobalt
+# renderer is producing.
+target(final_executable_type, "renderer_sandbox") {
+  sources = [ "renderer_sandbox_main.cc" ]
+
+  deps = [
+    "//cobalt/base",
+    "//cobalt/math",
+    "//cobalt/renderer",
+    "//cobalt/renderer/test/scenes",
+    "//cobalt/system_window",
+    "//cobalt/trace_event",
+  ]
+}
+
+# This target will build a sandbox application that allows for easy
+# experimentation with the renderer's handling of text where its scale
+# is constantly animating, which for many implementations can be a
+# performance problem.
+target(final_executable_type, "scaling_text_sandbox") {
+  sources = [ "scaling_text_sandbox_main.cc" ]
+
+  deps = [
+    "//cobalt/base",
+    "//cobalt/math",
+    "//cobalt/renderer",
+    "//cobalt/renderer/test/scenes",
+    "//cobalt/system_window",
+    "//cobalt/trace_event",
+  ]
+}
diff --git a/cobalt/renderer/test/scenes/BUILD.gn b/cobalt/renderer/test/scenes/BUILD.gn
new file mode 100644
index 0000000..e443ad2
--- /dev/null
+++ b/cobalt/renderer/test/scenes/BUILD.gn
@@ -0,0 +1,55 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a sample sandbox application for experimenting with the Cobalt
+# render tree/renderer interface.
+
+# Contains code to build a demonstration scene used within benchmarking
+# code along with the sandbox application code.  Is useful in general
+# wherever an interesting demo render_tree is required.
+static_library("scenes") {
+  sources = [
+    "all_scenes_combined_scene.cc",
+    "all_scenes_combined_scene.h",
+    "growing_rect_scene.cc",
+    "growing_rect_scene.h",
+    "image_wrap_scene.cc",
+    "image_wrap_scene.h",
+    "marquee_scene.cc",
+    "marquee_scene.h",
+    "scaling_text_scene.cc",
+    "scaling_text_scene.h",
+    "scene_helpers.cc",
+    "scene_helpers.h",
+    "spinning_sprites_scene.cc",
+    "spinning_sprites_scene.h",
+  ]
+
+  deps = [
+    ":scenes_copy_test_data",
+    "//cobalt/base",
+    "//cobalt/math",
+    "//cobalt/render_tree",
+    "//cobalt/render_tree:animations",
+    "//cobalt/renderer/test/png_utils",
+    "//cobalt/trace_event",
+  ]
+}
+
+copy("scenes_copy_test_data") {
+  sources = [ "demo_image.png" ]
+
+  outputs =
+      [ "$sb_static_contents_output_data_dir/test/test/scenes/demo_image.png" ]
+}
diff --git a/cobalt/site/docs/development/setup-android.md b/cobalt/site/docs/development/setup-android.md
index b2ddb55..621f5f3 100644
--- a/cobalt/site/docs/development/setup-android.md
+++ b/cobalt/site/docs/development/setup-android.md
@@ -9,7 +9,7 @@
 ## Preliminary Setup
 
 <aside class="note">
-<b>Note:</b> Before proceeding further, refer to the documentation for <a href="setup-linux.html">"Set up your environment - Linux"</a>. Complete the section **Set up your workstation**, then return and complete the following steps.
+<b>Note:</b> Before proceeding further, refer to the documentation for <a href="setup-linux.html">"Set up your environment - Linux"</a>. Complete the section <b>Set up your workstation</b>, then return and complete the following steps.
 </aside>
 
 1.  Additional build dependencies may need to be installed:
diff --git a/cobalt/site/docs/development/setup-docker.md b/cobalt/site/docs/development/setup-docker.md
new file mode 100644
index 0000000..afc7065
--- /dev/null
+++ b/cobalt/site/docs/development/setup-docker.md
@@ -0,0 +1,93 @@
+---
+layout: doc
+title: "Set up your environment - Docker"
+---
+
+We provide <a
+href="https://cobalt.googlesource.com/cobalt/+/refs/heads/22.lts.stable/src/docker/linux/">Docker image definitions</a> to simplify managing build environments.
+
+The instructions below assume Docker is installed and is able to run basic
+hello-world verification. `docker-compose` command is expected to be available as well.
+
+## Set up your workstation
+
+Clone the Cobalt code repository. The following `git` command creates a
+`cobalt` directory that contains the repository:
+
+```
+$ git clone https://cobalt.googlesource.com/cobalt
+$ cd cobalt
+```
+
+### Usage
+
+The simplest usage is:
+
+```
+docker-compose run <platform>
+```
+
+By default, a `debug` build will be built, with `cobalt` as a target.
+You can override this with an environment variable, e.g.
+
+```
+docker-compose run -e CONFIG=devel -e TARGET=nplb <platform>
+```
+
+where config is one of the four optimization levels, `debug`, `devel`,
+`qa` and `gold`, and target is the build target passed to ninja
+
+See <a
+href="https://cobalt.googlesource.com/cobalt/+/refs/heads/22.lts.stable/src/README.md#build-types">Cobalt README</a> for full details.
+
+Builds will be available in your `${COBALT_SRC}/out` directory.
+
+<aside class="note">
+Note that Docker runs processes as root user by default, hence
+output files in `src/out/<platform>` directory have `root` as file owner.
+</aside>
+
+### Customization
+
+To parametrize base operating system images used for the build, pass
+`BASE_OS` as an argument to `docker-compose` as follows:
+
+```
+docker-compose build --build-arg BASE_OS="ubuntu:bionic" base
+```
+
+This parameter is defined in `docker/linux/base/Dockerfile` and is passed
+to Docker `FROM ...` statement.
+
+Available parameters for customizing container execution are:
+
+**BASE_OS**: passed to `base` image at build time to select a Debian-based
+   base os image and version. Defaults to Debian 10. `ubuntu:bionic` and
+   `ubuntu:xenial` are other tested examples.
+
+**PLATFORM**: Cobalt build platform, passed to GYP
+
+**CONFIG**: Cobalt build config, passed to GYP. Defaults to `debug`
+
+**TARGET**: Build target, passed to `ninja`
+
+The `docker-compose.yml` contains the currently defined experimental build
+configurations. Edit or add new `service` entries as needed, to build custom
+configurations.
+
+
+### Pre-built images
+
+Note: Pre-built images from a public container registry are not yet available.
+
+### Troubleshooting
+
+To debug build issues, enter the shell of the corresponding build container
+by launching the bash shell, i.e.
+
+```
+docker-compose run linux-x64x11 /bin/bash
+```
+
+and try to build Cobalt with the <a
+href="https://cobalt.googlesource.com/cobalt/+/refs/heads/22.lts.stable/src/README.md#building-and-running-the-code">usual gyp / ninja flow.</a>
diff --git a/cobalt/site/docs/development/setup-linux.md b/cobalt/site/docs/development/setup-linux.md
index 288711b..21d07e5 100644
--- a/cobalt/site/docs/development/setup-linux.md
+++ b/cobalt/site/docs/development/setup-linux.md
@@ -9,16 +9,20 @@
 on the machine that you are using to view the client. For example, you cannot
 SSH into another machine and run the binary on that machine.
 
+These instructions were tested on a fresh ubuntu:20.04 Docker image. (1/12/22)
+Required libraries can differ depending on your Linux distribution and version.
+
 ## Set up your workstation
 
 1.  Run the following command to install packages needed to build and run
     Cobalt on Linux:
 
     ```
-    $ sudo apt install -qqy --no-install-recommends pkgconf ninja-build \
-        bison yasm binutils clang libgles2-mesa-dev mesa-common-dev \
-        libpulse-dev libavresample-dev libasound2-dev libxrender-dev \
-        libxcomposite-dev
+    $ sudo apt update && sudo apt install -qqy --no-install-recommends \
+        pkgconf ninja-build bison yasm binutils clang libgles2-mesa-dev \
+        mesa-common-dev libpulse-dev libavresample-dev libasound2-dev \
+        libxrender-dev libxcomposite-dev libxml2-dev curl git \
+        python3.8-venv python2
     ```
 
 1.  Install Node.js via `nvm`:
@@ -48,6 +52,14 @@
     $ ccache --max-size=20G
     ```
 
+1.  Install necessary python2 packages for GYP. Until Cobalt 23, when we have
+    migrated our build system to GN, we still require some python2 packages:
+
+    ```
+    $ curl https://bootstrap.pypa.io/pip/2.7/get-pip.py | python2
+    $ python2 -m pip install --user requests selenium six
+    ```
+
 1.  Clone the Cobalt code repository. The following `git` command creates a
     `cobalt` directory that contains the repository:
 
@@ -63,17 +75,17 @@
     $ cd cobalt
     ```
 
-<aside class="note">
-<b>Note:</b> Pre-commit is only available on branches later than 22.lts.1+,
-including trunk. The below commands will fail on 22.lts.1+ and earlier branches.
-For earlier branches, run `cd src` and move on to the next section.
-</aside>
+    <aside class="note">
+    <b>Note:</b> Pre-commit is only available on branches later than 22.lts.1+,
+    including trunk. The below commands will fail on 22.lts.1+ and earlier branches.
+    For earlier branches, run `cd src` and move on to the next section.
+    </aside>
 
 1.  Create a Python 3 virtual environment for working on Cobalt (feel free to use `virtualenvwrapper` instead):
 
     ```
-    $ python -m venv ~/.virtualenvs/cobalt_dev
-    $ source ~/.virtualenvs/cobalt_dev
+    $ python3 -m venv ~/.virtualenvs/cobalt_dev
+    $ source ~/.virtualenvs/cobalt_dev/bin/activate
     $ pip install -r requirements.txt
     ```
 
diff --git a/cobalt/site/docs/development/setup-raspi.md b/cobalt/site/docs/development/setup-raspi.md
index 48ca4cc..8badc0a 100644
--- a/cobalt/site/docs/development/setup-raspi.md
+++ b/cobalt/site/docs/development/setup-raspi.md
@@ -4,57 +4,42 @@
 ---
 
 These instructions explain how to set up Cobalt for your workstation and your
-Raspberry Pi device.
+Raspberry Pi device. They have been tested with Ubuntu:20.04 and a Raspberry Pi
+3 Model B.
 
 ## Set up your device
 
-<aside class="note">
-<b>Note:</b> Raspberry Pi <em>cannot</em> have MesaGL installed and will return
-an error, like `DRI2 not supported` or `DRI2 failed to authenticate` if MesaGL
-is installed.
-</aside>
+Download the latest Cobalt customized Raspbian image from <a
+href="https://storage.googleapis.com/cobalt-static-storage/2020-02-13-raspbian-buster-lite_shrunk_20210427.img">GCS bucket</a>
+(this is built via <a
+href="https://github.com/youtube/cobalt/tree/master/tools/raspi_image#readme">this
+customization tool</a>)
 
-<aside class="note">
-<b>Note:</b> The current builds of Cobalt currently are verified to work only
-with a fairly old version of Raspbian Lite from 2017-07-05 (
-<a href="http://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2017-07-05/2017-07-05-raspbian-jessie-lite.zip">download link</a>
-).  If you have a newer version, you may encounter linker errors when building
-Cobalt as the sysroot system libraries will differ in the latest version of
-Raspbian.
-</aside>
+On MacOS, use an image flashing tool like <a href="https://www.balena.io/etcher/">balenaEtcher</a> to write the image to a 32GB SD-card.
 
-Configure the Raspberry Pi memory split.
+On Linux, follow the steps below.
 
-1.  `sudo raspi-config`
-1.  Go to Advanced
-1.  Memory Split: 256 for RasPi-0, 512 for all others.
-
-Cobalt assumes the Raspberry Pi is configured to use non-default thread
-schedulers and priorities. Ensure that **/etc/security/limits.conf** sets
-**rtprio** and **nice** limits for the user. For example, if the user is **pi**,
-then limits.conf should have the following lines:
+Check the location of your SD card (/dev/sdX or /dev/mmcblkX)
 
 ```
-@pi hard rtprio 99
-@pi soft rtprio 99
-@pi hard nice -20
-@pi soft nice -20
+$ sudo fdisk -l
 ```
+Make sure the card isn't mounted ( `umount /dev/sdX` ).
 
-The following commands update the package configuration on your Raspberry Pi
-so that Cobalt can run properly:
+Copy the downloaded image to your SD card (the disk, not the partition. E.g. /dev/sdX or /dev/mmcblkX):
 
 ```
-$ apt-get remove -y --purge --auto-remove libgl1-mesa-dev \
-          libegl1-mesa-dev libgles2-mesa libgles2-mesa-dev
-$ apt-get install -y libpulse-dev libasound2-dev libavformat-dev \
-          libavresample-dev rsync
+$ sudo dd bs=4M if=2020-02-13-raspbian-buster-lite_shrunk_20210427.img of=/dev/sdX
 ```
+After flashing your device, you'll still need to setup your wifi. Login with the
+default pi login, and run `sudo raspi-config`. You'll find wifi settings under
+`1. System Options`, then `S1 Wireless LAN`.
+
 
 ## Set up your workstation
 
 <aside class="note">
-<b>Note:</b> Before proceeding further, refer to the documentation for <a href="setup-linux.html">"Set up your environment - Linux"</a>. Complete the section **Set up your workstation**, then return and complete the following steps.
+<b>Note:</b> Before proceeding further, refer to the documentation for <a href="setup-linux.html">"Set up your environment - Linux"</a>. Complete the section <b>Set up your workstation</b>, then return and complete the following steps.
 </aside>
 
 The following steps install the cross-compiling toolchain on your workstation.
@@ -66,7 +51,8 @@
 
     ```
     $ sudo apt install -qqy --no-install-recommends g++-multilib \
-          python-requests wget xz-utils
+        wget xz-utils libxml2  binutils-aarch64-linux-gnu \
+        binutils-arm-linux-gnueabi  libglib2.0-dev
     ```
 
 1.  Choose a location for the installed toolchain &ndash; e.g. `raspi-tools`
@@ -77,32 +63,19 @@
 1.  Create the directory for the installed toolchain and go to it:
 
     ```
-    mkdir -p $RASPI_HOME
-    cd $RASPI_HOME
+    $ mkdir -p $RASPI_HOME
+    $ cd $RASPI_HOME
     ```
 
-1.  Clone the GitHub repository for Raspberry Pi tools:
+1.  Download the pre-packaged toolchain and extract it in `$RASPI_HOME`.
 
     ```
-    git clone git://github.com/raspberrypi/tools.git
+    $ curl -O https://storage.googleapis.com/cobalt-static-storage/cobalt_raspi_tools.tar.bz2
+    $ tar xvpf cobalt_raspi_tools.tar.bz2
     ```
 
-1.  Sync your sysroot by completing the following steps:
-
-    1.  Boot up your RasPi, and set `$RASPI_ADDR` to the device's IP address.
-    1.  Run `mkdir -p $RASPI_HOME/sysroot`
-    1.  Run:
-
-        ```
-        rsync -avzh --safe-links \
-              --delete-after pi@$RASPI_ADDR:/{opt,lib,usr} \
-              --exclude="lib/firmware" --exclude="lib/modules" \
-              --include="usr/lib" --include="usr/include" \
-              --include="usr/local/include" --include="usr/local/lib" \
-              --exclude="usr/*" --include="opt/vc" --exclude="opt/*" \
-              $RASPI_HOME/sysroot
-        password: raspberry
-        ```
+    (This is a combination of old raspi tools and a newer one from linaro
+     to support older Raspbian Jessie and newer Raspbian Buster)
 
 ## Build, install, and run Cobalt for Raspberry Pi
 
@@ -123,7 +96,7 @@
     on the device:
 
     ```
-    rsync -avzLPh --exclude="obj*" --exclude="gen/" \
+    $ rsync -avzLPh --exclude="obj*" --exclude="gen/" \
           $COBALT_SRC/out/raspi-2_debug pi@$RASPI_ADDR:~/
     ```
 
@@ -135,9 +108,9 @@
     to quit or restart Cobalt.
 
     ```
-    ssh pi@$RASPI_ADDR
-    cd raspi-2_debug
-    ./cobalt
+    $ ssh pi@$RASPI_ADDR
+    $ cd raspi-2_debug
+    $ ./cobalt
     ```
 
     With this approach, you can just hit `[CTRL-C]` to close Cobalt. If you
@@ -147,21 +120,3 @@
     Note that you can also exit YouTube on Cobalt by hitting the `[Esc]` key
     enough times to bring up the "Do you want to quit YouTube?" dialog and
     selecting "yes".
-
-### Improving Cobalt performance on Raspberry Pi
-
-1.  You will find that there are some processes installed by default that run on the
-    Raspberry Pi and can take away CPU time from Cobalt. You may wish to consider
-    disabling these processes for maximum (and more consistent) performance, as they
-    have been found to occasionally take >10% of the CPU according to `top`.
-    You can do this by typing:
-
-    ```
-    apt-get remove -y --auto-remove [PACKAGE_NAME, ...]
-    ```
-
-    For example:
-
-    ```
-    apt-get remove -y --auto-remove avahi-daemon
-    ```
diff --git a/cobalt/site/docs/gen/cobalt/doc/deep_links.md b/cobalt/site/docs/gen/cobalt/doc/deep_links.md
new file mode 100644
index 0000000..c73491f
--- /dev/null
+++ b/cobalt/site/docs/gen/cobalt/doc/deep_links.md
@@ -0,0 +1,139 @@
+---
+layout: doc
+title: "Cobalt Deep Links"
+---
+# Cobalt Deep Links
+
+- [Cobalt Deep Links](#cobalt-deep-links)
+  - [Deep Links](#deep-links)
+  - [Web API](#web-api)
+  - [Platform (Starboard) API](#platform-starboard-api)
+  - [Behavior details](#behavior-details)
+## Deep Links
+
+For Cobalt, a deep link is a string that can be sent from the platform to an
+application running in Cobalt. Generally, it can be used as any string value
+signal, but typically deep links are used to specify a view, page, or content
+to be shown by the application. While these strings typically are URI formatted
+values, when deep link strings are received by Cobalt they are forwarded to the
+running application without separate validation or modification.
+
+Applications should interpret received deep links as superseding previous deep
+links. Deep links received by Cobalt in rapid succession are not guaranteed to
+all be delivered to the application. On a busy or slow device, intermediate
+deep links can be dropped before they are delivered to the application.
+
+The startup URL passed to Cobalt determines which application Cobalt will load.
+Web deep links intended as a signal to the application should not be sent to
+Cobalt as a startup URL because that would result in a different application
+being loaded. Since a deep link is a string that may originate from an
+untrusted source on the device, it should not be used directly to determine
+what application Cobalt will load.
+
+Deep links are made visible to applications by Cobalt with a Web API that is
+separate from the Location interface web API. Cobalt will never directly
+navigate as a result of a received deep link, even if the link matches the
+current application location, for example with a query or fragment identifier.
+Applications that wish to navigate as a result of incoming deep links should do
+so explicitly when they are received.
+
+## Web API
+
+The deep link Web API consists of two parts: The
+`h5vcc.runtime.initialDeepLink` property and `h5vcc.runtime.onDeepLink` event
+target.
+
+Applications can read the value of `initialDeepLink`, and/or they can use
+`h5vcc.runtime.onDeepLink.addListener(foo)` to register callback functions to
+be called when deep links are received.
+
+A deep link is considered 'consumed' when it is read from `initialDeepLink`, or
+when it is reported to a callback function registered to `onDeepLink`.
+
+The IDL for this Cobalt specific interface can be found in cobalt/h5vcc, and is
+repeated below.
+
+```
+interface H5vccRuntime {
+  readonly attribute DOMString initialDeepLink;
+  readonly attribute H5vccDeepLinkEventTarget onDeepLink;
+}
+interface H5vccDeepLinkEventTarget {
+  void addListener(H5vccDeepLinkEventCallback callback);
+};
+callback H5vccDeepLinkEventCallback = void(DOMString link);
+interface H5vcc {
+  readonly attribute H5vccRuntime runtime;
+}
+```
+
+## Platform (Starboard) API
+
+Deep links can be passed into Cobalt in two ways:
+ * As the 'Startup Link':
+   * When Cobalt is first started, a deep link can be passed in with the
+     initial event (either `kSbEventTypePreload` or `kSbEventTypeStart`). This
+     can be achieved by calling `Application::SetStartLink` or by using and
+     overload of `Application::Run` that has the 'link_data' parameter to start
+     Cobalt. The value passed in there is then passed into Cobalt via the
+     'link' member of the SbEventStartData event parameter, constructed in
+     `Application::CreateInitialEvent`.
+ * As a 'Deep Link Event':
+   * At any time while Cobalt is running, it can be sent as the string value
+     passed with a kSbEventTypeLink event. The `Application::Link` method can
+     be called to inject such an event.
+
+On many platforms, the 'Startup Link' value can also be set with the `--link`
+command-line parameter (See `kLinkSwitch` in `Application::Run`).
+
+The `Application` class mentioned above can be found at
+`starboard/shared/starboard/application.cc`.
+
+## Behavior details
+
+Both the 'Startup Link' and 'Deep Link Event' values are treated the same by
+Cobalt: A 'Startup Link' is treated as a 'Deep Link Event' that was received
+immediately at startup. For the application, it is transparent whether a deep
+link was received as a 'Startup Link' or arrived from a 'Deep Link Event'. Deep
+link values of either type are made available as soon as they are known, with
+the same Web API interface.
+
+The most recently received deep link is remembered by Cobalt until it is
+consumed by the application. This includes deep links received by Cobalt while
+the application is still being fetched and loaded, including during page
+redirects or reloads, and after the application is loaded, if it has not
+consumed the deep link.
+
+Deep link values are considered consumed when the application either reads them
+from the `initialDeepLink` attribute or receives them in a callback to an
+`onDeepLink` listener. An application can use either or both reads of
+`initialDeepLink` or `onDeepLink` listeners to consume the most recently
+received deep link value.
+
+Calls to `onDeepLink` listeners are done as soon as deep links are available.
+Specifically, they can be called before `document.onreadystatechange`,
+`document.onload` & `window.onload` event handlers are called. As a result,
+deep link values can be consumed in synchronously loaded JavaScript that
+executes before the `document.onload` event.
+
+Until the first `onDeepLink` listener is added, the `initialDeepLink` property
+will return the most recently received deep link value. When an `onDeepLink`
+listener is added, the `initialDeepLink` value will no longer be updated, even
+when additional deep link events are received subsequently.
+
+An application can decide to never register an `onDeepLink` listener and poll
+the `initialDeepLink` value instead. This will then always return the value of
+the most recently received deep link.
+
+An application can decide to register an `onDeepLink` listener without reading
+the `initialDeepLink` value. Upon registering, the most recently received deep
+link, which may be the 'Startup Link' or from a 'Deep Link Event', will be
+reported to the listener.
+
+If a deep link value is consumed, it will not be made available again if the
+page is navigated (e.g. redirected or reloaded). When a deep link is consumed
+before a page redirect or reload, the deep link will not be repeated later.
+
+If a deep link value is not consumed, it will be made available again if the
+page is navigated (e.g. redirected or reloaded). A deep link will not be lost
+if a page redirect or reload is done without consuming it.
diff --git a/cobalt/speech/BUILD.gn b/cobalt/speech/BUILD.gn
index b072bc4..8251374 100644
--- a/cobalt/speech/BUILD.gn
+++ b/cobalt/speech/BUILD.gn
@@ -29,14 +29,16 @@
 }
 
 copy("speech_testdata") {
+  install_content = true
+
   sources = [
-    "//cobalt/speech/testdata/audio1.raw",
-    "//cobalt/speech/testdata/audio2.raw",
-    "//cobalt/speech/testdata/audio3.raw",
-    "//cobalt/speech/testdata/quit.raw",
+    "testdata/audio1.raw",
+    "testdata/audio2.raw",
+    "testdata/audio3.raw",
+    "testdata/quit.raw",
   ]
 
-  outputs = [ "$root_build_dir/content/test/{{source_root_relative_dir}}/{{source_file_part}}" ]
+  outputs = [ "$sb_static_contents_output_data_dir/test/{{source_root_relative_dir}}/{{source_file_part}}" ]
 }
 
 static_library("speech") {
diff --git a/cobalt/speech/sandbox/BUILD.gn b/cobalt/speech/sandbox/BUILD.gn
new file mode 100644
index 0000000..bc8c99e
--- /dev/null
+++ b/cobalt/speech/sandbox/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is a sample sandbox application for experimenting with the Cobalt
+# Speech API.
+
+target(final_executable_type, "speech_sandbox") {
+  sources = [
+    "audio_loader.cc",
+    "audio_loader.h",
+    "speech_sandbox.cc",
+    "speech_sandbox.h",
+    "speech_sandbox_main.cc",
+  ]
+
+  deps = [
+    "//cobalt/audio",
+    "//cobalt/base",
+    "//cobalt/debug:console_command_manager",
+    "//cobalt/dom",
+    "//cobalt/loader",
+    "//cobalt/network",
+    "//cobalt/script",
+    "//cobalt/script:engine",
+    "//cobalt/speech",
+    "//cobalt/trace_event",
+    "//url",
+  ]
+
+  deps += cobalt_platform_dependencies
+}
diff --git a/cobalt/storage/store/BUILD.gn b/cobalt/storage/store/BUILD.gn
index f9c895f..852d03a 100644
--- a/cobalt/storage/store/BUILD.gn
+++ b/cobalt/storage/store/BUILD.gn
@@ -46,4 +46,6 @@
     "//third_party/protobuf:protobuf_lite",
     "//url",
   ]
+
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/cobalt/test/BUILD.gn b/cobalt/test/BUILD.gn
index 9b8f32a..77c79aa 100644
--- a/cobalt/test/BUILD.gn
+++ b/cobalt/test/BUILD.gn
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-static_library("run_all_unittests") {
+source_set("run_all_unittests") {
   testonly = true
   sources = [ "run_all_unittests.cc" ]
   public_deps = [ "//base/test:test_support" ]
diff --git a/cobalt/updater/BUILD.gn b/cobalt/updater/BUILD.gn
new file mode 100644
index 0000000..83f69c1
--- /dev/null
+++ b/cobalt/updater/BUILD.gn
@@ -0,0 +1,87 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+static_library("updater") {
+  sources = [
+    "configurator.cc",
+    "configurator.h",
+    "network_fetcher.cc",
+    "network_fetcher.h",
+    "patcher.cc",
+    "patcher.h",
+    "prefs.cc",
+    "prefs.h",
+    "unzipper.cc",
+    "unzipper.h",
+    "updater_constants.cc",
+    "updater_constants.h",
+    "updater_module.cc",
+    "updater_module.h",
+    "utils.cc",
+    "utils.h",
+  ]
+
+  deps = [
+    "//base",
+    "//cobalt/base",
+    "//cobalt/browser:browser_switches",
+    "//cobalt/loader",
+    "//cobalt/network",
+    "//cobalt/script",
+    "//cobalt/script:engine",
+    "//components/crx_file",
+    "//components/prefs",
+    "//components/update_client",
+    "//crypto",
+    "//net",
+    "//starboard:starboard_headers_only",
+    "//starboard/common",
+    "//starboard/loader_app:app_key_files",
+    "//starboard/loader_app:drain_file",
+    "//third_party/zlib:zip",
+    "//url",
+  ]
+
+  # TODO(b/213388707): resolve the dependency cycle and remove this exception.
+  allow_circular_includes_from = [ "//components/update_client" ]
+}
+
+target(final_executable_type, "updater_sandbox") {
+  sources = [
+    "updater.cc",
+    "updater.h",
+    "updater_sandbox.cc",
+  ]
+
+  deps = [
+    ":updater",
+    "//base",
+    "//cobalt/debug:console_command_manager",
+    "//cobalt/network",
+    "//components/crx_file",
+    "//components/prefs",
+    "//components/update_client",
+    "//starboard",
+  ]
+}
+
+target(final_executable_type, "crash_sandbox") {
+  sources = [ "crash_sandbox.cc" ]
+  deps = [ "//starboard" ]
+}
+
+target(final_executable_type, "noop_sandbox") {
+  sources = [ "noop_sandbox.cc" ]
+  deps = [ "//starboard" ]
+}
diff --git a/cobalt/updater/OWNERS b/cobalt/updater/OWNERS
new file mode 100644
index 0000000..5a5b03e
--- /dev/null
+++ b/cobalt/updater/OWNERS
@@ -0,0 +1,4 @@
+borisv@chromium.org
+ganesh@chromium.org
+sorin@chromium.org
+waffles@chromium.org
diff --git a/cobalt/updater/configurator.cc b/cobalt/updater/configurator.cc
index 8f184c2..e823bbf 100644
--- a/cobalt/updater/configurator.cc
+++ b/cobalt/updater/configurator.cc
@@ -251,6 +251,16 @@
   updater_status_ = status;
 }
 
+void Configurator::SetMinFreeSpaceBytes(uint64_t bytes) {
+  base::AutoLock auto_lock(const_cast<base::Lock&>(min_free_space_bytes_lock_));
+  min_free_space_bytes_ = bytes;
+}
+
+uint64_t Configurator::GetMinFreeSpaceBytes() {
+  base::AutoLock auto_lock(const_cast<base::Lock&>(min_free_space_bytes_lock_));
+  return min_free_space_bytes_;
+}
+
 std::string Configurator::GetPreviousUpdaterStatus() const {
   base::AutoLock auto_lock(
       const_cast<base::Lock&>(previous_updater_status_lock_));
diff --git a/cobalt/updater/configurator.h b/cobalt/updater/configurator.h
index 5314984..b7ed9e7 100644
--- a/cobalt/updater/configurator.h
+++ b/cobalt/updater/configurator.h
@@ -1,6 +1,16 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
+// Copyright 2019 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_UPDATER_CONFIGURATOR_H_
 #define COBALT_UPDATER_CONFIGURATOR_H_
@@ -85,6 +95,9 @@
   std::string GetPreviousUpdaterStatus() const override;
   void SetPreviousUpdaterStatus(const std::string& status) override;
 
+  void SetMinFreeSpaceBytes(uint64_t bytes) override;
+  uint64_t GetMinFreeSpaceBytes() override;
+
  private:
   friend class base::RefCountedThreadSafe<Configurator>;
   ~Configurator() override;
@@ -102,6 +115,8 @@
   std::string previous_updater_status_;
   base::Lock previous_updater_status_lock_;
   std::string user_agent_string_;
+  uint64_t min_free_space_bytes_ = 64 * 1024 * 1024;
+  base::Lock min_free_space_bytes_lock_;
 
   DISALLOW_COPY_AND_ASSIGN(Configurator);
 };
diff --git a/cobalt/updater/updater_module.cc b/cobalt/updater/updater_module.cc
index e2e4001..a9f9e6c 100644
--- a/cobalt/updater/updater_module.cc
+++ b/cobalt/updater/updater_module.cc
@@ -388,5 +388,12 @@
   return index;
 }
 
+void UpdaterModule::SetMinFreeSpaceBytes(uint64_t bytes) {
+  LOG(INFO) << "UpdaterModule::SetMinFreeSpaceBytes bytes=" << bytes;
+  if (updater_configurator_) {
+    updater_configurator_->SetMinFreeSpaceBytes(bytes);
+  }
+}
+
 }  // namespace updater
 }  // namespace cobalt
diff --git a/cobalt/updater/updater_module.h b/cobalt/updater/updater_module.h
index 353215e..0d6f793 100644
--- a/cobalt/updater/updater_module.h
+++ b/cobalt/updater/updater_module.h
@@ -151,6 +151,8 @@
 
   int GetInstallationIndex() const;
 
+  void SetMinFreeSpaceBytes(uint64_t bytes);
+
  private:
   std::unique_ptr<base::Thread> updater_thread_;
   scoped_refptr<update_client::UpdateClient> update_client_;
diff --git a/cobalt/webdriver/BUILD.gn b/cobalt/webdriver/BUILD.gn
index 1b35d40..2367a96 100644
--- a/cobalt/webdriver/BUILD.gn
+++ b/cobalt/webdriver/BUILD.gn
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-config("webdriver_public_config") {
+config("webdriver_all_dependent_config") {
   defines = [ "ENABLE_WEBDRIVER" ]
 }
 
@@ -104,14 +104,49 @@
       "window_driver.h",
     ]
     deps += [ ":copy_webdriver_data" ]
-    public_configs = [ ":webdriver_public_config" ]
+    all_dependent_configs = [ ":webdriver_all_dependent_config" ]
   }
 }
 
-# TODO: This declares content_deploy_subdirs which should be migrated for
-#       install targets.
 copy("copy_webdriver_data") {
+  install_content = true
   sources = [ "content/webdriver-init.js" ]
   outputs =
       [ "$sb_static_contents_output_data_dir/webdriver/webdriver-init.js" ]
 }
+
+copy("webdriver_copy_test_data") {
+  sources = [
+    "testdata/displayed_test.html",
+    "testdata/map.png",
+    "testdata/simple_test.py",
+  ]
+
+  outputs = [ "$sb_static_contents_output_data_dir/test/cobalt/webdriver_test/{{source_file_part}}" ]
+}
+
+target(gtest_target_type, "webdriver_test") {
+  testonly = true
+
+  sources = [
+    "execute_test.cc",
+    "get_element_text_test.cc",
+    "is_displayed_test.cc",
+    "keyboard_test.cc",
+  ]
+
+  deps = [
+    ":webdriver",
+    ":webdriver_copy_test_data",
+    "//cobalt/base",
+    "//cobalt/browser",
+    "//cobalt/css_parser:css_parser",
+    "//cobalt/cssom:cssom",
+    "//cobalt/dom/testing:dom_testing",
+    "//cobalt/script",
+    "//cobalt/test:run_all_unittests",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/devtools:devtools_all_files",
+  ]
+}
diff --git a/cobalt/websocket/BUILD.gn b/cobalt/websocket/BUILD.gn
index e50677e..469ef27 100644
--- a/cobalt/websocket/BUILD.gn
+++ b/cobalt/websocket/BUILD.gn
@@ -54,6 +54,7 @@
     "//cobalt/base",
     "//cobalt/dom",
     "//cobalt/dom:dom_exception",
+    "//cobalt/dom/testing:dom_testing",
     "//cobalt/network",
     "//cobalt/script",
     "//cobalt/test:run_all_unittests",
@@ -69,4 +70,6 @@
   if (!is_gold) {
     deps += [ "//cobalt/debug" ]
   }
+
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/cobalt/worker/BUILD.gn b/cobalt/worker/BUILD.gn
new file mode 100644
index 0000000..4d2336a
--- /dev/null
+++ b/cobalt/worker/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+static_library("worker") {
+  # Creates cycle with //cobalt/dom
+  check_includes = false
+
+  sources = [
+    "service_worker_container.cc",
+    "service_worker_container.h",
+  ]
+
+  public_deps = [
+    # Additionally, ensure that the include directories for generated
+    # headers are put on the include directories for targets that depend
+    # on this one.
+    "//cobalt/browser:generated_types",
+  ]
+}
diff --git a/cobalt/worker/navigator.idl b/cobalt/worker/navigator.idl
new file mode 100644
index 0000000..1eb4987
--- /dev/null
+++ b/cobalt/worker/navigator.idl
@@ -0,0 +1,19 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://w3c.github.io/ServiceWorker/#navigator-serviceworker
+
+partial interface Navigator {
+    readonly attribute ServiceWorkerContainer serviceWorker;
+};
diff --git a/cobalt/worker/service_worker_container.cc b/cobalt/worker/service_worker_container.cc
new file mode 100644
index 0000000..7b47b46
--- /dev/null
+++ b/cobalt/worker/service_worker_container.cc
@@ -0,0 +1,32 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/worker/service_worker_container.h"
+
+namespace cobalt {
+namespace worker {
+
+ServiceWorkerContainer::ServiceWorkerContainer(
+    script::ScriptValueFactory* script_value_factory)
+    : script_value_factory_(script_value_factory) {}
+
+script::Handle<script::Promise<void>> ServiceWorkerContainer::Register() {
+  LOG(INFO) << "The service worker is registered";
+  script::Handle<script::Promise<void>> promise =
+      script_value_factory_->CreateBasicPromise<void>();
+  return promise;
+}
+
+}  // namespace worker
+}  // namespace cobalt
diff --git a/cobalt/worker/service_worker_container.h b/cobalt/worker/service_worker_container.h
new file mode 100644
index 0000000..ed523f8
--- /dev/null
+++ b/cobalt/worker/service_worker_container.h
@@ -0,0 +1,44 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_WORKER_SERVICE_WORKER_CONTAINER_H_
+#define COBALT_WORKER_SERVICE_WORKER_CONTAINER_H_
+
+#include <memory>
+
+#include "cobalt/script/promise.h"
+#include "cobalt/script/script_value.h"
+#include "cobalt/script/script_value_factory.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace worker {
+
+class ServiceWorkerContainer : public script::Wrappable {
+ public:
+  explicit ServiceWorkerContainer(
+      script::ScriptValueFactory* script_value_factory);
+
+  script::Handle<script::Promise<void>> Register();
+
+  DEFINE_WRAPPABLE_TYPE(ServiceWorkerContainer);
+
+ private:
+  script::ScriptValueFactory* script_value_factory_;
+};
+
+}  // namespace worker
+}  // namespace cobalt
+
+#endif  // COBALT_WORKER_SERVICE_WORKER_CONTAINER_H_
diff --git a/cobalt/worker/service_worker_container.idl b/cobalt/worker/service_worker_container.idl
new file mode 100644
index 0000000..170e226
--- /dev/null
+++ b/cobalt/worker/service_worker_container.idl
@@ -0,0 +1,20 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://w3c.github.io/ServiceWorker/#serviceworkercontainer-interface
+
+[Exposed=Window]
+interface ServiceWorkerContainer {
+    Promise<void> register();
+};
diff --git a/cobalt/worker/testdata/service-worker.html b/cobalt/worker/testdata/service-worker.html
new file mode 100644
index 0000000..45916cb
--- /dev/null
+++ b/cobalt/worker/testdata/service-worker.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    // Demonstrates how the navigator.serviceWorker api can be used
+    const serviceWorker = navigator.serviceWorker;
+    serviceWorker.register().then(registration => {
+        console.log("success!");
+    }, err => {
+        console.error("Installing the worker failed!", err);
+    });
+  </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/cobalt/worker/worker.gyp b/cobalt/worker/worker.gyp
new file mode 100644
index 0000000..1d3458e
--- /dev/null
+++ b/cobalt/worker/worker.gyp
@@ -0,0 +1,35 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+  'targets': [
+    {
+      'target_name': 'worker',
+      'type': 'static_library',
+      'sources': [
+        'service_worker_container.cc',
+        'service_worker_container.h',
+      ],
+      'dependencies': [
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+      ],
+      'export_dependent_settings': [
+        # Additionally, ensure that the include directories for generated
+        # headers are put on the include directories for targets that depend
+        # on this one.
+        '<(DEPTH)/cobalt/browser/browser_bindings_gen.gyp:generated_types',
+      ]
+    },
+  ],
+}
diff --git a/cobalt/xhr/BUILD.gn b/cobalt/xhr/BUILD.gn
index fd7f33c..45c43a3 100644
--- a/cobalt/xhr/BUILD.gn
+++ b/cobalt/xhr/BUILD.gn
@@ -40,7 +40,7 @@
   if (enable_xhr_header_filtering && !sb_is_evergreen) {
     sources = [ "xhr_modify_headers.h" ]
     defines = [ "COBALT_ENABLE_XHR_HEADER_FILTERING" ]
-    deps += [ cobalt_platform_dependencies ]
+    deps += cobalt_platform_dependencies
   }
 }
 
@@ -53,6 +53,7 @@
     "//cobalt/base",
     "//cobalt/dom",
     "//cobalt/dom:dom_exception",
+    "//cobalt/dom/testing:dom_testing",
     "//cobalt/test:run_all_unittests",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/components/client_update_protocol/BUILD.gn b/components/client_update_protocol/BUILD.gn
new file mode 100644
index 0000000..6e0ddf2
--- /dev/null
+++ b/components/client_update_protocol/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2021 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.
+
+static_library("client_update_protocol") {
+  sources = [
+    "ecdsa.cc",
+    "ecdsa.h",
+  ]
+  deps = [
+    "//base",
+    "//crypto",
+  ]
+}
diff --git a/components/client_update_protocol/OWNERS b/components/client_update_protocol/OWNERS
new file mode 100644
index 0000000..c184691
--- /dev/null
+++ b/components/client_update_protocol/OWNERS
@@ -0,0 +1,4 @@
+sorin@chromium.org
+waffles@chromium.org
+# COMPONENT: Internals>Installer
+# TEAM: chrome-updates-dev@chromium.org
diff --git a/components/crx_file/BUILD.gn b/components/crx_file/BUILD.gn
new file mode 100644
index 0000000..2b103d2
--- /dev/null
+++ b/components/crx_file/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2021 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("//third_party/protobuf/proto_library.gni")
+
+# The accompanying crx_creator target has been left behind during the migration
+# from GYP to GN because it introduces some -Wc++11-narrowing compiler errors
+# and isn't actually used in Cobalt. If it's at some point needed we can
+# likely pull in the narrowing fixes made by the Chromium team in
+# https://source.chromium.org/chromium/chromium/src/+/379c52be13901beae4f773fe9e8054ad42a186c4.
+static_library("crx_file") {
+  sources = [
+    "crx3.pb.cc",
+    "crx3.pb.h",
+    "crx_file.h",
+    "crx_verifier.cc",
+    "crx_verifier.h",
+    "id_util.cc",
+    "id_util.h",
+  ]
+  deps = [
+    "//base",
+    "//crypto",
+    "//third_party/protobuf:protobuf_lite",
+  ]
+}
diff --git a/components/crx_file/OWNERS b/components/crx_file/OWNERS
new file mode 100644
index 0000000..9562f1b
--- /dev/null
+++ b/components/crx_file/OWNERS
@@ -0,0 +1,5 @@
+file://extensions/OWNERS
+
+waffles@chromium.org
+sorin@chromium.org
+# COMPONENT: Platform>Extensions
diff --git a/components/prefs/OWNERS b/components/prefs/OWNERS
new file mode 100644
index 0000000..ef0ab79
--- /dev/null
+++ b/components/prefs/OWNERS
@@ -0,0 +1,5 @@
+battre@chromium.org
+gab@chromium.org
+
+# COMPONENT: UI>Browser>Preferences
+# TEAM: chromium-dev@chromium.org
diff --git a/components/test/data/update_client/OWNERS b/components/test/data/update_client/OWNERS
new file mode 100644
index 0000000..2780b5b
--- /dev/null
+++ b/components/test/data/update_client/OWNERS
@@ -0,0 +1,7 @@
+cpu@chromium.org
+laforge@chromium.org
+mal@chromium.org
+sorin@chromium.org
+waffles@chromium.org
+
+# COMPONENT: Internals>Installer>Components
diff --git a/components/update_client/BUILD.gn b/components/update_client/BUILD.gn
new file mode 100644
index 0000000..e2ccd9b
--- /dev/null
+++ b/components/update_client/BUILD.gn
@@ -0,0 +1,252 @@
+# Copyright 2021 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("update_client_config") {
+  defines = [
+    "LIBXML_READER_ENABLED",
+    "LIBXML_WRITER_ENABLED",
+  ]
+}
+
+static_library("update_client") {
+  sources = [
+    "action_runner.cc",
+    "action_runner.h",
+    "activity_data_service.h",
+    "cobalt_slot_management.cc",
+    "cobalt_slot_management.h",
+    "command_line_config_policy.cc",
+    "command_line_config_policy.h",
+    "component.cc",
+    "component.h",
+    "component_patcher.cc",
+    "component_patcher.h",
+    "component_patcher_operation.cc",
+    "component_patcher_operation.h",
+    "component_unpacker.cc",
+    "component_unpacker.h",
+    "configurator.h",
+    "crx_downloader.cc",
+    "crx_downloader.h",
+    "crx_update_item.h",
+    "network.cc",
+    "network.h",
+    "patcher.h",
+    "persisted_data.cc",
+    "persisted_data.h",
+    "ping_manager.cc",
+    "ping_manager.h",
+    "protocol_definition.cc",
+    "protocol_definition.h",
+    "protocol_handler.cc",
+    "protocol_handler.h",
+    "protocol_parser.cc",
+    "protocol_parser.h",
+    "protocol_parser_json.cc",
+    "protocol_parser_json.h",
+    "protocol_serializer.cc",
+    "protocol_serializer.h",
+    "protocol_serializer_json.cc",
+    "protocol_serializer_json.h",
+    "request_sender.cc",
+    "request_sender.h",
+    "task.h",
+    "task_send_uninstall_ping.cc",
+    "task_send_uninstall_ping.h",
+    "task_traits.h",
+    "task_update.cc",
+    "task_update.h",
+    "unzipper.h",
+    "update_checker.cc",
+    "update_checker.h",
+    "update_client.cc",
+    "update_client.h",
+    "update_client_errors.h",
+    "update_client_internal.h",
+    "update_engine.cc",
+    "update_engine.h",
+    "update_query_params.cc",
+    "update_query_params.h",
+    "update_query_params_delegate.cc",
+    "update_query_params_delegate.h",
+    "updater_state.cc",
+    "updater_state.h",
+    "url_fetcher_downloader.cc",
+    "url_fetcher_downloader.h",
+    "utils.cc",
+    "utils.h",
+  ]
+
+  configs += [ ":update_client_config" ]
+
+  deps = [
+    "//components/client_update_protocol",
+    "//components/crx_file",
+    "//components/prefs",
+    "//crypto",
+    "//net",
+    "//starboard/loader_app:app_key_files",
+    "//starboard/loader_app:drain_file",
+    "//third_party/libxml",
+    "//url",
+  ]
+}
+
+static_library("test_support") {
+  testonly = true
+
+  sources = [
+    "net/network_cobalt.h",
+    "net/network_impl_cobalt.cc",
+    "net/network_impl_cobalt.h",
+    "net/url_request_post_interceptor.cc",
+    "net/url_request_post_interceptor.h",
+    "patch/patch_impl_cobalt.cc",
+    "patch/patch_impl_cobalt.h",
+    "test_configurator.cc",
+    "test_configurator.h",
+    "unzip/unzip_impl_cobalt.cc",
+    "unzip/unzip_impl_cobalt.h",
+  ]
+
+  deps = [
+    ":update_client",
+    "//base",
+    "//cobalt/base",
+    "//cobalt/debug:console_command_manager",
+    "//cobalt/loader",
+    "//cobalt/network",
+    "//components/prefs",
+    "//net",
+    "//net:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/zlib:zip",
+    "//url",
+  ]
+}
+
+copy("update_client_test_files") {
+  sources = [
+    "//components/test/data/update_client/ChromeRecovery.crx3",
+    "//components/test/data/update_client/binary_bsdiff_patch.bin",
+    "//components/test/data/update_client/binary_courgette_patch.bin",
+    "//components/test/data/update_client/binary_input.bin",
+    "//components/test/data/update_client/binary_output.bin",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc.pem",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1/a_changing_binary_file",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1/a_changing_text_file",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1/a_static_text_file",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1/manifest.json",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1to2/commands.json",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1to2/f0",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1to2/f1",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1to2/f2",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad/commands.json",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad/f1",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad/f2",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad/f3",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_2/a_changing_binary_file",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_2/a_changing_text_file",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_2/a_static_text_file",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc/ihfokbkgjpifnbbojhneepfflplebdkc_2/manifest.json",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx",
+    "//components/test/data/update_client/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx",
+    "//components/test/data/update_client/jebgalgnebhfojomionfpkfelancnnkf.crx",
+    "//components/test/data/update_client/jebgalgnebhfojomionfpkfelancnnkf.pem",
+    "//components/test/data/update_client/jebgalgnebhfojomionfpkfelancnnkf/component1.dll",
+    "//components/test/data/update_client/jebgalgnebhfojomionfpkfelancnnkf/manifest.json",
+    "//components/test/data/update_client/runaction_test_win.crx3",
+    "//components/test/data/update_client/updatecheck_reply_1.json",
+    "//components/test/data/update_client/updatecheck_reply_4.json",
+    "//components/test/data/update_client/updatecheck_reply_noupdate.json",
+    "//components/test/data/update_client/updatecheck_reply_parse_error.json",
+    "//components/test/data/update_client/updatecheck_reply_unknownapp.json",
+  ]
+
+  outputs = [ "$sb_static_contents_output_data_dir/test/{{source_root_relative_dir}}/{{source_file_part}}" ]
+}
+
+target(gtest_target_type, "update_client_test") {
+  testonly = true
+
+  sources = [
+    "component_unpacker_unittest.cc",
+
+    # TODO: enable the tests commented out
+    "crx_downloader_unittest.cc",
+    "persisted_data_unittest.cc",
+    "ping_manager_unittest.cc",
+    "protocol_parser_json_unittest.cc",
+
+    # "protocol_serializer_json_unittest.cc",
+    "protocol_serializer_unittest.cc",
+    "request_sender_unittest.cc",
+    "update_checker_unittest.cc",
+
+    # "update_client_unittest.cc",
+    "update_query_params_unittest.cc",
+    "updater_state_unittest.cc",
+    "utils_unittest.cc",
+  ]
+
+  deps = [
+    ":test_support",
+    ":update_client",
+    ":update_client_test_files",
+    "//cobalt/base",
+    "//cobalt/test:run_all_unittests",
+    "//cobalt/updater",
+    "//components/crx_file",
+    "//components/prefs",
+    "//components/prefs:test_support",
+    "//crypto",
+    "//net",
+    "//net:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//url",
+  ]
+}
+
+target(gtest_target_type, "cobalt_slot_management_test") {
+  testonly = true
+
+  sources = [
+    "//starboard/common/test_main.cc",
+    "//starboard/loader_app/system_get_extension_shim.cc",
+    "cobalt_slot_management_test.cc",
+  ]
+
+  deps = [
+    ":update_client",
+    "//cobalt/base",
+    "//cobalt/updater",
+    "//components/crx_file",
+    "//components/prefs",
+    "//components/prefs:test_support",
+    "//crypto",
+    "//net:test_support",
+    "//starboard/loader_app",
+    "//starboard/loader_app:app_key_files",
+    "//starboard/loader_app:drain_file",
+    "//starboard/loader_app:installation_manager",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+
+  content_deps = [ "//third_party/icu:icudata" ]
+}
diff --git a/components/update_client/OWNERS b/components/update_client/OWNERS
new file mode 100644
index 0000000..b68ef3b
--- /dev/null
+++ b/components/update_client/OWNERS
@@ -0,0 +1,6 @@
+cpu@chromium.org
+sorin@chromium.org
+waffles@chromium.org
+
+# COMPONENT: Internals>Installer>Components
+# TEAM: chrome-updates-dev@chromium.org
diff --git a/components/update_client/cobalt_slot_management.cc b/components/update_client/cobalt_slot_management.cc
index 0c601d7..d220b74 100644
--- a/components/update_client/cobalt_slot_management.cc
+++ b/components/update_client/cobalt_slot_management.cc
@@ -14,8 +14,10 @@
 
 #include "components/update_client/cobalt_slot_management.h"
 
+#include <algorithm>
 #include <vector>
 
+#include "base/files/file_util.h"
 #include "base/values.h"
 #include "cobalt/updater/utils.h"
 #include "components/update_client/utils.h"
@@ -37,6 +39,32 @@
   return !bad_app_key_file_path.empty() &&
          SbFileExists(bad_app_key_file_path.c_str());
 }
+
+uint64_t ComputeSlotSize(
+    const CobaltExtensionInstallationManagerApi* installation_api,
+    int index) {
+  if (!installation_api) {
+    LOG(WARNING) << "ComputeSlotSize: "
+                 << "Missing installation manager extension.";
+    return 0;
+  }
+  std::vector<char> installation_path(kSbFileMaxPath);
+  if (installation_api->GetInstallationPath(index, installation_path.data(),
+                                            kSbFileMaxPath) == IM_EXT_ERROR) {
+    LOG(WARNING) << "ComputeSlotSize: "
+                 << "Failed to get installation path for slot " << index;
+    return 0;
+  }
+  int64_t slot_size =
+      base::ComputeDirectorySize(base::FilePath(installation_path.data()));
+  LOG(INFO) << "ComputeSlotSize: slot_size=" << slot_size;
+  if (slot_size <= 0) {
+    LOG(WARNING) << "ComputeSlotSize: "
+                 << "Failed to compute slot " << index << " size";
+    return 0;
+  }
+  return slot_size;
+}
 }  // namespace
 
 CobaltSlotManagement::CobaltSlotManagement() : installation_api_(nullptr) {}
@@ -277,4 +305,52 @@
   return false;
 }
 
+bool CobaltSkipUpdate(
+    const CobaltExtensionInstallationManagerApi* installation_api,
+    uint64_t min_free_space_bytes,
+    int64_t free_space_bytes,
+    uint64_t installation_cleanup_size) {
+  LOG(INFO) << "CobaltSkipUpdate: "
+            << " min_free_space_bytes=" << min_free_space_bytes
+            << " free_space_bytes=" << free_space_bytes
+            << " installation_cleanup_size=" << installation_cleanup_size;
+
+  if (free_space_bytes < 0) {
+    LOG(WARNING) << "CobaltSkipUpdate: "
+                 << "Unable to determine free space";
+    return false;
+  }
+
+  if (free_space_bytes + installation_cleanup_size < min_free_space_bytes) {
+    LOG(WARNING) << "CobaltSkipUpdate: Not enough free space";
+    return true;
+  }
+
+  return false;
+}
+
+uint64_t CobaltInstallationCleanupSize(
+    const CobaltExtensionInstallationManagerApi* installation_api) {
+  if (!installation_api) {
+    LOG(WARNING) << "CobaltInstallationCleanupSize: "
+                 << "Missing installation manager extension.";
+    return 0;
+  }
+  int max_slots = installation_api->GetMaxNumberInstallations();
+  if (max_slots == IM_EXT_ERROR) {
+    LOG(ERROR)
+        << "CobaltInstallationCleanupSize: Failed to get max number of slots.";
+    return 0;
+  }
+  // Ignore the system slot 0 and start with slot 1.
+  uint64_t min_slot_size = ComputeSlotSize(installation_api, 1);
+  for (int i = 2; i < max_slots; i++) {
+    uint64_t slot_size = ComputeSlotSize(installation_api, i);
+    if (slot_size < min_slot_size) {
+      min_slot_size = slot_size;
+    }
+  }
+
+  return min_slot_size;
+}
 }  // namespace update_client
diff --git a/components/update_client/cobalt_slot_management.h b/components/update_client/cobalt_slot_management.h
index e5d185c..167d628 100644
--- a/components/update_client/cobalt_slot_management.h
+++ b/components/update_client/cobalt_slot_management.h
@@ -76,6 +76,22 @@
     const CobaltExtensionInstallationManagerApi* installation_api,
     const base::Version& current_version);
 
+// Computes whether Cobalt should skip the update based on the
+// |min_free_space_bytes|, the |free_space_bytes| and the amount of
+// space that can be recovered from |installation_cleanup_size|.
+// The default behavior is to NOT skip unless the measurements
+// show that there isn't enough space to perform the update.
+bool CobaltSkipUpdate(
+    const CobaltExtensionInstallationManagerApi* installation_api,
+    uint64_t min_free_space_bytes,
+    int64_t free_space_bytes,
+    uint64_t installation_cleanup_size);
+
+// Computes the installation cleanup size by taking the min space
+// from all the installation slots excluding the system slot 0.
+uint64_t CobaltInstallationCleanupSize(
+    const CobaltExtensionInstallationManagerApi* installation_api);
+
 }  // namespace update_client
 
 #endif  // COMPONENTS_UPDATE_CLIENT_COBALT_SLOT_MANAGEMENT_H_
diff --git a/components/update_client/cobalt_slot_management_test.cc b/components/update_client/cobalt_slot_management_test.cc
index 55256de..01aeb9a 100644
--- a/components/update_client/cobalt_slot_management_test.cc
+++ b/components/update_client/cobalt_slot_management_test.cc
@@ -14,7 +14,11 @@
 
 #include "components/update_client/cobalt_slot_management.h"
 
+#include <algorithm>
+#include <vector>
+
 #include "base/strings/string_util.h"
+#include "cobalt/extension/free_space.h"
 #include "starboard/common/file.h"
 #include "starboard/loader_app/app_key_files.h"
 #include "starboard/loader_app/drain_file.h"
@@ -45,7 +49,7 @@
 
 class CobaltSlotManagementTest : public testing::Test {
  protected:
-  virtual void SetUp() override {
+  void SetUp() override {
     std::vector<char> buf(kSbFileMaxPath);
     storage_path_implemented_ = SbSystemGetPath(kSbSystemPathStorageDirectory,
                                                 buf.data(), kSbFileMaxPath);
@@ -63,7 +67,7 @@
             kCobaltExtensionInstallationManagerName));
   }
 
-  virtual void TearDown() override {
+  void TearDown() override {
     starboard::SbFileDeleteRecursive(storage_path_.c_str(), true);
     ImUninitialize();
   }
@@ -229,6 +233,55 @@
   base::Version version("1.0.0");
   ASSERT_FALSE(CobaltQuickUpdate(api_, version));
 }
+
+TEST_F(CobaltSlotManagementTest, CobaltSkipUpdateNoInstallationMoreSpace) {
+  ASSERT_FALSE(
+      CobaltSkipUpdate(api_, 1024 /* min */, 1025 /* free */, 0 /* cleanup */));
+}
+
+TEST_F(CobaltSlotManagementTest, CobaltSkipUpdateNoInstallationExactSpace) {
+  ASSERT_FALSE(
+      CobaltSkipUpdate(api_, 1024 /* min */, 1024 /* free */, 0 /* cleanup */));
+}
+
+TEST_F(CobaltSlotManagementTest, CobaltSkipUpdateNoInstallationNotEnoughSpace) {
+  ASSERT_TRUE(
+      CobaltSkipUpdate(api_, 1024 /* min */, 1023 /* free */, 0 /* cleanup */));
+}
+
+TEST_F(CobaltSlotManagementTest, CobaltSkipUpdateWithInstallationMoreSpace) {
+  ASSERT_FALSE(
+      CobaltSkipUpdate(api_, 1024 /* min */, 1024 /* free */, 1 /* cleanup */));
+}
+
+TEST_F(CobaltSlotManagementTest, CobaltSkipUpdateWithInstallationExactSpace) {
+  ASSERT_FALSE(
+      CobaltSkipUpdate(api_, 1024 /* min */, 1023 /* free */, 1 /* cleanup */));
+}
+
+TEST_F(CobaltSlotManagementTest,
+       CobaltSkipUpdateWithInstallationNotEnoughSpace) {
+  ASSERT_TRUE(
+      CobaltSkipUpdate(api_, 1024 /* min */, 1022 /* free */, 1 /* cleanup */));
+}
+
+TEST_F(CobaltSlotManagementTest, CobaltInstallationCleanupSizeNoInstallation) {
+  uint64_t size = CobaltInstallationCleanupSize(api_);
+  ASSERT_EQ(size, 0);
+}
+
+TEST_F(CobaltSlotManagementTest,
+       CobaltInstallationCleanupSizeTwoInstallations) {
+  int len1 = strlen(kManifestV1);
+  int len2 = strlen(kManifestV2);
+  ASSERT_NE(len1, len2);
+
+  CreateManifest("installation_1", kManifestV2, len1);
+  CreateManifest("installation_2", kManifestV1, len2);
+
+  uint64_t size = CobaltInstallationCleanupSize(api_);
+  ASSERT_EQ(size, std::min(len1, len2));
+}
 }  // namespace
 
 }  // namespace update_client
diff --git a/components/update_client/component.cc b/components/update_client/component.cc
index 8b4fa5d..6570995 100644
--- a/components/update_client/component.cc
+++ b/components/update_client/component.cc
@@ -215,8 +215,14 @@
     // When there is an error unpacking the downloaded CRX, such as a failure to
     // verify the package, we should remember to clear out any drain files.
     if (base::DirectoryExists(crx_path.DirName())) {
-      CobaltSlotManagement cobalt_slot_management;
-      cobalt_slot_management.CleanupAllDrainFiles(crx_path.DirName());
+      const auto installation_api =
+        static_cast<const CobaltExtensionInstallationManagerApi*>(
+          SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
+      if (installation_api) {
+        CobaltSlotManagement cobalt_slot_management;
+        cobalt_slot_management.Init(installation_api);
+        cobalt_slot_management.CleanupAllDrainFiles(crx_path.DirName());
+      }
     }
 #endif
     main_task_runner->PostTask(
diff --git a/components/update_client/configurator.h b/components/update_client/configurator.h
index 16fd982..e4619e9 100644
--- a/components/update_client/configurator.h
+++ b/components/update_client/configurator.h
@@ -180,6 +180,8 @@
   // Compare and swap the is_channel_changed flag.
   virtual void CompareAndSwapChannelChanged(int old_value, int new_value) = 0;
 
+  virtual void SetMinFreeSpaceBytes(uint64_t bytes) = 0;
+  virtual uint64_t GetMinFreeSpaceBytes() = 0;
 #endif
 
  protected:
diff --git a/components/update_client/test_configurator.h b/components/update_client/test_configurator.h
index 6a9e5eb..9b37687 100644
--- a/components/update_client/test_configurator.h
+++ b/components/update_client/test_configurator.h
@@ -135,6 +135,10 @@
 
   std::string GetPreviousUpdaterStatus() const override { return ""; }
   void SetPreviousUpdaterStatus(const std::string& status) override {}
+
+  void SetMinFreeSpaceBytes(uint64_t bytes) override {}
+
+  uint64_t GetMinFreeSpaceBytes() override { return 0; }
 #else
   network::TestURLLoaderFactory* test_url_loader_factory() {
     return &test_url_loader_factory_;
diff --git a/components/update_client/update_checker.cc b/components/update_client/update_checker.cc
index 4951abf..d0d77a6 100644
--- a/components/update_client/update_checker.cc
+++ b/components/update_client/update_checker.cc
@@ -22,11 +22,11 @@
 #include "base/threading/thread_checker.h"
 #if defined(STARBOARD)
 #include "base/threading/thread_id_name_manager.h"
+#include "cobalt/extension/free_space.h"
 #endif
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #if defined(STARBOARD)
-#include "cobalt/extension/installation_manager.h"
 #include "cobalt/updater/utils.h"
 #include "components/update_client/cobalt_slot_management.h"
 #endif
@@ -102,7 +102,8 @@
       const base::flat_map<std::string, std::string>& additional_attributes,
       bool enabled_component_updates);
 #if defined(STARBOARD)
-  void Cancel();
+  void Cancel() ;
+  virtual bool SkipUpdate(const CobaltExtensionInstallationManagerApi* installation_api);
 #endif
   void OnRequestSenderComplete(int error,
                                const std::string& response,
@@ -228,6 +229,23 @@
 
     base::Version current_version = crx_component->version;
 #if defined(STARBOARD)
+    // Check if there is an available update already for quick roll-forward
+    auto installation_api =
+        static_cast<const CobaltExtensionInstallationManagerApi*>(
+            SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
+    if (!installation_api) {
+      LOG(ERROR) << "UpdaterChecker: "
+                 << "Failed to get installation manager extension.";
+      return;
+    }
+
+    if (SkipUpdate(installation_api)) {
+      LOG(WARNING) << "UpdaterChecker is skipping";
+      UpdateCheckFailed(ErrorCategory::kUpdateCheck,
+        static_cast<int>(UpdateCheckError::OUT_OF_SPACE), -1);
+      return;
+    }
+
     std::string unpacked_version =
         GetPersistedData()->GetLastUnpackedVersion(app_id);
     // If the version of the last unpacked update package is higher than the
@@ -238,15 +256,6 @@
       current_version = base::Version(unpacked_version);
     }
 
-    // Check if there is an available update already for quick roll-forward
-    auto installation_api =
-        static_cast<const CobaltExtensionInstallationManagerApi*>(
-            SbSystemGetExtension(kCobaltExtensionInstallationManagerName));
-    if (!installation_api) {
-      LOG(ERROR) << "Failed to get installation manager extension.";
-      return;
-    }
-
     if (CobaltQuickUpdate(installation_api, current_version)) {
       // The last parameter in UpdateCheckFailed below, which is to be passed to
       // update_check_callback_, indicates a throttling by the update server.
@@ -305,6 +314,24 @@
     request_sender_->Cancel();
   }
 }
+
+bool UpdateCheckerImpl::SkipUpdate(
+  const CobaltExtensionInstallationManagerApi* installation_api) {
+  auto free_space_ext = static_cast<const CobaltExtensionFreeSpaceApi*>(
+      SbSystemGetExtension(kCobaltExtensionFreeSpaceName));
+  if (!installation_api) {
+    LOG(WARNING) << "UpdaterChecker::SkipUpdate: missing installation api";
+     return false;
+  }
+  if (!free_space_ext) {
+    LOG(WARNING) << "UpdaterChecker::SkipUpdate: No FreeSpace Cobalt extension";
+    return false;
+  }
+
+  return CobaltSkipUpdate(installation_api, config_->GetMinFreeSpaceBytes(),
+     free_space_ext->MeasureFreeSpace(kSbSystemPathStorageDirectory),
+     CobaltInstallationCleanupSize(installation_api)) ;
+}
 #endif
 
 void UpdateCheckerImpl::OnRequestSenderComplete(int error,
diff --git a/components/update_client/update_checker.h b/components/update_client/update_checker.h
index 78eb968..7c7c062 100644
--- a/components/update_client/update_checker.h
+++ b/components/update_client/update_checker.h
@@ -18,6 +18,10 @@
 #include "components/update_client/protocol_parser.h"
 #include "url/gurl.h"
 
+#if defined(STARBOARD)
+#include "cobalt/extension/installation_manager.h"
+#endif
+
 namespace update_client {
 
 class Configurator;
@@ -53,6 +57,7 @@
 
 #if defined(STARBOARD)
   virtual void Cancel() = 0;
+  virtual bool SkipUpdate(const CobaltExtensionInstallationManagerApi* installation_api) = 0;
 #endif
 
   static std::unique_ptr<UpdateChecker> Create(
diff --git a/components/update_client/update_client.cc b/components/update_client/update_client.cc
index 0ba76cd..4d6dfb8 100644
--- a/components/update_client/update_client.cc
+++ b/components/update_client/update_client.cc
@@ -79,7 +79,8 @@
   DCHECK(tasks_.empty());
 
 #if defined(STARBOARD)
-  LOG(INFO) << "UpdateClientImpl::~UpdateClientImpl: task_queue_.size=" << task_queue_.size() << " tasks.size=" << tasks_.size();
+  LOG(INFO) << "UpdateClientImpl::~UpdateClientImpl: task_queue_.size="
+            << task_queue_.size() << " tasks.size=" << tasks_.size();
 #endif
 
   config_ = nullptr;
diff --git a/components/update_client/update_client_errors.h b/components/update_client/update_client_errors.h
index db08a24..4193ace 100644
--- a/components/update_client/update_client_errors.h
+++ b/components/update_client/update_client_errors.h
@@ -105,6 +105,7 @@
   // Using 21 that doesn't conflict with the exsiting error codes and stays away
   // from the other codes below 20.
   QUICK_ROLL_FORWARD = 21,
+  OUT_OF_SPACE = 22,
 };
 #endif
 
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn
index 3eff4fb..4c4de15 100644
--- a/crypto/BUILD.gn
+++ b/crypto/BUILD.gn
@@ -148,6 +148,7 @@
 
 target(gtest_target_type, "crypto_unittests") {
   testonly = true
+
   sources = [
     "aead_unittest.cc",
     "ec_private_key_unittest.cc",
@@ -187,6 +188,8 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+
+  content_deps = [ "//third_party/icu:icudata" ]
 }
 
 # This has no sources in some cases so can't be a static library.
diff --git a/crypto/DEPS b/crypto/DEPS
new file mode 100644
index 0000000..859cc4e
--- /dev/null
+++ b/crypto/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/boringssl/src/include",
+]
diff --git a/crypto/OWNERS b/crypto/OWNERS
new file mode 100644
index 0000000..963d05e
--- /dev/null
+++ b/crypto/OWNERS
@@ -0,0 +1,6 @@
+agl@chromium.org
+davidben@chromium.org
+rsleevi@chromium.org
+
+# TEAM: net-dev@chromium.org
+# COMPONENT: Internals>Network>SSL
diff --git a/docker-compose.yml b/docker-compose.yml
index 075e283..3d9b34b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -37,6 +37,7 @@
 x-shared-build-env: &shared-build-env
   NINJA_PARALLEL: ${NINJA_PARALLEL:-32}
   IS_CI: ${IS_CI:-0}
+  IS_DOCKER: 1
 
 services:
 #### Tools
@@ -201,7 +202,6 @@
     depends_on: [ build-android ]
     environment:
       <<: *shared-build-env
-      IS_DOCKER: 1
       PLATFORM: android-x86
       CONFIG: ${CONFIG:-debug}
 
@@ -214,7 +214,6 @@
       dockerfile: ./gn/Dockerfile
     environment:
       <<: *shared-build-env
-      IS_DOCKER: 1
       PLATFORM: android-x86
       CONFIG: ${CONFIG:-debug}
       TARGET_CPU: ${TARGET_CPU:-x86}
@@ -225,7 +224,6 @@
     depends_on: [ build-android ]
     environment:
       <<: *shared-build-env
-      IS_DOCKER: 1
       PLATFORM: android-arm
       CONFIG: ${CONFIG:-debug}
 
@@ -238,7 +236,6 @@
       dockerfile: ./gn/Dockerfile
     environment:
       <<: *shared-build-env
-      IS_DOCKER: 1
       PLATFORM: android-arm
       CONFIG: ${CONFIG:-debug}
       TARGET_CPU: ${TARGET_CPU:-arm}
@@ -249,7 +246,6 @@
     depends_on: [ build-android ]
     environment:
       <<: *shared-build-env
-      IS_DOCKER: 1
       PLATFORM: android-arm64
       CONFIG: ${CONFIG:-debug}
 
@@ -262,7 +258,6 @@
       dockerfile: ./gn/Dockerfile
     environment:
       <<: *shared-build-env
-      IS_DOCKER: 1
       PLATFORM: android-arm64
       CONFIG: ${CONFIG:-debug}
       TARGET_CPU: ${TARGET_CPU:-arm64}
diff --git a/docker/linux/android/Dockerfile b/docker/linux/android/Dockerfile
index 12e5b53..eb26f78 100644
--- a/docker/linux/android/Dockerfile
+++ b/docker/linux/android/Dockerfile
@@ -16,6 +16,7 @@
 
 RUN apt update -qqy \
     && apt install -qqy --no-install-recommends \
+        libxml2-dev \
         default-jdk \
         g++-multilib \
     && /opt/clean-after-apt.sh
diff --git a/docker/linux/base/build/Dockerfile b/docker/linux/base/build/Dockerfile
index 1289fcb..7c464bb 100644
--- a/docker/linux/base/build/Dockerfile
+++ b/docker/linux/base/build/Dockerfile
@@ -81,5 +81,15 @@
     && echo ${CLANG_VER} >> ${TC_HOME}/cr_build_revision \
     && rm clang-${CLANG_VER}.tgz
 
+#  === Install portable sccache binary
+ARG SCCACHE=sccache-dist-v0.2.15-x86_64-unknown-linux-musl.tar.gz
+RUN cd /tmp \
+    && curl -L -O https://github.com/mozilla/sccache/releases/download/v0.2.15/${SCCACHE} \
+    && tar xvzf ${SCCACHE} -C /usr/local/bin --strip-components=1 \
+    && mv /usr/local/bin/sccache-dist /usr/local/bin/sccache \
+    && chmod +x /usr/local/bin/sccache \
+    && rm -rf ${SCCACHE} \
+    && sccache --version
+
 WORKDIR /code
 CMD ["/usr/bin/python","--version"]
diff --git a/download_resources.py b/download_resources.py
index acd91dd..688f4e8 100644
--- a/download_resources.py
+++ b/download_resources.py
@@ -17,14 +17,13 @@
 import logging
 import os
 import platform
-
-import tools.download_from_gcs as download_from_gcs
-
+import subprocess
 try:
-  import download_resources_internal
+  import urllib.request as urllib
 except ImportError:
-  logging.warning('Skipping internal tools.')
-  download_resources_internal = None
+  import urllib2 as urllib
+
+from tools import download_from_gcs
 
 
 def DownloadClangFormat(force=False):
@@ -46,12 +45,38 @@
                                              clang_format_sha_path, force)
 
 
+def DownloadGerritCommitMsgHook(force=False):
+  git_dir = subprocess.check_output(['git', 'rev-parse', '--git-common-dir'
+                                    ]).strip().decode('utf-8')
+  git_commit_msg_hook_path = os.path.join(git_dir, 'hooks', 'commit-msg')
+
+  if not force and os.path.exists(git_commit_msg_hook_path):
+    logging.info('commit-msg hook found, skipping download.')
+    return
+
+  hook_url = 'https://gerrit-review.googlesource.com/tools/hooks/commit-msg'
+  try:
+    res = urllib.urlopen(hook_url)
+  except urllib.URLError:
+    # pylint:disable=import-outside-toplevel
+    from ssl import _create_unverified_context
+    context = _create_unverified_context()
+    res = urllib.urlopen(hook_url, context=context)
+
+  if not res:
+    logging.error('Could not fetch %s', hook_url)
+    return
+
+  with open(git_commit_msg_hook_path, 'wb') as fd:
+    fd.write(res.read())
+  download_from_gcs.AddExecutableBits(git_commit_msg_hook_path)
+  logging.info('Gerrit commit-msg hook installed.')
+
+
 if __name__ == '__main__':
   logging_format = '[%(levelname)s:%(filename)s:%(lineno)s] %(message)s'
   logging.basicConfig(
       level=logging.INFO, format=logging_format, datefmt='%H:%M:%S')
 
   DownloadClangFormat()
-
-  if download_resources_internal:
-    download_resources_internal.main()
+  DownloadGerritCommitMsgHook()
diff --git a/glimp/.gitattributes b/glimp/.gitattributes
new file mode 100644
index 0000000..0f70607
--- /dev/null
+++ b/glimp/.gitattributes
@@ -0,0 +1,30 @@
+# These files are text and should be normalized (convert crlf > lf).
+*.bat                   text eol=lf
+*.cc                    text eol=lf
+*.cg                    text eol=lf
+*.cpp                   text eol=lf
+*.css                   text eol=lf
+*.gyp                   text eol=lf
+*.gypi                  text eol=lf
+*.h                     text eol=lf
+*.html                  text eol=lf
+*.idl                   text eol=lf
+*.js                    text eol=lf
+*.pump                  text eol=lf
+*.py                    text eol=lf
+*.sublime-project       text eol=lf
+*.sublime-workspace     text eol=lf
+*.template              text eol=lf
+*.txt                   text eol=lf
+*.y                     text eol=lf
+.clang-format           text eol=lf
+codereview.settings     text eol=lf
+gyp_cobalt              text eol=lf
+
+# Images should be treated as binary files.
+*.exe                   binary
+*.jpg                   binary
+*.mp4                   binary
+*.png                   binary
+*.pyc                   binary
+*.ttf                   binary
diff --git a/glimp/BUILD.gn b/glimp/BUILD.gn
new file mode 100644
index 0000000..cd7670f
--- /dev/null
+++ b/glimp/BUILD.gn
@@ -0,0 +1,113 @@
+# Copyright 2021 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("glimp_config") {
+  include_dirs = [ "include" ]
+  defines = [
+    # There doesn't appear to be any way to use the C preprocessor to do
+    # string concatenation with the / character. This prevents us from using
+    # the preprocessor to assemble an include file path, so we have to do
+    # the concatenation here in GYP.
+    # http://stackoverflow.com/questions/29601786/c-preprocessor-building-a-path-string
+    "GLIMP_EGLPLATFORM_INCLUDE=\"../../$target_platform/eglplatform_public.h\"",
+    "GLIMP_KHRPLATFORM_INCLUDE=\"../../$target_platform/khrplatform_public.h\"",
+
+    # Uncomment the define below to enable and use tracing inside glimp.
+    # "ENABLE_GLIMP_TRACING",
+  ]
+}
+
+group("glimp") {
+  public_configs = [ ":glimp_config" ]
+  deps = [ "//glimp/${target_platform}:glimp_platform" ]
+}
+
+config("glimp_common_sources_public_config") {
+  configs = [ ":glimp_config" ]
+  include_dirs = [ "$target_platform/platform" ]
+}
+
+source_set("glimp_common_sources") {
+  check_includes = false
+  sources = [
+    "egl/attrib_map.cc",
+    "egl/attrib_map.h",
+    "egl/config.cc",
+    "egl/config.h",
+    "egl/display.cc",
+    "egl/display.h",
+    "egl/display_impl.h",
+    "egl/display_registry.cc",
+    "egl/display_registry.h",
+    "egl/error.cc",
+    "egl/error.h",
+    "egl/get_proc_address_impl.h",
+    "egl/scoped_egl_lock.cc",
+    "egl/scoped_egl_lock.h",
+    "egl/surface.cc",
+    "egl/surface.h",
+    "egl/surface_impl.h",
+    "entry_points/egl.cc",
+    "entry_points/egl_ext.cc",
+    "entry_points/gles_2_0.cc",
+    "entry_points/gles_2_0_ext.cc",
+    "entry_points/gles_3_0.cc",
+    "gles/blend_state.h",
+    "gles/buffer.cc",
+    "gles/buffer.h",
+    "gles/buffer_impl.h",
+    "gles/context.cc",
+    "gles/context.h",
+    "gles/context_impl.h",
+    "gles/convert_pixel_data.cc",
+    "gles/convert_pixel_data.h",
+    "gles/cull_face_state.h",
+    "gles/draw_mode.h",
+    "gles/draw_state.cc",
+    "gles/draw_state.h",
+    "gles/framebuffer.cc",
+    "gles/framebuffer.h",
+    "gles/index_data_type.h",
+    "gles/pixel_format.cc",
+    "gles/pixel_format.h",
+    "gles/program.cc",
+    "gles/program.h",
+    "gles/program_impl.h",
+    "gles/ref_counted_resource_map.h",
+    "gles/renderbuffer.cc",
+    "gles/renderbuffer.h",
+    "gles/resource_manager.cc",
+    "gles/resource_manager.h",
+    "gles/sampler.h",
+    "gles/shader.cc",
+    "gles/shader.h",
+    "gles/shader_impl.h",
+    "gles/texture.cc",
+    "gles/texture.h",
+    "gles/texture_impl.h",
+    "gles/uniform_info.h",
+    "gles/unique_id_generator.cc",
+    "gles/unique_id_generator.h",
+    "gles/vertex_attribute.h",
+    "shaders/glsl_shader_map_helpers.h",
+    "shaders/hash_glsl_source.cc",
+    "shaders/hash_glsl_source.h",
+  ]
+  public_configs = [ ":glimp_common_sources_public_config" ]
+  deps = [
+    "//glimp/tracing",
+    "//nb",
+    "//starboard:starboard_headers_only",
+  ]
+}
diff --git a/glimp/shaders/generate_glsl_shader_map.py b/glimp/shaders/generate_glsl_shader_map.py
index 2ce1841..2f4efd1 100644
--- a/glimp/shaders/generate_glsl_shader_map.py
+++ b/glimp/shaders/generate_glsl_shader_map.py
@@ -1,5 +1,5 @@
 #!/usr/bin/python
-# Copyright 2016 Google Inc. All Rights Reserved.
+# 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.
@@ -12,7 +12,6 @@
 # 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.
-
 """Associates GLSL files with platform-specific shaders based on filename.
 
 This script scans all input files and detects whether they are GLSL files or
@@ -54,9 +53,10 @@
   """Returns a string containing C++ that represents all file data in file."""
   with open(input_file, 'rb') as f:
     file_contents = f.read()
-    def chunks(contents, chunk_size):
+
+    def Chunks(contents, chunk_size):
       """ Yield successive |chunk_size|-sized chunks from |contents|."""
-      for i in xrange(0, len(contents), chunk_size):
+      for i in range(0, len(contents), chunk_size):
         yield contents[i:i + chunk_size]
 
     # Break up the data into chunk sizes such that the produced output lines
@@ -68,9 +68,9 @@
     # Convert each byte to ASCII hexadecimal form and output that to the C++
     # header file, line-by-line.
     data_definition_string = '{\n'
-    for output_line_data in chunks(file_contents, chunk_size):
+    for output_line_data in Chunks(file_contents, chunk_size):
       data_definition_string += (
-        ' '.join(['0x%02x,' % ord(y) for y in output_line_data]) + '\n')
+          ' '.join(['0x%02x,' % ord(y) for y in output_line_data]) + '\n')
     data_definition_string += '};\n\n'
     return data_definition_string
 
@@ -81,8 +81,8 @@
   data_definition_string = ''
   for path in files:
     input_file_variable_name = GetBasename(path)
-    data_definition_string += (
-        'const uint8_t %s[] =\n' % input_file_variable_name)
+    data_definition_string += ('const uint8_t %s[] =\n' %
+                               input_file_variable_name)
     data_definition_string += GetHeaderDataDefinitionStringForFile(path)
 
   return data_definition_string
@@ -114,7 +114,6 @@
   return generate_map_function_string
 
 
-
 def GetShaderNameFunctionString(hash_to_shader_map):
   """Generate C++ code to retrieve the shader name from a GLSL hash.
 
@@ -133,9 +132,8 @@
 
   for k, v in hash_to_shader_map.iteritems():
     input_file_variable_name = GetBasename(v)
-    get_shader_name_function_string += (
-        '    case %uU: return \"%s\";\n' %
-        (k, input_file_variable_name))
+    get_shader_name_function_string += ('    case %uU: return \"%s\";\n' %
+                                        (k, input_file_variable_name))
 
   get_shader_name_function_string += '  }\n  return NULL;\n}'
   return get_shader_name_function_string
@@ -167,6 +165,7 @@
 #endif  // {include_guard}
 """
 
+
 def GenerateHeaderFileOutput(output_file_name, hash_to_shader_map):
   """Generate the actual C++ header file code.
 
@@ -181,13 +180,13 @@
   with open(output_file_name, 'w') as output_file:
     output_file.write(
         HEADER_FILE_TEMPLATE.format(
-            include_guard = include_guard,
-            data_definitions = GetHeaderDataDefinitionString(
-                                   hash_to_shader_map.values()),
-            generate_map_function = GetGenerateMapFunctionString(
-                                        hash_to_shader_map),
-            shader_name_function = GetShaderNameFunctionString(
-                                       hash_to_shader_map)))
+            include_guard=include_guard,
+            data_definitions=GetHeaderDataDefinitionString(
+                hash_to_shader_map.values()),
+            generate_map_function=GetGenerateMapFunctionString(
+                hash_to_shader_map),
+            shader_name_function=GetShaderNameFunctionString(
+                hash_to_shader_map)))
 
 
 def AssociateGLSLFilesWithPlatformFiles(all_shaders):
@@ -205,6 +204,7 @@
     A dictionary mapping GLSL shader filenames to platform-specific shader
     filenames.
   """
+
   def IsGLSL(filename):
     return os.path.splitext(filename)[1].upper() == '.GLSL'
 
@@ -225,12 +225,13 @@
     if not IsGLSL(item):
       basename = GetBasename(item)
       if not basename in basename_to_glsl_file:
-        raise Exception(
-            'Platform-specific file ' + item + ' has no GLSL match.')
+        raise Exception('Platform-specific file ' + item +
+                        ' has no GLSL match.')
       glsl_file = basename_to_glsl_file[basename]
       if glsl_file in mapped_files:
-        raise Exception('Multiple platform-specific files match the same GLSL '
-                        + 'file with basename ' + basename)
+        raise Exception(
+            'Multiple platform-specific files match the same GLSL ' +
+            'file with basename ' + basename)
       mapped_files[glsl_file] = item
 
   return mapped_files
@@ -255,6 +256,7 @@
 
   def AddUint32(a, b):
     return (a + b) & 0xffffffff
+
   def XorUint32(a, b):
     return (a ^ b) & 0xffffffff
 
@@ -263,7 +265,7 @@
   # Introduce a generator function for returning only the hashable characters.
   def GetHashableCharacters(hash_string):
     str_without_comments = re.sub(r'//.*\n', '', hash_string)
-    str_without_whitespace = re.sub(r'[ \n\t]', '', str_without_comments);
+    str_without_whitespace = re.sub(r'[ \n\t]', '', str_without_comments)
     str_without_empty_braces = str_without_whitespace.replace('{}', '')
 
     for c in str_without_empty_braces:
@@ -287,8 +289,8 @@
   for k, v in glsl_to_shader_map.iteritems():
     hashed_glsl = HashGLSLShaderFile(k)
     if hashed_glsl in hash_shader_map:
-      raise Exception('Hash collision between GLSL files ' + k + ' and '
-                      + hash_to_glsl_map[hashed_glsl] + '.')
+      raise Exception('Hash collision between GLSL files ' + k + ' and ' +
+                      hash_to_glsl_map[hashed_glsl] + '.')
     hash_to_glsl_map[hashed_glsl] = k
     hash_shader_map[hashed_glsl] = v
   return hash_shader_map
@@ -304,18 +306,19 @@
   with open(input_files_filename) as input_files_file:
     files = [x.strip() for x in input_files_file.readlines()]
 
-  return [os.path.join(input_files_dir, x) for x in files]
+  # We filter out files that already have a full path (the case in GN).
+  return [os.path.join(input_files_dir, x) for x in files if '/' not in x]
 
 
 def main(output_path, input_files_filename, input_files_dir):
   all_shaders = GetAllShaderFiles(input_files_filename, input_files_dir)
 
-  glsl_to_shader_map = AssociateGLSLFilesWithPlatformFiles(
-      all_shaders)
+  glsl_to_shader_map = AssociateGLSLFilesWithPlatformFiles(all_shaders)
 
   hash_to_shader_map = CreateHashToShaderMap(glsl_to_shader_map)
 
   GenerateHeaderFileOutput(output_path, hash_to_shader_map)
 
+
 if __name__ == '__main__':
   main(sys.argv[1], sys.argv[2], sys.argv[3])
diff --git a/glimp/tracing/BUILD.gn b/glimp/tracing/BUILD.gn
new file mode 100644
index 0000000..d9605c1
--- /dev/null
+++ b/glimp/tracing/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2021 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.
+
+static_library("tracing") {
+  sources = [
+    "tracing.cc",
+    "tracing.h",
+  ]
+  configs += [ "//glimp:glimp_config" ]
+}
diff --git a/nb/BUILD.gn b/nb/BUILD.gn
index 2e19f21..6e9c036 100644
--- a/nb/BUILD.gn
+++ b/nb/BUILD.gn
@@ -68,6 +68,10 @@
       "//starboard:starboard_headers_only",
       "//starboard/common",
     ]
+
+    if (defined(has_nb_platform) && has_nb_platform) {
+      deps += [ "//nb/${target_platform}:nb_platform" ]
+    }
   }
 }
 
@@ -101,5 +105,7 @@
       "//testing/gmock:gmock",
       "//testing/gtest",
     ]
+
+    content_deps = [ "//third_party/icu:icudata" ]
   }
 }
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 5ca254f..d27cb0b 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2895,7 +2895,7 @@
     "data/url_fetcher_impl_unittest/simple.html",
   ]
 
-  outputs = [ "$root_build_dir/content/test/{{source_root_relative_dir}}/{{source_file_part}}" ]
+  outputs = [ "$sb_static_contents_output_data_dir/test/{{source_root_relative_dir}}/{{source_file_part}}" ]
 }
 
 copy("third_party_unittest_files") {
@@ -3480,746 +3480,746 @@
     "third_party/nist-pkits/crls/WrongCRLCACRL.crl",
   ]
 
-  outputs = [ "$root_build_dir/content/test/{{source_root_relative_dir}}/{{source_file_part}}" ]
+  outputs = [ "$sb_static_contents_output_data_dir/test/{{source_root_relative_dir}}/{{source_file_part}}" ]
 }
 
-if (!is_win) {
-  target(gtest_target_type, "net_unittests") {
-    testonly = true
+target(gtest_target_type, "net_unittests") {
+  testonly = true
 
-    sources = [
-      "base/address_family_unittest.cc",
-      "base/address_list_unittest.cc",
-      "base/arena_unittest.cc",
-      "base/backoff_entry_serializer_unittest.cc",
+  sources = [
+    "base/address_family_unittest.cc",
+    "base/address_list_unittest.cc",
+    "base/arena_unittest.cc",
+    "base/backoff_entry_serializer_unittest.cc",
 
-      # Previously excluded via sources/.
-      # "base/backoff_entry_unittest.cc",
-      "base/chunked_upload_data_stream_unittest.cc",
-      "base/data_url_unittest.cc",
-      "base/datagram_buffer_unittest.cc",
-      "base/directory_listing_unittest.cc",
-      "base/elements_upload_data_stream_unittest.cc",
-      "base/escape_unittest.cc",
-      "base/expiring_cache_unittest.cc",
-      "base/file_stream_unittest.cc",
-      "base/filename_util_unittest.cc",
-      "base/hex_utils_test.cc",
-      "base/host_mapping_rules_unittest.cc",
-      "base/host_port_pair_unittest.cc",
-      "base/interval_set_test.cc",
-      "base/interval_test.cc",
-      "base/ip_address_unittest.cc",
-      "base/ip_endpoint_unittest.cc",
-      "base/ip_pattern_unittest.cc",
-      "base/layered_network_delegate_unittest.cc",
-      "base/lookup_string_in_fixed_set_unittest.cc",
-      "base/mime_sniffer_unittest.cc",
-      "base/mime_util_unittest.cc",
-      "base/net_string_util_unittest.cc",
-      "base/network_activity_monitor_unittest.cc",
-      "base/network_change_notifier_unittest.cc",
-      "base/parse_number_unittest.cc",
-      "base/port_util_unittest.cc",
-      "base/prioritized_dispatcher_unittest.cc",
-      "base/prioritized_task_runner_unittest.cc",
-      "base/priority_queue_unittest.cc",
-      "base/registry_controlled_domains/registry_controlled_domain_unittest.cc",
-      "base/static_cookie_policy_unittest.cc",
-      "base/test_completion_callback_unittest.cc",
-      "base/upload_bytes_element_reader_unittest.cc",
-      "base/upload_file_element_reader_unittest.cc",
-      "base/url_util_unittest.cc",
-      "cert/caching_cert_verifier_unittest.cc",
-      "cert/cert_verifier_unittest.cc",
+    # Previously excluded via sources/.
+    # "base/backoff_entry_unittest.cc",
+    "base/chunked_upload_data_stream_unittest.cc",
+    "base/data_url_unittest.cc",
+    "base/datagram_buffer_unittest.cc",
+    "base/directory_listing_unittest.cc",
+    "base/elements_upload_data_stream_unittest.cc",
+    "base/escape_unittest.cc",
+    "base/expiring_cache_unittest.cc",
+    "base/file_stream_unittest.cc",
+    "base/filename_util_unittest.cc",
+    "base/hex_utils_test.cc",
+    "base/host_mapping_rules_unittest.cc",
+    "base/host_port_pair_unittest.cc",
+    "base/interval_set_test.cc",
+    "base/interval_test.cc",
+    "base/ip_address_unittest.cc",
+    "base/ip_endpoint_unittest.cc",
+    "base/ip_pattern_unittest.cc",
+    "base/layered_network_delegate_unittest.cc",
+    "base/lookup_string_in_fixed_set_unittest.cc",
+    "base/mime_sniffer_unittest.cc",
+    "base/mime_util_unittest.cc",
+    "base/net_string_util_unittest.cc",
+    "base/network_activity_monitor_unittest.cc",
+    "base/network_change_notifier_unittest.cc",
+    "base/parse_number_unittest.cc",
+    "base/port_util_unittest.cc",
+    "base/prioritized_dispatcher_unittest.cc",
+    "base/prioritized_task_runner_unittest.cc",
+    "base/priority_queue_unittest.cc",
+    "base/registry_controlled_domains/registry_controlled_domain_unittest.cc",
+    "base/static_cookie_policy_unittest.cc",
+    "base/test_completion_callback_unittest.cc",
+    "base/upload_bytes_element_reader_unittest.cc",
+    "base/upload_file_element_reader_unittest.cc",
+    "base/url_util_unittest.cc",
+    "cert/caching_cert_verifier_unittest.cc",
+    "cert/cert_verifier_unittest.cc",
 
-      # Previously excluded via sources/.
-      # "cert/cert_verify_proc_unittest.cc",
-      "cert/crl_set_unittest.cc",
-      "cert/ct_log_response_parser_unittest.cc",
-      "cert/ct_log_verifier_unittest.cc",
-      "cert/ct_objects_extractor_unittest.cc",
-      "cert/ct_serialization_unittest.cc",
-      "cert/ev_root_ca_metadata_unittest.cc",
-      "cert/internal/cert_issuer_source_aia_unittest.cc",
-      "cert/internal/cert_issuer_source_static_unittest.cc",
-      "cert/internal/cert_issuer_source_sync_unittest.h",
-      "cert/internal/certificate_policies_unittest.cc",
-      "cert/internal/extended_key_usage_unittest.cc",
+    # Previously excluded via sources/.
+    # "cert/cert_verify_proc_unittest.cc",
+    "cert/crl_set_unittest.cc",
+    "cert/ct_log_response_parser_unittest.cc",
+    "cert/ct_log_verifier_unittest.cc",
+    "cert/ct_objects_extractor_unittest.cc",
+    "cert/ct_serialization_unittest.cc",
+    "cert/ev_root_ca_metadata_unittest.cc",
+    "cert/internal/cert_issuer_source_aia_unittest.cc",
+    "cert/internal/cert_issuer_source_static_unittest.cc",
+    "cert/internal/cert_issuer_source_sync_unittest.h",
+    "cert/internal/certificate_policies_unittest.cc",
+    "cert/internal/extended_key_usage_unittest.cc",
 
-      # "cert/x509_util_nss_unittest.cc",
-      "cert/internal/general_names_unittest.cc",
-      "cert/internal/name_constraints_unittest.cc",
-      "cert/internal/nist_pkits_unittest.cc",
-      "cert/internal/nist_pkits_unittest.h",
-      "cert/internal/ocsp_unittest.cc",
-      "cert/internal/parse_certificate_unittest.cc",
+    # "cert/x509_util_nss_unittest.cc",
+    "cert/internal/general_names_unittest.cc",
+    "cert/internal/name_constraints_unittest.cc",
+    "cert/internal/nist_pkits_unittest.cc",
+    "cert/internal/nist_pkits_unittest.h",
+    "cert/internal/ocsp_unittest.cc",
+    "cert/internal/parse_certificate_unittest.cc",
 
-      # <certt.h> not found.
-      # "cert_net/nss_ocsp_unittest.cc",
-      "cert/internal/parse_name_unittest.cc",
-      "cert/internal/parsed_certificate_unittest.cc",
+    # <certt.h> not found.
+    # "cert_net/nss_ocsp_unittest.cc",
+    "cert/internal/parse_name_unittest.cc",
+    "cert/internal/parsed_certificate_unittest.cc",
 
-      # <cert.h> not found.
-      # "cert/nss_cert_database_unittest.cc",
-      "cert/internal/path_builder_pkits_unittest.cc",
-      "cert/internal/path_builder_unittest.cc",
-      "cert/internal/path_builder_verify_certificate_chain_unittest.cc",
-      "cert/internal/revocation_checker_unittest.cc",
-      "cert/internal/signature_algorithm_unittest.cc",
-      "cert/internal/simple_path_builder_delegate_unittest.cc",
-      "cert/internal/test_helpers.cc",
-      "cert/internal/test_helpers.h",
-      "cert/internal/trust_store_collection_unittest.cc",
+    # <cert.h> not found.
+    # "cert/nss_cert_database_unittest.cc",
+    "cert/internal/path_builder_pkits_unittest.cc",
+    "cert/internal/path_builder_unittest.cc",
+    "cert/internal/path_builder_verify_certificate_chain_unittest.cc",
+    "cert/internal/revocation_checker_unittest.cc",
+    "cert/internal/signature_algorithm_unittest.cc",
+    "cert/internal/simple_path_builder_delegate_unittest.cc",
+    "cert/internal/test_helpers.cc",
+    "cert/internal/test_helpers.h",
+    "cert/internal/trust_store_collection_unittest.cc",
 
-      # "cert/internal/trust_store_nss_unittest.cc",
-      "cert/internal/verify_certificate_chain_pkits_unittest.cc",
-      "cert/internal/verify_certificate_chain_typed_unittest.h",
-      "cert/internal/verify_certificate_chain_unittest.cc",
-      "cert/internal/verify_name_match_unittest.cc",
-      "cert/internal/verify_signed_data_unittest.cc",
-      "cert/jwk_serializer_unittest.cc",
-      "cert/known_roots_unittest.cc",
-      "cert/merkle_audit_proof_unittest.cc",
-      "cert/merkle_tree_leaf_unittest.cc",
-      "cert/multi_threaded_cert_verifier_unittest.cc",
-      "cert/pem_tokenizer_unittest.cc",
-      "cert/signed_certificate_timestamp_unittest.cc",
-      "cert/symantec_certs_unittest.cc",
-      "cert/test_root_certs_unittest.cc",
-      "cert/x509_cert_types_unittest.cc",
-      "cert/x509_certificate_unittest.cc",
-      "cert/x509_util_unittest.cc",
-      "cert_net/cert_net_fetcher_impl_unittest.cc",
-      "cookies/canonical_cookie_unittest.cc",
-      "cookies/cookie_constants_unittest.cc",
-      "cookies/cookie_deletion_info_unittest.cc",
-      "cookies/cookie_monster_unittest.cc",
-      "cookies/cookie_util_unittest.cc",
-      "cookies/parsed_cookie_unittest.cc",
-      "der/encode_values_unittest.cc",
-      "der/input_unittest.cc",
-      "der/parse_values_unittest.cc",
-      "der/parser_unittest.cc",
-      "dns/dns_config_service_unittest.cc",
-      "dns/dns_hosts_unittest.cc",
-      "dns/dns_query_unittest.cc",
-      "dns/dns_response_unittest.cc",
-      "dns/dns_session_unittest.cc",
-      "dns/dns_socket_pool_unittest.cc",
-      "dns/dns_transaction_unittest.cc",
-      "dns/dns_util_unittest.cc",
-      "dns/host_cache_unittest.cc",
-      "dns/host_resolver_impl_unittest.cc",
-      "dns/mapped_host_resolver_unittest.cc",
-      "dns/record_parsed_unittest.cc",
-      "dns/record_rdata_unittest.cc",
-      "dns/serial_worker_unittest.cc",
+    # "cert/internal/trust_store_nss_unittest.cc",
+    "cert/internal/verify_certificate_chain_pkits_unittest.cc",
+    "cert/internal/verify_certificate_chain_typed_unittest.h",
+    "cert/internal/verify_certificate_chain_unittest.cc",
+    "cert/internal/verify_name_match_unittest.cc",
+    "cert/internal/verify_signed_data_unittest.cc",
+    "cert/jwk_serializer_unittest.cc",
+    "cert/known_roots_unittest.cc",
+    "cert/merkle_audit_proof_unittest.cc",
+    "cert/merkle_tree_leaf_unittest.cc",
+    "cert/multi_threaded_cert_verifier_unittest.cc",
+    "cert/pem_tokenizer_unittest.cc",
+    "cert/signed_certificate_timestamp_unittest.cc",
+    "cert/symantec_certs_unittest.cc",
+    "cert/test_root_certs_unittest.cc",
+    "cert/x509_cert_types_unittest.cc",
+    "cert/x509_certificate_unittest.cc",
+    "cert/x509_util_unittest.cc",
+    "cert_net/cert_net_fetcher_impl_unittest.cc",
+    "cookies/canonical_cookie_unittest.cc",
+    "cookies/cookie_constants_unittest.cc",
+    "cookies/cookie_deletion_info_unittest.cc",
+    "cookies/cookie_monster_unittest.cc",
+    "cookies/cookie_util_unittest.cc",
+    "cookies/parsed_cookie_unittest.cc",
+    "der/encode_values_unittest.cc",
+    "der/input_unittest.cc",
+    "der/parse_values_unittest.cc",
+    "der/parser_unittest.cc",
+    "dns/dns_config_service_unittest.cc",
+    "dns/dns_hosts_unittest.cc",
+    "dns/dns_query_unittest.cc",
+    "dns/dns_response_unittest.cc",
+    "dns/dns_session_unittest.cc",
+    "dns/dns_socket_pool_unittest.cc",
+    "dns/dns_transaction_unittest.cc",
+    "dns/dns_util_unittest.cc",
+    "dns/host_cache_unittest.cc",
+    "dns/host_resolver_impl_unittest.cc",
+    "dns/mapped_host_resolver_unittest.cc",
+    "dns/record_parsed_unittest.cc",
+    "dns/record_rdata_unittest.cc",
+    "dns/serial_worker_unittest.cc",
 
-      # dial is a legacy component we kept from old Chromium net.
-      "dial/dial_http_server_unittest.cc",
-      "dial/dial_service_unittest.cc",
-      "dial/dial_test_helpers.h",
-      "dial/dial_udp_server_unittests.cc",
+    # dial is a legacy component we kept from old Chromium net.
+    "dial/dial_http_server_unittest.cc",
+    "dial/dial_service_unittest.cc",
+    "dial/dial_test_helpers.h",
+    "dial/dial_udp_server_unittests.cc",
 
-      # disk_cache component is disabled because only http cache depends on
-      # it and Cobalt does not support http cache yet.
-      # "disk_cache/backend_cleanup_tracker_unittest.cc",
-      # "disk_cache/backend_unittest.cc",
-      # "disk_cache/blockfile/addr_unittest.cc",
-      # "disk_cache/blockfile/bitmap_unittest.cc",
-      # "disk_cache/blockfile/block_files_unittest.cc",
-      # "disk_cache/blockfile/mapped_file_unittest.cc",
-      # "disk_cache/blockfile/stats_unittest.cc",
-      # "disk_cache/blockfile/storage_block_unittest.cc",
-      # "disk_cache/cache_util_unittest.cc",
-      # "disk_cache/entry_unittest.cc",
-      # "disk_cache/simple/simple_file_tracker_unittest.cc",
-      # "disk_cache/simple/simple_index_file_unittest.cc",
-      # "disk_cache/simple/simple_index_unittest.cc",
-      # "disk_cache/simple/simple_test_util.cc",
-      # "disk_cache/simple/simple_test_util.h",
-      # "disk_cache/simple/simple_util_unittest.cc",
-      # "disk_cache/simple/simple_version_upgrade_unittest.cc",
+    # disk_cache component is disabled because only http cache depends on
+    # it and Cobalt does not support http cache yet.
+    # "disk_cache/backend_cleanup_tracker_unittest.cc",
+    # "disk_cache/backend_unittest.cc",
+    # "disk_cache/blockfile/addr_unittest.cc",
+    # "disk_cache/blockfile/bitmap_unittest.cc",
+    # "disk_cache/blockfile/block_files_unittest.cc",
+    # "disk_cache/blockfile/mapped_file_unittest.cc",
+    # "disk_cache/blockfile/stats_unittest.cc",
+    # "disk_cache/blockfile/storage_block_unittest.cc",
+    # "disk_cache/cache_util_unittest.cc",
+    # "disk_cache/entry_unittest.cc",
+    # "disk_cache/simple/simple_file_tracker_unittest.cc",
+    # "disk_cache/simple/simple_index_file_unittest.cc",
+    # "disk_cache/simple/simple_index_unittest.cc",
+    # "disk_cache/simple/simple_test_util.cc",
+    # "disk_cache/simple/simple_test_util.h",
+    # "disk_cache/simple/simple_util_unittest.cc",
+    # "disk_cache/simple/simple_version_upgrade_unittest.cc",
 
-      # # Cobalt probably don"t need these.
-      # # "extras/sqlite/sqlite_channel_id_store_unittest.cc",
-      # # "extras/sqlite/sqlite_persistent_cookie_store_unittest.cc",
+    # # Cobalt probably don"t need these.
+    # # "extras/sqlite/sqlite_channel_id_store_unittest.cc",
+    # # "extras/sqlite/sqlite_persistent_cookie_store_unittest.cc",
 
-      "filter/brotli_source_stream_unittest.cc",
-      "filter/filter_source_stream_unittest.cc",
-      "filter/gzip_source_stream_unittest.cc",
+    "filter/brotli_source_stream_unittest.cc",
+    "filter/filter_source_stream_unittest.cc",
+    "filter/gzip_source_stream_unittest.cc",
 
-      # Cobalt does not support ftp.
-      # "ftp/ftp_auth_cache_unittest.cc",
-      # "ftp/ftp_ctrl_response_buffer_unittest.cc",
-      # "ftp/ftp_directory_listing_parser_ls_unittest.cc",
-      # "ftp/ftp_directory_listing_parser_unittest.cc",
-      # "ftp/ftp_directory_listing_parser_unittest.h",
-      # "ftp/ftp_directory_listing_parser_vms_unittest.cc",
-      # "ftp/ftp_directory_listing_parser_windows_unittest.cc",
-      # "ftp/ftp_network_transaction_unittest.cc",
-      # "ftp/ftp_util_unittest.cc",
+    # Cobalt does not support ftp.
+    # "ftp/ftp_auth_cache_unittest.cc",
+    # "ftp/ftp_ctrl_response_buffer_unittest.cc",
+    # "ftp/ftp_directory_listing_parser_ls_unittest.cc",
+    # "ftp/ftp_directory_listing_parser_unittest.cc",
+    # "ftp/ftp_directory_listing_parser_unittest.h",
+    # "ftp/ftp_directory_listing_parser_vms_unittest.cc",
+    # "ftp/ftp_directory_listing_parser_windows_unittest.cc",
+    # "ftp/ftp_network_transaction_unittest.cc",
+    # "ftp/ftp_util_unittest.cc",
 
-      "http/bidirectional_stream_unittest.cc",
-      "http/broken_alternative_services_unittest.cc",
-      "http/http_auth_cache_unittest.cc",
-      "http/http_auth_challenge_tokenizer_unittest.cc",
-      "http/http_auth_controller_unittest.cc",
-      "http/http_auth_filter_unittest.cc",
-      "http/http_auth_handler_basic_unittest.cc",
-      "http/http_auth_handler_digest_unittest.cc",
-      "http/http_auth_handler_factory_unittest.cc",
-      "http/http_auth_handler_mock.cc",
-      "http/http_auth_handler_mock.h",
+    "http/bidirectional_stream_unittest.cc",
+    "http/broken_alternative_services_unittest.cc",
+    "http/http_auth_cache_unittest.cc",
+    "http/http_auth_challenge_tokenizer_unittest.cc",
+    "http/http_auth_controller_unittest.cc",
+    "http/http_auth_filter_unittest.cc",
+    "http/http_auth_handler_basic_unittest.cc",
+    "http/http_auth_handler_digest_unittest.cc",
+    "http/http_auth_handler_factory_unittest.cc",
+    "http/http_auth_handler_mock.cc",
+    "http/http_auth_handler_mock.h",
 
-      # Cobalt does not support Kerberos(gssapi) yet.
-      # "http/http_auth_handler_negotiate_unittest.cc",
-      "http/http_auth_handler_ntlm_portable_unittest.cc",
-      "http/http_auth_handler_unittest.cc",
-      "http/http_auth_multi_round_parse_unittest.cc",
-      "http/http_auth_preferences_unittest.cc",
-      "http/http_auth_unittest.cc",
-      "http/http_basic_state_unittest.cc",
-      "http/http_byte_range_unittest.cc",
+    # Cobalt does not support Kerberos(gssapi) yet.
+    # "http/http_auth_handler_negotiate_unittest.cc",
+    "http/http_auth_handler_ntlm_portable_unittest.cc",
+    "http/http_auth_handler_unittest.cc",
+    "http/http_auth_multi_round_parse_unittest.cc",
+    "http/http_auth_preferences_unittest.cc",
+    "http/http_auth_unittest.cc",
+    "http/http_basic_state_unittest.cc",
+    "http/http_byte_range_unittest.cc",
 
-      # "http/http_cache_lookup_manager_unittest.cc",
-      # "http/http_cache_unittest.cc",
-      # "http/http_cache_writers_unittest.cc",
-      # "url_request/view_cache_helper_unittest.cc",
-      "http/http_chunked_decoder_unittest.cc",
+    # "http/http_cache_lookup_manager_unittest.cc",
+    # "http/http_cache_unittest.cc",
+    # "http/http_cache_writers_unittest.cc",
+    # "url_request/view_cache_helper_unittest.cc",
+    "http/http_chunked_decoder_unittest.cc",
 
-      # Known ICU issue.
-      # "http/http_content_disposition_unittest.cc",
-      "http/http_log_util_unittest.cc",
-      "http/http_network_layer_unittest.cc",
-      "http/http_network_transaction_ssl_unittest.cc",
-      "http/http_network_transaction_unittest.cc",
-      "http/http_proxy_client_socket_pool_unittest.cc",
-      "http/http_proxy_client_socket_unittest.cc",
-      "http/http_proxy_client_socket_wrapper_unittest.cc",
-      "http/http_request_headers_unittest.cc",
-      "http/http_response_body_drainer_unittest.cc",
-      "http/http_response_headers_unittest.cc",
-      "http/http_response_info_unittest.cc",
-      "http/http_security_headers_unittest.cc",
-      "http/http_server_properties_impl_unittest.cc",
-      "http/http_server_properties_manager_unittest.cc",
-      "http/http_status_code_unittest.cc",
-      "http/http_stream_factory_job_controller_unittest.cc",
-      "http/http_stream_factory_unittest.cc",
-      "http/http_stream_parser_unittest.cc",
-      "http/http_stream_request_unittest.cc",
-      "http/http_util_unittest.cc",
-      "http/http_vary_data_unittest.cc",
-      "http/mock_allow_http_auth_preferences.cc",
-      "http/mock_allow_http_auth_preferences.h",
+    # Known ICU issue.
+    # "http/http_content_disposition_unittest.cc",
+    "http/http_log_util_unittest.cc",
+    "http/http_network_layer_unittest.cc",
+    "http/http_network_transaction_ssl_unittest.cc",
+    "http/http_network_transaction_unittest.cc",
+    "http/http_proxy_client_socket_pool_unittest.cc",
+    "http/http_proxy_client_socket_unittest.cc",
+    "http/http_proxy_client_socket_wrapper_unittest.cc",
+    "http/http_request_headers_unittest.cc",
+    "http/http_response_body_drainer_unittest.cc",
+    "http/http_response_headers_unittest.cc",
+    "http/http_response_info_unittest.cc",
+    "http/http_security_headers_unittest.cc",
+    "http/http_server_properties_impl_unittest.cc",
+    "http/http_server_properties_manager_unittest.cc",
+    "http/http_status_code_unittest.cc",
+    "http/http_stream_factory_job_controller_unittest.cc",
+    "http/http_stream_factory_unittest.cc",
+    "http/http_stream_parser_unittest.cc",
+    "http/http_stream_request_unittest.cc",
+    "http/http_util_unittest.cc",
+    "http/http_vary_data_unittest.cc",
+    "http/mock_allow_http_auth_preferences.cc",
+    "http/mock_allow_http_auth_preferences.h",
 
-      # "http/mock_http_cache.cc",
-      # "http/mock_http_cache.h",
-      # Main test target not enabled yet.
-      # "http/transport_security_persister_unittest.cc",
-      # "log/file_net_log_observer_unittest.cc",
-      "http/transport_security_state_unittest.cc",
-      "http/url_security_manager_unittest.cc",
-      "log/net_log_capture_mode_unittest.cc",
-      "log/net_log_unittest.cc",
-      "log/net_log_util_unittest.cc",
-      "log/trace_net_log_observer_unittest.cc",
-      "nqe/effective_connection_type_unittest.cc",
-      "nqe/event_creator_unittest.cc",
-      "nqe/network_id_unittest.cc",
-      "nqe/network_qualities_prefs_manager_unittest.cc",
-      "nqe/network_quality_estimator_params_unittest.cc",
-      "nqe/network_quality_estimator_unittest.cc",
-      "nqe/network_quality_estimator_util_unittest.cc",
-      "nqe/network_quality_store_unittest.cc",
-      "nqe/observation_buffer_unittest.cc",
-      "nqe/socket_watcher_unittest.cc",
-      "nqe/throughput_analyzer_unittest.cc",
-      "ntlm/ntlm_buffer_reader_unittest.cc",
-      "ntlm/ntlm_buffer_writer_unittest.cc",
-      "ntlm/ntlm_client_unittest.cc",
-      "ntlm/ntlm_test_data.h",
-      "ntlm/ntlm_unittest.cc",
+    # "http/mock_http_cache.cc",
+    # "http/mock_http_cache.h",
+    # Main test target not enabled yet.
+    # "http/transport_security_persister_unittest.cc",
+    # "log/file_net_log_observer_unittest.cc",
+    "http/transport_security_state_unittest.cc",
+    "http/url_security_manager_unittest.cc",
+    "log/net_log_capture_mode_unittest.cc",
+    "log/net_log_unittest.cc",
+    "log/net_log_util_unittest.cc",
+    "log/trace_net_log_observer_unittest.cc",
+    "nqe/effective_connection_type_unittest.cc",
+    "nqe/event_creator_unittest.cc",
+    "nqe/network_id_unittest.cc",
+    "nqe/network_qualities_prefs_manager_unittest.cc",
+    "nqe/network_quality_estimator_params_unittest.cc",
+    "nqe/network_quality_estimator_unittest.cc",
+    "nqe/network_quality_estimator_util_unittest.cc",
+    "nqe/network_quality_store_unittest.cc",
+    "nqe/observation_buffer_unittest.cc",
+    "nqe/socket_watcher_unittest.cc",
+    "nqe/throughput_analyzer_unittest.cc",
+    "ntlm/ntlm_buffer_reader_unittest.cc",
+    "ntlm/ntlm_buffer_writer_unittest.cc",
+    "ntlm/ntlm_client_unittest.cc",
+    "ntlm/ntlm_test_data.h",
+    "ntlm/ntlm_unittest.cc",
 
-      # "http/http_auth_gssapi_starboard_unittest.cc",
-      "proxy_resolution/dhcp_pac_file_fetcher_factory_unittest.cc",
+    # "http/http_auth_gssapi_starboard_unittest.cc",
+    "proxy_resolution/dhcp_pac_file_fetcher_factory_unittest.cc",
 
-      # mojo not used.
-      # "proxy_resolution/mojo_proxy_resolver_v8_tracing_bindings_unittest.cc",
-      "proxy_resolution/multi_threaded_proxy_resolver_unittest.cc",
-      "proxy_resolution/network_delegate_error_observer_unittest.cc",
-      "proxy_resolution/pac_file_decider_unittest.cc",
-      "proxy_resolution/pac_file_fetcher_impl_unittest.cc",
-      "proxy_resolution/proxy_bypass_rules_unittest.cc",
-      "proxy_resolution/proxy_config_unittest.cc",
-      "proxy_resolution/proxy_info_unittest.cc",
-      "proxy_resolution/proxy_list_unittest.cc",
-      "proxy_resolution/proxy_resolution_service_unittest.cc",
-      "proxy_resolution/proxy_server_unittest.cc",
+    # mojo not used.
+    # "proxy_resolution/mojo_proxy_resolver_v8_tracing_bindings_unittest.cc",
+    "proxy_resolution/multi_threaded_proxy_resolver_unittest.cc",
+    "proxy_resolution/network_delegate_error_observer_unittest.cc",
+    "proxy_resolution/pac_file_decider_unittest.cc",
+    "proxy_resolution/pac_file_fetcher_impl_unittest.cc",
+    "proxy_resolution/proxy_bypass_rules_unittest.cc",
+    "proxy_resolution/proxy_config_unittest.cc",
+    "proxy_resolution/proxy_info_unittest.cc",
+    "proxy_resolution/proxy_list_unittest.cc",
+    "proxy_resolution/proxy_resolution_service_unittest.cc",
+    "proxy_resolution/proxy_server_unittest.cc",
 
-      # not useful to us.
-      # "proxy_resolution/proxy_resolver_v8_tracing_unittest.cc",
-      # "proxy_resolution/proxy_resolver_v8_tracing_wrapper_unittest.cc",
-      # "proxy_resolution/proxy_resolver_v8_unittest.cc",
-      "quic/bidirectional_stream_quic_impl_unittest.cc",
-      "quic/crypto/proof_test_chromium.cc",
-      "quic/crypto/proof_verifier_chromium_test.cc",
-      "quic/network_connection_unittest.cc",
-      "quic/properties_based_quic_server_info_test.cc",
-      "quic/quic_address_mismatch_test.cc",
-      "quic/quic_chromium_alarm_factory_test.cc",
-      "quic/quic_chromium_client_session_peer.cc",
-      "quic/quic_chromium_client_session_peer.h",
-      "quic/quic_chromium_client_session_test.cc",
-      "quic/quic_chromium_client_stream_test.cc",
-      "quic/quic_chromium_connection_helper_test.cc",
-      "quic/quic_clock_skew_detector_test.cc",
-      "quic/quic_connectivity_probing_manager_test.cc",
-      "quic/quic_end_to_end_unittest.cc",
-      "quic/quic_http_stream_test.cc",
-      "quic/quic_http_utils_test.cc",
-      "quic/quic_network_transaction_unittest.cc",
-      "quic/quic_proxy_client_socket_unittest.cc",
-      "quic/quic_stream_factory_peer.cc",
-      "quic/quic_stream_factory_peer.h",
-      "quic/quic_stream_factory_test.cc",
-      "quic/quic_utils_chromium_test.cc",
-      "socket/client_socket_pool_base_unittest.cc",
-      "socket/mock_client_socket_pool_manager.cc",
-      "socket/mock_client_socket_pool_manager.h",
-      "socket/sequenced_socket_data_unittest.cc",
-      "socket/socket_bio_adapter_unittest.cc",
-      "socket/socket_tag_unittest.cc",
-      "socket/socks5_client_socket_unittest.cc",
-      "socket/socks_client_socket_pool_unittest.cc",
-      "socket/socks_client_socket_unittest.cc",
-      "socket/ssl_client_socket_pool_unittest.cc",
+    # not useful to us.
+    # "proxy_resolution/proxy_resolver_v8_tracing_unittest.cc",
+    # "proxy_resolution/proxy_resolver_v8_tracing_wrapper_unittest.cc",
+    # "proxy_resolution/proxy_resolver_v8_unittest.cc",
+    "quic/bidirectional_stream_quic_impl_unittest.cc",
+    "quic/crypto/proof_test_chromium.cc",
+    "quic/crypto/proof_verifier_chromium_test.cc",
+    "quic/network_connection_unittest.cc",
+    "quic/properties_based_quic_server_info_test.cc",
+    "quic/quic_address_mismatch_test.cc",
+    "quic/quic_chromium_alarm_factory_test.cc",
+    "quic/quic_chromium_client_session_peer.cc",
+    "quic/quic_chromium_client_session_peer.h",
+    "quic/quic_chromium_client_session_test.cc",
+    "quic/quic_chromium_client_stream_test.cc",
+    "quic/quic_chromium_connection_helper_test.cc",
+    "quic/quic_clock_skew_detector_test.cc",
+    "quic/quic_connectivity_probing_manager_test.cc",
+    "quic/quic_end_to_end_unittest.cc",
+    "quic/quic_http_stream_test.cc",
+    "quic/quic_http_utils_test.cc",
+    "quic/quic_network_transaction_unittest.cc",
+    "quic/quic_proxy_client_socket_unittest.cc",
+    "quic/quic_stream_factory_peer.cc",
+    "quic/quic_stream_factory_peer.h",
+    "quic/quic_stream_factory_test.cc",
+    "quic/quic_utils_chromium_test.cc",
+    "socket/client_socket_pool_base_unittest.cc",
+    "socket/mock_client_socket_pool_manager.cc",
+    "socket/mock_client_socket_pool_manager.h",
+    "socket/sequenced_socket_data_unittest.cc",
+    "socket/socket_bio_adapter_unittest.cc",
+    "socket/socket_tag_unittest.cc",
+    "socket/socks5_client_socket_unittest.cc",
+    "socket/socks_client_socket_pool_unittest.cc",
+    "socket/socks_client_socket_unittest.cc",
+    "socket/ssl_client_socket_pool_unittest.cc",
 
-      # Previously excluded via sources/
-      # "socket/ssl_client_socket_unittest.cc",
-      "socket/ssl_server_socket_unittest.cc",
+    # Previously excluded via sources/
+    # "socket/ssl_client_socket_unittest.cc",
+    "socket/ssl_server_socket_unittest.cc",
 
-      # Previously excluded via sources/
-      # "socket/tcp_client_socket_unittest.cc",
-      # "socket/tcp_server_socket_unittest.cc",
-      "socket/tcp_socket_unittest.cc",
-      "socket/transport_client_socket_pool_test_util.cc",
-      "socket/transport_client_socket_pool_test_util.h",
-      "socket/transport_client_socket_pool_unittest.cc",
-      "socket/transport_client_socket_unittest.cc",
+    # Previously excluded via sources/
+    # "socket/tcp_client_socket_unittest.cc",
+    # "socket/tcp_server_socket_unittest.cc",
+    "socket/tcp_socket_unittest.cc",
+    "socket/transport_client_socket_pool_test_util.cc",
+    "socket/transport_client_socket_pool_test_util.h",
+    "socket/transport_client_socket_pool_unittest.cc",
+    "socket/transport_client_socket_unittest.cc",
 
-      # Previously excluded via sources/
-      # "socket/udp_socket_unittest.cc",
-      "socket/websocket_endpoint_lock_manager_unittest.cc",
-      "socket/websocket_transport_client_socket_pool_unittest.cc",
-      "spdy/bidirectional_stream_spdy_impl_unittest.cc",
-      "spdy/buffered_spdy_framer_unittest.cc",
-      "spdy/header_coalescer_test.cc",
-      "spdy/http2_priority_dependencies_unittest.cc",
-      "spdy/http2_push_promise_index_test.cc",
-      "spdy/spdy_buffer_unittest.cc",
-      "spdy/spdy_http_stream_unittest.cc",
-      "spdy/spdy_http_utils_unittest.cc",
-      "spdy/spdy_log_util_unittest.cc",
-      "spdy/spdy_network_transaction_unittest.cc",
-      "spdy/spdy_proxy_client_socket_unittest.cc",
-      "spdy/spdy_read_queue_unittest.cc",
-      "spdy/spdy_session_pool_unittest.cc",
-      "spdy/spdy_session_unittest.cc",
-      "spdy/spdy_stream_unittest.cc",
-      "spdy/spdy_write_queue_unittest.cc",
-      "ssl/channel_id_service_unittest.cc",
-      "ssl/client_cert_identity_unittest.cc",
-      "ssl/client_cert_store_unittest-inl.h",
-      "ssl/default_channel_id_store_unittest.cc",
-      "ssl/ssl_cipher_suite_names_unittest.cc",
-      "ssl/ssl_client_auth_cache_unittest.cc",
-      "ssl/ssl_client_session_cache_unittest.cc",
-      "ssl/ssl_config_service_unittest.cc",
-      "ssl/ssl_config_unittest.cc",
-      "ssl/ssl_connection_status_flags_unittest.cc",
-      "ssl/ssl_platform_key_util_unittest.cc",
-      "test/embedded_test_server/embedded_test_server_unittest.cc",
-      "test/embedded_test_server/http_request_unittest.cc",
-      "test/embedded_test_server/http_response_unittest.cc",
-      "test/run_all_unittests.cc",
-      "test/tcp_socket_proxy_unittest.cc",
-      "third_party/nist-pkits/pkits_testcases-inl.h",
-      "third_party/quic/core/congestion_control/bandwidth_sampler_test.cc",
-      "third_party/quic/core/congestion_control/bbr_sender_test.cc",
-      "third_party/quic/core/congestion_control/cubic_bytes_test.cc",
-      "third_party/quic/core/congestion_control/general_loss_algorithm_test.cc",
-      "third_party/quic/core/congestion_control/hybrid_slow_start_test.cc",
-      "third_party/quic/core/congestion_control/pacing_sender_test.cc",
-      "third_party/quic/core/congestion_control/prr_sender_test.cc",
-      "third_party/quic/core/congestion_control/rtt_stats_test.cc",
-      "third_party/quic/core/congestion_control/send_algorithm_test.cc",
-      "third_party/quic/core/congestion_control/uber_loss_algorithm_test.cc",
-      "third_party/quic/core/congestion_control/windowed_filter_test.cc",
-      "third_party/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc",
-      "third_party/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc",
-      "third_party/quic/core/crypto/aes_128_gcm_decrypter_test.cc",
-      "third_party/quic/core/crypto/aes_128_gcm_encrypter_test.cc",
-      "third_party/quic/core/crypto/aes_256_gcm_decrypter_test.cc",
-      "third_party/quic/core/crypto/aes_256_gcm_encrypter_test.cc",
-      "third_party/quic/core/crypto/cert_compressor_test.cc",
-      "third_party/quic/core/crypto/chacha20_poly1305_decrypter_test.cc",
-      "third_party/quic/core/crypto/chacha20_poly1305_encrypter_test.cc",
-      "third_party/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc",
-      "third_party/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc",
-      "third_party/quic/core/crypto/channel_id_test.cc",
+    # Previously excluded via sources/
+    # "socket/udp_socket_unittest.cc",
+    "socket/websocket_endpoint_lock_manager_unittest.cc",
+    "socket/websocket_transport_client_socket_pool_unittest.cc",
+    "spdy/bidirectional_stream_spdy_impl_unittest.cc",
+    "spdy/buffered_spdy_framer_unittest.cc",
+    "spdy/header_coalescer_test.cc",
+    "spdy/http2_priority_dependencies_unittest.cc",
+    "spdy/http2_push_promise_index_test.cc",
+    "spdy/spdy_buffer_unittest.cc",
+    "spdy/spdy_http_stream_unittest.cc",
+    "spdy/spdy_http_utils_unittest.cc",
+    "spdy/spdy_log_util_unittest.cc",
+    "spdy/spdy_network_transaction_unittest.cc",
+    "spdy/spdy_proxy_client_socket_unittest.cc",
+    "spdy/spdy_read_queue_unittest.cc",
+    "spdy/spdy_session_pool_unittest.cc",
+    "spdy/spdy_session_unittest.cc",
+    "spdy/spdy_stream_unittest.cc",
+    "spdy/spdy_write_queue_unittest.cc",
+    "ssl/channel_id_service_unittest.cc",
+    "ssl/client_cert_identity_unittest.cc",
+    "ssl/client_cert_store_unittest-inl.h",
+    "ssl/default_channel_id_store_unittest.cc",
+    "ssl/ssl_cipher_suite_names_unittest.cc",
+    "ssl/ssl_client_auth_cache_unittest.cc",
+    "ssl/ssl_client_session_cache_unittest.cc",
+    "ssl/ssl_config_service_unittest.cc",
+    "ssl/ssl_config_unittest.cc",
+    "ssl/ssl_connection_status_flags_unittest.cc",
+    "ssl/ssl_platform_key_util_unittest.cc",
+    "test/embedded_test_server/embedded_test_server_unittest.cc",
+    "test/embedded_test_server/http_request_unittest.cc",
+    "test/embedded_test_server/http_response_unittest.cc",
+    "test/run_all_unittests.cc",
+    "test/tcp_socket_proxy_unittest.cc",
+    "third_party/nist-pkits/pkits_testcases-inl.h",
+    "third_party/quic/core/congestion_control/bandwidth_sampler_test.cc",
+    "third_party/quic/core/congestion_control/bbr_sender_test.cc",
+    "third_party/quic/core/congestion_control/cubic_bytes_test.cc",
+    "third_party/quic/core/congestion_control/general_loss_algorithm_test.cc",
+    "third_party/quic/core/congestion_control/hybrid_slow_start_test.cc",
+    "third_party/quic/core/congestion_control/pacing_sender_test.cc",
+    "third_party/quic/core/congestion_control/prr_sender_test.cc",
+    "third_party/quic/core/congestion_control/rtt_stats_test.cc",
+    "third_party/quic/core/congestion_control/send_algorithm_test.cc",
+    "third_party/quic/core/congestion_control/uber_loss_algorithm_test.cc",
+    "third_party/quic/core/congestion_control/windowed_filter_test.cc",
+    "third_party/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc",
+    "third_party/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc",
+    "third_party/quic/core/crypto/aes_128_gcm_decrypter_test.cc",
+    "third_party/quic/core/crypto/aes_128_gcm_encrypter_test.cc",
+    "third_party/quic/core/crypto/aes_256_gcm_decrypter_test.cc",
+    "third_party/quic/core/crypto/aes_256_gcm_encrypter_test.cc",
+    "third_party/quic/core/crypto/cert_compressor_test.cc",
+    "third_party/quic/core/crypto/chacha20_poly1305_decrypter_test.cc",
+    "third_party/quic/core/crypto/chacha20_poly1305_encrypter_test.cc",
+    "third_party/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc",
+    "third_party/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc",
+    "third_party/quic/core/crypto/channel_id_test.cc",
 
-      # "third_party/quic/core/crypto/common_cert_set_test.cc",
-      "third_party/quic/core/crypto/crypto_framer_test.cc",
-      "third_party/quic/core/crypto/crypto_handshake_message_test.cc",
-      "third_party/quic/core/crypto/crypto_secret_boxer_test.cc",
-      "third_party/quic/core/crypto/crypto_server_test.cc",
-      "third_party/quic/core/crypto/crypto_utils_test.cc",
-      "third_party/quic/core/crypto/curve25519_key_exchange_test.cc",
-      "third_party/quic/core/crypto/null_decrypter_test.cc",
-      "third_party/quic/core/crypto/null_encrypter_test.cc",
-      "third_party/quic/core/crypto/p256_key_exchange_test.cc",
-      "third_party/quic/core/crypto/quic_compressed_certs_cache_test.cc",
-      "third_party/quic/core/crypto/quic_crypto_client_config_test.cc",
-      "third_party/quic/core/crypto/quic_crypto_server_config_test.cc",
-      "third_party/quic/core/crypto/quic_hkdf_test.cc",
-      "third_party/quic/core/crypto/quic_random_test.cc",
-      "third_party/quic/core/crypto/transport_parameters_test.cc",
-      "third_party/quic/core/frames/quic_frames_test.cc",
-      "third_party/quic/core/http/http_decoder_test.cc",
-      "third_party/quic/core/http/http_encoder_test.cc",
-      "third_party/quic/core/http/quic_client_promised_info_test.cc",
-      "third_party/quic/core/http/quic_client_push_promise_index_test.cc",
-      "third_party/quic/core/http/quic_header_list_test.cc",
-      "third_party/quic/core/http/quic_headers_stream_test.cc",
-      "third_party/quic/core/http/quic_server_session_base_test.cc",
-      "third_party/quic/core/http/quic_spdy_session_test.cc",
-      "third_party/quic/core/http/quic_spdy_stream_body_buffer_test.cc",
-      "third_party/quic/core/http/quic_spdy_stream_test.cc",
-      "third_party/quic/core/http/spdy_utils_test.cc",
-      "third_party/quic/core/legacy_quic_stream_id_manager_test.cc",
-      "third_party/quic/core/packet_number_indexed_queue_test.cc",
-      "third_party/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc",
-      "third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc",
-      "third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc",
-      "third_party/quic/core/qpack/qpack_decoder_test.cc",
-      "third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc",
-      "third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc",
-      "third_party/quic/core/qpack/qpack_encoder_test.cc",
-      "third_party/quic/core/qpack/qpack_header_table_test.cc",
-      "third_party/quic/core/qpack/qpack_instruction_decoder_test.cc",
-      "third_party/quic/core/qpack/qpack_instruction_encoder_test.cc",
-      "third_party/quic/core/qpack/qpack_progressive_decoder_test.cc",
-      "third_party/quic/core/qpack/qpack_round_trip_test.cc",
-      "third_party/quic/core/qpack/qpack_static_table_test.cc",
-      "third_party/quic/core/quic_alarm_test.cc",
-      "third_party/quic/core/quic_arena_scoped_ptr_test.cc",
-      "third_party/quic/core/quic_bandwidth_test.cc",
-      "third_party/quic/core/quic_buffered_packet_store_test.cc",
-      "third_party/quic/core/quic_config_test.cc",
-      "third_party/quic/core/quic_connection_id_test.cc",
-      "third_party/quic/core/quic_connection_test.cc",
-      "third_party/quic/core/quic_control_frame_manager_test.cc",
-      "third_party/quic/core/quic_crypto_client_handshaker_test.cc",
-      "third_party/quic/core/quic_crypto_client_stream_test.cc",
-      "third_party/quic/core/quic_crypto_server_stream_test.cc",
-      "third_party/quic/core/quic_crypto_stream_test.cc",
-      "third_party/quic/core/quic_data_writer_test.cc",
-      "third_party/quic/core/quic_dispatcher_test.cc",
-      "third_party/quic/core/quic_error_codes_test.cc",
-      "third_party/quic/core/quic_flow_controller_test.cc",
-      "third_party/quic/core/quic_framer_test.cc",
-      "third_party/quic/core/quic_ietf_framer_test.cc",
-      "third_party/quic/core/quic_interval_set_test.cc",
-      "third_party/quic/core/quic_interval_test.cc",
-      "third_party/quic/core/quic_lru_cache_test.cc",
-      "third_party/quic/core/quic_one_block_arena_test.cc",
-      "third_party/quic/core/quic_packet_creator_test.cc",
-      "third_party/quic/core/quic_packet_generator_test.cc",
-      "third_party/quic/core/quic_packet_number_test.cc",
-      "third_party/quic/core/quic_received_packet_manager_test.cc",
-      "third_party/quic/core/quic_sent_packet_manager_test.cc",
-      "third_party/quic/core/quic_server_id_test.cc",
-      "third_party/quic/core/quic_session_test.cc",
-      "third_party/quic/core/quic_simple_buffer_allocator_test.cc",
-      "third_party/quic/core/quic_socket_address_coder_test.cc",
-      "third_party/quic/core/quic_stream_id_manager_test.cc",
-      "third_party/quic/core/quic_stream_send_buffer_test.cc",
-      "third_party/quic/core/quic_stream_sequencer_buffer_test.cc",
-      "third_party/quic/core/quic_stream_sequencer_test.cc",
-      "third_party/quic/core/quic_stream_test.cc",
-      "third_party/quic/core/quic_sustained_bandwidth_recorder_test.cc",
-      "third_party/quic/core/quic_tag_test.cc",
-      "third_party/quic/core/quic_time_test.cc",
-      "third_party/quic/core/quic_time_wait_list_manager_test.cc",
+    # "third_party/quic/core/crypto/common_cert_set_test.cc",
+    "third_party/quic/core/crypto/crypto_framer_test.cc",
+    "third_party/quic/core/crypto/crypto_handshake_message_test.cc",
+    "third_party/quic/core/crypto/crypto_secret_boxer_test.cc",
+    "third_party/quic/core/crypto/crypto_server_test.cc",
+    "third_party/quic/core/crypto/crypto_utils_test.cc",
+    "third_party/quic/core/crypto/curve25519_key_exchange_test.cc",
+    "third_party/quic/core/crypto/null_decrypter_test.cc",
+    "third_party/quic/core/crypto/null_encrypter_test.cc",
+    "third_party/quic/core/crypto/p256_key_exchange_test.cc",
+    "third_party/quic/core/crypto/quic_compressed_certs_cache_test.cc",
+    "third_party/quic/core/crypto/quic_crypto_client_config_test.cc",
+    "third_party/quic/core/crypto/quic_crypto_server_config_test.cc",
+    "third_party/quic/core/crypto/quic_hkdf_test.cc",
+    "third_party/quic/core/crypto/quic_random_test.cc",
+    "third_party/quic/core/crypto/transport_parameters_test.cc",
+    "third_party/quic/core/frames/quic_frames_test.cc",
+    "third_party/quic/core/http/http_decoder_test.cc",
+    "third_party/quic/core/http/http_encoder_test.cc",
+    "third_party/quic/core/http/quic_client_promised_info_test.cc",
+    "third_party/quic/core/http/quic_client_push_promise_index_test.cc",
+    "third_party/quic/core/http/quic_header_list_test.cc",
+    "third_party/quic/core/http/quic_headers_stream_test.cc",
+    "third_party/quic/core/http/quic_server_session_base_test.cc",
+    "third_party/quic/core/http/quic_spdy_session_test.cc",
+    "third_party/quic/core/http/quic_spdy_stream_body_buffer_test.cc",
+    "third_party/quic/core/http/quic_spdy_stream_test.cc",
+    "third_party/quic/core/http/spdy_utils_test.cc",
+    "third_party/quic/core/legacy_quic_stream_id_manager_test.cc",
+    "third_party/quic/core/packet_number_indexed_queue_test.cc",
+    "third_party/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc",
+    "third_party/quic/core/qpack/qpack_decoder_stream_receiver_test.cc",
+    "third_party/quic/core/qpack/qpack_decoder_stream_sender_test.cc",
+    "third_party/quic/core/qpack/qpack_decoder_test.cc",
+    "third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc",
+    "third_party/quic/core/qpack/qpack_encoder_stream_sender_test.cc",
+    "third_party/quic/core/qpack/qpack_encoder_test.cc",
+    "third_party/quic/core/qpack/qpack_header_table_test.cc",
+    "third_party/quic/core/qpack/qpack_instruction_decoder_test.cc",
+    "third_party/quic/core/qpack/qpack_instruction_encoder_test.cc",
+    "third_party/quic/core/qpack/qpack_progressive_decoder_test.cc",
+    "third_party/quic/core/qpack/qpack_round_trip_test.cc",
+    "third_party/quic/core/qpack/qpack_static_table_test.cc",
+    "third_party/quic/core/quic_alarm_test.cc",
+    "third_party/quic/core/quic_arena_scoped_ptr_test.cc",
+    "third_party/quic/core/quic_bandwidth_test.cc",
+    "third_party/quic/core/quic_buffered_packet_store_test.cc",
+    "third_party/quic/core/quic_config_test.cc",
+    "third_party/quic/core/quic_connection_id_test.cc",
+    "third_party/quic/core/quic_connection_test.cc",
+    "third_party/quic/core/quic_control_frame_manager_test.cc",
+    "third_party/quic/core/quic_crypto_client_handshaker_test.cc",
+    "third_party/quic/core/quic_crypto_client_stream_test.cc",
+    "third_party/quic/core/quic_crypto_server_stream_test.cc",
+    "third_party/quic/core/quic_crypto_stream_test.cc",
+    "third_party/quic/core/quic_data_writer_test.cc",
+    "third_party/quic/core/quic_dispatcher_test.cc",
+    "third_party/quic/core/quic_error_codes_test.cc",
+    "third_party/quic/core/quic_flow_controller_test.cc",
+    "third_party/quic/core/quic_framer_test.cc",
+    "third_party/quic/core/quic_ietf_framer_test.cc",
+    "third_party/quic/core/quic_interval_set_test.cc",
+    "third_party/quic/core/quic_interval_test.cc",
+    "third_party/quic/core/quic_lru_cache_test.cc",
+    "third_party/quic/core/quic_one_block_arena_test.cc",
+    "third_party/quic/core/quic_packet_creator_test.cc",
+    "third_party/quic/core/quic_packet_generator_test.cc",
+    "third_party/quic/core/quic_packet_number_test.cc",
+    "third_party/quic/core/quic_received_packet_manager_test.cc",
+    "third_party/quic/core/quic_sent_packet_manager_test.cc",
+    "third_party/quic/core/quic_server_id_test.cc",
+    "third_party/quic/core/quic_session_test.cc",
+    "third_party/quic/core/quic_simple_buffer_allocator_test.cc",
+    "third_party/quic/core/quic_socket_address_coder_test.cc",
+    "third_party/quic/core/quic_stream_id_manager_test.cc",
+    "third_party/quic/core/quic_stream_send_buffer_test.cc",
+    "third_party/quic/core/quic_stream_sequencer_buffer_test.cc",
+    "third_party/quic/core/quic_stream_sequencer_test.cc",
+    "third_party/quic/core/quic_stream_test.cc",
+    "third_party/quic/core/quic_sustained_bandwidth_recorder_test.cc",
+    "third_party/quic/core/quic_tag_test.cc",
+    "third_party/quic/core/quic_time_test.cc",
+    "third_party/quic/core/quic_time_wait_list_manager_test.cc",
 
-      # TraceVisitor is not supported yet.
-      # "third_party/quic/core/quic_trace_visitor_test.cc",
-      "third_party/quic/core/quic_unacked_packet_map_test.cc",
-      "third_party/quic/core/quic_utils_test.cc",
-      "third_party/quic/core/quic_version_manager_test.cc",
-      "third_party/quic/core/quic_versions_test.cc",
-      "third_party/quic/core/quic_write_blocked_list_test.cc",
-      "third_party/quic/core/tls_handshaker_test.cc",
-      "third_party/quic/core/uber_quic_stream_id_manager_test.cc",
-      "third_party/quic/platform/api/quic_containers_test.cc",
-      "third_party/quic/platform/api/quic_endian_test.cc",
-      "third_party/quic/platform/api/quic_hostname_utils_test.cc",
-      "third_party/quic/platform/api/quic_mem_slice_span_test.cc",
-      "third_party/quic/platform/api/quic_mem_slice_storage_test.cc",
-      "third_party/quic/platform/api/quic_mem_slice_test.cc",
-      "third_party/quic/platform/api/quic_reference_counted_test.cc",
-      "third_party/quic/platform/api/quic_singleton_test.cc",
-      "third_party/quic/platform/api/quic_str_cat_test.cc",
-      "third_party/quic/platform/api/quic_text_utils_test.cc",
-      "third_party/quic/platform/impl/quic_chromium_clock_test.cc",
-      "third_party/quic/platform/impl/quic_uint128_impl_unittest.cc",
-      "third_party/quic/test_tools/crypto_test_utils_test.cc",
-      "third_party/quic/test_tools/mock_quic_time_wait_list_manager.cc",
-      "third_party/quic/test_tools/mock_quic_time_wait_list_manager.h",
-      "third_party/quic/test_tools/quic_test_utils_test.cc",
-      "third_party/quic/test_tools/simple_session_notifier_test.cc",
-      "third_party/quic/test_tools/simulator/quic_endpoint_test.cc",
-      "third_party/quic/test_tools/simulator/simulator_test.cc",
-      "third_party/quiche/src/http2/decoder/decode_buffer_test.cc",
-      "third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc",
-      "third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.cc",
-      "third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.h",
-      "third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.cc",
-      "third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.h",
-      "third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/http2_structure_decoder_test_util.cc",
-      "third_party/quiche/src/http2/decoder/http2_structure_decoder_test_util.h",
-      "third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h",
-      "third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.h",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder_test.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.h",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_test.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder_test.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.h",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc",
-      "third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc",
-      "third_party/quiche/src/http2/hpack/hpack_string_test.cc",
-      "third_party/quiche/src/http2/hpack/http2_hpack_constants_test.cc",
-      "third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc",
-      "third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc",
-      "third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc",
-      "third_party/quiche/src/http2/hpack/tools/hpack_block_builder.cc",
-      "third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h",
-      "third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc",
-      "third_party/quiche/src/http2/hpack/tools/hpack_example.cc",
-      "third_party/quiche/src/http2/hpack/tools/hpack_example.h",
-      "third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc",
-      "third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc",
-      "third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc",
-      "third_party/quiche/src/http2/http2_constants_test.cc",
-      "third_party/quiche/src/http2/http2_constants_test_util.cc",
-      "third_party/quiche/src/http2/http2_constants_test_util.h",
-      "third_party/quiche/src/http2/http2_structures_test.cc",
-      "third_party/quiche/src/http2/http2_structures_test_util.cc",
-      "third_party/quiche/src/http2/http2_structures_test_util.h",
-      "third_party/quiche/src/http2/platform/api/http2_mock_log.h",
-      "third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc",
-      "third_party/quiche/src/http2/platform/api/http2_test_helpers.h",
-      "third_party/quiche/src/http2/test_tools/frame_parts.cc",
-      "third_party/quiche/src/http2/test_tools/frame_parts.h",
-      "third_party/quiche/src/http2/test_tools/frame_parts_collector.cc",
-      "third_party/quiche/src/http2/test_tools/frame_parts_collector.h",
-      "third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.cc",
-      "third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.h",
-      "third_party/quiche/src/http2/test_tools/http2_random.cc",
-      "third_party/quiche/src/http2/test_tools/http2_random.h",
-      "third_party/quiche/src/http2/test_tools/http2_random_test.cc",
-      "third_party/quiche/src/http2/tools/http2_frame_builder.cc",
-      "third_party/quiche/src/http2/tools/http2_frame_builder.h",
-      "third_party/quiche/src/http2/tools/random_decoder_test.cc",
-      "third_party/quiche/src/http2/tools/random_decoder_test.h",
-      "third_party/quiche/src/http2/tools/random_util.cc",
-      "third_party/quiche/src/http2/tools/random_util.h",
-      "third_party/quiche/src/spdy/core/array_output_buffer.cc",
-      "third_party/quiche/src/spdy/core/array_output_buffer.h",
-      "third_party/quiche/src/spdy/core/array_output_buffer_test.cc",
-      "third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc",
-      "third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc",
-      "third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc",
-      "third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc",
-      "third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc",
-      "third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc",
-      "third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc",
-      "third_party/quiche/src/spdy/core/hpack/hpack_static_table_test.cc",
-      "third_party/quiche/src/spdy/core/mock_spdy_framer_visitor.cc",
-      "third_party/quiche/src/spdy/core/mock_spdy_framer_visitor.h",
-      "third_party/quiche/src/spdy/core/priority_write_scheduler_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc",
-      "third_party/quiche/src/spdy/core/spdy_deframer_visitor.h",
-      "third_party/quiche/src/spdy/core/spdy_deframer_visitor_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_frame_builder_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_frame_reader_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_framer_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_header_block_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_no_op_visitor.cc",
-      "third_party/quiche/src/spdy/core/spdy_no_op_visitor.h",
-      "third_party/quiche/src/spdy/core/spdy_pinnable_buffer_piece_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_protocol_test.cc",
-      "third_party/quiche/src/spdy/core/spdy_protocol_test_utils.cc",
-      "third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h",
-      "third_party/quiche/src/spdy/core/spdy_test_utils.cc",
-      "third_party/quiche/src/spdy/core/spdy_test_utils.h",
-      "third_party/quiche/src/spdy/platform/api/spdy_mem_slice_test.cc",
-      "third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc",
-      "third_party/quiche/src/spdy/platform/api/spdy_test_helpers.h",
-      "tools/content_decoder_tool/content_decoder_tool.cc",
-      "tools/content_decoder_tool/content_decoder_tool.h",
-      "tools/content_decoder_tool/content_decoder_tool_unittest.cc",
-      "tools/quic/quic_simple_client_test.cc",
+    # TraceVisitor is not supported yet.
+    # "third_party/quic/core/quic_trace_visitor_test.cc",
+    "third_party/quic/core/quic_unacked_packet_map_test.cc",
+    "third_party/quic/core/quic_utils_test.cc",
+    "third_party/quic/core/quic_version_manager_test.cc",
+    "third_party/quic/core/quic_versions_test.cc",
+    "third_party/quic/core/quic_write_blocked_list_test.cc",
+    "third_party/quic/core/tls_handshaker_test.cc",
+    "third_party/quic/core/uber_quic_stream_id_manager_test.cc",
+    "third_party/quic/platform/api/quic_containers_test.cc",
+    "third_party/quic/platform/api/quic_endian_test.cc",
+    "third_party/quic/platform/api/quic_hostname_utils_test.cc",
+    "third_party/quic/platform/api/quic_mem_slice_span_test.cc",
+    "third_party/quic/platform/api/quic_mem_slice_storage_test.cc",
+    "third_party/quic/platform/api/quic_mem_slice_test.cc",
+    "third_party/quic/platform/api/quic_reference_counted_test.cc",
+    "third_party/quic/platform/api/quic_singleton_test.cc",
+    "third_party/quic/platform/api/quic_str_cat_test.cc",
+    "third_party/quic/platform/api/quic_text_utils_test.cc",
+    "third_party/quic/platform/impl/quic_chromium_clock_test.cc",
+    "third_party/quic/platform/impl/quic_uint128_impl_unittest.cc",
+    "third_party/quic/test_tools/crypto_test_utils_test.cc",
+    "third_party/quic/test_tools/mock_quic_time_wait_list_manager.cc",
+    "third_party/quic/test_tools/mock_quic_time_wait_list_manager.h",
+    "third_party/quic/test_tools/quic_test_utils_test.cc",
+    "third_party/quic/test_tools/simple_session_notifier_test.cc",
+    "third_party/quic/test_tools/simulator/quic_endpoint_test.cc",
+    "third_party/quic/test_tools/simulator/simulator_test.cc",
+    "third_party/quiche/src/http2/decoder/decode_buffer_test.cc",
+    "third_party/quiche/src/http2/decoder/decode_http2_structures_test.cc",
+    "third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.cc",
+    "third_party/quiche/src/http2/decoder/frame_decoder_state_test_util.h",
+    "third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.cc",
+    "third_party/quiche/src/http2/decoder/http2_frame_decoder_listener_test_util.h",
+    "third_party/quiche/src/http2/decoder/http2_frame_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/http2_structure_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/http2_structure_decoder_test_util.cc",
+    "third_party/quiche/src/http2/decoder/http2_structure_decoder_test_util.h",
+    "third_party/quiche/src/http2/decoder/payload_decoders/altsvc_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/continuation_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/data_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/goaway_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/headers_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/payload_decoder_base_test_util.h",
+    "third_party/quiche/src/http2/decoder/payload_decoders/ping_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/priority_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/push_promise_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/rst_stream_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/settings_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/unknown_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/decoder/payload_decoders/window_update_payload_decoder_test.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_block_collector.h",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_block_decoder_test.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_decoder_state_test.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_decoder_tables_test.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_decoder_test.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_entry_collector.h",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder_test.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_entry_type_decoder_test.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.h",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_string_decoder_test.cc",
+    "third_party/quiche/src/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc",
+    "third_party/quiche/src/http2/hpack/hpack_string_test.cc",
+    "third_party/quiche/src/http2/hpack/http2_hpack_constants_test.cc",
+    "third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder_test.cc",
+    "third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder_test.cc",
+    "third_party/quiche/src/http2/hpack/huffman/hpack_huffman_transcoder_test.cc",
+    "third_party/quiche/src/http2/hpack/tools/hpack_block_builder.cc",
+    "third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h",
+    "third_party/quiche/src/http2/hpack/tools/hpack_block_builder_test.cc",
+    "third_party/quiche/src/http2/hpack/tools/hpack_example.cc",
+    "third_party/quiche/src/http2/hpack/tools/hpack_example.h",
+    "third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder_test.cc",
+    "third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder_test.cc",
+    "third_party/quiche/src/http2/hpack/varint/hpack_varint_round_trip_test.cc",
+    "third_party/quiche/src/http2/http2_constants_test.cc",
+    "third_party/quiche/src/http2/http2_constants_test_util.cc",
+    "third_party/quiche/src/http2/http2_constants_test_util.h",
+    "third_party/quiche/src/http2/http2_structures_test.cc",
+    "third_party/quiche/src/http2/http2_structures_test_util.cc",
+    "third_party/quiche/src/http2/http2_structures_test_util.h",
+    "third_party/quiche/src/http2/platform/api/http2_mock_log.h",
+    "third_party/quiche/src/http2/platform/api/http2_string_utils_test.cc",
+    "third_party/quiche/src/http2/platform/api/http2_test_helpers.h",
+    "third_party/quiche/src/http2/test_tools/frame_parts.cc",
+    "third_party/quiche/src/http2/test_tools/frame_parts.h",
+    "third_party/quiche/src/http2/test_tools/frame_parts_collector.cc",
+    "third_party/quiche/src/http2/test_tools/frame_parts_collector.h",
+    "third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.cc",
+    "third_party/quiche/src/http2/test_tools/frame_parts_collector_listener.h",
+    "third_party/quiche/src/http2/test_tools/http2_random.cc",
+    "third_party/quiche/src/http2/test_tools/http2_random.h",
+    "third_party/quiche/src/http2/test_tools/http2_random_test.cc",
+    "third_party/quiche/src/http2/tools/http2_frame_builder.cc",
+    "third_party/quiche/src/http2/tools/http2_frame_builder.h",
+    "third_party/quiche/src/http2/tools/random_decoder_test.cc",
+    "third_party/quiche/src/http2/tools/random_decoder_test.h",
+    "third_party/quiche/src/http2/tools/random_util.cc",
+    "third_party/quiche/src/http2/tools/random_util.h",
+    "third_party/quiche/src/spdy/core/array_output_buffer.cc",
+    "third_party/quiche/src/spdy/core/array_output_buffer.h",
+    "third_party/quiche/src/spdy/core/array_output_buffer_test.cc",
+    "third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter_test.cc",
+    "third_party/quiche/src/spdy/core/hpack/hpack_encoder_test.cc",
+    "third_party/quiche/src/spdy/core/hpack/hpack_entry_test.cc",
+    "third_party/quiche/src/spdy/core/hpack/hpack_header_table_test.cc",
+    "third_party/quiche/src/spdy/core/hpack/hpack_huffman_table_test.cc",
+    "third_party/quiche/src/spdy/core/hpack/hpack_output_stream_test.cc",
+    "third_party/quiche/src/spdy/core/hpack/hpack_round_trip_test.cc",
+    "third_party/quiche/src/spdy/core/hpack/hpack_static_table_test.cc",
+    "third_party/quiche/src/spdy/core/mock_spdy_framer_visitor.cc",
+    "third_party/quiche/src/spdy/core/mock_spdy_framer_visitor.h",
+    "third_party/quiche/src/spdy/core/priority_write_scheduler_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_deframer_visitor.cc",
+    "third_party/quiche/src/spdy/core/spdy_deframer_visitor.h",
+    "third_party/quiche/src/spdy/core/spdy_deframer_visitor_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_frame_builder_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_frame_reader_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_framer_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_header_block_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_no_op_visitor.cc",
+    "third_party/quiche/src/spdy/core/spdy_no_op_visitor.h",
+    "third_party/quiche/src/spdy/core/spdy_pinnable_buffer_piece_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_prefixed_buffer_reader_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_protocol_test.cc",
+    "third_party/quiche/src/spdy/core/spdy_protocol_test_utils.cc",
+    "third_party/quiche/src/spdy/core/spdy_protocol_test_utils.h",
+    "third_party/quiche/src/spdy/core/spdy_test_utils.cc",
+    "third_party/quiche/src/spdy/core/spdy_test_utils.h",
+    "third_party/quiche/src/spdy/platform/api/spdy_mem_slice_test.cc",
+    "third_party/quiche/src/spdy/platform/api/spdy_string_utils_test.cc",
+    "third_party/quiche/src/spdy/platform/api/spdy_test_helpers.h",
+    "tools/content_decoder_tool/content_decoder_tool.cc",
+    "tools/content_decoder_tool/content_decoder_tool.h",
+    "tools/content_decoder_tool/content_decoder_tool_unittest.cc",
+    "tools/quic/quic_simple_client_test.cc",
 
-      # "tools/tld_cleanup/tld_cleanup_util_unittest.cc",
-      "url_request/redirect_info_unittest.cc",
-      "url_request/redirect_util_unittest.cc",
-      "url_request/report_sender_unittest.cc",
-      "url_request/url_fetcher_impl_unittest.cc",
-      "url_request/url_fetcher_response_writer_unittest.cc",
-      "url_request/url_request_context_builder_unittest.cc",
-      "url_request/url_request_context_unittest.cc",
-      "url_request/url_request_data_job_unittest.cc",
+    # "tools/tld_cleanup/tld_cleanup_util_unittest.cc",
+    "url_request/redirect_info_unittest.cc",
+    "url_request/redirect_util_unittest.cc",
+    "url_request/report_sender_unittest.cc",
+    "url_request/url_fetcher_impl_unittest.cc",
+    "url_request/url_fetcher_response_writer_unittest.cc",
+    "url_request/url_request_context_builder_unittest.cc",
+    "url_request/url_request_context_unittest.cc",
+    "url_request/url_request_data_job_unittest.cc",
 
-      # Cobalt handles File Job itself.
-      # "url_request/url_request_file_job_unittest.cc",
-      "url_request/url_request_filter_unittest.cc",
+    # Cobalt handles File Job itself.
+    # "url_request/url_request_file_job_unittest.cc",
+    "url_request/url_request_filter_unittest.cc",
 
-      # ftp not supported by Cobalt.
-      # "url_request/url_request_file_dir_job_unittest.cc",
-      # "url_request/url_request_ftp_job_unittest.cc",
+    # ftp not supported by Cobalt.
+    # "url_request/url_request_file_dir_job_unittest.cc",
+    # "url_request/url_request_ftp_job_unittest.cc",
 
-      "url_request/url_request_http_job_unittest.cc",
-      "url_request/url_request_job_factory_impl_unittest.cc",
-      "url_request/url_request_job_unittest.cc",
-      "url_request/url_request_quic_unittest.cc",
-      "url_request/url_request_simple_job_unittest.cc",
-      "url_request/url_request_throttler_simulation_unittest.cc",
-      "url_request/url_request_throttler_test_support.cc",
-      "url_request/url_request_throttler_test_support.h",
-      "url_request/url_request_throttler_unittest.cc",
-      "url_request/url_request_unittest.cc",
+    "url_request/url_request_http_job_unittest.cc",
+    "url_request/url_request_job_factory_impl_unittest.cc",
+    "url_request/url_request_job_unittest.cc",
+    "url_request/url_request_quic_unittest.cc",
+    "url_request/url_request_simple_job_unittest.cc",
+    "url_request/url_request_throttler_simulation_unittest.cc",
+    "url_request/url_request_throttler_test_support.cc",
+    "url_request/url_request_throttler_test_support.h",
+    "url_request/url_request_throttler_unittest.cc",
+    "url_request/url_request_unittest.cc",
 
-      # Also include unittests for websockets.
-      "server/http_connection_unittest.cc",
-      "server/http_server_response_info_unittest.cc",
-      "server/http_server_unittest.cc",
-      "server/web_socket_encoder_unittest.cc",
-      "websockets/websocket_basic_handshake_stream_test.cc",
-      "websockets/websocket_basic_stream_adapters_test.cc",
-      "websockets/websocket_basic_stream_test.cc",
-      "websockets/websocket_channel_test.cc",
-      "websockets/websocket_deflate_parameters_test.cc",
-      "websockets/websocket_deflate_predictor_impl_test.cc",
-      "websockets/websocket_deflate_stream_test.cc",
-      "websockets/websocket_deflater_test.cc",
+    # Also include unittests for websockets.
+    "server/http_connection_unittest.cc",
+    "server/http_server_response_info_unittest.cc",
+    "server/http_server_unittest.cc",
+    "server/web_socket_encoder_unittest.cc",
+    "websockets/websocket_basic_handshake_stream_test.cc",
+    "websockets/websocket_basic_stream_adapters_test.cc",
+    "websockets/websocket_basic_stream_test.cc",
+    "websockets/websocket_channel_test.cc",
+    "websockets/websocket_deflate_parameters_test.cc",
+    "websockets/websocket_deflate_predictor_impl_test.cc",
+    "websockets/websocket_deflate_stream_test.cc",
+    "websockets/websocket_deflater_test.cc",
 
-      # Uses the unsupported SpawnedWebServer.
-      #"websockets/websocket_end_to_end_test.cc",
+    # Uses the unsupported SpawnedWebServer.
+    #"websockets/websocket_end_to_end_test.cc",
 
-      "websockets/websocket_errors_test.cc",
-      "websockets/websocket_extension_parser_test.cc",
-      "websockets/websocket_extension_test.cc",
-      "websockets/websocket_frame_parser_test.cc",
-      "websockets/websocket_frame_test.cc",
-      "websockets/websocket_handshake_challenge_test.cc",
-      "websockets/websocket_handshake_stream_create_helper_test.cc",
-      "websockets/websocket_inflater_test.cc",
-      "websockets/websocket_stream_cookie_test.cc",
-      "websockets/websocket_stream_create_test_base.cc",
-      "websockets/websocket_stream_create_test_base.h",
-      "websockets/websocket_stream_test.cc",
-      "websockets/websocket_test_util.cc",
-      "websockets/websocket_test_util.h",
+    "websockets/websocket_errors_test.cc",
+    "websockets/websocket_extension_parser_test.cc",
+    "websockets/websocket_extension_test.cc",
+    "websockets/websocket_frame_parser_test.cc",
+    "websockets/websocket_frame_test.cc",
+    "websockets/websocket_handshake_challenge_test.cc",
+    "websockets/websocket_handshake_stream_create_helper_test.cc",
+    "websockets/websocket_inflater_test.cc",
+    "websockets/websocket_stream_cookie_test.cc",
+    "websockets/websocket_stream_create_test_base.cc",
+    "websockets/websocket_stream_create_test_base.h",
+    "websockets/websocket_stream_test.cc",
+    "websockets/websocket_test_util.cc",
+    "websockets/websocket_test_util.h",
 
-      # Also include unittests for network reporting.
-      "reporting/reporting_browsing_data_remover_unittest.cc",
-      "reporting/reporting_cache_unittest.cc",
-      "reporting/reporting_delivery_agent_unittest.cc",
-      "reporting/reporting_endpoint_manager_unittest.cc",
-      "reporting/reporting_garbage_collector_unittest.cc",
-      "reporting/reporting_header_parser_unittest.cc",
-      "reporting/reporting_network_change_observer_unittest.cc",
-      "reporting/reporting_service_unittest.cc",
-    ]
+    # Also include unittests for network reporting.
+    "reporting/reporting_browsing_data_remover_unittest.cc",
+    "reporting/reporting_cache_unittest.cc",
+    "reporting/reporting_delivery_agent_unittest.cc",
+    "reporting/reporting_endpoint_manager_unittest.cc",
+    "reporting/reporting_garbage_collector_unittest.cc",
+    "reporting/reporting_header_parser_unittest.cc",
+    "reporting/reporting_network_change_observer_unittest.cc",
+    "reporting/reporting_service_unittest.cc",
+  ]
 
-    if (is_win) {
-      sources += [
-        "dns/dns_config_service_win_unittest.cc",
-        "http/http_auth_sspi_win_unittest.cc",
-        "proxy_resolution/dhcp_pac_file_adapter_fetcher_win_unittest.cc",
-        "proxy_resolution/dhcp_pac_file_fetcher_win_unittest.cc",
-        "ssl/ssl_platform_key_win_unittest.cc",
-      ]
-    }
-
-    # Avoid compiler errors due to conversion from "0x00" to char.
-    if (is_win) {
-      cflags = [ "/Wno-narrowing" ]
-    } else {
-      cflags = [ "-Wno-narrowing" ]
-    }
-
-    defines = [
-      # To be removed in the future when want to enable HTTP cache.
-      "HTTP_CACHE_DISABLED_FOR_STARBOARD",
-      "GMOCK_NO_MOVE_MOCK",
-    ]
-
-    configs += [ "//build/config/compiler:chromium_code" ]
-    configs -= [ "//starboard/build/config:size" ]
-
-    deps = [
-      # This is needed by dial_http_server in net
-      ":http_server",
-      ":net",
-      ":net_generated_registry_controlled_domains",
-      ":net_nqe_proto",
-      ":net_quic_proto",
-      ":net_unittest_files",
-      ":quic_test_tools",
-      ":test_support",
-      ":third_party_unittest_files",
-      "//base",
-      "//base:i18n",
-      "//base/test:test_support",
-      "//base/third_party/dynamic_annotations",
-      "//crypto",
-      "//testing/gmock",
-      "//testing/gtest",
-      "//third_party/boringssl:crypto",
-      "//third_party/zlib",
-      "//url",
-      "//url:url_features",
+  if (is_win && !is_starboard) {
+    sources += [
+      "dns/dns_config_service_win_unittest.cc",
+      "http/http_auth_sspi_win_unittest.cc",
+      "proxy_resolution/dhcp_pac_file_adapter_fetcher_win_unittest.cc",
+      "proxy_resolution/dhcp_pac_file_fetcher_win_unittest.cc",
+      "ssl/ssl_platform_key_win_unittest.cc",
     ]
   }
+
+  # Avoid compiler errors due to conversion from "0x00" to char.
+  if (is_win) {
+    if (!is_starboard) {
+      cflags = [ "/Wno-narrowing" ]
+    }
+  } else {
+    cflags = [ "-Wno-narrowing" ]
+  }
+
+  defines = [
+    # To be removed in the future when want to enable HTTP cache.
+    "HTTP_CACHE_DISABLED_FOR_STARBOARD",
+    "GMOCK_NO_MOVE_MOCK",
+  ]
+
+  configs += [ "//build/config/compiler:chromium_code" ]
+  configs -= [ "//starboard/build/config:size" ]
+
+  deps = [
+    # This is needed by dial_http_server in net
+    ":http_server",
+    ":net",
+    ":net_generated_registry_controlled_domains",
+    ":net_nqe_proto",
+    ":net_quic_proto",
+    ":net_unittest_files",
+    ":quic_test_tools",
+    ":test_support",
+    ":third_party_unittest_files",
+    "//base",
+    "//base:i18n",
+    "//base/test:test_support",
+    "//base/third_party/dynamic_annotations",
+    "//crypto",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/boringssl:crypto",
+    "//third_party/zlib",
+    "//url",
+    "//url:url_features",
+  ]
 }
 
 static_library("test_support") {
diff --git a/net/DEPS b/net/DEPS
new file mode 100644
index 0000000..df77e03
--- /dev/null
+++ b/net/DEPS
@@ -0,0 +1,80 @@
+include_rules = [
+  "+crypto",
+  "+gin",
+  "+jni",
+  "+mojo/public",
+  "+third_party/apple_apsl",
+  "+third_party/boringssl/src/include",
+  "+third_party/nss",
+  "+third_party/protobuf/src/google/protobuf",
+  "+third_party/zlib",
+  "+v8",
+
+  # Most of net should not depend on icu, and brotli to keep size down when
+  # built as a library.
+  "-base/i18n",
+  "-third_party/brotli",
+  "-third_party/icu",
+]
+
+specific_include_rules = {
+  # Within net, only used by file: requests.
+  "directory_lister(\.cc|_unittest\.cc)": [
+    "+base/i18n",
+  ],
+
+  # Functions largely not used by the rest of net.
+  "directory_listing\.cc": [
+    "+base/i18n",
+  ],
+
+  # Within net, only used by file: requests.
+  "filename_util_icu\.cc": [
+    "+base/i18n/file_util_icu.h",
+  ],
+
+  # Consolidated string functions that depend on icu.
+  "net_string_util_icu\.cc": [
+    "+base/i18n/case_conversion.h",
+    "+base/i18n/i18n_constants.h",
+    "+base/i18n/icu_string_conversions.h",
+    "+third_party/icu/source/common/unicode/ucnv.h"
+  ],
+
+  "websocket_channel\.h": [
+    "+base/i18n",
+  ],
+
+  "ftp_util\.cc": [
+    "+base/i18n",
+    "+third_party/icu",
+  ],
+  "ftp_directory_listing_parser\.cc": [
+    "+base/i18n",
+  ],
+
+  "run_all_unittests\.cc": [
+    "+mojo/core/embedder",
+  ],
+
+  "brotli_source_stream\.cc": [
+    "+third_party/brotli",
+  ],
+
+  "ssl_client_socket_impl\.cc": [
+    "+third_party/brotli",
+  ],
+
+  "fuzzer_test_support.cc": [
+    "+base/i18n",
+  ],
+
+  # Needed for fuzz targets written using libprotobuf-mutator library.
+  ".*fuzz.*": [
+    "+third_party/libprotobuf-mutator",
+  ]
+}
+
+skip_child_includes = [
+  "third_party",
+]
diff --git a/net/OWNERS b/net/OWNERS
new file mode 100644
index 0000000..5956672
--- /dev/null
+++ b/net/OWNERS
@@ -0,0 +1,21 @@
+agl@chromium.org
+asanka@chromium.org
+bnc@chromium.org
+davidben@chromium.org
+eroman@chromium.org
+jkarlin@chromium.org
+mattm@chromium.org
+mef@chromium.org
+mmenke@chromium.org
+morlovich@chromium.org
+nharper@chromium.org
+pauljensen@chromium.org
+rch@chromium.org
+rsleevi@chromium.org
+zhongyi@chromium.org
+
+per-file BUILD.gn=file://net/nqe/OWNERS
+per-file BUILD.gn=file://net/websockets/OWNERS
+
+# TEAM: net-dev@chromium.org
+# COMPONENT: Internals>Network
diff --git a/net/base/DEPS b/net/base/DEPS
new file mode 100644
index 0000000..fac79a6
--- /dev/null
+++ b/net/base/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+grit",  # For generated headers
+]
diff --git a/net/base/OWNERS b/net/base/OWNERS
new file mode 100644
index 0000000..ddc7882
--- /dev/null
+++ b/net/base/OWNERS
@@ -0,0 +1,5 @@
+# For security review of MIME sniffing to avoid introducing security bugs
+per-file mime_sniffer*=set noparent
+per-file mime_sniffer*=rsleevi@chromium.org
+per-file mime_sniffer*=asanka@chromium.org
+per-file mime_sniffer*=mmenke@chromium.org
diff --git a/net/base/registry_controlled_domains/OWNERS b/net/base/registry_controlled_domains/OWNERS
new file mode 100644
index 0000000..10be63d7
--- /dev/null
+++ b/net/base/registry_controlled_domains/OWNERS
@@ -0,0 +1,5 @@
+pam@chromium.org
+pkasting@chromium.org
+rsleevi@chromium.org
+
+# COMPONENT: Internals>Network
diff --git a/net/cert/OWNERS b/net/cert/OWNERS
new file mode 100644
index 0000000..c5d05bc
--- /dev/null
+++ b/net/cert/OWNERS
@@ -0,0 +1,7 @@
+davidben@chromium.org
+eroman@chromium.org
+mattm@chromium.org
+rsleevi@chromium.org
+svaldez@chromium.org
+
+# COMPONENT: Internals>Network>Certificate
diff --git a/net/cert_net/OWNERS b/net/cert_net/OWNERS
new file mode 100644
index 0000000..b7d227e
--- /dev/null
+++ b/net/cert_net/OWNERS
@@ -0,0 +1,5 @@
+eroman@chromium.org
+mattm@chromium.org
+rsleevi@chromium.org
+
+# COMPONENT: Internals>Network>Certificate
diff --git a/net/cookies/OWNERS b/net/cookies/OWNERS
new file mode 100644
index 0000000..fb0dd2d
--- /dev/null
+++ b/net/cookies/OWNERS
@@ -0,0 +1,6 @@
+estark@chromium.org
+mkwst@chromium.org
+mmenke@chromium.org
+morlovich@chromium.org
+
+# COMPONENT: Internals>Network>Cookies
diff --git a/net/data/fuzzer_data/.gitattributes b/net/data/fuzzer_data/.gitattributes
new file mode 100644
index 0000000..77d76d4
--- /dev/null
+++ b/net/data/fuzzer_data/.gitattributes
@@ -0,0 +1 @@
+*       binary
diff --git a/net/data/websocket/OWNERS b/net/data/websocket/OWNERS
new file mode 100644
index 0000000..d5f5069
--- /dev/null
+++ b/net/data/websocket/OWNERS
@@ -0,0 +1,5 @@
+ricea@chromium.org
+yhirano@chromium.org
+
+# TEAM: blink-network-dev@chromium.org
+# COMPONENT: Blink>Network>WebSockets
diff --git a/net/der/OWNERS b/net/der/OWNERS
new file mode 100644
index 0000000..4a621d5
--- /dev/null
+++ b/net/der/OWNERS
@@ -0,0 +1,6 @@
+davidben@chromium.org
+mattm@chromium.org
+nharper@chromium.org
+svaldez@chromium.org
+
+# COMPONENT: Internals>Network>Certificate
diff --git a/net/disk_cache/OWNERS b/net/disk_cache/OWNERS
new file mode 100644
index 0000000..7e2c65e
--- /dev/null
+++ b/net/disk_cache/OWNERS
@@ -0,0 +1,4 @@
+jkarlin@chromium.org
+morlovich@chromium.org
+
+# COMPONENT: Internals>Network>Cache
diff --git a/net/disk_cache/simple/OWNERS b/net/disk_cache/simple/OWNERS
new file mode 100644
index 0000000..bce1299
--- /dev/null
+++ b/net/disk_cache/simple/OWNERS
@@ -0,0 +1,3 @@
+pasko@chromium.org
+
+# COMPONENT: Internals>Network>Cache>Simple
diff --git a/net/dns/OWNERS b/net/dns/OWNERS
new file mode 100644
index 0000000..52b51f2
--- /dev/null
+++ b/net/dns/OWNERS
@@ -0,0 +1,4 @@
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+
+# COMPONENT: Internals>Network>DNS
diff --git a/net/extras/sqlite/DEPS b/net/extras/sqlite/DEPS
new file mode 100644
index 0000000..40ed5cf
--- /dev/null
+++ b/net/extras/sqlite/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+base",
+  "+net",
+  "+sql",
+]
diff --git a/net/extras/sqlite/OWNERS b/net/extras/sqlite/OWNERS
new file mode 100644
index 0000000..5b2fdf3
--- /dev/null
+++ b/net/extras/sqlite/OWNERS
@@ -0,0 +1 @@
+pwnall@chromium.org
diff --git a/net/filter/OWNERS b/net/filter/OWNERS
new file mode 100644
index 0000000..89b786c
--- /dev/null
+++ b/net/filter/OWNERS
@@ -0,0 +1,3 @@
+file://net/OWNERS
+
+# COMPONENT: Internals>Network>Filters
diff --git a/net/ftp/OWNERS b/net/ftp/OWNERS
new file mode 100644
index 0000000..71184cb
--- /dev/null
+++ b/net/ftp/OWNERS
@@ -0,0 +1,4 @@
+eroman@chromium.org
+mmenke@chromium.org
+
+# COMPONENT: Internals>Network>FTP
diff --git a/net/http/OWNERS b/net/http/OWNERS
new file mode 100644
index 0000000..09d2e61
--- /dev/null
+++ b/net/http/OWNERS
@@ -0,0 +1,3 @@
+per-file http_cache_*=shivanisha@chromium.org
+per-file transport_security_state_static.*=nharper@chromium.org
+per-file transport_security_state_static.*=palmer@chromium.org
diff --git a/net/interfaces/OWNERS b/net/interfaces/OWNERS
new file mode 100644
index 0000000..b957a91
--- /dev/null
+++ b/net/interfaces/OWNERS
@@ -0,0 +1,8 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_struct_traits*.*=set noparent
+per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/net/log/OWNERS b/net/log/OWNERS
new file mode 100644
index 0000000..be7a5ec
--- /dev/null
+++ b/net/log/OWNERS
@@ -0,0 +1,4 @@
+eroman@chromium.org
+mmenke@chormium.org
+
+# COMPONENT: Internals>Network>Logging
diff --git a/net/nqe/OWNERS b/net/nqe/OWNERS
new file mode 100644
index 0000000..30f461a
--- /dev/null
+++ b/net/nqe/OWNERS
@@ -0,0 +1,9 @@
+# Primary
+tbansal@chromium.org
+ryansturm@chromium.org
+
+# Secondary
+bengr@chromium.org
+
+# TEAM: net-dev@chromium.org
+# COMPONENT: Internals>Network>NetworkQuality
\ No newline at end of file
diff --git a/net/ntlm/OWNERS b/net/ntlm/OWNERS
new file mode 100644
index 0000000..156ce37
--- /dev/null
+++ b/net/ntlm/OWNERS
@@ -0,0 +1,6 @@
+asanka@chromium.org
+rsleevi@chromium.org
+zentaro@chromium.org
+
+# COMPONENT: Internals>Network>Auth
+# TEAM: net-dev@chromium.org
diff --git a/net/proxy_resolution/OWNERS b/net/proxy_resolution/OWNERS
new file mode 100644
index 0000000..b03baf3
--- /dev/null
+++ b/net/proxy_resolution/OWNERS
@@ -0,0 +1,6 @@
+eroman@chromium.org
+mmenke@chromium.org
+
+per-file *_v8*=jochen@chromium.org
+
+# COMPONENT: Internals>Network>Proxy
diff --git a/net/quic/OWNERS b/net/quic/OWNERS
new file mode 100644
index 0000000..1835f5b
--- /dev/null
+++ b/net/quic/OWNERS
@@ -0,0 +1,4 @@
+rch@chromium.org
+zhongyi@chromium.org
+
+# COMPONENT: Internals>Network>QUIC
diff --git a/net/socket/OWNERS b/net/socket/OWNERS
new file mode 100644
index 0000000..df81ef0
--- /dev/null
+++ b/net/socket/OWNERS
@@ -0,0 +1,2 @@
+# WebSocket
+per-file websocket*=file://net/websockets/OWNERS
diff --git a/net/spdy/OWNERS b/net/spdy/OWNERS
new file mode 100644
index 0000000..a5bbda6
--- /dev/null
+++ b/net/spdy/OWNERS
@@ -0,0 +1,3 @@
+file://net/third_party/quiche/src/http2/OWNERS
+
+# COMPONENT: Internals>Network>HTTP2
diff --git a/net/ssl/OWNERS b/net/ssl/OWNERS
new file mode 100644
index 0000000..0d1ddab
--- /dev/null
+++ b/net/ssl/OWNERS
@@ -0,0 +1,5 @@
+davidben@chromium.org
+mattm@chromium.org
+svaldez@chromium.org
+
+# COMPONENT: Internals>Network>SSL
diff --git a/net/test/android/javatests/src/org/chromium/net/test/OWNERS b/net/test/android/javatests/src/org/chromium/net/test/OWNERS
new file mode 100644
index 0000000..89442ab
--- /dev/null
+++ b/net/test/android/javatests/src/org/chromium/net/test/OWNERS
@@ -0,0 +1,2 @@
+per-file *.aidl=set noparent
+per-file *.aidl=file://ipc/SECURITY_OWNERS
\ No newline at end of file
diff --git a/net/third_party/nss/OWNERS b/net/third_party/nss/OWNERS
new file mode 100644
index 0000000..d332218
--- /dev/null
+++ b/net/third_party/nss/OWNERS
@@ -0,0 +1,6 @@
+agl@chromium.org
+davidben@chromium.org
+rsleevi@chromium.org
+wtc@chromium.org
+
+# COMPONENT: Internals>Network>SSL
diff --git a/net/third_party/uri_template/OWNERS b/net/third_party/uri_template/OWNERS
new file mode 100644
index 0000000..3c466ff
--- /dev/null
+++ b/net/third_party/uri_template/OWNERS
@@ -0,0 +1,3 @@
+mmenke@chromium.org
+
+# COMPONENT: Internals>Network>DNS
diff --git a/net/tools/DEPS b/net/tools/DEPS
new file mode 100644
index 0000000..3b22d68
--- /dev/null
+++ b/net/tools/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+  "+base/i18n",
+]
+
+skip_child_includes = [
+  "balsa",
+]
diff --git a/net/tools/dafsa/PRESUBMIT.py b/net/tools/dafsa/PRESUBMIT.py
new file mode 100644
index 0000000..253fcbb
--- /dev/null
+++ b/net/tools/dafsa/PRESUBMIT.py
@@ -0,0 +1,32 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+"""Chromium presubmit script for src/net/tools/dafsa."""
+
+
+def _RunMakeDafsaTests(input_api, output_api):
+  """Runs unittest for make_dafsa if any related file has been modified."""
+  files = ('net/tools/dafsa/make_dafsa.py',
+           'net/tools/dafsa/make_dafsa_unittest.py')
+  if not any(f in input_api.LocalPaths() for f in files):
+    return []
+  test_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
+                                     'make_dafsa_unittest.py')
+  cmd_name = 'make_dafsa_unittest'
+  cmd = [input_api.python_executable, test_path]
+  test_cmd = input_api.Command(
+    name=cmd_name,
+    cmd=cmd,
+    kwargs={},
+    message=output_api.PresubmitPromptWarning)
+  return input_api.RunTests([test_cmd])
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return _RunMakeDafsaTests(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return _RunMakeDafsaTests(input_api, output_api)
diff --git a/net/tools/tld_cleanup/OWNERS b/net/tools/tld_cleanup/OWNERS
new file mode 100644
index 0000000..f6e734a
--- /dev/null
+++ b/net/tools/tld_cleanup/OWNERS
@@ -0,0 +1 @@
+pam@chromium.org
diff --git a/net/websockets/OWNERS b/net/websockets/OWNERS
new file mode 100644
index 0000000..d5f5069
--- /dev/null
+++ b/net/websockets/OWNERS
@@ -0,0 +1,5 @@
+ricea@chromium.org
+yhirano@chromium.org
+
+# TEAM: blink-network-dev@chromium.org
+# COMPONENT: Blink>Network>WebSockets
diff --git a/precommit_hooks/check_copyright_year.py b/precommit_hooks/check_copyright_year.py
index 6e9f489..e682ac4 100755
--- a/precommit_hooks/check_copyright_year.py
+++ b/precommit_hooks/check_copyright_year.py
@@ -70,13 +70,13 @@
         if match:
           created_year = int(match.group('created'))
           if filename in new_files and created_year != current_year:
-            errors.append(f'Copyright header for file {filename}'
-                          f' has wrong year {created_year}')
+            print(f'Copyright header for file {filename} has wrong year '
+                  f'{created_year}')
 
           year = match.group('current')
           if year and int(year) != current_year:
-            errors.append(f'Copyright header for file {filename}'
-                          f' has wrong ending year {year}')
+            print(f'Copyright header for file {filename} has wrong ending year'
+                  f' {year}')
 
           # Strip to get rid of possible newline
           author = match.group('author').rstrip()
diff --git a/starboard/BUILD.gn b/starboard/BUILD.gn
index 22b5233..eefbcc9 100644
--- a/starboard/BUILD.gn
+++ b/starboard/BUILD.gn
@@ -16,17 +16,16 @@
   testonly = true
 
   deps = [
-    ":starboard",
+    ":default",
     "//starboard/client_porting/cwrappers:cwrappers_test",
     "//starboard/client_porting/eztime",
     "//starboard/client_porting/eztime:eztime_test",
     "//starboard/client_porting/icu_init",
     "//starboard/client_porting/poem:poem_unittests",
     "//starboard/examples/window:starboard_window_example",
+    "//starboard/loader_app:app_key_files_test",
     "//starboard/nplb",
     "//starboard/nplb/nplb_evergreen_compat_tests",
-
-    # "//starboard/tools",  TODO(andrewsavage)
   ]
 
   if (gl_type != "none") {
@@ -67,6 +66,13 @@
   }
 }
 
+group("default") {
+  deps = [
+    ":starboard",
+    "//starboard/tools:build_app_launcher_zip",
+  ]
+}
+
 group("starboard") {
   public_deps = [
     ":starboard_headers_only",
@@ -150,10 +156,11 @@
     "types.h",
     "user.h",
     "window.h",
-
-    # Include private headers, if present.
-    # "<!@pymod_do_main(starboard.build.gyp_functions file_glob <(DEPTH)/starboard/private *.h)",
   ]
+
+  if (is_internal_build) {
+    public_deps = [ "//starboard/private:private_starboard_headers" ]
+  }
 }
 
 if (platform_tests_path == "") {
@@ -168,5 +175,7 @@
       ":starboard",
       "//testing/gmock",
     ]
+
+    content_deps = [ "//third_party/icu:icudata" ]
   }
 }
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 1b54143..e24faf6 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
@@ -40,13 +40,14 @@
 
   @Override
   public void onCreate() {
-    super.onCreate();
-    Log.i(TAG, "Creating a Media playback foreground service.");
     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();
   }
 
   @Override
@@ -69,13 +70,12 @@
       Log.e(TAG, "StarboardBridge already destroyed.");
       return;
     }
+    Log.i(TAG, "Destroying the Media playback service.");
     getStarboardBridge().onServiceDestroy(this);
     super.onDestroy();
-    Log.i(TAG, "Destroying the Media playback service.");
   }
 
   public void startService() {
-    createChannel();
     try {
       startForeground(NOTIFICATION_ID, buildNotification());
     } catch (IllegalStateException e) {
@@ -84,25 +84,15 @@
   }
 
   public void stopService() {
-    // Do not remove notification here.
-    stopForeground(false);
+    // Let service itself handle notification deletion.
+    stopForeground(true);
     stopSelf();
-    // Delete notification after foreground stopped.
-    deleteChannel();
-    hideNotification();
   }
 
-  private void hideNotification() {
-    Log.i(TAG, "Hiding notification after stopped the service");
-    NotificationManager notificationManager =
-        (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
-    notificationManager.cancel(NOTIFICATION_ID);
-  }
-
-  private void createChannel() {
+  private void createNotificationChannel() {
     if (Build.VERSION.SDK_INT >= 26) {
       try {
-        createChannelInternalV26();
+        createNotificationChannelInternalV26();
       } catch (RemoteException e) {
         Log.e(TAG, "Failed to create Notification Channel.", e);
       }
@@ -110,7 +100,7 @@
   }
 
   @RequiresApi(26)
-  private void createChannelInternalV26() throws RemoteException {
+  private void createNotificationChannelInternalV26() throws RemoteException {
     NotificationManager notificationManager =
         (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
     NotificationChannel channel =
@@ -122,19 +112,6 @@
     notificationManager.createNotificationChannel(channel);
   }
 
-  public void deleteChannel() {
-    if (Build.VERSION.SDK_INT >= 26) {
-      deleteChannelInternalV26();
-    }
-  }
-
-  @RequiresApi(26)
-  private void deleteChannelInternalV26() {
-    NotificationManager notificationManager =
-        (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
-    notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
-  }
-
   Notification buildNotification() {
     NotificationCompat.Builder builder =
         new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
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 f265e98..5a13b01 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
@@ -60,14 +60,18 @@
       int channelCount,
       int preferredBufferSizeInBytes,
       boolean enableAudioDeviceCallback,
-      int tunnelModeAudioSessionId) {
+      boolean enablePcmContentTypeMovie,
+      int tunnelModeAudioSessionId,
+      boolean isWebAudio) {
     AudioTrackBridge audioTrackBridge =
         new AudioTrackBridge(
             sampleType,
             sampleRate,
             channelCount,
             preferredBufferSizeInBytes,
-            tunnelModeAudioSessionId);
+            enablePcmContentTypeMovie,
+            tunnelModeAudioSessionId,
+            isWebAudio);
     if (!audioTrackBridge.isAudioTrackValid()) {
       Log.e(TAG, "AudioTrackBridge has invalid audio track");
       return null;
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
index 0001fde..0a7270e 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
@@ -64,7 +64,9 @@
       int sampleRate,
       int channelCount,
       int preferredBufferSizeInBytes,
-      int tunnelModeAudioSessionId) {
+      boolean enablePcmContentTypeMovie,
+      int tunnelModeAudioSessionId,
+      boolean isWebAudio) {
 
     tunnelModeEnabled = tunnelModeAudioSessionId != -1;
     int channelConfig;
@@ -109,15 +111,13 @@
               .build();
     } else {
       // TODO: Support ENCODING_E_AC3_JOC for api level 28 or later.
-      final boolean is_surround =
+      final boolean isSurround =
           sampleType == AudioFormat.ENCODING_AC3 || sampleType == AudioFormat.ENCODING_E_AC3;
-      // TODO: We start to enforce |CONTENT_TYPE_MOVIE| for surround playback, investigate if we
-      //       can use |CONTENT_TYPE_MOVIE| for all non-surround AudioTrack used by video
-      //       playback.
+      final boolean useContentTypeMovie = isSurround || (!isWebAudio && enablePcmContentTypeMovie);
       attributes =
           new AudioAttributes.Builder()
               .setContentType(
-                  is_surround
+                  useContentTypeMovie
                       ? AudioAttributes.CONTENT_TYPE_MOVIE
                       : AudioAttributes.CONTENT_TYPE_MUSIC)
               .setUsage(AudioAttributes.USAGE_MEDIA)
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 f4f6d3e..4dd5674 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
@@ -1089,6 +1089,15 @@
   // https://github.com/google/ExoPlayer/blob/8595c65678a181296cdf673eacb93d8135479340/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java
   private void maybeSetMaxInputSize(MediaFormat format) {
     if (format.containsKey(android.media.MediaFormat.KEY_MAX_INPUT_SIZE)) {
+      try {
+        Log.i(
+            TAG,
+            "Use default value for KEY_MAX_INPUT_SIZE: "
+                + format.getInteger(android.media.MediaFormat.KEY_MAX_INPUT_SIZE)
+                + '.');
+      } catch (Exception e) {
+        Log.w(TAG, "MediaFormat.getInteger(KEY_MAX_INPUT_SIZE) failed with exception: ", e);
+      }
       // Already set. The source of the format may know better, so do nothing.
       return;
     }
@@ -1131,6 +1140,17 @@
     // Estimate the maximum input size assuming three channel 4:2:0 subsampled input frames.
     int maxInputSize = (maxPixels * 3) / (2 * minCompressionRatio);
     format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize);
+    try {
+      Log.i(
+          TAG,
+          "KEY_MAX_INPUT_SIZE is "
+              + format.getInteger(android.media.MediaFormat.KEY_MAX_INPUT_SIZE)
+              + " after setting it to "
+              + maxInputSize
+              + '.');
+    } catch (Exception e) {
+      Log.w(TAG, "MediaFormat.getInteger(KEY_MAX_INPUT_SIZE) failed with exception: ", e);
+    }
   }
 
   @SuppressWarnings("unused")
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java b/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
index 6afac46..622e92b 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
@@ -35,12 +35,10 @@
 import android.view.Choreographer;
 import android.view.Choreographer.FrameCallback;
 import android.view.Display;
-import androidx.annotation.RequiresApi;
 import dev.cobalt.util.DisplayUtil;
 import dev.cobalt.util.UsedByNative;
 
 /** Makes a best effort to adjust frame release timestamps for a smoother visual result. */
-@RequiresApi(16)
 @SuppressWarnings("unused")
 @UsedByNative
 public final class VideoFrameReleaseTimeHelper {
diff --git a/starboard/android/arm/args.gn b/starboard/android/arm/args.gn
new file mode 100644
index 0000000..c3b55c5
--- /dev/null
+++ b/starboard/android/arm/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "android-arm"
+target_os = "android"
+target_cpu = "arm"
diff --git a/starboard/android/arm/platform_configuration/BUILD.gn b/starboard/android/arm/platform_configuration/BUILD.gn
index b8996b8..56a3cda 100644
--- a/starboard/android/arm/platform_configuration/BUILD.gn
+++ b/starboard/android/arm/platform_configuration/BUILD.gn
@@ -16,5 +16,9 @@
   configs = [ "//starboard/android/shared/platform_configuration" ]
   if (current_toolchain == default_toolchain) {
     cflags = [ "-march=armv7-a" ]
+    ldflags = [
+      # Mimic build/cmake/android.toolchain.cmake in the Android NDK.
+      "-Wl,--exclude-libs,libunwind.a",
+    ]
   }
 }
diff --git a/starboard/android/arm/test_filters.py b/starboard/android/arm/test_filters.py
new file mode 100644
index 0000000..8529436
--- /dev/null
+++ b/starboard/android/arm/test_filters.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Android ARM Platform Test Filters."""
+
+from starboard.android.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return AndroidArmTestFilters()
+
+
+class AndroidArmTestFilters(shared_test_filters.TestFilters):
+  """Starboard Android ARM Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(AndroidArmTestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/android/arm/toolchain/BUILD.gn b/starboard/android/arm/toolchain/BUILD.gn
index 6ffe4f4..ddc2c46 100644
--- a/starboard/android/arm/toolchain/BUILD.gn
+++ b/starboard/android/arm/toolchain/BUILD.gn
@@ -12,13 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
-
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
+import("//starboard/android/shared/toolchain/toolchain.gni")
 
 clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -26,11 +25,8 @@
   }
 }
 
-_android_toolchain_path =
-    "$android_ndk_path/toolchains/llvm/prebuilt/linux-x86_64"
-
 gcc_toolchain("target") {
-  prefix = rebase_path("$_android_toolchain_path/bin", root_build_dir)
+  prefix = rebase_path("$android_toolchain_path/bin", root_build_dir)
   cc = "$prefix/armv7a-linux-androideabi${android_ndk_api_level}-clang"
   cxx = "$prefix/armv7a-linux-androideabi${android_ndk_api_level}-clang++"
   ld = cxx
diff --git a/starboard/android/arm64/args.gn b/starboard/android/arm64/args.gn
new file mode 100644
index 0000000..64c4ff9
--- /dev/null
+++ b/starboard/android/arm64/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "android-arm64"
+target_os = "android"
+target_cpu = "arm64"
diff --git a/starboard/android/arm64/test_filters.py b/starboard/android/arm64/test_filters.py
new file mode 100644
index 0000000..e0454ca
--- /dev/null
+++ b/starboard/android/arm64/test_filters.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Android ARM-64 Platform Test Filters."""
+
+from starboard.android.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return AndroidArm64TestFilters()
+
+
+class AndroidArm64TestFilters(shared_test_filters.TestFilters):
+  """Starboard Android ARM-64 Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(AndroidArm64TestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/android/arm64/toolchain/BUILD.gn b/starboard/android/arm64/toolchain/BUILD.gn
index 3f07d29..554b007 100644
--- a/starboard/android/arm64/toolchain/BUILD.gn
+++ b/starboard/android/arm64/toolchain/BUILD.gn
@@ -12,13 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
-
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
+import("//starboard/android/shared/toolchain/toolchain.gni")
 
 clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -26,11 +25,8 @@
   }
 }
 
-_android_toolchain_path =
-    "$android_ndk_path/toolchains/llvm/prebuilt/linux-x86_64"
-
 gcc_toolchain("target") {
-  prefix = rebase_path("$_android_toolchain_path/bin", root_build_dir)
+  prefix = rebase_path("$android_toolchain_path/bin", root_build_dir)
   cc = "$prefix/aarch64-linux-android${android_ndk_api_level}-clang"
   cxx = "$prefix/aarch64-linux-android${android_ndk_api_level}-clang++"
   ld = cxx
diff --git a/starboard/android/arm64/vulkan/args.gn b/starboard/android/arm64/vulkan/args.gn
new file mode 100644
index 0000000..64c4ff9
--- /dev/null
+++ b/starboard/android/arm64/vulkan/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "android-arm64"
+target_os = "android"
+target_cpu = "arm64"
diff --git a/starboard/android/arm64/vulkan/test_filters.py b/starboard/android/arm64/vulkan/test_filters.py
new file mode 100644
index 0000000..374038d
--- /dev/null
+++ b/starboard/android/arm64/vulkan/test_filters.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Android ARM-64 Vulkan Platform Test Filters."""
+
+from starboard.android.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return AndroidArm64VulkanTestFilters()
+
+
+class AndroidArm64VulkanTestFilters(shared_test_filters.TestFilters):
+  """Starboard Android ARM-64 Vulkan Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(AndroidArm64VulkanTestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/android/arm64/vulkan/toolchain/BUILD.gn b/starboard/android/arm64/vulkan/toolchain/BUILD.gn
index 3f07d29..554b007 100644
--- a/starboard/android/arm64/vulkan/toolchain/BUILD.gn
+++ b/starboard/android/arm64/vulkan/toolchain/BUILD.gn
@@ -12,13 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
-
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
+import("//starboard/android/shared/toolchain/toolchain.gni")
 
 clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -26,11 +25,8 @@
   }
 }
 
-_android_toolchain_path =
-    "$android_ndk_path/toolchains/llvm/prebuilt/linux-x86_64"
-
 gcc_toolchain("target") {
-  prefix = rebase_path("$_android_toolchain_path/bin", root_build_dir)
+  prefix = rebase_path("$android_toolchain_path/bin", root_build_dir)
   cc = "$prefix/aarch64-linux-android${android_ndk_api_level}-clang"
   cxx = "$prefix/aarch64-linux-android${android_ndk_api_level}-clang++"
   ld = cxx
diff --git a/starboard/android/shared/BUILD.gn b/starboard/android/shared/BUILD.gn
index 4ef3355..d2ac6c2 100644
--- a/starboard/android/shared/BUILD.gn
+++ b/starboard/android/shared/BUILD.gn
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//starboard/android/shared/toolchain/toolchain.gni")
 import("//starboard/shared/starboard/media/media_tests.gni")
 import("//starboard/shared/starboard/player/buildfiles.gni")
 import("//starboard/shared/starboard/player/player_tests.gni")
@@ -224,7 +225,6 @@
     "//starboard/shared/starboard/media/media_get_buffer_storage_type.cc",
     "//starboard/shared/starboard/media/media_get_progressive_buffer_budget.cc",
     "//starboard/shared/starboard/media/media_get_video_buffer_budget.cc",
-    "//starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc",
     "//starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc",
     "//starboard/shared/starboard/media/mime_type.cc",
     "//starboard/shared/starboard/media/mime_type.h",
@@ -361,6 +361,7 @@
     "media_get_initial_buffer_capacity.cc",
     "media_get_max_buffer_capacity.cc",
     "media_is_audio_supported.cc",
+    "media_is_buffer_pool_allocate_on_demand.cc",
     "media_is_video_supported.cc",
     "microphone_impl.cc",
     "network_status_impl.cc",
@@ -452,8 +453,8 @@
 
   if (is_internal_build) {
     sources += [
-      "input_events_filter.cc",
-      "input_events_filter.h",
+      "internal/input_events_filter.cc",
+      "internal/input_events_filter.h",
     ]
     defines = [ "STARBOARD_INPUT_EVENTS_FILTER" ]
     deps += [ "//starboard/android/shared/drm_system_extension" ]
diff --git a/starboard/android/shared/audio_renderer_passthrough.cc b/starboard/android/shared/audio_renderer_passthrough.cc
index 1f0849c..f80c44e 100644
--- a/starboard/android/shared/audio_renderer_passthrough.cc
+++ b/starboard/android/shared/audio_renderer_passthrough.cc
@@ -20,6 +20,7 @@
 #include "starboard/android/shared/audio_decoder_passthrough.h"
 #include "starboard/android/shared/jni_env_ext.h"
 #include "starboard/android/shared/jni_utils.h"
+#include "starboard/common/string.h"
 #include "starboard/memory.h"
 
 namespace starboard {
@@ -384,7 +385,8 @@
       optional<SbMediaAudioSampleType>(),  // Not required in passthrough mode
       audio_sample_info_.number_of_channels,
       audio_sample_info_.samples_per_second, kPreferredBufferSizeInBytes,
-      enable_audio_device_callback_, kTunnelModeAudioSessionId));
+      enable_audio_device_callback_, false /* enable_pcm_content_type_movie */,
+      kTunnelModeAudioSessionId, false /* is_web_audio */));
 
   if (!audio_track_bridge->is_valid()) {
     error_cb_(kSbPlayerErrorDecode, "Error creating AudioTrackBridge");
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 1c7d413..80656e2 100644
--- a/starboard/android/shared/audio_sink_min_required_frames_tester.cc
+++ b/starboard/android/shared/audio_sink_min_required_frames_tester.cc
@@ -125,7 +125,7 @@
             GetSampleSize(task.sample_type),
         &MinRequiredFramesTester::UpdateSourceStatusFunc,
         &MinRequiredFramesTester::ConsumeFramesFunc,
-        &MinRequiredFramesTester::ErrorFunc, 0, -1, false, this);
+        &MinRequiredFramesTester::ErrorFunc, 0, -1, false, false, false, this);
     {
       ScopedLock scoped_lock(mutex_);
       wait_timeout = !condition_variable_.WaitTimed(kSbTimeSecond * 5);
diff --git a/starboard/android/shared/audio_track_audio_sink_type.cc b/starboard/android/shared/audio_track_audio_sink_type.cc
index 57e9aed..0e9d29e 100644
--- a/starboard/android/shared/audio_track_audio_sink_type.cc
+++ b/starboard/android/shared/audio_track_audio_sink_type.cc
@@ -83,6 +83,8 @@
     SbTime start_time,
     int tunnel_mode_audio_session_id,
     bool enable_audio_device_callback,
+    bool enable_pcm_content_type_movie,
+    bool is_web_audio,
     void* context)
     : type_(type),
       channels_(channels),
@@ -106,7 +108,9 @@
               sampling_frequency_hz,
               preferred_buffer_size_in_bytes,
               enable_audio_device_callback,
-              tunnel_mode_audio_session_id) {
+              enable_pcm_content_type_movie,
+              tunnel_mode_audio_session_id,
+              is_web_audio) {
   SB_DCHECK(update_source_status_func_);
   SB_DCHECK(consume_frames_func_);
   SB_DCHECK(frame_buffer_);
@@ -167,11 +171,7 @@
   SB_LOG(INFO) << "AudioTrackAudioSink thread started.";
 
   int accumulated_written_frames = 0;
-  // TODO: |last_playback_head_changed_at| is also reset when a warning is
-  //       logged after the playback head position hasn't been updated for a
-  //       while.  We should refine the name to make it better reflect its
-  //       usage.
-  SbTime last_playback_head_changed_at = -1;
+  SbTime last_playback_head_event_at = -1;
   SbTime playback_head_not_changed_duration = 0;
   SbTime last_written_succeeded_at = -1;
 
@@ -197,21 +197,21 @@
           playback_head_position - last_playback_head_position_;
       SbTime now = SbTimeGetMonotonicNow();
 
-      if (last_playback_head_changed_at == -1) {
-        last_playback_head_changed_at = now;
+      if (last_playback_head_event_at == -1) {
+        last_playback_head_event_at = now;
       }
       if (last_playback_head_position_ == playback_head_position) {
-        SbTime elapsed = now - last_playback_head_changed_at;
+        SbTime elapsed = now - last_playback_head_event_at;
         if (elapsed > 5 * kSbTimeSecond) {
           playback_head_not_changed_duration += elapsed;
-          last_playback_head_changed_at = now;
+          last_playback_head_event_at = now;
           SB_LOG(INFO) << "last playback head position is "
                        << last_playback_head_position_
                        << " and it hasn't been updated for " << elapsed
                        << " microseconds.";
         }
       } else {
-        last_playback_head_changed_at = now;
+        last_playback_head_event_at = now;
         playback_head_not_changed_duration = 0;
       }
 
@@ -243,7 +243,7 @@
       bridge_.Pause();
     } else if (!was_playing && is_playing) {
       was_playing = true;
-      last_playback_head_changed_at = -1;
+      last_playback_head_event_at = -1;
       playback_head_not_changed_duration = 0;
       last_written_succeeded_at = -1;
       bridge_.Play();
@@ -424,11 +424,15 @@
   const int kTunnelModeAudioSessionId = -1;
   // Disable AudioDeviceCallback for WebAudio.
   const bool kEnableAudioDeviceCallback = false;
+  const bool kIsWebAudio = true;
+  // Disable AudioAttributes::CONTENT_TYPE_MOVIE for WebAudio.
+  const bool kEnablePcmContentTypeMovie = false;
   return Create(channels, sampling_frequency_hz, audio_sample_type,
                 audio_frame_storage_type, frame_buffers, frames_per_channel,
                 update_source_status_func, consume_frames_func, error_func,
                 kStartTime, kTunnelModeAudioSessionId,
-                kEnableAudioDeviceCallback, context);
+                kEnableAudioDeviceCallback, kEnablePcmContentTypeMovie,
+                kIsWebAudio, context);
 }
 
 SbAudioSink AudioTrackAudioSinkType::Create(
@@ -444,6 +448,8 @@
     SbTime start_media_time,
     int tunnel_mode_audio_session_id,
     bool enable_audio_device_callback,
+    bool enable_pcm_content_type_movie,
+    bool is_web_audio,
     void* context) {
   int min_required_frames = SbAudioSinkGetMinBufferSizeInFrames(
       channels, audio_sample_type, sampling_frequency_hz);
@@ -455,7 +461,8 @@
       frames_per_channel, preferred_buffer_size_in_bytes,
       update_source_status_func, consume_frames_func, error_func,
       start_media_time, tunnel_mode_audio_session_id,
-      enable_audio_device_callback, context);
+      enable_audio_device_callback, enable_pcm_content_type_movie, is_web_audio,
+      context);
   if (!audio_sink->IsAudioTrackValid()) {
     SB_DLOG(ERROR)
         << "AudioTrackAudioSinkType::Create failed to create audio track";
diff --git a/starboard/android/shared/audio_track_audio_sink_type.h b/starboard/android/shared/audio_track_audio_sink_type.h
index adf050d..035b093 100644
--- a/starboard/android/shared/audio_track_audio_sink_type.h
+++ b/starboard/android/shared/audio_track_audio_sink_type.h
@@ -69,6 +69,8 @@
       SbTime start_time,
       int tunnel_mode_audio_session_id,
       bool enable_audio_device_callback,
+      bool enable_pcm_content_type_movie,
+      bool is_web_audio,
       void* context);
 
   bool IsValid(SbAudioSink audio_sink) override {
@@ -112,6 +114,8 @@
       SbTime start_media_time,
       int tunnel_mode_audio_session_id,
       bool enable_audio_device_callback,
+      bool enable_pcm_content_type_movie,
+      bool is_web_audio,
       void* context);
   ~AudioTrackAudioSink() override;
 
diff --git a/starboard/android/shared/audio_track_bridge.cc b/starboard/android/shared/audio_track_bridge.cc
index 79fe83f..05b9c9b 100644
--- a/starboard/android/shared/audio_track_bridge.cc
+++ b/starboard/android/shared/audio_track_bridge.cc
@@ -40,7 +40,9 @@
                                    int sampling_frequency_hz,
                                    int preferred_buffer_size_in_bytes,
                                    bool enable_audio_device_callback,
-                                   int tunnel_mode_audio_session_id) {
+                                   bool enable_pcm_content_type_movie,
+                                   int tunnel_mode_audio_session_id,
+                                   bool is_web_audio) {
   if (coding_type == kSbMediaAudioCodingTypePcm) {
     SB_DCHECK(SbAudioSinkIsAudioSampleTypeSupported(sample_type.value()));
 
@@ -63,10 +65,11 @@
           "getAudioOutputManager", "()Ldev/cobalt/media/AudioOutputManager;"));
   jobject j_audio_track_bridge = env->CallObjectMethodOrAbort(
       j_audio_output_manager.Get(), "createAudioTrackBridge",
-      "(IIIIZI)Ldev/cobalt/media/AudioTrackBridge;",
+      "(IIIIZZIZ)Ldev/cobalt/media/AudioTrackBridge;",
       GetAudioFormatSampleType(coding_type, sample_type), sampling_frequency_hz,
       channels, preferred_buffer_size_in_bytes, enable_audio_device_callback,
-      tunnel_mode_audio_session_id);
+      enable_pcm_content_type_movie, tunnel_mode_audio_session_id,
+      is_web_audio);
   if (!j_audio_track_bridge) {
     // One of the cases that this may hit is when output happened to be switched
     // to a device that doesn't support tunnel mode.
diff --git a/starboard/android/shared/audio_track_bridge.h b/starboard/android/shared/audio_track_bridge.h
index 607837d..3804b6a 100644
--- a/starboard/android/shared/audio_track_bridge.h
+++ b/starboard/android/shared/audio_track_bridge.h
@@ -42,7 +42,9 @@
                    int sampling_frequency_hz,
                    int preferred_buffer_size_in_bytes,
                    bool enable_audio_device_callback,
-                   int tunnel_mode_audio_session_id);
+                   bool enable_pcm_content_type_movie,
+                   int tunnel_mode_audio_session_id,
+                   bool is_web_audio);
   ~AudioTrackBridge();
 
   static int GetMinBufferSizeInFrames(SbMediaAudioSampleType sample_type,
diff --git a/starboard/android/shared/input_events_generator.h b/starboard/android/shared/input_events_generator.h
index 9ea7e12..c192079 100644
--- a/starboard/android/shared/input_events_generator.h
+++ b/starboard/android/shared/input_events_generator.h
@@ -21,7 +21,7 @@
 #include <vector>
 
 #ifdef STARBOARD_INPUT_EVENTS_FILTER
-#include "starboard/android/shared/input_events_filter.h"
+#include "starboard/android/shared/internal/input_events_filter.h"
 #endif
 
 #include "starboard/input.h"
@@ -78,7 +78,7 @@
   SbWindow window_;
 
 #ifdef STARBOARD_INPUT_EVENTS_FILTER
-  InputEventsFilter input_events_filter_;
+  internal::InputEventsFilter input_events_filter_;
 #endif
 
   // Map the device id with joystick flat position.
diff --git a/starboard/android/shared/install_target.gni b/starboard/android/shared/install_target.gni
index dbfe29c..aef5ff2 100644
--- a/starboard/android/shared/install_target.gni
+++ b/starboard/android/shared/install_target.gni
@@ -12,23 +12,25 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//starboard/android/shared/toolchain/toolchain.gni")
+import("//starboard/build/config/install.gni")
+
 template("install_target") {
   not_needed(invoker, [ "type" ])
-  content_dir = "usr/share/cobalt"
+
+  installable_target_name = invoker.installable_target_name
 
   action("build_${target_name}_apk") {
-    forward_variables_from(invoker,
-                           [
-                             "installable_target_name",
-                             "testonly",
-                           ])
-    deps = [
+    forward_variables_from(invoker, [ "testonly" ])
+
+    deps = invoker.deps
+    deps += [
       ":$installable_target_name",
       "//starboard/android/apk:apk_sources",
     ]
 
     target_output = "$root_out_dir/lib${installable_target_name}.so"
-    gradle_content_dir = "$sb_install_output_dir/$content_dir"
+    gradle_content_dir = "$sb_install_output_dir/$sb_install_content_subdir"
     gradle_files_dir = "$root_out_dir/gradle/$installable_target_name"
     if (is_gold) {
       gradle_build_type = "release"
@@ -80,4 +82,12 @@
       "assembleCobalt_$gradle_build_type",
     ]
   }
+
+  group(target_name) {
+    forward_variables_from(invoker, [ "testonly" ])
+    deps = [
+      ":$installable_target_name",
+      ":build_${target_name}_apk",
+    ]
+  }
 }
diff --git a/starboard/android/shared/media_common.h b/starboard/android/shared/media_common.h
index aa17a47..f9e5a56 100644
--- a/starboard/android/shared/media_common.h
+++ b/starboard/android/shared/media_common.h
@@ -15,14 +15,12 @@
 #ifndef STARBOARD_ANDROID_SHARED_MEDIA_COMMON_H_
 #define STARBOARD_ANDROID_SHARED_MEDIA_COMMON_H_
 
-#include <deque>
-#include <queue>
+#include <cstring>
 
 #include "starboard/android/shared/jni_env_ext.h"
 #include "starboard/common/log.h"
 #include "starboard/common/mutex.h"
 #include "starboard/common/optional.h"
-#include "starboard/common/string.h"
 #include "starboard/configuration.h"
 #include "starboard/media.h"
 #include "starboard/shared/starboard/player/filter/audio_frame_tracker.h"
@@ -36,10 +34,6 @@
          strcmp(key_system, "com.widevine.alpha") == 0;
 }
 
-inline bool IsWidevineL3(const char* key_system) {
-  return strcmp(key_system, "com.youtube.widevine.l3") == 0;
-}
-
 // Map a supported |SbMediaAudioCodec| into its corresponding mime type
 // string.  Returns |nullptr| if |audio_codec| is not supported.
 // On return, |is_passthrough| will be set to true if the codec should be played
diff --git a/starboard/android/shared/media_is_audio_supported.cc b/starboard/android/shared/media_is_audio_supported.cc
index 419de2a..d9efe42 100644
--- a/starboard/android/shared/media_is_audio_supported.cc
+++ b/starboard/android/shared/media_is_audio_supported.cc
@@ -52,6 +52,10 @@
     mime_type.RegisterBoolParameter("tunnelmode");
     // Enables audio passthrough if the codec supports it.
     mime_type.RegisterBoolParameter("audiopassthrough");
+    // Allows for disabling the CONTENT_TYPE_MOVIE AudioAttribute for
+    // non-tunneled playbacks with PCM audio. Enabled by default.
+    // (https://developer.android.com/reference/android/media/AudioAttributes#CONTENT_TYPE_MOVIE)
+    mime_type.RegisterBoolParameter("enablepcmcontenttypemovie");
 
     if (!mime_type.is_valid()) {
       return false;
diff --git a/starboard/android/shared/media_is_buffer_pool_allocate_on_demand.cc b/starboard/android/shared/media_is_buffer_pool_allocate_on_demand.cc
new file mode 100644
index 0000000..3fc0676
--- /dev/null
+++ b/starboard/android/shared/media_is_buffer_pool_allocate_on_demand.cc
@@ -0,0 +1,20 @@
+// Copyright 2021 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/media.h"
+
+bool SbMediaIsBufferPoolAllocateOnDemand() {
+  // Minimize fragmentation by always having the media buffer pool allocated.
+  return false;
+}
diff --git a/starboard/android/shared/platform_configuration/BUILD.gn b/starboard/android/shared/platform_configuration/BUILD.gn
index 6e5fe5f..a2dbcff 100644
--- a/starboard/android/shared/platform_configuration/BUILD.gn
+++ b/starboard/android/shared/platform_configuration/BUILD.gn
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//starboard/android/shared/toolchain/toolchain.gni")
+
 config("platform_configuration") {
   configs = [ "//starboard/build/config/sabi" ]
   cflags = []
@@ -26,9 +28,6 @@
       # those, rather use lld everywhere. See release notes for NDK 19:
       # https://developer.android.com/ndk/downloads/revision_history
       "-fuse-ld=lld",
-
-      # Mimic build/cmake/android.toolchain.cmake in the Android NDK.
-      "-Wl,--exclude-libs,libunwind.a",
     ]
     if (is_debug) {
       cflags += [ "-O0" ]
diff --git a/starboard/android/shared/platform_configuration/configuration.gni b/starboard/android/shared/platform_configuration/configuration.gni
index 32bf7c5..4eebd62 100644
--- a/starboard/android/shared/platform_configuration/configuration.gni
+++ b/starboard/android/shared/platform_configuration/configuration.gni
@@ -15,23 +15,9 @@
 import("//starboard/build/config/base_configuration.gni")
 
 declare_args() {
-  android_sdk_path = getenv("ANDROID_HOME")
-  android_ndk_path = ""
-  sb_android_ndk_version = "21.1.6352462"
-  android_ndk_api_level = 21
-
   enable_vulkan = false
 }
 
-if (android_sdk_path == "") {
-  _home_dir = getenv("HOME")
-  android_sdk_path = "$_home_dir/starboard-toolchains/AndroidSdk"
-}
-
-if (android_ndk_path == "") {
-  android_ndk_path = "$android_sdk_path/ndk/$sb_android_ndk_version"
-}
-
 final_executable_type = "shared_library"
 gtest_target_type = "shared_library"
 sb_enable_benchmark = true
diff --git a/starboard/android/shared/player_components_factory.h b/starboard/android/shared/player_components_factory.h
index 0707a84..e129b0f 100644
--- a/starboard/android/shared/player_components_factory.h
+++ b/starboard/android/shared/player_components_factory.h
@@ -69,6 +69,7 @@
                                      filter::AudioRendererSinkImpl {
  public:
   explicit AudioRendererSinkAndroid(bool enable_audio_device_callback,
+                                    bool enable_pcm_content_type_movie,
                                     int tunnel_mode_audio_session_id = -1)
       : AudioRendererSinkImpl(
             [=](SbTime start_media_time,
@@ -92,6 +93,7 @@
                   frame_buffers_size_in_frames, update_source_status_func,
                   consume_frames_func, error_func, start_media_time,
                   tunnel_mode_audio_session_id, enable_audio_device_callback,
+                  enable_pcm_content_type_movie, false, /* is_web_audio */
                   context);
             }) {}
 
@@ -280,6 +282,7 @@
         strlen(creation_parameters.audio_mime()) > 0) {
       audio_mime_type.RegisterBoolParameter("tunnelmode");
       audio_mime_type.RegisterBoolParameter("enableaudiodevicecallback");
+      audio_mime_type.RegisterBoolParameter("enablepcmcontenttypemovie");
 
       if (!audio_mime_type.is_valid()) {
         *error_message =
@@ -390,6 +393,11 @@
           audio_mime_type.GetParamBoolValue("enableaudiodevicecallback", true);
       SB_LOG(INFO) << "AudioDeviceCallback is "
                    << (enable_audio_device_callback ? "enabled." : "disabled.");
+      bool enable_pcm_content_type_movie =
+          audio_mime_type.GetParamBoolValue("enablepcmcontenttypemovie", true);
+      SB_LOG(INFO) << "AudioAttributes::CONTENT_TYPE_MOVIE is "
+                   << (enable_pcm_content_type_movie ? "enabled" : "disabled")
+                   << " for non-tunneled PCM audio playback.";
 
       if (tunnel_mode_audio_session_id != -1) {
         *audio_renderer_sink = TryToCreateTunnelModeAudioRendererSink(
@@ -400,8 +408,8 @@
         }
       }
       if (!*audio_renderer_sink) {
-        audio_renderer_sink->reset(
-            new AudioRendererSinkAndroid(enable_audio_device_callback));
+        audio_renderer_sink->reset(new AudioRendererSinkAndroid(
+            enable_audio_device_callback, enable_pcm_content_type_movie));
       }
     }
 
@@ -589,7 +597,7 @@
       const CreationParameters& creation_parameters,
       bool enable_audio_device_callback) {
     scoped_ptr<AudioRendererSink> audio_sink(new AudioRendererSinkAndroid(
-        enable_audio_device_callback, tunnel_mode_audio_session_id));
+        enable_audio_device_callback, true, tunnel_mode_audio_session_id));
     // We need to double check if the audio sink can actually be created.
     int max_cached_frames, min_frames_per_append;
     GetAudioRendererParams(creation_parameters, &max_cached_frames,
diff --git a/starboard/android/shared/sdk_utils.py b/starboard/android/shared/sdk_utils.py
index be35f7c..237e5c7 100644
--- a/starboard/android/shared/sdk_utils.py
+++ b/starboard/android/shared/sdk_utils.py
@@ -29,6 +29,8 @@
 
 from starboard.tools import build
 
+# pylint: disable=consider-using-with
+
 # Which version of the Android NDK and CMake to install and build with.
 # Note that build.gradle parses these out of this file too.
 _NDK_VERSION = '21.1.6352462'
@@ -54,7 +56,8 @@
 
 # Location from which to download the SDK command-line tools
 # see https://developer.android.com/studio/index.html#command-tools
-_SDK_URL = 'https://dl.google.com/android/repository/commandlinetools-linux-6200805_latest.zip'
+_SDK_URL = ('https://dl.google.com/android/repository/'
+            'commandlinetools-linux-6200805_latest.zip')
 
 # Location from which to download the Android NDK.
 # see https://developer.android.com/ndk/downloads (perhaps in "NDK archives")
@@ -107,7 +110,7 @@
   zip_file = zipfile.ZipFile(zip_path)
   for info in zip_file.infolist():
     zip_file.extract(info.filename, dest_path)
-    os.chmod(os.path.join(dest_path, info.filename), info.external_attr >> 16L)
+    os.chmod(os.path.join(dest_path, info.filename), info.external_attr >> 16)
 
 
 def InstallSdkIfNeeded():
@@ -138,6 +141,7 @@
         logging.error('Error: ANDROID_HOME is is missing NDK %s.', _NDK_VERSION)
         sys.exit(1)
 
+      # pylint: disable=undefined-variable
       reply = raw_input(
           'Do you want to continue using your custom Android tools? [y/N]')
       if reply.upper() != 'Y':
@@ -225,6 +229,9 @@
 
   # If we can't access the "sdkmanager" tool, we need to download the SDK
   if not os.access(_SDKMANAGER_TOOL, os.X_OK):
+    if os.getenv('IS_CI', '') == '1':
+      raise Exception('Dynamic toolchain downloads are disabled in CI')
+
     logging.warning('Downloading Android SDK to %s',
                     _STARBOARD_TOOLCHAINS_SDK_DIR)
     if os.path.exists(_STARBOARD_TOOLCHAINS_SDK_DIR):
diff --git a/starboard/android/shared/starboard_platform.gypi b/starboard/android/shared/starboard_platform.gypi
index e8579cb..94eefcd 100644
--- a/starboard/android/shared/starboard_platform.gypi
+++ b/starboard/android/shared/starboard_platform.gypi
@@ -13,7 +13,7 @@
 # limitations under the License.
 {
   'variables': {
-    'has_input_events_filter' : '<!pymod_do_main(starboard.build.gyp_functions file_exists <(DEPTH)/starboard/android/shared/input_events_filter.cc)',
+    'has_input_events_filter' : '<!pymod_do_main(starboard.build.gyp_functions file_exists <(DEPTH)/starboard/android/shared/internal/input_events_filter.cc)',
     'has_drm_system_extension%': '<!pymod_do_main(starboard.build.gyp_functions file_exists <(DEPTH)/starboard/android/shared/drm_system_extension/drm_system_extension.gyp)',
   },
   'includes': [
@@ -141,6 +141,7 @@
         'media_get_initial_buffer_capacity.cc',
         'media_get_max_buffer_capacity.cc',
         'media_is_audio_supported.cc',
+        'media_is_buffer_pool_allocate_on_demand.cc',
         'media_is_video_supported.cc',
         'network_status_impl.cc',
         'microphone_impl.cc',
@@ -401,7 +402,6 @@
         '<(DEPTH)/starboard/shared/starboard/media/media_get_buffer_storage_type.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_get_progressive_buffer_budget.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_get_video_buffer_budget.cc',
-        '<(DEPTH)/starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc',
         '<(DEPTH)/starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc',
         '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc',
         '<(DEPTH)/starboard/shared/starboard/media/mime_type.h',
@@ -476,8 +476,8 @@
       'conditions': [
         ['has_input_events_filter==1', {
           'sources': [
-            'input_events_filter.cc',
-            'input_events_filter.h',
+            'internal/input_events_filter.cc',
+            'internal/input_events_filter.h',
           ],
           'defines': [
             'STARBOARD_INPUT_EVENTS_FILTER',
diff --git a/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc b/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
index 1231877..fb522af 100644
--- a/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
+++ b/starboard/android/shared/system_request_freeze_no_freezedone_callback.cc
@@ -18,7 +18,7 @@
 #include "starboard/shared/starboard/application.h"
 
 #if SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
-#include "starboard/loader_app/pending_restart.h"
+#include "starboard/loader_app/pending_restart.h"  // nogncheck
 #endif  // SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
 
 #if SB_API_VERSION >= 13
diff --git a/starboard/android/shared/test_filters.py b/starboard/android/shared/test_filters.py
new file mode 100644
index 0000000..c3d38ef
--- /dev/null
+++ b/starboard/android/shared/test_filters.py
@@ -0,0 +1,84 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Android Platform Test Filters."""
+
+from starboard.tools.testing import test_filter
+
+
+class TestFilters(object):
+  """Starboard Android platform test filters."""
+
+  def GetTestFilters(self):
+    filters = []
+    for target, tests in self._FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  # A map of failing or crashing tests per target.
+  _FILTERED_TESTS = {
+      'player_filter_tests': [
+          # GetMaxNumberOfCachedFrames() on Android is device dependent,
+          # and Android doesn't provide an API to get it. So, this function
+          # doesn't make sense on Android. But HoldFramesUntilFull tests depend
+          # on this number strictly.
+          'VideoDecoderTests/VideoDecoderTest.HoldFramesUntilFull/*',
+
+          # Currently, invalid input tests are not supported.
+          'VideoDecoderTests/VideoDecoderTest.SingleInvalidInput/*',
+          'VideoDecoderTests/VideoDecoderTest'
+          '.MultipleValidInputsAfterInvalidKeyFrame/*',
+          'VideoDecoderTests/VideoDecoderTest.MultipleInvalidInput/*',
+
+          # Android currently does not support multi-video playback, which
+          # the following tests depend upon.
+          'VideoDecoderTests/VideoDecoderTest.ThreeMoreDecoders/*',
+
+          # The video pipeline will hang if it doesn't receive any input.
+          'PlayerComponentsTests/PlayerComponentsTest.EOSWithoutInput/*',
+
+          # The e/eac3 audio time reporting during pause will be revisitied.
+          'PlayerComponentsTests/PlayerComponentsTest.Pause/15',
+      ],
+      'nplb': [
+          # This test is failing because localhost is not defined for IPv6 in
+          # /etc/hosts.
+          'SbSocketAddressTypes/SbSocketResolveTest.Localhost/1',
+
+          # These tests are taking longer due to interop on android. Work is
+          # underway to investigate whether this is acceptable.
+          'SbMediaCanPlayMimeAndKeySystem.ValidatePerformance',
+          'SbMediaConfigurationTest.ValidatePerformance',
+
+          # SbDirectory has problems with empty Asset dirs.
+          'SbDirectoryCanOpenTest.SunnyDayStaticContent',
+          'SbDirectoryGetNextTest.SunnyDayStaticContent',
+          'SbDirectoryOpenTest.SunnyDayStaticContent',
+          'SbFileGetPathInfoTest.WorksOnStaticContentDirectories',
+
+          # These tests are disabled due to not receiving the kEndOfStream
+          # player state update within the specified timeout.
+          'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/*',
+
+          # Android does not use SbDrmSessionClosedFunc, which these tests
+          # depend on.
+          'SbDrmSessionTest.SunnyDay',
+          'SbDrmSessionTest.CloseDrmSessionBeforeUpdateSession',
+
+          # This test is failing because Android calls the
+          # SbDrmSessionUpdateRequestFunc with SbDrmStatus::kSbDrmStatusSuccess
+          # when invalid initialization data is passed to
+          # SbDrmGenerateSessionUpdateRequest().
+          'SbDrmSessionTest.InvalidSessionUpdateRequestParams',
+      ],
+  }
diff --git a/starboard/android/shared/toolchain/toolchain.gni b/starboard/android/shared/toolchain/toolchain.gni
new file mode 100644
index 0000000..328edd6
--- /dev/null
+++ b/starboard/android/shared/toolchain/toolchain.gni
@@ -0,0 +1,33 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/config/clang/clang.gni")
+
+declare_args() {
+  android_sdk_path = getenv("ANDROID_HOME")
+  android_ndk_path = ""
+  sb_android_ndk_version = "21.1.6352462"
+  android_ndk_api_level = 21
+}
+
+if (android_sdk_path == "") {
+  android_sdk_path = "$starboard_toolchains_path/AndroidSdk"
+}
+
+if (android_ndk_path == "") {
+  android_ndk_path = "$android_sdk_path/ndk/$sb_android_ndk_version"
+}
+
+android_toolchain_path =
+    "$android_ndk_path/toolchains/llvm/prebuilt/linux-x86_64"
diff --git a/starboard/android/x86/args.gn b/starboard/android/x86/args.gn
new file mode 100644
index 0000000..977def0
--- /dev/null
+++ b/starboard/android/x86/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "android-x86"
+target_os = "android"
+target_cpu = "x86"
diff --git a/starboard/android/x86/test_filters.py b/starboard/android/x86/test_filters.py
new file mode 100644
index 0000000..a510516
--- /dev/null
+++ b/starboard/android/x86/test_filters.py
@@ -0,0 +1,54 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Android X86 Platform Test Filters."""
+
+from starboard.android.shared import test_filters as shared_test_filters
+from starboard.tools.testing import test_filter
+
+
+def CreateTestFilters():
+  return AndroidX86TestFilters()
+
+
+class AndroidX86TestFilters(shared_test_filters.TestFilters):
+  """Starboard Android X86 Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(AndroidX86TestFilters, self).GetTestFilters()
+    for target, tests in self._FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  # A map of failing or crashing tests per target
+  _FILTERED_TESTS = {
+      'nplb': [
+          'SbAccessibilityTest.CallSetCaptionsEnabled',
+          'SbAccessibilityTest.GetCaptionSettingsReturnIsValid',
+          'SbAudioSinkTest.*',
+          'SbMediaCanPlayMimeAndKeySystem.*',
+          'SbMicrophoneCloseTest.*',
+          'SbMicrophoneOpenTest.*',
+          'SbMicrophoneReadTest.*',
+          'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.*',
+          'SbMediaSetAudioWriteDurationTests/SbMediaSetAudioWriteDurationTest'
+          '.WriteContinuedLimitedInput/*',
+          'SbMediaSetAudioWriteDurationTests/SbMediaSetAudioWriteDurationTest'
+          '.WriteLimitedInput/*',
+      ],
+      'player_filter_tests': [
+          'AudioDecoderTests/*',
+          'VideoDecoderTests/*',
+          'PlayerComponentsTests/*',
+      ],
+  }
diff --git a/starboard/android/x86/toolchain/BUILD.gn b/starboard/android/x86/toolchain/BUILD.gn
index 3cd5126..3f6180e 100644
--- a/starboard/android/x86/toolchain/BUILD.gn
+++ b/starboard/android/x86/toolchain/BUILD.gn
@@ -12,13 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
-
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
+import("//starboard/android/shared/toolchain/toolchain.gni")
 
 clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -26,11 +25,8 @@
   }
 }
 
-_android_toolchain_path =
-    "$android_ndk_path/toolchains/llvm/prebuilt/linux-x86_64"
-
 gcc_toolchain("target") {
-  prefix = rebase_path("$_android_toolchain_path/bin", root_build_dir)
+  prefix = rebase_path("$android_toolchain_path/bin", root_build_dir)
   cc = "$prefix/i686-linux-android${android_ndk_api_level}-clang"
   cxx = "$prefix/i686-linux-android${android_ndk_api_level}-clang++"
   ld = cxx
diff --git a/starboard/build/config/BUILD.gn b/starboard/build/config/BUILD.gn
index 5491f81..3f585bf 100644
--- a/starboard/build/config/BUILD.gn
+++ b/starboard/build/config/BUILD.gn
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/compiler/compiler.gni")
+
 config("base") {
   defines = []
 
@@ -92,6 +94,8 @@
   }
 }
 
+# TODO(b/212641065): Scope global defines migrated from
+# cobalt_configuration.gypi to only the targets they're necessary in.
 config("starboard") {
   if (current_toolchain == default_toolchain) {
     defines = [
@@ -109,6 +113,7 @@
       defines += [
         "ENABLE_DEBUGGER",
         "ENABLE_DEBUG_COMMAND_LINE_SWITCHES",
+        "ENABLE_TEST_RUNNER",
       ]
     }
 
@@ -184,3 +189,21 @@
     }
   }
 }
+
+config("warnings_as_errors") {
+  if (is_win && treat_warnings_as_errors) {
+    cflags = [ "/WX" ]
+  }
+}
+
+# This config is defined here and added as a default config so that the flags
+# specified in it can be overridden by targets. It's not possible for targets to
+# override flags specified in a platform's "platform_configuration" config,
+# which is where these particular flags would otherwise naturally fit.
+config("default_compiler_flags") {
+  if (current_toolchain == default_toolchain && sb_is_evergreen &&
+      target_cpu == "arm") {
+    cflags = [ "-mfpu=vfpv3" ]
+    asmflags = cflags
+  }
+}
diff --git a/starboard/build/config/BUILDCONFIG.gn b/starboard/build/config/BUILDCONFIG.gn
index 5f69d58..9e10f5d 100644
--- a/starboard/build/config/BUILDCONFIG.gn
+++ b/starboard/build/config/BUILDCONFIG.gn
@@ -12,7 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//starboard/build/platforms.gni")
+_is_python2 = exec_script("//build/util/is_python2.py", [], "json")
+assert(!_is_python2, "`python` must resolve to Python 3 when building with GN.")
 
 declare_args() {
   is_clang = true
@@ -23,11 +24,11 @@
 
   is_starboard = true
 
-  cobalt_fastbuild = getenv("IS_CI") == 1
+  cobalt_fastbuild = getenv("IS_CI") == "1"
 
-  is_internal_build = false
+  is_internal_build = getenv("COBALT_USE_INTERNAL_BUILD") == "1"
 
-  is_docker_build = getenv("IS_DOCKER") == 1
+  is_docker_build = getenv("IS_DOCKER") == "1"
 }
 
 is_debug = build_type == "debug"
@@ -36,20 +37,11 @@
 is_gold = build_type == "gold"
 assert(is_debug || is_devel || is_qa || is_gold)
 
-declare_args() {
-  use_tsan = getenv("USE_TSAN") == 1
-}
-
-declare_args() {
-  use_asan = (is_debug || is_devel || getenv("USE_ASAN") == 1) && !use_tsan
-}
-
-assert(!(use_tsan && use_asan), "ASAN and TSAN are mutually exclusive.")
-
 # Set some variables we never want to change.
 sb_allows_memory_tracking = !is_gold
 host_byteorder = "little"
 is_official_build = false  # Chromium's build files expect this to be set.
+is_component_build = false
 
 # The os/cpu definitions are here to set the current platform type in
 # os_definitions.gni below.
@@ -75,13 +67,11 @@
 
 # Get the path to the starboard implementation and include its GN
 # configuration.
-foreach(platform, platforms) {
-  if (platform.name == target_platform) {
-    starboard_path = platform.path
-  }
-}
+starboard_path = exec_script("//starboard/build/platforms.py",
+                             [ target_platform ],
+                             "trim string")
 assert(defined(starboard_path),
-       "Please port your platform to starboard/build/platforms.gni")
+       "Please add your platform to starboard/build/platforms.py")
 host_toolchain = "//$starboard_path/toolchain:host"
 _default_toolchain = "//$starboard_path/toolchain:target"
 set_default_toolchain(_default_toolchain)
@@ -89,6 +79,18 @@
 import("//$starboard_path/platform_configuration/configuration.gni")
 import("//starboard/build/config/build_assertions.gni")
 
+declare_args() {
+  use_tsan = getenv("USE_TSAN") == 1
+}
+
+_use_asan_default = (is_debug || is_devel) && (is_linux || is_mac) &&
+                    is_clang && !sb_is_evergreen
+declare_args() {
+  use_asan = (_use_asan_default || getenv("USE_ASAN") == 1) && !use_tsan
+}
+
+assert(!(use_tsan && use_asan), "ASAN and TSAN are mutually exclusive.")
+
 # =============================================================================
 # TARGET DEFAULTS
 # =============================================================================
@@ -109,18 +111,23 @@
 default_compiler_configs = [
   "//build/config/compiler:default_include_dirs",
   "//build/config/compiler:no_exceptions",
-  "//build/config/compiler:thin_archive",
   "//starboard/build/config:base",
   "//starboard/build/config:host",
   "//starboard/build/config:size",
   "//starboard/build/config:target",
+  "//starboard/build/config:warnings_as_errors",
+  "//starboard/build/config:default_compiler_flags",
 ]
 
+if (use_thin_archive) {
+  default_compiler_configs += [ "//build/config/compiler:thin_archive" ]
+}
+
 if (is_starboard) {
   default_compiler_configs += [ "//starboard/build/config:starboard" ]
 }
 
-if (is_qa || is_gold) {
+if (is_qa || is_gold || sb_use_no_rtti) {
   default_compiler_configs += [ "//build/config/compiler:no_rtti" ]
 }
 
@@ -190,25 +197,97 @@
   }
 }
 
+# Import configuration variables needed for the install targets and install
+# content targets.
+import("//starboard/build/config/install.gni")
+
 # Set up the method of generating the install targets as defined by the
 # platform.
 import("$install_target_path")
+template("install_content") {
+  target(invoker.target_type, target_name) {
+    forward_variables_from(invoker, "*", [ "install_content" ])
+    if (defined(visibility)) {
+      visibility += [ ":${target_name}_install_content" ]
+    }
+  }
+
+  if (defined(invoker.install_content) && invoker.install_content) {
+    action("${target_name}_install_content") {
+      forward_variables_from(invoker, [ "testonly" ])
+
+      deps = [ ":${invoker.target_name}" ]
+
+      sources = get_target_outputs(":${invoker.target_name}")
+
+      install_content_dir = "$sb_install_output_dir/$sb_install_content_subdir"
+      outputs = []
+      foreach(source, sources) {
+        output_path = install_content_dir + "/" +
+                      rebase_path(source, "$sb_static_contents_output_data_dir")
+        outputs += [ output_path ]
+      }
+
+      script = "//starboard/build/copy_install_content.py"
+      args = [
+        "--output_dir",
+        rebase_path(install_content_dir, root_build_dir),
+        "--base_dir",
+        rebase_path(sb_static_contents_output_data_dir, root_build_dir),
+      ]
+      args += rebase_path(sources, root_build_dir)
+    }
+  }
+}
+
+template("action") {
+  install_content(target_name) {
+    forward_variables_from(invoker, "*")
+    target_type = "action"
+  }
+}
+
+template("action_foreach") {
+  install_content(target_name) {
+    forward_variables_from(invoker, "*")
+    target_type = "action_foreach"
+  }
+}
+
+template("copy") {
+  install_content(target_name) {
+    forward_variables_from(invoker, "*")
+    target_type = "copy"
+  }
+}
+
 template("executable") {
   target_with_platform_configs(target_name) {
     target_type = "executable"
-    forward_variables_from(invoker, "*", [ "install_target" ])
+    forward_variables_from(invoker,
+                           "*",
+                           [
+                             "install_target",
+                             "content_deps",
+                           ])
   }
 
   if (!defined(invoker.install_target) || invoker.install_target) {
     executable_target_name = target_name
     install_target(target_name + "_install") {
-      forward_variables_from(invoker,
-                             [
-                               "content",
-                               "testonly",
-                             ])
+      forward_variables_from(invoker, [ "testonly" ])
       installable_target_name = executable_target_name
       type = "executable"
+      if (defined(invoker.deps)) {
+        deps = invoker.deps
+      } else {
+        deps = []
+      }
+      if (defined(invoker.content_deps)) {
+        foreach(content_dep, invoker.content_deps) {
+          deps += [ "${content_dep}_install_content" ]
+        }
+      }
     }
   }
 }
@@ -216,19 +295,30 @@
 template("shared_library") {
   target_with_platform_configs(target_name) {
     target_type = "shared_library"
-    forward_variables_from(invoker, "*", [ "install_target" ])
+    forward_variables_from(invoker,
+                           "*",
+                           [
+                             "install_target",
+                             "content_deps",
+                           ])
   }
 
   if (!defined(invoker.install_target) || invoker.install_target) {
     shared_library_target_name = target_name
     install_target(target_name + "_install") {
-      forward_variables_from(invoker,
-                             [
-                               "content",
-                               "testonly",
-                             ])
+      forward_variables_from(invoker, [ "testonly" ])
       installable_target_name = shared_library_target_name
       type = "shared_library"
+      if (defined(invoker.deps)) {
+        deps = invoker.deps
+      } else {
+        deps = []
+      }
+      if (defined(invoker.content_deps)) {
+        foreach(content_dep, invoker.content_deps) {
+          deps += [ "${content_dep}_install_content" ]
+        }
+      }
     }
   }
 }
diff --git a/starboard/build/config/base_configuration.gni b/starboard/build/config/base_configuration.gni
index 16893bf..a789330 100644
--- a/starboard/build/config/base_configuration.gni
+++ b/starboard/build/config/base_configuration.gni
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import("//cobalt/content/fonts/font_configuration.gni")
+import("//starboard/build/config/enable_vr.gni")
 
 declare_args() {
   # Enables the yasm compiler to be used to compile .asm files.
@@ -32,11 +33,6 @@
   # Directory path to static contents' data.
   sb_static_contents_output_data_dir = "$root_out_dir/content/data"
 
-  # Top-level directory for staging deploy build output. Platform deploy
-  # actions should use <(target_deploy_dir) defined in deploy.gypi to place
-  # artifacts for each deploy target in its own subdirectoy.
-  sb_install_output_dir = "$root_out_dir/install"
-
   # Whether this is an Evergreen build.
   sb_is_evergreen = false
 
@@ -92,7 +88,7 @@
 
   # The path to the gni file containing the install_target template which
   # defines how the build should produce the install/ directory.
-  install_target_path = "//starboard/build/install/mock_install.gni"
+  install_target_path = "//starboard/build/install/no_install.gni"
 
   # Target-specific configurations for each platform.
   executable_configs = []
@@ -111,9 +107,6 @@
   # Note: Only enable if there's no system-wide DIAL support.
   in_app_dial = false
 
-  # Whether VR is enabled.
-  enable_vr = false
-
   # Override this value to adjust the default rasterizer setting for your
   # platform.
   default_renderer_options_dependency = "//cobalt/renderer:default_options"
@@ -136,4 +129,9 @@
   # TODO(b/173248397): Migrate to CobaltExtensions or PlatformServices.
   # List of platform-specific targets that get compiled into cobalt.
   cobalt_platform_dependencies = []
+
+  # Whether or not to link with thin archives.
+  use_thin_archive = true
+
+  sb_use_no_rtti = false
 }
diff --git a/starboard/build/config/components.gni b/starboard/build/config/components.gni
index 7aef3d1..7a75048 100644
--- a/starboard/build/config/components.gni
+++ b/starboard/build/config/components.gni
@@ -2,10 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-if (is_starboard) {
-  is_component_build = false
-}
-
 # A helper for forwarding testonly and visibility.
 # Forwarding "*" does not include variables from outer scopes (to avoid copying
 # all globals into each template invocation), so it will not pick up
diff --git a/starboard/build/config/enable_vr.gni b/starboard/build/config/enable_vr.gni
new file mode 100644
index 0000000..b7f2d81
--- /dev/null
+++ b/starboard/build/config/enable_vr.gni
@@ -0,0 +1,26 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  # Whether VR is enabled.
+  enable_vr = getenv("USE_VR")
+}
+
+if (enable_vr == true || enable_vr == "1") {
+  enable_vr = true
+} else if (enable_vr == false || enable_vr == "0" || enable_vr == "") {
+  enable_vr = false
+} else {
+  assert(false, "enable_vr was set to an invalid value.")
+}
diff --git a/starboard/build/config/install.gni b/starboard/build/config/install.gni
new file mode 100644
index 0000000..5c64ac2
--- /dev/null
+++ b/starboard/build/config/install.gni
@@ -0,0 +1,27 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  # Top-level directory for staging deploy build output. Platform deploy
+  # actions should use <(target_deploy_dir) defined in deploy.gypi to place
+  # artifacts for each deploy target in its own subdirectoy.
+  sb_install_output_dir = "$root_out_dir/install"
+
+  # Sub-directory for install content.
+  sb_install_content_subdir = ""
+}
+
+if (sb_install_content_subdir == "") {
+  sb_install_content_subdir = "usr/share/cobalt"
+}
diff --git a/starboard/build/convert_i18n_data.gni b/starboard/build/convert_i18n_data.gni
new file mode 100644
index 0000000..68a1300
--- /dev/null
+++ b/starboard/build/convert_i18n_data.gni
@@ -0,0 +1,36 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This template is meant to be used to convert a set of XLB files into files of
+# a simpler format (e.g. CSV) in the product directory, e.g.
+# out/stub_debug/content/data/i18n.
+
+template("convert_i18n_data") {
+  action_foreach(target_name) {
+    script = "//starboard/build/convert_i18n_data.py"
+
+    sources = invoker.xlb_files
+    inputs = sources
+
+    output_dir = "$sb_static_contents_output_data_dir/i18n"
+    outputs = [ "$output_dir/{{source_name_part}}.csv" ]
+
+    args = [
+      "--input_file",
+      "{{source}}",
+      "--output_file",
+      rebase_path(outputs[0], root_build_dir),
+    ]
+  }
+}
diff --git a/starboard/build/convert_i18n_data.py b/starboard/build/convert_i18n_data.py
index ddb0454..0d905f9 100644
--- a/starboard/build/convert_i18n_data.py
+++ b/starboard/build/convert_i18n_data.py
@@ -12,13 +12,14 @@
 # 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.
-
 """Converts XLB files into CSV files in a given output directory.
 
 Since the output of this script is intended to be use by GYP, all resulting
 paths are using Unix-style forward slashes.
 """
 
+from __future__ import print_function
+
 import argparse
 import os
 import posixpath
@@ -38,7 +39,7 @@
 def ChangeSuffix(filename, new_suffix):
   """Changes the suffix of |filename| to |new_suffix|. If no current suffix,
   adds |new_suffix| to the end of |filename|."""
-  (root, ext) = os.path.splitext(filename)
+  (root, _) = os.path.splitext(filename)
   return root + '.' + new_suffix
 
 
@@ -54,18 +55,18 @@
   with open(output_filename, 'wb') as output_file:
     for msg in messages:
       # Use ; as the separator. Which means it better not be in the name.
-      assert not (';' in msg.attrib['name'])
-      output_file.write(msg.attrib['name'])
-      output_file.write(';')
+      assert ';' not in msg.attrib['name']
+      output_file.write(msg.attrib['name'].encode('utf8'))
+      output_file.write(';'.encode('utf8'))
       # Encode the text as UTF8 to accommodate special characters.
       output_file.write(msg.text.encode('utf8'))
-      output_file.write('\n')
+      output_file.write('\n'.encode('utf8'))
 
 
 def GetOutputs(files_to_convert, output_basedir):
   """Returns a list of filenames relative to the output directory,
   based on a list of input files."""
-  outputs = [];
+  outputs = []
   for filename in files_to_convert:
     dirname = posixpath.dirname(filename)
     relative_filename = posixpath.relpath(filename, dirname)
@@ -88,7 +89,7 @@
       os.makedirs(output_dir)
 
     output_filename = ChangeSuffix(output_filename, 'csv')
-    print 'Converting ' + filename + ' to ' + output_filename
+    print('Converting ' + filename + ' to ' + output_filename)
     ConvertSingleFile(filename, output_filename)
 
 
@@ -96,12 +97,23 @@
   """Called by GYP using pymod_do_main."""
   parser = argparse.ArgumentParser()
   parser.add_argument('-o', dest='output_dir', help='output directory')
-  parser.add_argument('--inputs', action='store_true', dest='list_inputs',
-                      help='prints a list of all input files')
-  parser.add_argument('--outputs', action='store_true', dest='list_outputs',
-                      help='prints a list of all output files')
-  parser.add_argument('input_paths', metavar='path', nargs='+',
-                      help='path to an input file or directory')
+  parser.add_argument(
+      '--inputs',
+      action='store_true',
+      dest='list_inputs',
+      help='prints a list of all input files')
+  parser.add_argument(
+      '--outputs',
+      action='store_true',
+      dest='list_outputs',
+      help='prints a list of all output files')
+  parser.add_argument(
+      'input_paths',
+      metavar='path',
+      nargs='*',
+      help='path to an input file or directory')
+  parser.add_argument('--input_file', help='Input file to convert')
+  parser.add_argument('--output_file', help='Output file to write to')
   options = parser.parse_args(argv)
 
   files_to_convert = [EscapePath(x) for x in options.input_paths]
@@ -109,6 +121,10 @@
   if options.list_inputs:
     return '\n'.join(files_to_convert)
 
+  if options.input_file and options.output_file:
+    ConvertSingleFile(options.input_file, options.output_file)
+    return
+
   if not options.output_dir:
     raise WrongNumberOfArgumentsException('-o required.')
 
@@ -121,15 +137,16 @@
 
 
 def main(argv):
-  print 'Running... in main()'
+  print('Running... in main()')
   try:
     result = DoMain(argv[1:])
-  except WrongNumberOfArgumentsException, e:
-    print >> sys.stderr, e
+  except WrongNumberOfArgumentsException as e:
+    print(e, file=sys.stderr)
     return 1
   if result:
-    print result
+    print(result)
   return 0
 
+
 if __name__ == '__main__':
   sys.exit(main(sys.argv))
diff --git a/starboard/build/copy_file.py b/starboard/build/copy_file.py
new file mode 100644
index 0000000..dd1d24a
--- /dev/null
+++ b/starboard/build/copy_file.py
@@ -0,0 +1,21 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.

+#

+# Licensed under the Apache License, Version 2.0 (the "License");

+# you may not use this file except in compliance with the License.

+# You may obtain a copy of the License at

+#

+#     http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+"""GN Helper to copy a single file."""

+

+import shutil

+import sys

+

+src = sys.argv[1]

+dst = sys.argv[2]

+shutil.copyfile(src, dst)

diff --git a/starboard/build/copy_install_content.py b/starboard/build/copy_install_content.py
new file mode 100644
index 0000000..d63e884
--- /dev/null
+++ b/starboard/build/copy_install_content.py
@@ -0,0 +1,74 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO(b/216341587): Refactor install_content to get rid of this file.
+
+# This file is loosely based on starboard/build/copy_data.py
+"""Copies all input files to the output directory.
+
+The folder structure of the input files is maintained in the output relative
+to the 'base_dir' parameter.
+"""
+
+import argparse
+import os
+import shutil
+
+
+class InvalidArgumentException(Exception):
+  pass
+
+
+def copy_files(files_to_copy, base_dir, output_dir):
+  for path in files_to_copy:
+    # All input paths must point at files.
+    if not os.path.isfile(path):
+      raise InvalidArgumentException(path + ' is not a file.')
+
+    # Get the path of the file relative to the source base_dir.
+    rel_path = os.path.relpath(path, base_dir)
+
+    # In certain cases, files would fail to open on windows if relative paths
+    # were provided.  Using absolute paths fixes this.
+    filename = os.path.abspath(os.path.join(base_dir, rel_path))
+    output_dir = os.path.abspath(output_dir)
+    output_filename = os.path.abspath(os.path.join(output_dir, rel_path))
+
+    # In cases where a directory has turned into a file or vice versa, delete it
+    # before copying it below.
+    if os.path.exists(output_dir) and not os.path.isdir(output_dir):
+      os.remove(output_dir)
+    if os.path.exists(output_filename) and os.path.isdir(output_filename):
+      shutil.rmtree(output_filename)
+
+    if not os.path.exists(output_dir):
+      os.makedirs(output_dir)
+
+    shutil.copy(filename, output_filename)
+
+
+if __name__ == '__main__':
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--output_dir', dest='output_dir', required=True, help='output directory')
+  parser.add_argument(
+      '--base_dir',
+      dest='base_dir',
+      required=True,
+      help='source base directory')
+  parser.add_argument(
+      'input_paths', metavar='path', nargs='+', help='path to an input file')
+  options = parser.parse_args()
+
+  copy_files(options.input_paths, options.base_dir, options.output_dir)
diff --git a/starboard/build/install/install_target.gni b/starboard/build/install/install_target.gni
index e072622..765ed41 100644
--- a/starboard/build/install/install_target.gni
+++ b/starboard/build/install/install_target.gni
@@ -12,34 +12,34 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//starboard/build/config/install.gni")
+
 template("install_target") {
+  installable_target_name = invoker.installable_target_name
+
   if (invoker.type == "executable") {
     install_subdir = "bin"
-    source_name = invoker.installable_target_name
+    source_name = installable_target_name
   } else if (invoker.type == "shared_library") {
     install_subdir = "lib"
-    source_name = "lib${invoker.installable_target_name}.so"
+    source_name = "lib${installable_target_name}.so"
   } else {
     assert(false, "You can only install an executable or shared library.")
   }
 
   copy("copy_" + target_name) {
-    forward_variables_from(invoker,
-                           [
-                             "installable_target_name",
-                             "testonly",
-                           ])
-    deps = [ ":$installable_target_name" ]
-
+    forward_variables_from(invoker, [ "testonly" ])
+    deps = invoker.deps
+    deps += [ ":$installable_target_name" ]
     sources = [ "$root_out_dir/$source_name" ]
     outputs = [ "$root_out_dir/install/$install_subdir/{{source_file_part}}" ]
   }
 
-  if (defined(invoker.content)) {
-    copy("copy_content_" + target_name) {
-      forward_variables_from(invoker, [ "testonly" ])
-      sources = invoker.content
-      outputs = [ "$root_out_dir/install/usr/share/cobalt/{{source_root_relative_dir}}/{{source_file_part}}" ]
-    }
+  group(target_name) {
+    forward_variables_from(invoker, [ "testonly" ])
+    deps = [
+      ":$installable_target_name",
+      ":copy_${target_name}",
+    ]
   }
 }
diff --git a/starboard/build/install/mock_install.gni b/starboard/build/install/mock_install.gni
deleted file mode 100644
index 2c37068..0000000
--- a/starboard/build/install/mock_install.gni
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2021 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.
-
-template("install_target") {
-  not_needed("*")
-  not_needed(invoker, "*")
-}
diff --git a/starboard/build/install/no_install.gni b/starboard/build/install/no_install.gni
new file mode 100644
index 0000000..f2ca601
--- /dev/null
+++ b/starboard/build/install/no_install.gni
@@ -0,0 +1,31 @@
+# Copyright 2021 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.
+
+template("install_target") {
+  not_needed("*", [ "target_name" ])
+  not_needed(invoker,
+             "*",
+             [
+               "testonly",
+               "installable_target_name",
+             ])
+  forward_variables_from(invoker,
+                         [
+                           "testonly",
+                           "installable_target_name",
+                         ])
+  group(target_name) {
+    deps = [ ":$installable_target_name" ]
+  }
+}
diff --git a/starboard/build/nasm_assemble.gni b/starboard/build/nasm_assemble.gni
index eb1989c..9b23cd9 100644
--- a/starboard/build/nasm_assemble.gni
+++ b/starboard/build/nasm_assemble.gni
@@ -113,8 +113,6 @@
       rebase_path(outputs[0], root_build_dir),
       "{{source}}",
     ]
-
-    depfile = outputs[0] + ".d"
   }
 
   # Gather the .o files into a linkable thing. This doesn't actually link
diff --git a/starboard/build/platforms.gni b/starboard/build/platforms.gni
deleted file mode 100644
index dbddfb4..0000000
--- a/starboard/build/platforms.gni
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright 2021 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 contains a mapping of platform names to paths to the platforms's
-# implementation. To add another platform, simply add another entry in the same
-# style to this list.
-
-platforms = [
-  {
-    name = "stub"
-    path = "starboard/stub"
-  },
-  {
-    name = "linux-x64x11"
-    path = "starboard/linux/x64x11"
-  },
-  {
-    name = "linux-x64x11-egl"
-    path = "starboard/linux/x64x11/egl"
-  },
-  {
-    name = "linux-x64x11-internal"
-    path = "starboard/linux/x64x11/internal"
-  },
-  {
-    name = "linux-x64x11-skia"
-    path = "starboard/linux/x64x11/skia"
-  },
-  {
-    name = "android-arm"
-    path = "starboard/android/arm"
-  },
-  {
-    name = "android-arm64"
-    path = "starboard/android/arm64"
-  },
-  {
-    name = "android-arm64-vulkan"
-    path = "starboard/android/arm64/vulkan"
-  },
-  {
-    name = "android-x86"
-    path = "starboard/android/x86"
-  },
-  {
-    name = "raspi-2"
-    path = "starboard/raspi/2"
-  },
-  {
-    name = "raspi-2-skia"
-    path = "starboard/raspi/2/skia"
-  },
-  {
-    name = "win-win32"
-    path = "starboard/win/win32"
-  },
-  {
-    name = "darwin-tvos-arm64"
-    path = "starboard/darwin/tvos/arm64"
-  },
-  {
-    name = "darwin-tvos-simulator"
-    path = "starboard/darwin/tvos/simulator"
-  },
-  {
-    name = "evergreen-x64"
-    path = "starboard/evergreen/x64"
-  },
-]
diff --git a/starboard/build/platforms.py b/starboard/build/platforms.py
new file mode 100644
index 0000000..d090c56
--- /dev/null
+++ b/starboard/build/platforms.py
@@ -0,0 +1,41 @@
+# Copyright 2021 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 contains a mapping of platform names to paths to the platforms's
+implementation. To add another platform, simply add another entry in the same
+style to this dictionary.
+"""
+
+import sys
+
+PLATFORMS = {
+    'stub': 'starboard/stub',
+    'linux-x64x11': 'starboard/linux/x64x11',
+    'linux-x64x11-egl': 'starboard/linux/x64x11/egl',
+    'linux-x64x11-skia': 'starboard/linux/x64x11/skia',
+    'linux-x64x11-clang-crosstool': 'starboard/linux/x64x11/clang/crosstool',
+    'android-arm': 'starboard/android/arm',
+    'android-arm64': 'starboard/android/arm64',
+    'android-arm64-vulkan': 'starboard/android/arm64/vulkan',
+    'android-x86': 'starboard/android/x86',
+    'raspi-2': 'starboard/raspi/2',
+    'raspi-2-skia': 'starboard/raspi/2/skia',
+    'evergreen-x64': 'starboard/evergreen/x64',
+    'evergreen-arm-hardfp': 'starboard/evergreen/arm/hardfp',
+    'evergreen-arm-softfp': 'starboard/evergreen/arm/softfp',
+}
+
+if __name__ == '__main__':
+  if len(sys.argv) != 2:
+    raise TypeError('Usage: {} <platform_name>'.format(sys.argv[0]))
+  print(PLATFORMS[sys.argv[1]])
diff --git a/starboard/build/test.gni b/starboard/build/test.gni
index c78aac7..30e75b6 100644
--- a/starboard/build/test.gni
+++ b/starboard/build/test.gni
@@ -1,5 +1,19 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 template("test") {
-  executable(target_name) {
+  target(gtest_target_type, target_name) {
     testonly = true
     forward_variables_from(invoker, "*", [ "configs" ])
     configs += invoker.configs
diff --git a/starboard/build/toolchain/starboard_toolchains.gni b/starboard/build/toolchain/starboard_toolchains.gni
new file mode 100644
index 0000000..3e5f5bc
--- /dev/null
+++ b/starboard/build/toolchain/starboard_toolchains.gni
@@ -0,0 +1,24 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+_home_dir = getenv("HOME")
+_starboard_toolchains_default_path = "$_home_dir/starboard-toolchains"
+
+declare_args() {
+  starboard_toolchains_path = getenv("_STARBOARD_TOOLCHAINS_DIR_KEY")
+}
+
+if (starboard_toolchains_path == "") {
+  starboard_toolchains_path = _starboard_toolchains_default_path
+}
diff --git a/starboard/client_porting/cwrappers/BUILD.gn b/starboard/client_porting/cwrappers/BUILD.gn
index c9fd093..4594e64 100644
--- a/starboard/client_porting/cwrappers/BUILD.gn
+++ b/starboard/client_porting/cwrappers/BUILD.gn
@@ -14,7 +14,6 @@
 
 static_library("cwrappers") {
   sources = [ "pow_wrapper.cc" ]
-
   public_deps = [ "//starboard/common" ]
 }
 
@@ -30,4 +29,5 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/starboard/client_porting/eztime/BUILD.gn b/starboard/client_porting/eztime/BUILD.gn
index ce50e45..5611ee1 100644
--- a/starboard/client_porting/eztime/BUILD.gn
+++ b/starboard/client_porting/eztime/BUILD.gn
@@ -41,4 +41,6 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/starboard/client_porting/poem/BUILD.gn b/starboard/client_porting/poem/BUILD.gn
index eb23fef..a95274d 100644
--- a/starboard/client_porting/poem/BUILD.gn
+++ b/starboard/client_porting/poem/BUILD.gn
@@ -25,4 +25,6 @@
     "//starboard",
     "//testing/gtest",
   ]
+
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/starboard/common/BUILD.gn b/starboard/common/BUILD.gn
index 43e1716..ab4f53d 100644
--- a/starboard/common/BUILD.gn
+++ b/starboard/common/BUILD.gn
@@ -24,11 +24,12 @@
 }
 
 static_library("common") {
+  check_includes = false
+
   public_deps = [
     ":common_headers_only",
     "//starboard:starboard_headers_only",
   ]
-  check_includes = false
 
   sources = [
     "//starboard/shared/media_session/playback_state.cc",
@@ -82,3 +83,18 @@
     "thread_collision_warner.h",
   ]
 }
+
+target(gtest_target_type, "common_test") {
+  testonly = true
+  sources = [
+    "memory_test.cc",
+    "socket_test.cc",
+    "test_main.cc",
+  ]
+  deps = [
+    ":common",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+  content_deps = [ "//third_party/icu:icudata" ]
+}
diff --git a/starboard/configuration.h b/starboard/configuration.h
index 552565b..5eea69c 100644
--- a/starboard/configuration.h
+++ b/starboard/configuration.h
@@ -927,6 +927,21 @@
          " Please see configuration-public.md for more details."
 #endif  // SB_API_VERSION >= 12 && SB_HAS_QUIRK(SEEK_TO_KEYFRAME)
 
+#if SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if !defined(SB_HAS_COMPACT_10BITS_FRAME)
+#define SB_HAS_COMPACT_10BITS_FRAME 1
+#elif !SB_HAS(SB_HAS_COMPACT_10BITS_FRAME)
+#error "SB_HAS_COMPACT_10BITS_FRAME is required in this API version."
+#endif
+#endif  // SB_API_VERSION >= SB_EXPERIMENTAL_API_VERSION
+#if SB_HAS(SB_HAS_COMPACT_10BITS_FRAME)
+#if SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#error \
+    "SB_HAS(SB_HAS_COMPACT_10BITS_FRAME) requires " \
+     "SB_API_VERSION SB_EXPERIMENTAL_API_VERSION or later."
+#endif  // SB_API_VERSION < SB_EXPERIMENTAL_API_VERSION
+#endif  // SB_HAS(SB_HAS_COMPACT_10BITS_FRAME)
+
 // --- Derived Configuration -------------------------------------------------
 
 #if SB_API_VERSION < 12
diff --git a/starboard/decode_target.h b/starboard/decode_target.h
index bb4f72a..e8183f9 100644
--- a/starboard/decode_target.h
+++ b/starboard/decode_target.h
@@ -131,9 +131,14 @@
   kSbDecodeTargetFormat3PlaneYUVI420,
 
   // A decoder target format consisting of 10bit Y, U, and V planes, in that
-  // order.
+  // order. Each pixel is stored in 16 bits.
   kSbDecodeTargetFormat3Plane10BitYUVI420,
 
+  // A decoder target format consisting of 10bit Y, U, and V planes, in that
+  // order. The plane data is stored in a compact format. Every three 10-bit
+  // pixels are packed into 32 bits.
+  kSbDecodeTargetFormat3Plane10BitYUVI420Compact,
+
   // A decoder target format consisting of a single plane with pixels laid out
   // in the format UYVY.  Since there are two Y values per sample, but only one
   // U value and only one V value, horizontally the Y resolution is twice the
@@ -324,6 +329,7 @@
     case kSbDecodeTargetFormat2PlaneYUVNV12:
       return 2;
     case kSbDecodeTargetFormat3Plane10BitYUVI420:
+    case kSbDecodeTargetFormat3Plane10BitYUVI420Compact:
     case kSbDecodeTargetFormat3PlaneYUVI420:
       return 3;
     default:
diff --git a/starboard/egl_and_gles/BUILD.gn b/starboard/egl_and_gles/BUILD.gn
index ccf1cd83..3afc18c 100644
--- a/starboard/egl_and_gles/BUILD.gn
+++ b/starboard/egl_and_gles/BUILD.gn
@@ -23,13 +23,28 @@
 # in the configuration.gni for the current platform.
 
 group("egl_and_gles") {
-  deps = [ ":egl_and_gles_$gl_type" ]
+  public_deps = [ ":egl_and_gles_$gl_type" ]
 }
 
 group("egl_and_gles_system_gles2") {
   # Use the system-provided implementation of GLES2.
 }
 
+group("egl_and_gles_system_gles3") {
+  # Deprecated. Use GLES2.
+}
+
+config("egl_and_gles_glimp_public_config") {
+  defines = [ "GL_GLEXT_PROTOTYPES" ]
+}
+
+if (gl_type == "glimp") {
+  group("egl_and_gles_glimp") {
+    public_configs = [ ":egl_and_gles_glimp_public_config" ]
+    public_deps = [ "//glimp" ]
+  }
+}
+
 declare_args() {
   enable_d3d11_feature_level_11 = false
 }
diff --git a/starboard/elf_loader/BUILD.gn b/starboard/elf_loader/BUILD.gn
index 6b7d3f4..34182c3 100644
--- a/starboard/elf_loader/BUILD.gn
+++ b/starboard/elf_loader/BUILD.gn
@@ -60,31 +60,43 @@
   ]
 }
 
-static_library("elf_loader_sys") {
-  # System loader based on dlopen/dlsym.
-  # Should be used only for debugging/troubleshooting.
-  sources = _elf_loader_sources + [
-              "elf_loader_impl.h",
-              "elf_loader_sys_impl.cc",
-              "elf_loader_sys_impl.h",
-            ]
+if (sb_is_evergreen_compatible) {
+  static_library("elf_loader_sys") {
+    # System loader based on dlopen/dlsym.
+    # Should be used only for debugging/troubleshooting.
+    sources = _elf_loader_sources + [
+                "elf_loader_impl.h",
+                "elf_loader_sys_impl.cc",
+                "elf_loader_sys_impl.h",
+              ]
 
-  configs += [ ":elf_loader_config" ]
+    configs += [ ":elf_loader_config" ]
 
-  deps = [
-    ":evergreen_config",
-    ":evergreen_info",
-    "//starboard",
-  ]
+    deps = [
+      ":evergreen_config",
+      ":evergreen_info",
+      "//starboard",
+    ]
 
-  if (sb_is_evergreen_compatible) {
-    deps += [ "//third_party/crashpad/wrapper" ]
-  } else {
-    deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
+    if (sb_is_evergreen_compatible) {
+      deps += [ "//third_party/crashpad/wrapper" ]
+    } else {
+      deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
+    }
   }
 }
 
 target(final_executable_type, "elf_loader_sandbox") {
+  content_deps = [ "//third_party/icu:icudata" ]
+  if (cobalt_font_package == "empty") {
+    content_deps += [ "//cobalt/content/fonts:copy_font_data" ]
+  } else {
+    content_deps += [
+      "//cobalt/content/fonts:copy_fonts",
+      "//cobalt/content/fonts:fonts_xml",
+    ]
+  }
+
   sources = [ "sandbox.cc" ]
   configs += [ ":elf_loader_config" ]
 
@@ -104,34 +116,36 @@
   }
 }
 
-target(final_executable_type, "elf_loader_sys_sandbox") {
-  # To properly function the system loader requires the starboard
-  # symbols to be exported from the binary.
-  # To allow symbols to be exported remove the '-fvisibility=hidden' flag
-  # from your compiler_flags.gypi. For Linux this would be:
-  #   starboard/linux/shared/compiler_flags.gypi
-  # Example run:
-  # export LD_LIBRARY_PATH=.
-  # ./elf_loader_sys_sandbox --evergreen_library=app/cobalt/lib/libcobalt.so --evergreen_content=app/cobalt/content
-  sources = [ "sandbox.cc" ]
-  configs += [ ":elf_loader_config" ]
+if (sb_is_evergreen_compatible) {
+  target(final_executable_type, "elf_loader_sys_sandbox") {
+    # To properly function the system loader requires the starboard
+    # symbols to be exported from the binary.
+    # To allow symbols to be exported remove the '-fvisibility=hidden' flag
+    # from your compiler_flags.gypi. For Linux this would be:
+    #   starboard/linux/shared/compiler_flags.gypi
+    # Example run:
+    # export LD_LIBRARY_PATH=.
+    # ./elf_loader_sys_sandbox --evergreen_library=app/cobalt/lib/libcobalt.so --evergreen_content=app/cobalt/content
+    sources = [ "sandbox.cc" ]
+    configs += [ ":elf_loader_config" ]
 
-  starboard_syms_path =
-      rebase_path("//starboard/starboard.syms", root_build_dir)
-  ldflags = [
-    "-Wl,--dynamic-list=$starboard_syms_path",
-    "-ldl",
-  ]
+    starboard_syms_path =
+        rebase_path("//starboard/starboard.syms", root_build_dir)
+    ldflags = [
+      "-Wl,--dynamic-list=$starboard_syms_path",
+      "-ldl",
+    ]
 
-  deps = [
-    ":elf_loader_sys",
-    ":evergreen_info",
-    ":sabi_string",
-    "//starboard",
-  ]
+    deps = [
+      ":elf_loader_sys",
+      ":evergreen_info",
+      ":sabi_string",
+      "//starboard",
+    ]
 
-  if (!sb_is_evergreen_compatible) {
-    deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
+    if (!sb_is_evergreen_compatible) {
+      deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
+    }
   }
 }
 
@@ -158,11 +172,18 @@
       ":copy_elf_loader_testdata",
       ":elf_loader",
     ]
+
+    # TODO: Remove this dependency once MediaSession is migrated to use CobaltExtensions.
     deps += cobalt_platform_dependencies
+    content_deps = [
+      ":copy_elf_loader_testdata",
+      "//third_party/icu:icudata",
+    ]
   }
 }
 
 copy("copy_elf_loader_testdata") {
+  install_content = true
   sources = [
     "testdata/compressed.lz4",
     "testdata/uncompressed",
diff --git a/starboard/evergreen/arm/hardfp/args.gn b/starboard/evergreen/arm/hardfp/args.gn
new file mode 100644
index 0000000..a4160f8
--- /dev/null
+++ b/starboard/evergreen/arm/hardfp/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "evergreen-arm-hardfp"
+target_cpu = "arm"
+target_os = "linux"
diff --git a/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn b/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
new file mode 100644
index 0000000..1c13575
--- /dev/null
+++ b/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+config("sabi_flags") {
+  cflags = [
+    "-mfloat-abi=hard",
+    "-target",
+    "armv7a-none-eabihf",
+  ]
+}
+
+config("platform_configuration") {
+  configs = [ "//starboard/build/config/sabi" ]
+
+  if (current_toolchain == default_toolchain) {
+    configs += [
+      "//starboard/evergreen/arm/shared/platform_configuration:sabi_flags",
+      ":sabi_flags",
+      "//starboard/evergreen/arm/shared/platform_configuration",
+    ]
+  } else {
+    cflags = [ "-O2" ]
+  }
+}
diff --git a/starboard/evergreen/arm/hardfp/platform_configuration/configuration.gni b/starboard/evergreen/arm/hardfp/platform_configuration/configuration.gni
new file mode 100644
index 0000000..b9758e2
--- /dev/null
+++ b/starboard/evergreen/arm/hardfp/platform_configuration/configuration.gni
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/evergreen/shared/platform_configuration/configuration.gni")
+
+sabi_path = "//starboard/sabi/arm/hardfp/sabi-v$sb_api_version.json"
diff --git a/starboard/evergreen/arm/hardfp/sbversion/12/test_filters.py b/starboard/evergreen/arm/hardfp/sbversion/12/test_filters.py
new file mode 100644
index 0000000..f1521c8
--- /dev/null
+++ b/starboard/evergreen/arm/hardfp/sbversion/12/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen ARM Hard FP Platform Test Filters."""
+
+from starboard.evergreen.arm.hardfp import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.EvergreenArmHardFPTestFilters()
diff --git a/starboard/evergreen/arm/hardfp/sbversion/13/test_filters.py b/starboard/evergreen/arm/hardfp/sbversion/13/test_filters.py
new file mode 100644
index 0000000..f1521c8
--- /dev/null
+++ b/starboard/evergreen/arm/hardfp/sbversion/13/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen ARM Hard FP Platform Test Filters."""
+
+from starboard.evergreen.arm.hardfp import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.EvergreenArmHardFPTestFilters()
diff --git a/starboard/evergreen/arm/hardfp/test_filters.py b/starboard/evergreen/arm/hardfp/test_filters.py
new file mode 100644
index 0000000..574726f
--- /dev/null
+++ b/starboard/evergreen/arm/hardfp/test_filters.py
@@ -0,0 +1,29 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen ARM HardFP Platform Test Filters."""
+
+from starboard.evergreen.arm.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return EvergreenArmHardFPTestFilters()
+
+
+class EvergreenArmHardFPTestFilters(shared_test_filters.EvergreenArmTestFilters
+                                   ):
+  """Starboard Evergreen ARM HardFP Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(EvergreenArmHardFPTestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn b/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn
new file mode 100644
index 0000000..ac87933
--- /dev/null
+++ b/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn
@@ -0,0 +1,39 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/config/clang/clang.gni")
+import("//build/toolchain/gcc_toolchain.gni")
+import("//starboard/evergreen/arm/shared/toolchain/toolchain.gni")
+
+clang_toolchain("host") {
+  clang_base_path = clang_base_path
+
+  toolchain_args = {
+    current_os = "linux"
+    current_cpu = "x86"
+  }
+}
+
+gcc_toolchain("target") {
+  cc = evergreen_arm_target_cc
+  cxx = evergreen_arm_target_cxx
+  ld = evergreen_arm_target_ld
+  ar = evergreen_arm_target_ar
+  nm = evergreen_arm_target_nm
+  extra_ldflags = evergreen_arm_target_extra_ldflags
+
+  toolchain_args = {
+    is_clang = false
+  }
+}
diff --git a/starboard/evergreen/arm/shared/platform_configuration/BUILD.gn b/starboard/evergreen/arm/shared/platform_configuration/BUILD.gn
new file mode 100644
index 0000000..8a7581e
--- /dev/null
+++ b/starboard/evergreen/arm/shared/platform_configuration/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+config("sabi_flags") {
+  cflags = [ "-march=armv7a" ]
+}
+
+config("platform_configuration") {
+  configs = [
+    ":sabi_flags",
+    "//starboard/evergreen/shared/platform_configuration",
+  ]
+
+  cflags = [
+    "-isystem" + rebase_path("//third_party/musl/arch/arm", root_build_dir),
+
+    # Force char to be signed.
+    "-fsigned-char",
+  ]
+}
diff --git a/starboard/evergreen/arm/shared/test_filters.py b/starboard/evergreen/arm/shared/test_filters.py
new file mode 100644
index 0000000..00d578a
--- /dev/null
+++ b/starboard/evergreen/arm/shared/test_filters.py
@@ -0,0 +1,40 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen Arm Platform Test Filters."""
+
+from starboard.evergreen.shared import test_filters as shared_test_filters
+from starboard.tools.testing import test_filter
+
+
+def CreateTestFilters():
+  return EvergreenArmTestFilters()
+
+
+class EvergreenArmTestFilters(shared_test_filters.TestFilters):
+  """Starboard Evergreen Arm Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(EvergreenArmTestFilters, self).GetTestFilters()
+    for target, tests in self._FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  _FILTERED_TESTS = {
+      'nplb': [
+          'SbSystemGetStackTest.SunnyDayStackDirection',
+          'SbSystemGetStackTest.SunnyDay',
+          'SbSystemGetStackTest.SunnyDayShortStack',
+          'SbSystemSymbolizeTest.SunnyDay'
+      ],
+  }
diff --git a/starboard/evergreen/arm/shared/toolchain/toolchain.gni b/starboard/evergreen/arm/shared/toolchain/toolchain.gni
new file mode 100644
index 0000000..6fa7de4
--- /dev/null
+++ b/starboard/evergreen/arm/shared/toolchain/toolchain.gni
@@ -0,0 +1,43 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/config/clang/clang.gni")
+
+prefix = rebase_path("$clang_base_path/bin", root_build_dir)
+evergreen_arm_target_cc = "$prefix/clang"
+evergreen_arm_target_cxx = "$prefix/clang++"
+evergreen_arm_target_ld = "$prefix/ld.lld"
+evergreen_arm_target_ar = "${prefix}/llvm-ar"
+evergreen_arm_target_nm = "nm"
+
+# TODO(b/206642994): see if any of these flags, migrated from
+# starboard/tools/toolchain/evergreen_linker.py, can be removed.
+extra_ldflags_list = [
+  "--build-id",
+  "--gc-sections",
+  "-X",
+  "-v",
+  "-eh-frame-hdr",
+  "--fini=__cxa_finalize",
+  "-m armelf",
+  "-shared",
+  "-L$clang_base_path",
+  "-L/usr/lib",
+  "-L/lib",
+  "-nostdlib",
+  "--whole-archive",
+  "--no-whole-archive",
+  "-u GetEvergreenSabiString",
+]
+evergreen_arm_target_extra_ldflags = string_join(" ", extra_ldflags_list)
diff --git a/starboard/evergreen/arm/softfp/args.gn b/starboard/evergreen/arm/softfp/args.gn
new file mode 100644
index 0000000..8344f8a
--- /dev/null
+++ b/starboard/evergreen/arm/softfp/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "evergreen-arm-softfp"
+target_cpu = "arm"
+target_os = "linux"
diff --git a/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn b/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn
new file mode 100644
index 0000000..a17c539
--- /dev/null
+++ b/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+config("sabi_flags") {
+  cflags = [
+    "-mfloat-abi=softfp",
+    "-target",
+    "armv7a-none-eabi",
+  ]
+}
+
+config("platform_configuration") {
+  configs = [ "//starboard/build/config/sabi" ]
+
+  if (current_toolchain == default_toolchain) {
+    configs += [
+      "//starboard/evergreen/arm/shared/platform_configuration:sabi_flags",
+      ":sabi_flags",
+      "//starboard/evergreen/arm/shared/platform_configuration",
+    ]
+  } else {
+    cflags = [ "-O2" ]
+  }
+}
diff --git a/starboard/evergreen/arm/softfp/platform_configuration/configuration.gni b/starboard/evergreen/arm/softfp/platform_configuration/configuration.gni
new file mode 100644
index 0000000..98e1fe6
--- /dev/null
+++ b/starboard/evergreen/arm/softfp/platform_configuration/configuration.gni
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/evergreen/shared/platform_configuration/configuration.gni")
+
+sabi_path = "//starboard/sabi/arm/softfp/sabi-v$sb_api_version.json"
diff --git a/starboard/evergreen/arm/softfp/sbversion/12/test_filters.py b/starboard/evergreen/arm/softfp/sbversion/12/test_filters.py
new file mode 100644
index 0000000..9287f67
--- /dev/null
+++ b/starboard/evergreen/arm/softfp/sbversion/12/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen ARM Soft FP Platform Test Filters."""
+
+from starboard.evergreen.arm.softfp import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.EvergreenArmSoftFPTestFilters()
diff --git a/starboard/evergreen/arm/softfp/sbversion/13/test_filters.py b/starboard/evergreen/arm/softfp/sbversion/13/test_filters.py
new file mode 100644
index 0000000..9287f67
--- /dev/null
+++ b/starboard/evergreen/arm/softfp/sbversion/13/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen ARM Soft FP Platform Test Filters."""
+
+from starboard.evergreen.arm.softfp import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.EvergreenArmSoftFPTestFilters()
diff --git a/starboard/evergreen/arm/softfp/test_filters.py b/starboard/evergreen/arm/softfp/test_filters.py
new file mode 100644
index 0000000..b41a646
--- /dev/null
+++ b/starboard/evergreen/arm/softfp/test_filters.py
@@ -0,0 +1,29 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen ARM SoftFP Platform Test Filters."""
+
+from starboard.evergreen.arm.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return EvergreenArmSoftFPTestFilters()
+
+
+class EvergreenArmSoftFPTestFilters(shared_test_filters.EvergreenArmTestFilters
+                                   ):
+  """Starboard Evergreen ARM SoftFP Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(EvergreenArmSoftFPTestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/evergreen/arm/softfp/toolchain/BUILD.gn b/starboard/evergreen/arm/softfp/toolchain/BUILD.gn
new file mode 100644
index 0000000..ac87933
--- /dev/null
+++ b/starboard/evergreen/arm/softfp/toolchain/BUILD.gn
@@ -0,0 +1,39 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/config/clang/clang.gni")
+import("//build/toolchain/gcc_toolchain.gni")
+import("//starboard/evergreen/arm/shared/toolchain/toolchain.gni")
+
+clang_toolchain("host") {
+  clang_base_path = clang_base_path
+
+  toolchain_args = {
+    current_os = "linux"
+    current_cpu = "x86"
+  }
+}
+
+gcc_toolchain("target") {
+  cc = evergreen_arm_target_cc
+  cxx = evergreen_arm_target_cxx
+  ld = evergreen_arm_target_ld
+  ar = evergreen_arm_target_ar
+  nm = evergreen_arm_target_nm
+  extra_ldflags = evergreen_arm_target_extra_ldflags
+
+  toolchain_args = {
+    is_clang = false
+  }
+}
diff --git a/starboard/evergreen/arm64/sbversion/12/test_filters.py b/starboard/evergreen/arm64/sbversion/12/test_filters.py
new file mode 100644
index 0000000..37accf0
--- /dev/null
+++ b/starboard/evergreen/arm64/sbversion/12/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen ARM64 Platform Test Filters."""
+
+from starboard.evergreen.arm64 import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.EvergreenArm64TestFilters
diff --git a/starboard/evergreen/arm64/sbversion/13/test_filters.py b/starboard/evergreen/arm64/sbversion/13/test_filters.py
new file mode 100644
index 0000000..a58ce9a
--- /dev/null
+++ b/starboard/evergreen/arm64/sbversion/13/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen ARM64 Platform Test Filters."""
+
+from starboard.evergreen.arm64 import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.EvergreenArm64TestFilters()
diff --git a/starboard/evergreen/arm64/test_filters.py b/starboard/evergreen/arm64/test_filters.py
new file mode 100644
index 0000000..c9e6c4b
--- /dev/null
+++ b/starboard/evergreen/arm64/test_filters.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen Arm64 Platform Test Filters."""
+
+from starboard.evergreen.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return EvergreenArm64TestFilters()
+
+
+class EvergreenArm64TestFilters(shared_test_filters.TestFilters):
+  """Starboard Evergreen Arm64 Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(EvergreenArm64TestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/evergreen/shared/launcher.py b/starboard/evergreen/shared/launcher.py
index 4a90b0c..7e621a2 100644
--- a/starboard/evergreen/shared/launcher.py
+++ b/starboard/evergreen/shared/launcher.py
@@ -32,7 +32,7 @@
 
   This Evergreen launcher leverages the platform-specific launchers to start the
   Evergreen loader on a particular device. A staging directory is built that
-  resembles a typical deploy directory, with the Evergreen target and its
+  resembles a typical install directory, with the Evergreen target and its
   content included in the Evergreen loader's content. The platform-specific
   launcher, given command-line switches to tell the Evergreen loader where the
   Evergreen target and its content are, can run the loader without needing to
@@ -146,7 +146,7 @@
     regardless of whether or not we are running locally:
 
              platform:    raspi-2          raspi-2_devel
-             config:      devel             +-- deploy
+             config:      devel             +-- install
              target_name: nplb                   +-- nplb
                                                       +-- content
                                                       +-- nplb
@@ -154,15 +154,55 @@
     This function effectively builds a directory structure that matches both of
     these expectations while minimizing the hard copies made.
 
-    Note: The Linux launcher does not yet look in the deploy directory and
+    Note: The Linux launcher does not yet look in the install directory and
           instead runs the target from the top of its out-directory. This will
           be changed in the future.
     """
-
     if os.path.exists(self.staging_directory):
       shutil.rmtree(self.staging_directory)
     os.makedirs(self.staging_directory)
 
+    if os.path.exists(os.path.join(self.loader_out_directory, 'install')):
+      self._StageTargetsAndContentsGn()
+    else:
+      self._StageTargetsAndContentsGyp()
+
+  def _StageTargetsAndContentsGn(self):
+    content_subdir = os.path.join('usr', 'share', 'cobalt')
+
+    # Copy loader content and binaries
+    loader_install_path = os.path.join(self.loader_out_directory, 'install')
+
+    loader_content_src = os.path.join(loader_install_path, content_subdir)
+    loader_content_dst = os.path.join(self.staging_directory, 'content')
+    shutil.copytree(loader_content_src, loader_content_dst)
+
+    loader_target_src = os.path.join(loader_install_path, 'bin',
+                                     self.loader_target)
+    loader_target_dst = os.path.join(self.staging_directory, self.loader_target)
+    shutil.copy(loader_target_src, loader_target_dst)
+
+    crashpad_target_src = os.path.join(loader_install_path, 'bin',
+                                       _CRASHPAD_TARGET)
+    crashpad_target_dst = os.path.join(self.staging_directory, _CRASHPAD_TARGET)
+    shutil.copy(crashpad_target_src, crashpad_target_dst)
+
+    # Copy target content and binary
+    target_install_path = os.path.join(self.out_directory, 'install')
+    target_staging_dir = os.path.join(self.staging_directory, 'content', 'app',
+                                      self.target_name)
+
+    target_content_src = os.path.join(target_install_path, content_subdir)
+    target_content_dst = os.path.join(target_staging_dir, 'content')
+    shutil.copytree(target_content_src, target_content_dst)
+
+    shlib_name = 'lib{}.so'.format(self.target_name)
+    target_binary_src = os.path.join(target_install_path, 'lib', shlib_name)
+    target_binary_dst = os.path.join(target_staging_dir, 'lib', shlib_name)
+    os.makedirs(os.path.join(target_staging_dir, 'lib'))
+    shutil.copy(target_binary_src, target_binary_dst)
+
+  def _StageTargetsAndContentsGyp(self):
     # <outpath>/deploy/elf_loader_sandbox
     staging_directory_loader = os.path.join(self.staging_directory, 'deploy',
                                             self.loader_target)
@@ -172,7 +212,7 @@
                                                'content', 'app',
                                                self.target_name)
 
-    # Make a hard copy of the ELF Loader's deploy directory in the location
+    # Make a hard copy of the ELF Loader's install_directory in the location
     # specified by |staging_directory_loader|. A symbolic link here would cause
     # future symbolic links to fall through to the original out-directories.
     shutil.copytree(
@@ -186,7 +226,7 @@
         os.path.join(self.out_directory, 'deploy', self.target_name),
         staging_directory_evergreen)
 
-    # TODO: Make the Linux launcher run from the deploy directory, no longer
+    # TODO: Make the Linux launcher run from the install_directory, no longer
     #       create these symlinks, and remove the NOTE from the docstring.
     port_symlink.MakeSymLink(
         os.path.join(staging_directory_loader, self.loader_target),
diff --git a/starboard/evergreen/shared/platform_configuration/BUILD.gn b/starboard/evergreen/shared/platform_configuration/BUILD.gn
index 08c8621..43d8a69 100644
--- a/starboard/evergreen/shared/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/shared/platform_configuration/BUILD.gn
@@ -79,10 +79,7 @@
   }
 
   if (is_clang) {
-    ldflags += [
-      "-fuse-ld=lld",
-      "-nostdlib",
-    ]
+    ldflags += [ "-nostdlib" ]
     cflags += [
       "-fcolor-diagnostics",
 
diff --git a/starboard/evergreen/shared/platform_configuration/configuration.gni b/starboard/evergreen/shared/platform_configuration/configuration.gni
index 44586a3..5b331dc 100644
--- a/starboard/evergreen/shared/platform_configuration/configuration.gni
+++ b/starboard/evergreen/shared/platform_configuration/configuration.gni
@@ -36,3 +36,5 @@
     "//starboard/evergreen/shared/platform_configuration:no_pedantic_warnings"
 
 cobalt_licenses_platform = "evergreen"
+
+install_target_path = "//starboard/build/install/install_target.gni"
diff --git a/starboard/evergreen/shared/strip_install_target.gni b/starboard/evergreen/shared/strip_install_target.gni
new file mode 100644
index 0000000..675ae4d
--- /dev/null
+++ b/starboard/evergreen/shared/strip_install_target.gni
@@ -0,0 +1,50 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+template("strip_install_target") {
+  installable_target_name = invoker.installable_target_name
+
+  assert(defined(invoker.strip_executable) && invoker.strip_executable != "",
+         "strip executable required for stripping")
+
+  if (invoker.type == "executable") {
+    install_subdir = "bin"
+    source_name = installable_target_name
+  } else if (invoker.type == "shared_library") {
+    install_subdir = "lib"
+    source_name = "lib${installable_target_name}.so"
+  } else {
+    assert(false, "You can only install an executable or shared library.")
+  }
+
+  action(target_name) {
+    forward_variables_from(invoker, [ "testonly" ])
+
+    script = "//starboard/build/run_bash.py"
+
+    inputs = [ "$root_out_dir/$source_name" ]
+
+    deps = invoker.deps
+    deps += [ ":$installable_target_name" ]
+
+    outputs = [ "$sb_install_output_dir/$install_subdir/$source_name" ]
+
+    args = [
+      strip_executable,
+      "-o",
+      rebase_path(outputs[0], root_out_dir),
+      rebase_path("$root_out_dir/$source_name", root_out_dir),
+    ]
+  }
+}
diff --git a/starboard/evergreen/shared/test_filters.py b/starboard/evergreen/shared/test_filters.py
new file mode 100644
index 0000000..c5a6592
--- /dev/null
+++ b/starboard/evergreen/shared/test_filters.py
@@ -0,0 +1,40 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen Platform Test Filters."""
+
+from starboard.tools.testing import test_filter
+
+
+class TestFilters(object):
+  """Starboard Evergreen platform test filters."""
+
+  def GetTestFilters(self):
+    filters = []
+    for target, tests in self._FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  _FILTERED_TESTS = {
+      'nplb': [
+          'MemoryReportingTest.CapturesOperatorDeleteNothrow',
+          'SbAudioSinkTest.*', 'SbDrmTest.AnySupportedKeySystems'
+      ],
+
+      # player_filter_tests test the platform's Starboard implementation of
+      # the filter-based player, which is not exposed through the Starboard
+      # interface. Since Evergreen has no visibility of the platform's
+      # specific Starboard implementation, rely on the platform to test this
+      # directly instead.
+      'player_filter_tests': [test_filter.FILTER_ALL],
+  }
diff --git a/starboard/evergreen/testing/tests/out_of_storage_test.sh b/starboard/evergreen/testing/tests/out_of_storage_test.sh
index 851b54c..6c3c219 100755
--- a/starboard/evergreen/testing/tests/out_of_storage_test.sh
+++ b/starboard/evergreen/testing/tests/out_of_storage_test.sh
@@ -45,9 +45,21 @@
 
   OLD_TIMEOUT="${TIMEOUT}"
   TIMEOUT=300
+  RESULT=0
 
-  cycle_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "Failed to update, log \"error\" code is 12"
+  cycle_cobalt "file:///tests/${TEST_FILE}?channel=test" "${TEST_NAME}.0.log" "Failed to update, error code is 22"
+  if [[ $? -ne 0 ]]; then
+    log "error" "Failed to run out of storage"
+    RESULT=1
+  fi
 
+  if [[ $RESULT -ne 1 ]]; then
+    cycle_cobalt "file:///tests/${TEST_FILE}?channel=test\&min_storage=5" "${TEST_NAME}.0.log" "Failed to update, error code is 200"
+    if [[ $? -ne 0 ]]; then
+      log "error" "Failed to run out of storage"
+      RESULT=1
+    fi
+  fi
   # Remove the symbolic link.
   run_command "rm -f ${STORAGE_DIR}" 1> /dev/null
 
@@ -56,12 +68,7 @@
     run_command "mv \"${STORAGE_DIR}.tmp\" \"${STORAGE_DIR}\"" 1> /dev/null
   fi
 
-  if [[ $? -ne 0 ]]; then
-    log "error" "Failed to run out of storage"
-    return 1
-  fi
-
   TIMEOUT="${OLD_TIMEOUT}"
 
-  return 0
+  return $RESULT
 }
diff --git a/starboard/evergreen/testing/tests/suspend_resume_test.sh b/starboard/evergreen/testing/tests/suspend_resume_test.sh
index 35b549f..6d2dcfa 100755
--- a/starboard/evergreen/testing/tests/suspend_resume_test.sh
+++ b/starboard/evergreen/testing/tests/suspend_resume_test.sh
@@ -39,7 +39,7 @@
   clear_storage
 
   LOG="${TEST_NAME}.0.log"
-  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${LOG}" LOADER --update_check_delay=1
+  start_cobalt "file:///tests/${TEST_FILE}?channel=test" "${LOG}" LOADER --update_check_delay_seconds=1
 
   VAR=1
 
diff --git a/starboard/evergreen/testing/tests/test.html b/starboard/evergreen/testing/tests/test.html
index e53f706..97f205a 100644
--- a/starboard/evergreen/testing/tests/test.html
+++ b/starboard/evergreen/testing/tests/test.html
@@ -53,6 +53,9 @@
   }
 
   query.forEach(part => {
+    if (part.startsWith("min_storage=")) {
+      window.h5vcc.settings.set("Updater.MinFreeSpaceBytes", part.split("=")[1]);
+    }
     if (changeChannel) {
       return;
     }
@@ -61,6 +64,7 @@
       targetChannel = part.split("=")[1];
       changeChannel = setInterval(tryChangeChannel, 500);
     }
+
   });
 </script>
 </html>
diff --git a/starboard/evergreen/x64/args.gn b/starboard/evergreen/x64/args.gn
new file mode 100644
index 0000000..1c510ed
--- /dev/null
+++ b/starboard/evergreen/x64/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "evergreen-x64"
+target_os = "linux"
+target_cpu = "x64"
diff --git a/starboard/evergreen/x64/install_target.gni b/starboard/evergreen/x64/install_target.gni
new file mode 100644
index 0000000..ecfbc13
--- /dev/null
+++ b/starboard/evergreen/x64/install_target.gni
@@ -0,0 +1,23 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/evergreen/shared/strip_install_target.gni")
+import("//starboard/evergreen/x64/toolchain/strip.gni")
+
+template("install_target") {
+  strip_install_target(target_name) {
+    forward_variables_from(invoker, "*")
+    strip_executable = strip_executable
+  }
+}
diff --git a/starboard/evergreen/x64/platform_configuration/BUILD.gn b/starboard/evergreen/x64/platform_configuration/BUILD.gn
index 658a9ac..f07413a 100644
--- a/starboard/evergreen/x64/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/x64/platform_configuration/BUILD.gn
@@ -31,6 +31,10 @@
 
     cflags = [ "-isystem" +
                rebase_path("//third_party/musl/arch/x86_64", root_build_dir) ]
+
+    if (is_clang) {
+      ldflags = [ "-fuse-ld=lld" ]
+    }
   } else {
     cflags = [ "-O2" ]
   }
diff --git a/starboard/evergreen/x64/platform_configuration/configuration.gni b/starboard/evergreen/x64/platform_configuration/configuration.gni
index a5dff93..3d2eb48 100644
--- a/starboard/evergreen/x64/platform_configuration/configuration.gni
+++ b/starboard/evergreen/x64/platform_configuration/configuration.gni
@@ -15,3 +15,5 @@
 import("//starboard/evergreen/shared/platform_configuration/configuration.gni")
 
 sabi_path = "//starboard/sabi/x64/sysv/sabi-v$sb_api_version.json"
+
+install_target_path = "//starboard/evergreen/x64/install_target.gni"
diff --git a/starboard/evergreen/x64/sbversion/12/test_filters.py b/starboard/evergreen/x64/sbversion/12/test_filters.py
new file mode 100644
index 0000000..a56490a
--- /dev/null
+++ b/starboard/evergreen/x64/sbversion/12/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen X64 Platform Test Filters."""
+
+from starboard.evergreen.x64 import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  parent_test_filters.EvergreenX64TestFilters()
diff --git a/starboard/evergreen/x64/sbversion/13/test_filters.py b/starboard/evergreen/x64/sbversion/13/test_filters.py
new file mode 100644
index 0000000..53b30f2
--- /dev/null
+++ b/starboard/evergreen/x64/sbversion/13/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen X64 Platform Test Filters."""
+
+from starboard.evergreen.x64 import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.EvergreenX64TestFilters()
diff --git a/starboard/evergreen/x64/test_filters.py b/starboard/evergreen/x64/test_filters.py
new file mode 100644
index 0000000..f4e36d7
--- /dev/null
+++ b/starboard/evergreen/x64/test_filters.py
@@ -0,0 +1,46 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen X64 Platform Test Filters."""
+
+import os
+
+from starboard.tools import paths
+from starboard.evergreen.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return EvergreenX64TestFilters()
+
+
+class EvergreenX64TestFilters(shared_test_filters.TestFilters):
+  """Starboard Evergreen X64 Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(EvergreenX64TestFilters, self).GetTestFilters()
+    # Remove the exclusion filter on SbDrmTest.AnySupportedKeySystems.
+    # Generally, children of linux/shared do not support widevine, but children
+    # of linux/x64x11 do, if the content decryption module is present.
+
+    has_cdm = os.path.isfile(
+        os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'cdm', 'cdm',
+                     'include', 'content_decryption_module.h'))
+
+    if not has_cdm:
+      return filters
+
+    for test_filter in filters:
+      if (test_filter.target_name == 'nplb' and
+          test_filter.test_name == 'SbDrmTest.AnySupportedKeySystems'):
+        filters.remove(test_filter)
+    return filters
diff --git a/starboard/evergreen/x64/toolchain/BUILD.gn b/starboard/evergreen/x64/toolchain/BUILD.gn
index 20e9a1d..be6fcbb 100644
--- a/starboard/evergreen/x64/toolchain/BUILD.gn
+++ b/starboard/evergreen/x64/toolchain/BUILD.gn
@@ -12,17 +12,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
-
 clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 }
 
 clang_toolchain("target") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
   # TODO: additional work is likely needed to configure the linker for
   # evergreen (see //starboard/tools/toolchain/evergreen_linker.py).
 }
diff --git a/starboard/evergreen/x64/toolchain/strip.gni b/starboard/evergreen/x64/toolchain/strip.gni
new file mode 100644
index 0000000..fc1f4ee
--- /dev/null
+++ b/starboard/evergreen/x64/toolchain/strip.gni
@@ -0,0 +1,15 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+strip_executable = "strip"
diff --git a/starboard/evergreen/x86/sbversion/12/test_filters.py b/starboard/evergreen/x86/sbversion/12/test_filters.py
new file mode 100644
index 0000000..19fc15e
--- /dev/null
+++ b/starboard/evergreen/x86/sbversion/12/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen X86 Platform Test Filters."""
+
+from starboard.evergreen.x86 import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.EvergreenX64TestFilters()
diff --git a/starboard/evergreen/x86/sbversion/13/test_filters.py b/starboard/evergreen/x86/sbversion/13/test_filters.py
new file mode 100644
index 0000000..19fc15e
--- /dev/null
+++ b/starboard/evergreen/x86/sbversion/13/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen X86 Platform Test Filters."""
+
+from starboard.evergreen.x86 import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.EvergreenX64TestFilters()
diff --git a/starboard/evergreen/x86/test_filters.py b/starboard/evergreen/x86/test_filters.py
new file mode 100644
index 0000000..f8e9450
--- /dev/null
+++ b/starboard/evergreen/x86/test_filters.py
@@ -0,0 +1,46 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Evergreen X86 Platform Test Filters."""
+
+import os
+
+from starboard.tools import paths
+from starboard.evergreen.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return EvergreenX86TestFilters()
+
+
+class EvergreenX86TestFilters(shared_test_filters.TestFilters):
+  """Starboard Evergreen X86 Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(EvergreenX86TestFilters, self).GetTestFilters()
+    # Remove the exclusion filter on SbDrmTest.AnySupportedKeySystems.
+    # Generally, children of linux/shared do not support widevine, but children
+    # of linux/x64x11 do, if the content decryption module is present.
+
+    has_cdm = os.path.isfile(
+        os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'cdm', 'cdm',
+                     'include', 'content_decryption_module.h'))
+
+    if not has_cdm:
+      return filters
+
+    for test_filter in filters:
+      if (test_filter.target_name == 'nplb' and
+          test_filter.test_name == 'SbDrmTest.AnySupportedKeySystems'):
+        filters.remove(test_filter)
+    return filters
diff --git a/starboard/examples/window/BUILD.gn b/starboard/examples/window/BUILD.gn
index ddd7b02..beb0040 100644
--- a/starboard/examples/window/BUILD.gn
+++ b/starboard/examples/window/BUILD.gn
@@ -17,4 +17,5 @@
 
   sources = [ "main.cc" ]
   public_deps = [ "//starboard" ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/starboard/linux/shared/BUILD.gn b/starboard/linux/shared/BUILD.gn
index ea46558..73e2a93 100644
--- a/starboard/linux/shared/BUILD.gn
+++ b/starboard/linux/shared/BUILD.gn
@@ -71,6 +71,8 @@
     "//starboard/linux/shared/player_components_factory.cc",
     "//starboard/linux/shared/routes.cc",
     "//starboard/linux/shared/routes.h",
+    "//starboard/linux/shared/soft_mic_platform_service.cc",
+    "//starboard/linux/shared/soft_mic_platform_service.h",
     "//starboard/linux/shared/system_get_connection_type.cc",
     "//starboard/linux/shared/system_get_device_type.cc",
     "//starboard/linux/shared/system_get_extensions.cc",
@@ -178,6 +180,8 @@
     "//starboard/shared/posix/file_seek.cc",
     "//starboard/shared/posix/file_truncate.cc",
     "//starboard/shared/posix/file_write.cc",
+    "//starboard/shared/posix/free_space.cc",
+    "//starboard/shared/posix/free_space.h",
     "//starboard/shared/posix/log.cc",
     "//starboard/shared/posix/log_flush.cc",
     "//starboard/shared/posix/log_format.cc",
@@ -272,6 +276,8 @@
     "//starboard/shared/pulse/pulse_dynamic_load_dispatcher.h",
     "//starboard/shared/signal/crash_signals.h",
     "//starboard/shared/signal/crash_signals_sigaction.cc",
+    "//starboard/shared/signal/debug_signals.cc",
+    "//starboard/shared/signal/debug_signals.h",
     "//starboard/shared/signal/suspend_signals.cc",
     "//starboard/shared/signal/suspend_signals.h",
     "//starboard/shared/signal/system_request_conceal.cc",
@@ -417,7 +423,7 @@
   if (is_internal_build) {
     sources += [
       "//starboard/linux/shared/drm_create_system.cc",
-      "//starboard/linux/shared/oemcrypto_engine_device_properties_linux.cc",
+      "//starboard/linux/shared/internal/oemcrypto_engine_device_properties_linux.cc",
       "//starboard/shared/starboard/drm/drm_close_session.cc",
       "//starboard/shared/starboard/drm/drm_destroy_system.cc",
       "//starboard/shared/starboard/drm/drm_generate_session_update_request.cc",
diff --git a/starboard/linux/shared/platform_configuration/BUILD.gn b/starboard/linux/shared/platform_configuration/BUILD.gn
index 684a926..d221e63 100644
--- a/starboard/linux/shared/platform_configuration/BUILD.gn
+++ b/starboard/linux/shared/platform_configuration/BUILD.gn
@@ -83,12 +83,6 @@
     "-std=c99",
   ]
   cflags_cc = [ "-std=gnu++14" ]
-  ldflags += [
-    "-Wl,-rpath=\$ORIGIN/lib",
-
-    # Cleanup unused sections
-    "-Wl,-gc-sections",
-  ]
 
   if (use_asan) {
     cflags += [
diff --git a/starboard/linux/shared/soft_mic_platform_service.cc b/starboard/linux/shared/soft_mic_platform_service.cc
new file mode 100644
index 0000000..6d96f88
--- /dev/null
+++ b/starboard/linux/shared/soft_mic_platform_service.cc
@@ -0,0 +1,215 @@
+// Copyright 2021 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/soft_mic_platform_service.h"
+
+#include <memory>
+#include <string>
+
+#include "cobalt/extension/platform_service.h"
+#include "starboard/common/log.h"
+#include "starboard/common/string.h"
+#include "starboard/configuration.h"
+#include "starboard/shared/starboard/application.h"
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/elf_loader/evergreen_config.h"
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
+
+typedef struct CobaltExtensionPlatformServicePrivate {
+  void* context;
+  ReceiveMessageCallback receive_callback;
+} CobaltExtensionPlatformServicePrivate;
+
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace shared {
+
+namespace {
+
+const char kGetMicSupport[] = "\"getMicSupport\"";
+const char kNotifySearchActive[] = "\"notifySearchActive\"";
+const char kNotifySearchInactive[] = "\"notifySearchInactive\"";
+const char kHasHardMicSupport[] = "has_hard_mic_support";
+const char kHasSoftMicSupport[] = "has_soft_mic_support";
+const char kMicGesture[] = "mic_gesture";
+const char kJSONTrue[] = "true";
+const char kJSONFalse[] = "false";
+
+bool Has(const char* name) {
+  // Check if platform has service name.
+  SB_LOG(INFO) << "Has(): " << name;
+  return strcmp(name, "com.google.youtube.tv.SoftMic") == 0;
+}
+
+CobaltExtensionPlatformService Open(void* context,
+                                    const char* name,
+                                    ReceiveMessageCallback receive_callback) {
+  SB_DCHECK(context);
+
+  CobaltExtensionPlatformService service;
+
+  if (!Has(name)) {
+    SB_LOG(ERROR) << "Open() service name does not exist: " << name;
+    service = kCobaltExtensionPlatformServiceInvalid;
+  } else {
+    SB_LOG(INFO) << "Open() service created: " << name;
+    service =
+        new CobaltExtensionPlatformServicePrivate({context, receive_callback});
+  }
+
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  const bool is_evergreen = elf_loader::EvergreenConfig::GetInstance() != NULL;
+#else
+  const bool is_evergreen = false;
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
+
+  // The name parameter memory is allocated in h5vcc_platform_service::Open()
+  // with new[] and must be deallocated here.
+  // If we are in an Evergreen build, the name parameter must be deallocated
+  // with SbMemoryDeallocate(), since new[] will map to SbMemoryAllocate()
+  // in an Evergreen build.
+  if (is_evergreen) {
+    SbMemoryDeallocate((void*)name);  // NOLINT
+  } else {
+    delete[] name;
+  }
+
+  return service;
+}
+
+void Close(CobaltExtensionPlatformService service) {
+  SB_LOG(INFO) << "Close() Service.";
+  delete static_cast<CobaltExtensionPlatformServicePrivate*>(service);
+}
+
+void* Send(CobaltExtensionPlatformService service,
+           void* data,
+           uint64_t length,
+           uint64_t* output_length,
+           bool* invalid_state) {
+  SB_DCHECK(service);
+  SB_DCHECK(data);
+  SB_DCHECK(output_length);
+
+  // This bool flag is set to true if the data argument
+  // is successfully parsed as a known web app request,
+  // false otherwise. It is then sent as a synchronous
+  // response to the web app indicating this success or
+  // failure.
+  auto valid_message_received = false;
+
+  char message[length + 1];
+  std::memcpy(message, data, length);
+  message[length] = '\0';
+
+  SB_LOG(INFO) << "Send() message: " << message;
+
+  if (strcmp(message, kGetMicSupport) == 0) {
+    // Process "getMicSupport" web app message.
+    SB_LOG(INFO) << "Send() kGetMicSupport message received.";
+
+    auto has_hard_mic = false;
+    auto has_soft_mic = true;
+    auto mic_gesture_hold = false;
+    auto mic_gesture_tap = false;
+
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+    using shared::starboard::Application;
+
+    // Check for explicit true or false switch value for kHasHardMicSupport,
+    // kHasSoftMicSupport, kMicGestureHold, and kMicGestureTap optional target
+    // params. Use default values if none are set.
+    auto command_line = Application::Get()->GetCommandLine();
+
+    auto hard_mic_switch_value =
+        command_line->GetSwitchValue(kHasHardMicSupport);
+    if (hard_mic_switch_value == kJSONTrue) {
+      has_hard_mic = true;
+    } else if (hard_mic_switch_value == kJSONFalse) {
+      has_hard_mic = false;
+    }
+
+    auto soft_mic_switch_value =
+        command_line->GetSwitchValue(kHasSoftMicSupport);
+    if (soft_mic_switch_value == kJSONTrue) {
+      has_soft_mic = true;
+    } else if (soft_mic_switch_value == kJSONFalse) {
+      has_soft_mic = false;
+    }
+
+    auto mic_gesture_switch_value = command_line->GetSwitchValue(kMicGesture);
+    mic_gesture_hold = mic_gesture_switch_value == "hold";
+    mic_gesture_tap = mic_gesture_switch_value == "tap";
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
+
+    auto mic_gesture = "null";
+    if (mic_gesture_hold)
+      mic_gesture = "\"HOLD\"";
+    else if (mic_gesture_tap)
+      mic_gesture = "\"TAP\"";
+
+    auto response = FormatString(
+        "{\"hasHardMicSupport\": %s, \"hasSoftMicSupport\": %s, "
+        "\"micGesture\": %s}",
+        (has_hard_mic ? kJSONTrue : kJSONFalse),
+        (has_soft_mic ? kJSONTrue : kJSONFalse), mic_gesture);
+
+    SB_LOG(INFO) << "Send() kGetMicSupport response: " << response;
+
+    // Here we are synchronously calling the receive_callback() from within
+    // Send() which is unnecessary. Implementations should prioritize
+    // returning from Send() ASAP to avoid blocking the JavaScript thread.
+    static_cast<CobaltExtensionPlatformServicePrivate*>(service)
+        ->receive_callback(service->context,
+                           static_cast<const void*>(response.c_str()),
+                           response.length());
+
+    valid_message_received = true;
+  } else if (strcmp(message, kNotifySearchActive) == 0) {
+    // Process "notifySearchActive" web app message.
+    SB_LOG(INFO) << "Send() kNotifySearchActive message received";
+    valid_message_received = true;
+  } else if (strcmp(message, kNotifySearchInactive) == 0) {
+    // Process "notifySearchInactive" web app message.
+    SB_LOG(INFO) << "Send() kNotifySearchInactive message received";
+    valid_message_received = true;
+  }
+
+  // Synchronous bool response to the web app confirming the message
+  // has been successfully parsed by the platform. Response needs
+  // to be allocated with SbMemoryAllocate() as the caller of Send()
+  // in cobalt/h5vcc/h5vcc_platform_service.cc Send() uses
+  // SbMemoryDeallocate().
+  bool* ptr = reinterpret_cast<bool*>(SbMemoryAllocate(sizeof(bool)));
+  *ptr = valid_message_received;
+  *output_length = sizeof(bool);
+  return static_cast<void*>(ptr);
+}
+
+const CobaltExtensionPlatformServiceApi kPlatformServiceApi = {
+    kCobaltExtensionPlatformServiceName,
+    1,  // API version that's implemented.
+    &Has,
+    &Open,
+    &Close,
+    &Send};
+
+}  // namespace
+
+const void* GetPlatformServiceApi() {
+  return &kPlatformServiceApi;
+}
+
+}  // namespace shared
+}  // namespace starboard
diff --git a/starboard/linux/shared/soft_mic_platform_service.h b/starboard/linux/shared/soft_mic_platform_service.h
new file mode 100644
index 0000000..93f0e0d
--- /dev/null
+++ b/starboard/linux/shared/soft_mic_platform_service.h
@@ -0,0 +1,27 @@
+// Copyright 2021 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_SOFT_MIC_PLATFORM_SERVICE_H_
+#define STARBOARD_LINUX_SHARED_SOFT_MIC_PLATFORM_SERVICE_H_
+
+// Omit namespace linux due to symbol name conflict.
+namespace starboard {
+namespace shared {
+
+const void* GetPlatformServiceApi();
+
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_LINUX_SHARED_SOFT_MIC_PLATFORM_SERVICE_H_
diff --git a/starboard/linux/shared/starboard_platform.gypi b/starboard/linux/shared/starboard_platform.gypi
index fef07e1..9c3bec7 100644
--- a/starboard/linux/shared/starboard_platform.gypi
+++ b/starboard/linux/shared/starboard_platform.gypi
@@ -40,6 +40,8 @@
       '<(DEPTH)/starboard/linux/shared/player_components_factory.cc',
       '<(DEPTH)/starboard/linux/shared/routes.cc',
       '<(DEPTH)/starboard/linux/shared/routes.h',
+      '<(DEPTH)/starboard/linux/shared/soft_mic_platform_service.cc',
+      '<(DEPTH)/starboard/linux/shared/soft_mic_platform_service.h',
       '<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc',
       '<(DEPTH)/starboard/linux/shared/system_get_device_type.cc',
       '<(DEPTH)/starboard/linux/shared/system_get_extensions.cc',
@@ -147,6 +149,8 @@
       '<(DEPTH)/starboard/shared/posix/file_seek.cc',
       '<(DEPTH)/starboard/shared/posix/file_truncate.cc',
       '<(DEPTH)/starboard/shared/posix/file_write.cc',
+      '<(DEPTH)/starboard/shared/posix/free_space.cc',
+      '<(DEPTH)/starboard/shared/posix/free_space.h',
       '<(DEPTH)/starboard/shared/posix/log_flush.cc',
       '<(DEPTH)/starboard/shared/posix/log_format.cc',
       '<(DEPTH)/starboard/shared/posix/log_is_tty.cc',
@@ -241,6 +245,8 @@
       '<(DEPTH)/starboard/shared/pulse/pulse_dynamic_load_dispatcher.h',
       '<(DEPTH)/starboard/shared/signal/crash_signals_sigaction.cc',
       '<(DEPTH)/starboard/shared/signal/crash_signals.h',
+      '<(DEPTH)/starboard/shared/signal/debug_signals.cc',
+      '<(DEPTH)/starboard/shared/signal/debug_signals.h',
       '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
       '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
       '<(DEPTH)/starboard/shared/signal/system_request_conceal.cc',
@@ -379,7 +385,7 @@
         ],
         'starboard_platform_sources': [
           '<(DEPTH)/starboard/linux/shared/drm_create_system.cc',
-          '<(DEPTH)/starboard/linux/shared/oemcrypto_engine_device_properties_linux.cc',
+          '<(DEPTH)/starboard/linux/shared/internal/oemcrypto_engine_device_properties_linux.cc',
 
           '<(DEPTH)/starboard/shared/starboard/drm/drm_close_session.cc',
           '<(DEPTH)/starboard/shared/starboard/drm/drm_destroy_system.cc',
diff --git a/starboard/linux/shared/system_get_extensions.cc b/starboard/linux/shared/system_get_extensions.cc
index 3411d93..3c44ffd 100644
--- a/starboard/linux/shared/system_get_extensions.cc
+++ b/starboard/linux/shared/system_get_extensions.cc
@@ -16,8 +16,12 @@
 
 #include "cobalt/extension/configuration.h"
 #include "cobalt/extension/crash_handler.h"
+#include "cobalt/extension/free_space.h"
 #include "cobalt/extension/memory_mapped_file.h"
+#include "cobalt/extension/platform_service.h"
 #include "starboard/common/string.h"
+#include "starboard/linux/shared/soft_mic_platform_service.h"
+#include "starboard/shared/posix/free_space.h"
 #include "starboard/shared/posix/memory_mapped_file.h"
 #include "starboard/shared/starboard/crash_handler.h"
 #if SB_IS(EVERGREEN_COMPATIBLE)
@@ -37,6 +41,9 @@
     }
   }
 #endif
+  if (strcmp(name, kCobaltExtensionPlatformServiceName) == 0) {
+    return starboard::shared::GetPlatformServiceApi();
+  }
   if (strcmp(name, kCobaltExtensionConfigurationName) == 0) {
     return starboard::shared::GetConfigurationApi();
   }
@@ -46,5 +53,8 @@
   if (strcmp(name, kCobaltExtensionMemoryMappedFileName) == 0) {
     return starboard::shared::posix::GetMemoryMappedFileApi();
   }
+  if (strcmp(name, kCobaltExtensionFreeSpaceName) == 0) {
+    return starboard::shared::posix::GetFreeSpaceApi();
+  }
   return NULL;
 }
diff --git a/starboard/linux/shared/test_filters.py b/starboard/linux/shared/test_filters.py
new file mode 100644
index 0000000..7f0d6ea
--- /dev/null
+++ b/starboard/linux/shared/test_filters.py
@@ -0,0 +1,87 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux Platform Test Filters."""
+
+import os
+
+from starboard.tools import paths
+from starboard.tools.testing import test_filter
+
+
+class TestFilters(object):
+  """Starboard Linux platform test filters."""
+
+  def GetTestFilters(self):
+    filters = []
+
+    has_cdm = os.path.isfile(
+        os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'ce_cdm', 'cdm',
+                     'include', 'cdm.h'))
+
+    for target, tests in self._FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+
+    if has_cdm:
+      return filters
+
+    # Filter the drm related tests, as ce_cdm is not present.
+    for target, tests in self._DRM_RELATED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  _DRM_RELATED_TESTS = {
+      'nplb': [
+          'SbDrmTest.AnySupportedKeySystems',
+          'SbMediaCanPlayMimeAndKeySystem.AnySupportedKeySystems',
+      ],
+  }
+
+  # pylint: disable=line-too-long
+  _FILTERED_TESTS = {
+      'player_filter_tests': [
+          # libdav1d crashes when fed invalid data
+          'VideoDecoderTests/VideoDecoderTest.*Invalid*',
+      ],
+  }
+  _FILTERED_TESTS['nplb'] = []
+  # Conditionally disables tests that require ipv6
+  if os.getenv('IPV6_AVAILABLE', '1') == '0':
+    _FILTERED_TESTS['nplb'].extend([
+        'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDayDestination/1',
+        'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceForDestination/1',
+        'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceNotLoopback/1',
+    ])
+  # TODO: Re-enable once tests or infra fixed.
+  _FILTERED_TESTS['nplb'].extend([
+      'SbSocketAddressTypes/SbSocketBindTest.RainyDayBadInterface/1',
+      'SbSocketAddressTypes/PairSbSocketGetLocalAddressTest.SunnyDayConnected/1',
+      'SbSocketAddressTypes/PairSbSocketIsConnectedAndIdleTest.SunnyDay/1',
+      'SbSocketAddressTypes/PairSbSocketIsConnectedTest.SunnyDay/1',
+      'SbSocketAddressTypes/PairSbSocketReceiveFromTest.SunnyDay/1',
+      'SbSocketAddressTypes/SbSocketResolveTest.Localhost/1',
+      'SbSocketAddressTypes/SbSocketResolveTest.SunnyDayFiltered/1',
+      'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToClosedSocket/1',
+      'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToSocketUntilBlocking/1',
+      'SbSocketAddressTypes/PairSbSocketSendToTest.RainyDaySendToSocketConnectionReset/1',
+      'SbSocketAddressTypes/PairSbSocketWaiterWaitTest.SunnyDay/1',
+      'SbSocketAddressTypes/PairSbSocketWaiterWaitTest.SunnyDayAlreadyReady/1',
+      'SbSocketAddressTypes/PairSbSocketWaiterWaitTimedTest.SunnyDay/1',
+      'SbSocketAddressTypes/PairSbSocketWrapperTest.SunnyDay/1',
+  ])
+
+  # pylint: enable=line-too-long
+
+
+def CreateTestFilters():
+  return TestFilters()
diff --git a/starboard/linux/x64x11/args.gn b/starboard/linux/x64x11/args.gn
new file mode 100644
index 0000000..697d6f2
--- /dev/null
+++ b/starboard/linux/x64x11/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "linux-x64x11"
+target_os = "linux"
+target_cpu = "x64"
diff --git a/starboard/linux/x64x11/clang/3.9/test_filters.py b/starboard/linux/x64x11/clang/3.9/test_filters.py
new file mode 100644
index 0000000..256866d
--- /dev/null
+++ b/starboard/linux/x64x11/clang/3.9/test_filters.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 Clang 3.9 Platform Test Filters."""
+
+from starboard.linux.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return LinuxX64X11Clang39TestFilters()
+
+
+class LinuxX64X11Clang39TestFilters(shared_test_filters.TestFilters):
+  """Starboard Linux X64 X11 Clang 3.9 Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(LinuxX64X11Clang39TestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/linux/x64x11/egl/args.gn b/starboard/linux/x64x11/egl/args.gn
new file mode 100644
index 0000000..6bf2d33
--- /dev/null
+++ b/starboard/linux/x64x11/egl/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "linux-x64x11-egl"
+target_os = "linux"
+target_cpu = "x64"
diff --git a/starboard/linux/x64x11/egl/test_filters.py b/starboard/linux/x64x11/egl/test_filters.py
new file mode 100644
index 0000000..825987e
--- /dev/null
+++ b/starboard/linux/x64x11/egl/test_filters.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 EGL Platform Test Filters."""
+
+from starboard.linux.x64x11 import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return LinuxX64X11EglTestFilters()
+
+
+class LinuxX64X11EglTestFilters(shared_test_filters.LinuxX64X11TestFilters):
+  """Starboard Linux X64 X11 EGL Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(LinuxX64X11EglTestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/linux/x64x11/egl/toolchain/BUILD.gn b/starboard/linux/x64x11/egl/toolchain/BUILD.gn
index 1ab2e67..c1469cb 100644
--- a/starboard/linux/x64x11/egl/toolchain/BUILD.gn
+++ b/starboard/linux/x64x11/egl/toolchain/BUILD.gn
@@ -12,13 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
-
 clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -27,5 +25,5 @@
 }
 
 clang_toolchain("target") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 }
diff --git a/starboard/linux/x64x11/gcc/6.3/test_filters.py b/starboard/linux/x64x11/gcc/6.3/test_filters.py
new file mode 100644
index 0000000..5f24171
--- /dev/null
+++ b/starboard/linux/x64x11/gcc/6.3/test_filters.py
@@ -0,0 +1,36 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 gcc 6.3 Platform Test Filters."""
+
+from starboard.linux.shared import test_filters as shared_test_filters
+from starboard.tools.testing import test_filter
+
+
+def CreateTestFilters():
+  return LinuxX64X11Gcc63TestFilters()
+
+
+class LinuxX64X11Gcc63TestFilters(shared_test_filters.TestFilters):
+  """Starboard Linux X64 X11 gcc 6.3 Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(LinuxX64X11Gcc63TestFilters, self).GetTestFilters()
+    for target, tests in self._FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  _FILTERED_TESTS = {
+      # TODO(b/206117361): Re-enable starboard_platform_tests once fixed.
+      'starboard_platform_tests': ['JobQueueTest.JobsAreMovedAndNotCopied',],
+  }
diff --git a/starboard/linux/x64x11/gcc/6.3/toolchain/BUILD.gn b/starboard/linux/x64x11/gcc/6.3/toolchain/BUILD.gn
new file mode 100644
index 0000000..52826ea
--- /dev/null
+++ b/starboard/linux/x64x11/gcc/6.3/toolchain/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/shared/toolchain/overridable_gcc_toolchain.gni")
+
+# Directory for GCC 6.3 if it is installed as a system dependency.
+_default_gcc_6_3_bin_dir = "/usr/bin"
+
+overridable_gcc_toolchain("host") {
+  cc = "${_default_gcc_6_3_bin_dir}/gcc-6"
+  cxx = "${_default_gcc_6_3_bin_dir}/g++-6"
+  ld = cxx
+
+  # We use whatever 'ar' resolves to.
+  ar = "ar"
+
+  toolchain_args = {
+    target_os = "linux"
+    target_cpu = "x64"
+    is_clang = false
+  }
+}
+
+overridable_gcc_toolchain("target") {
+  cc = "${_default_gcc_6_3_bin_dir}/gcc-6"
+  cxx = "${_default_gcc_6_3_bin_dir}/g++-6"
+  ld = cxx
+
+  # We use whatever 'ar' resolves to.
+  ar = "ar"
+
+  toolchain_args = {
+    is_clang = false
+  }
+}
diff --git a/starboard/linux/x64x11/main.cc b/starboard/linux/x64x11/main.cc
index 0c7dcfb..cf076af 100644
--- a/starboard/linux/x64x11/main.cc
+++ b/starboard/linux/x64x11/main.cc
@@ -16,6 +16,7 @@
 
 #include "starboard/configuration.h"
 #include "starboard/shared/signal/crash_signals.h"
+#include "starboard/shared/signal/debug_signals.h"
 #include "starboard/shared/signal/suspend_signals.h"
 #include "starboard/shared/starboard/link_receiver.h"
 #include "starboard/shared/x11/application_x11.h"
@@ -25,6 +26,7 @@
 extern "C" SB_EXPORT_PLATFORM int main(int argc, char** argv) {
   tzset();
   starboard::shared::signal::InstallCrashSignalHandlers();
+  starboard::shared::signal::InstallDebugSignalHandlers();
   starboard::shared::signal::InstallSuspendSignalHandlers();
 
 #if SB_IS(EVERGREEN_COMPATIBLE)
@@ -44,6 +46,7 @@
     result = application.Run(argc, argv);
   }
   starboard::shared::signal::UninstallSuspendSignalHandlers();
+  starboard::shared::signal::UninstallDebugSignalHandlers();
   starboard::shared::signal::UninstallCrashSignalHandlers();
   return result;
 }
diff --git a/starboard/linux/x64x11/sbversion/11/test_filters.py b/starboard/linux/x64x11/sbversion/11/test_filters.py
new file mode 100644
index 0000000..c6f1e2d
--- /dev/null
+++ b/starboard/linux/x64x11/sbversion/11/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 Platform Test Filters."""
+
+from starboard.linux.x64x11 import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.LinuxX64X11TestFilters()
diff --git a/starboard/linux/x64x11/sbversion/12/test_filters.py b/starboard/linux/x64x11/sbversion/12/test_filters.py
new file mode 100644
index 0000000..c6f1e2d
--- /dev/null
+++ b/starboard/linux/x64x11/sbversion/12/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 Platform Test Filters."""
+
+from starboard.linux.x64x11 import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.LinuxX64X11TestFilters()
diff --git a/starboard/linux/x64x11/sbversion/13/test_filters.py b/starboard/linux/x64x11/sbversion/13/test_filters.py
new file mode 100644
index 0000000..c6f1e2d
--- /dev/null
+++ b/starboard/linux/x64x11/sbversion/13/test_filters.py
@@ -0,0 +1,20 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 Platform Test Filters."""
+
+from starboard.linux.x64x11 import test_filters as parent_test_filters
+
+
+def CreateTestFilters():
+  return parent_test_filters.LinuxX64X11TestFilters()
diff --git a/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn b/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn
index 99595e2..d4c0954 100644
--- a/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn
+++ b/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn
@@ -18,6 +18,7 @@
       ":libraries",
       "//starboard/linux/shared/platform_configuration",
       "//starboard/linux/shared/platform_configuration:compiler_flags",
+      ":linker_flags",
     ]
   } else {
     cflags = [ "-O2" ]
@@ -31,3 +32,12 @@
     "//third_party/libvpx/platforms/linux-x64/libvpx.a",
   ]
 }
+
+config("linker_flags") {
+  ldflags = [
+    "-Wl,-rpath=\$ORIGIN/lib",
+
+    # Cleanup unused sections
+    "-Wl,-gc-sections",
+  ]
+}
diff --git a/starboard/linux/x64x11/skia/args.gn b/starboard/linux/x64x11/skia/args.gn
new file mode 100644
index 0000000..68d39a3
--- /dev/null
+++ b/starboard/linux/x64x11/skia/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "linux-x64x11-skia"
+target_os = "linux"
+target_cpu = "x64"
diff --git a/starboard/linux/x64x11/skia/test_filters.py b/starboard/linux/x64x11/skia/test_filters.py
new file mode 100644
index 0000000..b34aca5
--- /dev/null
+++ b/starboard/linux/x64x11/skia/test_filters.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 Skia Platform Test Filters."""
+
+from starboard.linux.x64x11 import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return LinuxX64X11SkiaTestFilters()
+
+
+class LinuxX64X11SkiaTestFilters(shared_test_filters.LinuxX64X11TestFilters):
+  """Starboard Linux X64 X11 Skia Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(LinuxX64X11SkiaTestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/linux/x64x11/skia/toolchain/BUILD.gn b/starboard/linux/x64x11/skia/toolchain/BUILD.gn
index 1ab2e67..c1469cb 100644
--- a/starboard/linux/x64x11/skia/toolchain/BUILD.gn
+++ b/starboard/linux/x64x11/skia/toolchain/BUILD.gn
@@ -12,13 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
-
 clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -27,5 +25,5 @@
 }
 
 clang_toolchain("target") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 }
diff --git a/starboard/linux/x64x11/test_filters.py b/starboard/linux/x64x11/test_filters.py
new file mode 100644
index 0000000..60e6c1c
--- /dev/null
+++ b/starboard/linux/x64x11/test_filters.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Linux X64 X11 Platform Test Filters."""
+
+from starboard.linux.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return LinuxX64X11TestFilters()
+
+
+class LinuxX64X11TestFilters(shared_test_filters.TestFilters):
+  """Starboard Linux X64 X11 Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(LinuxX64X11TestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/linux/x64x11/toolchain/BUILD.gn b/starboard/linux/x64x11/toolchain/BUILD.gn
index 1ab2e67..9e483e1 100644
--- a/starboard/linux/x64x11/toolchain/BUILD.gn
+++ b/starboard/linux/x64x11/toolchain/BUILD.gn
@@ -12,13 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//build/toolchain/gcc_toolchain.gni")
+import("//build/config/clang/clang.gni")
+import("//starboard/shared/toolchain/overridable_gcc_toolchain.gni")
 
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
-
-clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+overridable_clang_toolchain("host") {
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -26,6 +24,6 @@
   }
 }
 
-clang_toolchain("target") {
-  clang_base_path = _clang_base_path
+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 9ce5395..3d11846 100644
--- a/starboard/loader_app/BUILD.gn
+++ b/starboard/loader_app/BUILD.gn
@@ -26,8 +26,15 @@
     ":installation_manager",
     ":slot_management",
     "//starboard",
+    "//starboard/elf_loader:evergreen_info",
     "//starboard/elf_loader:sabi_string",
   ]
+
+  if (sb_is_evergreen_compatible) {
+    public_deps += [ "//third_party/crashpad/wrapper" ]
+  } else {
+    public_deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
+  }
 }
 
 target(final_executable_type, "loader_app") {
@@ -43,23 +50,25 @@
   }
 }
 
-target(final_executable_type, "loader_app_sys") {
-  if (target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
-      target_cpu == "arm64") {
-    sources = _common_loader_app_sources
+if (sb_is_evergreen_compatible) {
+  target(final_executable_type, "loader_app_sys") {
+    if (target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
+        target_cpu == "arm64") {
+      sources = _common_loader_app_sources
 
-    starboard_syms_path =
-        rebase_path("//starboard/starboard.syms", root_build_dir)
-    ldflags = [
-      "-Wl,--dynamic-list=$starboard_syms_path",
-      "-ldl",
-    ]
-    deps = [
-      ":common_loader_app_dependencies",
-      "//cobalt/content/fonts:copy_font_data",
-      "//starboard/elf_loader:elf_loader_sys",
-    ]
-    deps += cobalt_platform_dependencies
+      starboard_syms_path =
+          rebase_path("//starboard/starboard.syms", root_build_dir)
+      ldflags = [
+        "-Wl,--dynamic-list=$starboard_syms_path",
+        "-ldl",
+      ]
+      deps = [
+        ":common_loader_app_dependencies",
+        "//cobalt/content/fonts:copy_font_data",
+        "//starboard/elf_loader:elf_loader_sys",
+      ]
+      deps += cobalt_platform_dependencies
+    }
   }
 }
 
@@ -82,6 +91,7 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
 
 static_library("app_key") {
@@ -108,6 +118,7 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
 
 static_library("drain_file") {
@@ -131,6 +142,7 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
 
 static_library("installation_store_proto") {
@@ -157,7 +169,7 @@
     "//starboard",
   ]
 
-  if (sb_evergreen_compatible_enable_lite) {
+  if (!sb_evergreen_compatible_enable_lite) {
     deps += [ ":pending_restart" ]
   }
 }
@@ -177,6 +189,7 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
+  content_deps = [ "//third_party/icu:icudata" ]
 }
 
 static_library("slot_management") {
@@ -192,6 +205,12 @@
     "//starboard/elf_loader",
     "//starboard/elf_loader:sabi_string",
   ]
+
+  if (sb_is_evergreen_compatible) {
+    deps += [ "//third_party/crashpad/wrapper" ]
+  } else {
+    deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
+  }
 }
 
 target(gtest_target_type, "slot_management_test") {
diff --git a/starboard/loader_app/installation_manager.cc b/starboard/loader_app/installation_manager.cc
index c004621..bb9601f 100644
--- a/starboard/loader_app/installation_manager.cc
+++ b/starboard/loader_app/installation_manager.cc
@@ -28,7 +28,7 @@
 #include "starboard/file.h"
 #include "starboard/loader_app/installation_store.pb.h"
 #if !SB_IS(EVERGREEN_COMPATIBLE_LITE)
-#include "starboard/loader_app/pending_restart.h"
+#include "starboard/loader_app/pending_restart.h"  // nogncheck
 #endif  // !SB_IS(EVERGREEN_COMPATIBLE_LITE)
 #include "starboard/once.h"
 #include "starboard/string.h"
diff --git a/starboard/nplb/BUILD.gn b/starboard/nplb/BUILD.gn
index 6ca3e60..a8c6697 100644
--- a/starboard/nplb/BUILD.gn
+++ b/starboard/nplb/BUILD.gn
@@ -270,8 +270,7 @@
     ]
   }
 
-  deps = [ "//starboard/nplb/testdata/file_tests:nplb_file_tests_data" ]
-  deps += cobalt_platform_dependencies
+  deps = cobalt_platform_dependencies
 
   if (is_internal_build) {
     deps += [ "//starboard/private/nplb:nplb_private" ]
@@ -281,11 +280,21 @@
     "//starboard",
     "//starboard/common",
     "//starboard/shared/starboard/media:media_util",
-    "//starboard/shared/starboard/player:player_download_test_data",
     "//starboard/shared/starboard/player:video_dmp",
     "//testing/gmock",
   ]
 
+  data_deps = [
+    "//starboard/nplb/testdata/file_tests:nplb_file_tests_data",
+    "//starboard/shared/starboard/player:player_download_test_data",
+  ]
+
+  content_deps = [
+    "//starboard/nplb/testdata/file_tests:nplb_file_tests_data",
+    "//starboard/shared/starboard/player:player_download_test_data",
+    "//third_party/icu:icudata",
+  ]
+
   if (gl_type != "none") {
     public_deps += [ "//starboard/egl_and_gles" ]
   }
diff --git a/starboard/nplb/media_can_play_mime_and_key_system_test.cc b/starboard/nplb/media_can_play_mime_and_key_system_test.cc
index 4c94240..81f85e2 100644
--- a/starboard/nplb/media_can_play_mime_and_key_system_test.cc
+++ b/starboard/nplb/media_can_play_mime_and_key_system_test.cc
@@ -12,11 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h"
+#include <map>
 
 #include "starboard/common/string.h"
 #include "starboard/media.h"
 #include "starboard/nplb/drm_helpers.h"
+#include "starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h"
 #include "starboard/nplb/performance_helpers.h"
 #include "starboard/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -291,280 +292,190 @@
   }
 }
 
-// TODO: Create an abstraction to shorten the length of this test.
 TEST(SbMediaCanPlayMimeAndKeySystem, PrintMaximumSupport) {
-  // AVC
-  std::string avc_resolution = "Unsupported";
-  // 1080p
-  SbMediaSupportType result = SbMediaCanPlayMimeAndKeySystem(
-      "video/mp4; codecs=\"avc1.4d402a\"; width=1920; height=1080; "
-      "framerate=30",
-      "");
-  if (result == kSbMediaSupportTypeProbably) {
-    avc_resolution = "1080p";
-    // 2K
-    result = SbMediaCanPlayMimeAndKeySystem(
-        "video/mp4; codecs=\"avc1.64002a\"; width=2560; height=1440; "
-        "framerate=30",
-        "");
-    if (result == kSbMediaSupportTypeProbably) {
-      avc_resolution = "2K";
-      // 4K
-      result = SbMediaCanPlayMimeAndKeySystem(
-          "video/mp4; codecs=\"avc1.64002a\"; width=3840; height=2160; "
-          "framerate=30",
-          "");
-      if (result == kSbMediaSupportTypeProbably) {
-        avc_resolution = "4K";
+  const char* kResolution1080p = "1080p";
+  const char* kResolution2k = "2K";
+  const char* kResolution4k = "4K";
+  const char* kResolution8k = "8K";
+  auto get_max_video_codec_resolution =
+      [=](std::string video_type,
+          const std::map<const char*, std::string> codec_params,
+          std::string framerate) {
+        std::map<const char*, std::string> resolutions = {
+            {kResolution1080p, "width=1920; height=1080;"},
+            {kResolution2k, "width=2560; height=1440;"},
+            {kResolution4k, "width=3840; height=2160;"},
+            {kResolution8k, "width=7680; height=4320;"}};
+
+        std::string max_supported_resolution;
+        for (auto&& it : resolutions) {
+          if (codec_params.find(it.first) == codec_params.end()) {
+            break;
+          }
+          std::string content_type = video_type + "; codecs=\"" +
+                                     codec_params.at(it.first) + "\"; " +
+                                     it.second + " framerate=" + framerate;
+          if (SbMediaCanPlayMimeAndKeySystem(content_type.c_str(), "") !=
+              kSbMediaSupportTypeProbably) {
+            break;
+          }
+          max_supported_resolution = it.first;
+        }
+        return max_supported_resolution.empty() ? "Unsupported"
+                                                : max_supported_resolution;
+      };
+
+  auto get_drm_system_support = [](std::string key_system) {
+    std::map<const char*, std::string> content_types = {
+        {"video/mp4; codecs=\"avc1.4d402a\"", "AVC"},
+        {"video/webm; codecs=\"vp9\"", "VP9"},
+        {"video/mp4; codecs=\"av01.0.08M.08\"", "AV1"},
+        {"audio/mp4; codecs=\"mp4a.40.2\"", "AAC"},
+        {"audio/webm; codecs=\"opus\"", "Opus"},
+        {"audio/mp4; codecs=\"ac-3\"", "AC-3"},
+        {"audio/mp4; codecs=\"ec-3\"", "E-AC-3"}};
+    std::string supported_codecs;
+    for (auto&& it : content_types) {
+      if (SbMediaCanPlayMimeAndKeySystem(it.first, key_system.c_str()) ==
+          kSbMediaSupportTypeProbably) {
+        supported_codecs += it.second + " ";
       }
     }
+    return supported_codecs.empty() ? "Unsupported" : supported_codecs;
+  };
+
+  std::string avc_support =
+      get_max_video_codec_resolution("video/mp4",
+                                     {{kResolution1080p, "avc1.4d402a"},
+                                      {kResolution2k, "avc1.64002a"},
+                                      {kResolution4k, "avc1.64002a"}},
+                                     "30");
+  if (avc_support != "Unsupported") {
+    avc_support += "\n\tAVC HFR: " + get_max_video_codec_resolution(
+                                         "video/mp4",
+                                         {{kResolution1080p, "avc1.4d402a"},
+                                          {kResolution2k, "avc1.64402a"},
+                                          {kResolution4k, "avc1.64402a"}},
+                                         "60");
   }
 
-  // AVC HFR
-  std::string avc_hfr_resolution = "Unsupported";
-  // 1080p
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "video/mp4; codecs=\"avc1.4d402a\"; width=1920; height=1080; "
-      "framerate=60",
-      "");
-  if (result == kSbMediaSupportTypeProbably) {
-    avc_hfr_resolution = "1080p";
-    // 2K
-    result = SbMediaCanPlayMimeAndKeySystem(
-        "video/mp4; codecs=\"avc1.64402a\"; width=2560; height=1440; "
-        "framerate=60",
-        "");
-    if (result == kSbMediaSupportTypeProbably) {
-      avc_hfr_resolution = "2K";
-      // 4K
-      result = SbMediaCanPlayMimeAndKeySystem(
-          "video/mp4; codecs=\"avc1.64402a\"; width=3840; height=2160; "
-          "framerate=60",
-          "");
-      if (result == kSbMediaSupportTypeProbably) {
-        avc_hfr_resolution = "4K";
-      }
+  std::string vp9_support =
+      get_max_video_codec_resolution("video/webm",
+                                     {{kResolution1080p, "vp9"},
+                                      {kResolution2k, "vp9"},
+                                      {kResolution4k, "vp9"}},
+                                     "30");
+  if (vp9_support != "Unsupported") {
+    vp9_support += "\n\tVP9 HFR: " +
+                   get_max_video_codec_resolution("video/webm",
+                                                  {{kResolution1080p, "vp9"},
+                                                   {kResolution2k, "vp9"},
+                                                   {kResolution4k, "vp9"}},
+                                                  "60");
+    std::string vp9_hdr_support = get_max_video_codec_resolution(
+        "video/webm",
+        {{kResolution1080p, "vp09.02.41.10.01.09.16.09.00"},
+         {kResolution2k, "vp09.02.51.10.01.09.16.09.00"},
+         {kResolution4k, "vp09.02.51.10.01.09.16.09.00"}},
+        "30");
+    if (vp9_hdr_support != "Unsupported") {
+      vp9_hdr_support +=
+          "\n\tVP9 HDR HFR: " +
+          get_max_video_codec_resolution(
+              "video/webm",
+              {{kResolution1080p, "vp09.02.41.10.01.09.16.09.00"},
+               {kResolution2k, "vp09.02.51.10.01.09.16.09.00"},
+               {kResolution4k, "vp09.02.51.10.01.09.16.09.00"}},
+              "60");
     }
+    vp9_support += "\n\tVP9 HDR: " + vp9_hdr_support;
   }
-
-  // VP9
-  std::string vp9_resolution = "Unsupported";
-  // 1080p
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "video/webm; codecs=\"vp9\"; width=1920; height=1080; framerate=30", "");
-  if (result == kSbMediaSupportTypeProbably) {
-    vp9_resolution = "1080p";
-    // 2K
-    result = SbMediaCanPlayMimeAndKeySystem(
-        "video/webm; codecs=\"vp9\"; width=2560; height=1440; framerate=30",
-        "");
-    if (result == kSbMediaSupportTypeProbably) {
-      vp9_resolution = "2K";
-      // 4K
-      result = SbMediaCanPlayMimeAndKeySystem(
-          "video/webm; codecs=\"vp9\"; width=3840; height=2160; framerate=30",
-          "");
-      if (result == kSbMediaSupportTypeProbably) {
-        vp9_resolution = "4K";
-      }
+  std::string av1_support =
+      get_max_video_codec_resolution("video/mp4",
+                                     {{kResolution1080p, "av01.0.08M.08"},
+                                      {kResolution2k, "av01.0.12M.08"},
+                                      {kResolution4k, "av01.0.12M.08"},
+                                      {kResolution8k, "av01.0.16M.08"}},
+                                     "30");
+  if (av1_support != "Unsupported") {
+    av1_support += "\n\tAV1 HFR: " + get_max_video_codec_resolution(
+                                         "video/mp4",
+                                         {{kResolution1080p, "av01.0.09M.08"},
+                                          {kResolution2k, "av01.0.12M.08"},
+                                          {kResolution4k, "av01.0.13M.08"},
+                                          {kResolution8k, "av01.0.17M.08"}},
+                                         "60");
+    std::string av1_hdr_support = get_max_video_codec_resolution(
+        "video/mp4",
+        {{kResolution1080p, "av01.0.09M.10.0.110.09.16.09.0"},
+         {kResolution2k, "av01.0.12M.10.0.110.09.16.09.0"},
+         {kResolution4k, "av01.0.13M.10.0.110.09.16.09.0"},
+         {kResolution8k, "av01.0.17M.10.0.110.09.16.09.0"}},
+        "30");
+    if (av1_hdr_support != "Unsupported") {
+      av1_hdr_support +=
+          "\n\tAV1 HDR HFR: " +
+          get_max_video_codec_resolution(
+              "video/mp4",
+              {{kResolution1080p, "av01.0.09M.10.0.110.09.16.09.0"},
+               {kResolution2k, "av01.0.12M.10.0.110.09.16.09.0"},
+               {kResolution4k, "av01.0.13M.10.0.110.09.16.09.0"},
+               {kResolution8k, "av01.0.17M.10.0.110.09.16.09.0"}},
+              "60");
     }
+    av1_support += "\n\tAV1 HDR: " + av1_hdr_support;
   }
 
-  // VP9 HFR
-  std::string vp9_hfr_resolution = "Unsupported";
-  // 1080p
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "video/webm; codecs=\"vp9\"; width=1920; height=1080; framerate=60", "");
-  if (result == kSbMediaSupportTypeProbably) {
-    vp9_hfr_resolution = "1080p";
-    // 2K
-    result = SbMediaCanPlayMimeAndKeySystem(
-        "video/webm; codecs=\"vp9\"; width=2560; height=1440; framerate=60",
-        "");
-    if (result == kSbMediaSupportTypeProbably) {
-      vp9_hfr_resolution = "2K";
-      // 4K
-      result = SbMediaCanPlayMimeAndKeySystem(
-          "video/webm; codecs=\"vp9\"; width=3840; height=2160; framerate=60",
-          "");
-      if (result == kSbMediaSupportTypeProbably) {
-        vp9_hfr_resolution = "4K";
-      }
-    }
-  }
+  std::string aac_support = SbMediaCanPlayMimeAndKeySystem(
+                                "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
+                                "") == kSbMediaSupportTypeProbably
+                                ? "Supported"
+                                : "Unsupported";
 
-  // VP9 HDR
-  std::string vp9_hdr_resolution = "Unsupported";
-  // 1080p
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=1920; "
-      "height=1080; framerate=30",
-      "");
-  if (result == kSbMediaSupportTypeProbably) {
-    vp9_hdr_resolution = "1080p";
-    // 2K
-    result = SbMediaCanPlayMimeAndKeySystem(
-        "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=2560; "
-        "height=1440; framerate=30",
-        "");
-    if (result == kSbMediaSupportTypeProbably) {
-      vp9_hdr_resolution = "2K";
-      // 4K
-      result = SbMediaCanPlayMimeAndKeySystem(
-          "video/webm; codecs=\"vp09.02.51.10.01.09.16.09.00\"; width=3840; "
-          "height=2160; framerate=30",
-          "");
-      if (result == kSbMediaSupportTypeProbably) {
-        vp9_hdr_resolution = "4K";
-      }
-    }
-  }
+  std::string aac51_support = SbMediaCanPlayMimeAndKeySystem(
+                                  "audio/mp4; codecs=\"mp4a.40.2\"; channels=6",
+                                  "") == kSbMediaSupportTypeProbably
+                                  ? "Supported"
+                                  : "Unsupported";
 
-  // AV1
-  std::string av1_resolution = "Unsupported";
-  // 1080p
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "video/mp4; codecs=\"av01.0.09M.08\"; width=1920; "
-      "height=1080; framerate=30",
-      "");
-  if (result == kSbMediaSupportTypeProbably) {
-    av1_resolution = "1080p";
-    // 2K
-    result = SbMediaCanPlayMimeAndKeySystem(
-        "video/mp4; codecs=\"av01.0.12M.08\"; width=2560; "
-        "height=1440; framerate=30",
-        "");
-    if (result == kSbMediaSupportTypeProbably) {
-      av1_resolution = "2K";
-      // 4K
-      result = SbMediaCanPlayMimeAndKeySystem(
-          "video/mp4; codecs=\"av01.0.12M.08\"; width=3840; "
-          "height=2160; framerate=30",
-          "");
-      if (result == kSbMediaSupportTypeProbably) {
-        av1_resolution = "4K";
-      }
-    }
-  }
-  // AV1 HFR
-  std::string av1_hfr_resolution = "Unsupported";
-  // 1080p
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "video/mp4; codecs=\"av01.0.09M.08\"; width=1920; height=1080; "
-      "framerate=60",
-      "");
-  if (result == kSbMediaSupportTypeProbably) {
-    av1_hfr_resolution = "1080p";
-    // 2K
-    result = SbMediaCanPlayMimeAndKeySystem(
-        "video/mp4; codecs=\"av01.0.12M.08\"; width=2560; height=1440; "
-        "framerate=60",
-        "");
-    if (result == kSbMediaSupportTypeProbably) {
-      av1_hfr_resolution = "2K";
-      // 4K
-      result = SbMediaCanPlayMimeAndKeySystem(
-          "video/mp4; codecs=\"av01.0.13M.08\"; width=3840; height=2160; "
-          "framerate=60",
-          "");
-      if (result == kSbMediaSupportTypeProbably) {
-        av1_hfr_resolution = "4K";
-      }
-    }
-  }
+  std::string opus_support = SbMediaCanPlayMimeAndKeySystem(
+                                 "audio/webm; codecs=\"opus\"; "
+                                 "channels=2; bitrate=128000;",
+                                 "") == kSbMediaSupportTypeProbably
+                                 ? "Supported"
+                                 : "Unsupported";
 
-  // AV1 HDR
-  std::string av1_hdr_resolution = "Unsupported";
-  // 1080p
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "video/mp4; codecs=\"av01.0.09M.10.0.110.09.16.09.0\"; width=1920; "
-      "height=1080",
-      "");
-  if (result == kSbMediaSupportTypeProbably) {
-    av1_hdr_resolution = "1080p";
-    // 2K
-    result = SbMediaCanPlayMimeAndKeySystem(
-        "video/mp4; codecs=\"av01.0.12M.10.0.110.09.16.09.0\"; width=2560; "
-        "height=1440",
-        "");
-    if (result == kSbMediaSupportTypeProbably) {
-      av1_hdr_resolution = "2K";
-      // 4K
-      result = SbMediaCanPlayMimeAndKeySystem(
-          "video/mp4; codecs=\"av01.0.13M.10.0.110.09.16.09.0\"; width=3840; "
-          "height=2160",
-          "");
-      if (result == kSbMediaSupportTypeProbably) {
-        av1_hdr_resolution = "4K";
-      }
-    }
-  }
+  std::string opus51_support = SbMediaCanPlayMimeAndKeySystem(
+                                   "audio/webm; codecs=\"opus\"; "
+                                   "channels=6; bitrate=576000;",
+                                   "") == kSbMediaSupportTypeProbably
+                                   ? "Supported"
+                                   : "Unsupported";
 
-  // AAC
-  std::string aac_support = "Unsupported";
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "audio/mp4; codecs=\"mp4a.40.2\"; channels=2", "");
-  if (result == kSbMediaSupportTypeProbably) {
-    aac_support = "Supported";
-  }
+  std::string ac3_support =
+      SbMediaCanPlayMimeAndKeySystem(
+          "audio/mp4; codecs=\"ac-3\"; channels=6; bitrate=512000", "") ==
+              kSbMediaSupportTypeProbably
+          ? "Supported"
+          : "Unsupported";
 
-  // AAC51
-  std::string aac51_support = "Unsupported";
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "audio/mp4; codecs=\"mp4a.40.2\"; channels=6", "");
-  if (result == kSbMediaSupportTypeProbably) {
-    aac51_support = "Supported";
-  }
+  std::string eac3_support =
+      SbMediaCanPlayMimeAndKeySystem(
+          "audio/mp4; codecs=\"ec-3\"; channels=6; bitrate=512000", "") ==
+              kSbMediaSupportTypeProbably
+          ? "Supported"
+          : "Unsupported";
 
-  // Opus
-  std::string opus_support = "Unsupported";
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "audio/webm; codecs=\"opus\"; "
-      "channels=2; bitrate=128000;",
-      "");
-  if (result == kSbMediaSupportTypeProbably) {
-    opus_support = "Supported";
-  }
-
-  // Opus51
-  std::string opus51_support = "Unsupported";
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "audio/webm; codecs=\"opus\"; "
-      "channels=6; bitrate=576000;",
-      "");
-  if (result == kSbMediaSupportTypeProbably) {
-    opus51_support = "Supported";
-  }
-
-  // AC-3
-  std::string ac3_support = "Unsupported";
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "audio/mp4; codecs=\"ac-3\"; channels=6; bitrate=512000", "");
-  if (result == kSbMediaSupportTypeProbably) {
-    ac3_support = "Supported";
-  }
-
-  // E-AC-3
-  std::string eac3_support = "Unsupported";
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "audio/mp4; codecs=\"ec-3\"; channels=6; bitrate=512000", "");
-  if (result == kSbMediaSupportTypeProbably) {
-    eac3_support = "Supported";
-  }
-
-  SB_LOG(INFO) << "\nVideo Codec Capability:\n\tAVC: " << avc_resolution
-               << "\n\tAVC HFR: " << avc_hfr_resolution
-               << "\n\tVP9: " << vp9_resolution
-               << "\n\tVP9 HFR: " << vp9_hfr_resolution
-               << "\n\tVP9 HDR: " << vp9_hdr_resolution
-               << "\n\tAV1: " << av1_resolution
-               << "\n\tAV1 HFR:" << av1_hfr_resolution
-               << "\n\tAV1 HDR:" << av1_hdr_resolution
+  SB_LOG(INFO) << "\nVideo Codec Capability:\n\tAVC: " << avc_support
+               << "\n\tVP9: " << vp9_support << "\n\tAV1: " << av1_support
                << "\n\nAudio Codec Capability:\n\tAAC: " << aac_support
                << "\n\tAAC 51: " << aac51_support
                << "\n\tOpus: " << opus_support
                << "\n\tOpus 51: " << opus51_support
-               << "\n\tAC-3: " << ac3_support << "\n\tE-AC-3: " << eac3_support;
+               << "\n\tAC-3: " << ac3_support << "\n\tE-AC-3: " << eac3_support
+               << "\n\nDRM Capability:\n\tWidevine: "
+               << get_drm_system_support("com.widevine") << "\n\tPlayReady: "
+               << get_drm_system_support("com.youtube.playready");
 }
 
 // TODO: Create an abstraction to shorten the length of this test.
@@ -818,6 +729,9 @@
   test_sequential_function_calls(kHdrQueryParams,
                                  SB_ARRAY_SIZE_INT(kHdrQueryParams),
                                  20 * kSbTimeMillisecond, "HDR queries");
+  test_sequential_function_calls(kDrmQueryParams,
+                                 SB_ARRAY_SIZE_INT(kDrmQueryParams),
+                                 50 * kSbTimeMillisecond, "DRM queries");
 }
 
 }  // namespace
diff --git a/starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h b/starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h
index a48201a..544961b 100644
--- a/starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h
+++ b/starboard/nplb/media_can_play_mime_and_key_system_test_helpers.h
@@ -205,6 +205,158 @@
     "audio/webm; codecs=\"opus\"; channels=2",
     "audio/webm; codecs=\"opus\"; channels=2"};
 
+// Query params from https://youtu.be/1mSzHxMpji0.
+static const char* kDrmQueryParams[] = {
+    "video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
+    "bitrate=281854",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
+    "bitrate=637760",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+    "bitrate=1164612",
+    "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
+    "bitrate=4362827",
+    "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
+    "video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
+    "bitrate=138907",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+    "bitrate=1746306",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+    "bitrate=3473564",
+    "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+    "bitrate=3481130",
+    "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+    "bitrate=5789806",
+    "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
+    "bitrate=5856175",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+    "height=480; framerate=24; bitrate=2629046; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+    "height=720; framerate=24; bitrate=1328071; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+    "height=1080; framerate=24; bitrate=2375894; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
+    "height=240; framerate=24; bitrate=229634; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
+    "height=360; framerate=24; bitrate=324585; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+    "height=480; framerate=24; bitrate=639196; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+    "height=480; framerate=24; bitrate=1055128; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "audio/mp4; codecs=\"ec-3\"; channels=6",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+    "height=720; framerate=24; bitrate=2111149; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+    "height=720; framerate=24; bitrate=3709033; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+    "height=1080; framerate=24; bitrate=3679792; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+    "height=1080; framerate=24; bitrate=5524689; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "audio/mp4; codecs=\"ac-3\"; channels=6",
+    "video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
+    "bitrate=281854",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
+    "bitrate=637760",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+    "bitrate=1164612",
+    "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
+    "bitrate=4362827",
+    "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
+    "video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
+    "bitrate=138907",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+    "bitrate=1746306",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+    "bitrate=3473564",
+    "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+    "bitrate=3481130",
+    "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+    "bitrate=5789806",
+    "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
+    "bitrate=5856175",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+    "height=480; framerate=24; bitrate=2629046; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+    "height=720; framerate=24; bitrate=1328071; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+    "height=1080; framerate=24; bitrate=2375894; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
+    "height=240; framerate=24; bitrate=229634; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
+    "height=360; framerate=24; bitrate=324585; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+    "height=480; framerate=24; bitrate=639196; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+    "height=480; framerate=24; bitrate=1055128; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "audio/mp4; codecs=\"ec-3\"; channels=6",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+    "height=720; framerate=24; bitrate=2111149; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+    "height=720; framerate=24; bitrate=3709033; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+    "height=1080; framerate=24; bitrate=3679792; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+    "height=1080; framerate=24; bitrate=5524689; eotf=bt709; "
+    "cryptoblockformat=subsample",
+    "audio/mp4; codecs=\"ac-3\"; channels=6",
+    "video/mp4; codecs=\"avc1.4d4015\"; width=426; height=240; framerate=24; "
+    "bitrate=149590",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=640; height=360; framerate=24; "
+    "bitrate=261202",
+    "video/mp4; codecs=\"avc1.4d401e\"; width=854; height=480; framerate=24; "
+    "bitrate=368187",
+    "video/mp4; codecs=\"avc1.4d401f\"; width=1280; height=720; framerate=24; "
+    "bitrate=676316",
+    "video/mp4; codecs=\"avc1.640028\"; width=1920; height=1080; framerate=24; "
+    "bitrate=2691722",
+    "audio/mp4; codecs=\"mp4a.40.2\"; channels=2",
+    "video/mp4; codecs=\"avc1.4d400c\"; width=256; height=144; framerate=24; "
+    "bitrate=84646",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=426; "
+    "height=240; framerate=24; bitrate=192698; eotf=bt709",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=640; "
+    "height=360; framerate=24; bitrate=342403; eotf=bt709",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=854; "
+    "height=480; framerate=24; bitrate=514976; eotf=bt709",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1280; "
+    "height=720; framerate=24; bitrate=852689; eotf=bt709",
+    "video/webm; codecs=\"vp09.00.51.08.01.01.01.01.00\"; width=1920; "
+    "height=1080; framerate=24; bitrate=2389269; eotf=bt709",
+    "audio/webm; codecs=\"opus\"; channels=2",
+    "audio/webm; codecs=\"opus\"; channels=2",
+    "video/mp4; codecs=\"av01.0.00M.08\"; width=256; height=144; framerate=24; "
+    "bitrate=74957; eotf=bt709",
+    "video/mp4; codecs=\"av01.0.00M.08\"; width=426; height=240; framerate=24; "
+    "bitrate=148691; eotf=bt709",
+    "video/mp4; codecs=\"av01.0.01M.08\"; width=640; height=360; framerate=24; "
+    "bitrate=305616; eotf=bt709",
+    "video/mp4; codecs=\"av01.0.04M.08\"; width=854; height=480; framerate=24; "
+    "bitrate=577104; eotf=bt709",
+    "video/mp4; codecs=\"av01.0.05M.08\"; width=1280; height=720; "
+    "framerate=24; bitrate=989646; eotf=bt709",
+    "video/mp4; codecs=\"av01.0.08M.08\"; width=1920; height=1080; "
+    "framerate=24; bitrate=1766589; eotf=bt709"};
+
 }  // namespace nplb
 }  // namespace starboard
 
diff --git a/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn b/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
index 05753b8..705025b 100644
--- a/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
+++ b/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
@@ -36,4 +36,6 @@
     "//starboard",
     "//testing/gmock",
   ]
+
+  content_deps = [ "//third_party/icu:icudata" ]
 }
diff --git a/starboard/nplb/testdata/file_tests/BUILD.gn b/starboard/nplb/testdata/file_tests/BUILD.gn
index 1566e4f..bb96507 100644
--- a/starboard/nplb/testdata/file_tests/BUILD.gn
+++ b/starboard/nplb/testdata/file_tests/BUILD.gn
@@ -13,6 +13,8 @@
 # limitations under the License.
 
 copy("nplb_file_tests_data") {
+  install_content = true
+
   sources = [
     "dir_with_files/file11",
     "dir_with_files/file12",
diff --git a/starboard/raspi/2/args.gn b/starboard/raspi/2/args.gn
new file mode 100644
index 0000000..0b190f8
--- /dev/null
+++ b/starboard/raspi/2/args.gn
@@ -0,0 +1,18 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "raspi-2"
+target_os = "linux"
+target_cpu = "arm"
+is_clang = false
diff --git a/starboard/raspi/2/sbversion/12/test_filters.py b/starboard/raspi/2/sbversion/12/test_filters.py
new file mode 100644
index 0000000..d83e77e
--- /dev/null
+++ b/starboard/raspi/2/sbversion/12/test_filters.py
@@ -0,0 +1,23 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Raspberry Pi 2 Platform Test Filters."""
+
+import importlib
+
+# Dynamically imported to get around the number in the path.
+_PARENT_TEST_FILTERS = importlib.import_module('starboard.raspi.2.test_filters')
+
+
+def CreateTestFilters():
+  return _PARENT_TEST_FILTERS.Raspi2TestFilters()
diff --git a/starboard/raspi/2/sbversion/13/test_filters.py b/starboard/raspi/2/sbversion/13/test_filters.py
new file mode 100644
index 0000000..d83e77e
--- /dev/null
+++ b/starboard/raspi/2/sbversion/13/test_filters.py
@@ -0,0 +1,23 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Raspberry Pi 2 Platform Test Filters."""
+
+import importlib
+
+# Dynamically imported to get around the number in the path.
+_PARENT_TEST_FILTERS = importlib.import_module('starboard.raspi.2.test_filters')
+
+
+def CreateTestFilters():
+  return _PARENT_TEST_FILTERS.Raspi2TestFilters()
diff --git a/starboard/raspi/2/skia/args.gn b/starboard/raspi/2/skia/args.gn
new file mode 100644
index 0000000..c6c8d1d
--- /dev/null
+++ b/starboard/raspi/2/skia/args.gn
@@ -0,0 +1,18 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "raspi-2-skia"
+target_os = "linux"
+target_cpu = "arm"
+is_clang = false
diff --git a/starboard/raspi/2/skia/test_filters.py b/starboard/raspi/2/skia/test_filters.py
new file mode 100644
index 0000000..12e54d4
--- /dev/null
+++ b/starboard/raspi/2/skia/test_filters.py
@@ -0,0 +1,31 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Raspberry Pi 2 Skia Platform Test Filters."""
+
+import importlib
+
+# Dynamically imported to get around the number in the path.
+_PARENT_TEST_FILTERS = importlib.import_module('starboard.raspi.2.test_filters')
+
+
+def CreateTestFilters():
+  return Raspi2SkiaTestFilters()
+
+
+class Raspi2SkiaTestFilters(_PARENT_TEST_FILTERS.Raspi2TestFilters):
+  """Starboard Raspberry Pi 2 Skia Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(Raspi2SkiaTestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/raspi/2/skia/toolchain/BUILD.gn b/starboard/raspi/2/skia/toolchain/BUILD.gn
index 3b83f52..0d9a282 100644
--- a/starboard/raspi/2/skia/toolchain/BUILD.gn
+++ b/starboard/raspi/2/skia/toolchain/BUILD.gn
@@ -12,11 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
 
 clang_toolchain("host") {
-  clang_base_path = raspi_clang_base_path
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -25,11 +26,13 @@
 }
 
 gcc_toolchain("target") {
-  cc = "$gcc_toolchain_cc"
-  cxx = "$gcc_toolchain_cxx"
+  cc = gcc_toolchain_cc
+  cxx = gcc_toolchain_cxx
   ld = cxx
 
-  ar = "$gcc_toolchain_ar"
+  ar = gcc_toolchain_ar
+
+  tail_lib_dependencies = "-l:libpthread.so.0"
 
   toolchain_args = {
     is_clang = false
diff --git a/starboard/raspi/2/test_filters.py b/starboard/raspi/2/test_filters.py
new file mode 100644
index 0000000..1254919
--- /dev/null
+++ b/starboard/raspi/2/test_filters.py
@@ -0,0 +1,28 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Raspberry Pi 2 Platform Test Filters."""
+
+from starboard.raspi.shared import test_filters as shared_test_filters
+
+
+def CreateTestFilters():
+  return Raspi2TestFilters()
+
+
+class Raspi2TestFilters(shared_test_filters.TestFilters):
+  """Starboard Raspberry Pi 2 Platform Test Filters."""
+
+  def GetTestFilters(self):
+    filters = super(Raspi2TestFilters, self).GetTestFilters()
+    return filters
diff --git a/starboard/raspi/2/toolchain/BUILD.gn b/starboard/raspi/2/toolchain/BUILD.gn
index b83ad10..9056b43 100644
--- a/starboard/raspi/2/toolchain/BUILD.gn
+++ b/starboard/raspi/2/toolchain/BUILD.gn
@@ -16,7 +16,7 @@
 import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
 
 clang_toolchain("host") {
-  clang_base_path = raspi_clang_base_path
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -25,12 +25,14 @@
 }
 
 gcc_toolchain("target") {
-  cc = "$gcc_toolchain_cc"
-  cxx = "$gcc_toolchain_cxx"
+  cc = gcc_toolchain_cc
+  cxx = gcc_toolchain_cxx
   ld = cxx
 
-  # We use whatever 'ar' resolves to in gyp.
-  ar = "$gcc_toolchain_ar"
+  # We use whatever 'ar' resolves to.
+  ar = gcc_toolchain_ar
+
+  tail_lib_dependencies = "-l:libpthread.so.0"
 
   toolchain_args = {
     is_clang = false
diff --git a/starboard/raspi/shared/BUILD.gn b/starboard/raspi/shared/BUILD.gn
index d224eda..70d60ff 100644
--- a/starboard/raspi/shared/BUILD.gn
+++ b/starboard/raspi/shared/BUILD.gn
@@ -254,6 +254,8 @@
     "//starboard/shared/pthread/thread_yield.cc",
     "//starboard/shared/signal/crash_signals.cc",
     "//starboard/shared/signal/crash_signals.h",
+    "//starboard/shared/signal/debug_signals.cc",
+    "//starboard/shared/signal/debug_signals.h",
     "//starboard/shared/signal/suspend_signals.cc",
     "//starboard/shared/signal/suspend_signals.h",
     "//starboard/shared/signal/system_request_conceal.cc",
diff --git a/starboard/raspi/shared/install_target.gni b/starboard/raspi/shared/install_target.gni
new file mode 100644
index 0000000..4c42ce1
--- /dev/null
+++ b/starboard/raspi/shared/install_target.gni
@@ -0,0 +1,52 @@
+# Copyright 2021 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("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
+template("install_target") {
+  installable_target_name = invoker.installable_target_name
+
+  if (invoker.type == "executable") {
+    install_subdir = "bin"
+    source_name = installable_target_name
+  } else if (invoker.type == "shared_library") {
+    install_subdir = "lib"
+    source_name = "lib${installable_target_name}.so"
+  } else {
+    assert(false, "You can only install an executable or shared library.")
+  }
+
+  action(target_name) {
+    forward_variables_from(invoker, [ "testonly" ])
+
+    script = "//starboard/build/run_bash.py"
+
+    strip_executable = gcc_toolchain_strip
+    inputs = [
+      strip_executable,
+      "$root_out_dir/$source_name",
+    ]
+
+    deps = invoker.deps
+    deps += [ ":$installable_target_name" ]
+
+    outputs = [ "$sb_install_output_dir/$install_subdir/$source_name" ]
+
+    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),
+    ]
+  }
+}
diff --git a/starboard/raspi/shared/launcher.py b/starboard/raspi/shared/launcher.py
index 4617abc..bee7f90 100644
--- a/starboard/raspi/shared/launcher.py
+++ b/starboard/raspi/shared/launcher.py
@@ -107,7 +107,10 @@
   def _InitPexpectCommands(self):
     """Initializes all of the pexpect commands needed for running the test."""
 
-    test_dir = os.path.join(self.out_directory, 'deploy', self.target_name)
+    test_dir = os.path.join(self.out_directory, 'install', 'bin')
+    # TODO(b/216356058): Delete this conditional that's just for GYP.
+    if not os.path.isdir(test_dir):
+      test_dir = os.path.join(self.out_directory, 'deploy', self.target_name)
     test_file = self.target_name
 
     test_path = os.path.join(test_dir, test_file)
diff --git a/starboard/raspi/shared/main.cc b/starboard/raspi/shared/main.cc
index 41d52da..dfd20d5 100644
--- a/starboard/raspi/shared/main.cc
+++ b/starboard/raspi/shared/main.cc
@@ -12,19 +12,24 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <malloc.h>
 #include <time.h>
 
 #include "starboard/configuration.h"
 #include "starboard/raspi/shared/application_dispmanx.h"
 #include "starboard/shared/signal/crash_signals.h"
+#include "starboard/shared/signal/debug_signals.h"
 #include "starboard/shared/signal/suspend_signals.h"
 
 #include "third_party/crashpad/wrapper/wrapper.h"
 
 int main(int argc, char** argv) {
+  // Set M_ARENA_MAX to a low value to slow memory growth due to fragmentation.
+  SB_CHECK(mallopt(M_ARENA_MAX, 2));
   tzset();
 
   starboard::shared::signal::InstallCrashSignalHandlers();
+  starboard::shared::signal::InstallDebugSignalHandlers();
   starboard::shared::signal::InstallSuspendSignalHandlers();
 
 #if SB_IS(EVERGREEN_COMPATIBLE)
@@ -33,6 +38,7 @@
   starboard::raspi::shared::ApplicationDispmanx application;
   int result = application.Run(argc, argv);
   starboard::shared::signal::UninstallSuspendSignalHandlers();
+  starboard::shared::signal::UninstallDebugSignalHandlers();
   starboard::shared::signal::UninstallCrashSignalHandlers();
   return result;
 }
diff --git a/starboard/raspi/shared/platform_configuration/BUILD.gn b/starboard/raspi/shared/platform_configuration/BUILD.gn
index 988df3e..f3d2403 100644
--- a/starboard/raspi/shared/platform_configuration/BUILD.gn
+++ b/starboard/raspi/shared/platform_configuration/BUILD.gn
@@ -130,7 +130,6 @@
     "avcodec",
     "avformat",
     "avutil",
-    ":libpthread.so.0",
     "pthread",
     "rt",
     "openmaxil",
diff --git a/starboard/raspi/shared/platform_configuration/configuration.gni b/starboard/raspi/shared/platform_configuration/configuration.gni
index dfa14b8..b55c36f 100644
--- a/starboard/raspi/shared/platform_configuration/configuration.gni
+++ b/starboard/raspi/shared/platform_configuration/configuration.gni
@@ -16,7 +16,7 @@
 
 arm_float_abi = "hard"
 has_drm_support = false
-sb_pedantic_warnings = true
+
 sb_static_contents_output_data_dir = "$root_out_dir/content"
 
 no_pedantic_warnings_config_path =
@@ -26,3 +26,5 @@
 sabi_path = "//starboard/sabi/arm/hardfp/sabi-v$sb_api_version.json"
 
 platform_tests_path = "//starboard/raspi/shared:starboard_platform_tests"
+
+install_target_path = "//starboard/raspi/shared/install_target.gni"
diff --git a/starboard/raspi/shared/starboard_platform.gypi b/starboard/raspi/shared/starboard_platform.gypi
index 46c8a8f..eae5092 100644
--- a/starboard/raspi/shared/starboard_platform.gypi
+++ b/starboard/raspi/shared/starboard_platform.gypi
@@ -274,6 +274,8 @@
         '<(DEPTH)/starboard/shared/pthread/thread_yield.cc',
         '<(DEPTH)/starboard/shared/signal/crash_signals.cc',
         '<(DEPTH)/starboard/shared/signal/crash_signals.h',
+        '<(DEPTH)/starboard/shared/signal/debug_signals.cc',
+        '<(DEPTH)/starboard/shared/signal/debug_signals.h',
         '<(DEPTH)/starboard/shared/signal/suspend_signals.cc',
         '<(DEPTH)/starboard/shared/signal/suspend_signals.h',
         '<(DEPTH)/starboard/shared/signal/system_request_conceal.cc',
diff --git a/starboard/raspi/shared/test_filters.py b/starboard/raspi/shared/test_filters.py
new file mode 100644
index 0000000..aeaed61
--- /dev/null
+++ b/starboard/raspi/shared/test_filters.py
@@ -0,0 +1,57 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Raspberry Pi Platform Test Filters."""
+
+from starboard.tools.testing import test_filter
+
+
+class TestFilters(object):
+  """Starboard Raspberry Pi platform test filters."""
+
+  def GetTestFilters(self):
+    filters = []
+    for target, tests in self._FILTERED_TESTS.iteritems():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+    return filters
+
+  _FILTERED_TESTS = {
+      'nplb': [
+          'SbAudioSinkTest.*',
+
+          # Permanently filter out drm system related tests as raspi doesn't
+          # support any drm systems and there is no plan to implement such
+          # support.
+          'SbDrmTest.AnySupportedKeySystems',
+          'SbMediaCanPlayMimeAndKeySystem.AnySupportedKeySystems',
+          'SbMediaCanPlayMimeAndKeySystem.KeySystemWithAttributes',
+          'SbMediaCanPlayMimeAndKeySystem.MinimumSupport',
+          'SbMediaSetAudioWriteDurationTests/*',
+          'SbPlayerWriteSampleTests*',
+          'SbUndefinedBehaviorTest.CallThisPointerIsNullRainyDay',
+          'SbSystemGetPropertyTest.FLAKY_ReturnsRequired',
+      ],
+      'player_filter_tests': [
+          # The implementations for the raspberry pi (0 and 2) are incomplete
+          # and not meant to be a reference implementation. As such we will
+          # not repair these failing tests for now.
+          'VideoDecoderTests/VideoDecoderTest.EndOfStreamWithoutAnyInput/0',
+          'VideoDecoderTests/VideoDecoderTest.MultipleResets/0',
+          # Filter failed tests.
+          'PlayerComponentsTests/PlayerComponentsTest.*',
+      ],
+  }
+
+
+def CreateTestFilters():
+  return TestFilters()
diff --git a/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni b/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
index 1b0b0e4..bf78fec 100644
--- a/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
+++ b/starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni
@@ -12,14 +12,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-_home_dir = getenv("HOME")
 _raspi_home_dir = getenv("RASPI_HOME")
 assert(_raspi_home_dir != "",
        "RasPi builds require the 'RASPI_HOME' environment variable to be set.")
 
-raspi_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
 _raspi_toolchain_path = "$_raspi_home_dir/tools/arm-bcm2708/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin"
 
 gcc_toolchain_ar = "ar"
 gcc_toolchain_cc = "$_raspi_toolchain_path/arm-linux-gnueabihf-gcc"
 gcc_toolchain_cxx = "$_raspi_toolchain_path/arm-linux-gnueabihf-g++"
+gcc_toolchain_strip = "$_raspi_toolchain_path/arm-linux-gnueabihf-strip"
diff --git a/starboard/shared/ffmpeg/BUILD.gn b/starboard/shared/ffmpeg/BUILD.gn
index d298a03..e6bc469 100644
--- a/starboard/shared/ffmpeg/BUILD.gn
+++ b/starboard/shared/ffmpeg/BUILD.gn
@@ -35,14 +35,19 @@
   ]
 }
 
-static_library("ffmpeg_linked") {
-  check_includes = false
-  sources = ffmpeg_specialization_sources + [
-              "ffmpeg_linked_audio_decoder_impl.cc",
-              "ffmpeg_linked_dispatch_impl.cc",
-              "ffmpeg_linked_video_decoder_impl.cc",
-            ]
-  public_deps = [ ":ffmpeg_dispatch_sources" ]
+# TODO(b/208910380): ffmpeg_linked doesn't compile for linux locally.
+_target_platform_name_parts = string_split(target_platform, "-")
+_building_linux_platform = _target_platform_name_parts[0] == "linux"
+if (!_building_linux_platform) {
+  static_library("ffmpeg_linked") {
+    check_includes = false
+    sources = ffmpeg_specialization_sources + [
+                "ffmpeg_linked_audio_decoder_impl.cc",
+                "ffmpeg_linked_dispatch_impl.cc",
+                "ffmpeg_linked_video_decoder_impl.cc",
+              ]
+    public_deps = [ ":ffmpeg_dispatch_sources" ]
+  }
 }
 
 # We recompile the specialization sources for each different library version.
diff --git a/starboard/shared/libjpeg/jpeg_image_decoder.cc b/starboard/shared/libjpeg/jpeg_image_decoder.cc
index c990179..5a24097 100644
--- a/starboard/shared/libjpeg/jpeg_image_decoder.cc
+++ b/starboard/shared/libjpeg/jpeg_image_decoder.cc
@@ -161,9 +161,11 @@
         FillRow<2, 1, 0, 3>(static_cast<int>(info->image_width), pixel_data,
                             sample_buffer);
         break;
+      // All the other decode formats are not supported.
       case kSbDecodeTargetFormat2PlaneYUVNV12:
       case kSbDecodeTargetFormat3PlaneYUVI420:
       case kSbDecodeTargetFormat3Plane10BitYUVI420:
+      case kSbDecodeTargetFormat3Plane10BitYUVI420Compact:
       case kSbDecodeTargetFormat1PlaneUYVY:
       case kSbDecodeTargetFormatInvalid:
         SB_NOTREACHED();
diff --git a/starboard/shared/posix/free_space.cc b/starboard/shared/posix/free_space.cc
new file mode 100644
index 0000000..f66de59
--- /dev/null
+++ b/starboard/shared/posix/free_space.cc
@@ -0,0 +1,55 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/posix/free_space.h"
+
+#include <sys/statvfs.h>
+#include <vector>
+
+#include "cobalt/extension/free_space.h"
+#include "starboard/common/log.h"
+#include "starboard/configuration_constants.h"
+
+namespace starboard {
+namespace shared {
+namespace posix {
+
+namespace {
+
+int64_t MeasureFreeSpace(SbSystemPathId system_path_id) {
+  std::vector<char> path(kSbFileMaxPath + 1);
+  if (!SbSystemGetPath(system_path_id, path.data(), path.size())) {
+    return -1;
+  }
+  struct statvfs s;
+  if (statvfs(path.data(), &s) != 0) {
+    return -1;
+  }
+
+  return s.f_bsize * s.f_bfree;
+}
+
+const CobaltExtensionFreeSpaceApi kFreeSpaceApi = {
+    kCobaltExtensionFreeSpaceName, 1, &MeasureFreeSpace,
+};
+
+}  // namespace
+
+const void* GetFreeSpaceApi() {
+  return &kFreeSpaceApi;
+}
+
+}  // namespace posix
+}  // namespace shared
+}  // namespace starboard
diff --git a/starboard/shared/posix/free_space.h b/starboard/shared/posix/free_space.h
new file mode 100644
index 0000000..1d44447
--- /dev/null
+++ b/starboard/shared/posix/free_space.h
@@ -0,0 +1,28 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_POSIX_FREE_SPACE_H_
+#define STARBOARD_SHARED_POSIX_FREE_SPACE_H_
+
+namespace starboard {
+namespace shared {
+namespace posix {
+
+const void* GetFreeSpaceApi();
+
+}  // namespace posix
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_POSIX_FREE_SPACE_H_
diff --git a/starboard/shared/signal/debug_signals.cc b/starboard/shared/signal/debug_signals.cc
new file mode 100644
index 0000000..15ea2f4
--- /dev/null
+++ b/starboard/shared/signal/debug_signals.cc
@@ -0,0 +1,49 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/signal/debug_signals.h"
+
+#include <malloc.h>
+#include <signal.h>
+
+#include "starboard/common/log.h"
+#include "starboard/shared/signal/signal_internal.h"
+
+namespace starboard {
+namespace shared {
+namespace signal {
+
+namespace {
+
+const int kDebugSignalsToTrap[] = {
+    SIGRTMIN,
+};
+
+void MallocInfo(int signal_id) {
+  LogSignalCaught(signal_id);
+  malloc_info(0, stderr);
+}
+}  // namespace
+
+void InstallDebugSignalHandlers() {
+  ::signal(SIGRTMIN, &MallocInfo);
+}
+
+void UninstallDebugSignalHandlers() {
+  ::signal(SIGRTMIN, SIG_DFL);
+}
+
+}  // namespace signal
+}  // namespace shared
+}  // namespace starboard
diff --git a/starboard/shared/signal/debug_signals.h b/starboard/shared/signal/debug_signals.h
new file mode 100644
index 0000000..86f893b
--- /dev/null
+++ b/starboard/shared/signal/debug_signals.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_SIGNAL_DEBUG_SIGNALS_H_
+#define STARBOARD_SHARED_SIGNAL_DEBUG_SIGNALS_H_
+
+#include "starboard/shared/internal_only.h"
+
+namespace starboard {
+namespace shared {
+namespace signal {
+
+void InstallDebugSignalHandlers();
+void UninstallDebugSignalHandlers();
+
+}  // namespace signal
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_SIGNAL_DEBUG_SIGNALS_H_
diff --git a/starboard/shared/signal/suspend_signals.cc b/starboard/shared/signal/suspend_signals.cc
index bc05466..4b6feb0 100644
--- a/starboard/shared/signal/suspend_signals.cc
+++ b/starboard/shared/signal/suspend_signals.cc
@@ -26,7 +26,7 @@
 #include "starboard/system.h"
 
 #if SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
-#include "starboard/loader_app/pending_restart.h"
+#include "starboard/loader_app/pending_restart.h"  // nogncheck
 #endif  // SB_IS(EVERGREEN_COMPATIBLE) && !SB_IS(EVERGREEN_COMPATIBLE_LITE)
 
 namespace starboard {
diff --git a/starboard/shared/starboard/log_raw_dump_stack.cc b/starboard/shared/starboard/log_raw_dump_stack.cc
index 8972ce1..ded8383 100644
--- a/starboard/shared/starboard/log_raw_dump_stack.cc
+++ b/starboard/shared/starboard/log_raw_dump_stack.cc
@@ -15,7 +15,7 @@
 #include "starboard/common/log.h"
 
 #if SB_IS(EVERGREEN_COMPATIBLE)
-#include "starboard/elf_loader/evergreen_info.h"
+#include "starboard/elf_loader/evergreen_info.h"  // nogncheck
 #include "starboard/memory.h"
 #endif
 #include "starboard/system.h"
diff --git a/starboard/shared/starboard/player/BUILD.gn b/starboard/shared/starboard/player/BUILD.gn
index 161834f..997a39a 100644
--- a/starboard/shared/starboard/player/BUILD.gn
+++ b/starboard/shared/starboard/player/BUILD.gn
@@ -30,6 +30,8 @@
 }
 
 action("player_download_test_data") {
+  install_content = true
+
   script = "//tools/download_from_gcs.py"
 
   sha_sources = []
@@ -42,7 +44,7 @@
   }
 
   sha_outputs = []
-  subdir = "starboard/shared/starboard/player/testdata"
+  subdir = "starboard/shared/starboard/player"
   outdir = "$sb_static_contents_output_data_dir/test/$subdir"
   foreach(sha_source, sha_sources) {
     sha_outputs +=
@@ -64,6 +66,6 @@
     "--sha1",
     sha1_dir,
     "--output",
-    rebase_path(outdir, root_build_dir),
+    rebase_path("$outdir/testdata", root_build_dir),
   ]
 }
diff --git a/starboard/shared/starboard/player/filter/testing/BUILD.gn b/starboard/shared/starboard/player/filter/testing/BUILD.gn
index 755b4e4..dc5cdcc 100644
--- a/starboard/shared/starboard/player/filter/testing/BUILD.gn
+++ b/starboard/shared/starboard/player/filter/testing/BUILD.gn
@@ -40,9 +40,14 @@
   ]
 
   deps = cobalt_platform_dependencies
+
+  content_deps = [
+    "//starboard/shared/starboard/player:player_download_test_data",
+    "//third_party/icu:icudata",
+  ]
 }
 
-if (!is_win) {
+if (host_os != "win") {
   target(final_executable_type, "player_filter_benchmarks") {
     testonly = true
 
diff --git a/starboard/shared/starboard/player/filter/tools/BUILD.gn b/starboard/shared/starboard/player/filter/tools/BUILD.gn
index af555d2..698d480 100644
--- a/starboard/shared/starboard/player/filter/tools/BUILD.gn
+++ b/starboard/shared/starboard/player/filter/tools/BUILD.gn
@@ -22,10 +22,13 @@
     "//starboard/shared/starboard/player/video_dmp_reader.h",
     "audio_dmp_player.cc",
   ]
-
   configs += [ "//starboard/build/config:starboard_implementation" ]
   public_deps = [
     "//starboard",
     "//starboard/shared/starboard/player:player_download_test_data",
   ]
+  content_deps = [
+    "//starboard/shared/starboard/player:player_download_test_data",
+    "//third_party/icu:icudata",
+  ]
 }
diff --git a/starboard/shared/starboard/player/testdata/.gitignore b/starboard/shared/starboard/player/testdata/.gitignore
index f1136ab..14e98ce 100644
--- a/starboard/shared/starboard/player/testdata/.gitignore
+++ b/starboard/shared/starboard/player/testdata/.gitignore
@@ -1,2 +1 @@
 *.dmp
-
diff --git a/starboard/shared/toolchain/overridable_gcc_toolchain.gni b/starboard/shared/toolchain/overridable_gcc_toolchain.gni
new file mode 100644
index 0000000..125f405
--- /dev/null
+++ b/starboard/shared/toolchain/overridable_gcc_toolchain.gni
@@ -0,0 +1,144 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/toolchain/gcc_toolchain.gni")
+
+# The environment variables that override arguments of
+# overridable_gcc_toolchain and overridable_clang_toolchain
+# templates in this file.
+declare_args() {
+  # The full path to the compiler for AR.
+  ar_compiler_override = getenv("GN_AR_COMPILER")
+
+  # The full path to the compiler for CC.
+  cc_compiler_override = getenv("GN_CC_COMPILER")
+
+  # The full path to the compiler for CXX.
+  cxx_compiler_override = getenv("GN_CXX_COMPILER")
+
+  # The full path to the compiler for LD.
+  ld_compiler_override = getenv("GN_LD_COMPILER")
+
+  # Optional parameters that control the tools:
+
+  # Extra flags to be appended when compiling C files (but not C++ files).
+  c_flags_override = getenv("GN_C_FLAGS")
+
+  # Extra flags to be appended when compiling both C and C++ files. "CPP"
+  # stands for "C PreProcessor" in this context, although it can be
+  # used for non-preprocessor flags as well. Not to be confused with
+  # "CXX" (which follows).
+  cpp_flags_override = getenv("GN_CPP_FLAGS")
+
+  # Extra flags to be appended when compiling C++ files (but not C files).
+  cxx_flags_override = getenv("GN_CXX_FLAGS")
+
+  # Extra flags to be appended when compiling assembly.
+  asm_flags_override = getenv("GN_ASM_FLAGS")
+
+  # Extra flags to be appended when linking.
+  ld_flags_override = getenv("GN_LD_FLAGS")
+}
+
+# This template applies environment variables overrides to gcc_toolchain in
+# build/toolchain/gcc_toolchain.gni.
+# The environment variables/gn arguments overrides specified in this file
+# will take precedence over the corresponding arguments passed in from the
+# invoked gcc_toolchain.
+template("overridable_gcc_toolchain") {
+  gcc_toolchain(target_name) {
+    forward_variables_from(invoker, "*")
+
+    if (ar_compiler_override != "") {
+      ar = ar_compiler_override
+    }
+
+    if (cc_compiler_override != "") {
+      cc = cc_compiler_override
+    }
+
+    if (cxx_compiler_override != "") {
+      cxx = cxx_compiler_override
+    }
+
+    if (ld_compiler_override != "") {
+      ld = ld_compiler_override
+    }
+
+    if (c_flags_override != "") {
+      extra_cflags = c_flags_override
+    }
+
+    if (cpp_flags_override != "") {
+      extra_cppflags = cpp_flags_override
+    }
+
+    if (cxx_flags_override != "") {
+      extra_cxxflags = cxx_flags_override
+    }
+
+    if (asm_flags_override != "") {
+      extra_asmflags = asm_flags_override
+    }
+
+    if (ld_flags_override != "") {
+      extra_ldflags = ld_flags_override
+    }
+  }
+}
+
+# This template applies environment variables overrides to clang_toolchain
+# in build/toolchain/gcc_toolchain.gni.
+# This is a shorthand for gcc_toolchain_set_env_vars instances based on the
+# Chromium-built version of Clang. Only the toolchain_cpu and toolchain_os
+# variables need to be specified by the invoker, and optionally toolprefix if
+# it's a cross-compile case. Note that for a cross-compile case this toolchain
+# requires a config to pass the appropriate -target option, or else it will
+# actually just be doing a native compile. The invoker can optionally override
+# use_gold too.
+template("overridable_clang_toolchain") {
+  if (defined(invoker.toolprefix)) {
+    toolprefix = invoker.toolprefix
+  } else {
+    toolprefix = ""
+  }
+  if (is_starboard) {
+    clang_base_path = invoker.clang_base_path
+  }
+
+  overridable_gcc_toolchain(target_name) {
+    prefix = rebase_path("$clang_base_path/bin", root_build_dir)
+    cc = "$prefix/clang"
+    cxx = "$prefix/clang++"
+    ld = cxx
+    readelf = "${toolprefix}readelf"
+    ar = "${prefix}/llvm-ar"
+    nm = "nm"
+
+    forward_variables_from(invoker,
+                           [
+                             "strip",
+                             "default_shlib_subdir",
+                             "enable_linker_map",
+                             "use_unstripped_as_runtime_outputs",
+                           ])
+
+    toolchain_args = {
+      if (defined(invoker.toolchain_args)) {
+        forward_variables_from(invoker.toolchain_args, "*")
+      }
+      is_clang = true
+    }
+  }
+}
diff --git a/starboard/shared/widevine/BUILD.gn b/starboard/shared/widevine/BUILD.gn
index 8ab037d..e10f1a3 100644
--- a/starboard/shared/widevine/BUILD.gn
+++ b/starboard/shared/widevine/BUILD.gn
@@ -31,8 +31,8 @@
   sources = [
     "//starboard/keyboxes/${sb_widevine_platform}/${sb_widevine_platform}.h",
     "//starboard/keyboxes/${sb_widevine_platform}/${sb_widevine_platform}_client.c",
+    "//starboard/shared/widevine/internal/wv_keybox.cc",
     "//starboard/shared/widevine/widevine_keybox_hash.cc",
-    "//starboard/shared/widevine/wv_keybox.cc",
   ]
 
   public_configs = [ ":oemcrypto_external" ]
diff --git a/starboard/shared/widevine/widevine3.gyp b/starboard/shared/widevine/widevine3.gyp
index fe8b7cf..8285e85 100644
--- a/starboard/shared/widevine/widevine3.gyp
+++ b/starboard/shared/widevine/widevine3.gyp
@@ -34,8 +34,8 @@
     'platform_oem_sources': [
       '<(DEPTH)/starboard/keyboxes/<(sb_widevine_platform)/<(sb_widevine_platform).h',
       '<(DEPTH)/starboard/keyboxes/<(sb_widevine_platform)/<(sb_widevine_platform)_client.c',
+      '<(DEPTH)/starboard/shared/widevine/internal/wv_keybox.cc',
       '<(DEPTH)/starboard/shared/widevine/widevine_keybox_hash.cc',
-      '<(DEPTH)/starboard/shared/widevine/wv_keybox.cc',
     ],
    },
   'target_defaults': {
diff --git a/starboard/stub/args.gn b/starboard/stub/args.gn
new file mode 100644
index 0000000..7b09b13
--- /dev/null
+++ b/starboard/stub/args.gn
@@ -0,0 +1,17 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+target_platform = "stub"
+target_os = "linux"
+target_cpu = "x64"
diff --git a/starboard/stub/test_filters.py b/starboard/stub/test_filters.py
new file mode 100644
index 0000000..d289036
--- /dev/null
+++ b/starboard/stub/test_filters.py
@@ -0,0 +1,30 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Starboard Stub Platform Test Filters."""
+
+
+def CreateTestFilters():
+  return TestFilters()
+
+
+class TestFilters():
+  """Starboard Stub platform test filters."""
+
+  def GetTestFilters(self):
+    """Gets all tests to be excluded from a unit test run.
+
+    Returns:
+      A list of initialized TestFilter objects.
+    """
+    return []
diff --git a/starboard/stub/toolchain/BUILD.gn b/starboard/stub/toolchain/BUILD.gn
index 1ab2e67..c1469cb 100644
--- a/starboard/stub/toolchain/BUILD.gn
+++ b/starboard/stub/toolchain/BUILD.gn
@@ -12,13 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 
-_home_dir = getenv("HOME")
-_clang_base_path = "$_home_dir/starboard-toolchains/x86_64-linux-gnu-clang-chromium-365097-f7e52fbd-8"
-
 clang_toolchain("host") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 
   toolchain_args = {
     current_os = "linux"
@@ -27,5 +25,5 @@
 }
 
 clang_toolchain("target") {
-  clang_base_path = _clang_base_path
+  clang_base_path = clang_base_path
 }
diff --git a/starboard/tools/BUILD.gn b/starboard/tools/BUILD.gn
new file mode 100644
index 0000000..b4f1ffd
--- /dev/null
+++ b/starboard/tools/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+action("build_app_launcher_zip") {
+  script = "//starboard/build/run_bash.py"
+  py_script = "//starboard/tools/app_launcher_packager.py"
+
+  file_list = exec_script("//starboard/build/run_bash.py",
+                          [
+                            "python2",
+                            rebase_path(py_script, root_build_dir),
+                            "-l",
+                          ],
+                          "trim string",
+                          [ py_script ])
+  inputs = string_split(file_list)
+  inputs += [ py_script ]
+  outputs = [ "$root_out_dir/app_launcher.zip" ]
+
+  args = [
+    "python2",
+    rebase_path(py_script, root_build_dir),
+    "-z",
+    rebase_path(outputs[0], root_build_dir),
+  ]
+}
diff --git a/starboard/tools/app_launcher_packager.py b/starboard/tools/app_launcher_packager.py
index 0420583..c228a95 100644
--- a/starboard/tools/app_launcher_packager.py
+++ b/starboard/tools/app_launcher_packager.py
@@ -259,6 +259,10 @@
       help='List to stdout the application resources relative to the current '
       'directory.')
   parser.add_argument(
+      '--return_list',
+      action='store_true',
+      help='Return the application resources instead of printing them.')
+  parser.add_argument(
       '-v',
       '--verbose',
       action='store_true',
@@ -289,7 +293,10 @@
       src_file = src_file.replace('\\', '/')
       src_files.append(src_file)
     out = ' '.join(src_files)
-    return out.strip()
+    if args.return_list:
+      return out.strip()
+    else:
+      print(out.strip())
   return 0
 
 
diff --git a/starboard/tools/build.py b/starboard/tools/build.py
index 08923fe..64eca42 100644
--- a/starboard/tools/build.py
+++ b/starboard/tools/build.py
@@ -15,7 +15,7 @@
 #
 """Build related constants and helper functions."""
 
-import imp
+import imp  # pylint: disable=deprecated-module
 import importlib
 import logging
 import os
@@ -198,6 +198,51 @@
   return extensionless_loaded_path == extensionless_module_path
 
 
+def _LoadPlatformModule(platform_name, file_name, function_name):
+  """Loads a platform module.
+
+  The function will use the provided platform name to load
+  a python file with a matching name that contains the module.
+
+  Args:
+    platform_name: Platform name.
+    file_name: The file that contains the module. It can be
+               gyp_configuration.py or test_filters.py
+    function_name: The function name of the module.
+                   For gyp_configuration.py, it is CreatePlatformConfig.
+                   For test_filters.py, it is GetTestFilters
+
+  Returns: Instance of a class derived from module path.
+  """
+  try:
+    logging.debug('Loading platform %s for "%s".', file_name, platform_name)
+    if platform.IsValid(platform_name):
+      platform_path = os.path.join(paths.REPOSITORY_ROOT,
+                                   platform.Get(platform_name).path)
+      module_path = os.path.join(platform_path, file_name)
+      if not _ModuleLoaded('platform_module', module_path):
+        platform_module = imp.load_source('platform_module', module_path)
+      else:
+        platform_module = sys.modules['platform_module']
+    else:
+      module_path = os.path.join('config', '%s.py' % platform_name)
+      platform_module = importlib.import_module('config.%s' % platform_name)
+  except (ImportError, IOError):
+    logging.exception('Unable to import "%s".', module_path)
+    return None
+
+  if not hasattr(platform_module, function_name):
+    logging.error('"%s" does not contain %s.', module_path, function_name)
+    return None
+
+  try:
+    platform_attr = getattr(platform_module, function_name)()
+    return platform_attr, platform_path
+  except RuntimeError:
+    logging.exception('Exception in %s.', function_name)
+    return None
+
+
 def _LoadPlatformConfig(platform_name):
   """Loads a platform specific configuration.
 
@@ -211,39 +256,35 @@
   Returns:
     Instance of a class derived from PlatformConfigBase.
   """
-  try:
-    logging.debug('Loading platform configuration for "%s".', platform_name)
-    if platform.IsValid(platform_name):
-      platform_path = os.path.join(paths.REPOSITORY_ROOT,
-                                   platform.Get(platform_name).path)
-      module_path = os.path.join(platform_path, 'gyp_configuration.py')
-      if not _ModuleLoaded('platform_module', module_path):
-        platform_module = imp.load_source('platform_module', module_path)
-      else:
-        platform_module = sys.modules['platform_module']
-    else:
-      module_path = os.path.join('config', '%s.py' % platform_name)
-      platform_module = importlib.import_module('config.%s' % platform_name)
-  except (ImportError, IOError):
-    logging.exception('Unable to import "%s".', module_path)
-    return None
+  platform_configuration, platform_path = _LoadPlatformModule(
+      platform_name, 'gyp_configuration.py', 'CreatePlatformConfig')
+  platform_configuration.SetDirectory(platform_path)
+  return platform_configuration
 
-  if not hasattr(platform_module, 'CreatePlatformConfig'):
-    logging.error('"%s" does not contain CreatePlatformConfig.', module_path)
-    return None
 
-  try:
-    platform_configuration = platform_module.CreatePlatformConfig()
-    platform_configuration.SetDirectory(platform_path)
-    return platform_configuration
-  except RuntimeError:
-    logging.exception('Exception in CreatePlatformConfig.')
-    return None
+def _LoadPlatformTestFilters(platform_name):
+  """Loads the platform specific test filters.
+
+  The function will use the provided platform name to load
+  a python file with a matching name that contains the platform
+  specific test filters.
+
+  Args:
+    platform_name: Platform name.
+
+  Returns:
+    Instance of a class derived from TestFilters.
+  """
+  platform_test_filters, _ = _LoadPlatformModule(platform_name,
+                                                 'test_filters.py',
+                                                 'CreateTestFilters')
+  return platform_test_filters
 
 
 # Global cache of the platform configurations, so that platform config objects
 # are only created once.
 _PLATFORM_CONFIG_DICT = {}
+_PLATFORM_TEST_FILTERS_DICT = {}
 
 
 def GetPlatformConfig(platform_name):
@@ -263,3 +304,20 @@
     _PLATFORM_CONFIG_DICT[platform_name] = _LoadPlatformConfig(platform_name)
 
   return _PLATFORM_CONFIG_DICT[platform_name]
+
+
+def GetPlatformTestFilters(platform_name):
+  """Returns a platform specific test filters.
+
+  Args:
+    platform_name: Platform name.
+
+  Returns:
+    Instance of a class derived from TestFilters.
+  """
+
+  if platform_name not in _PLATFORM_TEST_FILTERS_DICT:
+    _PLATFORM_TEST_FILTERS_DICT[platform_name] = _LoadPlatformTestFilters(
+        platform_name)
+
+  return _PLATFORM_CONFIG_DICT[platform_name]
diff --git a/starboard/tools/download_clang.py b/starboard/tools/download_clang.py
index 020aae0..274444b 100755
--- a/starboard/tools/download_clang.py
+++ b/starboard/tools/download_clang.py
@@ -75,6 +75,7 @@
     if not os.path.exists(output_dir):
       os.makedirs(output_dir)
 
+    # pylint: disable=consider-using-with
     t = tarfile.open(mode='r:gz', fileobj=tmpfile)
     t.extractall(path=output_dir)
 
@@ -92,6 +93,9 @@
   except IOError:
     pass
 
+  if os.getenv('IS_CI', '') == '1':
+    raise Exception('Dynamic toolchain downloads are disabled in CI')
+
   if os.path.exists(target_dir):
     shutil.rmtree(target_dir)
 
diff --git a/starboard/tools/testing/test_runner.py b/starboard/tools/testing/test_runner.py
index aa98b10..a51f5ca 100755
--- a/starboard/tools/testing/test_runner.py
+++ b/starboard/tools/testing/test_runner.py
@@ -242,8 +242,11 @@
 
     logging.info("Getting platform configuration")
     self._platform_config = build.GetPlatformConfig(platform)
+    self._platform_test_filters = build.GetPlatformTestFilters(platform)
     if self.loader_platform:
       self._loader_platform_config = build.GetPlatformConfig(loader_platform)
+      self._loader_platform_test_filters = build.GetPlatformTestFilters(
+          loader_platform)
     logging.info("Got platform configuration")
     logging.info("Getting application configuration")
     self._app_config = self._platform_config.GetApplicationConfiguration(
@@ -347,7 +350,7 @@
 
   def _GetTestFilters(self):
     """Get test filters for a given platform and configuration."""
-    filters = self._platform_config.GetTestFilters()
+    filters = self._platform_test_filters.GetTestFilters()
     app_filters = self._app_config.GetTestFilters()
     if app_filters:
       filters.extend(app_filters)
@@ -357,9 +360,11 @@
     # filtered tests when it is running on a Raspberry Pi 2.
     if self.loader_platform and self.loader_config:
       loader_platform_config = build.GetPlatformConfig(self.loader_platform)
+      loader_platform_test_filters = build.GetPlatformTestFilters(
+          self.loader_platform)
       loader_app_config = loader_platform_config.GetApplicationConfiguration(
           self.application_name)
-      for filter_ in (loader_platform_config.GetTestFilters() +
+      for filter_ in (loader_platform_test_filters.GetTestFilters() +
                       loader_app_config.GetTestFilters()):
         if filter_ not in filters:
           filters.append(filter_)
diff --git a/starboard/tools/tools.gyp b/starboard/tools/tools.gyp
index ce18897..f6fe913 100644
--- a/starboard/tools/tools.gyp
+++ b/starboard/tools/tools.gyp
@@ -29,7 +29,7 @@
           'action_name': 'package_app_launcher',
           'message': 'Zipping <(app_launcher_zip_file)',
           'inputs': [
-            '<!@pymod_do_main(starboard.tools.app_launcher_packager -l)',
+            '<!@pymod_do_main(starboard.tools.app_launcher_packager -l --return_list)',
           ],
           'outputs': [
             '<(app_launcher_zip_file)',
diff --git a/testing/OWNERS b/testing/OWNERS
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/testing/OWNERS
@@ -0,0 +1 @@
+*
diff --git a/testing/android/OWNERS b/testing/android/OWNERS
new file mode 100644
index 0000000..45b2d83
--- /dev/null
+++ b/testing/android/OWNERS
@@ -0,0 +1,4 @@
+bulach@chromium.org
+jrg@chromium.org
+nileshagrawal@chromium.org
+yfriedman@chromium.org
diff --git a/testing/gtest/BUILD.gn b/testing/gtest/BUILD.gn
index ca2258b..8758d77 100644
--- a/testing/gtest/BUILD.gn
+++ b/testing/gtest/BUILD.gn
@@ -24,7 +24,7 @@
     "GTEST_HAS_POSIX_RE=0",
     "_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING",
   ]
-  if (target_cpu != "ps4" && target_os != "win") {
+  if (!is_win) {
     defines += [ "GTEST_USE_OWN_TR1_TUPLE=1" ]
   }
 }
diff --git a/testing/iossim/OWNERS b/testing/iossim/OWNERS
new file mode 100644
index 0000000..1b3348e
--- /dev/null
+++ b/testing/iossim/OWNERS
@@ -0,0 +1,2 @@
+rohitrao@chromium.org
+stuartmorgan@chromium.org
diff --git a/third_party/OWNERS b/third_party/OWNERS
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/third_party/OWNERS
@@ -0,0 +1 @@
+*
diff --git a/third_party/angle/.gitattributes b/third_party/angle/.gitattributes
new file mode 100644
index 0000000..e6713d0
--- /dev/null
+++ b/third_party/angle/.gitattributes
@@ -0,0 +1,17 @@
+*                                          text=auto
+*.sln                                      eol=crlf
+*.vcxproj                                  eol=crlf
+*.vcxproj.filters                          eol=crlf
+*.bat                                      eol=crlf
+*.rc                                       eol=crlf
+**/compiled/*.h                            eol=crlf
+**/shaders/gen/*.inc                       eol=lf
+*.sh                                       eol=lf
+*.gn                                       eol=lf
+*.gni                                      eol=lf
+src/compiler/preprocessor/preprocessor_*.* eol=lf
+src/compiler/translator/glslang_*.*        eol=lf
+
+# Git conflict markers in the json file break the code generator.
+# Using a binary merge strategy forces conflicts without changing file contents.
+scripts/code_generation_hashes/*.json merge=binary
diff --git a/third_party/angle/BUILD.gn b/third_party/angle/BUILD.gn
index fbc64aa..5882ce0 100644
--- a/third_party/angle/BUILD.gn
+++ b/third_party/angle/BUILD.gn
@@ -120,6 +120,18 @@
   if (is_chromeos) {
     defines += [ "ANGLE_PLATFORM_CHROMEOS" ]
   }
+
+  if (is_starboard && is_win) {
+    defines += [ "__WRL_NO_DEFAULT_LIB__" ]
+    if (current_os == "winuwp") {
+      defines += [ "WINAPI_FAMILY=WINAPI_FAMILY_APP" ] # UWP
+    } else {
+      defines += [
+        "_WIN32",
+        "WINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP", # win32
+      ]
+    }
+  }
 }
 
 config("constructor_and_destructor_warnings") {
@@ -193,7 +205,7 @@
   }
 }
 
-_use_copy_compiler_dll = angle_has_build && is_win && target_cpu != "arm64"
+_use_copy_compiler_dll = !is_starboard && angle_has_build && is_win && target_cpu != "arm64"
 
 # Windows ARM64 is available since 10.0.16299 so no need to copy
 # d3dcompiler_47.dll because this file is available as inbox.
@@ -233,6 +245,10 @@
 angle_static_library("preprocessor") {
   sources = angle_preprocessor_sources
 
+  if (is_starboard) {
+    suppressed_configs += [ "//starboard/build/config:warnings_as_errors" ]
+  }
+
   public_deps = [
     ":angle_common",
     ":angle_translator_headers",
@@ -491,6 +507,10 @@
 
   public_configs += [ ":external_config" ]
 
+  if (is_starboard) {
+    suppressed_configs += [ "//starboard/build/config:warnings_as_errors" ]
+  }
+
   deps = [
     ":includes",
     ":preprocessor",
diff --git a/third_party/angle/DEPS b/third_party/angle/DEPS
new file mode 100644
index 0000000..76a0854
--- /dev/null
+++ b/third_party/angle/DEPS
@@ -0,0 +1,424 @@
+# This file is used to manage the dependencies of the ANGLE git repo. It is
+# used by gclient to determine what version of each dependency to check out, and
+# where.
+
+# Avoids the need for a custom root variable.
+use_relative_paths = True
+use_relative_hooks = True
+
+vars = {
+  'android_git': 'https://android.googlesource.com',
+  'chromium_git': 'https://chromium.googlesource.com',
+  'chrome_internal_git': 'https://chrome-internal.googlesource.com',
+  'swiftshader_git': 'https://swiftshader.googlesource.com',
+
+  # This variable is overrided in Chromium's DEPS file.
+  'build_with_chromium': False,
+
+  # Only check out public sources by default. This can be overridden with custom_vars.
+  # We overload Chromium's 'src-internal' for simplicity.
+  # TOOD(ynovikov): Use checkout_angle_internal custom variable instead.
+  'checkout_src_internal': False,
+
+  # Version of Chromium our Chromium-based DEPS are mirrored from.
+  'chromium_revision': 'd209d45ba4bacfaf948e0015f9f7ef71b0d93bbf',
+
+  # Current revision of VK-GL-CTS (a.k.a dEQP).
+  'vk_gl_cts_revision': '54ec6f2b1390bf33ea10424dca610f8bcbfefa06',
+
+  # Current revision of glslang, the Khronos SPIRV compiler.
+  'glslang_revision': '0de87ee9a5bf5d094a3faa1a71fd9080e80b6be0',
+
+  # Current revision of googletest.
+  # Note: this dep cannot be auto-rolled b/c of nesting.
+  'googletest_revision': 'f2fb48c3b3d79a75a88a99fba6576b25d42ec528',
+
+  # Current revision of jsoncpp.
+  # Note: this dep cannot be auto-rolled b/c of nesting.
+  'jsoncpp_revision': '645250b6690785be60ab6780ce4b58698d884d11',
+
+  # Current revision of patched-yasm.
+  # Note: this dep cannot be auto-rolled b/c of nesting.
+  'patched_yasm_revision': '720b70524a4424b15fc57e82263568c8ba0496ad',
+
+  # Current revision of spirv-cross, the Khronos SPIRV cross compiler.
+  'spirv_cross_revision': 'd253f41e17e27285756d031d8ba43bf370264e1f',
+
+  # Current revision fo the SPIRV-Headers Vulkan support library.
+  'spirv_headers_revision': 'af64a9e826bf5bb5fcd2434dd71be1e41e922563',
+
+  # Current revision of SPIRV-Tools for Vulkan.
+  'spirv_tools_revision': 'e82a428605f6ce0a07337b36f8ba3935c9f165ac',
+
+  # Current revision of Khronos Vulkan-Headers.
+  'vulkan_headers_revision': '2b89fd4e2734b728ca0be72a13f2265c5f5aa88e',
+
+  # Current revision of Khronos Vulkan-Loader.
+  'vulkan_loader_revision': '79e03670c2a328bea3c1a3f80ea913f296a487e6',
+
+  # Current revision of Khronos Vulkan-Tools.
+  'vulkan_tools_revision': '0a0625a3dca69b9d7ecb73558539ce5e3cd4ddfa',
+
+  # Current revision of Khronos Vulkan-ValidationLayers.
+  'vulkan_validation_revision': 'e72b61c7c20dd2443f955b956f96b2976ccee004',
+
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling catapult
+  # and whatever else without interference from each other.
+  'catapult_revision': '1b3fb455bf1849f1e6187e1eaeaef32b9f30d3c5',
+}
+
+deps = {
+
+  'build': {
+    'url': '{chromium_git}/chromium/src/build.git@ebec9c5ad46c21f00d2e305ac233676327f672cd',
+    'condition': 'not build_with_chromium',
+  },
+
+  'buildtools': {
+    'url': '{chromium_git}/chromium/src/buildtools.git@6b3e658d6fe8cd9c2588796d296f07312b776054',
+    'condition': 'not build_with_chromium',
+  },
+
+  'testing': {
+    'url': '{chromium_git}/chromium/src/testing@72e16daaa56023ddf5e38460b8cfb785db048812',
+    'condition': 'not build_with_chromium',
+  },
+
+  # Cherry is a dEQP/VK-GL-CTS management GUI written in Go. We use it for viewing test results.
+  'third_party/cherry': {
+    'url': '{android_git}/platform/external/cherry@4f8fb08d33ca5ff05a1c638f04c85bbb8d8b52cc',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/VK-GL-CTS/src': {
+    'url': '{chromium_git}/external/github.com/KhronosGroup/VK-GL-CTS@{vk_gl_cts_revision}',
+  },
+
+  'third_party/fuchsia-sdk': {
+    'url': '{chromium_git}/chromium/src/third_party/fuchsia-sdk.git@1785f0ac8e1fe81cb25e260acbe7de8f62fa3e44',
+    'condition': 'checkout_fuchsia and not build_with_chromium',
+  },
+
+  # Closed-source OpenGL ES 1.1 Conformance tests.
+  'third_party/gles1_conform': {
+    'url': '{chrome_internal_git}/angle/es-cts.git@dc9f502f709c9cd88d7f8d3974f1c77aa246958e',
+    'condition': 'checkout_src_internal',
+  },
+
+  # glmark2 is a GPL3-licensed OpenGL ES 2.0 benchmark. We use it for testing.
+  'third_party/glmark2/src': {
+    'url': '{chromium_git}/external/github.com/glmark2/glmark2@9e01aef1a786b28aca73135a5b00f85c357e8f5e',
+  },
+
+  'third_party/glslang/src': {
+    'url': '{chromium_git}/external/github.com/KhronosGroup/glslang@{glslang_revision}',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/googletest': {
+    'url': '{chromium_git}/chromium/src/third_party/googletest@60616473f7d8414aeb7575b487beecc7369fd52f',
+    'condition': 'not build_with_chromium',
+  },
+
+  # libjpeg_turbo is used by glmark2.
+  'third_party/libjpeg_turbo': {
+    'url': '{chromium_git}/chromium/deps/libjpeg_turbo.git@bc13578529255ec75005ffc98aae151666122892',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/libpng/src': {
+    'url': '{android_git}/platform/external/libpng@094e181e79a3d6c23fd005679025058b7df1ad6c',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/jsoncpp': {
+    'url': '{chromium_git}/chromium/src/third_party/jsoncpp@1cfec065ed4cd9a01fa8e351baa3e714a5868522',
+    'condition': 'not build_with_chromium',
+   },
+
+  'third_party/Python-Markdown': {
+    'url': '{chromium_git}/chromium/src/third_party/Python-Markdown@36657c103ce5964733bbbb29377085e9cc1a9472',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/qemu-linux-x64': {
+      'packages': [
+          {
+              'package': 'fuchsia/qemu/linux-amd64',
+              'version': '9cc486c5b18a0be515c39a280ca9a309c54cf994'
+          },
+      ],
+      'condition': 'not build_with_chromium and (host_os == "linux" and checkout_fuchsia)',
+      'dep_type': 'cipd',
+  },
+
+  'third_party/qemu-mac-x64': {
+      'packages': [
+          {
+              'package': 'fuchsia/qemu/mac-amd64',
+              'version': '2d3358ae9a569b2d4a474f498b32b202a152134f'
+          },
+      ],
+      'condition': 'not build_with_chromium and (host_os == "mac" and checkout_fuchsia)',
+      'dep_type': 'cipd',
+  },
+
+  'third_party/rapidjson/src': {
+    'url': '{chromium_git}/external/github.com/Tencent/rapidjson@7484e06c589873e1ed80382d262087e4fa80fb63',
+  },
+
+  'third_party/spirv-cross/src': {
+    'url': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@{spirv_cross_revision}',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/spirv-headers/src': {
+    'url': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@{spirv_headers_revision}',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/spirv-tools/src': {
+    'url': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@{spirv_tools_revision}',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/SwiftShader': {
+    'url': '{swiftshader_git}/SwiftShader@bbd0694f9ab2fbcebba7f674d832dcca9a898a59',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/vulkan-headers/src': {
+    'url': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@{vulkan_headers_revision}',
+  },
+
+  'third_party/vulkan-loader/src': {
+    'url': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@{vulkan_loader_revision}',
+  },
+
+  'third_party/vulkan-tools/src': {
+    'url': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@{vulkan_tools_revision}',
+  },
+
+  'third_party/vulkan-validation-layers/src': {
+    'url': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@{vulkan_validation_revision}',
+  },
+
+  'third_party/yasm': {
+    'url': '{chromium_git}/chromium/src/third_party/yasm@02a8d2167f476660411ea7e1ee6fbd21dda44e96',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/zlib': {
+    'url': '{chromium_git}/chromium/src/third_party/zlib@e77e1c06c8881abff0c7418368d147ff4a474d08',
+    'condition': 'not build_with_chromium',
+  },
+
+  'tools/clang': {
+    'url': '{chromium_git}/chromium/src/tools/clang.git@05979d8cad7315e31c42120dd83e5765a87629d3',
+    'condition': 'not build_with_chromium',
+  },
+
+  'tools/clang/dsymutil': {
+    'packages': [
+      {
+        'package': 'chromium/llvm-build-tools/dsymutil',
+        'version': 'M56jPzDv1620Rnm__jTMYS62Zi8rxHVq7yw0qeBFEgkC',
+      }
+    ],
+    'condition': 'checkout_mac and not build_with_chromium',
+    'dep_type': 'cipd',
+  },
+
+  'tools/md_browser': {
+    'url': '{chromium_git}/chromium/src/tools/md_browser@0bfd826f8566a99923e64a782908faca72bc457c',
+    'condition': 'not build_with_chromium',
+  },
+
+  'tools/memory': {
+    'url': '{chromium_git}/chromium/src/tools/memory@89552acb6e60f528fe3c98eac7b445d4c34183ee',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/catapult': {
+    'url': '{chromium_git}/catapult.git@{catapult_revision}',
+    'condition': 'not build_with_chromium',
+  },
+
+  'third_party/android_ndk': {
+    'url': '{chromium_git}/android_ndk.git@27c0a8d090c666a50e40fceb4ee5b40b1a2d3f87',
+    'condition': 'not build_with_chromium',
+  },
+}
+
+hooks = [
+  # Pull clang-format binaries using checked-in hashes.
+  {
+    'name': 'clang_format_win',
+    'pattern': '.',
+    'condition': 'host_os == "win" and not build_with_chromium',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32',
+                '--no_auth',
+                '--bucket', 'chromium-clang-format',
+                '-s', 'buildtools/win/clang-format.exe.sha1',
+    ],
+  },
+  {
+    'name': 'clang_format_mac',
+    'pattern': '.',
+    'condition': 'host_os == "mac" and not build_with_chromium',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=darwin',
+                '--no_auth',
+                '--bucket', 'chromium-clang-format',
+                '-s', 'buildtools/mac/clang-format.sha1',
+    ],
+  },
+  {
+    'name': 'clang_format_linux',
+    'pattern': '.',
+    'condition': 'host_os == "linux" and not build_with_chromium',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=linux*',
+                '--no_auth',
+                '--bucket', 'chromium-clang-format',
+                '-s', 'buildtools/linux64/clang-format.sha1',
+    ],
+  },
+  {
+    'name': 'sysroot_x86',
+    'pattern': '.',
+    'condition': 'checkout_linux and ((checkout_x86 or checkout_x64) and not build_with_chromium)',
+    'action': ['python', 'build/linux/sysroot_scripts/install-sysroot.py',
+               '--arch=x86'],
+  },
+  {
+    'name': 'sysroot_x64',
+    'pattern': '.',
+    'condition': 'checkout_linux and (checkout_x64 and not build_with_chromium)',
+    'action': ['python', 'build/linux/sysroot_scripts/install-sysroot.py',
+               '--arch=x64'],
+  },
+  {
+    # Update the Windows toolchain if necessary.  Must run before 'clang' below.
+    'name': 'win_toolchain',
+    'pattern': '.',
+    'condition': 'checkout_win and not build_with_chromium',
+    'action': ['python', 'build/vs_toolchain.py', 'update', '--force'],
+  },
+  {
+    # Update the Mac toolchain if necessary.
+    'name': 'mac_toolchain',
+    'pattern': '.',
+    'condition': 'checkout_mac and not build_with_chromium',
+    'action': ['python', 'build/mac_toolchain.py'],
+  },
+
+  {
+    # Note: On Win, this should run after win_toolchain, as it may use it.
+    'name': 'clang',
+    'pattern': '.',
+    'action': ['python', 'tools/clang/scripts/update.py'],
+    'condition': 'not build_with_chromium',
+  },
+
+  {
+    # Update LASTCHANGE.
+    'name': 'lastchange',
+    'pattern': '.',
+    'condition': 'not build_with_chromium',
+    'action': ['python', 'build/util/lastchange.py',
+               '-o', 'build/util/LASTCHANGE'],
+  },
+
+  # Pull rc binaries using checked-in hashes.
+  {
+    'name': 'rc_win',
+    'pattern': '.',
+    'condition': 'checkout_win and (host_os == "win" and not build_with_chromium)',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--no_auth',
+                '--bucket', 'chromium-browser-clang/rc',
+                '-s', 'build/toolchain/win/rc/win/rc.exe.sha1',
+    ],
+  },
+
+  {
+    'name': 'fuchsia_sdk',
+    'pattern': '.',
+    'condition': 'checkout_fuchsia and not build_with_chromium',
+    'action': [
+      'python',
+      'build/fuchsia/update_sdk.py',
+    ],
+  },
+
+  # Download glslang validator binary for Linux.
+  {
+    'name': 'linux_glslang_validator',
+    'pattern': '.',
+    'condition': 'checkout_linux and not build_with_chromium',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=linux*',
+                '--no_auth',
+                '--bucket', 'angle-glslang-validator',
+                '-s', 'tools/glslang/glslang_validator.sha1',
+    ],
+  },
+
+  # Download glslang validator binary for Windows.
+  {
+    'name': 'win_glslang_validator',
+    'pattern': '.',
+    'condition': 'checkout_win and not build_with_chromium',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32*',
+                '--no_auth',
+                '--bucket', 'angle-glslang-validator',
+                '-s', 'tools/glslang/glslang_validator.exe.sha1',
+    ],
+  },
+
+  # Download flex/bison binaries for Linux.
+  {
+    'name': 'linux_flex_bison',
+    'pattern': '.',
+    'condition': 'checkout_linux and not build_with_chromium',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=linux*',
+                '--no_auth',
+                '--bucket', 'angle-flex-bison',
+                '-d', 'tools/flex-bison/linux/',
+    ],
+  },
+
+  # Download flex/bison binaries for Windows.
+  {
+    'name': 'win_flex_bison',
+    'pattern': '.',
+    'condition': 'checkout_win and not build_with_chromium',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32*',
+                '--no_auth',
+                '--bucket', 'angle-flex-bison',
+                '-d', 'tools/flex-bison/windows/',
+    ],
+  },
+]
+
+recursedeps = [
+  # buildtools provides clang_format.
+  'buildtools',
+  'third_party/googletest',
+  'third_party/jsoncpp',
+  'third_party/yasm',
+]
diff --git a/third_party/angle/OWNERS b/third_party/angle/OWNERS
new file mode 100644
index 0000000..57a833b
--- /dev/null
+++ b/third_party/angle/OWNERS
@@ -0,0 +1,9 @@
+# See https://chromium.googlesource.com/angle/angle/+/master/doc/ContributingCode.md#selecting-reviewers for per-file owners
+cwallez@chromium.org
+geofflang@chromium.org
+jmadill@chromium.org
+syoussefi@chromium.org
+ynovikov@chromium.org
+
+# COMPONENT: Internals>GPU>ANGLE
+# TEAM: angleproject@googlegroups.com
diff --git a/third_party/angle/PRESUBMIT.py b/third_party/angle/PRESUBMIT.py
new file mode 100644
index 0000000..1e058c3
--- /dev/null
+++ b/third_party/angle/PRESUBMIT.py
@@ -0,0 +1,176 @@
+# Copyright 2019 The ANGLE Project Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Top-level presubmit script for code generation.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details on the presubmit API built into depot_tools.
+"""
+
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+# Fragment of a regular expression that matches C++ and Objective-C++ implementation files.
+_IMPLEMENTATION_EXTENSIONS = r'\.(cc|cpp|cxx|mm)$'
+
+# Fragment of a regular expression that matches C++ and Objective-C++ header files.
+_HEADER_EXTENSIONS = r'\.(h|hpp|hxx)$'
+
+_PRIMARY_EXPORT_TARGETS = [
+    '//:libEGL',
+    '//:libGLESv1_CM',
+    '//:libGLESv2',
+    '//:translator',
+]
+
+
+def _CheckChangeHasBugField(input_api, output_api):
+    """Requires that the changelist have a Bug: field."""
+    bugs = input_api.change.BugsFromDescription()
+    if not bugs:
+        return [
+            output_api.PresubmitError('Please ensure that your description contains:\n'
+                                      '"Bug: angleproject:[bug number]"\n'
+                                      'directly above the Change-Id tag.')
+        ]
+    elif not all([' ' not in bug for bug in bugs]):
+        return [
+            output_api.PresubmitError(
+                'Check bug tag formatting. Ensure there are no spaces after the colon.')
+        ]
+    else:
+        return []
+
+
+def _CheckCodeGeneration(input_api, output_api):
+
+    class Msg(output_api.PresubmitError):
+        """Specialized error message"""
+
+        def __init__(self, message):
+            super(output_api.PresubmitError, self).__init__(
+                message,
+                long_text='Please ensure your ANGLE repositiory is synced to tip-of-tree\n'
+                'and all ANGLE DEPS are fully up-to-date by running gclient sync.\n'
+                '\n'
+                'If that fails, run scripts/run_code_generation.py to refresh generated hashes.\n'
+                '\n'
+                'If you are building ANGLE inside Chromium you must bootstrap ANGLE\n'
+                'before gclient sync. See the DevSetup documentation for more details.\n')
+
+    code_gen_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
+                                           'scripts/run_code_generation.py')
+    cmd_name = 'run_code_generation'
+    cmd = [input_api.python_executable, code_gen_path, '--verify-no-dirty']
+    test_cmd = input_api.Command(name=cmd_name, cmd=cmd, kwargs={}, message=Msg)
+    if input_api.verbose:
+        print('Running ' + cmd_name)
+    return input_api.RunTests([test_cmd])
+
+
+# Taken directly from Chromium's PRESUBMIT.py
+def _CheckNewHeaderWithoutGnChange(input_api, output_api):
+    """Checks that newly added header files have corresponding GN changes.
+  Note that this is only a heuristic. To be precise, run script:
+  build/check_gn_headers.py.
+  """
+
+    def headers(f):
+        return input_api.FilterSourceFile(f, white_list=(r'.+%s' % _HEADER_EXTENSIONS,))
+
+    new_headers = []
+    for f in input_api.AffectedSourceFiles(headers):
+        if f.Action() != 'A':
+            continue
+        new_headers.append(f.LocalPath())
+
+    def gn_files(f):
+        return input_api.FilterSourceFile(f, white_list=(r'.+\.gn',))
+
+    all_gn_changed_contents = ''
+    for f in input_api.AffectedSourceFiles(gn_files):
+        for _, line in f.ChangedContents():
+            all_gn_changed_contents += line
+
+    problems = []
+    for header in new_headers:
+        basename = input_api.os_path.basename(header)
+        if basename not in all_gn_changed_contents:
+            problems.append(header)
+
+    if problems:
+        return [
+            output_api.PresubmitPromptWarning(
+                'Missing GN changes for new header files',
+                items=sorted(problems),
+                long_text='Please double check whether newly added header files need '
+                'corresponding changes in gn or gni files.\nThis checking is only a '
+                'heuristic. Run build/check_gn_headers.py to be precise.\n'
+                'Read https://crbug.com/661774 for more info.')
+        ]
+    return []
+
+
+def _CheckExportValidity(input_api, output_api):
+    outdir = tempfile.mkdtemp()
+    # shell=True is necessary on Windows, as otherwise subprocess fails to find
+    # either 'gn' or 'vpython3' even if they are findable via PATH.
+    use_shell = input_api.is_windows
+    try:
+        try:
+            subprocess.check_output(['gn', 'gen', outdir], shell=use_shell)
+        except subprocess.CalledProcessError as e:
+            return [
+                output_api.PresubmitError(
+                    'Unable to run gn gen for export_targets.py: %s' % e.output)
+            ]
+        export_target_script = os.path.join(input_api.PresubmitLocalPath(), 'scripts',
+                                            'export_targets.py')
+        try:
+            subprocess.check_output(
+                ['vpython3', export_target_script, outdir] + _PRIMARY_EXPORT_TARGETS,
+                stderr=subprocess.STDOUT,
+                shell=use_shell)
+        except subprocess.CalledProcessError as e:
+            if input_api.is_committing:
+                return [output_api.PresubmitError('export_targets.py failed: %s' % e.output)]
+            return [
+                output_api.PresubmitPromptWarning(
+                    'export_targets.py failed, this may just be due to your local checkout: %s' %
+                    e.output)
+            ]
+        return []
+    finally:
+        shutil.rmtree(outdir)
+
+
+def CheckChangeOnUpload(input_api, output_api):
+    results = []
+    # Disabled in Cobalt.
+    # results.extend(_CheckCodeGeneration(input_api, output_api))
+    # results.extend(_CheckChangeHasBugField(input_api, output_api))
+    results.extend(input_api.canned_checks.CheckChangeHasDescription(input_api, output_api))
+    results.extend(_CheckNewHeaderWithoutGnChange(input_api, output_api))
+    # Disabled in Cobalt.
+    # results.extend(_CheckExportValidity(input_api, output_api))
+    # results.extend(
+    #     input_api.canned_checks.CheckPatchFormatted(
+    #         input_api, output_api, result_factory=output_api.PresubmitError))
+    return results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+    results = []
+    # Disabled in Cobalt.
+    # results.extend(_CheckCodeGeneration(input_api, output_api))
+    results.extend(
+        input_api.canned_checks.CheckPatchFormatted(
+            input_api, output_api, result_factory=output_api.PresubmitError))
+    # Disabled in Cobalt.
+    # results.extend(_CheckChangeHasBugField(input_api, output_api))
+    # results.extend(_CheckExportValidity(input_api, output_api))
+    results.extend(input_api.canned_checks.CheckChangeHasDescription(input_api, output_api))
+    return results
diff --git a/third_party/angle/android/OWNERS b/third_party/angle/android/OWNERS
new file mode 100644
index 0000000..f38b366
--- /dev/null
+++ b/third_party/angle/android/OWNERS
@@ -0,0 +1,6 @@
+alanward@google.com
+cnorthrop@google.com
+courtneygo@google.com
+ianelliott@google.com
+timvp@google.com
+tobine@google.com
\ No newline at end of file
diff --git a/third_party/angle/infra/config/OWNERS b/third_party/angle/infra/config/OWNERS
new file mode 100644
index 0000000..9a1736e
--- /dev/null
+++ b/third_party/angle/infra/config/OWNERS
@@ -0,0 +1,2 @@
+geofflang@chromium.org
+jmadill@chromium.org
diff --git a/third_party/angle/infra/config/PRESUBMIT.py b/third_party/angle/infra/config/PRESUBMIT.py
new file mode 100644
index 0000000..0c43ad1
--- /dev/null
+++ b/third_party/angle/infra/config/PRESUBMIT.py
@@ -0,0 +1,11 @@
+# Copyright 2019 The ANGLE Project Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+def CheckChangeOnUpload(input_api, output_api):
+    return input_api.canned_checks.CheckChangedLUCIConfigs(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+    return input_api.canned_checks.CheckChangedLUCIConfigs(input_api, output_api)
diff --git a/third_party/angle/infra/config/branch/OWNERS b/third_party/angle/infra/config/branch/OWNERS
new file mode 100644
index 0000000..9a1736e
--- /dev/null
+++ b/third_party/angle/infra/config/branch/OWNERS
@@ -0,0 +1,2 @@
+geofflang@chromium.org
+jmadill@chromium.org
diff --git a/third_party/angle/infra/config/global/OWNERS b/third_party/angle/infra/config/global/OWNERS
new file mode 100644
index 0000000..9a1736e
--- /dev/null
+++ b/third_party/angle/infra/config/global/OWNERS
@@ -0,0 +1,2 @@
+geofflang@chromium.org
+jmadill@chromium.org
diff --git a/third_party/angle/src/common/system_utils_winuwp.cpp b/third_party/angle/src/common/system_utils_winuwp.cpp
index 24b60c5..ac60e08 100644
--- a/third_party/angle/src/common/system_utils_winuwp.cpp
+++ b/third_party/angle/src/common/system_utils_winuwp.cpp
@@ -16,7 +16,7 @@
 #include <string>
 #include <vector>
 
-#include "common/debug.h"
+#include "common/debug.h"  // nogncheck
 
 namespace angle
 {
diff --git a/third_party/angle/src/common/third_party/base/anglebase/numerics/OWNERS b/third_party/angle/src/common/third_party/base/anglebase/numerics/OWNERS
new file mode 100644
index 0000000..41f35fc
--- /dev/null
+++ b/third_party/angle/src/common/third_party/base/anglebase/numerics/OWNERS
@@ -0,0 +1,3 @@
+jschuh@chromium.org
+tsepez@chromium.org
+
diff --git a/third_party/angle/tools/flex-bison/third_party/.gitattributes b/third_party/angle/tools/flex-bison/third_party/.gitattributes
new file mode 100644
index 0000000..0688c8a
--- /dev/null
+++ b/third_party/angle/tools/flex-bison/third_party/.gitattributes
@@ -0,0 +1 @@
+*                    eol=lf
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index 47bb81f..430b77e 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -161,3 +161,27 @@
     configs += [ "//starboard/build/config:speed" ]
   }
 }
+
+target(final_executable_type, "crypto_tool") {
+  sources = [
+    "src/tool/args.cc",
+    "src/tool/ciphers.cc",
+    "src/tool/const.cc",
+    "src/tool/digest.cc",
+    "src/tool/file.cc",
+    "src/tool/generate_ed25519.cc",
+    "src/tool/genrsa.cc",
+    "src/tool/pkcs12.cc",
+    "src/tool/rand.cc",
+    "src/tool/sign.cc",
+    "src/tool/speed.cc",
+    "src/tool/tool.cc",
+  ]
+  include_dirs = [ "src/include" ]
+  defines = [ "OPENSSL_NO_SOCK" ]
+  deps = [
+    ":crypto",
+    "//starboard",
+  ]
+  content_deps = [ "//third_party/icu:icudata" ]
+}
diff --git a/third_party/boringssl/OWNERS b/third_party/boringssl/OWNERS
new file mode 100644
index 0000000..77896b3
--- /dev/null
+++ b/third_party/boringssl/OWNERS
@@ -0,0 +1,6 @@
+agl@chromium.org
+davidben@chromium.org
+rsleevi@chromium.org
+svaldez@chromium.org
+
+# COMPONENT: Internals>Network>SSL
diff --git a/third_party/boringssl/src/util/bot/DEPS b/third_party/boringssl/src/util/bot/DEPS
new file mode 100644
index 0000000..5dab203
--- /dev/null
+++ b/third_party/boringssl/src/util/bot/DEPS
@@ -0,0 +1,212 @@
+# Copyright (c) 2015, Google Inc.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+vars = {
+  'chromium_git': 'https://chromium.googlesource.com',
+
+  'checkout_clang': False,
+  'checkout_fuzzer': False,
+  'checkout_sde': False,
+  'checkout_nasm': False,
+  'checkout_libcxx': False,
+}
+
+deps = {
+  'boringssl/util/bot/android_ndk': {
+    'url': Var('chromium_git') + '/android_ndk.git' + '@' + '5cd86312e794bdf542a3685c6f10cbb96072990b',
+    'condition': 'checkout_android',
+  },
+
+  'boringssl/util/bot/android_tools': {
+    'url': Var('chromium_git') + '/android_tools.git' + '@' + '130499e25286f4d56acafa252fee09f3cc595c49',
+    'condition': 'checkout_android',
+  },
+
+  'boringssl/util/bot/gyp':
+    Var('chromium_git') + '/external/gyp.git' + '@' + 'd61a9397e668fa9843c4aa7da9e79460fe590bfb',
+
+  'boringssl/util/bot/libFuzzer': {
+    'url': Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git' + '@' + '658ff786a213703ff0df6ba4a288e9a1e218c074',
+    'condition': 'checkout_fuzzer',
+  },
+
+  # Update the following revisions from
+  # https://chromium.googlesource.com/chromium/buildtools/+/master/DEPS
+  'boringssl/util/bot/libcxx': {
+    'url': Var('chromium_git') + '/chromium/llvm-project/libcxx.git' + '@' + '85a7702b4cc5d69402791fe685f151cf3076be71',
+    'condition': 'checkout_libcxx',
+  },
+  'boringssl/util/bot/libcxxabi': {
+    'url': Var('chromium_git') + '/chromium/llvm-project/libcxxabi.git' + '@' + '05a73941f3fb177c4a891d4ff2a4ed5785e3b80c',
+    'condition': 'checkout_libcxx',
+  },
+}
+
+recursedeps = [
+  # android_tools pulls in the NDK from a separate repository.
+  'boringssl/util/bot/android_tools',
+]
+
+hooks = [
+  {
+    'name': 'cmake_linux64',
+    'pattern': '.',
+    'condition': 'host_os == "linux"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=linux*',
+                '--no_auth',
+                '--bucket', 'chromium-tools',
+                '-s', 'boringssl/util/bot/cmake-linux64.tar.gz.sha1',
+    ],
+  },
+  {
+    'name': 'cmake_linux64_extract',
+    'pattern': '.',
+    'condition': 'host_os == "linux"',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                'boringssl/util/bot/cmake-linux64.tar.gz',
+                'boringssl/util/bot/cmake-linux64/',
+    ],
+  },
+  {
+    'name': 'cmake_mac',
+    'pattern': '.',
+    'condition': 'host_os == "mac"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=darwin',
+                '--no_auth',
+                '--bucket', 'chromium-tools',
+                '-s', 'boringssl/util/bot/cmake-mac.tar.gz.sha1',
+    ],
+  },
+  {
+    'name': 'cmake_mac_extract',
+    'pattern': '.',
+    'condition': 'host_os == "mac"',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                'boringssl/util/bot/cmake-mac.tar.gz',
+                'boringssl/util/bot/cmake-mac/',
+    ],
+  },
+  {
+    'name': 'cmake_win32',
+    'pattern': '.',
+    'condition': 'host_os == "win"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32',
+                '--no_auth',
+                '--bucket', 'chromium-tools',
+                '-s', 'boringssl/util/bot/cmake-win32.zip.sha1',
+    ],
+  },
+  {
+    'name': 'cmake_win32_extract',
+    'pattern': '.',
+    'condition': 'host_os == "win"',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                'boringssl/util/bot/cmake-win32.zip',
+                'boringssl/util/bot/cmake-win32/',
+    ],
+  },
+  {
+    'name': 'perl_win32',
+    'pattern': '.',
+    'condition': 'host_os == "win"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32',
+                '--no_auth',
+                '--bucket', 'chromium-tools',
+                '-s', 'boringssl/util/bot/perl-win32.zip.sha1',
+    ],
+  },
+  {
+    'name': 'perl_win32_extract',
+    'pattern': '.',
+    'condition': 'host_os == "win"',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                '--no-prefix',
+                'boringssl/util/bot/perl-win32.zip',
+                'boringssl/util/bot/perl-win32/',
+    ],
+  },
+  {
+    'name': 'yasm_win32',
+    'pattern': '.',
+    'condition': 'host_os == "win" and not checkout_nasm',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32',
+                '--no_auth',
+                '--bucket', 'chromium-tools',
+                '-s', 'boringssl/util/bot/yasm-win32.exe.sha1',
+    ],
+  },
+  {
+    'name': 'nasm_win32',
+    'pattern': '.',
+    'condition': 'host_os == "win" and checkout_nasm',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--platform=win32',
+                '--no_auth',
+                '--bucket', 'chromium-tools',
+                '-s', 'boringssl/util/bot/nasm-win32.exe.sha1',
+    ],
+  },
+  {
+    'name': 'win_toolchain',
+    'pattern': '.',
+    'condition': 'host_os == "win"',
+    'action': [ 'python',
+                'boringssl/util/bot/vs_toolchain.py',
+                'update',
+    ],
+  },
+  {
+    'name': 'clang',
+    'pattern': '.',
+    'condition': 'checkout_clang',
+    'action': [ 'python',
+                'boringssl/util/bot/update_clang.py',
+    ],
+  },
+  {
+    'name': 'sde_linux64',
+    'pattern': '.',
+    'condition': 'checkout_sde and host_os == "linux"',
+    'action': [ 'download_from_google_storage',
+                '--no_resume',
+                '--bucket', 'chrome-boringssl-sde',
+                '-s', 'boringssl/util/bot/sde-linux64.tar.bz2.sha1'
+    ],
+  },
+  {
+    'name': 'sde_linux64_extract',
+    'pattern': '.',
+    'condition': 'checkout_sde and host_os == "linux"',
+    'action': [ 'python',
+                'boringssl/util/bot/extract.py',
+                'boringssl/util/bot/sde-linux64.tar.bz2',
+                'boringssl/util/bot/sde-linux64/',
+    ],
+  },
+]
diff --git a/third_party/brotli/.gitmodules b/third_party/brotli/.gitmodules
new file mode 100644
index 0000000..af7df38
--- /dev/null
+++ b/third_party/brotli/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "research/esaxx"]
+	path = research/esaxx
+	url = https://github.com/hillbig/esaxx
diff --git a/third_party/chromium/media/BUILD.gn b/third_party/chromium/media/BUILD.gn
new file mode 100644
index 0000000..d5c158c
--- /dev/null
+++ b/third_party/chromium/media/BUILD.gn
@@ -0,0 +1,463 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/buildflag_header.gni")
+import("//build/config/android/config.gni")
+import("//build/config/arm.gni")
+import("//build/config/chromecast_build.gni")
+import("//build/config/features.gni")
+import("//build/config/linux/pkg_config.gni")
+import("//build/config/ui.gni")
+import("//media/cdm/library_cdm/cdm_paths.gni")
+import("//media/media_options.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
+import("//testing/test.gni")
+import("//third_party/ffmpeg/ffmpeg_options.gni")
+
+buildflag_header("media_buildflags") {
+  header = "media_buildflags.h"
+
+  flags = [
+    "ALTERNATE_CDM_STORAGE_ID_KEY=\"$alternate_cdm_storage_id_key\"",
+    "CDM_PLATFORM_SPECIFIC_PATH=\"$cdm_platform_specific_path\"",
+    "ENABLE_PLATFORM_AC3_EAC3_AUDIO=$enable_platform_ac3_eac3_audio",
+    "ENABLE_CAST_AUDIO_RENDERER=$enable_cast_audio_renderer",
+    "ENABLE_CDM_HOST_VERIFICATION=$enable_cdm_host_verification",
+    "ENABLE_CDM_STORAGE_ID=$enable_cdm_storage_id",
+    "ENABLE_DAV1D_DECODER=$enable_dav1d_decoder",
+    "ENABLE_AV1_DECODER=$enable_av1_decoder",
+    "ENABLE_PLATFORM_DOLBY_VISION=$enable_platform_dolby_vision",
+    "ENABLE_FFMPEG=$media_use_ffmpeg",
+    "ENABLE_FFMPEG_VIDEO_DECODERS=$enable_ffmpeg_video_decoders",
+    "ENABLE_PLATFORM_HEVC=$enable_platform_hevc",
+    "ENABLE_PLATFORM_HEVC_DECODING=$enable_platform_hevc_decoding",
+    "ENABLE_PLATFORM_ENCRYPTED_HEVC=$enable_platform_encrypted_hevc",
+    "ENABLE_HLS_SAMPLE_AES=$enable_hls_sample_aes",
+    "ENABLE_LIBGAV1_DECODER=$enable_libgav1_decoder",
+    "ENABLE_LIBRARY_CDMS=$enable_library_cdms",
+    "ENABLE_LIBVPX=$media_use_libvpx",
+    "ENABLE_LOGGING_OVERRIDE=$enable_logging_override",
+    "ENABLE_MEDIA_DRM_STORAGE=$enable_media_drm_storage",
+    "ENABLE_MEDIA_REMOTING=$enable_media_remoting",
+    "ENABLE_MEDIA_REMOTING_RPC=$enable_media_remoting_rpc",
+    "ENABLE_OPENH264=$media_use_openh264",
+    "ENABLE_PLATFORM_MPEG_H_AUDIO=$enable_platform_mpeg_h_audio",
+    "ENABLE_MSE_MPEG2TS_STREAM_PARSER=$enable_mse_mpeg2ts_stream_parser",
+    "ENABLE_CAST_STREAMING_RENDERER=$enable_cast_streaming_renderer",
+    "USE_CHROMEOS_MEDIA_ACCELERATION=$use_vaapi||$use_v4l2_codec",
+    "USE_CHROMEOS_PROTECTED_AV1=$use_chromeos_protected_av1",
+    "USE_CHROMEOS_PROTECTED_MEDIA=$use_chromeos_protected_media",
+    "USE_PROPRIETARY_CODECS=$proprietary_codecs",
+  ]
+}
+
+if (proprietary_codecs && media_use_ffmpeg) {
+  assert(
+      ffmpeg_branding != "Chromium",
+      "proprietary codecs and ffmpeg_branding set to Chromium are incompatible")
+}
+
+# Common configuration for targets in the media directory; these must not be
+# exported since things like USE_NEON and USE_CRAS have different meanings
+# elsewhere in the code base.
+config("media_config") {
+  defines = []
+  if (current_cpu == "arm64" || (current_cpu == "arm" && arm_use_neon)) {
+    defines += [ "USE_NEON" ]
+  }
+  if (use_pulseaudio) {
+    defines += [ "USE_PULSEAUDIO" ]
+    if (!link_pulseaudio) {
+      defines += [ "DLOPEN_PULSEAUDIO" ]
+    }
+  }
+  if (use_cras) {
+    defines += [ "USE_CRAS" ]
+  }
+}
+
+# Internal grouping of the configs necessary to support sub-folders having their
+# own BUILD.gn files; only targets which roll up into the "media" target should
+# include this config. I.e., not "test_support" or "unit_tests" targets.
+#
+# Without these configs having individual sub-folders take a //media/base DEP
+# (or others) can yield incorrectly imported and exported symbols on Windows:
+#
+#    fatal error LNK1169: one or more multiply defined symbols found.
+#
+config("subcomponent_config") {
+  visibility = media_subcomponent_deps
+  if (is_mac) {
+    visibility += [ "//media/base/mac" ]
+  }
+  defines = [ "IS_MEDIA_IMPL" ]
+  configs = [
+    ":media_config",
+    "//build/config/compiler:wexit_time_destructors",
+  ]
+}
+
+component("media") {
+  libs = []
+
+  deps = [
+    "//base",
+    "//base:i18n",
+    "//base/third_party/dynamic_annotations",
+    "//cc/paint",
+    "//crypto:platform",
+    "//gpu/command_buffer/client:gles2_interface",
+    "//gpu/command_buffer/common",
+    "//third_party/libyuv",
+    "//ui/events:events_base",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+    "//ui/gl:gl",
+    "//url",
+  ]
+
+  public_configs = [ "//third_party/libwebm:libwebm_config" ]
+  public_deps = media_subcomponent_deps
+  public_deps += [
+    ":media_buildflags",
+    ":shared_memory_support",
+    "//ui/gfx:color_space",
+  ]
+
+  # This must be included here since it actually depends on //media/base.
+  if (is_apple) {
+    public_deps += [ "//media/base/mac" ]
+  }
+
+  if (use_ozone) {
+    deps += [ "//ui/ozone" ]
+  }
+}
+
+# Note: This can't be a static_library since it does not have any sources.
+source_set("test_support") {
+  testonly = true
+  public_deps = [
+    ":media",
+    "//media/audio:test_support",
+    "//media/base:test_support",
+    "//media/base/android:test_support",
+    "//media/filters:test_support",
+    "//media/formats:test_support",
+    "//media/renderers:test_support",
+    "//media/video:test_support",
+  ]
+}
+
+# Contains tests for all targets in the "media" folder.
+# TODO(xhwang): Move mojo/capture/remoting tests here where applicable.
+test("media_unittests") {
+  use_xvfb = use_xvfb_in_this_config
+
+  deps = [
+    "//media/audio:unit_tests",
+    "//media/base:unit_tests",
+    "//media/capabilities:unit_tests",
+    "//media/cast/receiver:unit_tests",
+    "//media/cdm:unit_tests",
+    "//media/device_monitors:unit_tests",
+    "//media/filters:unit_tests",
+    "//media/formats:unit_tests",
+    "//media/gpu:unit_tests",
+    "//media/learning:unit_tests",
+    "//media/mojo:unit_tests",
+    "//media/muxers:unit_tests",
+    "//media/parsers:unit_tests",
+    "//media/renderers:unit_tests",
+    "//media/test:pipeline_integration_tests",
+    "//media/test:run_all_unittests",
+    "//media/video:unit_tests",
+    "//media/webrtc:unit_tests",
+  ]
+
+  data = [
+    "test/data/",
+    "formats/mp4/h264_annex_b_fuzz_corpus/",
+  ]
+
+  data_deps = [ "//testing/buildbot/filters:media_unittests_filters" ]
+
+  if (media_use_ffmpeg) {
+    deps += [ "//media/ffmpeg:unit_tests" ]
+  }
+
+  if (is_android) {
+    deps += [
+      # The test needs the java dependencies to add the java classes for their
+      # native counterparts to the test apk.
+      "//gpu/command_buffer/service:android_texture_owner_unittests",
+      "//media/base/android:media_java",
+      "//media/base/android:unit_tests",
+      "//media/gpu:android_video_decode_accelerator_unittests",
+      "//ui/android:ui_java",
+    ]
+  }
+
+  if (is_fuchsia) {
+    deps += [
+      "//media/fuchsia/audio:unittests",
+      "//media/fuchsia/cdm/service:unittests",
+    ]
+
+    additional_manifest_fragments = [
+      "//build/config/fuchsia/test/audio_capabilities.test-cmx",
+
+      # TODO(crbug.com/1185811): Figure out why
+      # PaintCanvasVideoRendererWithGLTest.CopyVideoFrameYUVDataToGLTexture
+      # crashes without "deprecated-ambient-replace-as-executable".
+      "//build/config/fuchsia/test/jit_capabilities.test-cmx",
+
+      "//build/config/fuchsia/test/vulkan_capabilities.test-cmx",
+    ]
+  }
+
+  if (enable_media_remoting) {
+    deps += [ "//media/remoting:media_remoting_tests" ]
+  }
+
+  # The test needs OPUS_FIXED_POINT conditional define.
+  configs += [ "//third_party/opus:opus_config" ]
+}
+
+test("media_perftests") {
+  configs += [ ":media_config" ]
+  deps = [
+    ":test_support",
+    "//base/test:test_support",
+    "//media/base:perftests",
+    "//media/filters:perftests",
+    "//media/test:pipeline_integration_perftests",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//testing/perf",
+    "//third_party/widevine/cdm:headers",
+    "//ui/gfx:test_support",
+  ]
+  if (media_use_ffmpeg) {
+    # Direct dependency required to inherit config.
+    deps += [ "//third_party/ffmpeg" ]
+  }
+
+  # This target should not require the Chrome executable to run.
+  assert_no_deps = [ "//chrome" ]
+
+  data = [ "test/data/" ]
+
+  data_deps = [
+    # Needed for isolate script to execute.
+    "//testing:run_perf_test",
+  ]
+}
+
+# The audio subset of media_unittests. This target exists for running only the
+# audio tests on the GPU bots (which have audio hardware).
+test("audio_unittests") {
+  deps = [
+    ":test_support",
+    "//base/test:test_support",
+    "//media/audio:unit_tests",
+    "//media/test:run_all_unittests",
+  ]
+  if (is_android) {
+    deps += [
+      # The test needs the java dependencies to add the java classes for their
+      # native counterparts to the test apk.
+      "//media/base/android:media_java",
+      "//ui/android:ui_java",
+    ]
+  }
+}
+
+# Note: Most external components should just depend on //media unless they
+# specifically need this pared own target (NaCl, PPAPI, etc). Internal targets
+# should just depend on //media/base which will propagate this target to them.
+component("shared_memory_support") {
+  sources = [
+    "base/audio_bus.cc",
+    "base/audio_bus.h",
+    "base/audio_latency.cc",
+    "base/audio_latency.h",
+    "base/audio_parameters.cc",
+    "base/audio_parameters.h",
+    "base/audio_point.cc",
+    "base/audio_point.h",
+    "base/audio_sample_types.h",
+    "base/channel_layout.cc",
+    "base/channel_layout.h",
+    "base/limits.h",
+    "base/media_shmem_export.h",
+    "base/sample_format.cc",
+    "base/sample_format.h",
+    "base/vector_math.cc",
+    "base/vector_math.h",
+    "base/vector_math_testing.h",
+    "base/video_types.cc",
+    "base/video_types.h",
+  ]
+  if (is_mac) {
+    # These need to be included here because audio_latency.cc depends on them.
+    sources += [
+      "base/mac/audio_latency_mac.cc",
+      "base/mac/audio_latency_mac.h",
+    ]
+  }
+
+  # Do not use "subcomponent_config" here since these files are in their own
+  # component target and thus can't share the standard export macros.
+  configs += [ ":media_config" ]
+  defines = [ "MEDIA_SHMEM_IMPLEMENTATION" ]
+
+  if (!is_debug) {
+    configs -= [ "//build/config/compiler:default_optimization" ]
+    configs += [ "//build/config/compiler:optimize_max" ]
+  }
+  deps = [
+    "//base",
+    "//build:chromeos_buildflags",
+    "//ui/gfx/geometry",
+  ]
+}
+
+# TODO(watk): Refactor tests that could be made to run on Android. See
+# http://crbug.com/570762
+if (media_use_ffmpeg && !is_android) {
+  test("ffmpeg_regression_tests") {
+    configs += [ "//media:media_config" ]
+
+    deps = [
+      ":test_support",
+      "//base/test:test_support",
+      "//media/ffmpeg:ffmpeg_regression_tests",
+      "//media/test:pipeline_integration_tests",
+      "//media/test:run_all_unittests",
+      "//testing/gmock",
+      "//testing/gtest",
+      "//ui/gfx:test_support",
+      "//ui/gfx/geometry",
+    ]
+  }
+}
+
+if (proprietary_codecs) {
+  fuzzer_test("media_cenc_utils_fuzzer") {
+    sources = [ "cdm/cenc_utils_fuzzertest.cc" ]
+    deps = [ ":media" ]
+  }
+}
+
+fuzzer_test("media_vp9_parser_fuzzer") {
+  sources = [ "filters/vp9_parser_fuzzertest.cc" ]
+  deps = [
+    ":test_support",
+    "//base",
+  ]
+  libfuzzer_options = [ "max_len = 400000" ]
+}
+
+fuzzer_test("media_vp9_parser_encrypted_fuzzer") {
+  sources = [ "filters/vp9_parser_encrypted_fuzzertest.cc" ]
+  deps = [
+    ":test_support",
+    "//base",
+    "//base/test:test_support",
+  ]
+  seed_corpus = "//media/test/data"
+}
+
+fuzzer_test("media_vpx_video_decoder_fuzzer") {
+  sources = [ "filters/vpx_video_decoder_fuzzertest.cc" ]
+  deps = [
+    ":media",
+    "//base",
+    "//base/test:test_support",
+  ]
+  libfuzzer_options = [ "max_len = 400000" ]
+  seed_corpus = "//media/test/data"
+}
+
+fuzzer_test("media_webm_muxer_fuzzer") {
+  sources = [ "muxers/webm_muxer_fuzzertest.cc" ]
+  deps = [
+    ":media",
+    "//base",
+    "//third_party/libwebm",
+  ]
+}
+
+fuzzer_test("cbcs_decryptor_fuzzer") {
+  sources = [ "cdm/cbcs_decryptor_fuzzer.cc" ]
+  deps = [
+    ":media",
+    "//base",
+    "//crypto",
+  ]
+}
+
+fuzzer_test("cenc_decryptor_fuzzer") {
+  sources = [ "cdm/cenc_decryptor_fuzzer.cc" ]
+  deps = [
+    ":media",
+    "//base",
+    "//crypto",
+  ]
+}
+
+fuzzer_test("json_web_key_fuzzer") {
+  sources = [ "cdm/json_web_key_fuzzer.cc" ]
+  deps = [
+    ":media",
+    "//base",
+  ]
+}
+
+if (proprietary_codecs) {
+  fuzzer_test("media_mp4_avcc_parser_fuzzer") {
+    sources = [ "formats/mp4/mp4_avcc_parser_fuzzer.cc" ]
+    deps = [
+      ":media",
+      "//base",
+    ]
+  }
+
+  fuzzer_test("media_mp4_box_reader_fuzzer") {
+    sources = [ "formats/mp4/mp4_box_reader_fuzzer.cc" ]
+    deps = [
+      ":media",
+      "//base",
+    ]
+    libfuzzer_options = [ "max_len=500" ]
+    dict = "test/mp4.dict"
+  }
+}
+
+if (enable_mse_mpeg2ts_stream_parser) {
+  fuzzer_test("media_es_parser_adts_fuzzer") {
+    sources = [ "formats/mp2t/es_parser_adts_fuzzer.cc" ]
+    deps = [
+      ":media",
+      "//base",
+    ]
+  }
+
+  fuzzer_test("media_es_parser_h264_fuzzer") {
+    sources = [ "formats/mp2t/es_parser_h264_fuzzer.cc" ]
+    deps = [
+      ":media",
+      "//base",
+    ]
+  }
+
+  fuzzer_test("media_es_parser_mpeg1audio_fuzzer") {
+    sources = [ "formats/mp2t/es_parser_mpeg1audio_fuzzer.cc" ]
+    deps = [
+      ":media",
+      "//base",
+    ]
+  }
+}
diff --git a/third_party/chromium/media/COMMON_METADATA b/third_party/chromium/media/COMMON_METADATA
new file mode 100644
index 0000000..0198eda
--- /dev/null
+++ b/third_party/chromium/media/COMMON_METADATA
@@ -0,0 +1,3 @@
+monorail {
+  component: "Internals>Media"
+}
\ No newline at end of file
diff --git a/third_party/chromium/media/DEPS b/third_party/chromium/media/DEPS
new file mode 100644
index 0000000..1804063
--- /dev/null
+++ b/third_party/chromium/media/DEPS
@@ -0,0 +1,56 @@
+# Do NOT add net/ or ui/base without a great reason, they're huge!
+include_rules = [
+  "+cc/base/math_util.h",
+  "+cc/paint",
+  "+components/crash/core/common/crash_key.h",
+  "+components/system_media_controls/linux/buildflags",
+  "+crypto",
+  "+device/udev_linux",
+  "+gpu",
+  "+media/midi/midi_jni_headers",
+  "+mojo/public/cpp/bindings/callback_helpers.h",
+  "+mojo/public/cpp/system/platform_handle.h",
+  "+net/cookies/site_for_cookies.h",
+  "+services/device/public",
+  "+services/viz/public/cpp/gpu/context_provider_command_buffer.h",
+  "+skia/ext",
+  "+third_party/dav1d",
+  "+third_party/ffmpeg",
+  "+third_party/libgav1",
+  "+third_party/libvpx",
+  "+third_party/libyuv",
+  "+third_party/openh264/src/codec/api/svc",
+  "+third_party/opus",
+  "+third_party/skia",
+  "+ui/base/ui_base_features.h",
+  "+ui/base/x/x11_user_input_monitor.h",
+  "+ui/display",
+  "+ui/events",
+  "+ui/gfx",
+  "+ui/gl",
+  "+ui/ozone",
+  "+third_party/widevine/cdm/widevine_cdm_common.h",
+  "-ipc",
+  "-media/webrtc",
+]
+
+specific_include_rules = {
+  "audio_manager_unittest.cc": [
+    "+chromeos/dbus"
+  ],
+  "cras_input_unittest.cc": [
+    "+chromeos/dbus"
+  ],
+  "cras_unified_unittest.cc": [
+    "+chromeos/dbus"
+  ],
+  "fuchsia_video_decoder_unittest.cc": [
+    "+components/viz/test/test_context_support.h",
+  ],
+  "gpu_memory_buffer_video_frame_pool_unittest.cc": [
+    "+components/viz/test/test_context_provider.h",
+  ],
+  "null_video_sink_unittest.cc": [
+    "+components/viz/common/frame_sinks/begin_frame_args.h",
+  ],
+}
diff --git a/third_party/chromium/media/DIR_METADATA b/third_party/chromium/media/DIR_METADATA
new file mode 100644
index 0000000..69cd629
--- /dev/null
+++ b/third_party/chromium/media/DIR_METADATA
@@ -0,0 +1,9 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+mixins: "//media/COMMON_METADATA"
diff --git a/third_party/chromium/media/OWNERS b/third_party/chromium/media/OWNERS
new file mode 100644
index 0000000..262c8c2
--- /dev/null
+++ b/third_party/chromium/media/OWNERS
@@ -0,0 +1,28 @@
+# NOTE: Do not use these owners when you're in a subdirectory that has
+# OWNERS file. For example:
+# - cast
+# - midi
+# - ozone
+# - capture/{content,video}
+# Instead prefer the OWNERS in the subdirectory as they will be more familiar,
+# and to load balance. Only use OWNERS in this file for these subdirectories
+# when doing refactorings and general cleanups.
+
+set noparent
+cassew@google.com
+chcunningham@chromium.org
+dalecurtis@chromium.org
+eugene@chromium.org
+jrummell@chromium.org
+liberato@chromium.org
+sandersd@chromium.org
+tguilbert@chromium.org
+tmathmeyer@chromium.org
+wolenetz@chromium.org
+xhwang@chromium.org
+
+# For Fuchsia-specific changes:
+per-file ..._fuchsia*=file://build/fuchsia/OWNERS
+
+# For GpuMemoryBuffer-related changes:
+per-file *gpu_memory_buffer*=dcastagna@chromium.org
diff --git a/third_party/chromium/media/PRESUBMIT.py b/third_party/chromium/media/PRESUBMIT.py
new file mode 100644
index 0000000..d3c636d
--- /dev/null
+++ b/third_party/chromium/media/PRESUBMIT.py
@@ -0,0 +1,249 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Top-level presubmit script for Chromium media component.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+import re
+
+# This line is 'magic' in that git-cl looks for it to decide whether to
+# use Python3 instead of Python2 when running the code in this file.
+USE_PYTHON3 = True
+
+# Well-defined simple classes containing only <= 4 ints, or <= 2 floats.
+BASE_TIME_TYPES = [
+    'base::Time',
+    'base::TimeDelta',
+    'base::TimeTicks',
+]
+
+BASE_TIME_TYPES_RE = re.compile(r'\bconst (%s)&' % '|'.join(BASE_TIME_TYPES))
+
+def _FilterFile(affected_file):
+  """Return true if the file could contain code requiring a presubmit check."""
+  return affected_file.LocalPath().endswith(
+      ('.h', '.cc', '.cpp', '.cxx', '.mm'))
+
+
+def _CheckForUseOfWrongClock(input_api, output_api):
+  """Make sure new lines of media code don't use a clock susceptible to skew."""
+
+  # Regular expression that should detect any explicit references to the
+  # base::Time type (or base::Clock/DefaultClock), whether in using decls,
+  # typedefs, or to call static methods.
+  base_time_type_pattern = r'(^|\W)base::(Time|Clock|DefaultClock)(\W|$)'
+
+  # Regular expression that should detect references to the base::Time class
+  # members, such as a call to base::Time::Now.
+  base_time_member_pattern = r'(^|\W)(Time|Clock|DefaultClock)::'
+
+  # Regular expression to detect "using base::Time" declarations.  We want to
+  # prevent these from triggerring a warning.  For example, it's perfectly
+  # reasonable for code to be written like this:
+  #
+  #   using base::Time;
+  #   ...
+  #   int64_t foo_us = foo_s * Time::kMicrosecondsPerSecond;
+  using_base_time_decl_pattern = r'^\s*using\s+(::)?base::Time\s*;'
+
+  # Regular expression to detect references to the kXXX constants in the
+  # base::Time class.  We want to prevent these from triggerring a warning.
+  base_time_konstant_pattern = r'(^|\W)Time::k\w+'
+
+  problem_re = input_api.re.compile(
+      r'(' + base_time_type_pattern + r')|(' + base_time_member_pattern + r')')
+  exception_re = input_api.re.compile(
+      r'(' + using_base_time_decl_pattern + r')|(' +
+      base_time_konstant_pattern + r')')
+  problems = []
+  for f in input_api.AffectedSourceFiles(_FilterFile):
+    for line_number, line in f.ChangedContents():
+      if problem_re.search(line):
+        if not exception_re.search(line):
+          problems.append(
+              '  %s:%d\n    %s' % (f.LocalPath(), line_number, line.strip()))
+
+  if problems:
+    return [output_api.PresubmitPromptOrNotify(
+        'You added one or more references to the base::Time class and/or one\n'
+        'of its member functions (or base::Clock/DefaultClock). In media\n'
+        'code, it is rarely correct to use a clock susceptible to time skew!\n'
+        'Instead, could you use base::TimeTicks to track the passage of\n'
+        'real-world time?\n\n' +
+        '\n'.join(problems))]
+  else:
+    return []
+
+
+def _CheckForHistogramOffByOne(input_api, output_api):
+  """Make sure histogram enum maxes are used properly"""
+
+  # A general-purpose chunk of regex to match whitespace and/or comments
+  # that may be interspersed with the code we're interested in:
+  comment = r'/\*.*?\*/|//[^\n]*'
+  whitespace = r'(?:[\n\t ]|(?:' + comment + r'))*'
+
+  # The name is assumed to be a literal string.
+  histogram_name = r'"[^"]*"'
+
+  # This can be an arbitrary expression, so just ensure it isn't a ; to prevent
+  # matching past the end of this statement.
+  histogram_value = r'[^;]*'
+
+  # In parens so we can retrieve it for further checks.
+  histogram_max = r'([^;,]*)'
+
+  # This should match a uma histogram enumeration macro expression.
+  uma_macro_re = input_api.re.compile(
+      r'\bUMA_HISTOGRAM_ENUMERATION\(' + whitespace + histogram_name + r',' +
+      whitespace + histogram_value + r',' + whitespace + histogram_max +
+      whitespace + r'\)' + whitespace + r';(?:' + whitespace +
+      r'\/\/ (PRESUBMIT_IGNORE_UMA_MAX))?')
+
+  uma_max_re = input_api.re.compile(r'.*(?:Max|MAX).* \+ 1')
+
+  problems = []
+
+  for f in input_api.AffectedSourceFiles(_FilterFile):
+    contents = input_api.ReadFile(f)
+
+    # We want to match across lines, but still report a line number, so we keep
+    # track of the line we're on as we search through the file.
+    line_number = 1
+
+    # We search the entire file, then check if any violations are in the changed
+    # areas, this is inefficient, but simple. A UMA_HISTOGRAM_ENUMERATION call
+    # will often span multiple lines, so finding a match looking just at the
+    # deltas line-by-line won't catch problems.
+    match = uma_macro_re.search(contents)
+    while match:
+      line_number += contents.count('\n', 0, match.start())
+      max_arg = match.group(1) # The third argument.
+
+      if (not uma_max_re.match(max_arg) and match.group(2) !=
+          'PRESUBMIT_IGNORE_UMA_MAX'):
+        uma_range = range(match.start(), match.end() + 1)
+        # Check if any part of the match is in the changed lines:
+        for num, line in f.ChangedContents():
+          if line_number <= num <= line_number + match.group().count('\n'):
+            problems.append('%s:%d' % (f, line_number))
+            break
+
+      # Strip off the file contents up to the end of the match and update the
+      # line number.
+      contents = contents[match.end():]
+      line_number += match.group().count('\n')
+      match = uma_macro_re.search(contents)
+
+  if problems:
+    return [output_api.PresubmitError(
+      'UMA_HISTOGRAM_ENUMERATION reports in src/media/ are expected to adhere\n'
+      'to the following guidelines:\n'
+      ' - The max value (3rd argument) should be an enum value equal to the\n'
+      '   last valid value, e.g. FOO_MAX = LAST_VALID_FOO.\n'
+      ' - 1 must be added to that max value.\n'
+      'Contact dalecurtis@chromium.org if you have questions.' , problems)]
+
+  return []
+
+
+def _CheckPassByValue(input_api, output_api):
+  """Check that base::Time and derived classes are passed by value, and not by
+  const reference """
+
+  problems = []
+
+  for f in input_api.AffectedSourceFiles(_FilterFile):
+    for line_number, line in f.ChangedContents():
+      if BASE_TIME_TYPES_RE.search(line):
+        problems.append('%s:%d' % (f, line_number))
+
+  if problems:
+    return [output_api.PresubmitError(
+      'base::Time and derived classes should be passed by value and not by\n'
+      'const ref, see base/time/time.h for more information.', problems)]
+  return []
+
+
+def _CheckForUseOfLazyInstance(input_api, output_api):
+  """Check that base::LazyInstance is not used."""
+
+  problems = []
+
+  lazy_instance_re = re.compile(r'(^|\W)base::LazyInstance<')
+
+  for f in input_api.AffectedSourceFiles(_FilterFile):
+    for line_number, line in f.ChangedContents():
+      if lazy_instance_re.search(line):
+        problems.append('%s:%d' % (f, line_number))
+
+  if problems:
+    return [output_api.PresubmitError(
+      'base::LazyInstance is deprecated; use a thread safe static.', problems)]
+  return []
+
+def _CheckNoLoggingOverrideInHeaders(input_api, output_api):
+  """Checks to make sure no .h files include logging_override_if_enabled.h."""
+  files = []
+  pattern = input_api.re.compile(
+    r'^#include\s*"media/base/logging_override_if_enabled.h"',
+    input_api.re.MULTILINE)
+  for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
+    if not f.LocalPath().endswith('.h'):
+      continue
+    contents = input_api.ReadFile(f)
+    if pattern.search(contents):
+      files.append(f)
+
+  if len(files):
+    return [output_api.PresubmitError(
+        'Do not #include "logging_override_if_enabled.h" in header files, '
+        'since it overrides DVLOG() in every file including the header. '
+        'Instead, only include it in source files.',
+        files) ]
+  return []
+
+def _CheckForNoV4L2AggregateInitialization(input_api, output_api):
+  """Check that struct v4l2_* are not initialized as aggregates with a
+  braced-init-list"""
+
+  problems = []
+
+  v4l2_aggregate_initializer_re = re.compile(r'(^|\W)struct.+v4l2_.+=.+{+}+;')
+
+  for f in input_api.AffectedSourceFiles(_FilterFile):
+    for line_number, line in f.ChangedContents():
+      if v4l2_aggregate_initializer_re.search(line):
+        problems.append('%s:%d' % (f, line_number))
+
+  if problems:
+    return [output_api.PresubmitPromptWarning(
+      'Avoid initializing V4L2 structures with braced-init-lists, i.e. as '
+      'aggregates. V4L2 structs often contain unions of various sized members: '
+      'when a union is initialized by aggregate initialization, only the first '
+      'non-static member is initialized, leaving other members unitialized if '
+      'they are larger. Use memset instead.',
+      problems)]
+  return []
+
+def _CheckChange(input_api, output_api):
+  results = []
+  results.extend(_CheckForUseOfWrongClock(input_api, output_api))
+  results.extend(_CheckPassByValue(input_api, output_api))
+  results.extend(_CheckForHistogramOffByOne(input_api, output_api))
+  results.extend(_CheckForUseOfLazyInstance(input_api, output_api))
+  results.extend(_CheckNoLoggingOverrideInHeaders(input_api, output_api))
+  results.extend(_CheckForNoV4L2AggregateInitialization(input_api, output_api))
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  return _CheckChange(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return _CheckChange(input_api, output_api)
diff --git a/third_party/chromium/media/PRESUBMIT_test.py b/third_party/chromium/media/PRESUBMIT_test.py
new file mode 100755
index 0000000..80eabff
--- /dev/null
+++ b/third_party/chromium/media/PRESUBMIT_test.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import os
+import re
+import unittest
+
+import PRESUBMIT
+
+class MockInputApi(object):
+  def __init__(self):
+    self.re = re
+    self.os_path = os.path
+    self.files = []
+    self.is_committing = False
+
+  def AffectedFiles(self):
+    return self.files
+
+  def AffectedSourceFiles(self, fn):
+    # we'll just pretend everything is a source file for the sake of simplicity
+    return self.files
+
+  def ReadFile(self, f):
+    return f.NewContents()
+
+
+class MockOutputApi(object):
+  class PresubmitResult(object):
+    def __init__(self, message, items=None, long_text=''):
+      self.message = message
+      self.items = items
+      self.long_text = long_text
+
+  class PresubmitError(PresubmitResult):
+    def __init__(self, message, items, long_text=''):
+      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
+      self.type = 'error'
+
+  class PresubmitPromptWarning(PresubmitResult):
+    def __init__(self, message, items, long_text=''):
+      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
+      self.type = 'warning'
+
+  class PresubmitNotifyResult(PresubmitResult):
+    def __init__(self, message, items, long_text=''):
+      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
+      self.type = 'notify'
+
+  class PresubmitPromptOrNotify(PresubmitResult):
+    def __init__(self, message, items, long_text=''):
+      MockOutputApi.PresubmitResult.__init__(self, message, items, long_text)
+      self.type = 'promptOrNotify'
+
+
+class MockFile(object):
+  def __init__(self, local_path, new_contents):
+    self._local_path = local_path
+    self._new_contents = new_contents
+    self._changed_contents = [(i + 1, l) for i, l in enumerate(new_contents)]
+
+  def ChangedContents(self):
+    return self._changed_contents
+
+  def NewContents(self):
+    return self._new_contents
+
+  def LocalPath(self):
+    return self._local_path
+
+
+class MockChange(object):
+  def __init__(self, changed_files):
+    self._changed_files = changed_files
+
+  def LocalPaths(self):
+    return self._changed_files
+
+
+class HistogramOffByOneTest(unittest.TestCase):
+
+  # Take an input and make sure the problems found equals the expectation.
+  def simpleCheck(self, contents, expected_errors):
+    input_api = MockInputApi()
+    input_api.files.append(MockFile('test.cc', contents))
+    results = PRESUBMIT._CheckForHistogramOffByOne(input_api, MockOutputApi())
+    if expected_errors:
+      self.assertEqual(1, len(results))
+      self.assertEqual(expected_errors, len(results[0].items))
+    else:
+      self.assertEqual(0, len(results))
+
+  def testValid(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFooMax + 1);', 0)
+
+  def testValidComments(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", /*...*/ kFoo, /*...*/'
+                     'kFooMax + 1);', 0)
+
+  def testValidMultiLine(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test",\n'
+                     '                          kFoo,\n'
+                     '                          kFooMax + 1);', 0)
+
+  def testValidMultiLineComments(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test",  // This is the name\n'
+                     '                          kFoo,  /* The value */\n'
+                     '                          kFooMax + 1 /* The max */ );',
+                     0)
+
+  def testNoPlusOne(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFooMax);', 1)
+
+  def testInvalidWithIgnore(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFooMax); '
+                     '// PRESUBMIT_IGNORE_UMA_MAX', 0)
+
+  def testNoMax(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFoo + 1);', 1)
+
+  def testNoMaxNoPlusOne(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFoo);', 1)
+
+  def testMultipleErrors(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFoo);\n'
+                     'printf("hello, world!");\n'
+                     'UMA_HISTOGRAM_ENUMERATION("test", kBar, kBarMax);', 2)
+
+  def testValidAndInvalid(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFoo);\n'
+                     'UMA_HISTOGRAM_ENUMERATION("test", kFoo, kFooMax + 1);'
+                     'UMA_HISTOGRAM_ENUMERATION("test", kBar, kBarMax);', 2)
+
+  def testInvalidMultiLine(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test",\n'
+                     '                          kFoo,\n'
+                     '                          kFooMax + 2);', 1)
+
+  def testInvalidComments(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test", /*...*/, val, /*...*/,'
+                     'Max);\n', 1)
+
+  def testInvalidMultiLineComments(self):
+    self.simpleCheck('UMA_HISTOGRAM_ENUMERATION("test",  // This is the name\n'
+                     '                          kFoo,  /* The value */\n'
+                     '                          kFooMax + 2 /* The max */ );',
+                     1)
+
+class NoV4L2AggregateInitializationTest(unittest.TestCase):
+
+  def testValid(self):
+    self._testChange(['struct v4l2_format_ format;'], 0)
+
+  def testInvalid(self):
+    self._testChange(['struct v4l2_format format = {};'], 1)
+    self._testChange(['  struct v4l2_format format = {};'], 1)
+    self._testChange(['  struct std::vector<v4l2_format> format[] = {};'], 1)
+    self._testChange(['  struct std::vector<v4l2_format> format[] = {{}};'], 1)
+
+  def _testChange(self, content, expected_warnings):
+    mock_input_api = MockInputApi()
+    mock_input_api.files.append(MockFile('test.cc', content))
+    results = PRESUBMIT._CheckForNoV4L2AggregateInitialization(mock_input_api,
+                                                               MockOutputApi())
+    if expected_warnings:
+      self.assertEqual(1, len(results))
+      self.assertEqual(expected_warnings, len(results[0].items))
+    else:
+      self.assertEqual(0, len(results))
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/third_party/chromium/media/README.md b/third_party/chromium/media/README.md
new file mode 100644
index 0000000..eaf3d87
--- /dev/null
+++ b/third_party/chromium/media/README.md
@@ -0,0 +1,193 @@
+# media/
+
+Welcome to Chromium Media! This directory primarily contains a collection of
+components related to media capture and playback.  Feel free to reach out to the
+media-dev@chromium.org mailing list with questions.
+
+As a top level component this may be depended on by almost every other Chromium
+component except base/. Certain components may not work properly in sandboxed
+processes.
+
+
+
+# Directory Breakdown
+
+* audio/ - Code for audio input and output. Includes platform specific output
+and input implementations. Due to use of platform APIs, can not normally be used
+from within a sandboxed process.
+
+* base/ - Contains miscellaneous enums, utility classes, and shuttling
+primitives used throughout `media/` and beyond; i.e. `AudioBus`, `AudioCodec`, and
+`VideoFrame` just to name a few. Can be used in any process.
+
+* blink/ - Code for interfacing with the Blink rendering engine for `MediaStreams`
+as well as `<video>` and `<audio>` playback. Used only in the same process as Blink;
+typically the render process.
+
+* capture/ - Contains content (as in the content layer) capturing and platform
+specific video capture implementations.
+
+* cast/ - Contains the tab casting implementation; not to be confused with the
+Chromecast code which lives in the top-level cast/ directory.
+
+* cdm/ - Contains code related to the Content Decryption Module (CDM) used for
+playback of content via Encrypted Media Extensions (EME).
+
+* device_monitors/ - Contains code for monitoring device changes; e.g. webcam
+and microphone plugin and unplug events.
+
+* ffmpeg/ - Contains binding code and helper methods necessary to use the ffmpeg
+library located in //third_party/ffmpeg.
+
+* filters/ - Contains data sources, decoders, demuxers, parsers, and rendering
+algorithms used for media playback.
+
+* formats/ - Contains parsers used by Media Source Extensions (MSE).
+
+* gpu/ - Contains the platform hardware encoder and decoder implementations.
+
+* midi/ - Contains the WebMIDI API implementation.
+
+* mojo/ - Contains mojo services for media. Typically used for providing out of
+process media functionality to a sandboxed process.
+
+* muxers/ - Code for muxing content for the Media Recorder API.
+
+* remoting/ - Code for transmitting muxed packets to a remote endpoint for
+playback.
+
+* renderers/ - Code for rendering audio and video to an output sink.
+
+* test/ - Code and data for testing the media playback pipeline.
+
+* tools/ - Standalone media test tools.
+
+* video/ - Abstract hardware video decoder interfaces and tooling.
+
+
+
+# Capture
+
+TODO(miu, chfemer): Fill in this section.
+
+
+
+# mojo
+
+See [media/mojo documentation](/media/mojo).
+
+
+
+# MIDI
+
+TODO(toyoshim): Fill in this section.
+
+
+
+# Playback
+
+Media playback encompasses a large swatch of technologies, so by necessity this
+will provide only a brief outline. Inside this directory you'll find components
+for media demuxing, software and hardware video decode, audio output, as well as
+audio and video rendering.
+
+Specifically under the playback heading, media/ contains the implementations of
+components required for HTML media elements and extensions:
+
+* [HTML5 Audio & Video](https://dev.w3.org/html5/spec-author-view/video.html)
+* [Media Source Extensions](https://www.w3.org/TR/media-source/)
+* [Encrypted Media Extensions](https://www.w3.org/TR/encrypted-media/)
+
+The following diagram provides a simplified overview of the media playback
+pipeline.
+
+![Media Pipeline Overview](/docs/media/media_pipeline_overview.png)
+
+As a case study we'll consider the playback of a video through the `<video>` tag.
+
+`<video>` (and `<audio>`) starts in `blink::HTMLMediaElement` in
+third_party/blink/ and reaches third_party/blink/public/platform/media/ in
+`media::WebMediaPlayerImpl` after a brief hop through `content::MediaFactory`.
+Each `blink::HTMLMediaElement` owns a `media::WebMediaPlayerImpl` for handling
+things like play, pause, seeks, and volume changes (among other things).
+
+`media::WebMediaPlayerImpl` handles or delegates media loading over the network
+as well as demuxer and pipeline initialization. `media::WebMediaPlayerImpl`
+owns a `media::PipelineController` which manages the coordination of a
+`media::DataSource`, `media::Demuxer`, and `media::Renderer` during playback.
+
+During a normal playback, the `media::Demuxer` owned by WebMediaPlayerImpl may
+be either `media::FFmpegDemuxer` or `media::ChunkDemuxer`. The ffmpeg variant
+is used for standard src= playback where WebMediaPlayerImpl is responsible for
+loading bytes over the network. `media::ChunkDemuxer` is used with Media Source
+Extensions (MSE), where JavaScript code provides the muxed bytes.
+
+The media::Renderer is typically `media::RendererImpl` which owns and
+coordinates `media::AudioRenderer` and `media::VideoRenderer` instances. Each
+of these in turn own a set of `media::AudioDecoder` and `media::VideoDecoder`
+implementations. Each issues an async read to a `media::DemuxerStream` exposed
+by the `media::Demuxer` which is routed to the right decoder by
+`media::DecoderStream`. Decoding is again async, so decoded frames are
+delivered at some later time to each renderer.
+
+The media/ library contains hardware decoder implementations in media/gpu for
+all supported Chromium platforms, as well as software decoding implementations
+in media/filters backed by FFmpeg and libvpx. Decoders are attempted in the
+order provided via the `media::RendererFactory`; the first one which reports
+success will be used for playback (typically the hardware decoder for video).
+
+Each renderer manages timing and rendering of audio and video via the event-
+driven `media::AudioRendererSink` and `media::VideoRendererSink` interfaces
+respectively. These interfaces both accept a callback that they will issue
+periodically when new audio or video frames are required.
+
+On the audio side, again in the normal case, the `media::AudioRendererSink` is
+driven via a `base::SyncSocket` and shared memory segment owned by the browser
+process. This socket is ticked periodically by a platform level implementation
+of `media::AudioOutputStream` within media/audio.
+
+On the video side, the `media::VideoRendererSink` is driven by async callbacks
+issued by the compositor to `media::VideoFrameCompositor`. The
+`media::VideoRenderer` will talk to the `media::AudioRenderer` through a
+`media::TimeSource` for coordinating audio and video sync.
+
+With that we've covered the basic flow of a typical playback. When debugging
+issues, it's helpful to review the internal logs at chrome://media-internals.
+The internals page contains information about active
+`media::WebMediaPlayerImpl`, `media::AudioInputController`,
+`media::AudioOutputController`, and `media::AudioOutputStream` instances.
+
+
+
+# Logging
+
+Media playback typically involves multiple threads, in many cases even multiple
+processes. Media operations are often asynchronous running in a sandbox. These
+make attaching a debugger (e.g. GDB) sometimes less efficient than other
+mechanisms like logging.
+
+## DVLOG
+
+In media we use DVLOG() a lot. It makes filename-based filtering super easy.
+Within one file, not all logs are created equal. To make log filtering
+more convenient, use appropriate log levels. Here are some general
+recommendations:
+
+* DVLOG(1): Once per playback events or other important events, e.g.
+  construction/destruction, initialization, playback start/end, suspend/resume,
+  any error conditions.
+* DVLOG(2): Recurring events per playback, e.g. seek/reset/flush, config change.
+* DVLOG(3): Frequent events, e.g. demuxer read, audio/video buffer decrypt or
+  decode, audio/video frame rendering.
+
+## MediaLog
+
+MediaLog will send logs to `about://media-internals`, which is easily accessible
+by developers (including web developes), testers and even users to get detailed
+information about a playback instance. For guidance on how to use MediaLog, see
+`media/base/media_log.h`.
+
+MediaLog messages should be concise and free of implementation details. Error
+messages should provide clues as to how to fix them, usually by precisely
+describing the circumstances that led to the error. Use properties, rather
+than messages, to record metadata and state changes.
diff --git a/third_party/chromium/media/audio/BUILD.gn b/third_party/chromium/media/audio/BUILD.gn
new file mode 100644
index 0000000..9c8628a
--- /dev/null
+++ b/third_party/chromium/media/audio/BUILD.gn
@@ -0,0 +1,483 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chromeos/ui_mode.gni")
+import("//build/config/linux/pkg_config.gni")
+import("//media/media_options.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
+import("//tools/generate_stubs/rules.gni")
+
+# When libpulse is not directly linked, use stubs to allow for dlopening of the
+# binary.
+if (use_pulseaudio && !link_pulseaudio) {
+  generate_stubs("libpulse_stubs") {
+    extra_header = "pulse/pulse_stub_header.fragment"
+    sigs = [ "pulse/pulse.sigs" ]
+    output_name = "pulse/pulse_stubs"
+    deps = [ "//base" ]
+  }
+}
+
+if (is_android) {
+  generate_stubs("aaudio_stubs") {
+    extra_header = "android/aaudio_stub_header.fragment"
+    sigs = [ "android/aaudio.sigs" ]
+    output_name = "android/aaudio_stubs"
+    deps = [ "//base" ]
+  }
+}
+
+config("platform_config") {
+  defines = []
+  if (use_alsa) {
+    defines += [ "USE_ALSA" ]
+  }
+}
+
+source_set("audio") {
+  # Do not expand the visibility here without double-checking with OWNERS, this
+  # is a roll-up target which is part of the //media component. Most other DEPs
+  # should be using //media and not directly DEP this roll-up target.
+  visibility = [
+    ":wav_audio_handler_fuzzer",
+    "//media",
+    "//media/renderers",
+
+    # TODO(dalecurtis): CoreAudioUtil::IsCoreAudioSupported() should probably
+    # move into media/base/win.
+    "//media/device_monitors",
+
+    # TODO(dalecurtis): Move android audio pieces into //media/audio.
+    "//media/base/android",
+  ]
+  sources = [
+    "agc_audio_stream.h",
+    "alive_checker.cc",
+    "alive_checker.h",
+    "audio_debug_file_writer.cc",
+    "audio_debug_file_writer.h",
+    "audio_debug_recording_helper.cc",
+    "audio_debug_recording_helper.h",
+    "audio_debug_recording_manager.cc",
+    "audio_debug_recording_manager.h",
+    "audio_debug_recording_session.h",
+    "audio_debug_recording_session_impl.cc",
+    "audio_debug_recording_session_impl.h",
+    "audio_device_description.cc",
+    "audio_device_description.h",
+    "audio_device_name.cc",
+    "audio_device_name.h",
+    "audio_device_thread.cc",
+    "audio_device_thread.h",
+    "audio_features.cc",
+    "audio_features.h",
+    "audio_input_delegate.cc",
+    "audio_input_delegate.h",
+    "audio_input_device.cc",
+    "audio_input_device.h",
+    "audio_input_ipc.cc",
+    "audio_input_ipc.h",
+    "audio_input_stream_data_interceptor.cc",
+    "audio_input_stream_data_interceptor.h",
+    "audio_io.h",
+    "audio_manager.cc",
+    "audio_manager.h",
+    "audio_manager_base.cc",
+    "audio_manager_base.h",
+    "audio_opus_encoder.cc",
+    "audio_opus_encoder.h",
+    "audio_output_delegate.cc",
+    "audio_output_delegate.h",
+    "audio_output_device.cc",
+    "audio_output_device.h",
+    "audio_output_device_thread_callback.cc",
+    "audio_output_device_thread_callback.h",
+    "audio_output_dispatcher.cc",
+    "audio_output_dispatcher.h",
+    "audio_output_dispatcher_impl.cc",
+    "audio_output_dispatcher_impl.h",
+    "audio_output_ipc.cc",
+    "audio_output_ipc.h",
+    "audio_output_proxy.cc",
+    "audio_output_proxy.h",
+    "audio_output_resampler.cc",
+    "audio_output_resampler.h",
+    "audio_output_stream_sink.cc",
+    "audio_output_stream_sink.h",
+    "audio_sink_parameters.cc",
+    "audio_sink_parameters.h",
+    "audio_source_diverter.h",
+    "audio_source_parameters.cc",
+    "audio_source_parameters.h",
+    "audio_system.cc",
+    "audio_system.h",
+    "audio_system_helper.cc",
+    "audio_system_helper.h",
+    "audio_system_impl.cc",
+    "audio_system_impl.h",
+    "audio_thread.h",
+    "audio_thread_hang_monitor.cc",
+    "audio_thread_hang_monitor.h",
+    "audio_thread_impl.cc",
+    "audio_thread_impl.h",
+    "clockless_audio_sink.cc",
+    "clockless_audio_sink.h",
+    "fake_audio_input_stream.cc",
+    "fake_audio_input_stream.h",
+    "fake_audio_log_factory.cc",
+    "fake_audio_log_factory.h",
+    "fake_audio_manager.cc",
+    "fake_audio_manager.h",
+    "fake_audio_output_stream.cc",
+    "fake_audio_output_stream.h",
+    "null_audio_sink.cc",
+    "null_audio_sink.h",
+    "power_observer_helper.cc",
+    "power_observer_helper.h",
+    "scoped_task_runner_observer.cc",
+    "scoped_task_runner_observer.h",
+    "simple_sources.cc",
+    "simple_sources.h",
+    "wav_audio_handler.cc",
+    "wav_audio_handler.h",
+  ]
+  deps = [
+    "//base",
+    "//build:chromecast_buildflags",
+    "//build:chromeos_buildflags",
+    "//media/base",
+    "//third_party/opus:opus",
+    "//url",
+  ]
+  libs = []
+  configs += [
+    ":platform_config",
+    "//media:subcomponent_config",
+  ]
+
+  if (is_mac) {
+    sources += [
+      "mac/audio_auhal_mac.cc",
+      "mac/audio_auhal_mac.h",
+      "mac/audio_device_listener_mac.cc",
+      "mac/audio_device_listener_mac.h",
+      "mac/audio_input_mac.cc",
+      "mac/audio_input_mac.h",
+      "mac/audio_low_latency_input_mac.cc",
+      "mac/audio_low_latency_input_mac.h",
+      "mac/audio_manager_mac.cc",
+      "mac/audio_manager_mac.h",
+      "mac/core_audio_util_mac.cc",
+      "mac/core_audio_util_mac.h",
+      "mac/coreaudio_dispatch_override.cc",
+      "mac/coreaudio_dispatch_override.h",
+      "mac/scoped_audio_unit.cc",
+      "mac/scoped_audio_unit.h",
+    ]
+    frameworks = [
+      "AudioToolbox.framework",
+      "AudioUnit.framework",
+      "CoreAudio.framework",
+      "CoreFoundation.framework",
+    ]
+  }
+
+  if (is_win) {
+    sources += [
+      "win/audio_device_listener_win.cc",
+      "win/audio_device_listener_win.h",
+      "win/audio_low_latency_input_win.cc",
+      "win/audio_low_latency_input_win.h",
+      "win/audio_low_latency_output_win.cc",
+      "win/audio_low_latency_output_win.h",
+      "win/audio_manager_win.cc",
+      "win/audio_manager_win.h",
+      "win/audio_session_event_listener_win.cc",
+      "win/audio_session_event_listener_win.h",
+      "win/avrt_wrapper_win.cc",
+      "win/avrt_wrapper_win.h",
+      "win/core_audio_util_win.cc",
+      "win/core_audio_util_win.h",
+      "win/device_enumeration_win.cc",
+      "win/device_enumeration_win.h",
+      "win/volume_range_util.cc",
+      "win/volume_range_util.h",
+      "win/waveout_output_win.cc",
+      "win/waveout_output_win.h",
+    ]
+
+    libs += [
+      "dxguid.lib",
+      "setupapi.lib",
+      "winmm.lib",
+    ]
+  }
+
+  if (is_android) {
+    sources += [
+      "android/aaudio_output.cc",
+      "android/aaudio_output.h",
+      "android/audio_manager_android.cc",
+      "android/audio_manager_android.h",
+      "android/audio_track_output_stream.cc",
+      "android/audio_track_output_stream.h",
+      "android/muteable_audio_output_stream.h",
+      "android/opensles_input.cc",
+      "android/opensles_input.h",
+      "android/opensles_output.cc",
+      "android/opensles_output.h",
+      "android/opensles_util.cc",
+      "android/opensles_util.h",
+      "android/opensles_wrapper.cc",
+    ]
+
+    deps += [
+      ":aaudio_stubs",
+      "//media/base/android:media_jni_headers",
+    ]
+  }
+
+  if (is_linux || is_chromeos) {
+    sources += [ "linux/audio_manager_linux.cc" ]
+  }
+
+  if (use_alsa) {
+    libs += [ "asound" ]
+    sources += [
+      "alsa/alsa_input.cc",
+      "alsa/alsa_input.h",
+      "alsa/alsa_output.cc",
+      "alsa/alsa_output.h",
+      "alsa/alsa_util.cc",
+      "alsa/alsa_util.h",
+      "alsa/alsa_wrapper.cc",
+      "alsa/alsa_wrapper.h",
+      "alsa/audio_manager_alsa.cc",
+      "alsa/audio_manager_alsa.h",
+    ]
+  }
+
+  if (use_cras) {
+    sources += [
+      "cras/audio_manager_cras_base.cc",
+      "cras/audio_manager_cras_base.h",
+      "cras/cras_input.cc",
+      "cras/cras_input.h",
+      "cras/cras_unified.cc",
+      "cras/cras_unified.h",
+    ]
+    configs += [ ":libcras" ]
+    if (is_chromeos_ash) {
+      sources += [
+        "cras/audio_manager_chromeos.cc",
+        "cras/audio_manager_chromeos.h",
+      ]
+      deps += [ "//ash/components/audio" ]
+    } else if (is_linux || is_chromeos_lacros) {
+      sources += [
+        "cras/audio_manager_cras.cc",
+        "cras/audio_manager_cras.h",
+        "cras/cras_util.cc",
+        "cras/cras_util.h",
+      ]
+    }
+  }
+
+  if (use_pulseaudio) {
+    sources += [
+      "pulse/audio_manager_pulse.cc",
+      "pulse/audio_manager_pulse.h",
+      "pulse/pulse_input.cc",
+      "pulse/pulse_input.h",
+      "pulse/pulse_output.cc",
+      "pulse/pulse_output.h",
+      "pulse/pulse_util.cc",
+      "pulse/pulse_util.h",
+    ]
+
+    deps += [ "//build:branding_buildflags" ]
+
+    if (link_pulseaudio) {
+      configs += [ ":libpulse" ]
+    } else {
+      deps += [ ":libpulse_stubs" ]
+    }
+  }
+
+  if (is_fuchsia) {
+    sources += [
+      "fuchsia/audio_manager_fuchsia.cc",
+      "fuchsia/audio_manager_fuchsia.h",
+      "fuchsia/audio_output_stream_fuchsia.cc",
+      "fuchsia/audio_output_stream_fuchsia.h",
+    ]
+    deps += [
+      "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.media",
+      "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+    ]
+  }
+}
+
+if (use_cras) {
+  pkg_config("libcras") {
+    packages = [ "libcras" ]
+  }
+}
+
+if (use_pulseaudio && link_pulseaudio) {
+  pkg_config("libpulse") {
+    packages = [ "libpulse" ]
+  }
+}
+
+# Note: This is a roll-up only target; do not expand the visibility. DEPS should
+# depend on the //media:test_support target instead.
+static_library("test_support") {
+  visibility = [ "//media:test_support" ]
+  testonly = true
+  sources = [
+    "audio_debug_recording_test.cc",
+    "audio_debug_recording_test.h",
+    "audio_device_info_accessor_for_tests.cc",
+    "audio_device_info_accessor_for_tests.h",
+    "audio_system_test_util.cc",
+    "audio_system_test_util.h",
+    "audio_unittest_util.cc",
+    "audio_unittest_util.h",
+    "mock_audio_debug_recording_manager.cc",
+    "mock_audio_debug_recording_manager.h",
+    "mock_audio_manager.cc",
+    "mock_audio_manager.h",
+    "mock_audio_source_callback.cc",
+    "mock_audio_source_callback.h",
+    "test_audio_thread.cc",
+    "test_audio_thread.h",
+  ]
+  configs += [
+    ":platform_config",
+    "//media:media_config",
+  ]
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//build:chromeos_buildflags",
+
+    # Do not add any other //media deps except this; it will automatically pull
+    # a dep on //media which is required to ensure test_support targets all use
+    # the same //media component and not build a target's sources individually.
+    "//media/base:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+  if (use_alsa) {
+    sources += [
+      "alsa/mock_alsa_wrapper.cc",
+      "alsa/mock_alsa_wrapper.h",
+    ]
+  }
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "alive_checker_unittest.cc",
+    "audio_debug_file_writer_unittest.cc",
+    "audio_debug_recording_helper_unittest.cc",
+    "audio_debug_recording_manager_unittest.cc",
+    "audio_debug_recording_session_impl_unittest.cc",
+    "audio_encoders_unittest.cc",
+    "audio_input_device_unittest.cc",
+    "audio_input_stream_data_interceptor_unittest.cc",
+    "audio_input_unittest.cc",
+    "audio_manager_unittest.cc",
+    "audio_output_device_unittest.cc",
+    "audio_output_proxy_unittest.cc",
+    "audio_output_unittest.cc",
+    "audio_system_impl_unittest.cc",
+    "audio_thread_hang_monitor_unittest.cc",
+    "power_observer_helper_unittest.cc",
+    "simple_sources_unittest.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//build:chromeos_buildflags",
+    "//media:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+    "//third_party/opus:opus",
+    "//url",
+  ]
+
+  configs += [
+    ":platform_config",
+    "//media:media_config",
+  ]
+
+  if (is_android) {
+    sources += [ "android/audio_android_unittest.cc" ]
+    deps += [ "//ui/gl" ]
+  }
+
+  if (is_mac) {
+    sources += [
+      "mac/audio_auhal_mac_unittest.cc",
+      "mac/audio_device_listener_mac_unittest.cc",
+      "mac/audio_low_latency_input_mac_unittest.cc",
+    ]
+  }
+
+  if (is_chromeos_ash || is_chromecast) {
+    sources += [
+      "test_data.h",
+      "wav_audio_handler_unittest.cc",
+    ]
+
+    if (!is_chromecast) {
+      deps += [
+        "//ash/components/audio",
+        "//chromeos/dbus/audio",
+      ]
+    }
+
+    if (use_cras) {
+      sources += [
+        "cras/audio_manager_chromeos_unittest.cc",
+        "cras/cras_input_unittest.cc",
+        "cras/cras_unified_unittest.cc",
+      ]
+    }
+  }
+
+  if (is_win) {
+    sources += [
+      "win/audio_device_listener_win_unittest.cc",
+      "win/audio_low_latency_input_win_unittest.cc",
+      "win/audio_low_latency_output_win_unittest.cc",
+      "win/audio_output_win_unittest.cc",
+      "win/audio_session_event_listener_win_unittest.cc",
+      "win/core_audio_util_win_unittest.cc",
+      "win/device_enumeration_win_unittest.cc",
+      "win/volume_range_util_unittest.cc",
+    ]
+  }
+
+  if (use_alsa) {
+    sources += [
+      "alsa/alsa_output_unittest.cc",
+      "alsa/alsa_util_unittest.cc",
+      "audio_low_latency_input_output_unittest.cc",
+    ]
+  }
+}
+
+fuzzer_test("wav_audio_handler_fuzzer") {
+  sources = [ "wav_audio_handler_fuzzer.cc" ]
+  deps = [
+    ":audio",
+    "//base",
+    "//media:test_support",
+  ]
+}
diff --git a/third_party/chromium/media/audio/DEPS b/third_party/chromium/media/audio/DEPS
new file mode 100644
index 0000000..e58d970
--- /dev/null
+++ b/third_party/chromium/media/audio/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ash/components/audio",
+]
diff --git a/third_party/chromium/media/audio/DIR_METADATA b/third_party/chromium/media/audio/DIR_METADATA
new file mode 100644
index 0000000..e80d793
--- /dev/null
+++ b/third_party/chromium/media/audio/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Media>Audio"
+}
\ No newline at end of file
diff --git a/third_party/chromium/media/audio/OWNERS b/third_party/chromium/media/audio/OWNERS
new file mode 100644
index 0000000..2dc6241
--- /dev/null
+++ b/third_party/chromium/media/audio/OWNERS
@@ -0,0 +1,9 @@
+tommi@chromium.org
+olka@chromium.org
+
+# Windows
+henrika@chromium.org
+
+# Mirroring (and related glue) OWNERS.
+jophba@chromium.org
+mfoltz@chromium.org
diff --git a/third_party/chromium/media/audio/agc_audio_stream.h b/third_party/chromium/media/audio/agc_audio_stream.h
new file mode 100644
index 0000000..41448c5
--- /dev/null
+++ b/third_party/chromium/media/audio/agc_audio_stream.h
@@ -0,0 +1,201 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AGC_AUDIO_STREAM_H_
+#define MEDIA_AUDIO_AGC_AUDIO_STREAM_H_
+
+#include <atomic>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "base/timer/timer.h"
+#include "media/audio/audio_io.h"
+
+// The template based AgcAudioStream implements platform-independent parts
+// of the AudioInterface interface. Supported interfaces to pass as
+// AudioInterface are AudioIntputStream and AudioOutputStream. Each platform-
+// dependent implementation should derive from this class.
+//
+// Usage example (on Windows):
+//
+//  class WASAPIAudioInputStream : public AgcAudioStream<AudioInputStream> {
+//   public:
+//    WASAPIAudioInputStream();
+//    ...
+//  };
+//
+// Call flow example:
+//
+//   1) User creates AgcAudioStream<AudioInputStream>
+//   2) User calls AudioInputStream::SetAutomaticGainControl(true) =>
+//      AGC usage is now initialized but not yet started.
+//   3) User calls AudioInputStream::Start() => implementation calls
+//      AgcAudioStream<AudioInputStream>::StartAgc() which detects that AGC
+//      is enabled and then starts the periodic AGC timer.
+//   4) Microphone volume samples are now taken and included in all
+//      AudioInputCallback::OnData() callbacks.
+//   5) User calls AudioInputStream::Stop() => implementation calls
+//      AgcAudioStream<AudioInputStream>::StopAgc() which stops the timer.
+//
+// Note that, calling AudioInputStream::SetAutomaticGainControl(false) while
+// AGC measurements are active will not have an effect until StopAgc(),
+// StartAgc() are called again since SetAutomaticGainControl() only sets a
+// a state.
+//
+// Calling SetAutomaticGainControl(true) enables the AGC and StartAgc() starts
+// a periodic timer which calls QueryAndStoreNewMicrophoneVolume()
+// approximately once every second. QueryAndStoreNewMicrophoneVolume() asks
+// the actual microphone about its current volume level. This value is
+// normalized and stored so it can be read by GetAgcVolume() when the real-time
+// audio thread needs the value. The main idea behind this scheme is to avoid
+// accessing the audio hardware from the real-time audio thread and to ensure
+// that we don't take new microphone-level samples too often (~1 Hz is a
+// suitable compromise). The timer will be active until StopAgc() is called.
+//
+// This class should be created and destroyed on the audio manager thread and
+// a thread checker is added to ensure that this is the case (uses DCHECK).
+// All methods except GetAgcVolume() should be called on the creating thread
+// as well to ensure that thread safety is maintained. It will also guarantee
+// that the periodic timer runs on the audio manager thread.
+// |normalized_volume_|, which is updated by QueryAndStoreNewMicrophoneVolume()
+// and read in GetAgcVolume(), is atomic to ensure that it can be accessed from
+// any real-time audio thread that needs it to update the its AGC volume.
+
+namespace media {
+
+template <typename AudioInterface>
+class MEDIA_EXPORT AgcAudioStream : public AudioInterface {
+ public:
+  // Time between two successive timer events.
+  static const int kIntervalBetweenVolumeUpdatesMs = 1000;
+
+  AgcAudioStream()
+      : agc_is_enabled_(false), max_volume_(0.0), normalized_volume_(0.0) {
+  }
+
+  virtual ~AgcAudioStream() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+  }
+
+ protected:
+  // Starts the periodic timer which periodically checks and updates the
+  // current microphone volume level.
+  // The timer is only started if AGC mode is first enabled using the
+  // SetAutomaticGainControl() method.
+  void StartAgc() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    if (!agc_is_enabled_ || timer_.IsRunning())
+      return;
+
+    max_volume_ = static_cast<AudioInterface*>(this)->GetMaxVolume();
+    if (max_volume_ <= 0) {
+      DLOG(WARNING) << "Failed to get max volume from hardware. Won't provide "
+                    << "normalized volume.";
+      return;
+    }
+
+    // Query and cache the volume to avoid sending 0 as volume to AGC at the
+    // beginning of the audio stream, otherwise AGC will try to raise the
+    // volume from 0.
+    QueryAndStoreNewMicrophoneVolume();
+
+    timer_.Start(FROM_HERE, base::Milliseconds(kIntervalBetweenVolumeUpdatesMs),
+                 this, &AgcAudioStream::QueryAndStoreNewMicrophoneVolume);
+  }
+
+  // Stops the periodic timer which periodically checks and updates the
+  // current microphone volume level.
+  void StopAgc() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    if (timer_.IsRunning())
+      timer_.Stop();
+  }
+
+  // Stores a new microphone volume level by checking the audio input device.
+  // Called on the audio manager thread.
+  void UpdateAgcVolume() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+
+    if (!timer_.IsRunning())
+      return;
+
+    // We take new volume samples once every second when the AGC is enabled.
+    // To ensure that a new setting has an immediate effect, the new volume
+    // setting is cached here. It will ensure that the next OnData() callback
+    // will contain a new valid volume level. If this approach was not taken,
+    // we could report invalid volume levels to the client for a time period
+    // of up to one second.
+    QueryAndStoreNewMicrophoneVolume();
+  }
+
+  // Gets the latest stored volume level if AGC is enabled.
+  // Called at each capture callback on a real-time capture thread (platform
+  // dependent).
+  void GetAgcVolume(double* normalized_volume) {
+    *normalized_volume = normalized_volume_.load(std::memory_order_relaxed);
+  }
+
+  // Gets the current automatic gain control state.
+  bool GetAutomaticGainControl() override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return agc_is_enabled_;
+  }
+
+ private:
+  // Sets the automatic gain control (AGC) to on or off. When AGC is enabled,
+  // the microphone volume is queried periodically and the volume level can
+  // be read in each AudioInputCallback::OnData() callback and fed to the
+  // render-side AGC. User must call StartAgc() as well to start measuring
+  // the microphone level.
+  bool SetAutomaticGainControl(bool enabled) override {
+    DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")";
+    DCHECK(thread_checker_.CalledOnValidThread());
+    agc_is_enabled_ = enabled;
+    return true;
+  }
+
+  // Takes a new microphone volume sample and stores it in |normalized_volume_|.
+  // Range is normalized to [0.0,1.0] or [0.0, 1.5] on Linux.
+  // This method is called periodically when AGC is enabled and always on the
+  // audio manager thread. We use it to read the current microphone level and
+  // to store it so it can be read by the main capture thread. By using this
+  // approach, we can avoid accessing audio hardware from a real-time audio
+  // thread and it leads to a more stable capture performance.
+  void QueryAndStoreNewMicrophoneVolume() {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    DCHECK_GT(max_volume_, 0.0);
+
+    // Retrieve the current volume level by asking the audio hardware.
+    // Range is normalized to [0.0,1.0] or [0.0, 1.5] on Linux.
+    double normalized_volume =
+        static_cast<AudioInterface*>(this)->GetVolume() / max_volume_;
+    normalized_volume_.store(normalized_volume, std::memory_order_relaxed);
+  }
+
+  // Ensures that this class is created and destroyed on the same thread.
+  base::ThreadChecker thread_checker_;
+
+  // Repeating timer which cancels itself when it goes out of scope.
+  // Used to check the microphone volume periodically.
+  base::RepeatingTimer timer_;
+
+  // True when automatic gain control is enabled, false otherwise.
+  bool agc_is_enabled_;
+
+  // Stores the maximum volume which is used for normalization to a volume
+  // range of [0.0, 1.0].
+  double max_volume_;
+
+  // Contains last result of internal call to GetVolume(). We save resources
+  // by not querying the capture volume for each callback. The range is
+  // normalized to [0.0, 1.0].
+  std::atomic<double> normalized_volume_;
+
+  DISALLOW_COPY_AND_ASSIGN(AgcAudioStream<AudioInterface>);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AGC_AUDIO_STREAM_H_
diff --git a/third_party/chromium/media/audio/alive_checker.cc b/third_party/chromium/media/audio/alive_checker.cc
new file mode 100644
index 0000000..8ca4e93
--- /dev/null
+++ b/third_party/chromium/media/audio/alive_checker.cc
@@ -0,0 +1,152 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/alive_checker.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/threading/thread_task_runner_handle.h"
+
+namespace media {
+
+AliveChecker::AliveChecker(base::RepeatingClosure dead_callback,
+                           base::TimeDelta check_interval,
+                           base::TimeDelta timeout,
+                           bool stop_at_first_alive_notification,
+                           bool pause_check_during_suspend)
+    : AliveChecker(std::move(dead_callback),
+                   check_interval,
+                   timeout,
+                   stop_at_first_alive_notification,
+                   pause_check_during_suspend,
+                   PowerObserverHelperFactoryCallback()) {}
+
+AliveChecker::AliveChecker(
+    base::RepeatingClosure dead_callback,
+    base::TimeDelta check_interval,
+    base::TimeDelta timeout,
+    bool stop_at_first_alive_notification,
+    PowerObserverHelperFactoryCallback power_observer_helper_factory_callback)
+    : AliveChecker(std::move(dead_callback),
+                   check_interval,
+                   timeout,
+                   stop_at_first_alive_notification,
+                   true,
+                   std::move(power_observer_helper_factory_callback)) {}
+
+// The private constructor called by the above public constructors.
+AliveChecker::AliveChecker(
+    base::RepeatingClosure dead_callback,
+    base::TimeDelta check_interval,
+    base::TimeDelta timeout,
+    bool stop_at_first_alive_notification,
+    bool pause_check_during_suspend,
+    PowerObserverHelperFactoryCallback power_observer_helper_factory_callback)
+    : check_interval_(check_interval),
+      timeout_(timeout),
+      task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      dead_callback_(std::move(dead_callback)),
+      stop_at_first_alive_notification_(stop_at_first_alive_notification) {
+  DCHECK(!dead_callback_.is_null());
+  DCHECK_GT(check_interval_, base::TimeDelta());
+  DCHECK_GT(timeout_, check_interval_);
+
+  if (pause_check_during_suspend) {
+    // When suspending, we don't need to take any action. When resuming, we
+    // reset |last_alive_notification_time_| to avoid false alarms.
+    // Unretained is safe since the PowerObserverHelper runs the callback on
+    // the task runner the AliveChecker (and consequently the
+    // PowerObserverHelper) is destroyed on.
+    if (power_observer_helper_factory_callback.is_null()) {
+      power_observer_ = std::make_unique<PowerObserverHelper>(
+          task_runner_, base::DoNothing(),
+          base::BindRepeating(
+              &AliveChecker::SetLastAliveNotificationTimeToNowOnTaskRunner,
+              base::Unretained(this)));
+    } else {
+      power_observer_ =
+          std::move(power_observer_helper_factory_callback)
+              .Run(task_runner_, base::DoNothing(),
+                   base::BindRepeating(
+                       &AliveChecker::
+                           SetLastAliveNotificationTimeToNowOnTaskRunner,
+                       base::Unretained(this)));
+    }
+  } else {
+    // If |pause_check_during_suspend| is false, we expect an empty factory
+    // callback.
+    DCHECK(power_observer_helper_factory_callback.is_null());
+  }
+}
+
+AliveChecker::~AliveChecker() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+}
+
+void AliveChecker::Start() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+  SetLastAliveNotificationTimeToNowOnTaskRunner();
+  detected_dead_ = false;
+
+  DCHECK(!check_alive_timer_);
+  check_alive_timer_ = std::make_unique<base::RepeatingTimer>();
+  check_alive_timer_->Start(FROM_HERE, check_interval_, this,
+                            &AliveChecker::CheckIfAlive);
+  DCHECK(check_alive_timer_->IsRunning());
+}
+
+void AliveChecker::Stop() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  check_alive_timer_.reset();
+}
+
+bool AliveChecker::DetectedDead() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  return detected_dead_;
+}
+
+void AliveChecker::NotifyAlive() {
+  if (!task_runner_->RunsTasksInCurrentSequence()) {
+    // We don't need high precision for setting |last_alive_notification_time_|
+    // so we don't have to care about the delay added with posting the task.
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&AliveChecker::NotifyAlive, weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  SetLastAliveNotificationTimeToNowOnTaskRunner();
+  if (stop_at_first_alive_notification_)
+    Stop();
+}
+
+void AliveChecker::CheckIfAlive() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+
+  // The reason we check a flag instead of stopping the timer that runs this
+  // function at suspend is that it would require knowing what state we're in
+  // when resuming and maybe start the timer. Also, we would still need this
+  // flag anyway to maybe start the timer at stream creation.
+  // TODO(grunell): Suspend/resume notifications are not supported on Linux. We
+  // could possibly use wall clock time as a complement to be able to detect
+  // time jumps that probably are caused by suspend/resume.
+  if (power_observer_ && power_observer_->IsSuspending())
+    return;
+
+  if (base::TimeTicks::Now() - last_alive_notification_time_ > timeout_) {
+    Stop();
+    detected_dead_ = true;
+    dead_callback_.Run();
+  }
+}
+
+void AliveChecker::SetLastAliveNotificationTimeToNowOnTaskRunner() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  last_alive_notification_time_ = base::TimeTicks::Now();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/alive_checker.h b/third_party/chromium/media/audio/alive_checker.h
new file mode 100644
index 0000000..bea12bc
--- /dev/null
+++ b/third_party/chromium/media/audio/alive_checker.h
@@ -0,0 +1,146 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ALIVE_CHECKER_H_
+#define MEDIA_AUDIO_ALIVE_CHECKER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "base/timer/timer.h"
+#include "media/audio/power_observer_helper.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// A class that checks if a client that is expected to have regular activity
+// is alive. For example, audio streams expect regular callbacks from the
+// operating system. The client informs regularly that it's alive by calling
+// NotifyAlive(). At a certain interval the AliveChecker checks that it has been
+// notified within a timeout period. If not, it runs a callback to inform about
+// detecting dead. The callback is run once and further checking is stopped at
+// detection. Checking can be restarted if desired.
+//
+// The AliveChecker can pause checking when the machine is suspending, i.e.
+// between suspend and resume notification from base::PowerMonitor. Checking
+// during this period can cause false positives. Shorter timeout gives higher
+// risk of false positives.
+//
+// It lives on the task runner it's created on; all functions except
+// NotifyAlive() must be called on it. NotifyAlive() can be called on any task
+// runner.
+//
+// It stops at the first NotifyAlive() call if
+// |stop_at_first_alive_notification| is specified at construction time. This
+// can be useful for example if the platform doesn't support suspend/resume
+// notifications, as Linux.
+class MEDIA_EXPORT AliveChecker {
+ public:
+  // Factory callback to create a PowerObserverHelper that can be injected. Can
+  // be used by tests to provide a mock.
+  using PowerObserverHelperFactoryCallback =
+      base::OnceCallback<std::unique_ptr<PowerObserverHelper>(
+          scoped_refptr<base::SequencedTaskRunner> task_runner,
+          base::RepeatingClosure suspend_callback,
+          base::RepeatingClosure resume_callback)>;
+
+  // See class description for general explanation of parameters.
+  // In addition the following must be true: |timeout| > |check_interval| > 0.
+  // The first version creates a PowerObserverHelper internally, the second
+  // version takes a factory callback to allow injecting a PowerObserverHelper,
+  // typically a mock for testing. The callback is run in the constructor. The
+  // second version doesn't have |pause_check_during_suspend|, since that's
+  // implicitly true when providing a PowerObserverHelper.
+  AliveChecker(base::RepeatingClosure dead_callback,
+               base::TimeDelta check_interval,
+               base::TimeDelta timeout,
+               bool stop_at_first_alive_notification,
+               bool pause_check_during_suspend);
+  AliveChecker(base::RepeatingClosure dead_callback,
+               base::TimeDelta check_interval,
+               base::TimeDelta timeout,
+               bool stop_at_first_alive_notification,
+               PowerObserverHelperFactoryCallback
+                   power_observer_helper_factory_callback);
+
+  AliveChecker(const AliveChecker&) = delete;
+  AliveChecker& operator=(const AliveChecker&) = delete;
+
+  ~AliveChecker();
+
+  // Start and stop checking if the client is alive.
+  void Start();
+  void Stop();
+
+  // Returns whether dead was detected. Reset when Start() is called.
+  bool DetectedDead();
+
+  // Called regularly by the client to inform that it's alive. Can be called on
+  // any thread.
+  void NotifyAlive();
+
+ private:
+  // Internal version called by the public constructors, to keep the interface
+  // and contract clear in the public versions.
+  AliveChecker(base::RepeatingClosure dead_callback,
+               base::TimeDelta check_interval,
+               base::TimeDelta timeout,
+               bool stop_at_first_alive_notification,
+               bool pause_check_during_suspend,
+               PowerObserverHelperFactoryCallback
+                   power_observer_helper_factory_callback);
+
+  // Checks if we have gotten an alive notification within a certain time
+  // period. If not, run |dead_callback_|.
+  void CheckIfAlive();
+
+  // Sets |last_alive_notification_time_| to the current time.
+  void SetLastAliveNotificationTimeToNowOnTaskRunner();
+
+  // Timer to run the check regularly.
+  std::unique_ptr<base::RepeatingTimer> check_alive_timer_;
+
+  // Stores the time NotifyAlive() was last called.
+  // TODO(grunell): Change from TimeTicks to Atomic32 and remove the task
+  // posting in NotifyAlive(). The Atomic32 variable would have to
+  // represent some time in seconds or tenths of seconds to be able to span over
+  // enough time. Atomic64 cannot be used since it's not supported on 32-bit
+  // platforms.
+  base::TimeTicks last_alive_notification_time_;
+
+  // The interval at which we check if alive.
+  const base::TimeDelta check_interval_;
+
+  // The time interval since |last_alive_notification_time_| after which we
+  // decide the client is dead and run |dead_callback_|.
+  const base::TimeDelta timeout_;
+
+  // Flags that dead was detected. Set in CheckIfAlive() if we have decided that
+  // the client is dead. Cleared in Start().
+  bool detected_dead_ = false;
+
+  // The task runner on which this object lives.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // Dead notification callback.
+  base::RepeatingClosure dead_callback_;
+
+  // If true, checking stops after first alive notification, otherwise continues
+  // until Stop() is called or the client is decided to be dead.
+  const bool stop_at_first_alive_notification_;
+
+  // Used for getting suspend/resume notifications.
+  std::unique_ptr<PowerObserverHelper> power_observer_;
+
+  base::WeakPtrFactory<AliveChecker> weak_factory_{this};
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ALIVE_CHECKER_H_
diff --git a/third_party/chromium/media/audio/alive_checker_unittest.cc b/third_party/chromium/media/audio/alive_checker_unittest.cc
new file mode 100644
index 0000000..729cf86
--- /dev/null
+++ b/third_party/chromium/media/audio/alive_checker_unittest.cc
@@ -0,0 +1,552 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread.h"
+#include "media/audio/alive_checker.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+int kCheckIntervalMs = 10;
+int kNotifyIntervalMs = 7;
+int kTimeoutMs = 50;
+}  // namespace
+
+class MockPowerObserverHelper : public PowerObserverHelper {
+ public:
+  MockPowerObserverHelper(scoped_refptr<base::SequencedTaskRunner> task_runner,
+                          base::RepeatingClosure suspend_callback,
+                          base::RepeatingClosure resume_callback)
+
+      : PowerObserverHelper(std::move(task_runner),
+                            std::move(suspend_callback),
+                            std::move(resume_callback)) {}
+
+  bool IsSuspending() const override {
+    DCHECK(TaskRunnerForTesting()->RunsTasksInCurrentSequence());
+    return is_suspending_;
+  }
+
+  void Suspend() {
+    DCHECK(TaskRunnerForTesting()->RunsTasksInCurrentSequence());
+    is_suspending_ = true;
+    SuspendCallbackForTesting()->Run();
+  }
+
+  void Resume() {
+    DCHECK(TaskRunnerForTesting()->RunsTasksInCurrentSequence());
+    is_suspending_ = false;
+    ResumeCallbackForTesting()->Run();
+  }
+
+ private:
+  bool is_suspending_ = false;
+};
+
+class AliveCheckerTest : public testing::Test {
+ public:
+  AliveCheckerTest()
+      : alive_checker_thread_("AliveCheckerThread"),
+        detected_dead_event_(base::WaitableEvent::ResetPolicy::MANUAL,
+                             base::WaitableEvent::InitialState::NOT_SIGNALED) {
+    alive_checker_thread_.StartAndWaitForTesting();
+  }
+
+  void OnDetectedDead() {
+    EXPECT_TRUE(alive_checker_thread_.task_runner()->BelongsToCurrentThread());
+    detected_dead_event_.Signal();
+  }
+
+  std::unique_ptr<PowerObserverHelper> CreatePowerObserverHelper(
+      scoped_refptr<base::SequencedTaskRunner> task_runner,
+      base::RepeatingClosure suspend_callback,
+      base::RepeatingClosure resume_callback) {
+    std::unique_ptr<MockPowerObserverHelper> mock_power_observer_helper =
+        std::make_unique<MockPowerObserverHelper>(std::move(task_runner),
+                                                  std::move(suspend_callback),
+                                                  std::move(resume_callback));
+    mock_power_observer_helper_ = mock_power_observer_helper.get();
+    return mock_power_observer_helper;
+  }
+
+ protected:
+  ~AliveCheckerTest() override {
+    base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
+                             base::WaitableEvent::InitialState::NOT_SIGNALED);
+    alive_checker_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&AliveCheckerTest::ResetAliveCheckerOnAliveCheckerThread,
+                       base::Unretained(this), &done));
+    done.Wait();
+  }
+
+  void CreateAliveChecker(bool stop_at_first_alive_notification,
+                          bool pause_check_during_suspend) {
+    base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
+                             base::WaitableEvent::InitialState::NOT_SIGNALED);
+    alive_checker_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &AliveCheckerTest::CreateAliveCheckerOnAliveCheckerThread,
+            base::Unretained(this), stop_at_first_alive_notification,
+            pause_check_during_suspend, &done));
+    done.Wait();
+  }
+
+  void StartAliveChecker() {
+    alive_checker_thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&AliveChecker::Start,
+                                  base::Unretained(alive_checker_.get())));
+  }
+
+  void StopAliveChecker() {
+    alive_checker_thread_.task_runner()->PostTask(
+        FROM_HERE, base::BindOnce(&AliveChecker::Stop,
+                                  base::Unretained(alive_checker_.get())));
+  }
+
+  // Notifies |alive_checker_| that we're alive, and if
+  // |remaining_notifications| > 1, posts a delayed task to itself on
+  // |alive_checker_thread_| with |remaining_notifications| decreased by 1. Can
+  // be called on any task runner.
+  void NotifyAliveMultipleTimes(int remaining_notifications,
+                                base::TimeDelta delay) {
+    alive_checker_->NotifyAlive();
+    if (remaining_notifications > 1) {
+      alive_checker_thread_.task_runner()->PostDelayedTask(
+          FROM_HERE,
+          base::BindOnce(&AliveCheckerTest::NotifyAliveMultipleTimes,
+                         base::Unretained(this), remaining_notifications - 1,
+                         delay),
+          delay);
+    }
+  }
+
+  void WaitUntilDetectedDead() {
+    detected_dead_event_.Wait();
+    detected_dead_event_.Reset();
+  }
+
+  // Returns true if the dead callback (AliveCheckerTest::OnDetectedDead) is run
+  // by the AliveChecker, false if timed out.
+  bool WaitUntilDetectedDeadWithTimeout(base::TimeDelta timeout) {
+    bool signaled = detected_dead_event_.TimedWait(timeout);
+    detected_dead_event_.Reset();
+    return signaled;
+  }
+
+  // Calls AliveChecker::DetectedDead() on the |alive_checker_thread_| and
+  // returns the result.
+  bool GetDetectedDead() {
+    bool detected_dead = false;
+    base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
+                             base::WaitableEvent::InitialState::NOT_SIGNALED);
+    alive_checker_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&AliveCheckerTest::GetDetectedDeadOnAliveCheckerThread,
+                       base::Unretained(this), &detected_dead, &done));
+    done.Wait();
+    return detected_dead;
+  }
+
+  // The test task environment.
+  base::test::TaskEnvironment task_environment_;
+
+  // The thread the checker is run on.
+  base::Thread alive_checker_thread_;
+
+  // AliveChecker under test.
+  std::unique_ptr<AliveChecker> alive_checker_;
+
+  // Mocks suspend status. Set in CreatePowerObserverHelper, owned by
+  // |alive_checker_|.
+  MockPowerObserverHelper* mock_power_observer_helper_;
+
+ private:
+  void CreateAliveCheckerOnAliveCheckerThread(
+      bool stop_at_first_alive_notification,
+      bool pause_check_during_suspend,
+      base::WaitableEvent* done) {
+    EXPECT_TRUE(alive_checker_thread_.task_runner()->BelongsToCurrentThread());
+
+    if (pause_check_during_suspend) {
+      alive_checker_ = std::make_unique<AliveChecker>(
+          base::BindRepeating(&AliveCheckerTest::OnDetectedDead,
+                              base::Unretained(this)),
+          base::Milliseconds(kCheckIntervalMs), base::Milliseconds(kTimeoutMs),
+          stop_at_first_alive_notification,
+          base::BindOnce(&AliveCheckerTest::CreatePowerObserverHelper,
+                         base::Unretained(this)));
+    } else {
+      alive_checker_ = std::make_unique<AliveChecker>(
+          base::BindRepeating(&AliveCheckerTest::OnDetectedDead,
+                              base::Unretained(this)),
+          base::Milliseconds(kCheckIntervalMs), base::Milliseconds(kTimeoutMs),
+          stop_at_first_alive_notification, false);
+    }
+
+    done->Signal();
+  }
+
+  void GetDetectedDeadOnAliveCheckerThread(bool* detected_dead,
+                                           base::WaitableEvent* done) {
+    EXPECT_TRUE(alive_checker_thread_.task_runner()->BelongsToCurrentThread());
+    *detected_dead = alive_checker_->DetectedDead();
+    done->Signal();
+  }
+
+  void ResetAliveCheckerOnAliveCheckerThread(base::WaitableEvent* done) {
+    EXPECT_TRUE(alive_checker_thread_.task_runner()->BelongsToCurrentThread());
+    alive_checker_.reset();
+    done->Signal();
+  }
+
+  // Event to signal that we got a dead detection callback.
+  base::WaitableEvent detected_dead_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(AliveCheckerTest);
+};
+
+// Start and Stop the checker, verify that we get no dead detection.
+// TODO(crbug.com/789804): Fix the test not to be flaky, e.g. by switching to
+// using a mocked clock, and re-enable it.
+TEST_F(AliveCheckerTest, DISABLED_StartStop) {
+  CreateAliveChecker(false, false);
+
+  StartAliveChecker();
+  EXPECT_FALSE(GetDetectedDead());
+
+  StopAliveChecker();
+  EXPECT_FALSE(GetDetectedDead());
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+}
+
+// Start the checker, don't send alive notifications, and run until it detects
+// dead. Verify that it only detects once. Repeat once.
+TEST_F(AliveCheckerTest, NoAliveNotificationsDetectTwice) {
+  CreateAliveChecker(false, false);
+
+  StartAliveChecker();
+  EXPECT_FALSE(GetDetectedDead());
+
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+
+  // Verify that AliveChecker doesn't detect (runs the callback) a second time.
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this. The detect state should still be that we have detected
+  // dead.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_TRUE(GetDetectedDead());
+
+  // Start again, the detect state should be reset.
+  StartAliveChecker();
+  EXPECT_FALSE(GetDetectedDead());
+
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+}
+
+// Start the checker, notify that the client is alive several times, then stop
+// the checker. Verify that it doesn't detect dead.
+// TODO(crbug.com/789804): Fix the test not to be flaky, e.g. by switching to
+// using a mocked clock, and re-enable it.
+TEST_F(AliveCheckerTest, DISABLED_NotifyThenStop) {
+  CreateAliveChecker(false, false);
+
+  StartAliveChecker();
+  EXPECT_FALSE(GetDetectedDead());
+
+  NotifyAliveMultipleTimes(10, base::Milliseconds(kNotifyIntervalMs));
+  EXPECT_FALSE(GetDetectedDead());
+
+  StopAliveChecker();
+  EXPECT_FALSE(GetDetectedDead());
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+}
+
+// Start the checker, notify that the client is alive several times, then
+// run until detection. Repeat once.
+// TODO(crbug.com/789804): Fix the test not to be flaky, e.g. by switching to
+// using a mocked clock, and re-enable it.
+TEST_F(AliveCheckerTest, DISABLED_NotifyThenDetectDead) {
+  CreateAliveChecker(false, false);
+
+  StartAliveChecker();
+  NotifyAliveMultipleTimes(10, base::Milliseconds(kNotifyIntervalMs));
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+
+  StartAliveChecker();
+  EXPECT_FALSE(GetDetectedDead());
+  NotifyAliveMultipleTimes(10, base::Milliseconds(kNotifyIntervalMs));
+  EXPECT_FALSE(GetDetectedDead());
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+}
+
+// Setup the checker to stop at first alive notification. Start it and notify
+// that the client is alive once. Verify that we get no dead detection.
+TEST_F(AliveCheckerTest, StopAtFirstAliveNotification_DoNotify) {
+  CreateAliveChecker(true, false);
+
+  StartAliveChecker();
+  alive_checker_->NotifyAlive();
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+}
+
+// Setup the checker to stop at first alive notification. Start it and run until
+// it detects dead.
+TEST_F(AliveCheckerTest, StopAtFirstAliveNotification_DontNotify) {
+  CreateAliveChecker(true, false);
+  StartAliveChecker();
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+}
+
+// Setup the checker to pause checking when suspended. Start the checker, don't
+// send alive notifications, and run until it detects dead. Start it again and
+// notify that the client is alive several times. Suspend and verify that it
+// doesn't detect dead. Resume and run until detected dead.
+// TODO(crbug.com/789804): Fix the test not to be flaky, e.g. by switching to
+// using a mocked clock, and re-enable it.
+TEST_F(AliveCheckerTest, DISABLED_SuspendResume_StartBeforeSuspend) {
+  CreateAliveChecker(false, true);
+  ASSERT_TRUE(mock_power_observer_helper_);
+
+  StartAliveChecker();
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+
+  StartAliveChecker();
+  EXPECT_FALSE(GetDetectedDead());
+
+  NotifyAliveMultipleTimes(10, base::Milliseconds(kNotifyIntervalMs));
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+}
+
+// Setup the checker to pause checking when suspended. Suspend and verify that
+// it doesn't detect dead. Start the checker, don't send alive notifications,
+// and and verify that it doesn't detect dead. Resume and run until it detects
+// dead.
+TEST_F(AliveCheckerTest, SuspendResume_StartBetweenSuspendAndResume) {
+  CreateAliveChecker(false, true);
+  ASSERT_TRUE(mock_power_observer_helper_);
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  StartAliveChecker();
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+}
+
+// Setup the checker to stop at first alive notification and pause checking when
+// suspended. Start the checker, send one alive notifications, and verify it
+// doesn't detect dead. Suspend and verify that it doesn't detect dead. Resume
+// and and verify that it doesn't detect dead.
+TEST_F(AliveCheckerTest, SuspendResumeWithAutoStop_NotifyBeforeSuspend) {
+  CreateAliveChecker(true, true);
+  ASSERT_TRUE(mock_power_observer_helper_);
+
+  StartAliveChecker();
+  alive_checker_->NotifyAlive();
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+}
+
+// Setup the checker to stop at first alive notification and pause checking when
+// suspended. Start the checker, send one alive notifications, and verify it
+// doesn't detect dead. Start it again, suspend and verify that it doesn't
+// detect dead. Resume and run until detected dead.
+TEST_F(AliveCheckerTest,
+       SuspendResumeWithAutoStop_NotifyBeforeSuspendAndRestart) {
+  CreateAliveChecker(true, true);
+  ASSERT_TRUE(mock_power_observer_helper_);
+
+  StartAliveChecker();
+  alive_checker_->NotifyAlive();
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+
+  StartAliveChecker();
+  EXPECT_FALSE(GetDetectedDead());
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+}
+
+// Setup the checker to stop at first alive notification and pause checking when
+// suspended. Start the checker, suspend. Send one alive notification and
+// verify it doesn't detected dead. Resume and verify it doesn't detected dead.
+TEST_F(AliveCheckerTest,
+       SuspendResumeWithAutoStop_NotifyBetweenSuspendAndResume) {
+  CreateAliveChecker(true, true);
+  ASSERT_TRUE(mock_power_observer_helper_);
+
+  StartAliveChecker();
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  alive_checker_->NotifyAlive();
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+}
+
+// Setup the checker to stop at first alive notification and pause checking when
+// suspended. Start the checker, suspend, resume, send one alive notification
+// and verify it doesn't detected dead.
+TEST_F(AliveCheckerTest, SuspendResumeWithAutoStop_NotifyAfterResume) {
+  CreateAliveChecker(true, true);
+  ASSERT_TRUE(mock_power_observer_helper_);
+
+  StartAliveChecker();
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  alive_checker_->NotifyAlive();
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+}
+
+// Setup the checker to stop at first alive notification and pause checking when
+// suspended. Start the checker suspend, and and verify it doesn't detected
+// dead. Resume and run until it detects dead.
+TEST_F(AliveCheckerTest, SuspendResumeWithAutoStop_DontNotify) {
+  CreateAliveChecker(true, true);
+  ASSERT_TRUE(mock_power_observer_helper_);
+
+  StartAliveChecker();
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Suspend,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  // It can take up to the timeout + the check interval until detection. Add a
+  // margin to this.
+  EXPECT_FALSE(WaitUntilDetectedDeadWithTimeout(
+      base::Milliseconds(kTimeoutMs + kCheckIntervalMs + 10)));
+  EXPECT_FALSE(GetDetectedDead());
+
+  alive_checker_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&MockPowerObserverHelper::Resume,
+                                base::Unretained(mock_power_observer_helper_)));
+
+  WaitUntilDetectedDead();
+  EXPECT_TRUE(GetDetectedDead());
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/alsa/alsa_input.cc b/third_party/chromium/media/audio/alsa/alsa_input.cc
new file mode 100644
index 0000000..e4eaa0b
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_input.cc
@@ -0,0 +1,372 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/alsa/alsa_input.h"
+
+#include <stddef.h>
+
+#include "base/bind.h"
+#include "base/cxx17_backports.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "media/audio/alsa/alsa_output.h"
+#include "media/audio/alsa/alsa_util.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+#include "media/audio/alsa/audio_manager_alsa.h"
+#include "media/audio/audio_manager.h"
+
+namespace media {
+
+static const SampleFormat kSampleFormat = kSampleFormatS16;
+static const snd_pcm_format_t kAlsaSampleFormat = SND_PCM_FORMAT_S16;
+
+static const int kNumPacketsInRingBuffer = 3;
+
+static const char kDefaultDevice1[] = "default";
+static const char kDefaultDevice2[] = "plug:default";
+
+const char AlsaPcmInputStream::kAutoSelectDevice[] = "";
+
+AlsaPcmInputStream::AlsaPcmInputStream(AudioManagerBase* audio_manager,
+                                       const std::string& device_name,
+                                       const AudioParameters& params,
+                                       AlsaWrapper* wrapper)
+    : audio_manager_(audio_manager),
+      device_name_(device_name),
+      params_(params),
+      bytes_per_buffer_(params.GetBytesPerBuffer(kSampleFormat)),
+      wrapper_(wrapper),
+      buffer_duration_(base::Microseconds(
+          params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond /
+          static_cast<float>(params.sample_rate()))),
+      callback_(nullptr),
+      device_handle_(nullptr),
+      mixer_handle_(nullptr),
+      mixer_element_handle_(nullptr),
+      read_callback_behind_schedule_(false),
+      audio_bus_(AudioBus::Create(params)),
+      capture_thread_("AlsaInput"),
+      running_(false) {}
+
+AlsaPcmInputStream::~AlsaPcmInputStream() = default;
+
+AudioInputStream::OpenOutcome AlsaPcmInputStream::Open() {
+  if (device_handle_)
+    return OpenOutcome::kAlreadyOpen;
+
+  uint32_t packet_us = buffer_duration_.InMicroseconds();
+  uint32_t buffer_us = packet_us * kNumPacketsInRingBuffer;
+
+  // Use the same minimum required latency as output.
+  buffer_us = std::max(buffer_us, AlsaPcmOutputStream::kMinLatencyMicros);
+
+  if (device_name_ == kAutoSelectDevice) {
+    const char* device_names[] = { kDefaultDevice1, kDefaultDevice2 };
+    for (size_t i = 0; i < base::size(device_names); ++i) {
+      device_handle_ = alsa_util::OpenCaptureDevice(
+          wrapper_, device_names[i], params_.channels(), params_.sample_rate(),
+          kAlsaSampleFormat, buffer_us, packet_us);
+
+      if (device_handle_) {
+        device_name_ = device_names[i];
+        break;
+      }
+    }
+  } else {
+    device_handle_ = alsa_util::OpenCaptureDevice(
+        wrapper_, device_name_.c_str(), params_.channels(),
+        params_.sample_rate(), kAlsaSampleFormat, buffer_us, packet_us);
+  }
+
+  if (device_handle_) {
+    audio_buffer_.reset(new uint8_t[bytes_per_buffer_]);
+
+    // Open the microphone mixer.
+    mixer_handle_ = alsa_util::OpenMixer(wrapper_, device_name_);
+    if (mixer_handle_) {
+      mixer_element_handle_ = alsa_util::LoadCaptureMixerElement(
+          wrapper_, mixer_handle_);
+    }
+  }
+
+  return device_handle_ != nullptr ? OpenOutcome::kSuccess
+                                   : OpenOutcome::kFailed;
+}
+
+void AlsaPcmInputStream::Start(AudioInputCallback* callback) {
+  DCHECK(!callback_ && callback);
+  callback_ = callback;
+  StartAgc();
+  int error = wrapper_->PcmPrepare(device_handle_);
+  if (error < 0) {
+    HandleError("PcmPrepare", error);
+  } else {
+    error = wrapper_->PcmStart(device_handle_);
+    if (error < 0)
+      HandleError("PcmStart", error);
+  }
+
+  if (error < 0) {
+    callback_ = nullptr;
+  } else {
+    base::Thread::Options options;
+    options.priority = base::ThreadPriority::REALTIME_AUDIO;
+    CHECK(capture_thread_.StartWithOptions(std::move(options)));
+
+    // We start reading data half |buffer_duration_| later than when the
+    // buffer might have got filled, to accommodate some delays in the audio
+    // driver. This could also give us a smooth read sequence going forward.
+    base::TimeDelta delay = buffer_duration_ + buffer_duration_ / 2;
+    next_read_time_ = base::TimeTicks::Now() + delay;
+    running_ = true;
+    capture_thread_.task_runner()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&AlsaPcmInputStream::ReadAudio, base::Unretained(this)),
+        delay);
+  }
+}
+
+bool AlsaPcmInputStream::Recover(int original_error) {
+  DCHECK(capture_thread_.task_runner()->BelongsToCurrentThread());
+  int error = wrapper_->PcmRecover(device_handle_, original_error, 1);
+  if (error < 0) {
+    // Docs say snd_pcm_recover returns the original error if it is not one
+    // of the recoverable ones, so this log message will probably contain the
+    // same error twice.
+    LOG(WARNING) << "Unable to recover from \""
+                 << wrapper_->StrError(original_error) << "\": "
+                 << wrapper_->StrError(error);
+    return false;
+  }
+
+  if (original_error == -EPIPE) {  // Buffer underrun/overrun.
+    // For capture streams we have to repeat the explicit start() to get
+    // data flowing again.
+    error = wrapper_->PcmStart(device_handle_);
+    if (error < 0) {
+      HandleError("PcmStart", error);
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void AlsaPcmInputStream::StopRunningOnCaptureThread() {
+  DCHECK(capture_thread_.IsRunning());
+  if (!capture_thread_.task_runner()->BelongsToCurrentThread()) {
+    capture_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&AlsaPcmInputStream::StopRunningOnCaptureThread,
+                       base::Unretained(this)));
+    return;
+  }
+  running_ = false;
+}
+
+void AlsaPcmInputStream::ReadAudio() {
+  DCHECK(capture_thread_.task_runner()->BelongsToCurrentThread());
+  DCHECK(callback_);
+  if (!running_)
+    return;
+
+  snd_pcm_sframes_t frames = wrapper_->PcmAvailUpdate(device_handle_);
+  if (frames < 0) {  // Potentially recoverable error?
+    LOG(WARNING) << "PcmAvailUpdate(): " << wrapper_->StrError(frames);
+    Recover(frames);
+  }
+
+  if (frames < params_.frames_per_buffer()) {
+    // Not enough data yet or error happened. In both cases wait for a very
+    // small duration before checking again.
+    // Even Though read callback was behind schedule, there is no data, so
+    // reset the next_read_time_.
+    if (read_callback_behind_schedule_) {
+      next_read_time_ = base::TimeTicks::Now();
+      read_callback_behind_schedule_ = false;
+    }
+
+    base::TimeDelta next_check_time = buffer_duration_ / 2;
+    capture_thread_.task_runner()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&AlsaPcmInputStream::ReadAudio, base::Unretained(this)),
+        next_check_time);
+    return;
+  }
+
+  // Update the AGC volume level once every second. Note that, |volume| is
+  // also updated each time SetVolume() is called through IPC by the
+  // render-side AGC.
+  double normalized_volume = 0.0;
+  GetAgcVolume(&normalized_volume);
+
+  int num_buffers = frames / params_.frames_per_buffer();
+  while (num_buffers--) {
+    int frames_read = wrapper_->PcmReadi(device_handle_, audio_buffer_.get(),
+                                         params_.frames_per_buffer());
+    if (frames_read == params_.frames_per_buffer()) {
+      audio_bus_->FromInterleaved<SignedInt16SampleTypeTraits>(
+          reinterpret_cast<int16_t*>(audio_buffer_.get()),
+          audio_bus_->frames());
+
+      // TODO(dalecurtis): This should probably use snd_pcm_htimestamp() so that
+      // we can have |capture_time| directly instead of computing it as
+      // Now() - available frames.
+      snd_pcm_sframes_t avail_frames = wrapper_->PcmAvailUpdate(device_handle_);
+      if (avail_frames < 0) {
+        LOG(WARNING) << "PcmAvailUpdate(): "
+                     << wrapper_->StrError(avail_frames);
+        avail_frames = 0;  // Error getting number of avail frames, set it to 0
+      }
+      base::TimeDelta hardware_delay = base::Seconds(
+          avail_frames / static_cast<double>(params_.sample_rate()));
+
+      callback_->OnData(audio_bus_.get(),
+                        base::TimeTicks::Now() - hardware_delay,
+                        normalized_volume);
+    } else if (frames_read < 0) {
+      bool success = Recover(frames_read);
+      LOG(WARNING) << "PcmReadi failed with error "
+                   << wrapper_->StrError(frames_read) << ". "
+                   << (success ? "Successfully" : "Unsuccessfully")
+                   << " recovered.";
+    } else {
+      LOG(WARNING) << "PcmReadi returning less than expected frames: "
+                   << frames_read << " vs. " << params_.frames_per_buffer()
+                   << ". Dropping this buffer.";
+    }
+  }
+
+  next_read_time_ += buffer_duration_;
+  base::TimeDelta delay = next_read_time_ - base::TimeTicks::Now();
+  if (delay < base::TimeDelta()) {
+    DVLOG(1) << "Audio read callback behind schedule by "
+             << (buffer_duration_ - delay).InMicroseconds()
+             << " (us).";
+    // Read callback is behind schedule. Assuming there is data pending in
+    // the soundcard, invoke the read callback immediate in order to catch up.
+    read_callback_behind_schedule_ = true;
+    delay = base::TimeDelta();
+  }
+
+  capture_thread_.task_runner()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&AlsaPcmInputStream::ReadAudio, base::Unretained(this)),
+      delay);
+}
+
+void AlsaPcmInputStream::Stop() {
+  if (!device_handle_ || !callback_)
+    return;
+
+  StopAgc();
+
+  StopRunningOnCaptureThread();
+  capture_thread_.Stop();
+  int error = wrapper_->PcmDrop(device_handle_);
+  if (error < 0)
+    HandleError("PcmDrop", error);
+
+  callback_ = nullptr;
+}
+
+void AlsaPcmInputStream::Close() {
+  if (device_handle_) {
+    Stop();
+    int error = alsa_util::CloseDevice(wrapper_, device_handle_);
+    if (error < 0)
+      HandleError("PcmClose", error);
+
+    if (mixer_handle_)
+      alsa_util::CloseMixer(wrapper_, mixer_handle_, device_name_);
+
+    audio_buffer_.reset();
+    device_handle_ = nullptr;
+    mixer_handle_ = nullptr;
+    mixer_element_handle_ = nullptr;
+  }
+
+  audio_manager_->ReleaseInputStream(this);
+}
+
+double AlsaPcmInputStream::GetMaxVolume() {
+  if (!mixer_handle_ || !mixer_element_handle_) {
+    DLOG(WARNING) << "GetMaxVolume is not supported for " << device_name_;
+    return 0.0;
+  }
+
+  if (!wrapper_->MixerSelemHasCaptureVolume(mixer_element_handle_)) {
+    DLOG(WARNING) << "Unsupported microphone volume for " << device_name_;
+    return 0.0;
+  }
+
+  long min = 0;
+  long max = 0;
+  if (wrapper_->MixerSelemGetCaptureVolumeRange(mixer_element_handle_,
+                                                &min,
+                                                &max)) {
+    DLOG(WARNING) << "Unsupported max microphone volume for " << device_name_;
+    return 0.0;
+  }
+  DCHECK(min == 0);
+  DCHECK(max > 0);
+
+  return static_cast<double>(max);
+}
+
+void AlsaPcmInputStream::SetVolume(double volume) {
+  if (!mixer_handle_ || !mixer_element_handle_) {
+    DLOG(WARNING) << "SetVolume is not supported for " << device_name_;
+    return;
+  }
+
+  int error = wrapper_->MixerSelemSetCaptureVolumeAll(
+      mixer_element_handle_, static_cast<long>(volume));
+  if (error < 0) {
+    DLOG(WARNING) << "Unable to set volume for " << device_name_;
+  }
+
+  // Update the AGC volume level based on the last setting above. Note that,
+  // the volume-level resolution is not infinite and it is therefore not
+  // possible to assume that the volume provided as input parameter can be
+  // used directly. Instead, a new query to the audio hardware is required.
+  // This method does nothing if AGC is disabled.
+  UpdateAgcVolume();
+}
+
+double AlsaPcmInputStream::GetVolume() {
+  if (!mixer_handle_ || !mixer_element_handle_) {
+    DLOG(WARNING) << "GetVolume is not supported for " << device_name_;
+    return 0.0;
+  }
+
+  long current_volume = 0;
+  int error = wrapper_->MixerSelemGetCaptureVolume(
+      mixer_element_handle_, static_cast<snd_mixer_selem_channel_id_t>(0),
+      &current_volume);
+  if (error < 0) {
+    DLOG(WARNING) << "Unable to get volume for " << device_name_;
+    return 0.0;
+  }
+
+  return static_cast<double>(current_volume);
+}
+
+bool AlsaPcmInputStream::IsMuted() {
+  return false;
+}
+
+void AlsaPcmInputStream::SetOutputDeviceForAec(
+    const std::string& output_device_id) {
+  // Not supported. Do nothing.
+}
+
+void AlsaPcmInputStream::HandleError(const char* method, int error) {
+  LOG(WARNING) << method << ": " << wrapper_->StrError(error);
+  if (callback_)
+    callback_->OnError();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/alsa/alsa_input.h b/third_party/chromium/media/audio/alsa/alsa_input.h
new file mode 100644
index 0000000..f96b391
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_input.h
@@ -0,0 +1,102 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ALSA_ALSA_INPUT_H_
+#define MEDIA_AUDIO_ALSA_ALSA_INPUT_H_
+
+#include <alsa/asoundlib.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "media/audio/agc_audio_stream.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AlsaWrapper;
+class AudioManagerBase;
+
+// Provides an input stream for audio capture based on the ALSA PCM interface.
+// This object is not thread safe and all methods should be invoked in the
+// thread that created the object.
+class MEDIA_EXPORT AlsaPcmInputStream
+    : public AgcAudioStream<AudioInputStream> {
+ public:
+  // Pass this to the constructor if you want to attempt auto-selection
+  // of the audio recording device.
+  static const char kAutoSelectDevice[];
+
+  // Create a PCM Output stream for the ALSA device identified by
+  // |device_name|. If unsure of what to use for |device_name|, use
+  // |kAutoSelectDevice|.
+  AlsaPcmInputStream(AudioManagerBase* audio_manager,
+                     const std::string& device_name,
+                     const AudioParameters& params,
+                     AlsaWrapper* wrapper);
+
+  AlsaPcmInputStream(const AlsaPcmInputStream&) = delete;
+  AlsaPcmInputStream& operator=(const AlsaPcmInputStream&) = delete;
+
+  ~AlsaPcmInputStream() override;
+
+  // Implementation of AudioInputStream.
+  OpenOutcome Open() override;
+  void Start(AudioInputCallback* callback) override;
+  void Stop() override;
+  void Close() override;
+  double GetMaxVolume() override;
+  void SetVolume(double volume) override;
+  double GetVolume() override;
+  bool IsMuted() override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+ private:
+  // Logs the error and invokes any registered callbacks.
+  void HandleError(const char* method, int error);
+
+  // Reads one or more buffers of audio from the device, passes on to the
+  // registered callback and schedules the next read.
+  void ReadAudio();
+
+  // Recovers from any device errors if possible.
+  bool Recover(int error);
+
+  // Set |running_| to false on |capture_thread_|.
+  void StopRunningOnCaptureThread();
+
+  // Non-refcounted pointer back to the audio manager.
+  // The AudioManager indirectly holds on to stream objects, so we don't
+  // want circular references.  Additionally, stream objects live on the audio
+  // thread, which is owned by the audio manager and we don't want to addref
+  // the manager from that thread.
+  AudioManagerBase* audio_manager_;
+  std::string device_name_;
+  AudioParameters params_;
+  int bytes_per_buffer_;
+  AlsaWrapper* wrapper_;
+  base::TimeDelta buffer_duration_;  // Length of each recorded buffer.
+  AudioInputCallback* callback_;  // Valid during a recording session.
+  base::TimeTicks next_read_time_;  // Scheduled time for next read callback.
+  snd_pcm_t* device_handle_;  // Handle to the ALSA PCM recording device.
+  snd_mixer_t* mixer_handle_; // Handle to the ALSA microphone mixer.
+  snd_mixer_elem_t* mixer_element_handle_; // Handle to the capture element.
+  // Buffer used for reading audio data.
+  std::unique_ptr<uint8_t[]> audio_buffer_;
+  bool read_callback_behind_schedule_;
+  std::unique_ptr<AudioBus> audio_bus_;
+  base::Thread capture_thread_;
+  bool running_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ALSA_ALSA_INPUT_H_
diff --git a/third_party/chromium/media/audio/alsa/alsa_output.cc b/third_party/chromium/media/audio/alsa/alsa_output.cc
new file mode 100644
index 0000000..a05f60d
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_output.cc
@@ -0,0 +1,808 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// THREAD SAFETY
+//
+// AlsaPcmOutputStream object is *not* thread-safe and should only be used
+// from the audio thread.  We DCHECK on this assumption whenever we can.
+//
+// SEMANTICS OF Close()
+//
+// Close() is responsible for cleaning up any resources that were acquired after
+// a successful Open().  Close() will nullify any scheduled outstanding runnable
+// methods.
+//
+//
+// SEMANTICS OF ERROR STATES
+//
+// The object has two distinct error states: |state_| == kInError
+// and |stop_stream_|.  The |stop_stream_| variable is used to indicate
+// that the playback_handle should no longer be used either because of a
+// hardware/low-level event.
+//
+// When |state_| == kInError, all public API functions will fail with an error
+// (Start() will call the OnError() function on the callback immediately), or
+// no-op themselves with the exception of Close().  Even if an error state has
+// been entered, if Open() has previously returned successfully, Close() must be
+// called to cleanup the ALSA devices and release resources.
+//
+// When |stop_stream_| is set, no more commands will be made against the
+// ALSA device, and playback will effectively stop.  From the client's point of
+// view, it will seem that the device has just clogged and stopped requesting
+// data.
+
+#include "media/audio/alsa/alsa_output.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/free_deleter.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/default_tick_clock.h"
+#include "base/trace_event/trace_event.h"
+#include "media/audio/alsa/alsa_util.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+#include "media/audio/alsa/audio_manager_alsa.h"
+#include "media/base/audio_timestamp_helper.h"
+#include "media/base/channel_mixer.h"
+#include "media/base/data_buffer.h"
+#include "media/base/seekable_buffer.h"
+
+namespace media {
+
+// Set to 0 during debugging if you want error messages due to underrun
+// events or other recoverable errors.
+#if defined(NDEBUG)
+static const int kPcmRecoverIsSilent = 1;
+#else
+static const int kPcmRecoverIsSilent = 0;
+#endif
+
+// The output channel layout if we set up downmixing for the kDefaultDevice
+// device.
+static const ChannelLayout kDefaultOutputChannelLayout = CHANNEL_LAYOUT_STEREO;
+
+// While the "default" device may support multi-channel audio, in Alsa, only
+// the device names surround40, surround41, surround50, etc, have a defined
+// channel mapping according to Lennart:
+//
+// http://0pointer.de/blog/projects/guide-to-sound-apis.html
+//
+// This function makes a best guess at the specific > 2 channel device name
+// based on the number of channels requested.  nullptr is returned if no device
+// can be found to match the channel numbers.  In this case, using
+// kDefaultDevice is probably the best bet.
+//
+// A five channel source is assumed to be surround50 instead of surround41
+// (which is also 5 channels).
+//
+// TODO(ajwong): The source data should have enough info to tell us if we want
+// surround41 versus surround51, etc., instead of needing us to guess based on
+// channel number.  Fix API to pass that data down.
+static const char* GuessSpecificDeviceName(uint32_t channels) {
+  switch (channels) {
+    case 8:
+      return "surround71";
+
+    case 7:
+      return "surround70";
+
+    case 6:
+      return "surround51";
+
+    case 5:
+      return "surround50";
+
+    case 4:
+      return "surround40";
+
+    default:
+      return nullptr;
+  }
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         AlsaPcmOutputStream::InternalState state) {
+  switch (state) {
+    case AlsaPcmOutputStream::kInError:
+      os << "kInError";
+      break;
+    case AlsaPcmOutputStream::kCreated:
+      os << "kCreated";
+      break;
+    case AlsaPcmOutputStream::kIsOpened:
+      os << "kIsOpened";
+      break;
+    case AlsaPcmOutputStream::kIsPlaying:
+      os << "kIsPlaying";
+      break;
+    case AlsaPcmOutputStream::kIsStopped:
+      os << "kIsStopped";
+      break;
+    case AlsaPcmOutputStream::kIsClosed:
+      os << "kIsClosed";
+      break;
+  }
+  return os;
+}
+
+static const SampleFormat kSampleFormat = kSampleFormatS16;
+static const snd_pcm_format_t kAlsaSampleFormat = SND_PCM_FORMAT_S16;
+
+const char AlsaPcmOutputStream::kDefaultDevice[] = "default";
+const char AlsaPcmOutputStream::kAutoSelectDevice[] = "";
+const char AlsaPcmOutputStream::kPlugPrefix[] = "plug:";
+
+// We use 40ms as our minimum required latency. If it is needed, we may be able
+// to get it down to 20ms.
+const uint32_t AlsaPcmOutputStream::kMinLatencyMicros = 40 * 1000;
+
+AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name,
+                                         const AudioParameters& params,
+                                         AlsaWrapper* wrapper,
+                                         AudioManagerBase* manager)
+    : requested_device_name_(device_name),
+      pcm_format_(kAlsaSampleFormat),
+      channels_(params.channels()),
+      channel_layout_(params.channel_layout()),
+      sample_rate_(params.sample_rate()),
+      bytes_per_sample_(SampleFormatToBytesPerChannel(kSampleFormat)),
+      bytes_per_frame_(params.GetBytesPerFrame(kSampleFormat)),
+      packet_size_(params.GetBytesPerBuffer(kSampleFormat)),
+      latency_(std::max(
+          base::Microseconds(kMinLatencyMicros),
+          AudioTimestampHelper::FramesToTime(params.frames_per_buffer() * 2,
+                                             sample_rate_))),
+      bytes_per_output_frame_(bytes_per_frame_),
+      alsa_buffer_frames_(0),
+      stop_stream_(false),
+      wrapper_(wrapper),
+      manager_(manager),
+      task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      playback_handle_(nullptr),
+      frames_per_packet_(packet_size_ / bytes_per_frame_),
+      state_(kCreated),
+      volume_(1.0f),
+      source_callback_(nullptr),
+      audio_bus_(AudioBus::Create(params)),
+      tick_clock_(base::DefaultTickClock::GetInstance()) {
+  DCHECK(manager_->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK_EQ(audio_bus_->frames() * bytes_per_frame_, packet_size_);
+
+  // Sanity check input values.
+  if (!params.IsValid()) {
+    LOG(WARNING) << "Unsupported audio parameters.";
+    TransitionTo(kInError);
+  }
+}
+
+AlsaPcmOutputStream::~AlsaPcmOutputStream() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  InternalState current_state = state();
+  DCHECK(current_state == kCreated ||
+         current_state == kIsClosed ||
+         current_state == kInError);
+  DCHECK(!playback_handle_);
+}
+
+bool AlsaPcmOutputStream::Open() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (state() == kInError)
+    return false;
+
+  if (!CanTransitionTo(kIsOpened)) {
+    NOTREACHED() << "Invalid state: " << state();
+    return false;
+  }
+
+  // We do not need to check if the transition was successful because
+  // CanTransitionTo() was checked above, and it is assumed that this
+  // object's public API is only called on one thread so the state cannot
+  // transition out from under us.
+  TransitionTo(kIsOpened);
+
+  // Try to open the device.
+  if (requested_device_name_ == kAutoSelectDevice) {
+    playback_handle_ = AutoSelectDevice(latency_.InMicroseconds());
+    if (playback_handle_)
+      DVLOG(1) << "Auto-selected device: " << device_name_;
+  } else {
+    device_name_ = requested_device_name_;
+    playback_handle_ = alsa_util::OpenPlaybackDevice(
+        wrapper_, device_name_.c_str(), channels_, sample_rate_,
+        pcm_format_, latency_.InMicroseconds());
+  }
+
+  // Finish initializing the stream if the device was opened successfully.
+  if (playback_handle_ == nullptr) {
+    stop_stream_ = true;
+    TransitionTo(kInError);
+    return false;
+  }
+  bytes_per_output_frame_ =
+      channel_mixer_ ? mixed_audio_bus_->channels() * bytes_per_sample_
+                     : bytes_per_frame_;
+  uint32_t output_packet_size = frames_per_packet_ * bytes_per_output_frame_;
+  buffer_ = std::make_unique<SeekableBuffer>(0, output_packet_size);
+
+  // Get alsa buffer size.
+  snd_pcm_uframes_t buffer_size;
+  snd_pcm_uframes_t period_size;
+  int error =
+      wrapper_->PcmGetParams(playback_handle_, &buffer_size, &period_size);
+  if (error < 0) {
+    LOG(ERROR) << "Failed to get playback buffer size from ALSA: "
+               << wrapper_->StrError(error);
+    // Buffer size is at least twice of packet size.
+    alsa_buffer_frames_ = frames_per_packet_ * 2;
+  } else {
+    alsa_buffer_frames_ = buffer_size;
+  }
+
+  return true;
+}
+
+void AlsaPcmOutputStream::Close() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (state() != kIsClosed)
+    TransitionTo(kIsClosed);
+
+  // Shutdown the audio device.
+  if (playback_handle_) {
+    if (alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) {
+      LOG(WARNING) << "Unable to close audio device. Leaking handle.";
+    }
+    playback_handle_ = nullptr;
+
+    // Release the buffer.
+    buffer_.reset();
+
+    // Signal anything that might already be scheduled to stop.
+    stop_stream_ = true;  // Not necessary in production, but unit tests
+                          // uses the flag to verify that stream was closed.
+  }
+
+  weak_factory_.InvalidateWeakPtrs();
+
+  // Signal to the manager that we're closed and can be removed.
+  // Should be last call in the method as it deletes "this".
+  manager_->ReleaseOutputStream(this);
+}
+
+void AlsaPcmOutputStream::Start(AudioSourceCallback* callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  CHECK(callback);
+
+  if (stop_stream_)
+    return;
+
+  // Only post the task if we can enter the playing state.
+  if (TransitionTo(kIsPlaying) != kIsPlaying)
+    return;
+
+  // Before starting, the buffer might have audio from previous user of this
+  // device.
+  buffer_->Clear();
+
+  // When starting again, drop all packets in the device and prepare it again
+  // in case we are restarting from a pause state and need to flush old data.
+  int error = wrapper_->PcmDrop(playback_handle_);
+  if (error < 0 && error != -EAGAIN) {
+    LOG(ERROR) << "Failure clearing playback device ("
+               << wrapper_->PcmName(playback_handle_) << "): "
+               << wrapper_->StrError(error);
+    stop_stream_ = true;
+    return;
+  }
+
+  error = wrapper_->PcmPrepare(playback_handle_);
+  if (error < 0 && error != -EAGAIN) {
+    LOG(ERROR) << "Failure preparing stream ("
+               << wrapper_->PcmName(playback_handle_) << "): "
+               << wrapper_->StrError(error);
+    stop_stream_ = true;
+    return;
+  }
+
+  // Ensure the first buffer is silence to avoid startup glitches.
+  int buffer_size = GetAvailableFrames() * bytes_per_output_frame_;
+  scoped_refptr<DataBuffer> silent_packet = new DataBuffer(buffer_size);
+  silent_packet->set_data_size(buffer_size);
+  memset(silent_packet->writable_data(), 0, silent_packet->data_size());
+  buffer_->Append(silent_packet);
+  WritePacket();
+
+  // Start the callback chain.
+  set_source_callback(callback);
+  WriteTask();
+}
+
+void AlsaPcmOutputStream::Stop() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Reset the callback, so that it is not called anymore.
+  set_source_callback(nullptr);
+  weak_factory_.InvalidateWeakPtrs();
+
+  TransitionTo(kIsStopped);
+}
+
+// This stream is always used with sub second buffer sizes, where it's
+// sufficient to simply always flush upon Start().
+void AlsaPcmOutputStream::Flush() {}
+
+void AlsaPcmOutputStream::SetVolume(double volume) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  volume_ = static_cast<float>(volume);
+}
+
+void AlsaPcmOutputStream::GetVolume(double* volume) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  *volume = volume_;
+}
+
+void AlsaPcmOutputStream::SetTickClockForTesting(
+    const base::TickClock* tick_clock) {
+  DCHECK(tick_clock);
+  tick_clock_ = tick_clock;
+}
+
+void AlsaPcmOutputStream::BufferPacket(bool* source_exhausted) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // If stopped, simulate a 0-length packet.
+  if (stop_stream_) {
+    buffer_->Clear();
+    *source_exhausted = true;
+    return;
+  }
+
+  *source_exhausted = false;
+
+  // Request more data only when we run out of data in the buffer, because
+  // WritePacket() consumes only the current chunk of data.
+  if (!buffer_->forward_bytes()) {
+    // Before making a request to source for data we need to determine the
+    // delay for the requested data to be played.
+    const base::TimeDelta delay =
+        AudioTimestampHelper::FramesToTime(GetCurrentDelay(), sample_rate_);
+
+    scoped_refptr<DataBuffer> packet = new DataBuffer(packet_size_);
+    int frames_filled =
+        RunDataCallback(delay, tick_clock_->NowTicks(), audio_bus_.get());
+
+    size_t packet_size = frames_filled * bytes_per_frame_;
+    DCHECK_LE(packet_size, packet_size_);
+
+    // TODO(dalecurtis): Channel downmixing, upmixing, should be done in mixer;
+    // volume adjust should use SSE optimized vector_fmul() prior to interleave.
+    AudioBus* output_bus = audio_bus_.get();
+    ChannelLayout output_channel_layout = channel_layout_;
+    if (channel_mixer_) {
+      output_bus = mixed_audio_bus_.get();
+      channel_mixer_->Transform(audio_bus_.get(), output_bus);
+      output_channel_layout = kDefaultOutputChannelLayout;
+      // Adjust packet size for downmix.
+      packet_size = packet_size / bytes_per_frame_ * bytes_per_output_frame_;
+    }
+
+    // Reorder channels for 5.0, 5.1, and 7.1 to match ALSA's channel order,
+    // which has front center at channel index 4 and LFE at channel index 5.
+    // See http://ffmpeg.org/pipermail/ffmpeg-cvslog/2011-June/038454.html.
+    switch (output_channel_layout) {
+      case CHANNEL_LAYOUT_5_0:
+      case CHANNEL_LAYOUT_5_0_BACK:
+        output_bus->SwapChannels(2, 3);
+        output_bus->SwapChannels(3, 4);
+        break;
+      case CHANNEL_LAYOUT_5_1:
+      case CHANNEL_LAYOUT_5_1_BACK:
+      case CHANNEL_LAYOUT_7_1:
+        output_bus->SwapChannels(2, 4);
+        output_bus->SwapChannels(3, 5);
+        break;
+      default:
+        break;
+    }
+
+    // Note: If this ever changes to output raw float the data must be clipped
+    // and sanitized since it may come from an untrusted source such as NaCl.
+    output_bus->Scale(volume_);
+    output_bus->ToInterleaved<SignedInt16SampleTypeTraits>(
+        frames_filled, reinterpret_cast<int16_t*>(packet->writable_data()));
+
+    if (packet_size > 0) {
+      packet->set_data_size(packet_size);
+      // Add the packet to the buffer.
+      buffer_->Append(packet);
+    } else {
+      *source_exhausted = true;
+    }
+  }
+}
+
+void AlsaPcmOutputStream::WritePacket() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // If the device is in error, just eat the bytes.
+  if (stop_stream_) {
+    buffer_->Clear();
+    return;
+  }
+
+  if (state() != kIsPlaying)
+    return;
+
+  CHECK_EQ(buffer_->forward_bytes() % bytes_per_output_frame_, 0u);
+
+  const uint8_t* buffer_data;
+  int buffer_size;
+  if (buffer_->GetCurrentChunk(&buffer_data, &buffer_size)) {
+    snd_pcm_sframes_t frames = std::min(
+        static_cast<snd_pcm_sframes_t>(buffer_size / bytes_per_output_frame_),
+        GetAvailableFrames());
+
+    if (!frames)
+      return;
+
+    snd_pcm_sframes_t frames_written =
+        wrapper_->PcmWritei(playback_handle_, buffer_data, frames);
+    if (frames_written < 0) {
+      // Attempt once to immediately recover from EINTR,
+      // EPIPE (overrun/underrun), ESTRPIPE (stream suspended).  WritePacket
+      // will eventually be called again, so eventual recovery will happen if
+      // muliple retries are required.
+      frames_written = wrapper_->PcmRecover(playback_handle_,
+                                            frames_written,
+                                            kPcmRecoverIsSilent);
+      if (frames_written < 0) {
+        if (frames_written != -EAGAIN) {
+          LOG(ERROR) << "Failed to write to pcm device: "
+                     << wrapper_->StrError(frames_written);
+          RunErrorCallback(frames_written);
+          stop_stream_ = true;
+        }
+      }
+    } else {
+      DCHECK_EQ(frames_written, frames);
+
+      // Seek forward in the buffer after we've written some data to ALSA.
+      buffer_->Seek(frames_written * bytes_per_output_frame_);
+    }
+  } else {
+    // If nothing left to write and playback hasn't started yet, start it now.
+    // This ensures that shorter sounds will still play.
+    if (playback_handle_ &&
+        (wrapper_->PcmState(playback_handle_) == SND_PCM_STATE_PREPARED) &&
+        GetCurrentDelay() > 0) {
+      wrapper_->PcmStart(playback_handle_);
+    }
+  }
+}
+
+void AlsaPcmOutputStream::WriteTask() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (stop_stream_)
+    return;
+
+  if (state() == kIsStopped)
+    return;
+
+  bool source_exhausted;
+  BufferPacket(&source_exhausted);
+  WritePacket();
+
+  ScheduleNextWrite(source_exhausted);
+}
+
+void AlsaPcmOutputStream::ScheduleNextWrite(bool source_exhausted) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (stop_stream_ || state() != kIsPlaying)
+    return;
+
+  const uint32_t kTargetFramesAvailable = alsa_buffer_frames_ / 2;
+  uint32_t available_frames = GetAvailableFrames();
+
+  base::TimeDelta next_fill_time;
+  if (buffer_->forward_bytes() && available_frames) {
+    // If we've got data available and ALSA has room, deliver it immediately.
+    next_fill_time = base::TimeDelta();
+  } else if (buffer_->forward_bytes()) {
+    // If we've got data available and no room, poll until room is available.
+    // Polling in this manner allows us to ensure a more consistent callback
+    // schedule.  In testing this yields a variance of +/- 5ms versus the non-
+    // polling strategy which is around +/- 30ms and bimodal.
+    next_fill_time = base::Milliseconds(5);
+  } else if (available_frames < kTargetFramesAvailable) {
+    // Schedule the next write for the moment when the available buffer of the
+    // sound card hits |kTargetFramesAvailable|.
+    next_fill_time = AudioTimestampHelper::FramesToTime(
+        kTargetFramesAvailable - available_frames, sample_rate_);
+  } else if (!source_exhausted) {
+    // The sound card has |kTargetFramesAvailable| or more frames available.
+    // Invoke the next write immediately to avoid underrun.
+    next_fill_time = base::TimeDelta();
+  } else {
+    // The sound card has frames available, but our source is exhausted, so
+    // avoid busy looping by delaying a bit.
+    next_fill_time = base::Milliseconds(10);
+  }
+
+  task_runner_->PostDelayedTask(FROM_HERE,
+                                base::BindOnce(&AlsaPcmOutputStream::WriteTask,
+                                               weak_factory_.GetWeakPtr()),
+                                next_fill_time);
+}
+
+std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32_t channels) {
+  // Constants specified by the ALSA API for device hints.
+  static const int kGetAllDevices = -1;
+  static const char kPcmInterfaceName[] = "pcm";
+  static const char kIoHintName[] = "IOID";
+  static const char kNameHintName[] = "NAME";
+
+  const char* wanted_device = GuessSpecificDeviceName(channels);
+  if (!wanted_device)
+    return std::string();
+
+  std::string guessed_device;
+  void** hints = nullptr;
+  int error = wrapper_->DeviceNameHint(kGetAllDevices,
+                                       kPcmInterfaceName,
+                                       &hints);
+  if (error == 0) {
+    // NOTE: Do not early return from inside this if statement.  The
+    // hints above need to be freed.
+    for (void** hint_iter = hints; *hint_iter != nullptr; hint_iter++) {
+      // Only examine devices that are output capable..  Valid values are
+      // "Input", "Output", and nullptr which means both input and output.
+      std::unique_ptr<char, base::FreeDeleter> io(
+          wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName));
+      if (io != nullptr && strcmp(io.get(), "Input") == 0)
+        continue;
+
+      // Attempt to select the closest device for number of channels.
+      std::unique_ptr<char, base::FreeDeleter> name(
+          wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
+      if (strncmp(wanted_device, name.get(), strlen(wanted_device)) == 0) {
+        guessed_device = name.get();
+        break;
+      }
+    }
+
+    // Destroy the hint now that we're done with it.
+    wrapper_->DeviceNameFreeHint(hints);
+    hints = nullptr;
+  } else {
+    LOG(ERROR) << "Unable to get hints for devices: "
+               << wrapper_->StrError(error);
+  }
+
+  return guessed_device;
+}
+
+snd_pcm_sframes_t AlsaPcmOutputStream::GetCurrentDelay() {
+  snd_pcm_sframes_t delay = -1;
+  // Don't query ALSA's delay if we have underrun since it'll be jammed at some
+  // non-zero value and potentially even negative!
+  //
+  // Also, if we're in the prepared state, don't query because that seems to
+  // cause an I/O error when we do query the delay.
+  snd_pcm_state_t pcm_state = wrapper_->PcmState(playback_handle_);
+  if (pcm_state != SND_PCM_STATE_XRUN &&
+      pcm_state != SND_PCM_STATE_PREPARED) {
+    int error = wrapper_->PcmDelay(playback_handle_, &delay);
+    if (error < 0) {
+      // Assume a delay of zero and attempt to recover the device.
+      delay = -1;
+      error = wrapper_->PcmRecover(playback_handle_,
+                                   error,
+                                   kPcmRecoverIsSilent);
+      if (error < 0) {
+        LOG(ERROR) << "Failed querying delay: " << wrapper_->StrError(error);
+      }
+    }
+  }
+
+  // snd_pcm_delay() sometimes returns crazy values.  In this case return delay
+  // of data we know currently is in ALSA's buffer.  Note: When the underlying
+  // driver is PulseAudio based, certain configuration settings (e.g., tsched=1)
+  // will generate much larger delay values than |alsa_buffer_frames_|, so only
+  // clip if delay is truly crazy (> 10x expected).
+  if (delay < 0 ||
+      static_cast<snd_pcm_uframes_t>(delay) > alsa_buffer_frames_ * 10) {
+    delay = alsa_buffer_frames_ - GetAvailableFrames();
+  }
+
+  if (delay < 0) {
+    delay = 0;
+  }
+
+  return delay;
+}
+
+snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (stop_stream_)
+    return 0;
+
+  // Find the number of frames queued in the sound device.
+  snd_pcm_sframes_t available_frames =
+      wrapper_->PcmAvailUpdate(playback_handle_);
+  if (available_frames < 0) {
+    available_frames = wrapper_->PcmRecover(playback_handle_,
+                                            available_frames,
+                                            kPcmRecoverIsSilent);
+  }
+  if (available_frames < 0) {
+    LOG(ERROR) << "Failed querying available frames. Assuming 0: "
+               << wrapper_->StrError(available_frames);
+    return 0;
+  }
+  if (static_cast<uint32_t>(available_frames) > alsa_buffer_frames_ * 2) {
+    LOG(ERROR) << "ALSA returned " << available_frames << " of "
+               << alsa_buffer_frames_ << " frames available.";
+    return alsa_buffer_frames_;
+  }
+
+  return available_frames;
+}
+
+snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) {
+  // For auto-selection:
+  //   1) Attempt to open a device that best matches the number of channels
+  //      requested.
+  //   2) If that fails, attempt the "plug:" version of it in case ALSA can
+  //      remap and do some software conversion to make it work.
+  //   3) If that fails, attempt the "plug:" version of the guessed name in
+  //      case ALSA can remap and do some software conversion to make it work.
+  //   4) Fallback to kDefaultDevice.
+  //   5) If that fails too, try the "plug:" version of kDefaultDevice.
+  //   6) Give up.
+  snd_pcm_t* handle = nullptr;
+  device_name_ = FindDeviceForChannels(channels_);
+
+  // Step 1.
+  if (!device_name_.empty()) {
+    if ((handle = alsa_util::OpenPlaybackDevice(
+             wrapper_, device_name_.c_str(), channels_, sample_rate_,
+             pcm_format_, latency)) != nullptr) {
+      return handle;
+    }
+
+    // Step 2.
+    device_name_ = kPlugPrefix + device_name_;
+    if ((handle = alsa_util::OpenPlaybackDevice(
+             wrapper_, device_name_.c_str(), channels_, sample_rate_,
+             pcm_format_, latency)) != nullptr) {
+      return handle;
+    }
+
+    // Step 3.
+    device_name_ = GuessSpecificDeviceName(channels_);
+    if (!device_name_.empty()) {
+      device_name_ = kPlugPrefix + device_name_;
+      if ((handle = alsa_util::OpenPlaybackDevice(
+               wrapper_, device_name_.c_str(), channels_, sample_rate_,
+               pcm_format_, latency)) != nullptr) {
+        return handle;
+      }
+    }
+  }
+
+  // For the kDefaultDevice device, we can only reliably depend on 2-channel
+  // output to have the correct ordering according to Lennart.  For the channel
+  // formats that we know how to downmix from (3 channel to 8 channel), setup
+  // downmixing.
+  uint32_t default_channels = channels_;
+  if (default_channels > 2) {
+    channel_mixer_ = std::make_unique<ChannelMixer>(
+        channel_layout_, kDefaultOutputChannelLayout);
+    default_channels = 2;
+    mixed_audio_bus_ = AudioBus::Create(
+        default_channels, audio_bus_->frames());
+  }
+
+  // Step 4.
+  device_name_ = kDefaultDevice;
+  if ((handle = alsa_util::OpenPlaybackDevice(
+           wrapper_, device_name_.c_str(), default_channels, sample_rate_,
+           pcm_format_, latency)) != nullptr) {
+    return handle;
+  }
+
+  // Step 5.
+  device_name_ = kPlugPrefix + device_name_;
+  if ((handle = alsa_util::OpenPlaybackDevice(
+           wrapper_, device_name_.c_str(), default_channels, sample_rate_,
+           pcm_format_, latency)) != nullptr) {
+    return handle;
+  }
+
+  // Unable to open any device.
+  device_name_.clear();
+  return nullptr;
+}
+
+bool AlsaPcmOutputStream::CanTransitionTo(InternalState to) {
+  switch (state_) {
+    case kCreated:
+      return to == kIsOpened || to == kIsClosed || to == kInError;
+
+    case kIsOpened:
+      return to == kIsPlaying || to == kIsStopped ||
+          to == kIsClosed || to == kInError;
+
+    case kIsPlaying:
+      return to == kIsPlaying || to == kIsStopped ||
+          to == kIsClosed || to == kInError;
+
+    case kIsStopped:
+      return to == kIsPlaying || to == kIsStopped ||
+          to == kIsClosed || to == kInError;
+
+    case kInError:
+      return to == kIsClosed || to == kInError;
+
+    case kIsClosed:
+    default:
+      return false;
+  }
+}
+
+AlsaPcmOutputStream::InternalState
+AlsaPcmOutputStream::TransitionTo(InternalState to) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!CanTransitionTo(to)) {
+    NOTREACHED() << "Cannot transition from: " << state_ << " to: " << to;
+    state_ = kInError;
+  } else {
+    state_ = to;
+  }
+  return state_;
+}
+
+AlsaPcmOutputStream::InternalState AlsaPcmOutputStream::state() {
+  return state_;
+}
+
+int AlsaPcmOutputStream::RunDataCallback(base::TimeDelta delay,
+                                         base::TimeTicks delay_timestamp,
+                                         AudioBus* audio_bus) {
+  TRACE_EVENT0("audio", "AlsaPcmOutputStream::RunDataCallback");
+
+  if (source_callback_)
+    return source_callback_->OnMoreData(delay, delay_timestamp, 0, audio_bus);
+
+  return 0;
+}
+
+void AlsaPcmOutputStream::RunErrorCallback(int code) {
+  // TODO(dalecurtis): Consider sending a translated |code| value.
+  if (source_callback_)
+    source_callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+}
+
+// Changes the AudioSourceCallback to proxy calls to.  Pass in nullptr to
+// release ownership of the currently registered callback.
+void AlsaPcmOutputStream::set_source_callback(AudioSourceCallback* callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  source_callback_ = callback;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/alsa/alsa_output.h b/third_party/chromium/media/audio/alsa/alsa_output.h
new file mode 100644
index 0000000..d10bbba
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_output.h
@@ -0,0 +1,233 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Creates an output stream based on the ALSA PCM interface.
+//
+// On device write failure, the stream will move itself to an invalid state.
+// No more data will be pulled from the data source, or written to the device.
+// All calls to public API functions will either no-op themselves, or return an
+// error if possible.  Specifically, If the stream is in an error state, Open()
+// will return false, and Start() will call OnError() immediately on the
+// provided callback.
+//
+// If the stream is successfully opened, Close() must be called.  After Close
+// has been called, the object should be regarded as deleted and not touched.
+//
+// AlsaPcmOutputStream is a single threaded class that should only be used from
+// the audio thread. When modifying the code in this class, please read the
+// threading assumptions at the top of the implementation.
+
+#ifndef MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
+#define MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
+
+#include <alsa/asoundlib.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AlsaWrapper;
+class AudioManagerBase;
+class ChannelMixer;
+class SeekableBuffer;
+
+class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream {
+ public:
+  // String for the generic "default" ALSA device that has the highest
+  // compatibility and chance of working.
+  static const char kDefaultDevice[];
+
+  // Pass this to the AlsaPcmOutputStream if you want to attempt auto-selection
+  // of the audio device.
+  static const char kAutoSelectDevice[];
+
+  // Prefix for device names to enable ALSA library resampling.
+  static const char kPlugPrefix[];
+
+  // The minimum latency that is accepted by the device.
+  static const uint32_t kMinLatencyMicros;
+
+  // Create a PCM Output stream for the ALSA device identified by
+  // |device_name|.  The AlsaPcmOutputStream uses |wrapper| to communicate with
+  // the alsa libraries, allowing for dependency injection during testing.  All
+  // requesting of data, and writing to the alsa device will be done on
+  // the current thread.
+  //
+  // If unsure of what to use for |device_name|, use |kAutoSelectDevice|.
+  AlsaPcmOutputStream(const std::string& device_name,
+                      const AudioParameters& params,
+                      AlsaWrapper* wrapper,
+                      AudioManagerBase* manager);
+
+  AlsaPcmOutputStream(const AlsaPcmOutputStream&) = delete;
+  AlsaPcmOutputStream& operator=(const AlsaPcmOutputStream&) = delete;
+
+  ~AlsaPcmOutputStream() override;
+
+  // Implementation of AudioOutputStream.
+  bool Open() override;
+  void Close() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void Flush() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+
+  void SetTickClockForTesting(const base::TickClock* tick_clock);
+
+ private:
+  friend class AlsaPcmOutputStreamTest;
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
+                           AutoSelectDevice_DeviceSelect);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
+                           AutoSelectDevice_FallbackDevices);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, AutoSelectDevice_HintFail);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Negative);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_StopStream);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Underrun);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_FullBuffer);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ConstructedState);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, LatencyFloor);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, OpenClose);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmOpenFailed);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmSetParamsFailed);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ScheduleNextWrite);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
+                           ScheduleNextWrite_StopStream);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, StartStop);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_NormalPacket);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_StopStream);
+  FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_WriteFails);
+
+  // Flags indicating the state of the stream.
+  enum InternalState {
+    kInError = 0,
+    kCreated,
+    kIsOpened,
+    kIsPlaying,
+    kIsStopped,
+    kIsClosed
+  };
+  friend std::ostream& operator<<(std::ostream& os, InternalState);
+
+  // Functions to get another packet from the data source and write it into the
+  // ALSA device.
+  void BufferPacket(bool* source_exhausted);
+  void WritePacket();
+  void WriteTask();
+  void ScheduleNextWrite(bool source_exhausted);
+
+  // Utility functions for talking with the ALSA API.
+  std::string FindDeviceForChannels(uint32_t channels);
+  snd_pcm_sframes_t GetAvailableFrames();
+  snd_pcm_sframes_t GetCurrentDelay();
+
+  // Attempts to find the best matching linux audio device for the given number
+  // of channels.  This function will set |device_name_| and |channel_mixer_|.
+  snd_pcm_t* AutoSelectDevice(uint32_t latency);
+
+  // Functions to safeguard state transitions.  All changes to the object state
+  // should go through these functions.
+  bool CanTransitionTo(InternalState to);
+  InternalState TransitionTo(InternalState to);
+  InternalState state();
+
+  // API for Proxying calls to the AudioSourceCallback provided during
+  // Start().
+  //
+  // TODO(ajwong): This is necessary because the ownership semantics for the
+  // |source_callback_| object are incorrect in AudioRenderHost. The callback
+  // is passed into the output stream, but ownership is not transfered which
+  // requires a synchronization on access of the |source_callback_| to avoid
+  // using a deleted callback.
+  int RunDataCallback(base::TimeDelta delay,
+                      base::TimeTicks delay_timestamp,
+                      AudioBus* audio_bus);
+  void RunErrorCallback(int code);
+
+  // Changes the AudioSourceCallback to proxy calls to.  Pass in nullptr to
+  // release ownership of the currently registered callback.
+  void set_source_callback(AudioSourceCallback* callback);
+
+  // Configuration constants from the constructor.  Referenceable by all threads
+  // since they are constants.
+  const std::string requested_device_name_;
+  const snd_pcm_format_t pcm_format_;
+  const uint32_t channels_;
+  const ChannelLayout channel_layout_;
+  const uint32_t sample_rate_;
+  const uint32_t bytes_per_sample_;
+  const uint32_t bytes_per_frame_;
+
+  // Device configuration data. Populated after OpenTask() completes.
+  std::string device_name_;
+  uint32_t packet_size_;
+  base::TimeDelta latency_;
+  uint32_t bytes_per_output_frame_;
+  uint32_t alsa_buffer_frames_;
+
+  // Flag indicating the code should stop reading from the data source or
+  // writing to the ALSA device.  This is set because the device has entered
+  // an unrecoverable error state, or the ClosedTask() has executed.
+  bool stop_stream_;
+
+  // Wrapper class to invoke all the ALSA functions.
+  AlsaWrapper* wrapper_;
+
+  // Audio manager that created us.  Used to report that we've been closed.
+  AudioManagerBase* manager_;
+
+  // Task runner to use for polling.
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // Handle to the actual PCM playback device.
+  snd_pcm_t* playback_handle_;
+
+  std::unique_ptr<SeekableBuffer> buffer_;
+  uint32_t frames_per_packet_;
+
+  InternalState state_;
+  float volume_;  // Volume level from 0.0 to 1.0.
+
+  AudioSourceCallback* source_callback_;
+
+  // Container for retrieving data from AudioSourceCallback::OnMoreData().
+  std::unique_ptr<AudioBus> audio_bus_;
+
+  // Channel mixer and temporary bus for the final mixed channel data.
+  std::unique_ptr<ChannelMixer> channel_mixer_;
+  std::unique_ptr<AudioBus> mixed_audio_bus_;
+
+  const base::TickClock* tick_clock_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // Allows us to run tasks on the AlsaPcmOutputStream instance which are
+  // bound by its lifetime.
+  // NOTE: Weak pointers must be invalidated before all other member variables.
+  base::WeakPtrFactory<AlsaPcmOutputStream> weak_factory_{this};
+};
+
+MEDIA_EXPORT std::ostream& operator<<(std::ostream& os,
+                                      AlsaPcmOutputStream::InternalState);
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
diff --git a/third_party/chromium/media/audio/alsa/alsa_output_unittest.cc b/third_party/chromium/media/audio/alsa/alsa_output_unittest.cc
new file mode 100644
index 0000000..33fd1c5
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_output_unittest.cc
@@ -0,0 +1,828 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/test/test_message_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "media/audio/alsa/alsa_output.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+#include "media/audio/alsa/audio_manager_alsa.h"
+#include "media/audio/alsa/mock_alsa_wrapper.h"
+#include "media/audio/fake_audio_log_factory.h"
+#include "media/audio/mock_audio_source_callback.h"
+#include "media/audio/test_audio_thread.h"
+#include "media/base/audio_timestamp_helper.h"
+#include "media/base/data_buffer.h"
+#include "media/base/seekable_buffer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AllOf;
+using testing::AtLeast;
+using testing::DoAll;
+using testing::Field;
+using testing::InSequence;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::Mock;
+using testing::MockFunction;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::StrictMock;
+using testing::StrEq;
+using testing::Unused;
+
+namespace media {
+
+class MockAudioManagerAlsa : public AudioManagerAlsa {
+ public:
+  MockAudioManagerAlsa()
+      : AudioManagerAlsa(std::make_unique<TestAudioThread>(),
+                         &fake_audio_log_factory_) {}
+  MOCK_METHOD0(Init, void());
+  MOCK_METHOD0(HasAudioOutputDevices, bool());
+  MOCK_METHOD0(HasAudioInputDevices, bool());
+  MOCK_METHOD2(MakeLinearOutputStream,
+               AudioOutputStream*(const AudioParameters& params,
+                                  const LogCallback& log_callback));
+  MOCK_METHOD3(MakeLowLatencyOutputStream,
+               AudioOutputStream*(const AudioParameters& params,
+                                  const std::string& device_id,
+                                  const LogCallback& log_callback));
+  MOCK_METHOD3(MakeLowLatencyInputStream,
+               AudioInputStream*(const AudioParameters& params,
+                                 const std::string& device_id,
+                                 const LogCallback& log_callback));
+
+  // We need to override this function in order to skip the checking the number
+  // of active output streams. It is because the number of active streams
+  // is managed inside MakeAudioOutputStream, and we don't use
+  // MakeAudioOutputStream to create the stream in the tests.
+  void ReleaseOutputStream(AudioOutputStream* stream) override {
+    DCHECK(stream);
+    delete stream;
+  }
+
+ private:
+  FakeAudioLogFactory fake_audio_log_factory_;
+};
+
+class AlsaPcmOutputStreamTest : public testing::Test {
+ protected:
+  AlsaPcmOutputStreamTest() {
+    mock_manager_ = std::make_unique<StrictMock<MockAudioManagerAlsa>>();
+  }
+
+  ~AlsaPcmOutputStreamTest() override { mock_manager_->Shutdown(); }
+
+  AlsaPcmOutputStream* CreateStream(ChannelLayout layout) {
+    return CreateStream(layout, kTestFramesPerPacket);
+  }
+
+  AlsaPcmOutputStream* CreateStream(ChannelLayout layout,
+                                    int32_t samples_per_packet) {
+    AudioParameters params(kTestFormat, layout, kTestSampleRate,
+                           samples_per_packet);
+    return new AlsaPcmOutputStream(kTestDeviceName,
+                                   params,
+                                   &mock_alsa_wrapper_,
+                                   mock_manager_.get());
+  }
+
+  // Helper function to malloc the string returned by DeviceNameHint for NAME.
+  static char* EchoHint(const void* name, Unused) {
+    return strdup(static_cast<const char*>(name));
+  }
+
+  // Helper function to malloc the string returned by DeviceNameHint for IOID.
+  static char* OutputHint(Unused, Unused) {
+    return strdup("Output");
+  }
+
+  // Helper function to initialize |test_stream->buffer_|. Must be called
+  // in all tests that use buffer_ without opening the stream.
+  void InitBuffer(AlsaPcmOutputStream* test_stream) {
+    DCHECK(test_stream);
+    packet_ = new DataBuffer(kTestPacketSize);
+    packet_->set_data_size(kTestPacketSize);
+    test_stream->buffer_ = std::make_unique<SeekableBuffer>(0, kTestPacketSize);
+    test_stream->buffer_->Append(packet_.get());
+  }
+
+  static const ChannelLayout kTestChannelLayout;
+  static const int kTestSampleRate;
+  static const int kTestBitsPerSample;
+  static const int kTestBytesPerFrame;
+  static const AudioParameters::Format kTestFormat;
+  static const char kTestDeviceName[];
+  static const char kDummyMessage[];
+  static const uint32_t kTestFramesPerPacket;
+  static const int kTestPacketSize;
+  static const int kTestFailedErrno;
+  static snd_pcm_t* const kFakeHandle;
+
+  // Used to simulate DeviceNameHint.
+  static char kSurround40[];
+  static char kSurround41[];
+  static char kSurround50[];
+  static char kSurround51[];
+  static char kSurround70[];
+  static char kSurround71[];
+  static void* kFakeHints[];
+  static char kGenericSurround50[];
+
+  base::TestMessageLoop message_loop_;
+  StrictMock<MockAlsaWrapper> mock_alsa_wrapper_;
+  std::unique_ptr<StrictMock<MockAudioManagerAlsa>> mock_manager_;
+  scoped_refptr<DataBuffer> packet_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AlsaPcmOutputStreamTest);
+};
+
+const ChannelLayout AlsaPcmOutputStreamTest::kTestChannelLayout =
+    CHANNEL_LAYOUT_STEREO;
+const int AlsaPcmOutputStreamTest::kTestSampleRate =
+    AudioParameters::kAudioCDSampleRate;
+const int AlsaPcmOutputStreamTest::kTestBitsPerSample = 16;
+const int AlsaPcmOutputStreamTest::kTestBytesPerFrame =
+    AlsaPcmOutputStreamTest::kTestBitsPerSample / 8 *
+    ChannelLayoutToChannelCount(AlsaPcmOutputStreamTest::kTestChannelLayout);
+const AudioParameters::Format AlsaPcmOutputStreamTest::kTestFormat =
+    AudioParameters::AUDIO_PCM_LINEAR;
+const char AlsaPcmOutputStreamTest::kTestDeviceName[] = "TestDevice";
+const char AlsaPcmOutputStreamTest::kDummyMessage[] = "dummy";
+const uint32_t AlsaPcmOutputStreamTest::kTestFramesPerPacket = 1000;
+const int AlsaPcmOutputStreamTest::kTestPacketSize =
+    AlsaPcmOutputStreamTest::kTestFramesPerPacket *
+    AlsaPcmOutputStreamTest::kTestBytesPerFrame;
+const int AlsaPcmOutputStreamTest::kTestFailedErrno = -EACCES;
+snd_pcm_t* const AlsaPcmOutputStreamTest::kFakeHandle =
+    reinterpret_cast<snd_pcm_t*>(1);
+
+char AlsaPcmOutputStreamTest::kSurround40[] = "surround40:CARD=foo,DEV=0";
+char AlsaPcmOutputStreamTest::kSurround41[] = "surround41:CARD=foo,DEV=0";
+char AlsaPcmOutputStreamTest::kSurround50[] = "surround50:CARD=foo,DEV=0";
+char AlsaPcmOutputStreamTest::kSurround51[] = "surround51:CARD=foo,DEV=0";
+char AlsaPcmOutputStreamTest::kSurround70[] = "surround70:CARD=foo,DEV=0";
+char AlsaPcmOutputStreamTest::kSurround71[] = "surround71:CARD=foo,DEV=0";
+void* AlsaPcmOutputStreamTest::kFakeHints[] = {
+    kSurround40, kSurround41, kSurround50, kSurround51,
+    kSurround70, kSurround71, nullptr};
+char AlsaPcmOutputStreamTest::kGenericSurround50[] = "surround50";
+
+// Custom action to clear a memory buffer.
+ACTION(ClearBuffer) {
+  arg3->Zero();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, ConstructedState) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  EXPECT_EQ(AlsaPcmOutputStream::kCreated, test_stream->state());
+  test_stream->Close();
+
+  // Should support mono.
+  test_stream = CreateStream(CHANNEL_LAYOUT_MONO);
+  EXPECT_EQ(AlsaPcmOutputStream::kCreated, test_stream->state());
+  test_stream->Close();
+
+  // Should support multi-channel.
+  test_stream = CreateStream(CHANNEL_LAYOUT_SURROUND);
+  EXPECT_EQ(AlsaPcmOutputStream::kCreated, test_stream->state());
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, LatencyFloor) {
+  const double kMicrosPerFrame =
+      static_cast<double>(1000000) / kTestSampleRate;
+  const double kPacketFramesInMinLatency =
+      AlsaPcmOutputStream::kMinLatencyMicros / kMicrosPerFrame / 2.0;
+
+  // Test that packets which would cause a latency under less than
+  // AlsaPcmOutputStream::kMinLatencyMicros will get clipped to
+  // AlsaPcmOutputStream::kMinLatencyMicros,
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(kFakeHandle), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_,
+              PcmSetParams(_, _, _, _, _, _,
+                           AlsaPcmOutputStream::kMinLatencyMicros))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(kTestFramesPerPacket),
+                      SetArgPointee<2>(kTestFramesPerPacket / 2), Return(0)));
+
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout,
+                                                  kPacketFramesInMinLatency);
+  ASSERT_TRUE(test_stream->Open());
+
+  // Now close it and test that everything was released.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle)).WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
+      .WillOnce(Return(kTestDeviceName));
+  test_stream->Close();
+
+  Mock::VerifyAndClear(&mock_alsa_wrapper_);
+  Mock::VerifyAndClear(mock_manager_.get());
+
+  // Test that having more packets ends up with a latency based on packet size.
+  const int kOverMinLatencyPacketSize = kPacketFramesInMinLatency + 1;
+  int64_t expected_micros = AudioTimestampHelper::FramesToTime(
+                                kOverMinLatencyPacketSize * 2, kTestSampleRate)
+                                .InMicroseconds();
+
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(kFakeHandle), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_,
+              PcmSetParams(_, _, _, _, _, _, expected_micros))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(kTestFramesPerPacket),
+                      SetArgPointee<2>(kTestFramesPerPacket / 2), Return(0)));
+
+  test_stream = CreateStream(kTestChannelLayout,
+                             kOverMinLatencyPacketSize);
+  ASSERT_TRUE(test_stream->Open());
+
+  // Now close it and test that everything was released.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
+      .WillOnce(Return(kTestDeviceName));
+  test_stream->Close();
+
+  Mock::VerifyAndClear(&mock_alsa_wrapper_);
+  Mock::VerifyAndClear(mock_manager_.get());
+}
+
+TEST_F(AlsaPcmOutputStreamTest, OpenClose) {
+  int64_t expected_micros = AudioTimestampHelper::FramesToTime(
+                                2 * kTestFramesPerPacket, kTestSampleRate)
+                                .InMicroseconds();
+
+  // Open() call opens the playback device, sets the parameters, posts a task
+  // with the resulting configuration data, and transitions the object state to
+  // kIsOpened.
+  EXPECT_CALL(mock_alsa_wrapper_,
+              PcmOpen(_, StrEq(kTestDeviceName), SND_PCM_STREAM_PLAYBACK,
+                      SND_PCM_NONBLOCK))
+      .WillOnce(DoAll(SetArgPointee<0>(kFakeHandle), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_,
+              PcmSetParams(kFakeHandle, SND_PCM_FORMAT_S16_LE,
+                           SND_PCM_ACCESS_RW_INTERLEAVED,
+                           ChannelLayoutToChannelCount(kTestChannelLayout),
+                           kTestSampleRate, 1, expected_micros))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(kFakeHandle, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(kTestFramesPerPacket),
+                      SetArgPointee<2>(kTestFramesPerPacket / 2), Return(0)));
+
+  // Open the stream.
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  ASSERT_TRUE(test_stream->Open());
+
+  EXPECT_EQ(AlsaPcmOutputStream::kIsOpened, test_stream->state());
+  EXPECT_EQ(kFakeHandle, test_stream->playback_handle_);
+  EXPECT_EQ(kTestFramesPerPacket, test_stream->frames_per_packet_);
+  EXPECT_TRUE(test_stream->buffer_.get());
+  EXPECT_FALSE(test_stream->stop_stream_);
+
+  // Now close it and test that everything was released.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
+      .WillOnce(Return(kTestDeviceName));
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, PcmOpenFailed) {
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+      .WillOnce(Return(kTestFailedErrno));
+  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
+      .WillOnce(Return(kDummyMessage));
+
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  ASSERT_FALSE(test_stream->Open());
+  ASSERT_EQ(AlsaPcmOutputStream::kInError, test_stream->state());
+
+  // Ensure internal state is set for a no-op stream if PcmOpen() failes.
+  EXPECT_TRUE(test_stream->stop_stream_);
+  EXPECT_FALSE(test_stream->playback_handle_);
+  EXPECT_FALSE(test_stream->buffer_.get());
+
+  // Close the stream since we opened it to make destruction happy.
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, PcmSetParamsFailed) {
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(kFakeHandle), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
+      .WillOnce(Return(kTestFailedErrno));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
+      .WillOnce(Return(kTestDeviceName));
+  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
+      .WillOnce(Return(kDummyMessage));
+
+  // If open fails, the stream stays in kCreated because it has effectively had
+  // no changes.
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  ASSERT_FALSE(test_stream->Open());
+  EXPECT_EQ(AlsaPcmOutputStream::kInError, test_stream->state());
+
+  // Ensure internal state is set for a no-op stream if PcmSetParams() failes.
+  EXPECT_TRUE(test_stream->stop_stream_);
+  EXPECT_FALSE(test_stream->playback_handle_);
+  EXPECT_FALSE(test_stream->buffer_.get());
+
+  // Close the stream since we opened it to make destruction happy.
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, StartStop) {
+  // Open() call opens the playback device, sets the parameters, posts a task
+  // with the resulting configuration data, and transitions the object state to
+  // kIsOpened.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(kFakeHandle), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(kTestFramesPerPacket),
+                      SetArgPointee<2>(kTestFramesPerPacket / 2), Return(0)));
+
+  // Open the stream.
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  ASSERT_TRUE(test_stream->Open());
+  base::SimpleTestTickClock tick_clock;
+  tick_clock.SetNowTicks(base::TimeTicks::Now());
+  test_stream->SetTickClockForTesting(&tick_clock);
+
+  // Expect Device setup.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmDrop(kFakeHandle))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmPrepare(kFakeHandle))
+      .WillOnce(Return(0));
+
+  // Expect the pre-roll.
+  MockAudioSourceCallback mock_callback;
+  EXPECT_CALL(mock_alsa_wrapper_, PcmState(kFakeHandle))
+      .WillRepeatedly(Return(SND_PCM_STATE_RUNNING));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(kFakeHandle, _))
+      .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(0)));
+  EXPECT_CALL(mock_callback,
+              OnMoreData(base::TimeDelta(), tick_clock.NowTicks(), 0, _))
+      .WillRepeatedly(DoAll(ClearBuffer(), Return(kTestFramesPerPacket)));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
+      .WillRepeatedly(Return(kTestFramesPerPacket));
+
+  // Expect scheduling.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
+      .Times(AtLeast(2))
+      .WillRepeatedly(Return(kTestFramesPerPacket));
+
+  test_stream->Start(&mock_callback);
+  // Start() will issue a WriteTask() directly and then schedule the next one,
+  // call Stop() immediately after to ensure we don't run the message loop
+  // forever.
+  test_stream->Stop();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
+      .WillOnce(Return(kTestDeviceName));
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  InitBuffer(test_stream);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsOpened);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+
+  // Nothing should happen.  Don't set any expectations and Our strict mocks
+  // should verify most of this.
+
+  // Test empty buffer.
+  test_stream->buffer_->Clear();
+  test_stream->WritePacket();
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, WritePacket_NormalPacket) {
+  // We need to open the stream before writing data to ALSA.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(kFakeHandle), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(kTestFramesPerPacket),
+                      SetArgPointee<2>(kTestFramesPerPacket / 2), Return(0)));
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  ASSERT_TRUE(test_stream->Open());
+  InitBuffer(test_stream);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+
+  // Write a little less than half the data.
+  int written = packet_->data_size() / kTestBytesPerFrame / 2 - 1;
+  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
+        .WillOnce(Return(written));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, packet_->data(), _))
+      .WillOnce(Return(written));
+
+  test_stream->WritePacket();
+
+  ASSERT_EQ(test_stream->buffer_->forward_bytes(),
+            packet_->data_size() - written * kTestBytesPerFrame);
+
+  // Write the rest.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
+      .WillOnce(Return(kTestFramesPerPacket - written));
+  EXPECT_CALL(mock_alsa_wrapper_,
+              PcmWritei(kFakeHandle,
+                        packet_->data() + written * kTestBytesPerFrame,
+                        _))
+      .WillOnce(Return(packet_->data_size() / kTestBytesPerFrame - written));
+  test_stream->WritePacket();
+  EXPECT_EQ(0, test_stream->buffer_->forward_bytes());
+
+  // Now close it and test that everything was released.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
+      .WillOnce(Return(kTestDeviceName));
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, WritePacket_WriteFails) {
+  // We need to open the stream before writing data to ALSA.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, _, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(kFakeHandle), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmSetParams(_, _, _, _, _, _, _))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmGetParams(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<1>(kTestFramesPerPacket),
+                      SetArgPointee<2>(kTestFramesPerPacket / 2), Return(0)));
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  ASSERT_TRUE(test_stream->Open());
+  InitBuffer(test_stream);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+
+  // Fail due to a recoverable error and see that PcmRecover code path
+  // continues normally.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
+      .WillOnce(Return(kTestFramesPerPacket));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
+      .WillOnce(Return(-EINTR));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmRecover(kFakeHandle, _, _))
+      .WillOnce(Return(0));
+
+  test_stream->WritePacket();
+
+  ASSERT_EQ(test_stream->buffer_->forward_bytes(), packet_->data_size());
+
+  // Fail the next write, and see that stop_stream_ is set.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(kFakeHandle))
+        .WillOnce(Return(kTestFramesPerPacket));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmWritei(kFakeHandle, _, _))
+      .WillOnce(Return(kTestFailedErrno));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmRecover(kFakeHandle, _, _))
+      .WillOnce(Return(kTestFailedErrno));
+  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
+      .WillOnce(Return(kDummyMessage));
+  test_stream->WritePacket();
+  EXPECT_EQ(test_stream->buffer_->forward_bytes(), packet_->data_size());
+  EXPECT_TRUE(test_stream->stop_stream_);
+
+  // Now close it and test that everything was released.
+  EXPECT_CALL(mock_alsa_wrapper_, PcmClose(kFakeHandle))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmName(kFakeHandle))
+      .WillOnce(Return(kTestDeviceName));
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, WritePacket_StopStream) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  InitBuffer(test_stream);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsOpened);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+
+  // No expectations set on the strict mock because nothing should be called.
+  test_stream->stop_stream_ = true;
+  test_stream->WritePacket();
+  EXPECT_EQ(0, test_stream->buffer_->forward_bytes());
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, BufferPacket) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  base::SimpleTestTickClock tick_clock;
+  tick_clock.SetNowTicks(base::TimeTicks::Now());
+  test_stream->SetTickClockForTesting(&tick_clock);
+  InitBuffer(test_stream);
+  test_stream->buffer_->Clear();
+
+  MockAudioSourceCallback mock_callback;
+  EXPECT_CALL(mock_alsa_wrapper_, PcmState(_))
+      .WillOnce(Return(SND_PCM_STATE_RUNNING));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(_, _))
+      .WillOnce(DoAll(SetArgPointee<1>(1), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
+      .WillRepeatedly(Return(0));  // Buffer is full.
+
+  // Return a partially filled packet.
+  EXPECT_CALL(mock_callback,
+              OnMoreData(base::TimeDelta(), tick_clock.NowTicks(), 0, _))
+      .WillOnce(DoAll(ClearBuffer(), Return(kTestFramesPerPacket / 2)));
+
+  bool source_exhausted;
+  test_stream->set_source_callback(&mock_callback);
+  test_stream->packet_size_ = kTestPacketSize;
+  test_stream->BufferPacket(&source_exhausted);
+
+  EXPECT_EQ(kTestPacketSize / 2, test_stream->buffer_->forward_bytes());
+  EXPECT_FALSE(source_exhausted);
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Negative) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  base::SimpleTestTickClock tick_clock;
+  tick_clock.SetNowTicks(base::TimeTicks::Now());
+  test_stream->SetTickClockForTesting(&tick_clock);
+  InitBuffer(test_stream);
+  test_stream->buffer_->Clear();
+
+  // Simulate where the underrun has occurred right after checking the delay.
+  MockAudioSourceCallback mock_callback;
+  EXPECT_CALL(mock_alsa_wrapper_, PcmState(_))
+      .WillOnce(Return(SND_PCM_STATE_RUNNING));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmDelay(_, _))
+      .WillOnce(DoAll(SetArgPointee<1>(-1), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
+      .WillRepeatedly(Return(0));  // Buffer is full.
+  EXPECT_CALL(mock_callback,
+              OnMoreData(base::TimeDelta(), tick_clock.NowTicks(), 0, _))
+      .WillOnce(DoAll(ClearBuffer(), Return(kTestFramesPerPacket / 2)));
+
+  bool source_exhausted;
+  test_stream->set_source_callback(&mock_callback);
+  test_stream->packet_size_ = kTestPacketSize;
+  test_stream->BufferPacket(&source_exhausted);
+
+  EXPECT_EQ(kTestPacketSize / 2, test_stream->buffer_->forward_bytes());
+  EXPECT_FALSE(source_exhausted);
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, BufferPacket_Underrun) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  base::SimpleTestTickClock tick_clock;
+  tick_clock.SetNowTicks(base::TimeTicks::Now());
+  test_stream->SetTickClockForTesting(&tick_clock);
+  InitBuffer(test_stream);
+  test_stream->buffer_->Clear();
+
+  // If ALSA has underrun then we should assume a delay of zero.
+  MockAudioSourceCallback mock_callback;
+  EXPECT_CALL(mock_alsa_wrapper_, PcmState(_))
+      .WillOnce(Return(SND_PCM_STATE_XRUN));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
+      .WillRepeatedly(Return(0));  // Buffer is full.
+  EXPECT_CALL(mock_callback,
+              OnMoreData(base::TimeDelta(), tick_clock.NowTicks(), 0, _))
+      .WillOnce(DoAll(ClearBuffer(), Return(kTestFramesPerPacket / 2)));
+
+  bool source_exhausted;
+  test_stream->set_source_callback(&mock_callback);
+  test_stream->packet_size_ = kTestPacketSize;
+  test_stream->BufferPacket(&source_exhausted);
+
+  EXPECT_EQ(kTestPacketSize / 2, test_stream->buffer_->forward_bytes());
+  EXPECT_FALSE(source_exhausted);
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, BufferPacket_FullBuffer) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  InitBuffer(test_stream);
+  // No expectations set on the strict mock because nothing should be called.
+  bool source_exhausted;
+  test_stream->packet_size_ = kTestPacketSize;
+  test_stream->BufferPacket(&source_exhausted);
+  EXPECT_EQ(kTestPacketSize, test_stream->buffer_->forward_bytes());
+  EXPECT_FALSE(source_exhausted);
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_DeviceSelect) {
+  // Try channels from 1 -> 9. and see that we get the more specific surroundXX
+  // device opened for channels 4-8.  For all other channels, the device should
+  // default to |AlsaPcmOutputStream::kDefaultDevice|.  We should also not
+  // downmix any channel in this case because downmixing is only defined for
+  // channels 4-8, which we are guaranteeing to work.
+  //
+  // Note that the loop starts at "1", so the first parameter is ignored in
+  // these arrays.
+  const char* kExpectedDeviceName[] = {nullptr,
+                                       AlsaPcmOutputStream::kDefaultDevice,
+                                       AlsaPcmOutputStream::kDefaultDevice,
+                                       AlsaPcmOutputStream::kDefaultDevice,
+                                       kSurround40,
+                                       kSurround50,
+                                       kSurround51,
+                                       kSurround70,
+                                       kSurround71,
+                                       AlsaPcmOutputStream::kDefaultDevice};
+  bool kExpectedDownmix[] = { false, false, false, false, false, true,
+                              false, false, false, false };
+  ChannelLayout kExpectedLayouts[] = { CHANNEL_LAYOUT_NONE,
+                                       CHANNEL_LAYOUT_MONO,
+                                       CHANNEL_LAYOUT_STEREO,
+                                       CHANNEL_LAYOUT_SURROUND,
+                                       CHANNEL_LAYOUT_4_0,
+                                       CHANNEL_LAYOUT_5_0,
+                                       CHANNEL_LAYOUT_5_1,
+                                       CHANNEL_LAYOUT_7_0,
+                                       CHANNEL_LAYOUT_7_1 };
+
+
+  for (int i = 1; i < 9; ++i) {
+    if (i == 3 || i == 4 || i == 5)  // invalid number of channels
+      continue;
+    SCOPED_TRACE(base::StringPrintf("Attempting %d Channel", i));
+
+    // Hints will only be grabbed for channel numbers that have non-default
+    // devices associated with them.
+    if (kExpectedDeviceName[i] != AlsaPcmOutputStream::kDefaultDevice) {
+      // The DeviceNameHint and DeviceNameFreeHint need to be paired to avoid a
+      // memory leak.
+      EXPECT_CALL(mock_alsa_wrapper_, DeviceNameHint(_, _, _))
+          .WillOnce(DoAll(SetArgPointee<2>(&kFakeHints[0]), Return(0)));
+      EXPECT_CALL(mock_alsa_wrapper_, DeviceNameFreeHint(&kFakeHints[0]))
+          .Times(1);
+    }
+
+    EXPECT_CALL(mock_alsa_wrapper_,
+                PcmOpen(_, StrEq(kExpectedDeviceName[i]), _, _))
+        .WillOnce(DoAll(SetArgPointee<0>(kFakeHandle), Return(0)));
+    EXPECT_CALL(mock_alsa_wrapper_,
+                PcmSetParams(kFakeHandle, _, _, i, _, _, _))
+        .WillOnce(Return(0));
+
+    // The parameters are specified by ALSA documentation, and are in constants
+    // in the implementation files.
+    EXPECT_CALL(mock_alsa_wrapper_, DeviceNameGetHint(_, StrEq("IOID")))
+        .WillRepeatedly(Invoke(OutputHint));
+    EXPECT_CALL(mock_alsa_wrapper_, DeviceNameGetHint(_, StrEq("NAME")))
+        .WillRepeatedly(Invoke(EchoHint));
+
+    AlsaPcmOutputStream* test_stream = CreateStream(kExpectedLayouts[i]);
+    EXPECT_TRUE(test_stream->AutoSelectDevice(i));
+    EXPECT_EQ(kExpectedDownmix[i],
+              static_cast<bool>(test_stream->channel_mixer_));
+
+    Mock::VerifyAndClearExpectations(&mock_alsa_wrapper_);
+    Mock::VerifyAndClearExpectations(mock_manager_.get());
+    test_stream->Close();
+  }
+}
+
+TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_FallbackDevices) {
+  using std::string;
+
+  // If there are problems opening a multi-channel device, it the fallbacks
+  // operations should be as follows.  Assume the multi-channel device name is
+  // surround50:
+  //
+  //   1) Try open "surround50:CARD=foo,DEV=0"
+  //   2) Try open "plug:surround50:CARD=foo,DEV=0".
+  //   3) Try open "plug:surround50".
+  //   4) Try open "default".
+  //   5) Try open "plug:default".
+  //   6) Give up trying to open.
+  //
+  const string first_try = kSurround50;
+  const string second_try = string(AlsaPcmOutputStream::kPlugPrefix) +
+                            kSurround50;
+  const string third_try = string(AlsaPcmOutputStream::kPlugPrefix) +
+                           kGenericSurround50;
+  const string fourth_try = AlsaPcmOutputStream::kDefaultDevice;
+  const string fifth_try = string(AlsaPcmOutputStream::kPlugPrefix) +
+                           AlsaPcmOutputStream::kDefaultDevice;
+
+  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameHint(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(&kFakeHints[0]), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameFreeHint(&kFakeHints[0]))
+      .Times(1);
+  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameGetHint(_, StrEq("IOID")))
+      .WillRepeatedly(Invoke(OutputHint));
+  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameGetHint(_, StrEq("NAME")))
+      .WillRepeatedly(Invoke(EchoHint));
+  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
+      .WillRepeatedly(Return(kDummyMessage));
+
+  InSequence s;
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(first_try.c_str()), _, _))
+      .WillOnce(Return(kTestFailedErrno));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(second_try.c_str()), _, _))
+      .WillOnce(Return(kTestFailedErrno));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(third_try.c_str()), _, _))
+      .WillOnce(Return(kTestFailedErrno));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(fourth_try.c_str()), _, _))
+      .WillOnce(Return(kTestFailedErrno));
+  EXPECT_CALL(mock_alsa_wrapper_, PcmOpen(_, StrEq(fifth_try.c_str()), _, _))
+      .WillOnce(Return(kTestFailedErrno));
+
+  AlsaPcmOutputStream* test_stream = CreateStream(CHANNEL_LAYOUT_5_0);
+  EXPECT_FALSE(test_stream->AutoSelectDevice(5));
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, AutoSelectDevice_HintFail) {
+  // Should get |kDefaultDevice|, and force a 2-channel downmix on a failure to
+  // enumerate devices.
+  EXPECT_CALL(mock_alsa_wrapper_, DeviceNameHint(_, _, _))
+      .WillRepeatedly(Return(kTestFailedErrno));
+  EXPECT_CALL(mock_alsa_wrapper_,
+              PcmOpen(_, StrEq(AlsaPcmOutputStream::kDefaultDevice), _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(kFakeHandle), Return(0)));
+  EXPECT_CALL(mock_alsa_wrapper_,
+              PcmSetParams(kFakeHandle, _, _, 2, _, _, _))
+      .WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper_, StrError(kTestFailedErrno))
+      .WillOnce(Return(kDummyMessage));
+
+  AlsaPcmOutputStream* test_stream = CreateStream(CHANNEL_LAYOUT_5_0);
+  EXPECT_TRUE(test_stream->AutoSelectDevice(5));
+  EXPECT_TRUE(test_stream->channel_mixer_);
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, BufferPacket_StopStream) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  InitBuffer(test_stream);
+  test_stream->stop_stream_ = true;
+  bool source_exhausted;
+  test_stream->BufferPacket(&source_exhausted);
+  EXPECT_EQ(0, test_stream->buffer_->forward_bytes());
+  EXPECT_TRUE(source_exhausted);
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsOpened);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+  InitBuffer(test_stream);
+  DVLOG(1) << test_stream->state();
+  EXPECT_CALL(mock_alsa_wrapper_, PcmAvailUpdate(_))
+      .WillOnce(Return(10));
+  test_stream->ScheduleNextWrite(false);
+  DVLOG(1) << test_stream->state();
+  // TODO(sergeyu): Figure out how to check that the task has been added to the
+  // message loop.
+
+  // Cleanup the message queue. Currently ~MessageQueue() doesn't free pending
+  // tasks unless running on valgrind. The code below is needed to keep
+  // heapcheck happy.
+
+  test_stream->stop_stream_ = true;
+  DVLOG(1) << test_stream->state();
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsClosed);
+  DVLOG(1) << test_stream->state();
+  test_stream->Close();
+}
+
+TEST_F(AlsaPcmOutputStreamTest, ScheduleNextWrite_StopStream) {
+  AlsaPcmOutputStream* test_stream = CreateStream(kTestChannelLayout);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsOpened);
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsPlaying);
+
+  InitBuffer(test_stream);
+
+  test_stream->stop_stream_ = true;
+  test_stream->ScheduleNextWrite(true);
+
+  // TODO(ajwong): Find a way to test whether or not another task has been
+  // posted so we can verify that the Alsa code will indeed break the task
+  // posting loop.
+
+  test_stream->TransitionTo(AlsaPcmOutputStream::kIsClosed);
+  test_stream->Close();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/alsa/alsa_util.cc b/third_party/chromium/media/audio/alsa/alsa_util.cc
new file mode 100644
index 0000000..84b4133
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_util.cc
@@ -0,0 +1,380 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/alsa/alsa_util.h"
+
+#include <stddef.h>
+
+#include <functional>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+
+namespace alsa_util {
+
+namespace {
+
+// Set hardware parameters of PCM. It does the same thing as the corresponding
+// part in snd_pcm_set_params() (https://www.alsa-project.org, source code:
+// https://github.com/tiwai/alsa-lib/blob/master/src/pcm/pcm.c#L8459), except
+// that it configures buffer size and period size both to closest available
+// values instead of forcing the buffer size be 4 times of the period size.
+int ConfigureHwParams(media::AlsaWrapper* wrapper,
+                      snd_pcm_t* handle,
+                      snd_pcm_format_t format,
+                      snd_pcm_access_t access,
+                      unsigned int channels,
+                      unsigned int sample_rate,
+                      int soft_resample,
+                      snd_pcm_uframes_t frames_per_buffer,
+                      snd_pcm_uframes_t frames_per_period) {
+  int error = 0;
+
+  snd_pcm_hw_params_t* hw_params = nullptr;
+  error = wrapper->PcmHwParamsMalloc(&hw_params);
+  if (error < 0) {
+    LOG(ERROR) << "PcmHwParamsMalloc: " << wrapper->StrError(error);
+    return error;
+  }
+  // |snd_pcm_hw_params_t| is not exposed and requires memory allocation through
+  // ALSA API. Therefore, use a smart pointer to pointer to insure freeing
+  // memory when the function returns.
+  std::unique_ptr<snd_pcm_hw_params_t*,
+                  std::function<void(snd_pcm_hw_params_t**)>>
+      params_holder(&hw_params, [wrapper](snd_pcm_hw_params_t** params) {
+        wrapper->PcmHwParamsFree(*params);
+      });
+
+  error = wrapper->PcmHwParamsAny(handle, hw_params);
+  if (error < 0) {
+    LOG(ERROR) << "PcmHwParamsAny: " << wrapper->StrError(error);
+    return error;
+  }
+
+  error = wrapper->PcmHwParamsSetRateResample(handle, hw_params, soft_resample);
+  if (error < 0) {
+    LOG(ERROR) << "PcmHwParamsSetRateResample: " << wrapper->StrError(error);
+    return error;
+  }
+
+  error = wrapper->PcmHwParamsSetAccess(handle, hw_params, access);
+  if (error < 0) {
+    LOG(ERROR) << "PcmHwParamsSetAccess: " << wrapper->StrError(error);
+    return error;
+  }
+
+  error = wrapper->PcmHwParamsSetFormat(handle, hw_params, format);
+  if (error < 0) {
+    LOG(ERROR) << "PcmHwParamsSetFormat: " << wrapper->StrError(error);
+    return error;
+  }
+
+  error = wrapper->PcmHwParamsSetChannels(handle, hw_params, channels);
+  if (error < 0) {
+    LOG(ERROR) << "PcmHwParamsSetChannels: " << wrapper->StrError(error);
+    return error;
+  }
+
+  unsigned int rate = sample_rate;
+  error = wrapper->PcmHwParamsSetRateNear(handle, hw_params, &rate, nullptr);
+  if (error < 0) {
+    LOG(ERROR) << "PcmHwParamsSetRateNear: " << wrapper->StrError(error);
+    return error;
+  }
+  if (rate != sample_rate) {
+    LOG(ERROR) << "Rate doesn't match, required: " << sample_rate
+               << "Hz, but get: " << rate << "Hz.";
+    return -EINVAL;
+  }
+
+  error = wrapper->PcmHwParamsSetBufferSizeNear(handle, hw_params,
+                                                &frames_per_buffer);
+  if (error < 0) {
+    LOG(ERROR) << "PcmHwParamsSetBufferSizeNear: " << wrapper->StrError(error);
+    return error;
+  }
+
+  int direction = 0;
+  error = wrapper->PcmHwParamsSetPeriodSizeNear(handle, hw_params,
+                                                &frames_per_period, &direction);
+  if (error < 0) {
+    LOG(ERROR) << "PcmHwParamsSetPeriodSizeNear: " << wrapper->StrError(error);
+    return error;
+  }
+
+  if (frames_per_period > frames_per_buffer / 2) {
+    LOG(ERROR) << "Period size (" << frames_per_period
+               << ") is too big; buffer size = " << frames_per_buffer;
+    return -EINVAL;
+  }
+
+  error = wrapper->PcmHwParams(handle, hw_params);
+  if (error < 0)
+    LOG(ERROR) << "PcmHwParams: " << wrapper->StrError(error);
+
+  return error;
+}
+
+// Set software parameters of PCM. It does the same thing as the corresponding
+// part in snd_pcm_set_params()
+// (https://github.com/tiwai/alsa-lib/blob/master/src/pcm/pcm.c#L8603).
+int ConfigureSwParams(media::AlsaWrapper* wrapper,
+                      snd_pcm_t* handle,
+                      snd_pcm_uframes_t frames_per_buffer,
+                      snd_pcm_uframes_t frames_per_period) {
+  int error = 0;
+
+  snd_pcm_sw_params_t* sw_params = nullptr;
+  error = wrapper->PcmSwParamsMalloc(&sw_params);
+  if (error < 0) {
+    LOG(ERROR) << "PcmSwParamsMalloc: " << wrapper->StrError(error);
+    return error;
+  }
+  // |snd_pcm_sw_params_t| is not exposed and thus use a smart pointer to
+  // pointer to insure freeing memory when the function returns.
+  std::unique_ptr<snd_pcm_sw_params_t*,
+                  std::function<void(snd_pcm_sw_params_t**)>>
+      params_holder(&sw_params, [wrapper](snd_pcm_sw_params_t** params) {
+        wrapper->PcmSwParamsFree(*params);
+      });
+
+  error = wrapper->PcmSwParamsCurrent(handle, sw_params);
+  if (error < 0) {
+    LOG(ERROR) << "PcmSwParamsCurrent: " << wrapper->StrError(error);
+    return error;
+  }
+
+  // For playback, start the transfer when the buffer is almost full.
+  int start_threshold =
+      (frames_per_buffer / frames_per_period) * frames_per_period;
+  error =
+      wrapper->PcmSwParamsSetStartThreshold(handle, sw_params, start_threshold);
+  if (error < 0) {
+    LOG(ERROR) << "PcmSwParamsSetStartThreshold: " << wrapper->StrError(error);
+    return error;
+  }
+
+  // For capture, wake capture thread as soon as possible (1 period).
+  error = wrapper->PcmSwParamsSetAvailMin(handle, sw_params, frames_per_period);
+  if (error < 0) {
+    LOG(ERROR) << "PcmSwParamsSetAvailMin: " << wrapper->StrError(error);
+    return error;
+  }
+
+  error = wrapper->PcmSwParams(handle, sw_params);
+  if (error < 0)
+    LOG(ERROR) << "PcmSwParams: " << wrapper->StrError(error);
+
+  return error;
+}
+
+int SetParams(media::AlsaWrapper* wrapper,
+              snd_pcm_t* handle,
+              snd_pcm_format_t format,
+              unsigned int channels,
+              unsigned int rate,
+              unsigned int frames_per_buffer,
+              unsigned int frames_per_period) {
+  int error = ConfigureHwParams(
+      wrapper, handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, channels, rate,
+      1 /* Enable resampling */, frames_per_buffer, frames_per_period);
+  if (error == 0) {
+    error = ConfigureSwParams(wrapper, handle, frames_per_buffer,
+                              frames_per_period);
+  }
+  return error;
+}
+
+}  // namespace
+
+static snd_pcm_t* OpenDevice(media::AlsaWrapper* wrapper,
+                             const char* device_name,
+                             snd_pcm_stream_t type,
+                             int channels,
+                             int sample_rate,
+                             snd_pcm_format_t pcm_format,
+                             int buffer_us,
+                             int period_us = 0) {
+  snd_pcm_t* handle = NULL;
+  int error = wrapper->PcmOpen(&handle, device_name, type, SND_PCM_NONBLOCK);
+  if (error < 0) {
+    LOG(ERROR) << "PcmOpen: " << device_name << "," << wrapper->StrError(error);
+    return NULL;
+  }
+
+  error =
+      wrapper->PcmSetParams(handle, pcm_format, SND_PCM_ACCESS_RW_INTERLEAVED,
+                            channels, sample_rate, 1, buffer_us);
+  if (error < 0) {
+    LOG(WARNING) << "PcmSetParams: " << device_name << ", "
+                 << wrapper->StrError(error);
+    // Default parameter setting function failed, try again with the customized
+    // one if |period_us| is set, which is the case for capture but not for
+    // playback.
+    if (period_us > 0) {
+      const unsigned int frames_per_buffer = static_cast<unsigned int>(
+          static_cast<int64_t>(buffer_us) * sample_rate /
+          base::Time::kMicrosecondsPerSecond);
+      const unsigned int frames_per_period = static_cast<unsigned int>(
+          static_cast<int64_t>(period_us) * sample_rate /
+          base::Time::kMicrosecondsPerSecond);
+      LOG(WARNING) << "SetParams: " << device_name
+                   << " - Format: " << pcm_format << " Channels: " << channels
+                   << " Sample rate: " << sample_rate
+                   << " Buffer size: " << frames_per_buffer
+                   << " Period size: " << frames_per_period;
+      error = SetParams(wrapper, handle, pcm_format, channels, sample_rate,
+                        frames_per_buffer, frames_per_period);
+    }
+  }
+  if (error < 0) {
+    if (alsa_util::CloseDevice(wrapper, handle) < 0) {
+      // TODO(ajwong): Retry on certain errors?
+      LOG(WARNING) << "Unable to close audio device. Leaking handle.";
+    }
+    return NULL;
+  }
+
+  return handle;
+}
+
+static std::string DeviceNameToControlName(const std::string& device_name) {
+  const char kMixerPrefix[] = "hw";
+  std::string control_name;
+  size_t pos1 = device_name.find(':');
+  if (pos1 == std::string::npos) {
+    control_name = device_name;
+  } else {
+    // Examples:
+    // deviceName: "front:CARD=Intel,DEV=0", controlName: "hw:CARD=Intel".
+    // deviceName: "default:CARD=Intel", controlName: "CARD=Intel".
+    size_t pos2 = device_name.find(',');
+    control_name = (pos2 == std::string::npos)
+                       ? device_name.substr(pos1 + 1)
+                       : kMixerPrefix + device_name.substr(pos1, pos2 - pos1);
+  }
+
+  return control_name;
+}
+
+int CloseDevice(media::AlsaWrapper* wrapper, snd_pcm_t* handle) {
+  std::string device_name = wrapper->PcmName(handle);
+  int error = wrapper->PcmClose(handle);
+  if (error < 0) {
+    LOG(ERROR) << "PcmClose: " << device_name << ", "
+               << wrapper->StrError(error);
+  }
+
+  return error;
+}
+
+snd_pcm_t* OpenCaptureDevice(media::AlsaWrapper* wrapper,
+                             const char* device_name,
+                             int channels,
+                             int sample_rate,
+                             snd_pcm_format_t pcm_format,
+                             int buffer_us,
+                             int period_us) {
+  return OpenDevice(wrapper, device_name, SND_PCM_STREAM_CAPTURE, channels,
+                    sample_rate, pcm_format, buffer_us, period_us);
+}
+
+snd_pcm_t* OpenPlaybackDevice(media::AlsaWrapper* wrapper,
+                              const char* device_name,
+                              int channels,
+                              int sample_rate,
+                              snd_pcm_format_t pcm_format,
+                              int buffer_us) {
+  return OpenDevice(wrapper, device_name, SND_PCM_STREAM_PLAYBACK, channels,
+                    sample_rate, pcm_format, buffer_us);
+}
+
+snd_mixer_t* OpenMixer(media::AlsaWrapper* wrapper,
+                       const std::string& device_name) {
+  snd_mixer_t* mixer = NULL;
+
+  int error = wrapper->MixerOpen(&mixer, 0);
+  if (error < 0) {
+    LOG(ERROR) << "MixerOpen: " << device_name << ", "
+               << wrapper->StrError(error);
+    return NULL;
+  }
+
+  std::string control_name = DeviceNameToControlName(device_name);
+  error = wrapper->MixerAttach(mixer, control_name.c_str());
+  if (error < 0) {
+    LOG(ERROR) << "MixerAttach, " << control_name << ", "
+               << wrapper->StrError(error);
+    alsa_util::CloseMixer(wrapper, mixer, device_name);
+    return NULL;
+  }
+
+  error = wrapper->MixerElementRegister(mixer, NULL, NULL);
+  if (error < 0) {
+    LOG(ERROR) << "MixerElementRegister: " << control_name << ", "
+               << wrapper->StrError(error);
+    alsa_util::CloseMixer(wrapper, mixer, device_name);
+    return NULL;
+  }
+
+  return mixer;
+}
+
+void CloseMixer(media::AlsaWrapper* wrapper, snd_mixer_t* mixer,
+                const std::string& device_name) {
+  if (!mixer)
+    return;
+
+  wrapper->MixerFree(mixer);
+
+  int error = 0;
+  if (!device_name.empty()) {
+    std::string control_name = DeviceNameToControlName(device_name);
+    error = wrapper->MixerDetach(mixer, control_name.c_str());
+    if (error < 0) {
+      LOG(WARNING) << "MixerDetach: " << control_name << ", "
+                   << wrapper->StrError(error);
+    }
+  }
+
+  error = wrapper->MixerClose(mixer);
+  if (error < 0) {
+    LOG(WARNING) << "MixerClose: " << wrapper->StrError(error);
+  }
+}
+
+snd_mixer_elem_t* LoadCaptureMixerElement(media::AlsaWrapper* wrapper,
+                                          snd_mixer_t* mixer) {
+  if (!mixer)
+    return NULL;
+
+  int error = wrapper->MixerLoad(mixer);
+  if (error < 0) {
+    LOG(ERROR) << "MixerLoad: " << wrapper->StrError(error);
+    return NULL;
+  }
+
+  snd_mixer_elem_t* elem = NULL;
+  snd_mixer_elem_t* mic_elem = NULL;
+  const char kCaptureElemName[] = "Capture";
+  const char kMicElemName[] = "Mic";
+  for (elem = wrapper->MixerFirstElem(mixer);
+       elem;
+       elem = wrapper->MixerNextElem(elem)) {
+    if (wrapper->MixerSelemIsActive(elem)) {
+      const char* elem_name = wrapper->MixerSelemName(elem);
+      if (strcmp(elem_name, kCaptureElemName) == 0)
+        return elem;
+      else if (strcmp(elem_name, kMicElemName) == 0)
+        mic_elem = elem;
+    }
+  }
+
+  // Did not find any Capture handle, use the Mic handle.
+  return mic_elem;
+}
+
+}  // namespace alsa_util
diff --git a/third_party/chromium/media/audio/alsa/alsa_util.h b/third_party/chromium/media/audio/alsa/alsa_util.h
new file mode 100644
index 0000000..47b5d1b
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_util.h
@@ -0,0 +1,52 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ALSA_ALSA_UTIL_H_
+#define MEDIA_AUDIO_ALSA_ALSA_UTIL_H_
+
+#include <alsa/asoundlib.h>
+#include <string>
+
+#include "media/base/media_export.h"
+
+namespace media {
+class AlsaWrapper;
+}
+
+namespace alsa_util {
+
+// When opening ALSA devices, |period_us| is the size of a packet and
+// |buffer_us| is the size of the ring buffer, which consists of multiple
+// packets. In capture devices, the latency relies more on |period_us|, and thus
+// one may require more details upon the value implicitly set by ALSA.
+MEDIA_EXPORT snd_pcm_t* OpenCaptureDevice(media::AlsaWrapper* wrapper,
+                                          const char* device_name,
+                                          int channels,
+                                          int sample_rate,
+                                          snd_pcm_format_t pcm_format,
+                                          int buffer_us,
+                                          int period_us);
+
+snd_pcm_t* OpenPlaybackDevice(media::AlsaWrapper* wrapper,
+                              const char* device_name,
+                              int channels,
+                              int sample_rate,
+                              snd_pcm_format_t pcm_format,
+                              int buffer_us);
+
+int CloseDevice(media::AlsaWrapper* wrapper, snd_pcm_t* handle);
+
+snd_mixer_t* OpenMixer(media::AlsaWrapper* wrapper,
+                       const std::string& device_name);
+
+void CloseMixer(media::AlsaWrapper* wrapper,
+                snd_mixer_t* mixer,
+                const std::string& device_name);
+
+snd_mixer_elem_t* LoadCaptureMixerElement(media::AlsaWrapper* wrapper,
+                                          snd_mixer_t* mixer);
+
+}  // namespace alsa_util
+
+#endif  // MEDIA_AUDIO_ALSA_ALSA_UTIL_H_
diff --git a/third_party/chromium/media/audio/alsa/alsa_util_unittest.cc b/third_party/chromium/media/audio/alsa/alsa_util_unittest.cc
new file mode 100644
index 0000000..343730d
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_util_unittest.cc
@@ -0,0 +1,44 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/alsa/alsa_util.h"
+#include "media/audio/alsa/mock_alsa_wrapper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace alsa_util {
+
+namespace {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+
+}  // namespace
+
+TEST(AlsaUtilTest, FreeHwParams) {
+  InSequence seq;
+  media::MockAlsaWrapper mock_alsa_wrapper;
+  snd_pcm_hw_params_t* params_ptr = (snd_pcm_hw_params_t*)malloc(1);
+  EXPECT_CALL(mock_alsa_wrapper, PcmOpen(_, _, _, _)).WillOnce(Return(0));
+  EXPECT_CALL(mock_alsa_wrapper, PcmSetParams(_, _, _, _, _, _, _))
+      .WillOnce(Return(-1));
+  EXPECT_CALL(mock_alsa_wrapper, StrError(_)).WillOnce(Return("error"));
+  EXPECT_CALL(mock_alsa_wrapper, PcmHwParamsMalloc(_))
+      .WillOnce(Invoke([params_ptr](snd_pcm_hw_params_t** params) {
+        *params = params_ptr;
+        return 0;
+      }));
+  EXPECT_CALL(mock_alsa_wrapper, PcmHwParamsAny(_, _)).WillOnce(Return(-1));
+  EXPECT_CALL(mock_alsa_wrapper, StrError(_)).WillOnce(Return("error"));
+  EXPECT_CALL(mock_alsa_wrapper, PcmHwParamsFree(params_ptr));
+  EXPECT_CALL(mock_alsa_wrapper, PcmName(_)).WillOnce(Return("default"));
+  EXPECT_CALL(mock_alsa_wrapper, PcmClose(_)).WillOnce(Return(0));
+  snd_pcm_t* handle = OpenCaptureDevice(&mock_alsa_wrapper, "default", 2, 48000,
+                                        SND_PCM_FORMAT_S16, 40000, 10000);
+  EXPECT_EQ(handle, nullptr);
+  free(params_ptr);
+}
+
+}  // namespace alsa_util
diff --git a/third_party/chromium/media/audio/alsa/alsa_wrapper.cc b/third_party/chromium/media/audio/alsa/alsa_wrapper.cc
new file mode 100644
index 0000000..b24630d
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_wrapper.cc
@@ -0,0 +1,383 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/alsa/alsa_wrapper.h"
+
+namespace media {
+
+AlsaWrapper::AlsaWrapper() = default;
+
+AlsaWrapper::~AlsaWrapper() = default;
+
+int AlsaWrapper::PcmOpen(snd_pcm_t** handle, const char* name,
+                         snd_pcm_stream_t stream, int mode) {
+  return snd_pcm_open(handle, name, stream, mode);
+}
+
+int AlsaWrapper::DeviceNameHint(int card, const char* iface, void*** hints) {
+  return snd_device_name_hint(card, iface, hints);
+}
+
+char* AlsaWrapper::DeviceNameGetHint(const void* hint, const char* id) {
+  return snd_device_name_get_hint(hint, id);
+}
+
+int AlsaWrapper::DeviceNameFreeHint(void** hints) {
+  return snd_device_name_free_hint(hints);
+}
+
+int AlsaWrapper::CardNext(int* rcard) {
+  return snd_card_next(rcard);
+}
+
+int AlsaWrapper::PcmClose(snd_pcm_t* handle) {
+  return snd_pcm_close(handle);
+}
+
+int AlsaWrapper::PcmPrepare(snd_pcm_t* handle) {
+  return snd_pcm_prepare(handle);
+}
+
+int AlsaWrapper::PcmDrain(snd_pcm_t* handle) {
+  return snd_pcm_drain(handle);
+}
+
+int AlsaWrapper::PcmDrop(snd_pcm_t* handle) {
+  return snd_pcm_drop(handle);
+}
+
+int AlsaWrapper::PcmDelay(snd_pcm_t* handle, snd_pcm_sframes_t* delay) {
+  return snd_pcm_delay(handle, delay);
+}
+
+int AlsaWrapper::PcmResume(snd_pcm_t* handle) {
+  return snd_pcm_resume(handle);
+}
+
+snd_pcm_sframes_t AlsaWrapper::PcmWritei(snd_pcm_t* handle,
+                                         const void* buffer,
+                                         snd_pcm_uframes_t size) {
+  return snd_pcm_writei(handle, buffer, size);
+}
+
+snd_pcm_sframes_t AlsaWrapper::PcmReadi(snd_pcm_t* handle,
+                                        void* buffer,
+                                        snd_pcm_uframes_t size) {
+  return snd_pcm_readi(handle, buffer, size);
+}
+
+int AlsaWrapper::PcmRecover(snd_pcm_t* handle, int err, int silent) {
+  return snd_pcm_recover(handle, err, silent);
+}
+
+const char* AlsaWrapper::PcmName(snd_pcm_t* handle) {
+  return snd_pcm_name(handle);
+}
+
+int AlsaWrapper::PcmSetParams(snd_pcm_t* handle, snd_pcm_format_t format,
+                              snd_pcm_access_t access, unsigned int channels,
+                              unsigned int rate, int soft_resample,
+                              unsigned int latency) {
+  return snd_pcm_set_params(handle,
+                            format,
+                            access,
+                            channels,
+                            rate,
+                            soft_resample,
+                            latency);
+}
+
+int AlsaWrapper::PcmGetParams(snd_pcm_t* handle, snd_pcm_uframes_t* buffer_size,
+                              snd_pcm_uframes_t* period_size) {
+  return snd_pcm_get_params(handle, buffer_size, period_size);
+}
+
+int AlsaWrapper::PcmHwParamsMalloc(snd_pcm_hw_params_t** hw_params) {
+  return snd_pcm_hw_params_malloc(hw_params);
+}
+
+int AlsaWrapper::PcmHwParamsAny(snd_pcm_t* handle,
+                                snd_pcm_hw_params_t* hw_params) {
+  return snd_pcm_hw_params_any(handle, hw_params);
+}
+
+int AlsaWrapper::PcmHwParamsCanResume(snd_pcm_hw_params_t* hw_params) {
+  return snd_pcm_hw_params_can_resume(hw_params);
+}
+
+int AlsaWrapper::PcmHwParamsSetRateResample(snd_pcm_t* handle,
+                                            snd_pcm_hw_params_t* hw_params,
+                                            unsigned int value) {
+  return snd_pcm_hw_params_set_rate_resample(handle, hw_params, value);
+}
+
+int AlsaWrapper::PcmHwParamsSetRateNear(snd_pcm_t* handle,
+                                        snd_pcm_hw_params_t* hw_params,
+                                        unsigned int* rate,
+                                        int* direction) {
+  return snd_pcm_hw_params_set_rate_near(handle, hw_params, rate, direction);
+}
+
+int AlsaWrapper::PcmHwParamsTestFormat(snd_pcm_t* handle,
+                                       snd_pcm_hw_params_t* hw_params,
+                                       snd_pcm_format_t format) {
+  return snd_pcm_hw_params_test_format(handle, hw_params, format);
+}
+
+int AlsaWrapper::PcmFormatSize(snd_pcm_format_t format, size_t samples) {
+  return snd_pcm_format_size(format, samples);
+}
+
+int AlsaWrapper::PcmHwParamsGetChannelsMin(const snd_pcm_hw_params_t* hw_params,
+                                           unsigned int* min_channels) {
+  return snd_pcm_hw_params_get_channels_min(hw_params, min_channels);
+}
+
+int AlsaWrapper::PcmHwParamsGetChannelsMax(const snd_pcm_hw_params_t* hw_params,
+                                           unsigned int* max_channels) {
+  return snd_pcm_hw_params_get_channels_min(hw_params, max_channels);
+}
+
+int AlsaWrapper::PcmHwParamsSetFormat(snd_pcm_t* handle,
+                                      snd_pcm_hw_params_t* hw_params,
+                                      snd_pcm_format_t format) {
+  return snd_pcm_hw_params_set_format(handle, hw_params, format);
+}
+
+int AlsaWrapper::PcmHwParamsSetAccess(snd_pcm_t* handle,
+                                      snd_pcm_hw_params_t* hw_params,
+                                      snd_pcm_access_t access) {
+  return snd_pcm_hw_params_set_access(handle, hw_params, access);
+}
+
+int AlsaWrapper::PcmHwParamsSetChannels(snd_pcm_t* handle,
+                                        snd_pcm_hw_params_t* hw_params,
+                                        unsigned int channels) {
+  return snd_pcm_hw_params_set_channels(handle, hw_params, channels);
+}
+
+int AlsaWrapper::PcmHwParamsSetBufferSizeNear(snd_pcm_t* handle,
+                                              snd_pcm_hw_params_t* hw_params,
+                                              snd_pcm_uframes_t* buffer_size) {
+  return snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, buffer_size);
+}
+
+int AlsaWrapper::PcmHwParamsSetPeriodSizeNear(snd_pcm_t* handle,
+                                              snd_pcm_hw_params_t* hw_params,
+                                              snd_pcm_uframes_t* period_size,
+                                              int* direction) {
+  return snd_pcm_hw_params_set_period_size_near(handle, hw_params, period_size,
+                                                direction);
+}
+
+int AlsaWrapper::PcmHwParams(snd_pcm_t* handle,
+                             snd_pcm_hw_params_t* hw_params) {
+  return snd_pcm_hw_params(handle, hw_params);
+}
+
+void AlsaWrapper::PcmHwParamsFree(snd_pcm_hw_params_t* hw_params) {
+  return snd_pcm_hw_params_free(hw_params);
+}
+
+int AlsaWrapper::PcmSwParamsMalloc(snd_pcm_sw_params_t** sw_params) {
+  return snd_pcm_sw_params_malloc(sw_params);
+}
+
+int AlsaWrapper::PcmSwParamsCurrent(snd_pcm_t* handle,
+                                    snd_pcm_sw_params_t* sw_params) {
+  return snd_pcm_sw_params_current(handle, sw_params);
+}
+
+int AlsaWrapper::PcmSwParamsSetStartThreshold(
+    snd_pcm_t* handle,
+    snd_pcm_sw_params_t* sw_params,
+    snd_pcm_uframes_t start_threshold) {
+  return snd_pcm_sw_params_set_start_threshold(handle, sw_params,
+                                               start_threshold);
+}
+
+int AlsaWrapper::PcmSwParamsSetAvailMin(snd_pcm_t* handle,
+                                        snd_pcm_sw_params_t* sw_params,
+                                        snd_pcm_uframes_t period_size) {
+  return snd_pcm_sw_params_set_avail_min(handle, sw_params, period_size);
+}
+
+int AlsaWrapper::PcmSwParams(snd_pcm_t* handle,
+                             snd_pcm_sw_params_t* sw_params) {
+  return snd_pcm_sw_params(handle, sw_params);
+}
+
+void AlsaWrapper::PcmSwParamsFree(snd_pcm_sw_params_t* sw_params) {
+  return snd_pcm_sw_params_free(sw_params);
+}
+
+snd_pcm_sframes_t AlsaWrapper::PcmAvailUpdate(snd_pcm_t* handle) {
+  return snd_pcm_avail_update(handle);
+}
+
+snd_pcm_state_t AlsaWrapper::PcmState(snd_pcm_t* handle) {
+  return snd_pcm_state(handle);
+}
+
+const char* AlsaWrapper::StrError(int errnum) {
+  return snd_strerror(errnum);
+}
+
+int AlsaWrapper::PcmStart(snd_pcm_t* handle) {
+  return snd_pcm_start(handle);
+}
+
+int AlsaWrapper::MixerOpen(snd_mixer_t** mixer, int mode) {
+  return snd_mixer_open(mixer, mode);
+}
+
+int AlsaWrapper::MixerAttach(snd_mixer_t* mixer, const char* name) {
+  return snd_mixer_attach(mixer, name);
+}
+
+int AlsaWrapper::MixerElementRegister(snd_mixer_t* mixer,
+                                      struct snd_mixer_selem_regopt* options,
+                                      snd_mixer_class_t** classp) {
+  return snd_mixer_selem_register(mixer, options, classp);
+}
+
+void AlsaWrapper::MixerFree(snd_mixer_t* mixer) {
+  snd_mixer_free(mixer);
+}
+
+int AlsaWrapper::MixerDetach(snd_mixer_t* mixer, const char* name) {
+  return snd_mixer_detach(mixer, name);
+}
+
+int AlsaWrapper::MixerClose(snd_mixer_t* mixer) {
+  return snd_mixer_close(mixer);
+}
+
+int AlsaWrapper::MixerLoad(snd_mixer_t* mixer) {
+  return snd_mixer_load(mixer);
+}
+
+snd_mixer_elem_t* AlsaWrapper::MixerFirstElem(snd_mixer_t* mixer) {
+  return snd_mixer_first_elem(mixer);
+}
+
+snd_mixer_elem_t* AlsaWrapper::MixerNextElem(snd_mixer_elem_t* elem) {
+  return snd_mixer_elem_next(elem);
+}
+
+int AlsaWrapper::MixerSelemIsActive(snd_mixer_elem_t* elem) {
+  return snd_mixer_selem_is_active(elem);
+}
+
+const char* AlsaWrapper::MixerSelemName(snd_mixer_elem_t* elem) {
+  return snd_mixer_selem_get_name(elem);
+}
+
+int AlsaWrapper::MixerSelemSetCaptureVolumeAll(
+    snd_mixer_elem_t* elem, long value) {
+  return snd_mixer_selem_set_capture_volume_all(elem, value);
+}
+
+int AlsaWrapper::MixerSelemGetCaptureVolume(
+    snd_mixer_elem_t* elem, snd_mixer_selem_channel_id_t channel, long* value) {
+  return snd_mixer_selem_get_capture_volume(elem, channel, value);
+}
+
+int AlsaWrapper::MixerSelemHasCaptureVolume(snd_mixer_elem_t* elem) {
+  return snd_mixer_selem_has_capture_volume(elem);
+}
+
+int AlsaWrapper::MixerSelemGetCaptureVolumeRange(snd_mixer_elem_t* elem,
+                                                 long* min, long* max) {
+  return snd_mixer_selem_get_capture_volume_range(elem, min, max);
+}
+
+void* AlsaWrapper::MixerElemGetCallbackPrivate(const snd_mixer_elem_t* obj) {
+  return snd_mixer_elem_get_callback_private(obj);
+}
+
+void AlsaWrapper::MixerElemSetCallback(snd_mixer_elem_t* obj,
+                                       snd_mixer_elem_callback_t val) {
+  snd_mixer_elem_set_callback(obj, val);
+}
+
+void AlsaWrapper::MixerElemSetCallbackPrivate(snd_mixer_elem_t* obj,
+                                              void* val) {
+  snd_mixer_elem_set_callback_private(obj, val);
+}
+
+snd_mixer_elem_t* AlsaWrapper::MixerFindSelem(snd_mixer_t* mixer,
+                                              const snd_mixer_selem_id_t* id) {
+  return snd_mixer_find_selem(mixer, id);
+}
+
+int AlsaWrapper::MixerHandleEvents(snd_mixer_t* mixer) {
+  return snd_mixer_handle_events(mixer);
+}
+
+int AlsaWrapper::MixerPollDescriptors(snd_mixer_t* mixer,
+                                      struct pollfd* pfds,
+                                      unsigned int space) {
+  return snd_mixer_poll_descriptors(mixer, pfds, space);
+}
+
+int AlsaWrapper::MixerPollDescriptorsCount(snd_mixer_t* mixer) {
+  return snd_mixer_poll_descriptors_count(mixer);
+}
+
+int AlsaWrapper::MixerSelemGetPlaybackSwitch(
+    snd_mixer_elem_t* elem,
+    snd_mixer_selem_channel_id_t channel,
+    int* value) {
+  return snd_mixer_selem_get_playback_switch(elem, channel, value);
+}
+
+int AlsaWrapper::MixerSelemGetPlaybackVolume(
+    snd_mixer_elem_t* elem,
+    snd_mixer_selem_channel_id_t channel,
+    long* value) {
+  return snd_mixer_selem_get_playback_volume(elem, channel, value);
+}
+
+int AlsaWrapper::MixerSelemGetPlaybackVolumeRange(snd_mixer_elem_t* elem,
+                                                  long* min,
+                                                  long* max) {
+  return snd_mixer_selem_get_playback_volume_range(elem, min, max);
+}
+
+int AlsaWrapper::MixerSelemHasPlaybackSwitch(snd_mixer_elem_t* elem) {
+  return snd_mixer_selem_has_playback_switch(elem);
+}
+
+void AlsaWrapper::MixerSelemIdSetIndex(snd_mixer_selem_id_t* obj,
+                                       unsigned int val) {
+  snd_mixer_selem_id_set_index(obj, val);
+}
+
+void AlsaWrapper::MixerSelemIdSetName(snd_mixer_selem_id_t* obj,
+                                      const char* val) {
+  snd_mixer_selem_id_set_name(obj, val);
+}
+
+int AlsaWrapper::MixerSelemSetPlaybackSwitch(
+    snd_mixer_elem_t* elem,
+    snd_mixer_selem_channel_id_t channel,
+    int value) {
+  return snd_mixer_selem_set_playback_switch(elem, channel, value);
+}
+
+int AlsaWrapper::MixerSelemSetPlaybackVolumeAll(snd_mixer_elem_t* elem,
+                                                long value) {
+  return snd_mixer_selem_set_playback_volume_all(elem, value);
+}
+
+int AlsaWrapper::MixerSelemIdMalloc(snd_mixer_selem_id_t** ptr) {
+  return snd_mixer_selem_id_malloc(ptr);
+}
+
+void AlsaWrapper::MixerSelemIdFree(snd_mixer_selem_id_t* obj) {
+  snd_mixer_selem_id_free(obj);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/alsa/alsa_wrapper.h b/third_party/chromium/media/audio/alsa/alsa_wrapper.h
new file mode 100644
index 0000000..30905b4
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/alsa_wrapper.h
@@ -0,0 +1,163 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// AlsaWrapper is a simple stateless class that wraps the alsa library commands
+// we want to use.  It's purpose is to allow injection of a mock so that the
+// higher level code is testable.
+
+#ifndef MEDIA_AUDIO_ALSA_ALSA_WRAPPER_H_
+#define MEDIA_AUDIO_ALSA_ALSA_WRAPPER_H_
+
+#include <alsa/asoundlib.h>
+
+#include "base/macros.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class MEDIA_EXPORT AlsaWrapper {
+ public:
+  AlsaWrapper();
+
+  AlsaWrapper(const AlsaWrapper&) = delete;
+  AlsaWrapper& operator=(const AlsaWrapper&) = delete;
+
+  virtual ~AlsaWrapper();
+
+  virtual int DeviceNameHint(int card, const char* iface, void*** hints);
+  virtual char* DeviceNameGetHint(const void* hint, const char* id);
+  virtual int DeviceNameFreeHint(void** hints);
+  virtual int CardNext(int* rcard);
+
+  virtual int PcmOpen(snd_pcm_t** handle, const char* name,
+                      snd_pcm_stream_t stream, int mode);
+  virtual int PcmClose(snd_pcm_t* handle);
+  virtual int PcmPrepare(snd_pcm_t* handle);
+  virtual int PcmDrain(snd_pcm_t* handle);
+  virtual int PcmDrop(snd_pcm_t* handle);
+  virtual int PcmDelay(snd_pcm_t* handle, snd_pcm_sframes_t* delay);
+  virtual int PcmResume(snd_pcm_t* handle);
+  virtual snd_pcm_sframes_t PcmWritei(snd_pcm_t* handle,
+                                      const void* buffer,
+                                      snd_pcm_uframes_t size);
+  virtual snd_pcm_sframes_t PcmReadi(snd_pcm_t* handle,
+                                     void* buffer,
+                                     snd_pcm_uframes_t size);
+  virtual int PcmRecover(snd_pcm_t* handle, int err, int silent);
+  virtual int PcmSetParams(snd_pcm_t* handle, snd_pcm_format_t format,
+                           snd_pcm_access_t access, unsigned int channels,
+                           unsigned int rate, int soft_resample,
+                           unsigned int latency);
+  virtual int PcmGetParams(snd_pcm_t* handle, snd_pcm_uframes_t* buffer_size,
+                           snd_pcm_uframes_t* period_size);
+  virtual int PcmHwParamsMalloc(snd_pcm_hw_params_t** hw_params);
+  virtual int PcmHwParamsAny(snd_pcm_t* handle, snd_pcm_hw_params_t* hw_params);
+  virtual int PcmHwParamsCanResume(snd_pcm_hw_params_t* hw_params);
+  virtual int PcmHwParamsSetRateResample(snd_pcm_t* handle,
+                                         snd_pcm_hw_params_t* hw_params,
+                                         unsigned int value);
+  virtual int PcmHwParamsSetRateNear(snd_pcm_t* handle,
+                                     snd_pcm_hw_params_t* hw_params,
+                                     unsigned int* rate,
+                                     int* direction);
+  virtual int PcmHwParamsTestFormat(snd_pcm_t* handle,
+                                    snd_pcm_hw_params_t* hw_params,
+                                    snd_pcm_format_t format);
+  virtual int PcmFormatSize(snd_pcm_format_t format, size_t samples);
+  virtual int PcmHwParamsGetChannelsMin(const snd_pcm_hw_params_t* hw_params,
+                                        unsigned int* min_channels);
+  virtual int PcmHwParamsGetChannelsMax(const snd_pcm_hw_params_t* hw_params,
+                                        unsigned int* max_channels);
+  virtual int PcmHwParamsSetFormat(snd_pcm_t* handle,
+                                   snd_pcm_hw_params_t* hw_params,
+                                   snd_pcm_format_t format);
+  virtual int PcmHwParamsSetAccess(snd_pcm_t* handle,
+                                   snd_pcm_hw_params_t* hw_params,
+                                   snd_pcm_access_t access);
+  virtual int PcmHwParamsSetChannels(snd_pcm_t* handle,
+                                     snd_pcm_hw_params_t* hw_params,
+                                     unsigned int channels);
+  virtual int PcmHwParamsSetBufferSizeNear(snd_pcm_t* handle,
+                                           snd_pcm_hw_params_t* hw_params,
+                                           snd_pcm_uframes_t* buffer_size);
+  virtual int PcmHwParamsSetPeriodSizeNear(snd_pcm_t* handle,
+                                           snd_pcm_hw_params_t* hw_params,
+                                           snd_pcm_uframes_t* period_size,
+                                           int* direction);
+  virtual int PcmHwParams(snd_pcm_t* handle, snd_pcm_hw_params_t* hw_params);
+  virtual void PcmHwParamsFree(snd_pcm_hw_params_t* hw_params);
+  virtual int PcmSwParamsMalloc(snd_pcm_sw_params_t** sw_params);
+  virtual int PcmSwParamsCurrent(snd_pcm_t* handle,
+                                 snd_pcm_sw_params_t* sw_params);
+  virtual int PcmSwParamsSetStartThreshold(snd_pcm_t* handle,
+                                           snd_pcm_sw_params_t* sw_params,
+                                           snd_pcm_uframes_t start_threshold);
+  virtual int PcmSwParamsSetAvailMin(snd_pcm_t* handle,
+                                     snd_pcm_sw_params_t* sw_params,
+                                     snd_pcm_uframes_t period_size);
+  virtual int PcmSwParams(snd_pcm_t* handle, snd_pcm_sw_params_t* sw_params);
+  virtual void PcmSwParamsFree(snd_pcm_sw_params_t* sw_params);
+  virtual const char* PcmName(snd_pcm_t* handle);
+  virtual snd_pcm_sframes_t PcmAvailUpdate(snd_pcm_t* handle);
+  virtual snd_pcm_state_t PcmState(snd_pcm_t* handle);
+  virtual int PcmStart(snd_pcm_t* handle);
+
+  virtual int MixerOpen(snd_mixer_t** mixer, int mode);
+  virtual int MixerAttach(snd_mixer_t* mixer, const char* name);
+  virtual int MixerElementRegister(snd_mixer_t* mixer,
+                                   struct snd_mixer_selem_regopt* options,
+                                   snd_mixer_class_t** classp);
+  virtual void MixerFree(snd_mixer_t* mixer);
+  virtual int MixerDetach(snd_mixer_t* mixer, const char* name);
+  virtual int MixerClose(snd_mixer_t* mixer);
+  virtual int MixerLoad(snd_mixer_t* mixer);
+  virtual snd_mixer_elem_t* MixerFirstElem(snd_mixer_t* mixer);
+  virtual snd_mixer_elem_t* MixerNextElem(snd_mixer_elem_t* elem);
+  virtual int MixerSelemIsActive(snd_mixer_elem_t* elem);
+  virtual const char* MixerSelemName(snd_mixer_elem_t* elem);
+  virtual int MixerSelemSetCaptureVolumeAll(snd_mixer_elem_t* elem, long value);
+  virtual int MixerSelemGetCaptureVolume(snd_mixer_elem_t* elem,
+                                         snd_mixer_selem_channel_id_t channel,
+                                         long* value);
+  virtual int MixerSelemHasCaptureVolume(snd_mixer_elem_t* elem);
+  virtual int MixerSelemGetCaptureVolumeRange(snd_mixer_elem_t* elem,
+                                              long* min, long* max);
+  virtual void* MixerElemGetCallbackPrivate(const snd_mixer_elem_t* obj);
+  virtual void MixerElemSetCallback(snd_mixer_elem_t* obj,
+                                    snd_mixer_elem_callback_t val);
+  virtual void MixerElemSetCallbackPrivate(snd_mixer_elem_t* obj, void* val);
+  virtual snd_mixer_elem_t* MixerFindSelem(snd_mixer_t* mixer,
+                                           const snd_mixer_selem_id_t* id);
+  virtual int MixerHandleEvents(snd_mixer_t* mixer);
+  virtual int MixerPollDescriptors(snd_mixer_t* mixer,
+                                   struct pollfd* pfds,
+                                   unsigned int space);
+  virtual int MixerPollDescriptorsCount(snd_mixer_t* mixer);
+  virtual int MixerSelemGetPlaybackSwitch(snd_mixer_elem_t* elem,
+                                          snd_mixer_selem_channel_id_t channel,
+                                          int* value);
+  virtual int MixerSelemGetPlaybackVolume(snd_mixer_elem_t* elem,
+                                          snd_mixer_selem_channel_id_t channel,
+                                          long* value);
+  virtual int MixerSelemGetPlaybackVolumeRange(snd_mixer_elem_t* elem,
+                                               long* min,
+                                               long* max);
+  virtual int MixerSelemHasPlaybackSwitch(snd_mixer_elem_t* elem);
+  virtual void MixerSelemIdSetIndex(snd_mixer_selem_id_t* obj,
+                                    unsigned int val);
+  virtual void MixerSelemIdSetName(snd_mixer_selem_id_t* obj, const char* val);
+  virtual int MixerSelemSetPlaybackSwitch(snd_mixer_elem_t* elem,
+                                          snd_mixer_selem_channel_id_t channel,
+                                          int value);
+  virtual int MixerSelemSetPlaybackVolumeAll(snd_mixer_elem_t* elem,
+                                             long value);
+  virtual int MixerSelemIdMalloc(snd_mixer_selem_id_t** ptr);
+  virtual void MixerSelemIdFree(snd_mixer_selem_id_t* obj);
+
+  virtual const char* StrError(int errnum);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ALSA_ALSA_WRAPPER_H_
diff --git a/third_party/chromium/media/audio/alsa/audio_manager_alsa.cc b/third_party/chromium/media/audio/alsa/audio_manager_alsa.cc
new file mode 100644
index 0000000..12a95b3
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/audio_manager_alsa.cc
@@ -0,0 +1,324 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/alsa/audio_manager_alsa.h"
+
+#include <stddef.h>
+
+#include "base/command_line.h"
+#include "base/cxx17_backports.h"
+#include "base/logging.h"
+#include "base/memory/free_deleter.h"
+#include "base/metrics/histogram.h"
+#include "media/audio/alsa/alsa_input.h"
+#include "media/audio/alsa/alsa_output.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_output_dispatcher.h"
+#if defined(USE_PULSEAUDIO)
+#include "media/audio/pulse/audio_manager_pulse.h"
+#endif
+#include "media/base/audio_parameters.h"
+#include "media/base/channel_layout.h"
+#include "media/base/limits.h"
+#include "media/base/media_switches.h"
+
+namespace media {
+
+// Maximum number of output streams that can be open simultaneously.
+static const int kMaxOutputStreams = 50;
+
+// Default sample rate for input and output streams.
+static const int kDefaultSampleRate = 48000;
+
+// Since "default", "pulse" and "dmix" devices are virtual devices mapped to
+// real devices, we remove them from the list to avoiding duplicate counting.
+// In addition, note that we support no more than 2 channels for recording,
+// hence surround devices are not stored in the list.
+static const char* const kInvalidAudioInputDevices[] = {
+    "default", "dmix", "null", "pulse", "surround",
+};
+
+AudioManagerAlsa::AudioManagerAlsa(std::unique_ptr<AudioThread> audio_thread,
+                                   AudioLogFactory* audio_log_factory)
+    : AudioManagerBase(std::move(audio_thread), audio_log_factory),
+      wrapper_(new AlsaWrapper()) {
+  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
+}
+
+AudioManagerAlsa::~AudioManagerAlsa() = default;
+
+bool AudioManagerAlsa::HasAudioOutputDevices() {
+  return HasAnyAlsaAudioDevice(kStreamPlayback);
+}
+
+bool AudioManagerAlsa::HasAudioInputDevices() {
+  return HasAnyAlsaAudioDevice(kStreamCapture);
+}
+
+void AudioManagerAlsa::GetAudioInputDeviceNames(
+    AudioDeviceNames* device_names) {
+  DCHECK(device_names->empty());
+  GetAlsaAudioDevices(kStreamCapture, device_names);
+}
+
+void AudioManagerAlsa::GetAudioOutputDeviceNames(
+    AudioDeviceNames* device_names) {
+  DCHECK(device_names->empty());
+  GetAlsaAudioDevices(kStreamPlayback, device_names);
+}
+
+AudioParameters AudioManagerAlsa::GetInputStreamParameters(
+    const std::string& device_id) {
+  static const int kDefaultInputBufferSize = 1024;
+
+  return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                         CHANNEL_LAYOUT_STEREO, kDefaultSampleRate,
+                         kDefaultInputBufferSize);
+}
+
+const char* AudioManagerAlsa::GetName() {
+  return "ALSA";
+}
+
+void AudioManagerAlsa::GetAlsaAudioDevices(StreamType type,
+                                           AudioDeviceNames* device_names) {
+  // Constants specified by the ALSA API for device hints.
+  static const char kPcmInterfaceName[] = "pcm";
+  int card = -1;
+
+  // Loop through the sound cards to get ALSA device hints.
+  while (!wrapper_->CardNext(&card) && card >= 0) {
+    void** hints = NULL;
+    int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
+    if (!error) {
+      GetAlsaDevicesInfo(type, hints, device_names);
+
+      // Destroy the hints now that we're done with it.
+      wrapper_->DeviceNameFreeHint(hints);
+    } else {
+      DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: "
+                    << wrapper_->StrError(error);
+    }
+  }
+}
+
+void AudioManagerAlsa::GetAlsaDevicesInfo(AudioManagerAlsa::StreamType type,
+                                          void** hints,
+                                          AudioDeviceNames* device_names) {
+  static const char kIoHintName[] = "IOID";
+  static const char kNameHintName[] = "NAME";
+  static const char kDescriptionHintName[] = "DESC";
+
+  const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type);
+
+  for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
+    // Only examine devices of the right type.  Valid values are
+    // "Input", "Output", and NULL which means both input and output.
+    std::unique_ptr<char, base::FreeDeleter> io(
+        wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName));
+    if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0)
+      continue;
+
+    // Found a device, prepend the default device since we always want
+    // it to be on the top of the list for all platforms. And there is
+    // no duplicate counting here since it is only done if the list is
+    // still empty.  Note, pulse has exclusively opened the default
+    // device, so we must open the device via the "default" moniker.
+    if (device_names->empty())
+      device_names->push_front(AudioDeviceName::CreateDefault());
+
+    // Get the unique device name for the device.
+    std::unique_ptr<char, base::FreeDeleter> unique_device_name(
+        wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
+
+    // Find out if the device is available.
+    if (IsAlsaDeviceAvailable(type, unique_device_name.get())) {
+      // Get the description for the device.
+      std::unique_ptr<char, base::FreeDeleter> desc(
+          wrapper_->DeviceNameGetHint(*hint_iter, kDescriptionHintName));
+
+      AudioDeviceName name;
+      name.unique_id = unique_device_name.get();
+      if (desc) {
+        // Use the more user friendly description as name.
+        // Replace '\n' with '-'.
+        char* pret = strchr(desc.get(), '\n');
+        if (pret)
+          *pret = '-';
+        name.device_name = desc.get();
+      } else {
+        // Virtual devices don't necessarily have descriptions.
+        // Use their names instead.
+        name.device_name = unique_device_name.get();
+      }
+
+      // Store the device information.
+      device_names->push_back(name);
+    }
+  }
+}
+
+// static
+bool AudioManagerAlsa::IsAlsaDeviceAvailable(
+    AudioManagerAlsa::StreamType type,
+    const char* device_name) {
+  if (!device_name)
+    return false;
+
+  // We do prefix matches on the device name to see whether to include
+  // it or not.
+  if (type == kStreamCapture) {
+    // Check if the device is in the list of invalid devices.
+    for (size_t i = 0; i < base::size(kInvalidAudioInputDevices); ++i) {
+      if (strncmp(kInvalidAudioInputDevices[i], device_name,
+                  strlen(kInvalidAudioInputDevices[i])) == 0)
+        return false;
+    }
+    return true;
+  }
+
+  DCHECK_EQ(kStreamPlayback, type);
+  // We prefer the device type that maps straight to hardware but
+  // goes through software conversion if needed (e.g. incompatible
+  // sample rate).
+  // TODO(joi): Should we prefer "hw" instead?
+  static const char kDeviceTypeDesired[] = "plughw";
+  return strncmp(kDeviceTypeDesired, device_name,
+                 base::size(kDeviceTypeDesired) - 1) == 0;
+}
+
+// static
+const char* AudioManagerAlsa::UnwantedDeviceTypeWhenEnumerating(
+    AudioManagerAlsa::StreamType wanted_type) {
+  return wanted_type == kStreamPlayback ? "Input" : "Output";
+}
+
+bool AudioManagerAlsa::HasAnyAlsaAudioDevice(
+    AudioManagerAlsa::StreamType stream) {
+  static const char kPcmInterfaceName[] = "pcm";
+  static const char kIoHintName[] = "IOID";
+  void** hints = NULL;
+  bool has_device = false;
+  int card = -1;
+
+  // Loop through the sound cards.
+  // Don't use snd_device_name_hint(-1,..) since there is a access violation
+  // inside this ALSA API with libasound.so.2.0.0.
+  while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) {
+    int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
+    if (!error) {
+      for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
+        // Only examine devices that are |stream| capable.  Valid values are
+        // "Input", "Output", and NULL which means both input and output.
+        std::unique_ptr<char, base::FreeDeleter> io(
+            wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName));
+        const char* unwanted_type = UnwantedDeviceTypeWhenEnumerating(stream);
+        if (io != NULL && strcmp(unwanted_type, io.get()) == 0)
+          continue;  // Wrong type, skip the device.
+
+        // Found an input device.
+        has_device = true;
+        break;
+      }
+
+      // Destroy the hints now that we're done with it.
+      wrapper_->DeviceNameFreeHint(hints);
+      hints = NULL;
+    } else {
+      DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: "
+                    << wrapper_->StrError(error);
+    }
+  }
+
+  return has_device;
+}
+
+AudioOutputStream* AudioManagerAlsa::MakeLinearOutputStream(
+    const AudioParameters& params,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+  return MakeOutputStream(params);
+}
+
+AudioOutputStream* AudioManagerAlsa::MakeLowLatencyOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+  return MakeOutputStream(params);
+}
+
+AudioInputStream* AudioManagerAlsa::MakeLinearInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+  return MakeInputStream(params, device_id);
+}
+
+AudioInputStream* AudioManagerAlsa::MakeLowLatencyInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+  return MakeInputStream(params, device_id);
+}
+
+AudioParameters AudioManagerAlsa::GetPreferredOutputStreamParameters(
+    const std::string& output_device_id,
+    const AudioParameters& input_params) {
+  // TODO(tommi): Support |output_device_id|.
+  DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!";
+  static const int kDefaultOutputBufferSize = 2048;
+  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
+  int sample_rate = kDefaultSampleRate;
+  int buffer_size = kDefaultOutputBufferSize;
+  if (input_params.IsValid()) {
+    // Some clients, such as WebRTC, have a more limited use case and work
+    // acceptably with a smaller buffer size.  The check below allows clients
+    // which want to try a smaller buffer size on Linux to do so.
+    // TODO(dalecurtis): This should include bits per channel and channel layout
+    // eventually.
+    sample_rate = input_params.sample_rate();
+    channel_layout = input_params.channel_layout();
+    buffer_size = std::min(input_params.frames_per_buffer(), buffer_size);
+  }
+
+  int user_buffer_size = GetUserBufferSize();
+  if (user_buffer_size)
+    buffer_size = user_buffer_size;
+
+  return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
+                         sample_rate, buffer_size);
+}
+
+AudioOutputStream* AudioManagerAlsa::MakeOutputStream(
+    const AudioParameters& params) {
+  std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice;
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kAlsaOutputDevice)) {
+    device_name = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+        switches::kAlsaOutputDevice);
+  }
+  return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this);
+}
+
+AudioInputStream* AudioManagerAlsa::MakeInputStream(
+    const AudioParameters& params, const std::string& device_id) {
+  std::string device_name =
+      (device_id == AudioDeviceDescription::kDefaultDeviceId)
+          ? AlsaPcmInputStream::kAutoSelectDevice
+          : device_id;
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kAlsaInputDevice)) {
+    device_name = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+        switches::kAlsaInputDevice);
+  }
+
+  return new AlsaPcmInputStream(this, device_name, params, wrapper_.get());
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/alsa/audio_manager_alsa.h b/third_party/chromium/media/audio/alsa/audio_manager_alsa.h
new file mode 100644
index 0000000..8b15c0a
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/audio_manager_alsa.h
@@ -0,0 +1,99 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ALSA_AUDIO_MANAGER_ALSA_H_
+#define MEDIA_AUDIO_ALSA_AUDIO_MANAGER_ALSA_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread.h"
+#include "media/audio/audio_manager_base.h"
+
+namespace media {
+
+class AlsaWrapper;
+
+class MEDIA_EXPORT AudioManagerAlsa : public AudioManagerBase {
+ public:
+  AudioManagerAlsa(std::unique_ptr<AudioThread> audio_thread,
+                   AudioLogFactory* audio_log_factory);
+
+  AudioManagerAlsa(const AudioManagerAlsa&) = delete;
+  AudioManagerAlsa& operator=(const AudioManagerAlsa&) = delete;
+
+  ~AudioManagerAlsa() override;
+
+  // Implementation of AudioManager.
+  bool HasAudioOutputDevices() override;
+  bool HasAudioInputDevices() override;
+  void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override;
+  void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+  const char* GetName() override;
+
+  // Implementation of AudioManagerBase.
+  AudioOutputStream* MakeLinearOutputStream(
+      const AudioParameters& params,
+      const LogCallback& log_callback) override;
+  AudioOutputStream* MakeLowLatencyOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLinearInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLowLatencyInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+
+ protected:
+  AudioParameters GetPreferredOutputStreamParameters(
+      const std::string& output_device_id,
+      const AudioParameters& input_params) override;
+
+ private:
+  enum StreamType {
+    kStreamPlayback = 0,
+    kStreamCapture,
+  };
+
+  // Gets a list of available ALSA devices.
+  void GetAlsaAudioDevices(StreamType type, AudioDeviceNames* device_names);
+
+  // Gets the ALSA devices' names and ids that support streams of the
+  // given type.
+  void GetAlsaDevicesInfo(StreamType type,
+                          void** hint,
+                          AudioDeviceNames* device_names);
+
+  // Checks if the specific ALSA device is available.
+  static bool IsAlsaDeviceAvailable(StreamType type,
+                                    const char* device_name);
+
+  static const char* UnwantedDeviceTypeWhenEnumerating(
+      StreamType wanted_type);
+
+  // Returns true if a device is present for the given stream type.
+  bool HasAnyAlsaAudioDevice(StreamType stream);
+
+  // Called by MakeLinearOutputStream and MakeLowLatencyOutputStream.
+  AudioOutputStream* MakeOutputStream(const AudioParameters& params);
+
+  // Called by MakeLinearInputStream and MakeLowLatencyInputStream.
+  AudioInputStream* MakeInputStream(const AudioParameters& params,
+                                    const std::string& device_id);
+
+  std::unique_ptr<AlsaWrapper> wrapper_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ALSA_AUDIO_MANAGER_ALSA_H_
diff --git a/third_party/chromium/media/audio/alsa/mock_alsa_wrapper.cc b/third_party/chromium/media/audio/alsa/mock_alsa_wrapper.cc
new file mode 100644
index 0000000..fdb9573
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/mock_alsa_wrapper.cc
@@ -0,0 +1,13 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/alsa/mock_alsa_wrapper.h"
+
+namespace media {
+
+MockAlsaWrapper::MockAlsaWrapper() {}
+
+MockAlsaWrapper::~MockAlsaWrapper() = default;
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/alsa/mock_alsa_wrapper.h b/third_party/chromium/media/audio/alsa/mock_alsa_wrapper.h
new file mode 100644
index 0000000..91371ef
--- /dev/null
+++ b/third_party/chromium/media/audio/alsa/mock_alsa_wrapper.h
@@ -0,0 +1,191 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ALSA_MOCK_ALSA_WRAPPER_H_
+#define MEDIA_AUDIO_ALSA_MOCK_ALSA_WRAPPER_H_
+
+#include "base/macros.h"
+#include "media/audio/alsa/alsa_wrapper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+
+class MockAlsaWrapper : public AlsaWrapper {
+ public:
+  MockAlsaWrapper();
+
+  MockAlsaWrapper(const MockAlsaWrapper&) = delete;
+  MockAlsaWrapper& operator=(const MockAlsaWrapper&) = delete;
+
+  ~MockAlsaWrapper() override;
+
+  MOCK_METHOD3(DeviceNameHint, int(int card, const char* iface, void*** hints));
+  MOCK_METHOD2(DeviceNameGetHint, char*(const void* hint, const char* id));
+  MOCK_METHOD1(DeviceNameFreeHint, int(void** hints));
+  MOCK_METHOD1(CardNext, int(int* rcard));
+  MOCK_METHOD4(PcmOpen,
+               int(snd_pcm_t** handle,
+                   const char* name,
+                   snd_pcm_stream_t stream,
+                   int mode));
+  MOCK_METHOD1(PcmClose, int(snd_pcm_t* handle));
+  MOCK_METHOD1(PcmPrepare, int(snd_pcm_t* handle));
+  MOCK_METHOD1(PcmDrain, int(snd_pcm_t* handle));
+  MOCK_METHOD1(PcmDrop, int(snd_pcm_t* handle));
+  MOCK_METHOD2(PcmDelay, int(snd_pcm_t* handle, snd_pcm_sframes_t* delay));
+  MOCK_METHOD1(PcmResume, int(snd_pcm_t* handle));
+  MOCK_METHOD3(PcmWritei,
+               snd_pcm_sframes_t(snd_pcm_t* handle,
+                                 const void* buffer,
+                                 snd_pcm_uframes_t size));
+  MOCK_METHOD3(PcmReadi,
+               snd_pcm_sframes_t(snd_pcm_t* handle,
+                                 void* buffer,
+                                 snd_pcm_uframes_t size));
+  MOCK_METHOD3(PcmRecover, int(snd_pcm_t* handle, int err, int silent));
+  MOCK_METHOD7(PcmSetParams,
+               int(snd_pcm_t* handle,
+                   snd_pcm_format_t format,
+                   snd_pcm_access_t access,
+                   unsigned int channels,
+                   unsigned int rate,
+                   int soft_resample,
+                   unsigned int latency));
+  MOCK_METHOD3(PcmGetParams,
+               int(snd_pcm_t* handle,
+                   snd_pcm_uframes_t* buffer_size,
+                   snd_pcm_uframes_t* period_size));
+  MOCK_METHOD1(PcmHwParamsMalloc, int(snd_pcm_hw_params_t** hw_params));
+  MOCK_METHOD2(PcmHwParamsAny,
+               int(snd_pcm_t* handle, snd_pcm_hw_params_t* hw_params));
+  MOCK_METHOD1(PcmHwParamsCanResume, int(snd_pcm_hw_params_t* hw_params));
+
+  MOCK_METHOD3(PcmHwParamsSetRateResample,
+               int(snd_pcm_t* handle,
+                   snd_pcm_hw_params_t* hw_params,
+                   unsigned int value));
+  MOCK_METHOD4(PcmHwParamsSetRateNear,
+               int(snd_pcm_t* handle,
+                   snd_pcm_hw_params_t* hw_params,
+                   unsigned int* rate,
+                   int* direction));
+  MOCK_METHOD3(PcmHwParamsTestFormat,
+               int(snd_pcm_t* handle,
+                   snd_pcm_hw_params_t* hw_params,
+                   snd_pcm_format_t format));
+  MOCK_METHOD2(PcmFormatSize, int(snd_pcm_format_t format, size_t samples));
+  MOCK_METHOD2(PcmHwParamsGetChannelsMin,
+               int(const snd_pcm_hw_params_t* hw_params,
+                   unsigned int* min_channels));
+  MOCK_METHOD2(PcmHwParamsGetChannelsMax,
+               int(const snd_pcm_hw_params_t* hw_params,
+                   unsigned int* max_channels));
+  MOCK_METHOD3(PcmHwParamsSetFormat,
+               int(snd_pcm_t* handle,
+                   snd_pcm_hw_params_t* hw_params,
+                   snd_pcm_format_t format));
+  MOCK_METHOD3(PcmHwParamsSetAccess,
+               int(snd_pcm_t* handle,
+                   snd_pcm_hw_params_t* hw_params,
+                   snd_pcm_access_t access));
+  MOCK_METHOD3(PcmHwParamsSetChannels,
+               int(snd_pcm_t* handle,
+                   snd_pcm_hw_params_t* hw_params,
+                   unsigned int channels));
+  MOCK_METHOD3(PcmHwParamsSetBufferSizeNear,
+               int(snd_pcm_t* handle,
+                   snd_pcm_hw_params_t* hw_params,
+                   snd_pcm_uframes_t* buffer_size));
+  MOCK_METHOD4(PcmHwParamsSetPeriodSizeNear,
+               int(snd_pcm_t* handle,
+                   snd_pcm_hw_params_t* hw_params,
+                   snd_pcm_uframes_t* period_size,
+                   int* direction));
+  MOCK_METHOD2(PcmHwParams,
+               int(snd_pcm_t* handle, snd_pcm_hw_params_t* hw_params));
+  MOCK_METHOD1(PcmHwParamsFree, void(snd_pcm_hw_params_t* hw_params));
+  MOCK_METHOD1(PcmSwParamsMalloc, int(snd_pcm_sw_params_t** sw_params));
+  MOCK_METHOD2(PcmSwParamsCurrent,
+               int(snd_pcm_t* handle, snd_pcm_sw_params_t* sw_params));
+  MOCK_METHOD3(PcmSwParamsSetStartThreshold,
+               int(snd_pcm_t* handle,
+                   snd_pcm_sw_params_t* sw_params,
+                   snd_pcm_uframes_t start_threshold));
+  MOCK_METHOD3(PcmSwParamsSetAvailMin,
+               int(snd_pcm_t* handle,
+                   snd_pcm_sw_params_t* sw_params,
+                   snd_pcm_uframes_t period_size));
+  MOCK_METHOD2(PcmSwParams,
+               int(snd_pcm_t* handle, snd_pcm_sw_params_t* sw_params));
+  MOCK_METHOD1(PcmSwParamsFree, void(snd_pcm_sw_params_t* sw_params));
+  MOCK_METHOD1(PcmName, const char*(snd_pcm_t* handle));
+  MOCK_METHOD1(PcmAvailUpdate, snd_pcm_sframes_t(snd_pcm_t* handle));
+  MOCK_METHOD1(PcmState, snd_pcm_state_t(snd_pcm_t* handle));
+  MOCK_METHOD1(PcmStart, int(snd_pcm_t* handle));
+  MOCK_METHOD2(MixerOpen, int(snd_mixer_t** mixer, int mode));
+  MOCK_METHOD2(MixerAttach, int(snd_mixer_t* mixer, const char* name));
+  MOCK_METHOD3(MixerElementRegister,
+               int(snd_mixer_t* mixer,
+                   struct snd_mixer_selem_regopt* options,
+                   snd_mixer_class_t** classp));
+  MOCK_METHOD1(MixerFree, void(snd_mixer_t* mixer));
+  MOCK_METHOD2(MixerDetach, int(snd_mixer_t* mixer, const char* name));
+  MOCK_METHOD1(MixerClose, int(snd_mixer_t* mixer));
+  MOCK_METHOD1(MixerLoad, int(snd_mixer_t* mixer));
+  MOCK_METHOD1(MixerFirstElem, snd_mixer_elem_t*(snd_mixer_t* mixer));
+  MOCK_METHOD1(MixerNextElem, snd_mixer_elem_t*(snd_mixer_elem_t* elem));
+  MOCK_METHOD1(MixerSelemIsActive, int(snd_mixer_elem_t* elem));
+  MOCK_METHOD1(MixerSelemName, const char*(snd_mixer_elem_t* elem));
+  MOCK_METHOD2(MixerSelemSetCaptureVolumeAll,
+               int(snd_mixer_elem_t* elem, long value));
+  MOCK_METHOD3(MixerSelemGetCaptureVolume,
+               int(snd_mixer_elem_t* elem,
+                   snd_mixer_selem_channel_id_t channel,
+                   long* value));
+  MOCK_METHOD1(MixerSelemHasCaptureVolume, int(snd_mixer_elem_t* elem));
+  MOCK_METHOD3(MixerSelemGetCaptureVolumeRange,
+               int(snd_mixer_elem_t* elem, long* min, long* max));
+  MOCK_METHOD1(MixerElemGetCallbackPrivate, void*(const snd_mixer_elem_t* obj));
+  MOCK_METHOD2(MixerElemSetCallback,
+               void(snd_mixer_elem_t* obj, snd_mixer_elem_callback_t val));
+  MOCK_METHOD2(MixerElemSetCallbackPrivate,
+               void(snd_mixer_elem_t* obj, void* val));
+  MOCK_METHOD2(MixerFindSelem,
+               snd_mixer_elem_t*(snd_mixer_t* mixer,
+                                 const snd_mixer_selem_id_t* id));
+  MOCK_METHOD1(MixerHandleEvents, int(snd_mixer_t* mixer));
+  MOCK_METHOD3(MixerPollDescriptors,
+               int(snd_mixer_t* mixer,
+                   struct pollfd* pfds,
+                   unsigned int space));
+  MOCK_METHOD1(MixerPollDescriptorsCount, int(snd_mixer_t* mixer));
+  MOCK_METHOD3(MixerSelemGetPlaybackSwitch,
+               int(snd_mixer_elem_t* elem,
+                   snd_mixer_selem_channel_id_t channel,
+                   int* value));
+  MOCK_METHOD3(MixerSelemGetPlaybackVolume,
+               int(snd_mixer_elem_t* elem,
+                   snd_mixer_selem_channel_id_t channel,
+                   long* value));
+  MOCK_METHOD3(MixerSelemGetPlaybackVolumeRange,
+               int(snd_mixer_elem_t* elem, long* min, long* max));
+  MOCK_METHOD1(MixerSelemHasPlaybackSwitch, int(snd_mixer_elem_t* elem));
+  MOCK_METHOD2(MixerSelemIdSetIndex,
+               void(snd_mixer_selem_id_t* obj, unsigned int val));
+  MOCK_METHOD2(MixerSelemIdSetName,
+               void(snd_mixer_selem_id_t* obj, const char* val));
+  MOCK_METHOD3(MixerSelemSetPlaybackSwitch,
+               int(snd_mixer_elem_t* elem,
+                   snd_mixer_selem_channel_id_t channel,
+                   int value));
+  MOCK_METHOD2(MixerSelemSetPlaybackVolumeAll,
+               int(snd_mixer_elem_t* elem, long value));
+  MOCK_METHOD1(MixerSelemIdMalloc, int(snd_mixer_selem_id_t** ptr));
+  MOCK_METHOD1(MixerSelemIdFree, void(snd_mixer_selem_id_t* obj));
+  MOCK_METHOD1(StrError, const char*(int errnum));
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ALSA_MOCK_ALSA_WRAPPER_H_
diff --git a/third_party/chromium/media/audio/android/aaudio.sigs b/third_party/chromium/media/audio/android/aaudio.sigs
new file mode 100644
index 0000000..1d89b56
--- /dev/null
+++ b/third_party/chromium/media/audio/android/aaudio.sigs
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//------------------------------------------------
+// Functions from AAudio used in media code.
+//------------------------------------------------
+
+const char * AAudio_convertResultToText(aaudio_result_t returnCode);
+aaudio_result_t AAudio_createStreamBuilder(AAudioStreamBuilder** builder);
+void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder, int32_t deviceId);
+void AAudioStreamBuilder_setSampleRate(AAudioStreamBuilder* builder, int32_t sampleRate);
+void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder, int32_t channelCount);
+void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder, int32_t samplesPerFrame);
+void AAudioStreamBuilder_setFormat(AAudioStreamBuilder* builder, aaudio_format_t format);
+void AAudioStreamBuilder_setDirection(AAudioStreamBuilder* builder, aaudio_direction_t direction);
+void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilder* builder, int32_t numFrames);
+void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* builder, aaudio_performance_mode_t mode);
+void AAudioStreamBuilder_setFramesPerDataCallback(AAudioStreamBuilder* builder, int32_t numFrames);
+void AAudioStreamBuilder_setUsage(AAudioStreamBuilder* builder, aaudio_usage_t usage);
+void AAudioStreamBuilder_setDataCallback(AAudioStreamBuilder* builder, AAudioStream_dataCallback callback, void *userData);
+void AAudioStreamBuilder_setErrorCallback(AAudioStreamBuilder* builder, AAudioStream_errorCallback callback, void *userData);
+aaudio_result_t AAudioStreamBuilder_openStream(AAudioStreamBuilder* builder, AAudioStream** stream);
+aaudio_result_t AAudioStreamBuilder_delete(AAudioStreamBuilder* builder);
+aaudio_result_t AAudioStream_close(AAudioStream* stream);
+aaudio_result_t AAudioStream_requestStart(AAudioStream* stream);
+aaudio_result_t AAudioStream_requestStop(AAudioStream* stream);
+aaudio_result_t AAudioStream_getTimestamp(AAudioStream* stream, clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds);
+aaudio_result_t AAudioStream_setBufferSizeInFrames(AAudioStream* stream, int32_t numFrames);
+int32_t AAudioStream_getFramesPerBurst(AAudioStream* stream);
+int64_t AAudioStream_getFramesWritten(AAudioStream* stream);
+aaudio_result_t AAudioStream_waitForStateChange(AAudioStream* stream, aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds);
\ No newline at end of file
diff --git a/third_party/chromium/media/audio/android/aaudio_output.cc b/third_party/chromium/media/audio/android/aaudio_output.cc
new file mode 100644
index 0000000..d06a6b8
--- /dev/null
+++ b/third_party/chromium/media/audio/android/aaudio_output.cc
@@ -0,0 +1,367 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/android/aaudio_output.h"
+
+#include "base/android/build_info.h"
+#include "base/logging.h"
+#include "base/thread_annotations.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "media/audio/android/aaudio_stubs.h"
+#include "media/audio/android/audio_manager_android.h"
+#include "media/base/audio_bus.h"
+
+namespace media {
+
+// Used to circumvent issues where the AAudio thread callbacks continue
+// after AAudioStream_requestStop() completes. See crbug.com/1183255.
+class LOCKABLE AAudioDestructionHelper {
+ public:
+  explicit AAudioDestructionHelper(AAudioOutputStream* stream)
+      : output_stream_(stream) {}
+
+  ~AAudioDestructionHelper() {
+    DCHECK(is_closing_);
+    if (aaudio_stream_)
+      AAudioStream_close(aaudio_stream_);
+  }
+
+  AAudioOutputStream* GetAndLockStream() EXCLUSIVE_LOCK_FUNCTION() {
+    lock_.Acquire();
+    return is_closing_ ? nullptr : output_stream_;
+  }
+
+  void UnlockStream() UNLOCK_FUNCTION() { lock_.Release(); }
+
+  void DeferStreamClosure(AAudioStream* stream) {
+    base::AutoLock al(lock_);
+    DCHECK(!is_closing_);
+
+    is_closing_ = true;
+    aaudio_stream_ = stream;
+  }
+
+ private:
+  base::Lock lock_;
+  AAudioOutputStream* output_stream_ GUARDED_BY(lock_) = nullptr;
+  AAudioStream* aaudio_stream_ GUARDED_BY(lock_) = nullptr;
+  bool is_closing_ GUARDED_BY(lock_) = false;
+};
+
+static aaudio_data_callback_result_t OnAudioDataRequestedCallback(
+    AAudioStream* stream,
+    void* user_data,
+    void* audio_data,
+    int32_t num_frames) {
+  AAudioDestructionHelper* destruction_helper =
+      reinterpret_cast<AAudioDestructionHelper*>(user_data);
+
+  AAudioOutputStream* output_stream = destruction_helper->GetAndLockStream();
+
+  aaudio_data_callback_result_t result = AAUDIO_CALLBACK_RESULT_STOP;
+  if (output_stream)
+    result = output_stream->OnAudioDataRequested(audio_data, num_frames);
+
+  destruction_helper->UnlockStream();
+
+  return result;
+}
+
+static void OnStreamErrorCallback(AAudioStream* stream,
+                                  void* user_data,
+                                  aaudio_result_t error) {
+  AAudioDestructionHelper* destruction_helper =
+      reinterpret_cast<AAudioDestructionHelper*>(user_data);
+
+  AAudioOutputStream* output_stream = destruction_helper->GetAndLockStream();
+
+  if (output_stream)
+    output_stream->OnStreamError(error);
+
+  destruction_helper->UnlockStream();
+}
+
+AAudioOutputStream::AAudioOutputStream(AudioManagerAndroid* manager,
+                                       const AudioParameters& params,
+                                       aaudio_usage_t usage)
+    : audio_manager_(manager),
+      params_(params),
+      usage_(usage),
+      performance_mode_(AAUDIO_PERFORMANCE_MODE_NONE),
+      ns_per_frame_(base::Time::kNanosecondsPerSecond /
+                    static_cast<double>(params.sample_rate())),
+      destruction_helper_(std::make_unique<AAudioDestructionHelper>(this)) {
+  DCHECK(manager);
+  DCHECK(params.IsValid());
+
+  if (AudioManagerAndroid::SupportsPerformanceModeForOutput()) {
+    switch (params.latency_tag()) {
+      case AudioLatency::LATENCY_EXACT_MS:
+      case AudioLatency::LATENCY_INTERACTIVE:
+      case AudioLatency::LATENCY_RTC:
+        performance_mode_ = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
+        break;
+      case AudioLatency::LATENCY_PLAYBACK:
+        performance_mode_ = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
+        break;
+      default:
+        performance_mode_ = AAUDIO_PERFORMANCE_MODE_NONE;
+    }
+  }
+
+  TRACE_EVENT2("audio", "AAudioOutputStream::AAudioOutputStream",
+               "AAUDIO_PERFORMANCE_MODE_LOW_LATENCY",
+               performance_mode_ == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
+                   ? "true" : "false",
+               "frames_per_buffer", params.frames_per_buffer());
+}
+
+AAudioOutputStream::~AAudioOutputStream() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (base::android::SdkVersion::SDK_VERSION_S >=
+      base::android::BuildInfo::GetInstance()->sdk_int()) {
+    // On Android S+, |destruction_helper_| can be destroyed as part of the
+    // normal class teardown.
+    return;
+  }
+
+  // In R and earlier, it is possible for callbacks to still be running even
+  // after calling AAudioStream_close(). The code below is a mitigation to work
+  // around this issue. See crbug.com/1183255.
+
+  // Keep |destruction_helper_| alive longer than |this|, so the |user_data|
+  // bound to the callback stays valid, until the callbacks stop.
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce([](std::unique_ptr<AAudioDestructionHelper>) {},
+                     std::move(destruction_helper_)),
+      base::Seconds(1));
+}
+
+void AAudioOutputStream::Flush() {}
+
+bool AAudioOutputStream::Open() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  AAudioStreamBuilder* builder;
+  auto result = AAudio_createStreamBuilder(&builder);
+  if (AAUDIO_OK != result)
+    return false;
+
+  // Parameters
+  AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
+  AAudioStreamBuilder_setSampleRate(builder, params_.sample_rate());
+  AAudioStreamBuilder_setChannelCount(builder, params_.channels());
+  AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+  AAudioStreamBuilder_setUsage(builder, usage_);
+  AAudioStreamBuilder_setPerformanceMode(builder, performance_mode_);
+  AAudioStreamBuilder_setFramesPerDataCallback(builder,
+                                               params_.frames_per_buffer());
+
+  // Callbacks
+  AAudioStreamBuilder_setDataCallback(builder, OnAudioDataRequestedCallback,
+                                      destruction_helper_.get());
+  AAudioStreamBuilder_setErrorCallback(builder, OnStreamErrorCallback,
+                                       destruction_helper_.get());
+
+  result = AAudioStreamBuilder_openStream(builder, &aaudio_stream_);
+
+  AAudioStreamBuilder_delete(builder);
+
+  if (AAUDIO_OK != result)
+    return false;
+
+  // After opening the stream, sets the effective buffer size to 3X the burst
+  // size to prevent glitching if the burst is small (e.g. < 128). On some
+  // devices you can get by with 1X or 2X, but 3X is safer.
+  int32_t framesPerBurst = AAudioStream_getFramesPerBurst(aaudio_stream_);
+  int32_t sizeRequested = framesPerBurst * (framesPerBurst < 128 ? 3 : 2);
+  AAudioStream_setBufferSizeInFrames(aaudio_stream_, sizeRequested);
+
+  audio_bus_ = AudioBus::Create(params_);
+
+  TRACE_EVENT2("audio", "AAudioOutputStream::Open",
+               "params_", params_.AsHumanReadableString(),
+               "requested BufferSizeInFrames", sizeRequested);
+
+  return true;
+}
+
+void AAudioOutputStream::Close() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  Stop();
+
+  // |destruction_helper_->GetStreamAndLock()| will return nullptr after this.
+  destruction_helper_->DeferStreamClosure(aaudio_stream_);
+
+  // We shouldn't be acessing |aaudio_stream_| after it's stopped.
+  aaudio_stream_ = nullptr;
+
+  // Note: This must be last, it will delete |this|.
+  audio_manager_->ReleaseOutputStream(this);
+}
+
+void AAudioOutputStream::Start(AudioSourceCallback* callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(aaudio_stream_);
+
+  {
+    base::AutoLock al(lock_);
+
+    // The device might have been disconnected between Open() and Start().
+    if (device_changed_) {
+      callback->OnError(AudioSourceCallback::ErrorType::kDeviceChange);
+      return;
+    }
+
+    DCHECK(!callback_);
+    callback_ = callback;
+  }
+
+  auto result = AAudioStream_requestStart(aaudio_stream_);
+  if (result != AAUDIO_OK) {
+    DLOG(ERROR) << "Failed to start audio stream, result: "
+                << AAudio_convertResultToText(result);
+
+    // Lock is required in case a previous asynchronous requestStop() still has
+    // not completed by the time we reach this point.
+    base::AutoLock al(lock_);
+    callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+    callback_ = nullptr;
+  }
+}
+
+void AAudioOutputStream::Stop() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  {
+    base::AutoLock al(lock_);
+    if (!callback_ || !aaudio_stream_)
+      return;
+  }
+
+  // Note: This call may be asynchronous, so we must clear |callback_| under
+  // lock below to ensure no further calls occur after Stop(). Since it may
+  // not always be asynchronous, we don't hold |lock_| while we call stop.
+  auto result = AAudioStream_requestStop(aaudio_stream_);
+
+  {
+    base::AutoLock al(lock_);
+    if (result != AAUDIO_OK) {
+      DLOG(ERROR) << "Failed to stop audio stream, result: "
+                  << AAudio_convertResultToText(result);
+      callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+    }
+
+    callback_ = nullptr;
+  }
+
+  // Wait for AAUDIO_STREAM_STATE_STOPPED, but do not explicitly check for the
+  // success of this wait.
+  aaudio_stream_state_t current_state = AAUDIO_STREAM_STATE_STOPPING;
+  aaudio_stream_state_t next_state = AAUDIO_STREAM_STATE_UNINITIALIZED;
+  static const int64_t kTimeoutNanoseconds = 1e8;
+  result = AAudioStream_waitForStateChange(aaudio_stream_, current_state,
+                                           &next_state, kTimeoutNanoseconds);
+}
+
+base::TimeDelta AAudioOutputStream::GetDelay(base::TimeTicks delay_timestamp) {
+  // Get the time that a known audio frame was presented for playing.
+  int64_t existing_frame_index;
+  int64_t existing_frame_pts;
+  auto result =
+      AAudioStream_getTimestamp(aaudio_stream_, CLOCK_MONOTONIC,
+                                &existing_frame_index, &existing_frame_pts);
+
+  if (result != AAUDIO_OK) {
+    DLOG(ERROR) << "Failed to get audio latency, result: "
+                << AAudio_convertResultToText(result);
+    return base::TimeDelta();
+  }
+
+  // Calculate the number of frames between our known frame and the write index.
+  const int64_t frame_index_delta =
+      AAudioStream_getFramesWritten(aaudio_stream_) - existing_frame_index;
+
+  // Calculate the time which the next frame will be presented.
+  const base::TimeDelta next_frame_pts =
+      base::Nanoseconds(existing_frame_pts + frame_index_delta * ns_per_frame_);
+
+  // Calculate the latency between write time and presentation time. At startup
+  // we may end up with negative values here.
+  return std::max(base::TimeDelta(),
+                  next_frame_pts - (delay_timestamp - base::TimeTicks()));
+}
+
+aaudio_data_callback_result_t AAudioOutputStream::OnAudioDataRequested(
+    void* audio_data,
+    int32_t num_frames) {
+  // TODO(tguilbert): This can be downgraded to a DCHECK after we've launched.
+  CHECK_EQ(num_frames, audio_bus_->frames());
+
+  base::AutoLock al(lock_);
+  if (!callback_)
+    return AAUDIO_CALLBACK_RESULT_STOP;
+
+  const base::TimeTicks delay_timestamp = base::TimeTicks::Now();
+  const base::TimeDelta delay = GetDelay(delay_timestamp);
+
+  const int frames_filled =
+      callback_->OnMoreData(delay, delay_timestamp, 0, audio_bus_.get());
+
+  audio_bus_->Scale(muted_ ? 0.0 : volume_);
+  audio_bus_->ToInterleaved<Float32SampleTypeTraits>(
+      frames_filled, reinterpret_cast<float*>(audio_data));
+  return AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+void AAudioOutputStream::OnStreamError(aaudio_result_t error) {
+  base::AutoLock al(lock_);
+
+  if (error == AAUDIO_ERROR_DISCONNECTED)
+    device_changed_ = true;
+
+  if (!callback_)
+    return;
+
+  if (device_changed_) {
+    callback_->OnError(AudioSourceCallback::ErrorType::kDeviceChange);
+    return;
+  }
+
+  // TODO(dalecurtis): Consider sending a translated |error| code.
+  callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+}
+
+void AAudioOutputStream::SetVolume(double volume) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  double volume_override = 0;
+  if (audio_manager_->HasOutputVolumeOverride(&volume_override))
+    volume = volume_override;
+
+  if (volume < 0.0 || volume > 1.0)
+    return;
+
+  base::AutoLock al(lock_);
+  volume_ = volume;
+}
+
+void AAudioOutputStream::GetVolume(double* volume) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  base::AutoLock al(lock_);
+  *volume = volume_;
+}
+
+void AAudioOutputStream::SetMute(bool muted) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  base::AutoLock al(lock_);
+  muted_ = muted;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/android/aaudio_output.h b/third_party/chromium/media/audio/android/aaudio_output.h
new file mode 100644
index 0000000..9576ab3
--- /dev/null
+++ b/third_party/chromium/media/audio/android/aaudio_output.h
@@ -0,0 +1,84 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ANDROID_AAUDIO_OUTPUT_H_
+#define MEDIA_AUDIO_ANDROID_AAUDIO_OUTPUT_H_
+
+#include <aaudio/AAudio.h>
+
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/android/muteable_audio_output_stream.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AAudioDestructionHelper;
+class AudioManagerAndroid;
+
+class AAudioOutputStream : public MuteableAudioOutputStream {
+ public:
+  AAudioOutputStream(AudioManagerAndroid* manager,
+                     const AudioParameters& params,
+                     aaudio_usage_t usage);
+
+  AAudioOutputStream(const AAudioOutputStream&) = delete;
+  AAudioOutputStream& operator=(const AAudioOutputStream&) = delete;
+
+  ~AAudioOutputStream() override;
+
+  // Implementation of MuteableAudioOutputStream.
+  bool Open() override;
+  void Close() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void Flush() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+  void SetMute(bool muted) override;
+
+  // Public callbacks.
+  aaudio_data_callback_result_t OnAudioDataRequested(void* audio_data,
+                                                     int32_t num_frames);
+  void OnStreamError(aaudio_result_t error);
+
+ private:
+  // Returns the amount of unplayed audio relative to |delay_timestamp|. See the
+  // definition for AudioOutputStream::AudioSourceCallback::OnMoreData() for
+  // more information on these terms.
+  base::TimeDelta GetDelay(base::TimeTicks delay_timestamp);
+
+  THREAD_CHECKER(thread_checker_);
+
+  AudioManagerAndroid* const audio_manager_;
+  const AudioParameters params_;
+
+  aaudio_usage_t usage_;
+  aaudio_performance_mode_t performance_mode_;
+
+  // Constant used for calculating latency. Amount of nanoseconds per frame.
+  const double ns_per_frame_;
+
+  std::unique_ptr<AudioBus> audio_bus_;
+
+  AAudioStream* aaudio_stream_ = nullptr;
+
+  // Bound to the audio data callback. Outlives |this| in case the callbacks
+  // continue after |this| is destroyed. See crbug.com/1183255.
+  std::unique_ptr<AAudioDestructionHelper> destruction_helper_;
+
+  // Lock protects all members below which may be read concurrently from the
+  // audio manager thread and the OS provided audio thread.
+  base::Lock lock_;
+
+  AudioSourceCallback* callback_ GUARDED_BY(lock_) = nullptr;
+  bool muted_ GUARDED_BY(lock_) = false;
+  double volume_ GUARDED_BY(lock_) = 1.0;
+  bool device_changed_ GUARDED_BY(lock_) = false;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ANDROID_AAUDIO_OUTPUT_H_
diff --git a/third_party/chromium/media/audio/android/aaudio_stub_header.fragment b/third_party/chromium/media/audio/android/aaudio_stub_header.fragment
new file mode 100644
index 0000000..01fce79
--- /dev/null
+++ b/third_party/chromium/media/audio/android/aaudio_stub_header.fragment
@@ -0,0 +1,8 @@
+// The extra include header needed in the generated stub file for defining
+// various AAudio types.
+
+extern "C" {
+
+#include <aaudio/AAudio.h>
+
+}
diff --git a/third_party/chromium/media/audio/android/audio_android_unittest.cc b/third_party/chromium/media/audio/android/audio_android_unittest.cc
new file mode 100644
index 0000000..f95b6a5
--- /dev/null
+++ b/third_party/chromium/media/audio/android/audio_android_unittest.cc
@@ -0,0 +1,938 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/android/build_info.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "media/audio/android/audio_manager_android.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_device_info_accessor_for_tests.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_unittest_util.h"
+#include "media/audio/mock_audio_source_callback.h"
+#include "media/audio/test_audio_thread.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/seekable_buffer.h"
+#include "media/base/test_data_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::NotNull;
+using ::testing::Return;
+
+namespace media {
+namespace {
+
+ACTION_P4(CheckCountAndPostQuitTask, count, limit, task_runner, quit_closure) {
+  if (++*count >= limit)
+    task_runner->PostTask(FROM_HERE, quit_closure);
+}
+
+const float kCallbackTestTimeMs = 2000.0;
+const int kBytesPerSample = 2;
+const SampleFormat kSampleFormat = kSampleFormatS16;
+
+// Converts AudioParameters::Format enumerator to readable string.
+std::string FormatToString(AudioParameters::Format format) {
+  switch (format) {
+    case AudioParameters::AUDIO_PCM_LINEAR:
+      return std::string("AUDIO_PCM_LINEAR");
+    case AudioParameters::AUDIO_PCM_LOW_LATENCY:
+      return std::string("AUDIO_PCM_LOW_LATENCY");
+    case AudioParameters::AUDIO_FAKE:
+      return std::string("AUDIO_FAKE");
+    default:
+      return std::string();
+  }
+}
+
+// Converts ChannelLayout enumerator to readable string. Does not include
+// multi-channel cases since these layouts are not supported on Android.
+std::string LayoutToString(ChannelLayout channel_layout) {
+  switch (channel_layout) {
+    case CHANNEL_LAYOUT_NONE:
+      return std::string("CHANNEL_LAYOUT_NONE");
+    case CHANNEL_LAYOUT_MONO:
+      return std::string("CHANNEL_LAYOUT_MONO");
+    case CHANNEL_LAYOUT_STEREO:
+      return std::string("CHANNEL_LAYOUT_STEREO");
+    case CHANNEL_LAYOUT_UNSUPPORTED:
+    default:
+      return std::string("CHANNEL_LAYOUT_UNSUPPORTED");
+  }
+}
+
+double ExpectedTimeBetweenCallbacks(AudioParameters params) {
+  return (base::Microseconds(params.frames_per_buffer() *
+                             base::Time::kMicrosecondsPerSecond /
+                             static_cast<double>(params.sample_rate())))
+      .InMillisecondsF();
+}
+
+// Helper method which verifies that the device list starts with a valid
+// default device name followed by non-default device names.
+void CheckDeviceDescriptions(
+    const AudioDeviceDescriptions& device_descriptions) {
+  DVLOG(2) << "Got " << device_descriptions.size() << " audio devices.";
+  if (device_descriptions.empty()) {
+    // Log a warning so we can see the status on the build bots.  No need to
+    // break the test though since this does successfully test the code and
+    // some failure cases.
+    LOG(WARNING) << "No input devices detected";
+    return;
+  }
+
+  AudioDeviceDescriptions::const_iterator it = device_descriptions.begin();
+
+  // The first device in the list should always be the default device.
+  EXPECT_EQ(std::string(AudioDeviceDescription::kDefaultDeviceId),
+            it->unique_id);
+  ++it;
+
+  // Other devices should have non-empty name and id and should not contain
+  // default name or id.
+  while (it != device_descriptions.end()) {
+    EXPECT_FALSE(it->device_name.empty());
+    EXPECT_FALSE(it->unique_id.empty());
+    EXPECT_FALSE(it->group_id.empty());
+    DVLOG(2) << "Device ID(" << it->unique_id << "), label: " << it->device_name
+             << " group: " << it->group_id;
+    EXPECT_NE(AudioDeviceDescription::GetDefaultDeviceName(), it->device_name);
+    EXPECT_NE(std::string(AudioDeviceDescription::kDefaultDeviceId),
+              it->unique_id);
+    ++it;
+  }
+}
+
+// We clear the data bus to ensure that the test does not cause noise.
+int RealOnMoreData(base::TimeDelta /* delay */,
+                   base::TimeTicks /* delay_timestamp */,
+                   int /* prior_frames_skipped */,
+                   AudioBus* dest) {
+  dest->Zero();
+  return dest->frames();
+}
+
+}  // namespace
+
+std::ostream& operator<<(std::ostream& os, const AudioParameters& params) {
+  using std::endl;
+  os << endl
+     << "format: " << FormatToString(params.format()) << endl
+     << "channel layout: " << LayoutToString(params.channel_layout()) << endl
+     << "sample rate: " << params.sample_rate() << endl
+     << "frames per buffer: " << params.frames_per_buffer() << endl
+     << "channels: " << params.channels() << endl
+     << "bytes per buffer: " << params.GetBytesPerBuffer(kSampleFormat) << endl
+     << "bytes per second: "
+     << params.sample_rate() * params.GetBytesPerFrame(kSampleFormat) << endl
+     << "bytes per frame: " << params.GetBytesPerFrame(kSampleFormat) << endl
+     << "chunk size in ms: " << ExpectedTimeBetweenCallbacks(params) << endl
+     << "echo_canceller: "
+     << (params.effects() & AudioParameters::ECHO_CANCELLER);
+  return os;
+}
+
+// Gmock implementation of AudioInputStream::AudioInputCallback.
+class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
+ public:
+  MOCK_METHOD3(OnData,
+               void(const AudioBus* src,
+                    base::TimeTicks capture_time,
+                    double volume));
+  MOCK_METHOD0(OnError, void());
+};
+
+// Implements AudioOutputStream::AudioSourceCallback and provides audio data
+// by reading from a data file.
+class FileAudioSource : public AudioOutputStream::AudioSourceCallback {
+ public:
+  explicit FileAudioSource(base::WaitableEvent* event, const std::string& name)
+      : event_(event), pos_(0) {
+    // Reads a test file from media/test/data directory and stores it in
+    // a DecoderBuffer.
+    file_ = ReadTestDataFile(name);
+
+    // Log the name of the file which is used as input for this test.
+    base::FilePath file_path = GetTestDataFilePath(name);
+    DVLOG(0) << "Reading from file: " << file_path.value().c_str();
+  }
+
+  FileAudioSource(const FileAudioSource&) = delete;
+  FileAudioSource& operator=(const FileAudioSource&) = delete;
+
+  ~FileAudioSource() override {}
+
+  // AudioOutputStream::AudioSourceCallback implementation.
+
+  // Use samples read from a data file and fill up the audio buffer
+  // provided to us in the callback.
+  int OnMoreData(base::TimeDelta /* delay */,
+                 base::TimeTicks /* delay_timestamp */,
+                 int /* prior_frames_skipped */,
+                 AudioBus* dest) override {
+    bool stop_playing = false;
+    int max_size = dest->frames() * dest->channels() * kBytesPerSample;
+
+    // Adjust data size and prepare for end signal if file has ended.
+    if (pos_ + max_size > file_size()) {
+      stop_playing = true;
+      max_size = file_size() - pos_;
+    }
+
+    // File data is stored as interleaved 16-bit values. Copy data samples from
+    // the file and deinterleave to match the audio bus format.
+    // FromInterleaved() will zero out any unfilled frames when there is not
+    // sufficient data remaining in the file to fill up the complete frame.
+    int frames = max_size / (dest->channels() * kBytesPerSample);
+    if (max_size) {
+      auto* source = reinterpret_cast<const int16_t*>(file_->data() + pos_);
+      dest->FromInterleaved<SignedInt16SampleTypeTraits>(source, frames);
+      pos_ += max_size;
+    }
+
+    // Set event to ensure that the test can stop when the file has ended.
+    if (stop_playing)
+      event_->Signal();
+
+    return frames;
+  }
+
+  void OnError(ErrorType type) override {}
+
+  int file_size() { return file_->data_size(); }
+
+ private:
+  base::WaitableEvent* event_;
+  int pos_;
+  scoped_refptr<DecoderBuffer> file_;
+};
+
+// Implements AudioInputStream::AudioInputCallback and writes the recorded
+// audio data to a local output file. Note that this implementation should
+// only be used for manually invoked and evaluated tests, hence the created
+// file will not be destroyed after the test is done since the intention is
+// that it shall be available for off-line analysis.
+class FileAudioSink : public AudioInputStream::AudioInputCallback {
+ public:
+  explicit FileAudioSink(base::WaitableEvent* event,
+                         const AudioParameters& params,
+                         const std::string& file_name)
+      : event_(event), params_(params) {
+    // Allocate space for ~10 seconds of data.
+    const int kMaxBufferSize =
+        10 * params.sample_rate() * params.GetBytesPerFrame(kSampleFormat);
+    buffer_ = std::make_unique<media::SeekableBuffer>(0, kMaxBufferSize);
+
+    // Open up the binary file which will be written to in the destructor.
+    base::FilePath file_path;
+    EXPECT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
+    file_path = file_path.AppendASCII(file_name.c_str());
+    binary_file_ = base::OpenFile(file_path, "wb");
+    DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
+    DVLOG(0) << "Writing to file: " << file_path.value().c_str();
+  }
+
+  FileAudioSink(const FileAudioSink&) = delete;
+  FileAudioSink& operator=(const FileAudioSink&) = delete;
+
+  ~FileAudioSink() override {
+    int bytes_written = 0;
+    while (bytes_written < buffer_->forward_capacity()) {
+      const uint8_t* chunk;
+      int chunk_size;
+
+      // Stop writing if no more data is available.
+      if (!buffer_->GetCurrentChunk(&chunk, &chunk_size))
+        break;
+
+      // Write recorded data chunk to the file and prepare for next chunk.
+      // TODO(henrika): use file_util:: instead.
+      fwrite(chunk, 1, chunk_size, binary_file_);
+      buffer_->Seek(chunk_size);
+      bytes_written += chunk_size;
+    }
+    base::CloseFile(binary_file_);
+  }
+
+  // AudioInputStream::AudioInputCallback implementation.
+  void OnData(const AudioBus* src,
+              base::TimeTicks capture_time,
+              double volume) override {
+    const int num_samples = src->frames() * src->channels();
+    std::unique_ptr<int16_t> interleaved(new int16_t[num_samples]);
+    src->ToInterleaved<SignedInt16SampleTypeTraits>(src->frames(),
+                                                    interleaved.get());
+
+    // Store data data in a temporary buffer to avoid making blocking
+    // fwrite() calls in the audio callback. The complete buffer will be
+    // written to file in the destructor.
+    const int bytes_per_sample = sizeof(*interleaved);
+    const int size = bytes_per_sample * num_samples;
+    if (!buffer_->Append((const uint8_t*)interleaved.get(), size))
+      event_->Signal();
+  }
+
+  void OnError() override {}
+
+ private:
+  base::WaitableEvent* event_;
+  AudioParameters params_;
+  std::unique_ptr<media::SeekableBuffer> buffer_;
+  FILE* binary_file_;
+};
+
+// Implements AudioInputCallback and AudioSourceCallback to support full
+// duplex audio where captured samples are played out in loopback after
+// reading from a temporary FIFO storage.
+class FullDuplexAudioSinkSource
+    : public AudioInputStream::AudioInputCallback,
+      public AudioOutputStream::AudioSourceCallback {
+ public:
+  explicit FullDuplexAudioSinkSource(const AudioParameters& params)
+      : params_(params),
+        previous_time_(base::TimeTicks::Now()),
+        started_(false) {
+    // Start with a reasonably small FIFO size. It will be increased
+    // dynamically during the test if required.
+    size_t buffer_size = params.GetBytesPerBuffer(kSampleFormat);
+    fifo_ = std::make_unique<media::SeekableBuffer>(0, 2 * buffer_size);
+    buffer_.reset(new uint8_t[buffer_size]);
+  }
+
+  FullDuplexAudioSinkSource(const FullDuplexAudioSinkSource&) = delete;
+  FullDuplexAudioSinkSource& operator=(const FullDuplexAudioSinkSource&) =
+      delete;
+
+  ~FullDuplexAudioSinkSource() override {}
+
+  // AudioInputStream::AudioInputCallback implementation
+  void OnError() override {}
+  void OnData(const AudioBus* src,
+              base::TimeTicks capture_time,
+              double volume) override {
+    const base::TimeTicks now_time = base::TimeTicks::Now();
+    const int diff = (now_time - previous_time_).InMilliseconds();
+
+    const int num_samples = src->frames() * src->channels();
+    std::unique_ptr<int16_t> interleaved(new int16_t[num_samples]);
+    src->ToInterleaved<SignedInt16SampleTypeTraits>(src->frames(),
+                                                    interleaved.get());
+    const int bytes_per_sample = sizeof(*interleaved);
+    const int size = bytes_per_sample * num_samples;
+
+    base::AutoLock lock(lock_);
+    if (diff > 1000) {
+      started_ = true;
+      previous_time_ = now_time;
+
+      // Log out the extra delay added by the FIFO. This is a best effort
+      // estimate. We might be +- 10ms off here.
+      int extra_fifo_delay =
+          static_cast<int>(BytesToMilliseconds(fifo_->forward_bytes() + size));
+      DVLOG(1) << extra_fifo_delay;
+    }
+
+    // We add an initial delay of ~1 second before loopback starts to ensure
+    // a stable callback sequence and to avoid initial bursts which might add
+    // to the extra FIFO delay.
+    if (!started_)
+      return;
+
+    // Append new data to the FIFO and extend the size if the max capacity
+    // was exceeded. Flush the FIFO when extended just in case.
+    if (!fifo_->Append((const uint8_t*)interleaved.get(), size)) {
+      fifo_->set_forward_capacity(2 * fifo_->forward_capacity());
+      fifo_->Clear();
+    }
+  }
+
+  // AudioOutputStream::AudioSourceCallback implementation
+  void OnError(ErrorType type) override {}
+  int OnMoreData(base::TimeDelta /* delay */,
+                 base::TimeTicks /* delay_timestamp */,
+                 int /* prior_frames_skipped */,
+                 AudioBus* dest) override {
+    const int size_in_bytes =
+        kBytesPerSample * dest->frames() * dest->channels();
+    EXPECT_EQ(size_in_bytes, params_.GetBytesPerBuffer(kSampleFormat));
+
+    base::AutoLock lock(lock_);
+
+    // We add an initial delay of ~1 second before loopback starts to ensure
+    // a stable callback sequences and to avoid initial bursts which might add
+    // to the extra FIFO delay.
+    if (!started_) {
+      dest->Zero();
+      return dest->frames();
+    }
+
+    // Fill up destination with zeros if the FIFO does not contain enough
+    // data to fulfill the request.
+    if (fifo_->forward_bytes() < size_in_bytes) {
+      dest->Zero();
+    } else {
+      fifo_->Read(buffer_.get(), size_in_bytes);
+      dest->FromInterleaved<SignedInt16SampleTypeTraits>(
+          reinterpret_cast<int16_t*>(buffer_.get()), dest->frames());
+    }
+
+    return dest->frames();
+  }
+
+ private:
+  // Converts from bytes to milliseconds given number of bytes and existing
+  // audio parameters.
+  double BytesToMilliseconds(int bytes) const {
+    const int frames = bytes / params_.GetBytesPerFrame(kSampleFormat);
+    return (base::Microseconds(frames * base::Time::kMicrosecondsPerSecond /
+                               static_cast<double>(params_.sample_rate())))
+        .InMillisecondsF();
+  }
+
+  AudioParameters params_;
+  base::TimeTicks previous_time_;
+  base::Lock lock_;
+  std::unique_ptr<media::SeekableBuffer> fifo_;
+  std::unique_ptr<uint8_t[]> buffer_;
+  bool started_;
+};
+
+// Test fixture class for tests which only exercise the output path.
+class AudioAndroidOutputTest : public testing::Test {
+ public:
+  AudioAndroidOutputTest()
+      : task_environment_(
+            base::test::SingleThreadTaskEnvironment::MainThreadType::UI),
+        audio_manager_(AudioManager::CreateForTesting(
+            std::make_unique<TestAudioThread>())),
+        audio_manager_device_info_(audio_manager_.get()),
+        audio_output_stream_(nullptr) {
+    // Flush the message loop to ensure that AudioManager is fully initialized.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  AudioAndroidOutputTest(const AudioAndroidOutputTest&) = delete;
+  AudioAndroidOutputTest& operator=(const AudioAndroidOutputTest&) = delete;
+
+  ~AudioAndroidOutputTest() override {
+    audio_manager_->Shutdown();
+    base::RunLoop().RunUntilIdle();
+  }
+
+ protected:
+  AudioManager* audio_manager() { return audio_manager_.get(); }
+  AudioDeviceInfoAccessorForTests* audio_manager_device_info() {
+    return &audio_manager_device_info_;
+  }
+  const AudioParameters& audio_output_parameters() {
+    return audio_output_parameters_;
+  }
+
+  // Synchronously runs the provided callback/closure on the audio thread.
+  void RunOnAudioThread(base::OnceClosure closure) {
+    if (!audio_manager()->GetTaskRunner()->BelongsToCurrentThread()) {
+      base::WaitableEvent event(
+          base::WaitableEvent::ResetPolicy::AUTOMATIC,
+          base::WaitableEvent::InitialState::NOT_SIGNALED);
+      audio_manager()->GetTaskRunner()->PostTask(
+          FROM_HERE,
+          base::BindOnce(&AudioAndroidOutputTest::RunOnAudioThreadImpl,
+                         base::Unretained(this), std::move(closure), &event));
+      event.Wait();
+    } else {
+      std::move(closure).Run();
+    }
+  }
+
+  void RunOnAudioThreadImpl(base::OnceClosure closure,
+                            base::WaitableEvent* event) {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    std::move(closure).Run();
+    event->Signal();
+  }
+
+  void GetDefaultOutputStreamParametersOnAudioThread() {
+    RunOnAudioThread(base::BindOnce(
+        &AudioAndroidOutputTest::GetDefaultOutputStreamParameters,
+        base::Unretained(this)));
+  }
+
+  void MakeAudioOutputStreamOnAudioThread(const AudioParameters& params) {
+    RunOnAudioThread(base::BindOnce(&AudioAndroidOutputTest::MakeOutputStream,
+                                    base::Unretained(this), params));
+  }
+
+  void OpenAndCloseAudioOutputStreamOnAudioThread() {
+    RunOnAudioThread(base::BindOnce(&AudioAndroidOutputTest::OpenAndClose,
+                                    base::Unretained(this)));
+  }
+
+  void OpenAndStartAudioOutputStreamOnAudioThread(
+      AudioOutputStream::AudioSourceCallback* source) {
+    RunOnAudioThread(base::BindOnce(&AudioAndroidOutputTest::OpenAndStart,
+                                    base::Unretained(this), source));
+  }
+
+  void StopAndCloseAudioOutputStreamOnAudioThread() {
+    RunOnAudioThread(base::BindOnce(&AudioAndroidOutputTest::StopAndClose,
+                                    base::Unretained(this)));
+  }
+
+  double AverageTimeBetweenCallbacks(int num_callbacks) const {
+    return ((end_time_ - start_time_) / static_cast<double>(num_callbacks - 1))
+        .InMillisecondsF();
+  }
+
+  void StartOutputStreamCallbacks(const AudioParameters& params) {
+    double expected_time_between_callbacks_ms =
+        ExpectedTimeBetweenCallbacks(params);
+    const int num_callbacks =
+        (kCallbackTestTimeMs / expected_time_between_callbacks_ms);
+    MakeAudioOutputStreamOnAudioThread(params);
+
+    int count = 0;
+    MockAudioSourceCallback source;
+
+    base::RunLoop run_loop;
+    EXPECT_CALL(source, OnMoreData(_, _, 0, NotNull()))
+        .Times(AtLeast(num_callbacks))
+        .WillRepeatedly(
+            DoAll(CheckCountAndPostQuitTask(&count, num_callbacks,
+                                            base::ThreadTaskRunnerHandle::Get(),
+                                            run_loop.QuitWhenIdleClosure()),
+                  Invoke(RealOnMoreData)));
+    EXPECT_CALL(source, OnError(_)).Times(0);
+
+    OpenAndStartAudioOutputStreamOnAudioThread(&source);
+
+    start_time_ = base::TimeTicks::Now();
+    run_loop.Run();
+    end_time_ = base::TimeTicks::Now();
+
+    StopAndCloseAudioOutputStreamOnAudioThread();
+
+    double average_time_between_callbacks_ms =
+        AverageTimeBetweenCallbacks(num_callbacks);
+    DVLOG(0) << "expected time between callbacks: "
+             << expected_time_between_callbacks_ms << " ms";
+    DVLOG(0) << "average time between callbacks: "
+             << average_time_between_callbacks_ms << " ms";
+    EXPECT_GE(average_time_between_callbacks_ms,
+              0.70 * expected_time_between_callbacks_ms);
+    EXPECT_LE(average_time_between_callbacks_ms,
+              1.50 * expected_time_between_callbacks_ms);
+  }
+
+  void GetDefaultOutputStreamParameters() {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    audio_output_parameters_ =
+        audio_manager_device_info()->GetDefaultOutputStreamParameters();
+    EXPECT_TRUE(audio_output_parameters_.IsValid());
+  }
+
+  void MakeOutputStream(const AudioParameters& params) {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    audio_output_stream_ = audio_manager()->MakeAudioOutputStream(
+        params, std::string(), AudioManager::LogCallback());
+    EXPECT_TRUE(audio_output_stream_);
+  }
+
+  void OpenAndClose() {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    EXPECT_TRUE(audio_output_stream_->Open());
+    audio_output_stream_->Close();
+    audio_output_stream_ = nullptr;
+  }
+
+  void OpenAndStart(AudioOutputStream::AudioSourceCallback* source) {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    EXPECT_TRUE(audio_output_stream_->Open());
+    audio_output_stream_->Start(source);
+  }
+
+  void StopAndClose() {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    audio_output_stream_->Stop();
+    audio_output_stream_->Close();
+    audio_output_stream_ = nullptr;
+  }
+
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  std::unique_ptr<AudioManager> audio_manager_;
+  AudioDeviceInfoAccessorForTests audio_manager_device_info_;
+  AudioParameters audio_output_parameters_;
+  AudioOutputStream* audio_output_stream_;
+  base::TimeTicks start_time_;
+  base::TimeTicks end_time_;
+};
+
+// Test fixture class for tests which exercise the input path, or both input and
+// output paths. It is value-parameterized to test against both the Java
+// AudioRecord (when true) and native OpenSLES (when false) input paths.
+class AudioAndroidInputTest : public AudioAndroidOutputTest,
+                              public testing::WithParamInterface<bool> {
+ public:
+  AudioAndroidInputTest() : audio_input_stream_(nullptr) {}
+
+ protected:
+  const AudioParameters& audio_input_parameters() {
+    return audio_input_parameters_;
+  }
+
+  AudioParameters GetInputStreamParameters() {
+    GetDefaultInputStreamParametersOnAudioThread();
+
+    AudioParameters params = audio_input_parameters();
+
+    // Only the AudioRecord path supports effects, so we can force it to be
+    // selected for the test by requesting one. OpenSLES is used otherwise.
+    params.set_effects(GetParam() ? AudioParameters::ECHO_CANCELLER
+                                  : AudioParameters::NO_EFFECTS);
+    return params;
+  }
+
+  void GetDefaultInputStreamParametersOnAudioThread() {
+    RunOnAudioThread(
+        base::BindOnce(&AudioAndroidInputTest::GetDefaultInputStreamParameters,
+                       base::Unretained(this)));
+  }
+
+  void MakeAudioInputStreamOnAudioThread(const AudioParameters& params) {
+    RunOnAudioThread(base::BindOnce(&AudioAndroidInputTest::MakeInputStream,
+                                    base::Unretained(this), params));
+  }
+
+  void OpenAndCloseAudioInputStreamOnAudioThread() {
+    RunOnAudioThread(base::BindOnce(&AudioAndroidInputTest::OpenAndClose,
+                                    base::Unretained(this)));
+  }
+
+  void OpenAndStartAudioInputStreamOnAudioThread(
+      AudioInputStream::AudioInputCallback* sink) {
+    RunOnAudioThread(base::BindOnce(&AudioAndroidInputTest::OpenAndStart,
+                                    base::Unretained(this), sink));
+  }
+
+  void StopAndCloseAudioInputStreamOnAudioThread() {
+    RunOnAudioThread(base::BindOnce(&AudioAndroidInputTest::StopAndClose,
+                                    base::Unretained(this)));
+  }
+
+  void StartInputStreamCallbacks(const AudioParameters& params) {
+    double expected_time_between_callbacks_ms =
+        ExpectedTimeBetweenCallbacks(params);
+    const int num_callbacks =
+        (kCallbackTestTimeMs / expected_time_between_callbacks_ms);
+
+    MakeAudioInputStreamOnAudioThread(params);
+
+    int count = 0;
+    MockAudioInputCallback sink;
+
+    base::RunLoop run_loop;
+    EXPECT_CALL(sink, OnData(NotNull(), _, _))
+        .Times(AtLeast(num_callbacks))
+        .WillRepeatedly(CheckCountAndPostQuitTask(
+            &count, num_callbacks, base::ThreadTaskRunnerHandle::Get(),
+            run_loop.QuitWhenIdleClosure()));
+    EXPECT_CALL(sink, OnError()).Times(0);
+
+    OpenAndStartAudioInputStreamOnAudioThread(&sink);
+
+    start_time_ = base::TimeTicks::Now();
+    run_loop.Run();
+    end_time_ = base::TimeTicks::Now();
+
+    StopAndCloseAudioInputStreamOnAudioThread();
+
+    double average_time_between_callbacks_ms =
+        AverageTimeBetweenCallbacks(num_callbacks);
+    DVLOG(0) << "expected time between callbacks: "
+             << expected_time_between_callbacks_ms << " ms";
+    DVLOG(0) << "average time between callbacks: "
+             << average_time_between_callbacks_ms << " ms";
+    EXPECT_GE(average_time_between_callbacks_ms,
+              0.70 * expected_time_between_callbacks_ms);
+    EXPECT_LE(average_time_between_callbacks_ms,
+              1.30 * expected_time_between_callbacks_ms);
+  }
+
+  void GetDefaultInputStreamParameters() {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    audio_input_parameters_ =
+        audio_manager_device_info()->GetInputStreamParameters(
+            AudioDeviceDescription::kDefaultDeviceId);
+  }
+
+  void MakeInputStream(const AudioParameters& params) {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    audio_input_stream_ = audio_manager()->MakeAudioInputStream(
+        params, AudioDeviceDescription::kDefaultDeviceId,
+        AudioManager::LogCallback());
+    EXPECT_TRUE(audio_input_stream_);
+  }
+
+  void OpenAndClose() {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    EXPECT_EQ(audio_input_stream_->Open(),
+              AudioInputStream::OpenOutcome::kSuccess);
+    audio_input_stream_->Close();
+    audio_input_stream_ = nullptr;
+  }
+
+  void OpenAndStart(AudioInputStream::AudioInputCallback* sink) {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    EXPECT_EQ(audio_input_stream_->Open(),
+              AudioInputStream::OpenOutcome::kSuccess);
+    audio_input_stream_->Start(sink);
+  }
+
+  void StopAndClose() {
+    DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+    audio_input_stream_->Stop();
+    audio_input_stream_->Close();
+    audio_input_stream_ = nullptr;
+  }
+
+  AudioInputStream* audio_input_stream_;
+  AudioParameters audio_input_parameters_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AudioAndroidInputTest);
+};
+
+// Get the default audio input parameters and log the result.
+TEST_P(AudioAndroidInputTest, GetDefaultInputStreamParameters) {
+  // We don't go through AudioAndroidInputTest::GetInputStreamParameters() here
+  // so that we can log the real (non-overridden) values of the effects.
+  GetDefaultInputStreamParametersOnAudioThread();
+  EXPECT_TRUE(audio_input_parameters().IsValid());
+  DVLOG(1) << audio_input_parameters();
+}
+
+// Get the default audio output parameters and log the result.
+TEST_F(AudioAndroidOutputTest, GetDefaultOutputStreamParameters) {
+  GetDefaultOutputStreamParametersOnAudioThread();
+  DVLOG(1) << audio_output_parameters();
+}
+
+// Verify input device enumeration.
+TEST_F(AudioAndroidInputTest, GetAudioInputDeviceDescriptions) {
+  ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info()->HasAudioInputDevices());
+  AudioDeviceDescriptions devices;
+  RunOnAudioThread(base::BindOnce(
+      &AudioDeviceInfoAccessorForTests::GetAudioInputDeviceDescriptions,
+      base::Unretained(audio_manager_device_info()), &devices));
+  CheckDeviceDescriptions(devices);
+}
+
+// Verify output device enumeration.
+TEST_F(AudioAndroidOutputTest, GetAudioOutputDeviceDescriptions) {
+  ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info()->HasAudioOutputDevices());
+  AudioDeviceDescriptions devices;
+  RunOnAudioThread(base::BindOnce(
+      &AudioDeviceInfoAccessorForTests::GetAudioOutputDeviceDescriptions,
+      base::Unretained(audio_manager_device_info()), &devices));
+  CheckDeviceDescriptions(devices);
+}
+
+// Ensure that a default input stream can be created and closed.
+TEST_P(AudioAndroidInputTest, CreateAndCloseInputStream) {
+  AudioParameters params = GetInputStreamParameters();
+  MakeAudioInputStreamOnAudioThread(params);
+  RunOnAudioThread(base::BindOnce(&AudioInputStream::Close,
+                                  base::Unretained(audio_input_stream_)));
+}
+
+// Ensure that a default output stream can be created and closed.
+// TODO(henrika): should we also verify that this API changes the audio mode
+// to communication mode, and calls RegisterHeadsetReceiver, the first time
+// it is called?
+TEST_F(AudioAndroidOutputTest, CreateAndCloseOutputStream) {
+  GetDefaultOutputStreamParametersOnAudioThread();
+  MakeAudioOutputStreamOnAudioThread(audio_output_parameters());
+  RunOnAudioThread(base::BindOnce(&AudioOutputStream::Close,
+                                  base::Unretained(audio_output_stream_)));
+}
+
+// Ensure that a default input stream can be opened and closed.
+TEST_P(AudioAndroidInputTest, OpenAndCloseInputStream) {
+  AudioParameters params = GetInputStreamParameters();
+  MakeAudioInputStreamOnAudioThread(params);
+  OpenAndCloseAudioInputStreamOnAudioThread();
+}
+
+// Ensure that a default output stream can be opened and closed.
+TEST_F(AudioAndroidOutputTest, OpenAndCloseOutputStream) {
+  GetDefaultOutputStreamParametersOnAudioThread();
+  MakeAudioOutputStreamOnAudioThread(audio_output_parameters());
+  OpenAndCloseAudioOutputStreamOnAudioThread();
+}
+
+// Start input streaming using default input parameters and ensure that the
+// callback sequence is sane.
+// Flaky, see crbug.com/683408.
+TEST_P(AudioAndroidInputTest, DISABLED_StartInputStreamCallbacks) {
+  AudioParameters native_params = GetInputStreamParameters();
+  StartInputStreamCallbacks(native_params);
+}
+
+// Start input streaming using non default input parameters and ensure that the
+// callback sequence is sane. The only change we make in this test is to select
+// a 10ms buffer size instead of the default size.
+// Flaky, see crbug.com/683408.
+TEST_P(AudioAndroidInputTest,
+       DISABLED_StartInputStreamCallbacksNonDefaultParameters) {
+  AudioParameters params = GetInputStreamParameters();
+  params.set_frames_per_buffer(params.sample_rate() / 100);
+  StartInputStreamCallbacks(params);
+}
+
+// Start output streaming using default output parameters and ensure that the
+// callback sequence is sane.
+TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacks) {
+  GetDefaultOutputStreamParametersOnAudioThread();
+  StartOutputStreamCallbacks(audio_output_parameters());
+}
+
+// Start output streaming using non default output parameters and ensure that
+// the callback sequence is sane. The only change we make in this test is to
+// select a 10ms buffer size instead of the default size and to open up the
+// device in mono.
+// TODO(henrika): possibly add support for more variations.
+TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacksNonDefaultParameters) {
+  GetDefaultOutputStreamParametersOnAudioThread();
+  AudioParameters params(audio_output_parameters().format(),
+                         CHANNEL_LAYOUT_MONO,
+                         audio_output_parameters().sample_rate(),
+                         audio_output_parameters().sample_rate() / 100);
+  StartOutputStreamCallbacks(params);
+}
+
+// Start input streaming and run it for ten seconds while recording to a
+// local audio file.
+// NOTE: this test requires user interaction and is not designed to run as an
+// automatized test on bots.
+TEST_P(AudioAndroidInputTest, DISABLED_RunSimplexInputStreamWithFileAsSink) {
+  AudioParameters params = GetInputStreamParameters();
+  DVLOG(1) << params;
+  MakeAudioInputStreamOnAudioThread(params);
+
+  std::string file_name = base::StringPrintf("out_simplex_%d_%d_%d.pcm",
+                                             params.sample_rate(),
+                                             params.frames_per_buffer(),
+                                             params.channels());
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  FileAudioSink sink(&event, params, file_name);
+
+  OpenAndStartAudioInputStreamOnAudioThread(&sink);
+  DVLOG(0) << ">> Speak into the microphone to record audio...";
+  EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
+  StopAndCloseAudioInputStreamOnAudioThread();
+}
+
+// Same test as RunSimplexInputStreamWithFileAsSink but this time output
+// streaming is active as well (reads zeros only).
+// NOTE: this test requires user interaction and is not designed to run as an
+// automatized test on bots.
+TEST_P(AudioAndroidInputTest, DISABLED_RunDuplexInputStreamWithFileAsSink) {
+  AudioParameters in_params = GetInputStreamParameters();
+  DVLOG(1) << in_params;
+  MakeAudioInputStreamOnAudioThread(in_params);
+
+  GetDefaultOutputStreamParametersOnAudioThread();
+  DVLOG(1) << audio_output_parameters();
+  MakeAudioOutputStreamOnAudioThread(audio_output_parameters());
+
+  std::string file_name = base::StringPrintf("out_duplex_%d_%d_%d.pcm",
+                                             in_params.sample_rate(),
+                                             in_params.frames_per_buffer(),
+                                             in_params.channels());
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  FileAudioSink sink(&event, in_params, file_name);
+  MockAudioSourceCallback source;
+
+  EXPECT_CALL(source, OnMoreData(_, _, 0, NotNull()))
+      .WillRepeatedly(Invoke(RealOnMoreData));
+  EXPECT_CALL(source, OnError(_)).Times(0);
+
+  OpenAndStartAudioInputStreamOnAudioThread(&sink);
+  OpenAndStartAudioOutputStreamOnAudioThread(&source);
+  DVLOG(0) << ">> Speak into the microphone to record audio";
+  EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
+  StopAndCloseAudioOutputStreamOnAudioThread();
+  StopAndCloseAudioInputStreamOnAudioThread();
+}
+
+// Start audio in both directions while feeding captured data into a FIFO so
+// it can be read directly (in loopback) by the render side. A small extra
+// delay will be added by the FIFO and an estimate of this delay will be
+// printed out during the test.
+// NOTE: this test requires user interaction and is not designed to run as an
+// automatized test on bots.
+TEST_P(AudioAndroidInputTest,
+       DISABLED_RunSymmetricInputAndOutputStreamsInFullDuplex) {
+  // Get native audio parameters for the input side.
+  AudioParameters default_input_params = GetInputStreamParameters();
+
+  // Modify the parameters so that both input and output can use the same
+  // parameters by selecting 10ms as buffer size. This will also ensure that
+  // the output stream will be a mono stream since mono is default for input
+  // audio on Android.
+  AudioParameters io_params = default_input_params;
+  default_input_params.set_frames_per_buffer(io_params.sample_rate() / 100);
+  DVLOG(1) << io_params;
+
+  // Create input and output streams using the common audio parameters.
+  MakeAudioInputStreamOnAudioThread(io_params);
+  MakeAudioOutputStreamOnAudioThread(io_params);
+
+  FullDuplexAudioSinkSource full_duplex(io_params);
+
+  // Start a full duplex audio session and print out estimates of the extra
+  // delay we should expect from the FIFO. If real-time delay measurements are
+  // performed, the result should be reduced by this extra delay since it is
+  // something that has been added by the test.
+  OpenAndStartAudioInputStreamOnAudioThread(&full_duplex);
+  OpenAndStartAudioOutputStreamOnAudioThread(&full_duplex);
+  DVLOG(1) << "HINT: an estimate of the extra FIFO delay will be updated "
+           << "once per second during this test.";
+  DVLOG(0) << ">> Speak into the mic and listen to the audio in loopback...";
+  fflush(stdout);
+  base::PlatformThread::Sleep(base::Seconds(20));
+  printf("\n");
+  StopAndCloseAudioOutputStreamOnAudioThread();
+  StopAndCloseAudioInputStreamOnAudioThread();
+}
+
+INSTANTIATE_TEST_SUITE_P(AudioAndroidInputTest,
+                         AudioAndroidInputTest,
+                         testing::Bool());
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/android/audio_manager_android.cc b/third_party/chromium/media/audio/android/audio_manager_android.cc
new file mode 100644
index 0000000..7008e59
--- /dev/null
+++ b/third_party/chromium/media/audio/android/audio_manager_android.cc
@@ -0,0 +1,503 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/android/audio_manager_android.h"
+
+#include <memory>
+
+#include "base/android/build_info.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "media/audio/android/aaudio_output.h"
+#include "media/audio/android/aaudio_stubs.h"
+#include "media/audio/android/audio_track_output_stream.h"
+#include "media/audio/android/opensles_input.h"
+#include "media/audio/android/opensles_output.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_features.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/fake_audio_input_stream.h"
+#include "media/base/android/media_jni_headers/AudioManagerAndroid_jni.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/channel_layout.h"
+
+using base::android::AppendJavaStringArrayToStringVector;
+using base::android::AttachCurrentThread;
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::JavaParamRef;
+using base::android::JavaRef;
+using base::android::ScopedJavaLocalRef;
+
+using media_audio_android::InitializeStubs;
+using media_audio_android::kModuleAaudio;
+using media_audio_android::StubPathMap;
+
+static const base::FilePath::CharType kAaudioLib[] =
+    FILE_PATH_LITERAL("libaaudio.so");
+
+namespace media {
+namespace {
+
+void AddDefaultDevice(AudioDeviceNames* device_names) {
+  DCHECK(device_names->empty());
+  device_names->push_front(AudioDeviceName::CreateDefault());
+}
+
+// Maximum number of output streams that can be open simultaneously.
+const int kMaxOutputStreams = 10;
+
+const int kDefaultInputBufferSize = 1024;
+const int kDefaultOutputBufferSize = 2048;
+
+}  // namespace
+
+static bool InitAAudio() {
+  StubPathMap paths;
+
+  // Check if the AAudio library is available.
+  paths[kModuleAaudio].push_back(kAaudioLib);
+  if (!InitializeStubs(paths)) {
+    VLOG(1) << "Failed on loading the AAudio library and symbols";
+    return false;
+  }
+  return true;
+}
+
+std::unique_ptr<AudioManager> CreateAudioManager(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory) {
+  return std::make_unique<AudioManagerAndroid>(std::move(audio_thread),
+                                               audio_log_factory);
+}
+
+AudioManagerAndroid::AudioManagerAndroid(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory)
+    : AudioManagerBase(std::move(audio_thread), audio_log_factory),
+      communication_mode_is_on_(false),
+      output_volume_override_set_(false),
+      output_volume_override_(0) {
+  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
+}
+
+AudioManagerAndroid::~AudioManagerAndroid() = default;
+
+void AudioManagerAndroid::InitializeIfNeeded() {
+  GetTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(base::IgnoreResult(
+                                    &AudioManagerAndroid::GetJavaAudioManager),
+                                base::Unretained(this)));
+}
+
+void AudioManagerAndroid::ShutdownOnAudioThread() {
+  AudioManagerBase::ShutdownOnAudioThread();
+
+  // Destory java android manager here because it can only be accessed on the
+  // audio thread.
+  if (!j_audio_manager_.is_null()) {
+    DVLOG(2) << "Destroying Java part of the audio manager";
+    Java_AudioManagerAndroid_close(base::android::AttachCurrentThread(),
+                                   j_audio_manager_);
+    j_audio_manager_.Reset();
+  }
+}
+
+bool AudioManagerAndroid::HasAudioOutputDevices() {
+  return true;
+}
+
+bool AudioManagerAndroid::HasAudioInputDevices() {
+  return true;
+}
+
+void AudioManagerAndroid::GetAudioInputDeviceNames(
+    AudioDeviceNames* device_names) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  // Always add default device parameters as first element.
+  DCHECK(device_names->empty());
+  AddDefaultDevice(device_names);
+
+  // Get list of available audio devices.
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jobjectArray> j_device_array =
+      Java_AudioManagerAndroid_getAudioInputDeviceNames(env,
+                                                        GetJavaAudioManager());
+  if (j_device_array.is_null()) {
+    // Most probable reason for a NULL result here is that the process lacks
+    // MODIFY_AUDIO_SETTINGS or RECORD_AUDIO permissions.
+    return;
+  }
+  AudioDeviceName device;
+  for (auto j_device : j_device_array.ReadElements<jobject>()) {
+    ScopedJavaLocalRef<jstring> j_device_name =
+        Java_AudioDeviceName_name(env, j_device);
+    ConvertJavaStringToUTF8(env, j_device_name.obj(), &device.device_name);
+    ScopedJavaLocalRef<jstring> j_device_id =
+        Java_AudioDeviceName_id(env, j_device);
+    ConvertJavaStringToUTF8(env, j_device_id.obj(), &device.unique_id);
+    device_names->push_back(device);
+  }
+
+  for (auto d : *device_names) {
+    DVLOG(1) << "device_name: " << d.device_name;
+    DVLOG(1) << "unique_id: " << d.unique_id;
+  }
+}
+
+void AudioManagerAndroid::GetAudioOutputDeviceNames(
+    AudioDeviceNames* device_names) {
+  // TODO(henrika): enumerate using GetAudioInputDeviceNames().
+  AddDefaultDevice(device_names);
+}
+
+AudioParameters AudioManagerAndroid::GetInputStreamParameters(
+    const std::string& device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  // Use mono as preferred number of input channels on Android to save
+  // resources. Using mono also avoids a driver issue seen on Samsung
+  // Galaxy S3 and S4 devices. See http://crbug.com/256851 for details.
+  JNIEnv* env = AttachCurrentThread();
+  ChannelLayout channel_layout = CHANNEL_LAYOUT_MONO;
+  int buffer_size = Java_AudioManagerAndroid_getMinInputFrameSize(
+      env, GetNativeOutputSampleRate(),
+      ChannelLayoutToChannelCount(channel_layout));
+  buffer_size = buffer_size <= 0 ? kDefaultInputBufferSize : buffer_size;
+  int effects = AudioParameters::NO_EFFECTS;
+  effects |= Java_AudioManagerAndroid_acousticEchoCancelerIsAvailable(env)
+                 ? AudioParameters::ECHO_CANCELLER
+                 : AudioParameters::NO_EFFECTS;
+
+  int user_buffer_size = GetUserBufferSize();
+  if (user_buffer_size)
+    buffer_size = user_buffer_size;
+
+  AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
+                         GetNativeOutputSampleRate(), buffer_size);
+  params.set_effects(effects);
+  DVLOG(1) << params.AsHumanReadableString();
+  return params;
+}
+
+const char* AudioManagerAndroid::GetName() {
+  return "Android";
+}
+
+AudioOutputStream* AudioManagerAndroid::MakeAudioOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  AudioOutputStream* stream = AudioManagerBase::MakeAudioOutputStream(
+      params, device_id, AudioManager::LogCallback());
+  if (stream)
+    streams_.insert(static_cast<MuteableAudioOutputStream*>(stream));
+  return stream;
+}
+
+AudioInputStream* AudioManagerAndroid::MakeAudioInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  bool has_no_input_streams = HasNoAudioInputStreams();
+  AudioInputStream* stream = AudioManagerBase::MakeAudioInputStream(
+      params, device_id, AudioManager::LogCallback());
+
+  // By default, the audio manager for Android creates streams intended for
+  // real-time VoIP sessions and therefore sets the audio mode to
+  // MODE_IN_COMMUNICATION. However, the user might have asked for a special
+  // mode where all audio input processing is disabled, and if that is the case
+  // we avoid changing the mode.
+  if (stream && has_no_input_streams &&
+      params.effects() != AudioParameters::NO_EFFECTS) {
+    communication_mode_is_on_ = true;
+    SetCommunicationAudioModeOn(true);
+  }
+  return stream;
+}
+
+void AudioManagerAndroid::ReleaseOutputStream(AudioOutputStream* stream) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  streams_.erase(static_cast<MuteableAudioOutputStream*>(stream));
+  AudioManagerBase::ReleaseOutputStream(stream);
+}
+
+void AudioManagerAndroid::ReleaseInputStream(AudioInputStream* stream) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  AudioManagerBase::ReleaseInputStream(stream);
+
+  // Restore the audio mode which was used before the first communication-
+  // mode stream was created.
+  if (HasNoAudioInputStreams() && communication_mode_is_on_) {
+    communication_mode_is_on_ = false;
+    SetCommunicationAudioModeOn(false);
+  }
+}
+
+AudioOutputStream* AudioManagerAndroid::MakeLinearOutputStream(
+    const AudioParameters& params,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  if (UseAAudio())
+    return new AAudioOutputStream(this, params, AAUDIO_USAGE_MEDIA);
+
+  return new OpenSLESOutputStream(this, params, SL_ANDROID_STREAM_MEDIA);
+}
+
+AudioOutputStream* AudioManagerAndroid::MakeLowLatencyOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+
+  if (UseAAudio()) {
+    const aaudio_usage_t usage = communication_mode_is_on_
+                                     ? AAUDIO_USAGE_VOICE_COMMUNICATION
+                                     : AAUDIO_USAGE_MEDIA;
+    return new AAudioOutputStream(this, params, usage);
+  }
+
+  // Set stream type which matches the current system-wide audio mode used by
+  // the Android audio manager.
+  const SLint32 stream_type = communication_mode_is_on_
+                                  ? SL_ANDROID_STREAM_VOICE
+                                  : SL_ANDROID_STREAM_MEDIA;
+  return new OpenSLESOutputStream(this, params, stream_type);
+}
+
+AudioOutputStream* AudioManagerAndroid::MakeBitstreamOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK(params.IsBitstreamFormat());
+  return new AudioTrackOutputStream(this, params);
+}
+
+AudioInputStream* AudioManagerAndroid::MakeLinearInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  // TODO(henrika): add support for device selection if/when any client
+  // needs it.
+  DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+  return new OpenSLESInputStream(this, params);
+}
+
+AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DVLOG(1) << "MakeLowLatencyInputStream: " << params.effects();
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+  DLOG_IF(ERROR, device_id.empty()) << "Invalid device ID!";
+
+  // Use the device ID to select the correct input device.
+  // Note that the input device is always associated with a certain output
+  // device, i.e., this selection does also switch the output device.
+  // All input and output streams will be affected by the device selection.
+  if (!SetAudioDevice(device_id)) {
+    LOG(ERROR) << "Unable to select audio device!";
+    return NULL;
+  }
+
+  // Create a new audio input stream and enable or disable all audio effects
+  // given |params.effects()|.
+  return new OpenSLESInputStream(this, params);
+}
+
+// static
+bool AudioManagerAndroid::SupportsPerformanceModeForOutput() {
+  return base::android::BuildInfo::GetInstance()->sdk_int() >=
+         base::android::SDK_VERSION_NOUGAT_MR1;
+}
+
+void AudioManagerAndroid::SetMute(JNIEnv* env,
+                                  const JavaParamRef<jobject>& obj,
+                                  jboolean muted) {
+  GetTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&AudioManagerAndroid::DoSetMuteOnAudioThread,
+                                base::Unretained(this), muted));
+}
+
+void AudioManagerAndroid::SetOutputVolumeOverride(double volume) {
+  GetTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&AudioManagerAndroid::DoSetVolumeOnAudioThread,
+                                base::Unretained(this), volume));
+}
+
+bool AudioManagerAndroid::HasOutputVolumeOverride(double* out_volume) const {
+  if (output_volume_override_set_) {
+    *out_volume = output_volume_override_;
+  }
+  return output_volume_override_set_;
+}
+
+base::TimeDelta AudioManagerAndroid::GetOutputLatency() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  JNIEnv* env = AttachCurrentThread();
+  return base::Milliseconds(
+      Java_AudioManagerAndroid_getOutputLatency(env, GetJavaAudioManager()));
+}
+
+AudioParameters AudioManagerAndroid::GetPreferredOutputStreamParameters(
+    const std::string& output_device_id,
+    const AudioParameters& input_params) {
+  DVLOG(1) << __FUNCTION__;
+  // TODO(tommi): Support |output_device_id|.
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!";
+  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
+  int sample_rate = GetNativeOutputSampleRate();
+  int buffer_size = GetOptimalOutputFrameSize(sample_rate, 2);
+  if (input_params.IsValid()) {
+    // Use the client's input parameters if they are valid.
+    sample_rate = input_params.sample_rate();
+
+    // Pre-Lollipop devices don't support > stereo OpenSLES output and the
+    // AudioManager APIs for GetOptimalOutputFrameSize() don't support channel
+    // layouts greater than stereo unless low latency audio is supported.
+    if (input_params.channels() <= 2 ||
+        (base::android::BuildInfo::GetInstance()->sdk_int() >=
+             base::android::SDK_VERSION_LOLLIPOP &&
+         IsAudioLowLatencySupported())) {
+      channel_layout = input_params.channel_layout();
+    }
+
+    // For high latency playback on supported platforms, pass through the
+    // requested buffer size; this provides significant power savings (~25%) and
+    // reduces the potential for glitches under load.
+    if (SupportsPerformanceModeForOutput() &&
+        input_params.latency_tag() == AudioLatency::LATENCY_PLAYBACK) {
+      buffer_size = input_params.frames_per_buffer();
+    } else {
+      buffer_size = GetOptimalOutputFrameSize(
+          sample_rate, ChannelLayoutToChannelCount(channel_layout));
+    }
+  }
+
+  int user_buffer_size = GetUserBufferSize();
+  if (user_buffer_size)
+    buffer_size = user_buffer_size;
+
+  return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
+                         sample_rate, buffer_size);
+}
+
+bool AudioManagerAndroid::HasNoAudioInputStreams() {
+  return input_stream_count() == 0;
+}
+
+const JavaRef<jobject>& AudioManagerAndroid::GetJavaAudioManager() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  if (j_audio_manager_.is_null()) {
+    // Create the Android audio manager on the audio thread.
+    DVLOG(2) << "Creating Java part of the audio manager";
+    j_audio_manager_.Reset(Java_AudioManagerAndroid_createAudioManagerAndroid(
+        base::android::AttachCurrentThread(),
+        reinterpret_cast<intptr_t>(this)));
+
+    // Prepare the list of audio devices and register receivers for device
+    // notifications.
+    Java_AudioManagerAndroid_init(base::android::AttachCurrentThread(),
+                                  j_audio_manager_);
+  }
+  return j_audio_manager_;
+}
+
+void AudioManagerAndroid::SetCommunicationAudioModeOn(bool on) {
+  DVLOG(1) << __FUNCTION__ << ": " << on;
+  Java_AudioManagerAndroid_setCommunicationAudioModeOn(
+      base::android::AttachCurrentThread(), GetJavaAudioManager(), on);
+}
+
+bool AudioManagerAndroid::SetAudioDevice(const std::string& device_id) {
+  DVLOG(1) << __FUNCTION__ << ": " << device_id;
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  // Send the unique device ID to the Java audio manager and make the
+  // device switch. Provide an empty string to the Java audio manager
+  // if the default device is selected.
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jstring> j_device_id = ConvertUTF8ToJavaString(
+      env, device_id == AudioDeviceDescription::kDefaultDeviceId ? std::string()
+                                                                 : device_id);
+  return Java_AudioManagerAndroid_setDevice(env, GetJavaAudioManager(),
+                                            j_device_id);
+}
+
+int AudioManagerAndroid::GetNativeOutputSampleRate() {
+  return Java_AudioManagerAndroid_getNativeOutputSampleRate(
+      base::android::AttachCurrentThread(), GetJavaAudioManager());
+}
+
+bool AudioManagerAndroid::IsAudioLowLatencySupported() {
+  return Java_AudioManagerAndroid_isAudioLowLatencySupported(
+      base::android::AttachCurrentThread(), GetJavaAudioManager());
+}
+
+int AudioManagerAndroid::GetAudioLowLatencyOutputFrameSize() {
+  return Java_AudioManagerAndroid_getAudioLowLatencyOutputFrameSize(
+      base::android::AttachCurrentThread(), GetJavaAudioManager());
+}
+
+int AudioManagerAndroid::GetOptimalOutputFrameSize(int sample_rate,
+                                                   int channels) {
+  if (IsAudioLowLatencySupported())
+    return GetAudioLowLatencyOutputFrameSize();
+
+  return std::max(kDefaultOutputBufferSize,
+                  Java_AudioManagerAndroid_getMinOutputFrameSize(
+                      base::android::AttachCurrentThread(),
+                      sample_rate, channels));
+}
+
+void AudioManagerAndroid::DoSetMuteOnAudioThread(bool muted) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  for (OutputStreams::iterator it = streams_.begin();
+       it != streams_.end(); ++it) {
+    (*it)->SetMute(muted);
+  }
+}
+
+void AudioManagerAndroid::DoSetVolumeOnAudioThread(double volume) {
+  output_volume_override_set_ = true;
+  output_volume_override_ = volume;
+
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  for (OutputStreams::iterator it = streams_.begin(); it != streams_.end();
+       ++it) {
+    (*it)->SetVolume(volume);
+  }
+}
+
+bool AudioManagerAndroid::UseAAudio() {
+  if (!base::FeatureList::IsEnabled(features::kUseAAudioDriver))
+    return false;
+
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_Q) {
+    // We need APIs that weren't added until API Level 28. Also, AAudio crashes
+    // on Android P, so only consider Q and above.
+    return false;
+  }
+
+  if (!is_aaudio_available_.has_value())
+    is_aaudio_available_ = InitAAudio();
+
+  return is_aaudio_available_.value();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/android/audio_manager_android.h b/third_party/chromium/media/audio/android/audio_manager_android.h
new file mode 100644
index 0000000..92632a8
--- /dev/null
+++ b/third_party/chromium/media/audio/android/audio_manager_android.h
@@ -0,0 +1,138 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
+#define MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
+
+#include <set>
+
+#include "base/android/jni_android.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "media/audio/audio_manager_base.h"
+
+namespace media {
+
+class MuteableAudioOutputStream;
+
+// Android implemention of AudioManager.
+class MEDIA_EXPORT AudioManagerAndroid : public AudioManagerBase {
+ public:
+  AudioManagerAndroid(std::unique_ptr<AudioThread> audio_thread,
+                      AudioLogFactory* audio_log_factory);
+
+  AudioManagerAndroid(const AudioManagerAndroid&) = delete;
+  AudioManagerAndroid& operator=(const AudioManagerAndroid&) = delete;
+
+  ~AudioManagerAndroid() override;
+
+  void InitializeIfNeeded();
+
+  // Implementation of AudioManager.
+  bool HasAudioOutputDevices() override;
+  bool HasAudioInputDevices() override;
+  void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override;
+  void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+
+  AudioOutputStream* MakeAudioOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeAudioInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  void ReleaseOutputStream(AudioOutputStream* stream) override;
+  void ReleaseInputStream(AudioInputStream* stream) override;
+  const char* GetName() override;
+
+  // Implementation of AudioManagerBase.
+  AudioOutputStream* MakeLinearOutputStream(
+      const AudioParameters& params,
+      const LogCallback& log_callback) override;
+  AudioOutputStream* MakeLowLatencyOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioOutputStream* MakeBitstreamOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLinearInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLowLatencyInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+
+  // Indicates if there's support for the OpenSLES performance mode keys. See
+  // OpenSLESOutputStream for specific details. Essentially this allows for low
+  // power audio when large buffer sizes can be used.
+  static bool SupportsPerformanceModeForOutput();
+
+  void SetMute(JNIEnv* env,
+               const base::android::JavaParamRef<jobject>& obj,
+               jboolean muted);
+
+  // Sets a volume that applies to all this manager's output audio streams.
+  // This overrides other SetVolume calls (e.g. through AudioHostMsg_SetVolume).
+  void SetOutputVolumeOverride(double volume);
+  bool HasOutputVolumeOverride(double* out_volume) const;
+
+  // Get the latency introduced by the hardware.  It relies on
+  // AudioManager.getOutputLatency, which is both (a) hidden and (b) not
+  // guaranteed to be meaningful.  Do not use this, except in the context of
+  // b/80326798 to adjust (hackily) for hardware latency that OpenSLES isn't
+  // otherwise accounting for.
+  base::TimeDelta GetOutputLatency();
+
+  bool IsUsingAAudioForTesting() { return UseAAudio(); }
+
+ protected:
+  void ShutdownOnAudioThread() override;
+  AudioParameters GetPreferredOutputStreamParameters(
+      const std::string& output_device_id,
+      const AudioParameters& input_params) override;
+
+ private:
+  const base::android::JavaRef<jobject>& GetJavaAudioManager();
+  bool HasNoAudioInputStreams();
+  void SetCommunicationAudioModeOn(bool on);
+  bool SetAudioDevice(const std::string& device_id);
+  int GetNativeOutputSampleRate();
+  bool IsAudioLowLatencySupported();
+  int GetAudioLowLatencyOutputFrameSize();
+  int GetOptimalOutputFrameSize(int sample_rate, int channels);
+
+  void DoSetMuteOnAudioThread(bool muted);
+  void DoSetVolumeOnAudioThread(double volume);
+
+  // Returns whether or not we can and should use AAudio.
+  bool UseAAudio();
+
+  // Java AudioManager instance.
+  base::android::ScopedJavaGlobalRef<jobject> j_audio_manager_;
+
+  typedef std::set<MuteableAudioOutputStream*> OutputStreams;
+  OutputStreams streams_;
+
+  // Enabled when first input stream is created and set to false when last
+  // input stream is destroyed. Also affects the stream type of output streams.
+  bool communication_mode_is_on_;
+
+  absl::optional<bool> is_aaudio_available_;
+
+  // If set, overrides volume level on output streams
+  bool output_volume_override_set_;
+  double output_volume_override_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ANDROID_AUDIO_MANAGER_ANDROID_H_
diff --git a/third_party/chromium/media/audio/android/audio_track_output_stream.cc b/third_party/chromium/media/audio/android/audio_track_output_stream.cc
new file mode 100644
index 0000000..5b98e2d
--- /dev/null
+++ b/third_party/chromium/media/audio/android/audio_track_output_stream.cc
@@ -0,0 +1,185 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/android/audio_track_output_stream.h"
+
+#include <cmath>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/default_tick_clock.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/base/android/media_jni_headers/AudioTrackOutputStream_jni.h"
+#include "media/base/audio_sample_types.h"
+#include "media/base/audio_timestamp_helper.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+
+namespace media {
+
+// Android audio format. For more information, please see:
+// https://developer.android.com/reference/android/media/AudioFormat.html
+enum {
+  kEncodingPcm16bit = 2,  // ENCODING_PCM_16BIT
+  kEncodingAc3 = 5,       // ENCODING_AC3
+  kEncodingEac3 = 6,      // ENCODING_E_AC3
+};
+
+AudioTrackOutputStream::AudioTrackOutputStream(AudioManagerBase* manager,
+                                               const AudioParameters& params)
+    : params_(params),
+      audio_manager_(manager),
+      tick_clock_(base::DefaultTickClock::GetInstance()) {
+  if (!params_.IsBitstreamFormat()) {
+    audio_bus_ = AudioBus::Create(params_);
+  }
+}
+
+AudioTrackOutputStream::~AudioTrackOutputStream() {
+  DCHECK(!callback_);
+}
+
+bool AudioTrackOutputStream::Open() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  JNIEnv* env = AttachCurrentThread();
+  j_audio_output_stream_.Reset(Java_AudioTrackOutputStream_create(env));
+
+  int format = kEncodingPcm16bit;
+  if (params_.IsBitstreamFormat()) {
+    if (params_.format() == AudioParameters::AUDIO_BITSTREAM_AC3) {
+      format = kEncodingAc3;
+    } else if (params_.format() == AudioParameters::AUDIO_BITSTREAM_EAC3) {
+      format = kEncodingEac3;
+    } else {
+      NOTREACHED();
+    }
+  }
+
+  return Java_AudioTrackOutputStream_open(env, j_audio_output_stream_,
+                                          params_.channels(),
+                                          params_.sample_rate(), format);
+}
+
+void AudioTrackOutputStream::Start(AudioSourceCallback* callback) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  callback_ = callback;
+  Java_AudioTrackOutputStream_start(AttachCurrentThread(),
+                                    j_audio_output_stream_,
+                                    reinterpret_cast<intptr_t>(this));
+}
+
+void AudioTrackOutputStream::Stop() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  Java_AudioTrackOutputStream_stop(AttachCurrentThread(),
+                                   j_audio_output_stream_);
+  callback_ = nullptr;
+}
+
+void AudioTrackOutputStream::Close() {
+  DCHECK(!callback_);
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+
+  Java_AudioTrackOutputStream_close(AttachCurrentThread(),
+                                    j_audio_output_stream_);
+  audio_manager_->ReleaseOutputStream(this);
+}
+
+// This stream is always used with sub second buffer sizes, where it's
+// sufficient to simply always flush upon Start().
+void AudioTrackOutputStream::Flush() {}
+
+void AudioTrackOutputStream::SetMute(bool muted) {
+  if (params_.IsBitstreamFormat() && muted) {
+    LOG(WARNING)
+        << "Mute is not supported for compressed audio bitstream formats.";
+    return;
+  }
+
+  if (muted_ == muted)
+    return;
+
+  muted_ = muted;
+  Java_AudioTrackOutputStream_setVolume(
+      AttachCurrentThread(), j_audio_output_stream_, muted_ ? 0.0 : volume_);
+}
+
+void AudioTrackOutputStream::SetVolume(double volume) {
+  if (params_.IsBitstreamFormat()) {
+    LOG(WARNING) << "Volume change is not supported for compressed audio "
+                    "bitstream formats.";
+    return;
+  }
+
+  // Track |volume_| since AudioTrack uses a scaled value.
+  volume_ = volume;
+  if (muted_)
+    return;
+
+  Java_AudioTrackOutputStream_setVolume(AttachCurrentThread(),
+                                        j_audio_output_stream_, volume);
+}
+
+void AudioTrackOutputStream::GetVolume(double* volume) {
+  *volume = volume_;
+}
+
+// AudioOutputStream::SourceCallback implementation methods called from Java.
+ScopedJavaLocalRef<jobject> AudioTrackOutputStream::OnMoreData(
+    JNIEnv* env,
+    jobject obj,
+    jobject audio_data,
+    jlong delay_in_frame) {
+  DCHECK(callback_);
+
+  base::TimeDelta delay =
+      AudioTimestampHelper::FramesToTime(delay_in_frame, params_.sample_rate());
+
+  void* native_buffer = env->GetDirectBufferAddress(audio_data);
+
+  if (params_.IsBitstreamFormat()) {
+    // For bitstream formats, use the direct buffer memory to avoid additional
+    // memory copy.
+    std::unique_ptr<AudioBus> audio_bus(
+        AudioBus::WrapMemory(params_, native_buffer));
+    audio_bus->set_is_bitstream_format(true);
+
+    callback_->OnMoreData(delay, tick_clock_->NowTicks(), 0, audio_bus.get());
+
+    if (audio_bus->GetBitstreamDataSize() <= 0)
+      return nullptr;
+
+    return Java_AudioTrackOutputStream_createAudioBufferInfo(
+        env, j_audio_output_stream_, audio_bus->GetBitstreamFrames(),
+        audio_bus->GetBitstreamDataSize());
+  }
+
+  // For PCM format, we need extra memory to convert planar float32 into
+  // interleaved int16.
+
+  callback_->OnMoreData(delay, tick_clock_->NowTicks(), 0, audio_bus_.get());
+
+  int16_t* native_bus = reinterpret_cast<int16_t*>(native_buffer);
+  audio_bus_->ToInterleaved<SignedInt16SampleTypeTraits>(audio_bus_->frames(),
+                                                         native_bus);
+
+  return Java_AudioTrackOutputStream_createAudioBufferInfo(
+      env, j_audio_output_stream_, audio_bus_->frames(),
+      sizeof(*native_bus) * audio_bus_->channels() * audio_bus_->frames());
+}
+
+void AudioTrackOutputStream::OnError(JNIEnv* env, jobject obj) {
+  DCHECK(callback_);
+  callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+}
+
+jlong AudioTrackOutputStream::GetAddress(JNIEnv* env,
+                                         jobject obj,
+                                         jobject byte_buffer) {
+  return reinterpret_cast<jlong>(env->GetDirectBufferAddress(byte_buffer));
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/android/audio_track_output_stream.h b/third_party/chromium/media/audio/android/audio_track_output_stream.h
new file mode 100644
index 0000000..6bc6e69
--- /dev/null
+++ b/third_party/chromium/media/audio/android/audio_track_output_stream.h
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ANDROID_AUDIO_TRACK_OUTPUT_STREAM_H_
+#define MEDIA_AUDIO_ANDROID_AUDIO_TRACK_OUTPUT_STREAM_H_
+
+#include <memory>
+
+#include "base/android/jni_android.h"
+#include "base/time/tick_clock.h"
+#include "media/audio/android/muteable_audio_output_stream.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioManagerBase;
+
+// A MuteableAudioOutputStream implementation based on the Android AudioTrack
+// API.
+class MEDIA_EXPORT AudioTrackOutputStream : public MuteableAudioOutputStream {
+ public:
+  AudioTrackOutputStream(AudioManagerBase* manager,
+                         const AudioParameters& params);
+
+  AudioTrackOutputStream(const AudioTrackOutputStream&) = delete;
+  AudioTrackOutputStream& operator=(const AudioTrackOutputStream&) = delete;
+
+  ~AudioTrackOutputStream() override;
+
+  // AudioOutputStream implementation.
+  bool Open() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+  void Close() override;
+  void Flush() override;
+
+  // MuteableAudioOutputStream implementation.
+  void SetMute(bool muted) override;
+
+  // AudioOutputStream::SourceCallback implementation methods called from Java.
+  base::android::ScopedJavaLocalRef<jobject> OnMoreData(JNIEnv* env,
+                                                        jobject obj,
+                                                        jobject audio_data,
+                                                        jlong delay);
+  void OnError(JNIEnv* env, jobject obj);
+  jlong GetAddress(JNIEnv* env, jobject obj, jobject byte_buffer);
+
+ private:
+  const AudioParameters params_;
+
+  AudioManagerBase* audio_manager_;
+  AudioSourceCallback* callback_ = nullptr;
+  bool muted_ = false;
+  double volume_ = 1.0;
+
+  // Extra buffer for PCM format.
+  std::unique_ptr<AudioBus> audio_bus_;
+
+  const base::TickClock* tick_clock_;
+
+  // Java AudioTrackOutputStream instance.
+  base::android::ScopedJavaGlobalRef<jobject> j_audio_output_stream_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ANDROID_AUDIO_TRACK_OUTPUT_STREAM_H_
diff --git a/third_party/chromium/media/audio/android/muteable_audio_output_stream.h b/third_party/chromium/media/audio/android/muteable_audio_output_stream.h
new file mode 100644
index 0000000..db4f6b1
--- /dev/null
+++ b/third_party/chromium/media/audio/android/muteable_audio_output_stream.h
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ANDROID_MUTEABLE_AUDIO_OUTPUT_STREAM_H_
+#define MEDIA_AUDIO_ANDROID_MUTEABLE_AUDIO_OUTPUT_STREAM_H_
+
+#include "media/audio/audio_io.h"
+
+namespace media {
+
+class MEDIA_EXPORT MuteableAudioOutputStream : public AudioOutputStream {
+ public:
+  // Volume control coming from hardware. It overrides volume when it's
+  // true. Otherwise, use SetVolume(double volume) for scaling.
+  // This is needed because platform voice volume never goes to zero in
+  // COMMUNICATION mode on Android.
+  virtual void SetMute(bool muted) = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ANDROID_MUTEABLE_AUDIO_OUTPUT_STREAM_H_
diff --git a/third_party/chromium/media/audio/android/opensles_input.cc b/third_party/chromium/media/audio/android/opensles_input.cc
new file mode 100644
index 0000000..ca10473
--- /dev/null
+++ b/third_party/chromium/media/audio/android/opensles_input.cc
@@ -0,0 +1,351 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/android/opensles_input.h"
+
+#include "base/cxx17_backports.h"
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "media/audio/android/audio_manager_android.h"
+#include "media/base/audio_bus.h"
+
+#define LOG_ON_FAILURE_AND_RETURN(op, ...)      \
+  do {                                          \
+    SLresult err = (op);                        \
+    if (err != SL_RESULT_SUCCESS) {             \
+      DLOG(ERROR) << #op << " failed: " << err; \
+      return __VA_ARGS__;                       \
+    }                                           \
+  } while (0)
+
+namespace media {
+
+OpenSLESInputStream::OpenSLESInputStream(AudioManagerAndroid* audio_manager,
+                                         const AudioParameters& params)
+    : audio_manager_(audio_manager),
+      callback_(nullptr),
+      recorder_(nullptr),
+      simple_buffer_queue_(nullptr),
+      active_buffer_index_(0),
+      buffer_size_bytes_(0),
+      started_(false),
+      audio_bus_(media::AudioBus::Create(params)),
+      no_effects_(params.effects() == AudioParameters::NO_EFFECTS) {
+  DVLOG(2) << __PRETTY_FUNCTION__;
+  DVLOG(1) << "Audio effects enabled: " << !no_effects_;
+
+  const SampleFormat kSampleFormat = kSampleFormatS16;
+
+  format_.formatType = SL_DATAFORMAT_PCM;
+  format_.numChannels = static_cast<SLuint32>(params.channels());
+  // Provides sampling rate in milliHertz to OpenSLES.
+  format_.samplesPerSec = static_cast<SLuint32>(params.sample_rate() * 1000);
+  format_.bitsPerSample = format_.containerSize =
+      SampleFormatToBitsPerChannel(kSampleFormat);
+  format_.endianness = SL_BYTEORDER_LITTLEENDIAN;
+  format_.channelMask = ChannelCountToSLESChannelMask(params.channels());
+
+  buffer_size_bytes_ = params.GetBytesPerBuffer(kSampleFormat);
+  hardware_delay_ = base::Seconds(params.frames_per_buffer() /
+                                  static_cast<double>(params.sample_rate()));
+
+  memset(&audio_data_, 0, sizeof(audio_data_));
+}
+
+OpenSLESInputStream::~OpenSLESInputStream() {
+  DVLOG(2) << __PRETTY_FUNCTION__;
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!recorder_object_.Get());
+  DCHECK(!engine_object_.Get());
+  DCHECK(!recorder_);
+  DCHECK(!simple_buffer_queue_);
+  DCHECK(!audio_data_[0]);
+}
+
+AudioInputStream::OpenOutcome OpenSLESInputStream::Open() {
+  DVLOG(2) << __PRETTY_FUNCTION__;
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (engine_object_.Get())
+    return AudioInputStream::OpenOutcome::kFailed;
+
+  if (!CreateRecorder())
+    return AudioInputStream::OpenOutcome::kFailed;
+
+  SetupAudioBuffer();
+  return AudioInputStream::OpenOutcome::kSuccess;
+}
+
+void OpenSLESInputStream::Start(AudioInputCallback* callback) {
+  DVLOG(2) << __PRETTY_FUNCTION__;
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(callback);
+  DCHECK(recorder_);
+  DCHECK(simple_buffer_queue_);
+  if (started_)
+    return;
+
+  base::AutoLock lock(lock_);
+  DCHECK(!callback_ || callback_ == callback);
+  callback_ = callback;
+  active_buffer_index_ = 0;
+
+  // Enqueues kMaxNumOfBuffersInQueue zero buffers to get the ball rolling.
+  // TODO(henrika): add support for Start/Stop/Start sequences when we are
+  // able to clear the buffer queue. There is currently a bug in the OpenSLES
+  // implementation which forces us to always call Stop() and Close() before
+  // calling Start() again.
+  SLresult err = SL_RESULT_UNKNOWN_ERROR;
+  for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
+    err = (*simple_buffer_queue_)->Enqueue(
+        simple_buffer_queue_, audio_data_[i], buffer_size_bytes_);
+    if (SL_RESULT_SUCCESS != err) {
+      HandleError(err);
+      started_ = false;
+      return;
+    }
+  }
+
+  // Start the recording by setting the state to SL_RECORDSTATE_RECORDING.
+  // When the object is in the SL_RECORDSTATE_RECORDING state, adding buffers
+  // will implicitly start the filling process.
+  err = (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_RECORDING);
+  if (SL_RESULT_SUCCESS != err) {
+    HandleError(err);
+    started_ = false;
+    return;
+  }
+
+  started_ = true;
+}
+
+void OpenSLESInputStream::Stop() {
+  DVLOG(2) << __PRETTY_FUNCTION__;
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!started_)
+    return;
+
+  base::AutoLock lock(lock_);
+
+  // Stop recording by setting the record state to SL_RECORDSTATE_STOPPED.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*recorder_)->SetRecordState(recorder_, SL_RECORDSTATE_STOPPED));
+
+  // Clear the buffer queue to get rid of old data when resuming recording.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*simple_buffer_queue_)->Clear(simple_buffer_queue_));
+
+  started_ = false;
+  callback_ = nullptr;
+}
+
+void OpenSLESInputStream::Close() {
+  DVLOG(2) << __PRETTY_FUNCTION__;
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Stop the stream if it is still recording.
+  Stop();
+  {
+    // TODO(henrika): Do we need to hold the lock here?
+    base::AutoLock lock(lock_);
+
+    // Destroy the buffer queue recorder object and invalidate all associated
+    // interfaces.
+    recorder_object_.Reset();
+    simple_buffer_queue_ = nullptr;
+    recorder_ = nullptr;
+
+    // Destroy the engine object. We don't store any associated interface for
+    // this object.
+    engine_object_.Reset();
+    ReleaseAudioBuffer();
+  }
+
+  audio_manager_->ReleaseInputStream(this);
+}
+
+double OpenSLESInputStream::GetMaxVolume() {
+  return 0.0;
+}
+
+void OpenSLESInputStream::SetVolume(double volume) {
+}
+
+double OpenSLESInputStream::GetVolume() {
+  return 0.0;
+}
+
+bool OpenSLESInputStream::SetAutomaticGainControl(bool enabled) {
+  return false;
+}
+
+bool OpenSLESInputStream::GetAutomaticGainControl() {
+  return false;
+}
+
+bool OpenSLESInputStream::IsMuted() {
+  return false;
+}
+
+void OpenSLESInputStream::SetOutputDeviceForAec(
+    const std::string& output_device_id) {
+  // Not supported. Do nothing.
+}
+
+bool OpenSLESInputStream::CreateRecorder() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!engine_object_.Get());
+  DCHECK(!recorder_object_.Get());
+  DCHECK(!recorder_);
+  DCHECK(!simple_buffer_queue_);
+
+  // Initializes the engine object with specific option. After working with the
+  // object, we need to free the object and its resources.
+  SLEngineOption option[] = {
+      {SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE)}};
+  LOG_ON_FAILURE_AND_RETURN(
+      slCreateEngine(engine_object_.Receive(), 1, option, 0, nullptr, nullptr),
+      false);
+
+  // Realize the SL engine object in synchronous mode.
+  LOG_ON_FAILURE_AND_RETURN(
+      engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE), false);
+
+  // Get the SL engine interface which is implicit.
+  SLEngineItf engine;
+  LOG_ON_FAILURE_AND_RETURN(engine_object_->GetInterface(
+                                engine_object_.Get(), SL_IID_ENGINE, &engine),
+                            false);
+
+  // Audio source configuration.
+  SLDataLocator_IODevice mic_locator = {SL_DATALOCATOR_IODEVICE,
+                                        SL_IODEVICE_AUDIOINPUT,
+                                        SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr};
+  SLDataSource audio_source = {&mic_locator, nullptr};
+
+  // Audio sink configuration.
+  SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {
+      SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
+      static_cast<SLuint32>(kMaxNumOfBuffersInQueue)};
+  SLDataSink audio_sink = {&buffer_queue, &format_};
+
+  // Create an audio recorder.
+  const SLInterfaceID interface_id[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                        SL_IID_ANDROIDCONFIGURATION};
+  const SLboolean interface_required[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
+
+  // Create AudioRecorder and specify SL_IID_ANDROIDCONFIGURATION.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*engine)->CreateAudioRecorder(
+          engine, recorder_object_.Receive(), &audio_source, &audio_sink,
+          base::size(interface_id), interface_id, interface_required),
+      false);
+
+  SLAndroidConfigurationItf recorder_config;
+  LOG_ON_FAILURE_AND_RETURN(
+      recorder_object_->GetInterface(recorder_object_.Get(),
+                                     SL_IID_ANDROIDCONFIGURATION,
+                                     &recorder_config),
+      false);
+
+  // Uses the main microphone tuned for audio communications if effects are
+  // enabled and disables all audio processing if effects are disabled.
+  SLint32 stream_type = no_effects_
+                            ? SL_ANDROID_RECORDING_PRESET_CAMCORDER
+                            : SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
+  LOG_ON_FAILURE_AND_RETURN(
+      (*recorder_config)->SetConfiguration(recorder_config,
+                                           SL_ANDROID_KEY_RECORDING_PRESET,
+                                           &stream_type,
+                                           sizeof(SLint32)),
+      false);
+
+  // Realize the recorder object in synchronous mode.
+  LOG_ON_FAILURE_AND_RETURN(
+      recorder_object_->Realize(recorder_object_.Get(), SL_BOOLEAN_FALSE),
+      false);
+
+  // Get an implicit recorder interface.
+  LOG_ON_FAILURE_AND_RETURN(
+      recorder_object_->GetInterface(
+          recorder_object_.Get(), SL_IID_RECORD, &recorder_),
+      false);
+
+  // Get the simple buffer queue interface.
+  LOG_ON_FAILURE_AND_RETURN(
+      recorder_object_->GetInterface(recorder_object_.Get(),
+                                     SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
+                                     &simple_buffer_queue_),
+      false);
+
+  // Register the input callback for the simple buffer queue.
+  // This callback will be called when receiving new data from the device.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*simple_buffer_queue_)->RegisterCallback(
+          simple_buffer_queue_, SimpleBufferQueueCallback, this),
+      false);
+
+  return true;
+}
+
+void OpenSLESInputStream::SimpleBufferQueueCallback(
+    SLAndroidSimpleBufferQueueItf buffer_queue,
+    void* instance) {
+  OpenSLESInputStream* stream =
+      reinterpret_cast<OpenSLESInputStream*>(instance);
+  stream->ReadBufferQueue();
+}
+
+void OpenSLESInputStream::ReadBufferQueue() {
+  base::AutoLock lock(lock_);
+  if (!started_)
+    return;
+
+  TRACE_EVENT0("audio", "OpenSLESOutputStream::ReadBufferQueue");
+
+  // Convert from interleaved format to deinterleaved audio bus format.
+  audio_bus_->FromInterleaved<SignedInt16SampleTypeTraits>(
+      reinterpret_cast<int16_t*>(audio_data_[active_buffer_index_]),
+      audio_bus_->frames());
+
+  // TODO(henrika): Investigate if it is possible to get an accurate
+  // delay estimation.
+  callback_->OnData(audio_bus_.get(), base::TimeTicks::Now() - hardware_delay_,
+                    0.0);
+
+  // Done with this buffer. Send it to device for recording.
+  SLresult err =
+      (*simple_buffer_queue_)->Enqueue(simple_buffer_queue_,
+                                       audio_data_[active_buffer_index_],
+                                       buffer_size_bytes_);
+  if (SL_RESULT_SUCCESS != err)
+    HandleError(err);
+
+  active_buffer_index_ = (active_buffer_index_ + 1) % kMaxNumOfBuffersInQueue;
+}
+
+void OpenSLESInputStream::SetupAudioBuffer() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!audio_data_[0]);
+  for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
+    audio_data_[i] = new uint8_t[buffer_size_bytes_];
+  }
+}
+
+void OpenSLESInputStream::ReleaseAudioBuffer() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (audio_data_[0]) {
+    for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
+      delete[] audio_data_[i];
+      audio_data_[i] = nullptr;
+    }
+  }
+}
+
+void OpenSLESInputStream::HandleError(SLresult error) {
+  DLOG(ERROR) << "OpenSLES Input error " << error;
+  if (callback_)
+    callback_->OnError();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/android/opensles_input.h b/third_party/chromium/media/audio/android/opensles_input.h
new file mode 100644
index 0000000..f0d86fd
--- /dev/null
+++ b/third_party/chromium/media/audio/android/opensles_input.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
+#define MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
+
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/android/opensles_util.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioBus;
+class AudioManagerAndroid;
+
+// Implements PCM audio input support for Android using the OpenSLES API.
+// This class is created and lives on the Audio Manager thread but recorded
+// audio buffers are delivered on an internal OpenSLES audio thread. All public
+// methods should be called on the Audio Manager thread.
+class OpenSLESInputStream : public AudioInputStream {
+ public:
+  static const int kMaxNumOfBuffersInQueue = 2;
+
+  OpenSLESInputStream(AudioManagerAndroid* manager,
+                      const AudioParameters& params);
+
+  OpenSLESInputStream(const OpenSLESInputStream&) = delete;
+  OpenSLESInputStream& operator=(const OpenSLESInputStream&) = delete;
+
+  ~OpenSLESInputStream() override;
+
+  // Implementation of AudioInputStream.
+  OpenOutcome Open() override;
+  void Start(AudioInputCallback* callback) override;
+  void Stop() override;
+  void Close() override;
+  double GetMaxVolume() override;
+  void SetVolume(double volume) override;
+  double GetVolume() override;
+  bool SetAutomaticGainControl(bool enabled) override;
+  bool GetAutomaticGainControl() override;
+  bool IsMuted() override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+ private:
+  bool CreateRecorder();
+
+  // Called from OpenSLES specific audio worker thread.
+  static void SimpleBufferQueueCallback(
+      SLAndroidSimpleBufferQueueItf buffer_queue,
+      void* instance);
+
+  // Called from OpenSLES specific audio worker thread.
+  void ReadBufferQueue();
+
+  // Called in Open();
+  void SetupAudioBuffer();
+
+  // Called in Close();
+  void ReleaseAudioBuffer();
+
+  // If OpenSLES reports an error this function handles it and passes it to
+  // the attached AudioInputCallback::OnError().
+  void HandleError(SLresult error);
+
+  base::ThreadChecker thread_checker_;
+
+  // Protects |callback_|, |active_buffer_index_|, |audio_data_|,
+  // |buffer_size_bytes_| and |simple_buffer_queue_|.
+  base::Lock lock_;
+
+  AudioManagerAndroid* audio_manager_;
+
+  AudioInputCallback* callback_;
+
+  // Shared engine interfaces for the app.
+  media::ScopedSLObjectItf recorder_object_;
+  media::ScopedSLObjectItf engine_object_;
+
+  SLRecordItf recorder_;
+
+  // Buffer queue recorder interface.
+  SLAndroidSimpleBufferQueueItf simple_buffer_queue_;
+
+  SLDataFormat_PCM format_;
+
+  // Audio buffers that are allocated in the constructor based on
+  // info from audio parameters.
+  uint8_t* audio_data_[kMaxNumOfBuffersInQueue];
+
+  int active_buffer_index_;
+  int buffer_size_bytes_;
+
+  bool started_;
+
+  base::TimeDelta hardware_delay_;
+
+  std::unique_ptr<media::AudioBus> audio_bus_;
+
+  // Set to true at construction if user wants to disable all audio effects.
+  const bool no_effects_ = false;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ANDROID_OPENSLES_INPUT_H_
diff --git a/third_party/chromium/media/audio/android/opensles_output.cc b/third_party/chromium/media/audio/android/opensles_output.cc
new file mode 100644
index 0000000..a15692a
--- /dev/null
+++ b/third_party/chromium/media/audio/android/opensles_output.cc
@@ -0,0 +1,508 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/android/opensles_output.h"
+
+#include "base/android/build_info.h"
+#include "base/cxx17_backports.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "media/audio/android/audio_manager_android.h"
+#include "media/base/audio_sample_types.h"
+#include "media/base/audio_timestamp_helper.h"
+#include "media/base/media_switches.h"
+
+#define LOG_ON_FAILURE_AND_RETURN(op, ...)      \
+  do {                                          \
+    SLresult err = (op);                        \
+    if (err != SL_RESULT_SUCCESS) {             \
+      DLOG(ERROR) << #op << " failed: " << err; \
+      return __VA_ARGS__;                       \
+    }                                           \
+  } while (0)
+
+namespace media {
+
+static bool IsFloatAudioSupported() {
+  const auto* build_info = base::android::BuildInfo::GetInstance();
+  if (build_info->sdk_int() < base::android::SDK_VERSION_LOLLIPOP)
+    return false;
+
+  // Vivo devices up until Lollipop used their own Audio Mixer which does not
+  // support float audio output. https://crbug.com/737188.
+  if (build_info->sdk_int() == base::android::SDK_VERSION_LOLLIPOP &&
+      base::EqualsCaseInsensitiveASCII(build_info->manufacturer(), "vivo")) {
+    return false;
+  }
+
+  return true;
+}
+
+OpenSLESOutputStream::OpenSLESOutputStream(AudioManagerAndroid* manager,
+                                           const AudioParameters& params,
+                                           SLint32 stream_type)
+    : audio_manager_(manager),
+      stream_type_(stream_type),
+      callback_(nullptr),
+      player_(nullptr),
+      simple_buffer_queue_(nullptr),
+      audio_data_(),
+      active_buffer_index_(0),
+      started_(false),
+      muted_(false),
+      volume_(1.0),
+      samples_per_second_(params.sample_rate()),
+      sample_format_(IsFloatAudioSupported() ? kSampleFormatF32
+                                             : kSampleFormatS16),
+      bytes_per_frame_(params.GetBytesPerFrame(sample_format_)),
+      buffer_size_bytes_(params.GetBytesPerBuffer(sample_format_)),
+      performance_mode_(SL_ANDROID_PERFORMANCE_NONE),
+      delay_calculator_(samples_per_second_) {
+  DVLOG(2) << "OpenSLESOutputStream::OpenSLESOutputStream("
+           << "stream_type=" << stream_type << ")";
+
+  if (AudioManagerAndroid::SupportsPerformanceModeForOutput()) {
+    if (params.latency_tag() == AudioLatency::LATENCY_PLAYBACK)
+      performance_mode_ = SL_ANDROID_PERFORMANCE_POWER_SAVING;
+    else if (params.latency_tag() == AudioLatency::LATENCY_RTC)
+      performance_mode_ = SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS;
+  }
+
+  audio_bus_ = AudioBus::Create(params);
+
+  if (sample_format_ == kSampleFormatF32) {
+    float_format_.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
+    float_format_.numChannels = static_cast<SLuint32>(params.channels());
+    // Despite the name, this field is actually the sampling rate in millihertz.
+    float_format_.sampleRate =
+        static_cast<SLuint32>(samples_per_second_ * 1000);
+    float_format_.bitsPerSample = float_format_.containerSize =
+        SampleFormatToBitsPerChannel(sample_format_);
+    float_format_.endianness = SL_BYTEORDER_LITTLEENDIAN;
+    float_format_.channelMask =
+        ChannelCountToSLESChannelMask(params.channels());
+    float_format_.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
+    return;
+  }
+
+  format_.formatType = SL_DATAFORMAT_PCM;
+  format_.numChannels = static_cast<SLuint32>(params.channels());
+  // Despite the name, this field is actually the sampling rate in millihertz :|
+  format_.samplesPerSec = static_cast<SLuint32>(samples_per_second_ * 1000);
+  format_.bitsPerSample = format_.containerSize =
+      SampleFormatToBitsPerChannel(sample_format_);
+  format_.endianness = SL_BYTEORDER_LITTLEENDIAN;
+  format_.channelMask = ChannelCountToSLESChannelMask(params.channels());
+}
+
+OpenSLESOutputStream::~OpenSLESOutputStream() {
+  DVLOG(2) << "OpenSLESOutputStream::~OpenSLESOutputStream()";
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!engine_object_.Get());
+  DCHECK(!player_object_.Get());
+  DCHECK(!output_mixer_.Get());
+  DCHECK(!player_);
+  DCHECK(!simple_buffer_queue_);
+  DCHECK(!audio_data_[0]);
+}
+
+bool OpenSLESOutputStream::Open() {
+  DVLOG(2) << "OpenSLESOutputStream::Open()";
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (engine_object_.Get())
+    return false;
+
+  if (!CreatePlayer())
+    return false;
+
+  SetupAudioBuffer();
+  active_buffer_index_ = 0;
+
+  return true;
+}
+
+void OpenSLESOutputStream::Start(AudioSourceCallback* callback) {
+  DVLOG(2) << "OpenSLESOutputStream::Start()";
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(callback);
+  DCHECK(player_);
+  DCHECK(simple_buffer_queue_);
+  if (started_)
+    return;
+
+  base::AutoLock lock(lock_);
+  DCHECK(!callback_);
+  callback_ = callback;
+
+  CacheHardwareLatencyIfNeeded();
+
+  // Fill audio data with silence to avoid start-up glitches. Don't use
+  // FillBufferQueueNoLock() since it can trigger recursive entry if an error
+  // occurs while writing into the stream. See http://crbug.com/624877.
+  memset(audio_data_[active_buffer_index_], 0, buffer_size_bytes_);
+  LOG_ON_FAILURE_AND_RETURN((*simple_buffer_queue_)
+                                ->Enqueue(simple_buffer_queue_,
+                                          audio_data_[active_buffer_index_],
+                                          buffer_size_bytes_));
+  active_buffer_index_ = (active_buffer_index_ + 1) % kMaxNumOfBuffersInQueue;
+
+  // Start streaming data by setting the play state to SL_PLAYSTATE_PLAYING.
+  // For a player object, when the object is in the SL_PLAYSTATE_PLAYING
+  // state, adding buffers will implicitly start playback.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*player_)->SetPlayState(player_, SL_PLAYSTATE_PLAYING));
+
+  // On older version of Android, the position may not be reset even though we
+  // call Clear() during Stop(), in this case the best we can do is assume that
+  // we're continuing on from this previous position.
+  uint32_t position_in_ms = 0;
+  LOG_ON_FAILURE_AND_RETURN((*player_)->GetPosition(player_, &position_in_ms));
+  delay_calculator_.SetBaseTimestamp(base::Milliseconds(position_in_ms));
+  delay_calculator_.AddFrames(audio_bus_->frames());
+
+  started_ = true;
+}
+
+void OpenSLESOutputStream::Stop() {
+  DVLOG(2) << "OpenSLESOutputStream::Stop()";
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!started_)
+    return;
+
+  base::AutoLock lock(lock_);
+
+  // Stop playing by setting the play state to SL_PLAYSTATE_STOPPED.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*player_)->SetPlayState(player_, SL_PLAYSTATE_STOPPED));
+
+  // Clear the buffer queue so that the old data won't be played when
+  // resuming playing.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*simple_buffer_queue_)->Clear(simple_buffer_queue_));
+
+#ifndef NDEBUG
+  // Verify that the buffer queue is in fact cleared as it should.
+  SLAndroidSimpleBufferQueueState buffer_queue_state;
+  LOG_ON_FAILURE_AND_RETURN((*simple_buffer_queue_)->GetState(
+      simple_buffer_queue_, &buffer_queue_state));
+  DCHECK_EQ(0u, buffer_queue_state.count);
+  DCHECK_EQ(0u, buffer_queue_state.index);
+#endif
+
+  callback_ = nullptr;
+  started_ = false;
+}
+
+void OpenSLESOutputStream::Close() {
+  DVLOG(2) << "OpenSLESOutputStream::Close()";
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Stop the stream if it is still playing.
+  Stop();
+  {
+    // Destroy the buffer queue player object and invalidate all associated
+    // interfaces.
+    player_object_.Reset();
+    simple_buffer_queue_ = nullptr;
+    player_ = nullptr;
+
+    // Destroy the mixer object. We don't store any associated interface for
+    // this object.
+    output_mixer_.Reset();
+
+    // Destroy the engine object. We don't store any associated interface for
+    // this object.
+    engine_object_.Reset();
+    ReleaseAudioBuffer();
+  }
+
+  audio_manager_->ReleaseOutputStream(this);
+}
+
+// This stream is always used with sub second buffer sizes, where it's
+// sufficient to simply always flush upon Start().
+void OpenSLESOutputStream::Flush() {}
+
+void OpenSLESOutputStream::SetVolume(double volume) {
+  DVLOG(2) << "OpenSLESOutputStream::SetVolume(" << volume << ")";
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  double volume_override = 0;
+  if (audio_manager_->HasOutputVolumeOverride(&volume_override)) {
+    volume = volume_override;
+  }
+
+  float volume_float = static_cast<float>(volume);
+  if (volume_float < 0.0f || volume_float > 1.0f) {
+    return;
+  }
+  volume_ = volume_float;
+}
+
+void OpenSLESOutputStream::GetVolume(double* volume) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  *volume = static_cast<double>(volume_);
+}
+
+void OpenSLESOutputStream::SetMute(bool muted) {
+  DVLOG(2) << "OpenSLESOutputStream::SetMute(" << muted << ")";
+  DCHECK(thread_checker_.CalledOnValidThread());
+  muted_ = muted;
+}
+
+bool OpenSLESOutputStream::CreatePlayer() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!engine_object_.Get());
+  DCHECK(!player_object_.Get());
+  DCHECK(!output_mixer_.Get());
+  DCHECK(!player_);
+  DCHECK(!simple_buffer_queue_);
+
+  // Initializes the engine object with specific option. After working with the
+  // object, we need to free the object and its resources.
+  SLEngineOption option[] = {
+      {SL_ENGINEOPTION_THREADSAFE, static_cast<SLuint32>(SL_BOOLEAN_TRUE)}};
+  LOG_ON_FAILURE_AND_RETURN(
+      slCreateEngine(engine_object_.Receive(), 1, option, 0, nullptr, nullptr),
+      false);
+
+  // Realize the SL engine object in synchronous mode.
+  LOG_ON_FAILURE_AND_RETURN(
+      engine_object_->Realize(engine_object_.Get(), SL_BOOLEAN_FALSE), false);
+
+  // Get the SL engine interface which is implicit.
+  SLEngineItf engine;
+  LOG_ON_FAILURE_AND_RETURN(engine_object_->GetInterface(
+                                engine_object_.Get(), SL_IID_ENGINE, &engine),
+                            false);
+
+  // Create output mixer object to be used by the player.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*engine)->CreateOutputMix(engine, output_mixer_.Receive(), 0, nullptr,
+                                 nullptr),
+      false);
+
+  // Realizing the output mix object in synchronous mode.
+  LOG_ON_FAILURE_AND_RETURN(
+      output_mixer_->Realize(output_mixer_.Get(), SL_BOOLEAN_FALSE), false);
+
+  // Audio source configuration.
+  SLDataLocator_AndroidSimpleBufferQueue simple_buffer_queue = {
+      SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
+      static_cast<SLuint32>(kMaxNumOfBuffersInQueue)};
+  SLDataSource audio_source;
+  if (sample_format_ == kSampleFormatF32)
+    audio_source = {&simple_buffer_queue, &float_format_};
+  else
+    audio_source = {&simple_buffer_queue, &format_};
+
+  // Audio sink configuration.
+  SLDataLocator_OutputMix locator_output_mix = {SL_DATALOCATOR_OUTPUTMIX,
+                                                output_mixer_.Get()};
+  SLDataSink audio_sink = {&locator_output_mix, nullptr};
+
+  // Create an audio player.
+  const SLInterfaceID interface_id[] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME,
+                                        SL_IID_ANDROIDCONFIGURATION};
+  const SLboolean interface_required[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
+                                          SL_BOOLEAN_TRUE};
+  LOG_ON_FAILURE_AND_RETURN(
+      (*engine)->CreateAudioPlayer(
+          engine, player_object_.Receive(), &audio_source, &audio_sink,
+          base::size(interface_id), interface_id, interface_required),
+      false);
+
+  // Create AudioPlayer and specify SL_IID_ANDROIDCONFIGURATION.
+  SLAndroidConfigurationItf player_config;
+  LOG_ON_FAILURE_AND_RETURN(
+      player_object_->GetInterface(
+          player_object_.Get(), SL_IID_ANDROIDCONFIGURATION, &player_config),
+      false);
+
+  // Set configuration using the stream type provided at construction.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*player_config)
+          ->SetConfiguration(player_config, SL_ANDROID_KEY_STREAM_TYPE,
+                             &stream_type_, sizeof(SLint32)),
+      false);
+
+  // Set configuration using the stream type provided at construction.
+  if (performance_mode_ > SL_ANDROID_PERFORMANCE_NONE) {
+    LOG_ON_FAILURE_AND_RETURN(
+        (*player_config)
+            ->SetConfiguration(player_config, SL_ANDROID_KEY_PERFORMANCE_MODE,
+                               &performance_mode_, sizeof(SLuint32)),
+        false);
+  }
+
+  // Realize the player object in synchronous mode.
+  LOG_ON_FAILURE_AND_RETURN(
+      player_object_->Realize(player_object_.Get(), SL_BOOLEAN_FALSE), false);
+
+  // Get an implicit player interface.
+  LOG_ON_FAILURE_AND_RETURN(
+      player_object_->GetInterface(player_object_.Get(), SL_IID_PLAY, &player_),
+      false);
+
+  // Get the simple buffer queue interface.
+  LOG_ON_FAILURE_AND_RETURN(
+      player_object_->GetInterface(
+          player_object_.Get(), SL_IID_BUFFERQUEUE, &simple_buffer_queue_),
+      false);
+
+  // Register the input callback for the simple buffer queue.
+  // This callback will be called when the soundcard needs data.
+  LOG_ON_FAILURE_AND_RETURN(
+      (*simple_buffer_queue_)->RegisterCallback(
+          simple_buffer_queue_, SimpleBufferQueueCallback, this),
+      false);
+
+  return true;
+}
+
+void OpenSLESOutputStream::SimpleBufferQueueCallback(
+    SLAndroidSimpleBufferQueueItf buffer_queue,
+    void* instance) {
+  OpenSLESOutputStream* stream =
+      reinterpret_cast<OpenSLESOutputStream*>(instance);
+  stream->FillBufferQueue();
+}
+
+void OpenSLESOutputStream::FillBufferQueue() {
+  base::AutoLock lock(lock_);
+  if (!started_)
+    return;
+
+  TRACE_EVENT0("audio", "OpenSLESOutputStream::FillBufferQueue");
+
+  // Verify that we are in a playing state.
+  SLuint32 state;
+  SLresult err = (*player_)->GetPlayState(player_, &state);
+  if (SL_RESULT_SUCCESS != err) {
+    HandleError(err);
+    return;
+  }
+  if (state != SL_PLAYSTATE_PLAYING) {
+    DLOG(WARNING) << "Received callback in non-playing state";
+    return;
+  }
+
+  // Fill up one buffer in the queue by asking the registered source for
+  // data using the OnMoreData() callback.
+  FillBufferQueueNoLock();
+}
+
+void OpenSLESOutputStream::FillBufferQueueNoLock() {
+  // Ensure that the calling thread has acquired the lock since it is not
+  // done in this method.
+  lock_.AssertAcquired();
+
+  // Calculate the position relative to the number of frames written.
+  uint32_t position_in_ms = 0;
+  SLresult err = (*player_)->GetPosition(player_, &position_in_ms);
+
+  // Given the position of the playback head, compute the approximate number of
+  // frames that have been queued to the buffer but not yet played out.
+  // Note that the value returned by GetFramesToTarget() is negative because
+  // more frames have been added to |delay_calculator_| than have been played
+  // out and thus the target timestamp is earlier than the current timestamp of
+  // |delay_calculator_|.
+  const int delay_frames =
+      err == SL_RESULT_SUCCESS
+          ? -delay_calculator_.GetFramesToTarget(
+                AdjustPositionForHardwareLatency(position_in_ms))
+          : 0;
+  DCHECK_GE(delay_frames, 0);
+
+  // Note: *DO NOT* use format_.samplesPerSecond in any calculations, it is not
+  // actually the sample rate! See constructor comments. :|
+  const base::TimeDelta delay =
+      AudioTimestampHelper::FramesToTime(delay_frames, samples_per_second_);
+
+  // Read data from the registered client source.
+  const int frames_filled =
+      callback_->OnMoreData(delay, base::TimeTicks::Now(), 0, audio_bus_.get());
+  if (frames_filled <= 0) {
+    // Audio source is shutting down, or halted on error.
+    return;
+  }
+
+  // Note: If the internal representation ever changes from 16-bit PCM to
+  // raw float, the data must be clipped and sanitized since it may come
+  // from an untrusted source such as NaCl.
+  audio_bus_->Scale(muted_ ? 0.0f : volume_);
+  if (sample_format_ == kSampleFormatS16) {
+    audio_bus_->ToInterleaved<SignedInt16SampleTypeTraits>(
+        frames_filled,
+        reinterpret_cast<int16_t*>(audio_data_[active_buffer_index_]));
+  } else {
+    DCHECK_EQ(sample_format_, kSampleFormatF32);
+
+    // We skip clipping since that occurs at the shared memory boundary.
+    audio_bus_->ToInterleaved<Float32SampleTypeTraitsNoClip>(
+        frames_filled,
+        reinterpret_cast<float*>(audio_data_[active_buffer_index_]));
+  }
+
+  delay_calculator_.AddFrames(frames_filled);
+  const int num_filled_bytes = frames_filled * bytes_per_frame_;
+  DCHECK_LE(static_cast<size_t>(num_filled_bytes), buffer_size_bytes_);
+
+  // Enqueue the buffer for playback.
+  err = (*simple_buffer_queue_)
+            ->Enqueue(simple_buffer_queue_, audio_data_[active_buffer_index_],
+                      num_filled_bytes);
+  if (SL_RESULT_SUCCESS != err)
+    HandleError(err);
+
+  active_buffer_index_ = (active_buffer_index_ + 1) % kMaxNumOfBuffersInQueue;
+}
+
+void OpenSLESOutputStream::SetupAudioBuffer() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!audio_data_[0]);
+  for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i)
+    audio_data_[i] = new uint8_t[buffer_size_bytes_];
+}
+
+void OpenSLESOutputStream::ReleaseAudioBuffer() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (audio_data_[0]) {
+    for (int i = 0; i < kMaxNumOfBuffersInQueue; ++i) {
+      delete[] audio_data_[i];
+      audio_data_[i] = nullptr;
+    }
+  }
+}
+
+void OpenSLESOutputStream::HandleError(SLresult error) {
+  DLOG(ERROR) << "OpenSLES Output error " << error;
+  // TODO(dalecurtis): Consider sending a translated |error|.
+  if (callback_)
+    callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+}
+
+void OpenSLESOutputStream::CacheHardwareLatencyIfNeeded() {
+  // If the feature is turned off, then leave it at its default (zero) value.
+  // In general, GetOutputLatency is not reliable.
+  if (!base::FeatureList::IsEnabled(kUseAudioLatencyFromHAL))
+    return;
+
+  hardware_latency_ = audio_manager_->GetOutputLatency();
+}
+
+base::TimeDelta OpenSLESOutputStream::AdjustPositionForHardwareLatency(
+    uint32_t position_in_ms) {
+  base::TimeDelta position = base::Milliseconds(position_in_ms);
+
+  if (position <= hardware_latency_)
+    return base::Milliseconds(0);
+
+  return position - hardware_latency_;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/android/opensles_output.h b/third_party/chromium/media/audio/android/opensles_output.h
new file mode 100644
index 0000000..a932da9
--- /dev/null
+++ b/third_party/chromium/media/audio/android/opensles_output.h
@@ -0,0 +1,160 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ANDROID_OPENSLES_OUTPUT_H_
+#define MEDIA_AUDIO_ANDROID_OPENSLES_OUTPUT_H_
+
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/android/muteable_audio_output_stream.h"
+#include "media/audio/android/opensles_util.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/audio_timestamp_helper.h"
+
+namespace media {
+
+class AudioManagerAndroid;
+
+// Implements PCM audio output support for Android using the OpenSLES API.
+// This class is created and lives on the Audio Manager thread but recorded
+// audio buffers are given to us from an internal OpenSLES audio thread.
+// All public methods should be called on the Audio Manager thread.
+class OpenSLESOutputStream : public MuteableAudioOutputStream {
+ public:
+  static const int kMaxNumOfBuffersInQueue = 2;
+
+  OpenSLESOutputStream(AudioManagerAndroid* manager,
+                       const AudioParameters& params,
+                       SLint32 stream_type);
+
+  OpenSLESOutputStream(const OpenSLESOutputStream&) = delete;
+  OpenSLESOutputStream& operator=(const OpenSLESOutputStream&) = delete;
+
+  ~OpenSLESOutputStream() override;
+
+  // Implementation of MuteableAudioOutputStream.
+  bool Open() override;
+  void Close() override;
+  void Flush() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+
+  // Set the value of |muted_|. It does not affect |volume_| which can be
+  // got by calling GetVolume(). See comments for |muted_| below.
+  void SetMute(bool muted) override;
+
+ private:
+  bool CreatePlayer();
+
+  // Called from OpenSLES specific audio worker thread.
+  static void SimpleBufferQueueCallback(
+      SLAndroidSimpleBufferQueueItf buffer_queue,
+      void* instance);
+
+  // Fills up one buffer by asking the registered source for data.
+  // Called from OpenSLES specific audio worker thread.
+  void FillBufferQueue();
+
+  // Called from the audio manager thread.
+  void FillBufferQueueNoLock();
+
+  // Called in Open();
+  void SetupAudioBuffer();
+
+  // Called in Close();
+  void ReleaseAudioBuffer();
+
+  // If OpenSLES reports an error this function handles it and passes it to
+  // the attached AudioOutputCallback::OnError().
+  void HandleError(SLresult error);
+
+  // Cache |hardware_latency_in_ms_| by asking |audio_manager_| for it, if the
+  // kUseAudioLatencyFromHAL is enabled.
+  void CacheHardwareLatencyIfNeeded();
+
+  // Adjust |position_in_ms| for hardware latency, and return the result.
+  base::TimeDelta AdjustPositionForHardwareLatency(uint32_t position_in_ms);
+
+  base::ThreadChecker thread_checker_;
+
+  // Protects |callback_|, |active_buffer_index_|, |audio_data_|,
+  // |buffer_size_bytes_| and |simple_buffer_queue_|.
+  base::Lock lock_;
+
+  AudioManagerAndroid* audio_manager_;
+
+  // Audio playback stream type.
+  // See SLES/OpenSLES_Android.h for details.
+  SLint32 stream_type_;
+
+  AudioSourceCallback* callback_;
+
+  // Shared engine interfaces for the app.
+  media::ScopedSLObjectItf engine_object_;
+  media::ScopedSLObjectItf player_object_;
+  media::ScopedSLObjectItf output_mixer_;
+
+  SLPlayItf player_;
+
+  // Buffer queue recorder interface.
+  SLAndroidSimpleBufferQueueItf simple_buffer_queue_;
+
+  SLDataFormat_PCM format_;
+  SLAndroidDataFormat_PCM_EX float_format_;
+
+  // Audio buffers that are allocated during Open() based on parameters given
+  // during construction.
+  uint8_t* audio_data_[kMaxNumOfBuffersInQueue];
+
+  int active_buffer_index_;
+
+  bool started_;
+
+  // Volume control coming from hardware. It overrides |volume_| when it's
+  // true. Otherwise, use |volume_| for scaling.
+  // This is needed because platform voice volume never goes to zero in
+  // COMMUNICATION mode on Android.
+  bool muted_;
+
+  // Volume level from 0 to 1.
+  float volume_;
+
+  int samples_per_second_;
+
+  // On Android 5.0+ we can output directly to float instead of in integer, so
+  // there we'll use kSampleFormatF32. If not, this will be kSampleFormatS16.
+  SampleFormat sample_format_;
+
+  int bytes_per_frame_;
+  size_t buffer_size_bytes_;
+
+  // On API level 25+ we can provide hints to OpenSLES about what type of
+  // content the stream is being used for.
+  SLuint32 performance_mode_;
+
+  // Used to calculate the delay value for each OnMoreData() call.
+  AudioTimestampHelper delay_calculator_;
+
+  // Container for retrieving data from AudioSourceCallback::OnMoreData().
+  std::unique_ptr<AudioBus> audio_bus_;
+
+  // Adjustment for hardware latency.  Needed for some cast targets, since
+  // OpenSLES's GetPosition doesn't properly account for HAL latency.
+  base::TimeDelta hardware_latency_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ANDROID_OPENSLES_OUTPUT_H_
diff --git a/third_party/chromium/media/audio/android/opensles_util.cc b/third_party/chromium/media/audio/android/opensles_util.cc
new file mode 100644
index 0000000..128a3bd
--- /dev/null
+++ b/third_party/chromium/media/audio/android/opensles_util.cc
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/android/opensles_util.h"
+
+#include "base/logging.h"
+
+namespace media {
+
+#define SL_ANDROID_SPEAKER_QUAD                                            \
+  (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_BACK_LEFT | \
+   SL_SPEAKER_BACK_RIGHT)
+#define SL_ANDROID_SPEAKER_5DOT1                                              \
+  (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_FRONT_CENTER | \
+   SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
+#define SL_ANDROID_SPEAKER_7DOT1 \
+  (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
+
+// Ported from:
+// https://android.googlesource.com/platform/frameworks/wilhelm/+/refs/heads/master/src/android/channels.h
+// https://android.googlesource.com/platform/frameworks/wilhelm/+/refs/heads/master/src/android/channels.c
+SLuint32 ChannelCountToSLESChannelMask(int channel_count) {
+  if (channel_count > 2) {
+    LOG(WARNING) << "Guessing channel layout for " << channel_count
+                 << " channels; speaker order may be incorrect.";
+  }
+
+  switch (channel_count) {
+    case 1:
+      return SL_SPEAKER_FRONT_LEFT;
+    case 2:
+      return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+    case 3:
+      return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
+             SL_SPEAKER_FRONT_CENTER;
+    case 4:
+      return SL_ANDROID_SPEAKER_QUAD;
+    case 5:
+      return SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER;
+    case 6:
+      return SL_ANDROID_SPEAKER_5DOT1;
+    case 7:
+      return SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_BACK_CENTER;
+    case 8:
+      return SL_ANDROID_SPEAKER_7DOT1;
+  }
+
+  return 0;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/android/opensles_util.h b/third_party/chromium/media/audio/android/opensles_util.h
new file mode 100644
index 0000000..2e8d270
--- /dev/null
+++ b/third_party/chromium/media/audio/android/opensles_util.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_
+#define MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_
+
+#include <SLES/OpenSLES.h>
+
+#include "base/check.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+template <typename SLType, typename SLDerefType>
+class ScopedSLObject {
+ public:
+  ScopedSLObject() : obj_(NULL) {}
+
+  ~ScopedSLObject() { Reset(); }
+
+  SLType* Receive() {
+    DCHECK(!obj_);
+    return &obj_;
+  }
+
+  SLDerefType operator->() { return *obj_; }
+
+  SLType Get() const { return obj_; }
+
+  void Reset() {
+    if (obj_) {
+      (*obj_)->Destroy(obj_);
+      obj_ = NULL;
+    }
+  }
+
+ private:
+  SLType obj_;
+};
+
+typedef ScopedSLObject<SLObjectItf, const SLObjectItf_*> ScopedSLObjectItf;
+
+// Guesses the channel mask for a given channel count. Android does not offer a
+// way to configure the layout, so this will be incorrect for less common
+// channel layouts.
+MEDIA_EXPORT SLuint32 ChannelCountToSLESChannelMask(int channel_count);
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_ANDROID_OPENSLES_UTIL_H_
diff --git a/third_party/chromium/media/audio/android/opensles_wrapper.cc b/third_party/chromium/media/audio/android/opensles_wrapper.cc
new file mode 100644
index 0000000..ba7dba7
--- /dev/null
+++ b/third_party/chromium/media/audio/android/opensles_wrapper.cc
@@ -0,0 +1,123 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file defines the symbols from OpenSLES that android is using. It then
+// loads the library dynamically on first use.
+
+// The openSLES API is using constant as part of the API. This file will define
+// proxies for those constants and redefine those when the library is first
+// loaded. For this, it need to be able to change their content and so import
+// the headers without const.  This is correct because OpenSLES.h is a C API.
+
+// We include stdint.h here as a workaround for an issue caused by the
+// #define const below. The inclusion of OpenSLES headers on newer Android NDK
+// versions causes stdint.h to be included, which in turn includes __config.
+// This causes the declaration of __sanitizer_annotate_contiguous_container to
+// not use const parameters, which causes compile issues when building with
+// asan. Including here forces __config to be included while const is still
+// untouched.
+#include <stdint.h>
+
+#define const
+#include <SLES/OpenSLES.h>
+#include <SLES/OpenSLES_Android.h>
+#undef const
+
+#include <stddef.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/native_library.h"
+
+// The constants used in chromium. SLInterfaceID is actually a pointer to
+// SLInterfaceID_. Those symbols are defined as extern symbols in the OpenSLES
+// headers. They will be initialized to their correct values when the library is
+// loaded.
+SLInterfaceID SL_IID_ENGINE = NULL;
+SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE = NULL;
+SLInterfaceID SL_IID_ANDROIDCONFIGURATION = NULL;
+SLInterfaceID SL_IID_RECORD = NULL;
+SLInterfaceID SL_IID_BUFFERQUEUE = NULL;
+SLInterfaceID SL_IID_VOLUME = NULL;
+SLInterfaceID SL_IID_PLAY = NULL;
+
+namespace {
+
+// The name of the library to load.
+const char kOpenSLLibraryName[] = "libOpenSLES.so";
+
+// Loads the OpenSLES library, and initializes all the proxies.
+base::NativeLibrary IntializeLibraryHandle() {
+  base::NativeLibrary handle =
+      base::LoadNativeLibrary(base::FilePath(kOpenSLLibraryName), NULL);
+  if (!handle) {
+    DLOG(ERROR) << "Unable to load " << kOpenSLLibraryName;
+    return NULL;
+  }
+
+  // Setup the proxy for each symbol.
+  // Attach the symbol name to the proxy address.
+  struct SymbolDefinition {
+    const char* name;
+    SLInterfaceID* sl_iid;
+  };
+
+  // The list of defined symbols.
+  const SymbolDefinition kSymbols[] = {
+      {"SL_IID_ENGINE", &SL_IID_ENGINE},
+      {"SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &SL_IID_ANDROIDSIMPLEBUFFERQUEUE},
+      {"SL_IID_ANDROIDCONFIGURATION", &SL_IID_ANDROIDCONFIGURATION},
+      {"SL_IID_RECORD", &SL_IID_RECORD},
+      {"SL_IID_BUFFERQUEUE", &SL_IID_BUFFERQUEUE},
+      {"SL_IID_VOLUME", &SL_IID_VOLUME},
+      {"SL_IID_PLAY", &SL_IID_PLAY}};
+
+  for (size_t i = 0; i < sizeof(kSymbols) / sizeof(kSymbols[0]); ++i) {
+    void* func_ptr =
+        base::GetFunctionPointerFromNativeLibrary(handle, kSymbols[i].name);
+    if (!func_ptr) {
+      DLOG(ERROR) << "Unable to find symbol for " << kSymbols[i].name;
+      return NULL;
+    }
+    memcpy(kSymbols[i].sl_iid, func_ptr, sizeof(SLInterfaceID));
+  }
+  return handle;
+}
+
+// Returns the handler to the shared library. The library itself will be lazily
+// loaded during the first call to this function.
+base::NativeLibrary LibraryHandle() {
+  // The handle is lazily initialized on the first call.
+  static base::NativeLibrary g_handle = IntializeLibraryHandle();
+  return g_handle;
+}
+
+}  // namespace
+
+// Redefine slCreateEngine symbol.
+SLresult slCreateEngine(SLObjectItf* engine,
+                        SLuint32 num_options,
+                        SLEngineOption* engine_options,
+                        SLuint32 num_interfaces,
+                        SLInterfaceID* interface_ids,
+                        SLboolean* interfaces_required) {
+  typedef SLresult (*SlCreateEngineSignature)(SLObjectItf*, SLuint32,
+                                              SLEngineOption*, SLuint32,
+                                              SLInterfaceID*, SLboolean*);
+  base::NativeLibrary handle = LibraryHandle();
+  if (!handle)
+    return SL_RESULT_INTERNAL_ERROR;
+
+  static SlCreateEngineSignature g_sl_create_engine_handle =
+      reinterpret_cast<SlCreateEngineSignature>(
+          base::GetFunctionPointerFromNativeLibrary(handle, "slCreateEngine"));
+  if (!g_sl_create_engine_handle) {
+    DLOG(ERROR) << "Unable to find symbol for slCreateEngine";
+    return SL_RESULT_INTERNAL_ERROR;
+  }
+
+  return g_sl_create_engine_handle(engine, num_options, engine_options,
+                                   num_interfaces, interface_ids,
+                                   interfaces_required);
+}
diff --git a/third_party/chromium/media/audio/audio_debug_file_writer.cc b/third_party/chromium/media/audio/audio_debug_file_writer.cc
new file mode 100644
index 0000000..8fd566a
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_file_writer.cc
@@ -0,0 +1,306 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_debug_file_writer.h"
+
+#include <stdint.h>
+#include <array>
+#include <limits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/sys_byteorder.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_sample_types.h"
+
+namespace media {
+
+namespace {
+
+// Windows WAVE format header
+// Byte order: Little-endian
+// Offset Length  Content
+//  0      4     "RIFF"
+//  4      4     <file length - 8>
+//  8      4     "WAVE"
+// 12      4     "fmt "
+// 16      4     <length of the fmt data> (=16)
+// 20      2     <WAVE file encoding tag>
+// 22      2     <channels>
+// 24      4     <sample rate>
+// 28      4     <bytes per second> (sample rate * block align)
+// 32      2     <block align>  (channels * bits per sample / 8)
+// 34      2     <bits per sample>
+// 36      4     "data"
+// 40      4     <sample data size(n)>
+// 44     (n)    <sample data>
+
+// We write 16 bit PCM only.
+static const uint16_t kBytesPerSample = 2;
+
+static const uint32_t kWavHeaderSize = 44;
+static const uint32_t kFmtChunkSize = 16;
+// 4 bytes for ID + 4 bytes for size.
+static const uint32_t kChunkHeaderSize = 8;
+static const uint16_t kWavFormatPcm = 1;
+
+static const char kRiff[] = {'R', 'I', 'F', 'F'};
+static const char kWave[] = {'W', 'A', 'V', 'E'};
+static const char kFmt[] = {'f', 'm', 't', ' '};
+static const char kData[] = {'d', 'a', 't', 'a'};
+
+typedef std::array<char, kWavHeaderSize> WavHeaderBuffer;
+
+class CharBufferWriter {
+ public:
+  CharBufferWriter(char* buf, int max_size)
+      : buf_(buf), max_size_(max_size), size_(0) {}
+
+  void Write(const char* data, int data_size) {
+    CHECK_LE(size_ + data_size, max_size_);
+    memcpy(&buf_[size_], data, data_size);
+    size_ += data_size;
+  }
+
+  void Write(const char (&data)[4]) {
+    Write(static_cast<const char*>(data), 4);
+  }
+
+  void WriteLE16(uint16_t data) {
+    uint16_t val = base::ByteSwapToLE16(data);
+    Write(reinterpret_cast<const char*>(&val), sizeof(val));
+  }
+
+  void WriteLE32(uint32_t data) {
+    uint32_t val = base::ByteSwapToLE32(data);
+    Write(reinterpret_cast<const char*>(&val), sizeof(val));
+  }
+
+ private:
+  char* buf_;
+  const int max_size_;
+  int size_;
+
+  DISALLOW_COPY_AND_ASSIGN(CharBufferWriter);
+};
+
+// Writes Wave header to the specified address, there should be at least
+// kWavHeaderSize bytes allocated for it.
+void WriteWavHeader(WavHeaderBuffer* buf,
+                    uint32_t channels,
+                    uint32_t sample_rate,
+                    uint64_t samples) {
+  // We'll need to add (kWavHeaderSize - kChunkHeaderSize) to payload to
+  // calculate Riff chunk size.
+  static const uint32_t kMaxBytesInPayload =
+      std::numeric_limits<uint32_t>::max() -
+      (kWavHeaderSize - kChunkHeaderSize);
+  const uint64_t bytes_in_payload_64 = samples * kBytesPerSample;
+
+  // In case payload is too large and causes uint32_t overflow, we just specify
+  // the maximum possible value; all the payload above that count will be
+  // interpreted as garbage.
+  const uint32_t bytes_in_payload = bytes_in_payload_64 > kMaxBytesInPayload
+                                        ? kMaxBytesInPayload
+                                        : bytes_in_payload_64;
+  LOG_IF(WARNING, bytes_in_payload < bytes_in_payload_64)
+      << "Number of samples is too large and will be clipped by Wave header,"
+      << " all the data above " << kMaxBytesInPayload
+      << " bytes will appear as junk";
+  const uint32_t block_align = channels * kBytesPerSample;
+  const uint32_t byte_rate = channels * sample_rate * kBytesPerSample;
+  const uint32_t riff_chunk_size =
+      bytes_in_payload + kWavHeaderSize - kChunkHeaderSize;
+
+  CharBufferWriter writer(&(*buf)[0], kWavHeaderSize);
+
+  writer.Write(kRiff);
+  writer.WriteLE32(riff_chunk_size);
+  writer.Write(kWave);
+  writer.Write(kFmt);
+  writer.WriteLE32(kFmtChunkSize);
+  writer.WriteLE16(kWavFormatPcm);
+  writer.WriteLE16(channels);
+  writer.WriteLE32(sample_rate);
+  writer.WriteLE32(byte_rate);
+  writer.WriteLE16(block_align);
+  writer.WriteLE16(kBytesPerSample * 8);
+  writer.Write(kData);
+  writer.WriteLE32(bytes_in_payload);
+}
+
+}  // namespace
+
+// Manages the debug recording file and writes to it. Can be created on any
+// thread. All the operations must be executed on a thread that has IO
+// permissions.
+class AudioDebugFileWriter::AudioFileWriter {
+ public:
+  static AudioFileWriterUniquePtr Create(
+      base::File file,
+      const AudioParameters& params,
+      scoped_refptr<base::SequencedTaskRunner> task_runner);
+
+  ~AudioFileWriter();
+
+  // Write data from |data| to file.
+  void Write(const AudioBus* data);
+
+ private:
+  explicit AudioFileWriter(const AudioParameters& params);
+
+  // Write wave header to file. Called on the |task_runner_| twice: on
+  // construction
+  // of AudioFileWriter size of the wave data is unknown, so the header is
+  // written with zero sizes; then on destruction it is re-written with the
+  // actual size info accumulated throughout the object lifetime.
+  void WriteHeader();
+
+  void StartRecording(base::File file);
+
+  // The file to write to.
+  base::File file_;
+
+  // Number of written samples.
+  uint64_t samples_;
+
+  // Audio parameters required to build wave header. Number of channels and
+  // sample rate are used.
+  const AudioParameters params_;
+
+  // Intermediate buffer to be written to file. Interleaved 16 bit audio data.
+  std::unique_ptr<int16_t[]> interleaved_data_;
+  int interleaved_data_size_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+// static
+AudioDebugFileWriter::AudioFileWriterUniquePtr
+AudioDebugFileWriter::AudioFileWriter::Create(
+    base::File file,
+    const AudioParameters& params,
+    scoped_refptr<base::SequencedTaskRunner> task_runner) {
+  AudioFileWriterUniquePtr file_writer(new AudioFileWriter(params),
+                                       base::OnTaskRunnerDeleter(task_runner));
+
+  // base::Unretained is safe, because destructor is called on
+  // |task_runner|.
+  task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(&AudioFileWriter::StartRecording,
+                     base::Unretained(file_writer.get()), std::move(file)));
+  return file_writer;
+}
+
+AudioDebugFileWriter::AudioFileWriter::AudioFileWriter(
+    const AudioParameters& params)
+    : samples_(0), params_(params), interleaved_data_size_(0) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+AudioDebugFileWriter::AudioFileWriter::~AudioFileWriter() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (file_.IsValid())
+    WriteHeader();
+}
+
+void AudioDebugFileWriter::AudioFileWriter::Write(const AudioBus* data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(params_.channels(), data->channels());
+  if (!file_.IsValid())
+    return;
+
+  // Convert to 16 bit audio and write to file.
+  int data_size = data->frames() * data->channels();
+  if (!interleaved_data_ || interleaved_data_size_ < data_size) {
+    interleaved_data_.reset(new int16_t[data_size]);
+    interleaved_data_size_ = data_size;
+  }
+  samples_ += data_size;
+  data->ToInterleaved<media::SignedInt16SampleTypeTraits>(
+      data->frames(), interleaved_data_.get());
+
+#ifndef ARCH_CPU_LITTLE_ENDIAN
+  static_assert(sizeof(interleaved_data_[0]) == sizeof(uint16_t),
+                "Only 2 bytes per channel is supported.");
+  for (int i = 0; i < data_size; ++i)
+    interleaved_data_[i] = base::ByteSwapToLE16(interleaved_data_[i]);
+#endif
+
+  file_.WriteAtCurrentPos(reinterpret_cast<char*>(interleaved_data_.get()),
+                          data_size * sizeof(interleaved_data_[0]));
+}
+
+void AudioDebugFileWriter::AudioFileWriter::WriteHeader() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!file_.IsValid())
+    return;
+  WavHeaderBuffer buf;
+  WriteWavHeader(&buf, params_.channels(), params_.sample_rate(), samples_);
+  file_.Write(0, &buf[0], kWavHeaderSize);
+
+  // Write() does not move the cursor if file is not in APPEND mode; Seek() so
+  // that the header is not overwritten by the following writes.
+  file_.Seek(base::File::FROM_BEGIN, kWavHeaderSize);
+}
+
+void AudioDebugFileWriter::AudioFileWriter::StartRecording(base::File file) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!file_.IsValid());
+
+  file_ = std::move(file);
+  WriteHeader();
+}
+
+AudioDebugFileWriter::AudioDebugFileWriter(const AudioParameters& params)
+    : params_(params),
+      file_writer_(nullptr, base::OnTaskRunnerDeleter(nullptr)) {
+  DETACH_FROM_SEQUENCE(client_sequence_checker_);
+}
+
+AudioDebugFileWriter::~AudioDebugFileWriter() {
+  // |file_writer_| will be deleted on |task_runner_|.
+}
+
+void AudioDebugFileWriter::Start(base::File file) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+  DCHECK(!file_writer_);
+  file_writer_ =
+      AudioFileWriter::Create(std::move(file), params_, file_task_runner_);
+}
+
+void AudioDebugFileWriter::Stop() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+  // |file_writer_| is deleted on FILE thread.
+  file_writer_.reset();
+  DETACH_FROM_SEQUENCE(client_sequence_checker_);
+}
+
+void AudioDebugFileWriter::Write(std::unique_ptr<AudioBus> data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+  if (!file_writer_)
+    return;
+
+  // base::Unretained for |file_writer_| is safe, see the destructor.
+  file_task_runner_->PostTask(
+      FROM_HERE,
+      // Callback takes ownership of |data|:
+      base::BindOnce(&AudioFileWriter::Write,
+                     base::Unretained(file_writer_.get()),
+                     base::Owned(data.release())));
+}
+
+bool AudioDebugFileWriter::WillWrite() {
+  // Note that if this is called from any place other than
+  // |client_sequence_checker_| then there is a data race here, but it's fine,
+  // because Write() will check for |file_writer_|. So, we are not very precise
+  // here, but it's fine: we can afford missing some data or scheduling some
+  // no-op writes.
+  return !!file_writer_;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_debug_file_writer.h b/third_party/chromium/media/audio/audio_debug_file_writer.h
new file mode 100644
index 0000000..bf073c7
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_file_writer.h
@@ -0,0 +1,80 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEBUG_FILE_WRITER_H_
+#define MEDIA_AUDIO_AUDIO_DEBUG_FILE_WRITER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/files/file.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/task/thread_pool.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class AudioBus;
+
+// Writes audio data to a 16 bit PCM WAVE file used for debugging purposes. All
+// operations are non-blocking.
+// Functions are virtual for the purpose of test mocking.
+class MEDIA_EXPORT AudioDebugFileWriter {
+ public:
+  // Number of channels and sample rate are used from |params|, the other
+  // parameters are ignored. The number of channels in the data passed to
+  // Write() must match |params|.
+  explicit AudioDebugFileWriter(const AudioParameters& params);
+
+  AudioDebugFileWriter(const AudioDebugFileWriter&) = delete;
+  AudioDebugFileWriter& operator=(const AudioDebugFileWriter&) = delete;
+
+  virtual ~AudioDebugFileWriter();
+
+  // Must be called before calling Write() for the first time after creation or
+  // Stop() call. Can be called on any sequence; Write() and Stop() must be
+  // called on the same sequence as Start().
+  virtual void Start(base::File file);
+
+  // Must be called to finish recording. Each call to Start() requires a call to
+  // Stop(). Will be automatically called on destruction.
+  virtual void Stop();
+
+  // Write |data| to file.
+  virtual void Write(std::unique_ptr<AudioBus> data);
+
+  // Returns true if Write() call scheduled at this point will most likely write
+  // data to the file, and false if it most likely will be a no-op. The result
+  // may be ambigulous if Start() or Stop() is executed at the moment. Can be
+  // called from any sequence.
+  virtual bool WillWrite();
+
+ protected:
+  const AudioParameters params_;
+
+ private:
+  class AudioFileWriter;
+
+  using AudioFileWriterUniquePtr =
+      std::unique_ptr<AudioFileWriter, base::OnTaskRunnerDeleter>;
+
+  // The task runner to do file output operations on.
+  const scoped_refptr<base::SequencedTaskRunner> file_task_runner_ =
+      base::ThreadPool::CreateSequencedTaskRunner(
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+           base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
+
+  AudioFileWriterUniquePtr file_writer_;
+  SEQUENCE_CHECKER(client_sequence_checker_);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_DEBUG_FILE_WRITER_H_
diff --git a/third_party/chromium/media/audio/audio_debug_file_writer_unittest.cc b/third_party/chromium/media/audio/audio_debug_file_writer_unittest.cc
new file mode 100644
index 0000000..0ad0b6e
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_file_writer_unittest.cc
@@ -0,0 +1,354 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/sys_byteorder.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread.h"
+#include "media/audio/audio_debug_file_writer.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_sample_types.h"
+#include "media/base/test_helpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+static const uint16_t kBytesPerSample = sizeof(uint16_t);
+static const uint16_t kPcmEncoding = 1;
+static const size_t kWavHeaderSize = 44;
+
+uint16_t ReadLE2(const char* buf) {
+  return static_cast<uint8_t>(buf[0]) | (static_cast<uint8_t>(buf[1]) << 8);
+}
+
+uint32_t ReadLE4(const char* buf) {
+  return static_cast<uint8_t>(buf[0]) | (static_cast<uint8_t>(buf[1]) << 8) |
+         (static_cast<uint8_t>(buf[2]) << 16) |
+         (static_cast<uint8_t>(buf[3]) << 24);
+}
+
+base::File OpenFile(const base::FilePath& file_path) {
+  return base::File(file_path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
+}
+
+}  // namespace
+
+// <channel layout, sample rate, frames per buffer, number of buffer writes
+typedef std::tuple<ChannelLayout, int, int, int> AudioDebugFileWriterTestData;
+
+class AudioDebugFileWriterTest
+    : public testing::TestWithParam<AudioDebugFileWriterTestData> {
+ public:
+  explicit AudioDebugFileWriterTest(
+      base::test::TaskEnvironment::ThreadPoolExecutionMode execution_mode)
+      : task_environment_(base::test::TaskEnvironment::MainThreadType::DEFAULT,
+                          execution_mode),
+        params_(AudioParameters::Format::AUDIO_PCM_LINEAR,
+                std::get<0>(GetParam()),
+                std::get<1>(GetParam()),
+                std::get<2>(GetParam())),
+        writes_(std::get<3>(GetParam())),
+        source_samples_(params_.frames_per_buffer() * params_.channels() *
+                        writes_),
+        source_interleaved_(source_samples_ ? new int16_t[source_samples_]
+                                            : nullptr) {
+    InitSourceInterleaved(source_interleaved_.get(), source_samples_);
+  }
+  AudioDebugFileWriterTest()
+      : AudioDebugFileWriterTest(
+            base::test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC) {}
+
+ protected:
+  virtual ~AudioDebugFileWriterTest() = default;
+
+  static void InitSourceInterleaved(int16_t* source_interleaved,
+                                    int source_samples) {
+    if (source_samples) {
+      // equal steps to cover int16_t range of values
+      int16_t step = 0xffff / source_samples;
+      int16_t val = std::numeric_limits<int16_t>::min();
+      for (int i = 0; i < source_samples; ++i, val += step)
+        source_interleaved[i] = val;
+    }
+  }
+
+  static void VerifyHeader(const char (&wav_header)[kWavHeaderSize],
+                           const AudioParameters& params,
+                           int writes,
+                           int64_t file_length) {
+    uint32_t block_align = params.channels() * kBytesPerSample;
+    uint32_t data_size =
+        static_cast<uint32_t>(params.frames_per_buffer() * params.channels() *
+                              writes * kBytesPerSample);
+    // Offset Length  Content
+    //  0      4     "RIFF"
+    EXPECT_EQ(0, strncmp(wav_header, "RIFF", 4));
+    //  4      4     <file length - 8>
+    ASSERT_GT(file_length, 8);
+    EXPECT_EQ(static_cast<uint64_t>(file_length - 8), ReadLE4(wav_header + 4));
+    EXPECT_EQ(static_cast<uint32_t>(data_size + kWavHeaderSize - 8),
+              ReadLE4(wav_header + 4));
+    //  8      4     "WAVE"
+    // 12      4     "fmt "
+    EXPECT_EQ(0, strncmp(wav_header + 8, "WAVEfmt ", 8));
+    // 16      4     <length of the fmt data> (=16)
+    EXPECT_EQ(16U, ReadLE4(wav_header + 16));
+    // 20      2     <WAVE file encoding tag>
+    EXPECT_EQ(kPcmEncoding, ReadLE2(wav_header + 20));
+    // 22      2     <channels>
+    EXPECT_EQ(params.channels(), ReadLE2(wav_header + 22));
+    // 24      4     <sample rate>
+    EXPECT_EQ(static_cast<uint32_t>(params.sample_rate()),
+              ReadLE4(wav_header + 24));
+    // 28      4     <bytes per second> (sample rate * block align)
+    EXPECT_EQ(static_cast<uint32_t>(params.sample_rate()) * block_align,
+              ReadLE4(wav_header + 28));
+    // 32      2     <block align>  (channels * bits per sample / 8)
+    EXPECT_EQ(block_align, ReadLE2(wav_header + 32));
+    // 34      2     <bits per sample>
+    EXPECT_EQ(kBytesPerSample * 8, ReadLE2(wav_header + 34));
+    // 36      4     "data"
+    EXPECT_EQ(0, strncmp(wav_header + 36, "data", 4));
+    // 40      4     <sample data size(n)>
+    EXPECT_EQ(data_size, ReadLE4(wav_header + 40));
+  }
+
+  // |result_interleaved| is expected to be little-endian.
+  static void VerifyDataRecording(const int16_t* source_interleaved,
+                                  const int16_t* result_interleaved,
+                                  int16_t source_samples) {
+    // Allow mismatch by 1 due to rounding error in int->float->int
+    // calculations.
+    for (int i = 0; i < source_samples; ++i)
+      EXPECT_LE(std::abs(static_cast<int16_t>(
+                             base::ByteSwapToLE16(source_interleaved[i])) -
+                         result_interleaved[i]),
+                1)
+          << "i = " << i << " source " << source_interleaved[i] << " result "
+          << result_interleaved[i];
+  }
+
+  void VerifyRecording(const base::FilePath& file_path) {
+    base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+    ASSERT_TRUE(file.IsValid());
+
+    char wav_header[kWavHeaderSize];
+    EXPECT_EQ(file.Read(0, wav_header, kWavHeaderSize),
+              static_cast<int>(kWavHeaderSize));
+    VerifyHeader(wav_header, params_, writes_, file.GetLength());
+
+    if (source_samples_ > 0) {
+      std::unique_ptr<int16_t[]> result_interleaved(
+          new int16_t[source_samples_]);
+      memset(result_interleaved.get(), 0, source_samples_ * kBytesPerSample);
+
+      // Recording is read from file as a byte sequence, so it stored as
+      // little-endian.
+      int read = file.Read(kWavHeaderSize,
+                           reinterpret_cast<char*>(result_interleaved.get()),
+                           source_samples_ * kBytesPerSample);
+      EXPECT_EQ(static_cast<int>(file.GetLength() - kWavHeaderSize), read);
+
+      VerifyDataRecording(source_interleaved_.get(), result_interleaved.get(),
+                          source_samples_);
+    }
+  }
+
+  void DoDebugRecording() {
+    for (int i = 0; i < writes_; ++i) {
+      std::unique_ptr<AudioBus> bus =
+          AudioBus::Create(params_.channels(), params_.frames_per_buffer());
+
+      bus->FromInterleaved<media::SignedInt16SampleTypeTraits>(
+          source_interleaved_.get() +
+              i * params_.channels() * params_.frames_per_buffer(),
+          params_.frames_per_buffer());
+
+      debug_writer_->Write(std::move(bus));
+    }
+  }
+
+  void RecordAndVerifyOnce() {
+    base::FilePath file_path;
+    ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
+    base::File file = OpenFile(file_path);
+    ASSERT_TRUE(file.IsValid());
+
+    debug_writer_->Start(std::move(file));
+
+    DoDebugRecording();
+
+    debug_writer_->Stop();
+
+    task_environment_.RunUntilIdle();
+
+    VerifyRecording(file_path);
+
+    if (::testing::Test::HasFailure()) {
+      LOG(ERROR) << "Test failed; keeping recording(s) at ["
+                 << file_path.value().c_str() << "].";
+    } else {
+      ASSERT_TRUE(base::DeleteFile(file_path));
+    }
+  }
+
+ protected:
+  // The test task environment.
+  base::test::TaskEnvironment task_environment_;
+
+  // Writer under test.
+  std::unique_ptr<AudioDebugFileWriter> debug_writer_;
+
+  // AudioBus parameters.
+  AudioParameters params_;
+
+  // Number of times to write AudioBus to the file.
+  int writes_;
+
+  // Number of samples in the source data.
+  int source_samples_;
+
+  // Source data.
+  std::unique_ptr<int16_t[]> source_interleaved_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AudioDebugFileWriterTest);
+};
+
+class AudioDebugFileWriterBehavioralTest : public AudioDebugFileWriterTest {};
+
+class AudioDebugFileWriterSingleThreadTest : public AudioDebugFileWriterTest {
+ public:
+  AudioDebugFileWriterSingleThreadTest()
+      : AudioDebugFileWriterTest(
+            base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED) {}
+};
+
+TEST_P(AudioDebugFileWriterTest, WaveRecordingTest) {
+  debug_writer_ = std::make_unique<AudioDebugFileWriter>(params_);
+  RecordAndVerifyOnce();
+}
+
+TEST_P(AudioDebugFileWriterSingleThreadTest,
+       DeletedBeforeRecordingFinishedOnFileThread) {
+  debug_writer_ = std::make_unique<AudioDebugFileWriter>(params_);
+
+  base::FilePath file_path;
+  ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
+  base::File file = OpenFile(file_path);
+  ASSERT_TRUE(file.IsValid());
+
+  debug_writer_->Start(std::move(file));
+
+  DoDebugRecording();
+
+  debug_writer_.reset();
+
+  task_environment_.RunUntilIdle();
+
+  VerifyRecording(file_path);
+
+  if (::testing::Test::HasFailure()) {
+    LOG(ERROR) << "Test failed; keeping recording(s) at ["
+               << file_path.value().c_str() << "].";
+  } else {
+    ASSERT_TRUE(base::DeleteFile(file_path));
+  }
+}
+
+TEST_P(AudioDebugFileWriterBehavioralTest, StartWithInvalidFile) {
+  debug_writer_ = std::make_unique<AudioDebugFileWriter>(params_);
+  base::File file;  // Invalid file, recording should not crash
+  debug_writer_->Start(std::move(file));
+  DoDebugRecording();
+}
+
+TEST_P(AudioDebugFileWriterBehavioralTest, StartStopStartStop) {
+  debug_writer_ = std::make_unique<AudioDebugFileWriter>(params_);
+  RecordAndVerifyOnce();
+  RecordAndVerifyOnce();
+}
+
+TEST_P(AudioDebugFileWriterBehavioralTest, DestroyNotStarted) {
+  debug_writer_ = std::make_unique<AudioDebugFileWriter>(params_);
+  debug_writer_.reset();
+}
+
+TEST_P(AudioDebugFileWriterBehavioralTest, DestroyStarted) {
+  debug_writer_ = std::make_unique<AudioDebugFileWriter>(params_);
+  base::FilePath file_path;
+  ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
+  base::File file = OpenFile(file_path);
+  ASSERT_TRUE(file.IsValid());
+  debug_writer_->Start(std::move(file));
+  debug_writer_.reset();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AudioDebugFileWriterTest,
+    AudioDebugFileWriterTest,
+    // Using 10ms frames per buffer everywhere.
+    testing::Values(
+        // No writes.
+        std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
+                        44100,
+                        44100 / 100,
+                        0),
+        // 1 write of mono.
+        std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
+                        44100,
+                        44100 / 100,
+                        1),
+        // 1 second of mono.
+        std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
+                        44100,
+                        44100 / 100,
+                        100),
+        // 1 second of mono, higher rate.
+        std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
+                        48000,
+                        48000 / 100,
+                        100),
+        // 1 second of stereo.
+        std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_STEREO,
+                        44100,
+                        44100 / 100,
+                        100),
+        // 15 seconds of stereo, higher rate.
+        std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_STEREO,
+                        48000,
+                        48000 / 100,
+                        1500)));
+
+INSTANTIATE_TEST_SUITE_P(AudioDebugFileWriterBehavioralTest,
+                         AudioDebugFileWriterBehavioralTest,
+                         // Using 10ms frames per buffer everywhere.
+                         testing::Values(
+                             // No writes.
+                             std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
+                                             44100,
+                                             44100 / 100,
+                                             100)));
+
+INSTANTIATE_TEST_SUITE_P(AudioDebugFileWriterSingleThreadTest,
+                         AudioDebugFileWriterSingleThreadTest,
+                         // Using 10ms frames per buffer everywhere.
+                         testing::Values(
+                             // No writes.
+                             std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
+                                             44100,
+                                             44100 / 100,
+                                             100)));
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_debug_recording_helper.cc b/third_party/chromium/media/audio/audio_debug_recording_helper.cc
new file mode 100644
index 0000000..7b0341d
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_helper.cc
@@ -0,0 +1,117 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_debug_recording_helper.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "media/audio/audio_debug_file_writer.h"
+
+namespace media {
+
+AudioDebugRecordingHelper::AudioDebugRecordingHelper(
+    const AudioParameters& params,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    base::OnceClosure on_destruction_closure)
+    : params_(params),
+      recording_enabled_(0),
+      task_runner_(std::move(task_runner)),
+      on_destruction_closure_(std::move(on_destruction_closure)) {}
+
+AudioDebugRecordingHelper::~AudioDebugRecordingHelper() {
+  if (on_destruction_closure_)
+    std::move(on_destruction_closure_).Run();
+}
+
+void AudioDebugRecordingHelper::EnableDebugRecording(
+    AudioDebugRecordingStreamType stream_type,
+    uint32_t id,
+    CreateWavFileCallback create_file_callback) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(!debug_writer_);
+
+  debug_writer_ = CreateAudioDebugFileWriter(params_);
+  std::move(create_file_callback)
+      .Run(stream_type, id,
+           base::BindOnce(&AudioDebugRecordingHelper::StartDebugRecordingToFile,
+                          weak_factory_.GetWeakPtr()));
+}
+
+void AudioDebugRecordingHelper::StartDebugRecordingToFile(base::File file) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  if (!file.IsValid()) {
+    PLOG(ERROR) << "Invalid debug recording file, error="
+                << file.error_details();
+    debug_writer_.reset();
+    return;
+  }
+
+  debug_writer_->Start(std::move(file));
+
+  base::subtle::NoBarrier_Store(&recording_enabled_, 1);
+}
+
+void AudioDebugRecordingHelper::DisableDebugRecording() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  base::subtle::NoBarrier_Store(&recording_enabled_, 0);
+
+  if (debug_writer_) {
+    debug_writer_->Stop();
+    debug_writer_.reset();
+  }
+}
+
+void AudioDebugRecordingHelper::OnData(const AudioBus* source) {
+  // Check if debug recording is enabled to avoid an unecessary copy and thread
+  // jump if not. Recording can be disabled between the atomic Load() here and
+  // DoWrite(), but it's fine with a single unnecessary copy+jump at disable
+  // time. We use an atomic operation for accessing the flag on different
+  // threads. No memory barrier is needed for the same reason; a race is no
+  // problem at enable and disable time. Missing one buffer of data doesn't
+  // matter.
+  base::subtle::Atomic32 recording_enabled =
+      base::subtle::NoBarrier_Load(&recording_enabled_);
+  if (!recording_enabled)
+    return;
+
+  // TODO(tommi): This is costly. AudioBus heap allocs and we create a new one
+  // for every callback. We could instead have a pool of bus objects that get
+  // returned to us somehow.
+  // We should also avoid calling PostTask here since the implementation of the
+  // debug writer will basically do a PostTask straight away anyway. Might
+  // require some modifications to AudioDebugFileWriter though since there are
+  // some threading concerns there and AudioDebugFileWriter's lifetime
+  // guarantees need to be longer than that of associated active audio streams.
+  std::unique_ptr<AudioBus> audio_bus_copy =
+      AudioBus::Create(source->channels(), source->frames());
+  source->CopyTo(audio_bus_copy.get());
+
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&AudioDebugRecordingHelper::DoWrite,
+                     weak_factory_.GetWeakPtr(), std::move(audio_bus_copy)));
+}
+
+void AudioDebugRecordingHelper::DoWrite(std::unique_ptr<media::AudioBus> data) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  if (debug_writer_)
+    debug_writer_->Write(std::move(data));
+}
+
+std::unique_ptr<AudioDebugFileWriter>
+AudioDebugRecordingHelper::CreateAudioDebugFileWriter(
+    const AudioParameters& params) {
+  return std::make_unique<AudioDebugFileWriter>(params);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_debug_recording_helper.h b/third_party/chromium/media/audio/audio_debug_recording_helper.h
new file mode 100644
index 0000000..86d2ed7
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_helper.h
@@ -0,0 +1,115 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_HELPER_H_
+#define MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_HELPER_H_
+
+#include <memory>
+
+#include "base/atomicops.h"
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/audio_debug_file_writer.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+
+namespace base {
+class File;
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+
+class AudioBus;
+
+enum class AudioDebugRecordingStreamType { kInput = 0, kOutput = 1 };
+
+// Interface for feeding data to a recorder.
+class AudioDebugRecorder {
+ public:
+  virtual ~AudioDebugRecorder() {}
+
+  // If debug recording is enabled, copies audio data and makes sure it's
+  // written on the right thread. Otherwise ignores the data. Can be called on
+  // any thread.
+  virtual void OnData(const AudioBus* source) = 0;
+};
+
+// A helper class for those who want to use AudioDebugFileWriter. It handles
+// copying AudioBus data, thread jump (OnData() can be called on any
+// thread), and creating and deleting the AudioDebugFileWriter at enable and
+// disable. All functions except OnData() must be called on the thread
+// |task_runner| belongs to.
+// TODO(grunell): When input debug recording is moved to AudioManager, it should
+// be possible to merge this class into AudioDebugFileWriter. One thread jump
+// could be skipped then. Currently we have
+// soundcard thread -> control thread -> file thread,
+// and with the merge we should be able to do
+// soundcard thread -> file thread.
+class MEDIA_EXPORT AudioDebugRecordingHelper : public AudioDebugRecorder {
+ public:
+  using CreateWavFileCallback = base::OnceCallback<void(
+      AudioDebugRecordingStreamType stream_type,
+      uint32_t id,
+      base::OnceCallback<void(base::File)> reply_callback)>;
+
+  AudioDebugRecordingHelper(
+      const AudioParameters& params,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      base::OnceClosure on_destruction_closure);
+
+  AudioDebugRecordingHelper(const AudioDebugRecordingHelper&) = delete;
+  AudioDebugRecordingHelper& operator=(const AudioDebugRecordingHelper&) =
+      delete;
+
+  ~AudioDebugRecordingHelper() override;
+
+  // Enable debug recording. Creates |debug_writer_| and runs
+  // |create_file_callback| to create debug recording file.
+  virtual void EnableDebugRecording(AudioDebugRecordingStreamType stream_type,
+                                    uint32_t id,
+                                    CreateWavFileCallback create_file_callback);
+
+  // Disable debug recording. Destroys |debug_writer_|.
+  virtual void DisableDebugRecording();
+
+  // AudioDebugRecorder implementation. Can be called on any thread.
+  void OnData(const AudioBus* source) override;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(AudioDebugRecordingHelperTest, EnableDisable);
+  FRIEND_TEST_ALL_PREFIXES(AudioDebugRecordingHelperTest, OnData);
+
+  // Writes debug data to |debug_writer_|.
+  void DoWrite(std::unique_ptr<media::AudioBus> data);
+
+  // Creates an AudioDebugFileWriter. Overridden by test.
+  virtual std::unique_ptr<AudioDebugFileWriter> CreateAudioDebugFileWriter(
+      const AudioParameters& params);
+
+  // Passed to |create_file_callback| in EnableDebugRecording, to be called
+  // after debug recording file was created.
+  void StartDebugRecordingToFile(base::File file);
+
+  const AudioParameters params_;
+  std::unique_ptr<AudioDebugFileWriter> debug_writer_;
+
+  // Used as a flag to indicate if recording is enabled, accessed on different
+  // threads.
+  base::subtle::Atomic32 recording_enabled_;
+
+  // The task runner for accessing |debug_writer_|.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // Runs in destructor if set.
+  base::OnceClosure on_destruction_closure_;
+
+  base::WeakPtrFactory<AudioDebugRecordingHelper> weak_factory_{this};
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_HELPER_H_
diff --git a/third_party/chromium/media/audio/audio_debug_recording_helper_unittest.cc b/third_party/chromium/media/audio/audio_debug_recording_helper_unittest.cc
new file mode 100644
index 0000000..99f67b3
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_helper_unittest.cc
@@ -0,0 +1,280 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_debug_recording_helper.h"
+
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/task_environment.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_sample_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Return;
+
+namespace media {
+
+namespace {
+
+const base::FilePath::CharType kFileName[] =
+    FILE_PATH_LITERAL("debug_recording.output.1.wav");
+
+}  // namespace
+
+// Mock class for the audio file writer that the helper wraps.
+class MockAudioDebugFileWriter : public AudioDebugFileWriter {
+ public:
+  explicit MockAudioDebugFileWriter(const AudioParameters& params)
+      : AudioDebugFileWriter(params), reference_data_(nullptr) {}
+
+  MockAudioDebugFileWriter(const MockAudioDebugFileWriter&) = delete;
+  MockAudioDebugFileWriter& operator=(const MockAudioDebugFileWriter&) = delete;
+
+  ~MockAudioDebugFileWriter() override = default;
+
+  MOCK_METHOD1(DoStart, void(bool));
+  void Start(base::File file) override { DoStart(file.IsValid()); }
+  MOCK_METHOD0(Stop, void());
+
+  // Functions with move-only types as arguments can't be mocked directly, so
+  // we pass on to DoWrite(). Also, we can verify the data this way.
+  MOCK_METHOD1(DoWrite, void(AudioBus*));
+  void Write(std::unique_ptr<AudioBus> data) override {
+    CHECK(reference_data_);
+    EXPECT_EQ(reference_data_->channels(), data->channels());
+    EXPECT_EQ(reference_data_->frames(), data->frames());
+    for (int i = 0; i < data->channels(); ++i) {
+      float* data_ptr = data->channel(i);
+      float* ref_data_ptr = reference_data_->channel(i);
+      for (int j = 0; j < data->frames(); ++j, ++data_ptr, ++ref_data_ptr)
+        EXPECT_EQ(*ref_data_ptr, *data_ptr);
+    }
+    DoWrite(data.get());
+  }
+
+  MOCK_METHOD0(WillWrite, bool());
+
+  // Set reference data to compare against. Must be called before Write() is
+  // called.
+  void SetReferenceData(AudioBus* reference_data) {
+    EXPECT_EQ(params_.channels(), reference_data->channels());
+    EXPECT_EQ(params_.frames_per_buffer(), reference_data->frames());
+    reference_data_ = reference_data;
+  }
+
+ private:
+  AudioBus* reference_data_;
+};
+
+// Sub-class of the helper that overrides the CreateAudioDebugFileWriter
+// function to create the above mock instead.
+class AudioDebugRecordingHelperUnderTest : public AudioDebugRecordingHelper {
+ public:
+  AudioDebugRecordingHelperUnderTest(
+      const AudioParameters& params,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      base::OnceClosure on_destruction_closure)
+      : AudioDebugRecordingHelper(params,
+                                  std::move(task_runner),
+                                  std::move(on_destruction_closure)) {}
+
+  AudioDebugRecordingHelperUnderTest(
+      const AudioDebugRecordingHelperUnderTest&) = delete;
+  AudioDebugRecordingHelperUnderTest& operator=(
+      const AudioDebugRecordingHelperUnderTest&) = delete;
+
+  ~AudioDebugRecordingHelperUnderTest() override = default;
+
+ private:
+  // Creates the mock writer. After the mock writer is returned, we always
+  // expect Start() to be called on it by the helper.
+  std::unique_ptr<AudioDebugFileWriter> CreateAudioDebugFileWriter(
+      const AudioParameters& params) override {
+    MockAudioDebugFileWriter* writer = new MockAudioDebugFileWriter(params);
+    EXPECT_CALL(*writer, DoStart(true));
+    return base::WrapUnique<AudioDebugFileWriter>(writer);
+  }
+};
+
+class AudioDebugRecordingHelperTest : public ::testing::Test {
+ public:
+  AudioDebugRecordingHelperTest() {}
+
+  AudioDebugRecordingHelperTest(const AudioDebugRecordingHelperTest&) = delete;
+  AudioDebugRecordingHelperTest& operator=(
+      const AudioDebugRecordingHelperTest&) = delete;
+
+  ~AudioDebugRecordingHelperTest() override = default;
+
+  // Helper function that creates a recording helper.
+  std::unique_ptr<AudioDebugRecordingHelper> CreateRecordingHelper(
+      const AudioParameters& params,
+      base::OnceClosure on_destruction_closure) {
+    return std::make_unique<AudioDebugRecordingHelperUnderTest>(
+        params, task_environment_.GetMainThreadTaskRunner(),
+        std::move(on_destruction_closure));
+  }
+
+  MOCK_METHOD0(OnAudioDebugRecordingHelperDestruction, void());
+
+  // Bound and passed to AudioDebugRecordingHelper::EnableDebugRecording as
+  // AudioDebugRecordingHelper::CreateWavFileCallback.
+  void CreateWavFile(AudioDebugRecordingStreamType stream_type,
+                     uint32_t id,
+                     base::OnceCallback<void(base::File)> reply_callback) {
+    // Check that AudioDebugRecordingHelper::EnableDebugRecording calls
+    // CreateWavFileCallback with expected stream type and id.
+    EXPECT_EQ(stream_type_, stream_type);
+    EXPECT_EQ(id_, id);
+    base::ScopedTempDir temp_dir;
+    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+    base::FilePath path(temp_dir.GetPath().Append(base::FilePath(kFileName)));
+    base::File debug_file(
+        path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+    // Run |reply_callback| with a valid file for expected
+    // MockAudioDebugFileWriter::Start mocked call to happen.
+    std::move(reply_callback).Run(std::move(debug_file));
+    // File can be removed right away because MockAudioDebugFileWriter::Start is
+    // called synchronously.
+    ASSERT_TRUE(base::DeleteFile(path));
+  }
+
+ protected:
+  const AudioDebugRecordingStreamType stream_type_ =
+      AudioDebugRecordingStreamType::kInput;
+  const uint32_t id_ = 1;
+
+  // The test task environment.
+  base::test::TaskEnvironment task_environment_;
+};
+
+// Creates a helper with an on destruction closure, and verifies that it's run.
+TEST_F(AudioDebugRecordingHelperTest, TestDestructionClosure) {
+  const AudioParameters params;
+  std::unique_ptr<AudioDebugRecordingHelper> recording_helper =
+      CreateRecordingHelper(
+          params, base::BindOnce(&AudioDebugRecordingHelperTest::
+                                     OnAudioDebugRecordingHelperDestruction,
+                                 base::Unretained(this)));
+
+  EXPECT_CALL(*this, OnAudioDebugRecordingHelperDestruction());
+}
+
+// Verifies that disable can be called without being enabled.
+TEST_F(AudioDebugRecordingHelperTest, OnlyDisable) {
+  const AudioParameters params;
+  std::unique_ptr<AudioDebugRecordingHelper> recording_helper =
+      CreateRecordingHelper(params, base::OnceClosure());
+
+  recording_helper->DisableDebugRecording();
+}
+
+TEST_F(AudioDebugRecordingHelperTest, EnableDisable) {
+  const AudioParameters params;
+  std::unique_ptr<AudioDebugRecordingHelper> recording_helper =
+      CreateRecordingHelper(params, base::OnceClosure());
+
+  recording_helper->EnableDebugRecording(
+      stream_type_, id_,
+      base::BindOnce(&AudioDebugRecordingHelperTest::CreateWavFile,
+                     base::Unretained(this)));
+  EXPECT_CALL(*static_cast<MockAudioDebugFileWriter*>(
+                  recording_helper->debug_writer_.get()),
+              Stop());
+  recording_helper->DisableDebugRecording();
+
+  recording_helper->EnableDebugRecording(
+      stream_type_, id_,
+      base::BindOnce(&AudioDebugRecordingHelperTest::CreateWavFile,
+                     base::Unretained(this)));
+  EXPECT_CALL(*static_cast<MockAudioDebugFileWriter*>(
+                  recording_helper->debug_writer_.get()),
+              Stop());
+  recording_helper->DisableDebugRecording();
+}
+
+TEST_F(AudioDebugRecordingHelperTest, OnData) {
+  // Only channel layout and frames per buffer is used in the file writer and
+  // AudioBus, the other parameters are ignored.
+  const int number_of_frames = 100;
+  const AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR,
+                               ChannelLayout::CHANNEL_LAYOUT_STEREO, 0,
+                               number_of_frames);
+
+  // Setup some data.
+  const int number_of_samples = number_of_frames * params.channels();
+  const float step = std::numeric_limits<int16_t>::max() / number_of_frames;
+  std::unique_ptr<float[]> source_data(new float[number_of_samples]);
+  for (float i = 0; i < number_of_samples; ++i)
+    source_data[i] = i * step;
+  std::unique_ptr<AudioBus> audio_bus = AudioBus::Create(params);
+  audio_bus->FromInterleaved<Float32SampleTypeTraits>(source_data.get(),
+                                                      number_of_frames);
+
+  std::unique_ptr<AudioDebugRecordingHelper> recording_helper =
+      CreateRecordingHelper(params, base::OnceClosure());
+
+  // Should not do anything.
+  recording_helper->OnData(audio_bus.get());
+
+  recording_helper->EnableDebugRecording(
+      stream_type_, id_,
+      base::BindOnce(&AudioDebugRecordingHelperTest::CreateWavFile,
+                     base::Unretained(this)));
+  MockAudioDebugFileWriter* mock_audio_file_writer =
+      static_cast<MockAudioDebugFileWriter*>(
+          recording_helper->debug_writer_.get());
+  mock_audio_file_writer->SetReferenceData(audio_bus.get());
+
+  EXPECT_CALL(*mock_audio_file_writer, DoWrite(_));
+  recording_helper->OnData(audio_bus.get());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_CALL(*mock_audio_file_writer, Stop());
+  recording_helper->DisableDebugRecording();
+
+  // Make sure we clear the loop before enabling again.
+  base::RunLoop().RunUntilIdle();
+
+  // Enable again, this time with two OnData() calls, one OnData() call without
+  // running the message loop until after disabling, and one call after
+  // disabling.
+  recording_helper->EnableDebugRecording(
+      stream_type_, id_,
+      base::BindOnce(&AudioDebugRecordingHelperTest::CreateWavFile,
+                     base::Unretained(this)));
+  mock_audio_file_writer = static_cast<MockAudioDebugFileWriter*>(
+      recording_helper->debug_writer_.get());
+  mock_audio_file_writer->SetReferenceData(audio_bus.get());
+
+  EXPECT_CALL(*mock_audio_file_writer, DoWrite(_)).Times(2);
+  recording_helper->OnData(audio_bus.get());
+  recording_helper->OnData(audio_bus.get());
+  base::RunLoop().RunUntilIdle();
+
+  // This call should not yield a DoWrite() call on the mock, since the message
+  // loop isn't run until after disabling. WillWrite() is expected since
+  // recording is enabled.
+  recording_helper->OnData(audio_bus.get());
+
+  EXPECT_CALL(*mock_audio_file_writer, Stop());
+  recording_helper->DisableDebugRecording();
+
+  // This call should not yield a DoWrite() call on the mock either.
+  recording_helper->OnData(audio_bus.get());
+  base::RunLoop().RunUntilIdle();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_debug_recording_manager.cc b/third_party/chromium/media/audio/audio_debug_recording_manager.cc
new file mode 100644
index 0000000..1a668a7
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_manager.cc
@@ -0,0 +1,101 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_debug_recording_manager.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task_runner_util.h"
+
+namespace media {
+
+namespace {
+// Running id recording sources.
+uint32_t g_next_stream_id = 1;
+}
+
+AudioDebugRecordingManager::AudioDebugRecordingManager(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : task_runner_(std::move(task_runner)) {}
+
+AudioDebugRecordingManager::~AudioDebugRecordingManager() = default;
+
+void AudioDebugRecordingManager::EnableDebugRecording(
+    CreateWavFileCallback create_file_callback) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(!create_file_callback.is_null());
+  create_file_callback_ = std::move(create_file_callback);
+
+  for (const auto& it : debug_recording_helpers_) {
+    uint32_t id = it.first;
+    AudioDebugRecordingHelper* recording_helper = it.second.first;
+    AudioDebugRecordingStreamType stream_type = it.second.second;
+    recording_helper->EnableDebugRecording(stream_type, id,
+                                           create_file_callback_);
+  }
+}
+
+void AudioDebugRecordingManager::DisableDebugRecording() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(!create_file_callback_.is_null());
+  for (const auto& it : debug_recording_helpers_) {
+    AudioDebugRecordingHelper* recording_helper = it.second.first;
+    recording_helper->DisableDebugRecording();
+  }
+  create_file_callback_.Reset();
+}
+
+std::unique_ptr<AudioDebugRecorder>
+AudioDebugRecordingManager::RegisterDebugRecordingSource(
+    AudioDebugRecordingStreamType stream_type,
+    const AudioParameters& params) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  const uint32_t id = g_next_stream_id++;
+
+  // Normally, the manager will outlive the one who registers and owns the
+  // returned recorder. But to not require this we use a weak pointer.
+  std::unique_ptr<AudioDebugRecordingHelper> recording_helper =
+      CreateAudioDebugRecordingHelper(
+          params, task_runner_,
+          base::BindOnce(
+              &AudioDebugRecordingManager::UnregisterDebugRecordingSource,
+              weak_factory_.GetWeakPtr(), id));
+
+  if (IsDebugRecordingEnabled()) {
+    recording_helper->EnableDebugRecording(stream_type, id,
+                                           create_file_callback_);
+  }
+
+  debug_recording_helpers_[id] =
+      std::make_pair(recording_helper.get(), stream_type);
+
+  return base::WrapUnique<AudioDebugRecorder>(recording_helper.release());
+}
+
+void AudioDebugRecordingManager::UnregisterDebugRecordingSource(uint32_t id) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  auto it = debug_recording_helpers_.find(id);
+  DCHECK(it != debug_recording_helpers_.end());
+  debug_recording_helpers_.erase(id);
+}
+
+std::unique_ptr<AudioDebugRecordingHelper>
+AudioDebugRecordingManager::CreateAudioDebugRecordingHelper(
+    const AudioParameters& params,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    base::OnceClosure on_destruction_closure) {
+  return std::make_unique<AudioDebugRecordingHelper>(
+      params, task_runner, std::move(on_destruction_closure));
+}
+
+bool AudioDebugRecordingManager::IsDebugRecordingEnabled() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  return !create_file_callback_.is_null();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_debug_recording_manager.h b/third_party/chromium/media/audio/audio_debug_recording_manager.h
new file mode 100644
index 0000000..5ea162d
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_manager.h
@@ -0,0 +1,123 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_MANAGER_H_
+#define MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/audio_debug_recording_helper.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+
+// A manager for audio debug recording that handles registration of data
+// sources and hands them a recorder (AudioDebugRecordingHelper) to feed data
+// to. The recorder will unregister with the manager automatically when deleted.
+// When debug recording is enabled, it is enabled on all recorders and
+// constructs a unique file name for each recorder by using a running ID.
+// A somewhat simplified diagram of the the debug recording infrastructure,
+// interfaces omitted:
+//
+//                                AudioDebugFileWriter
+//                                        ^
+//                                        | owns
+//                        owns            |                     owns
+//   OnMoreDataConverter  ---->  AudioDebugRecordingHelper <---------
+//            ^                           ^                          |
+//            | owns several              | raw pointer to several   |
+//            |                   AudioDebugRecordingManager         |
+//   AudioOutputResampler                 ^                          |
+//            ^                           |      AudioInputStreamDataInterceptor
+//            |                           |                          ^
+//            | owns several              | owns        owns several |
+//             ------------------  AudioManagerBase  ----------------
+//
+// AudioDebugRecordingManager is created when
+// AudioManager::InitializeDebugRecording() is called. That is done in
+// AudioManager::Create() in WebRTC enabled builds, but not in non WebRTC
+// enabled builds. If AudioDebugRecordingManager is not created, neither is
+// AudioDebugRecordingHelper or AudioDebugFileWriter. In this case the pointers
+// to AudioDebugRecordingManager and AudioDebugRecordingHelper are null.
+
+class MEDIA_EXPORT AudioDebugRecordingManager {
+ public:
+  using CreateWavFileCallback = base::RepeatingCallback<void(
+      AudioDebugRecordingStreamType stream_type,
+      uint32_t id,
+      base::OnceCallback<void(base::File)> reply_callback)>;
+
+  AudioDebugRecordingManager(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  AudioDebugRecordingManager(const AudioDebugRecordingManager&) = delete;
+  AudioDebugRecordingManager& operator=(const AudioDebugRecordingManager&) =
+      delete;
+
+  virtual ~AudioDebugRecordingManager();
+
+  // Enables and disables debug recording.
+  virtual void EnableDebugRecording(CreateWavFileCallback create_file_callback);
+  virtual void DisableDebugRecording();
+
+  // Registers a source and returns a wrapped recorder. |stream_type| is added
+  // to the base filename, along with a unique running ID.
+  std::unique_ptr<AudioDebugRecorder> RegisterDebugRecordingSource(
+      AudioDebugRecordingStreamType stream_type,
+      const AudioParameters& params);
+
+ protected:
+  // Creates a AudioDebugRecordingHelper. Overridden by test.
+  virtual std::unique_ptr<AudioDebugRecordingHelper>
+  CreateAudioDebugRecordingHelper(
+      const AudioParameters& params,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      base::OnceClosure on_destruction_closure);
+
+  // The task runner this class lives on. Also handed to
+  // AudioDebugRecordingHelpers.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(AudioDebugRecordingManagerTest,
+                           RegisterAutomaticUnregisterAtDelete);
+  FRIEND_TEST_ALL_PREFIXES(AudioDebugRecordingManagerTest,
+                           RegisterEnableDisable);
+  FRIEND_TEST_ALL_PREFIXES(AudioDebugRecordingManagerTest,
+                           EnableRegisterDisable);
+
+  // Map type from source id to recorder and stream type (input/output).
+  using DebugRecordingHelperMap = std::map<
+      uint32_t,
+      std::pair<AudioDebugRecordingHelper*, AudioDebugRecordingStreamType>>;
+
+  // Unregisters a source.
+  void UnregisterDebugRecordingSource(uint32_t id);
+
+  bool IsDebugRecordingEnabled();
+
+  // Recorders, one per source.
+  DebugRecordingHelperMap debug_recording_helpers_;
+
+  // Callback for creating debug recording files. When this is not null, debug
+  // recording is enabled.
+  CreateWavFileCallback create_file_callback_;
+
+  base::WeakPtrFactory<AudioDebugRecordingManager> weak_factory_{this};
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_MANAGER_H_
diff --git a/third_party/chromium/media/audio/audio_debug_recording_manager_unittest.cc b/third_party/chromium/media/audio/audio_debug_recording_manager_unittest.cc
new file mode 100644
index 0000000..a9f903e
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_manager_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_debug_recording_manager.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/task_environment.h"
+#include "media/audio/audio_debug_recording_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace media {
+
+namespace {
+
+// The stream type expected to be added to file name.
+const AudioDebugRecordingStreamType kStreamType(
+    AudioDebugRecordingStreamType::kOutput);
+
+// Used to be able to set call expectations in the MockAudioDebugRecordingHelper
+// ctor. See also comment on the test EnableRegisterDisable.
+bool g_expect_enable_after_create_helper = false;
+
+// A helper struct to be able to set and unset
+// |g_expect_enable_after_create_helper| scoped.
+struct ScopedExpectEnableAfterCreateHelper {
+  ScopedExpectEnableAfterCreateHelper() {
+    CHECK(!g_expect_enable_after_create_helper);
+    g_expect_enable_after_create_helper = true;
+  }
+  ~ScopedExpectEnableAfterCreateHelper() {
+    CHECK(g_expect_enable_after_create_helper);
+    g_expect_enable_after_create_helper = false;
+  }
+};
+
+// Function bound and passed to AudioDebugRecordingManager::EnableDebugRecording
+// as AudioDebugRecordingManager::CreateWavFileCallback.
+void CreateWavFile(AudioDebugRecordingStreamType stream_type,
+                   uint32_t id,
+                   base::OnceCallback<void(base::File)>) {}
+
+}  // namespace
+
+// Mock class to verify enable and disable calls.
+class MockAudioDebugRecordingHelper : public AudioDebugRecordingHelper {
+ public:
+  MockAudioDebugRecordingHelper(
+      const AudioParameters& params,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      base::OnceClosure on_destruction_closure)
+      : AudioDebugRecordingHelper(params,
+                                  std::move(task_runner),
+                                  base::OnceClosure()),
+        on_destruction_closure_in_mock_(std::move(on_destruction_closure)) {
+    if (g_expect_enable_after_create_helper)
+      EXPECT_CALL(*this, DoEnableDebugRecording(_, _));
+  }
+
+  MockAudioDebugRecordingHelper(const MockAudioDebugRecordingHelper&) = delete;
+  MockAudioDebugRecordingHelper& operator=(
+      const MockAudioDebugRecordingHelper&) = delete;
+
+  ~MockAudioDebugRecordingHelper() override {
+    if (on_destruction_closure_in_mock_)
+      std::move(on_destruction_closure_in_mock_).Run();
+  }
+
+  MOCK_METHOD2(DoEnableDebugRecording,
+               void(AudioDebugRecordingStreamType, uint32_t));
+  void EnableDebugRecording(AudioDebugRecordingStreamType stream_type,
+                            uint32_t id,
+                            AudioDebugRecordingHelper::CreateWavFileCallback
+                                create_file_callback) override {
+    DoEnableDebugRecording(stream_type, id);
+  }
+
+  MOCK_METHOD0(DisableDebugRecording, void());
+
+ private:
+  // We let the mock run the destruction closure to not rely on the real
+  // implementation.
+  base::OnceClosure on_destruction_closure_in_mock_;
+};
+
+// Sub-class of the manager that overrides the CreateAudioDebugRecordingHelper
+// function to create the above mock instead.
+class AudioDebugRecordingManagerUnderTest : public AudioDebugRecordingManager {
+ public:
+  AudioDebugRecordingManagerUnderTest(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+      : AudioDebugRecordingManager(std::move(task_runner)) {}
+
+  AudioDebugRecordingManagerUnderTest(
+      const AudioDebugRecordingManagerUnderTest&) = delete;
+  AudioDebugRecordingManagerUnderTest& operator=(
+      const AudioDebugRecordingManagerUnderTest&) = delete;
+
+  ~AudioDebugRecordingManagerUnderTest() override = default;
+
+ private:
+  std::unique_ptr<AudioDebugRecordingHelper> CreateAudioDebugRecordingHelper(
+      const AudioParameters& params,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      base::OnceClosure on_destruction_closure) override {
+    return std::make_unique<MockAudioDebugRecordingHelper>(
+        params, std::move(task_runner),
+        std::move(on_destruction_closure));
+  }
+};
+
+// The test fixture.
+class AudioDebugRecordingManagerTest : public ::testing::Test {
+ public:
+  AudioDebugRecordingManagerTest()
+      : manager_(task_environment_.GetMainThreadTaskRunner()) {}
+
+  AudioDebugRecordingManagerTest(const AudioDebugRecordingManagerTest&) =
+      delete;
+  AudioDebugRecordingManagerTest& operator=(
+      const AudioDebugRecordingManagerTest&) = delete;
+
+  ~AudioDebugRecordingManagerTest() override = default;
+
+  // Registers a source and increases counter for the expected next source id.
+  std::unique_ptr<AudioDebugRecorder> RegisterDebugRecordingSource(
+      const AudioParameters& params) {
+    ++expected_next_source_id_;
+    return manager_.RegisterDebugRecordingSource(kStreamType, params);
+  }
+
+ protected:
+  // The test task environment.
+  base::test::TaskEnvironment task_environment_;
+
+  AudioDebugRecordingManagerUnderTest manager_;
+
+  // The expected next source id the manager will assign. It's static since the
+  // manager uses a global running id, thus doesn't restart at each
+  // instantiation.
+  static uint32_t expected_next_source_id_;
+};
+
+uint32_t AudioDebugRecordingManagerTest::expected_next_source_id_ = 1;
+
+// Shouldn't do anything but store the CreateWavFileCallback, i.e. no calls to
+// recorders.
+TEST_F(AudioDebugRecordingManagerTest, EnableDisable) {
+  manager_.EnableDebugRecording(base::BindRepeating(&CreateWavFile));
+  manager_.DisableDebugRecording();
+}
+
+// Tests registration and automatic unregistration on destruction of a recorder.
+// The unregistration relies on that the MockAudioDebugRecordingHelper runs the
+// |on_destruction_closure| given to it.
+TEST_F(AudioDebugRecordingManagerTest, RegisterAutomaticUnregisterAtDelete) {
+  const AudioParameters params;
+  std::vector<std::unique_ptr<AudioDebugRecorder>> recorders;
+  recorders.push_back(RegisterDebugRecordingSource(params));
+  recorders.push_back(RegisterDebugRecordingSource(params));
+  recorders.push_back(RegisterDebugRecordingSource(params));
+  EXPECT_EQ(3ul, recorders.size());
+  EXPECT_EQ(recorders.size(), manager_.debug_recording_helpers_.size());
+
+  while (!recorders.empty()) {
+    recorders.pop_back();
+    EXPECT_EQ(recorders.size(), manager_.debug_recording_helpers_.size());
+  }
+  EXPECT_EQ(0ul, recorders.size());
+}
+
+TEST_F(AudioDebugRecordingManagerTest, RegisterEnableDisable) {
+  // Store away the extected id for the next source to use after registering all
+  // sources.
+  uint32_t expected_id = expected_next_source_id_;
+
+  const AudioParameters params;
+  std::vector<std::unique_ptr<AudioDebugRecorder>> recorders;
+  recorders.push_back(RegisterDebugRecordingSource(params));
+  recorders.push_back(RegisterDebugRecordingSource(params));
+  recorders.push_back(RegisterDebugRecordingSource(params));
+  EXPECT_EQ(3ul, recorders.size());
+  EXPECT_EQ(recorders.size(), manager_.debug_recording_helpers_.size());
+
+  for (const auto& recorder : recorders) {
+    MockAudioDebugRecordingHelper* mock_recording_helper =
+        static_cast<MockAudioDebugRecordingHelper*>(recorder.get());
+    EXPECT_CALL(*mock_recording_helper,
+                DoEnableDebugRecording(kStreamType, expected_id++));
+    EXPECT_CALL(*mock_recording_helper, DisableDebugRecording());
+  }
+
+  manager_.EnableDebugRecording(base::BindRepeating(&CreateWavFile));
+  manager_.DisableDebugRecording();
+}
+
+// Test enabling first, then registering. This should call enable on the
+// recoders, but we can't set expectation for that since the mock object is
+// created and called enable upon in RegisterDebugRecordingSource(), then
+// returned. Instead expectation is set in the ctor of the mock by setting
+// |g_expect_enable_after_create_helper| to true here (by using the scoped
+// variable).
+TEST_F(AudioDebugRecordingManagerTest, EnableRegisterDisable) {
+  ScopedExpectEnableAfterCreateHelper scoped_enable_after_create_helper;
+
+  manager_.EnableDebugRecording(base::BindRepeating(&CreateWavFile));
+
+  const AudioParameters params;
+  std::vector<std::unique_ptr<AudioDebugRecorder>> recorders;
+  recorders.push_back(RegisterDebugRecordingSource(params));
+  recorders.push_back(RegisterDebugRecordingSource(params));
+  recorders.push_back(RegisterDebugRecordingSource(params));
+  EXPECT_EQ(3ul, recorders.size());
+  EXPECT_EQ(recorders.size(), manager_.debug_recording_helpers_.size());
+
+  for (const auto& recorder : recorders) {
+    MockAudioDebugRecordingHelper* mock_recording_helper =
+        static_cast<MockAudioDebugRecordingHelper*>(recorder.get());
+    EXPECT_CALL(*mock_recording_helper, DisableDebugRecording());
+  }
+
+  manager_.DisableDebugRecording();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_debug_recording_session.h b/third_party/chromium/media/audio/audio_debug_recording_session.h
new file mode 100644
index 0000000..b6b582a
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_session.h
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_SESSION_H_
+#define MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_SESSION_H_
+
+#include "base/macros.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Enables/disables audio debug recording on construction/destruction. Objects
+// are created using audio::CreateAudioDebugRecordingSession.
+class MEDIA_EXPORT AudioDebugRecordingSession {
+ public:
+  AudioDebugRecordingSession(const AudioDebugRecordingSession&) = delete;
+  AudioDebugRecordingSession& operator=(const AudioDebugRecordingSession&) =
+      delete;
+
+  virtual ~AudioDebugRecordingSession() = default;
+
+ protected:
+  AudioDebugRecordingSession() = default;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_SESSION_H_
diff --git a/third_party/chromium/media/audio/audio_debug_recording_session_impl.cc b/third_party/chromium/media/audio/audio_debug_recording_session_impl.cc
new file mode 100644
index 0000000..93113a3
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_session_impl.cc
@@ -0,0 +1,119 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_debug_recording_session_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "build/build_config.h"
+#include "media/audio/audio_debug_recording_manager.h"
+#include "media/audio/audio_manager.h"
+
+namespace media {
+
+// Posting AudioManager::Get() as unretained is safe because
+// AudioManager::Shutdown() (called before AudioManager destruction) shuts down
+// AudioManager thread.
+
+// TODO(https://crbug.com/788657) remove this file after switching to mojo
+// implementation.
+
+namespace {
+
+#if defined(OS_WIN)
+#define NumberToStringType base::NumberToWString
+#else
+#define NumberToStringType base::NumberToString
+#endif
+
+bool StreamTypeToStringType(AudioDebugRecordingStreamType stream_type,
+                            base::FilePath::StringType* out) {
+  switch (stream_type) {
+    case AudioDebugRecordingStreamType::kInput:
+      *out = FILE_PATH_LITERAL("input");
+      return true;
+    case AudioDebugRecordingStreamType::kOutput:
+      *out = FILE_PATH_LITERAL("output");
+      return true;
+  }
+  NOTREACHED();
+  return false;
+}
+
+void CreateWavFile(const base::FilePath& debug_recording_file_path,
+                   AudioDebugRecordingStreamType stream_type,
+                   uint32_t id,
+                   base::OnceCallback<void(base::File)> reply_callback) {
+  base::FilePath::StringType stream_type_str;
+  if (!StreamTypeToStringType(stream_type, &stream_type_str)) {
+    std::move(reply_callback).Run(base::File());
+    return;
+  }
+
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+      base::BindOnce(
+          [](const base::FilePath& file_name) {
+            return base::File(file_name, base::File::FLAG_CREATE_ALWAYS |
+                                             base::File::FLAG_WRITE);
+          },
+          debug_recording_file_path.AddExtension(stream_type_str)
+              .AddExtension(NumberToStringType(id))
+              .AddExtension(FILE_PATH_LITERAL("wav"))),
+      std::move(reply_callback));
+}
+
+}  // namespace
+
+AudioDebugRecordingSessionImpl::AudioDebugRecordingSessionImpl(
+    const base::FilePath& debug_recording_file_path) {
+  AudioManager* audio_manager = AudioManager::Get();
+  if (audio_manager == nullptr)
+    return;
+
+  audio_manager->GetTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](AudioManager* manager,
+             AudioDebugRecordingManager::CreateWavFileCallback
+                 create_file_callback) {
+            AudioDebugRecordingManager* debug_recording_manager =
+                manager->GetAudioDebugRecordingManager();
+            if (debug_recording_manager == nullptr)
+              return;
+            debug_recording_manager->EnableDebugRecording(
+                std::move(create_file_callback));
+          },
+          base::Unretained(audio_manager),
+          base::BindRepeating(&CreateWavFile, debug_recording_file_path)));
+}
+
+AudioDebugRecordingSessionImpl::~AudioDebugRecordingSessionImpl() {
+  AudioManager* audio_manager = AudioManager::Get();
+  if (audio_manager == nullptr)
+    return;
+
+  audio_manager->GetTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(
+                     [](AudioManager* manager) {
+                       AudioDebugRecordingManager* debug_recording_manager =
+                           manager->GetAudioDebugRecordingManager();
+                       if (debug_recording_manager == nullptr)
+                         return;
+                       debug_recording_manager->DisableDebugRecording();
+                     },
+                     base::Unretained(audio_manager)));
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_debug_recording_session_impl.h b/third_party/chromium/media/audio/audio_debug_recording_session_impl.h
new file mode 100644
index 0000000..957c39b
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_session_impl.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_SESSION_IMPL_H_
+#define MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_SESSION_IMPL_H_
+
+#include "media/audio/audio_debug_recording_session.h"
+#include "media/base/media_export.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace media {
+
+class MEDIA_EXPORT AudioDebugRecordingSessionImpl
+    : public AudioDebugRecordingSession {
+ public:
+  explicit AudioDebugRecordingSessionImpl(
+      const base::FilePath& debug_recording_file_path);
+
+  AudioDebugRecordingSessionImpl(const AudioDebugRecordingSessionImpl&) =
+      delete;
+  AudioDebugRecordingSessionImpl& operator=(
+      const AudioDebugRecordingSessionImpl&) = delete;
+
+  ~AudioDebugRecordingSessionImpl() override;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_SESSION_IMPL_H_
diff --git a/third_party/chromium/media/audio/audio_debug_recording_session_impl_unittest.cc b/third_party/chromium/media/audio/audio_debug_recording_session_impl_unittest.cc
new file mode 100644
index 0000000..a30b205
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_session_impl_unittest.cc
@@ -0,0 +1,144 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_debug_recording_session_impl.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/task_environment.h"
+#include "build/build_config.h"
+#include "media/audio/audio_debug_recording_test.h"
+#include "media/audio/mock_audio_debug_recording_manager.h"
+#include "media/audio/mock_audio_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+
+namespace {
+
+#if defined(OS_WIN)
+#define NumberToStringType base::NumberToWString
+#else
+#define NumberToStringType base::NumberToString
+#endif
+
+const base::FilePath::CharType kBaseFileName[] =
+    FILE_PATH_LITERAL("debug_recording");
+const base::FilePath::CharType kInput[] = FILE_PATH_LITERAL("input");
+const base::FilePath::CharType kOutput[] = FILE_PATH_LITERAL("output");
+const int kId = 1;
+const base::FilePath::CharType kWavExtension[] = FILE_PATH_LITERAL("wav");
+
+void OnFileCreated(base::File debug_file) {}
+
+// Action function called on
+// MockAudioDebugRecordingManager::EnableDebugRecording mocked method to test
+// |create_file_callback| behavior.
+void CreateInputOutputDebugRecordingFiles(
+    const AudioDebugRecordingManager::CreateWavFileCallback&
+        create_file_callback) {
+  create_file_callback.Run(AudioDebugRecordingStreamType::kInput, kId,
+                           base::BindOnce(&OnFileCreated));
+  create_file_callback.Run(AudioDebugRecordingStreamType::kOutput, kId,
+                           base::BindOnce(&OnFileCreated));
+}
+
+}  // namespace
+
+class AudioDebugRecordingSessionImplTest : public AudioDebugRecordingTest {
+ public:
+  AudioDebugRecordingSessionImplTest() {
+    CHECK(temp_dir_.CreateUniqueTempDir());
+    base_file_path_ = temp_dir_.GetPath().Append(base::FilePath(kBaseFileName));
+  }
+
+ protected:
+  void CreateDebugRecordingSession() {
+    audio_debug_recording_session_impl_ =
+        std::make_unique<media::AudioDebugRecordingSessionImpl>(
+            base_file_path_);
+  }
+
+  void DestroyDebugRecordingSession() {
+    audio_debug_recording_session_impl_.reset();
+  }
+
+  base::FilePath GetFileName(const base::FilePath::StringType& stream_type,
+                             uint32_t id) {
+    return base_file_path_.AddExtension(stream_type)
+        .AddExtension(NumberToStringType(id))
+        .AddExtension(kWavExtension);
+  }
+
+  base::FilePath base_file_path_;
+
+ private:
+  base::ScopedTempDir temp_dir_;
+  std::unique_ptr<AudioDebugRecordingSessionImpl>
+      audio_debug_recording_session_impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioDebugRecordingSessionImplTest);
+};
+
+TEST_F(AudioDebugRecordingSessionImplTest,
+       ConstructorEnablesAndDestructorDisablesDebugRecordingOnAudioManager) {
+  ::testing::InSequence seq;
+
+  CreateAudioManager();
+  InitializeAudioDebugRecordingManager();
+  EXPECT_CALL(*mock_debug_recording_manager_, EnableDebugRecording(testing::_));
+  CreateDebugRecordingSession();
+
+  EXPECT_CALL(*mock_debug_recording_manager_, DisableDebugRecording());
+  DestroyDebugRecordingSession();
+
+  ShutdownAudioManager();
+}
+
+TEST_F(AudioDebugRecordingSessionImplTest,
+       CreateDestroySessionDontCrashWithNoAudioManager) {
+  ASSERT_EQ(nullptr, AudioManager::Get());
+  CreateDebugRecordingSession();
+  DestroyDebugRecordingSession();
+}
+
+TEST_F(AudioDebugRecordingSessionImplTest,
+       CreateDestroySessionDontCrashWithoutInitializingDebugRecordingManager) {
+  CreateAudioManager();
+  CreateDebugRecordingSession();
+  DestroyDebugRecordingSession();
+  ShutdownAudioManager();
+}
+
+// Tests the CreateWavFile method from AudioDebugRecordingSessionImpl unnamed
+// namespace.
+TEST_F(AudioDebugRecordingSessionImplTest, CreateWavFileCreatesExpectedFiles) {
+  CreateAudioManager();
+  InitializeAudioDebugRecordingManager();
+  EXPECT_CALL(*mock_debug_recording_manager_, EnableDebugRecording(testing::_))
+      .WillOnce(testing::Invoke(CreateInputOutputDebugRecordingFiles));
+  CreateDebugRecordingSession();
+
+  // Wait for files to be created.
+  task_environment_.RunUntilIdle();
+
+  // Check that expected files were created.
+  base::FilePath input_recording_filename(GetFileName(kInput, kId));
+  base::FilePath output_recording_filename(GetFileName(kOutput, kId));
+  EXPECT_TRUE(base::PathExists(output_recording_filename));
+  EXPECT_TRUE(base::PathExists(input_recording_filename));
+
+  // Clean-up.
+  EXPECT_CALL(*mock_debug_recording_manager_, DisableDebugRecording());
+  DestroyDebugRecordingSession();
+  ShutdownAudioManager();
+  EXPECT_TRUE(base::DeleteFile(output_recording_filename));
+  EXPECT_TRUE(base::DeleteFile(input_recording_filename));
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_debug_recording_test.cc b/third_party/chromium/media/audio/audio_debug_recording_test.cc
new file mode 100644
index 0000000..bc19e83
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_test.cc
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_debug_recording_test.h"
+
+#include "media/audio/mock_audio_debug_recording_manager.h"
+#include "media/audio/mock_audio_manager.h"
+#include "media/audio/test_audio_thread.h"
+
+namespace media {
+
+AudioDebugRecordingTest::AudioDebugRecordingTest() = default;
+
+AudioDebugRecordingTest::~AudioDebugRecordingTest() = default;
+
+void AudioDebugRecordingTest::CreateAudioManager() {
+  DCHECK(AudioManager::Get() == nullptr);
+  mock_audio_manager_ =
+      std::make_unique<MockAudioManager>(std::make_unique<TestAudioThread>());
+  ASSERT_NE(nullptr, AudioManager::Get());
+  ASSERT_EQ(static_cast<AudioManager*>(mock_audio_manager_.get()),
+            AudioManager::Get());
+}
+
+void AudioDebugRecordingTest::ShutdownAudioManager() {
+  DCHECK(mock_audio_manager_);
+  ASSERT_TRUE(mock_audio_manager_->Shutdown());
+}
+
+void AudioDebugRecordingTest::InitializeAudioDebugRecordingManager() {
+  DCHECK(mock_audio_manager_);
+  mock_audio_manager_->InitializeDebugRecording();
+  mock_debug_recording_manager_ = static_cast<MockAudioDebugRecordingManager*>(
+      mock_audio_manager_->GetAudioDebugRecordingManager());
+  ASSERT_NE(nullptr, mock_debug_recording_manager_);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_debug_recording_test.h b/third_party/chromium/media/audio/audio_debug_recording_test.h
new file mode 100644
index 0000000..2c20ccc
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_debug_recording_test.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_TEST_H_
+#define MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_TEST_H_
+
+#include <memory>
+
+#include "base/test/task_environment.h"
+#include "media/base/media_export.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class MockAudioDebugRecordingManager;
+class MockAudioManager;
+
+// Base test class for media/audio/ and services/audio/ debug recording test
+// classes.
+class AudioDebugRecordingTest : public testing::Test {
+ public:
+  AudioDebugRecordingTest();
+
+  AudioDebugRecordingTest(const AudioDebugRecordingTest&) = delete;
+  AudioDebugRecordingTest& operator=(const AudioDebugRecordingTest&) = delete;
+
+  ~AudioDebugRecordingTest() override;
+
+ protected:
+  void CreateAudioManager();
+  void ShutdownAudioManager();
+  void InitializeAudioDebugRecordingManager();
+
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<MockAudioManager> mock_audio_manager_;
+  MockAudioDebugRecordingManager* mock_debug_recording_manager_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_DEBUG_RECORDING_TEST_H_
diff --git a/third_party/chromium/media/audio/audio_device_description.cc b/third_party/chromium/media/audio/audio_device_description.cc
new file mode 100644
index 0000000..bd3f611
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_device_description.cc
@@ -0,0 +1,112 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_device_description.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/notreached.h"
+#include "build/chromecast_buildflags.h"
+#include "media/base/localized_strings.h"
+
+namespace media {
+const char AudioDeviceDescription::kDefaultDeviceId[] = "default";
+const char AudioDeviceDescription::kCommunicationsDeviceId[] = "communications";
+const char AudioDeviceDescription::kLoopbackInputDeviceId[] = "loopback";
+const char AudioDeviceDescription::kLoopbackWithMuteDeviceId[] =
+    "loopbackWithMute";
+
+// static
+bool AudioDeviceDescription::IsDefaultDevice(const std::string& device_id) {
+  return device_id.empty() ||
+         device_id == AudioDeviceDescription::kDefaultDeviceId;
+}
+
+// static
+bool AudioDeviceDescription::IsCommunicationsDevice(
+    const std::string& device_id) {
+  return device_id == AudioDeviceDescription::kCommunicationsDeviceId;
+}
+
+// static
+bool AudioDeviceDescription::IsLoopbackDevice(const std::string& device_id) {
+  return device_id.compare(kLoopbackInputDeviceId) == 0 ||
+         device_id.compare(kLoopbackWithMuteDeviceId) == 0;
+}
+
+// static
+bool AudioDeviceDescription::UseSessionIdToSelectDevice(
+    const base::UnguessableToken& session_id,
+    const std::string& device_id) {
+  return !session_id.is_empty() && device_id.empty();
+}
+
+// static
+std::string AudioDeviceDescription::GetDefaultDeviceName() {
+#if !defined(OS_IOS)
+  return GetLocalizedStringUTF8(DEFAULT_AUDIO_DEVICE_NAME);
+#else
+  NOTREACHED();
+  return "";
+#endif
+}
+
+// static
+std::string AudioDeviceDescription::GetCommunicationsDeviceName() {
+#if defined(OS_WIN)
+  return GetLocalizedStringUTF8(COMMUNICATIONS_AUDIO_DEVICE_NAME);
+#elif BUILDFLAG(IS_CHROMECAST)
+  return "";
+#else
+  NOTREACHED();
+  return "";
+#endif
+}
+
+// static
+std::string AudioDeviceDescription::GetDefaultDeviceName(
+    const std::string& real_device_name) {
+  if (real_device_name.empty())
+    return GetDefaultDeviceName();
+  // TODO(guidou): Put the names together in a localized manner.
+  // http://crbug.com/788767
+  return GetDefaultDeviceName() + " - " + real_device_name;
+}
+
+// static
+std::string AudioDeviceDescription::GetCommunicationsDeviceName(
+    const std::string& real_device_name) {
+  if (real_device_name.empty())
+    return GetCommunicationsDeviceName();
+  // TODO(guidou): Put the names together in a localized manner.
+  // http://crbug.com/788767
+  return GetCommunicationsDeviceName() + " - " + real_device_name;
+}
+
+// static
+void AudioDeviceDescription::LocalizeDeviceDescriptions(
+    AudioDeviceDescriptions* device_descriptions) {
+  for (auto& description : *device_descriptions) {
+    if (media::AudioDeviceDescription::IsDefaultDevice(description.unique_id)) {
+      description.device_name =
+          media::AudioDeviceDescription::GetDefaultDeviceName(
+              description.device_name);
+    } else if (media::AudioDeviceDescription::IsCommunicationsDevice(
+                   description.unique_id)) {
+      description.device_name =
+          media::AudioDeviceDescription::GetCommunicationsDeviceName(
+              description.device_name);
+    }
+  }
+}
+
+AudioDeviceDescription::AudioDeviceDescription(std::string device_name,
+                                               std::string unique_id,
+                                               std::string group_id)
+    : device_name(std::move(device_name)),
+      unique_id(std::move(unique_id)),
+      group_id(std::move(group_id)) {}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_device_description.h b/third_party/chromium/media/audio/audio_device_description.h
new file mode 100644
index 0000000..ccba18e
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_device_description.h
@@ -0,0 +1,98 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEVICE_DESCRIPTION_H_
+#define MEDIA_AUDIO_AUDIO_DEVICE_DESCRIPTION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/unguessable_token.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Provides common information on audio device names and ids.
+struct MEDIA_EXPORT AudioDeviceDescription {
+  // Unique Id of the generic "default" device. Associated with the localized
+  // name returned from GetDefaultDeviceName().
+  static const char kDefaultDeviceId[];
+
+  // Unique Id of the generic default communications device. Associated with
+  // the localized name returned from GetCommunicationsDeviceName().
+  static const char kCommunicationsDeviceId[];
+
+  // Input device ID used to capture the default system playback stream. When
+  // this device ID is passed to MakeAudioInputStream() the returned
+  // AudioInputStream will be capturing audio currently being played on the
+  // default playback device. At the moment this feature is supported only on
+  // some platforms. AudioInputStream::Intialize() will return an error on
+  // platforms that don't support it. GetInputStreamParameters() must be used
+  // to get the parameters of the loopback device before creating a loopback
+  // stream, otherwise stream initialization may fail.
+  static const char kLoopbackInputDeviceId[];
+
+  // Similar to |kLoopbackInputDeviceId|, with only difference that this ID
+  // will mute system audio during capturing.
+  static const char kLoopbackWithMuteDeviceId[];
+
+  // Returns true if |device_id| represents the default device.
+  static bool IsDefaultDevice(const std::string& device_id);
+
+  // Returns true if |device_id| represents the communications device.
+  static bool IsCommunicationsDevice(const std::string& device_id);
+
+  // Returns true if |device_id| represents a loopback audio capture device.
+  static bool IsLoopbackDevice(const std::string& device_id);
+
+  // If |device_id| is not empty, |session_id| should be ignored and the output
+  // device should be selected basing on |device_id|.
+  // If |device_id| is empty and |session_id| is nonzero, output device
+  // associated with the opened input device designated by |session_id| should
+  // be used.
+  static bool UseSessionIdToSelectDevice(
+      const base::UnguessableToken& session_id,
+      const std::string& device_id);
+
+  // The functions dealing with localization are not reliable in the audio
+  // service, and should be avoided there.
+  // Returns the localized name of the generic "default" device.
+  static std::string GetDefaultDeviceName();
+
+  // Returns a localized version of name of the generic "default" device that
+  // includes the given |real_device_name|.
+  static std::string GetDefaultDeviceName(const std::string& real_device_name);
+
+  // Returns the localized name of the generic default communications device.
+  // This device is not supported on all platforms.
+  static std::string GetCommunicationsDeviceName();
+
+  // Returns a localized version of name of the generic communications device
+  // that includes the given |real_device_name|.
+  static std::string GetCommunicationsDeviceName(
+      const std::string& real_device_name);
+
+  // This prepends localized "Default" or "Communications" strings to
+  // default and communications device names in |device_descriptions|.
+  static void LocalizeDeviceDescriptions(
+      std::vector<AudioDeviceDescription>* device_descriptions);
+
+  AudioDeviceDescription() = default;
+  AudioDeviceDescription(const AudioDeviceDescription& other) = default;
+  AudioDeviceDescription(std::string device_name,
+                         std::string unique_id,
+                         std::string group_id);
+
+  ~AudioDeviceDescription() = default;
+
+  std::string device_name;  // Friendly name of the device.
+  std::string unique_id;    // Unique identifier for the device.
+  std::string group_id;     // Group identifier.
+};
+
+typedef std::vector<AudioDeviceDescription> AudioDeviceDescriptions;
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_DEVICE_DESCRIPTION_H_
diff --git a/third_party/chromium/media/audio/audio_device_info_accessor_for_tests.cc b/third_party/chromium/media/audio/audio_device_info_accessor_for_tests.cc
new file mode 100644
index 0000000..6c0a9d6
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_device_info_accessor_for_tests.cc
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_device_info_accessor_for_tests.h"
+
+#include "base/single_thread_task_runner.h"
+#include "media/audio/audio_manager.h"
+
+namespace media {
+
+AudioDeviceInfoAccessorForTests::AudioDeviceInfoAccessorForTests(
+    AudioManager* audio_manager)
+    : audio_manager_(audio_manager) {
+  DCHECK(audio_manager_);
+}
+
+bool AudioDeviceInfoAccessorForTests::HasAudioOutputDevices() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->HasAudioOutputDevices();
+}
+
+bool AudioDeviceInfoAccessorForTests::HasAudioInputDevices() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->HasAudioInputDevices();
+}
+
+void AudioDeviceInfoAccessorForTests::GetAudioInputDeviceDescriptions(
+    AudioDeviceDescriptions* device_descriptions) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  audio_manager_->GetAudioInputDeviceDescriptions(device_descriptions);
+}
+
+void AudioDeviceInfoAccessorForTests::GetAudioOutputDeviceDescriptions(
+    AudioDeviceDescriptions* device_descriptions) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  audio_manager_->GetAudioOutputDeviceDescriptions(device_descriptions);
+}
+
+AudioParameters
+AudioDeviceInfoAccessorForTests::GetDefaultOutputStreamParameters() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->GetDefaultOutputStreamParameters();
+}
+
+AudioParameters AudioDeviceInfoAccessorForTests::GetOutputStreamParameters(
+    const std::string& device_id) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->GetOutputStreamParameters(device_id);
+}
+
+AudioParameters AudioDeviceInfoAccessorForTests::GetInputStreamParameters(
+    const std::string& device_id) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->GetInputStreamParameters(device_id);
+}
+
+std::string AudioDeviceInfoAccessorForTests::GetAssociatedOutputDeviceID(
+    const std::string& input_device_id) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->GetAssociatedOutputDeviceID(input_device_id);
+}
+
+std::string AudioDeviceInfoAccessorForTests::GetDefaultInputDeviceID() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->GetDefaultInputDeviceID();
+}
+
+std::string AudioDeviceInfoAccessorForTests::GetDefaultOutputDeviceID() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->GetDefaultOutputDeviceID();
+}
+
+std::string AudioDeviceInfoAccessorForTests::GetCommunicationsInputDeviceID() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->GetCommunicationsInputDeviceID();
+}
+
+std::string AudioDeviceInfoAccessorForTests::GetCommunicationsOutputDeviceID() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return audio_manager_->GetCommunicationsOutputDeviceID();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_device_info_accessor_for_tests.h b/third_party/chromium/media/audio/audio_device_info_accessor_for_tests.h
new file mode 100644
index 0000000..79bedf7
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_device_info_accessor_for_tests.h
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEVICE_INFO_ACCESSOR_FOR_TESTS_H_
+#define MEDIA_AUDIO_AUDIO_DEVICE_INFO_ACCESSOR_FOR_TESTS_H_
+
+#include <string>
+
+#include "media/audio/audio_device_description.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioManager;
+
+// Accessor for protected device info-related AudioManager. To be used in media
+// unit tests only.
+class AudioDeviceInfoAccessorForTests {
+ public:
+  explicit AudioDeviceInfoAccessorForTests(AudioManager* audio_manager);
+
+  bool HasAudioOutputDevices();
+
+  bool HasAudioInputDevices();
+
+  void GetAudioInputDeviceDescriptions(
+      AudioDeviceDescriptions* device_descriptions);
+
+  void GetAudioOutputDeviceDescriptions(
+      AudioDeviceDescriptions* device_descriptions);
+
+  AudioParameters GetDefaultOutputStreamParameters();
+
+  AudioParameters GetOutputStreamParameters(const std::string& device_id);
+
+  AudioParameters GetInputStreamParameters(const std::string& device_id);
+
+  std::string GetAssociatedOutputDeviceID(const std::string& input_device_id);
+
+  std::string GetDefaultInputDeviceID();
+
+  std::string GetDefaultOutputDeviceID();
+
+  std::string GetCommunicationsInputDeviceID();
+
+  std::string GetCommunicationsOutputDeviceID();
+
+ private:
+  AudioManager* const audio_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioDeviceInfoAccessorForTests);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_DEVICE_INFO_ACCESSOR_FOR_TESTS_H_
diff --git a/third_party/chromium/media/audio/audio_device_name.cc b/third_party/chromium/media/audio/audio_device_name.cc
new file mode 100644
index 0000000..c14ba73
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_device_name.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_device_name.h"
+
+#include <utility>
+
+#include "media/audio/audio_device_description.h"
+
+namespace media {
+
+AudioDeviceName::AudioDeviceName() = default;
+
+AudioDeviceName::AudioDeviceName(std::string device_name, std::string unique_id)
+    : device_name(std::move(device_name)), unique_id(std::move(unique_id)) {}
+
+// static
+AudioDeviceName AudioDeviceName::CreateDefault() {
+  return AudioDeviceName(std::string(),
+                         AudioDeviceDescription::kDefaultDeviceId);
+}
+
+// static
+AudioDeviceName AudioDeviceName::CreateCommunications() {
+  return AudioDeviceName(std::string(),
+                         AudioDeviceDescription::kCommunicationsDeviceId);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_device_name.h b/third_party/chromium/media/audio/audio_device_name.h
new file mode 100644
index 0000000..a0ecfb5
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_device_name.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEVICE_NAME_H_
+#define MEDIA_AUDIO_AUDIO_DEVICE_NAME_H_
+
+#include <list>
+#include <string>
+#include "media/base/media_export.h"
+
+namespace media {
+
+struct MEDIA_EXPORT AudioDeviceName {
+  AudioDeviceName();
+  AudioDeviceName(std::string device_name, std::string unique_id);
+
+  // Creates default device representation.
+  // Shouldn't be used in the audio service, since the audio service doesn't
+  // have access to localized device names.
+  static AudioDeviceName CreateDefault();
+
+  // Creates communications device representation.
+  // Shouldn't be used in the audio service, since the audio service doesn't
+  // have access to localized device names.
+  static AudioDeviceName CreateCommunications();
+
+  std::string device_name;  // Friendly name of the device.
+  std::string unique_id;    // Unique identifier for the device.
+};
+
+typedef std::list<AudioDeviceName> AudioDeviceNames;
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_DEVICE_NAME_H_
diff --git a/third_party/chromium/media/audio/audio_device_thread.cc b/third_party/chromium/media/audio/audio_device_thread.cc
new file mode 100644
index 0000000..2340cc1
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_device_thread.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_device_thread.h"
+
+#include <limits>
+
+#include "base/check_op.h"
+#include "base/system/sys_info.h"
+#include "build/build_config.h"
+
+namespace media {
+
+// AudioDeviceThread::Callback implementation
+
+AudioDeviceThread::Callback::Callback(const AudioParameters& audio_parameters,
+                                      uint32_t segment_length,
+                                      uint32_t total_segments)
+    : audio_parameters_(audio_parameters),
+      memory_length_(
+          base::CheckMul(segment_length, total_segments).ValueOrDie()),
+      total_segments_(total_segments),
+      segment_length_(segment_length) {
+  CHECK_GT(total_segments_, 0u);
+  thread_checker_.DetachFromThread();
+}
+
+AudioDeviceThread::Callback::~Callback() = default;
+
+void AudioDeviceThread::Callback::InitializeOnAudioThread() {
+  // Normally this function is called before the thread checker is used
+  // elsewhere, but it's not guaranteed. DCHECK to ensure it was not used on
+  // another thread before we get here.
+  DCHECK(thread_checker_.CalledOnValidThread())
+      << "Thread checker was attached on the wrong thread";
+  MapSharedMemory();
+}
+
+// AudioDeviceThread implementation
+
+AudioDeviceThread::AudioDeviceThread(Callback* callback,
+                                     base::SyncSocket::ScopedHandle socket,
+                                     const char* thread_name,
+                                     base::ThreadPriority thread_priority)
+    : callback_(callback),
+      thread_name_(thread_name),
+      socket_(std::move(socket)) {
+#if defined(ARCH_CPU_X86)
+  // Audio threads don't need a huge stack, they don't have a message loop and
+  // they are used exclusively for polling the next frame of audio. See
+  // https://crbug.com/1141563 for discussion.
+  constexpr size_t kStackSize = 256 * 1024;
+#else
+  constexpr size_t kStackSize = 0;  // Default.
+#endif
+
+  CHECK(base::PlatformThread::CreateWithPriority(
+      kStackSize, this, &thread_handle_, thread_priority));
+
+  DCHECK(!thread_handle_.is_null());
+}
+
+AudioDeviceThread::~AudioDeviceThread() {
+  socket_.Shutdown();
+  if (thread_handle_.is_null())
+    return;
+  base::PlatformThread::Join(thread_handle_);
+}
+
+base::TimeDelta AudioDeviceThread::GetRealtimePeriod() {
+  return callback_->buffer_duration();
+}
+
+void AudioDeviceThread::ThreadMain() {
+  base::PlatformThread::SetName(thread_name_);
+  callback_->InitializeOnAudioThread();
+
+  uint32_t buffer_index = 0;
+  while (true) {
+    uint32_t pending_data = 0;
+    size_t bytes_read = socket_.Receive(&pending_data, sizeof(pending_data));
+    if (bytes_read != sizeof(pending_data))
+      break;
+
+    // std::numeric_limits<uint32_t>::max() is a special signal which is
+    // returned after the browser stops the output device in response to a
+    // renderer side request.
+    //
+    // Avoid running Process() for the paused signal, we still need to update
+    // the buffer index for synchronized buffers though.
+    //
+    // See comments in AudioOutputController::DoPause() for details on why.
+    if (pending_data != std::numeric_limits<uint32_t>::max())
+      callback_->Process(pending_data);
+
+    // The usage of synchronized buffers differs between input and output cases.
+    //
+    // Input: Let the other end know that we have read data, so that it can
+    // verify it doesn't overwrite any data before read. The |buffer_index|
+    // value is not used. For more details, see AudioInputSyncWriter::Write().
+    //
+    // Output: Let the other end know which buffer we just filled. The
+    // |buffer_index| is used to ensure the other end is getting the buffer it
+    // expects. For more details on how this works see
+    // AudioSyncReader::WaitUntilDataIsReady().
+    ++buffer_index;
+    size_t bytes_sent = socket_.Send(&buffer_index, sizeof(buffer_index));
+    if (bytes_sent != sizeof(buffer_index))
+      break;
+  }
+}
+
+}  // namespace media.
diff --git a/third_party/chromium/media/audio/audio_device_thread.h b/third_party/chromium/media/audio/audio_device_thread.h
new file mode 100644
index 0000000..dd88b91
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_device_thread.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_DEVICE_THREAD_H_
+#define MEDIA_AUDIO_AUDIO_DEVICE_THREAD_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/sync_socket.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread_checker.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Data transfer between browser and render process uses a combination
+// of sync sockets and shared memory. To read from the socket and render
+// data, we use a worker thread, a.k.a. the AudioDeviceThread, which reads
+// data from the browser via the socket and fills the shared memory from the
+// audio thread via the AudioDeviceThread::Callback interface/class.
+class MEDIA_EXPORT AudioDeviceThread : public base::PlatformThread::Delegate {
+ public:
+  // This is the callback interface/base class that Audio[Output|Input]Device
+  // implements to render input/output data. The callbacks run on the
+  // thread owned by AudioDeviceThread.
+  class Callback {
+   public:
+    Callback(const AudioParameters& audio_parameters,
+             uint32_t segment_length,
+             uint32_t total_segments);
+
+    // One time initialization for the callback object on the audio thread.
+    void InitializeOnAudioThread();
+
+    // Derived implementations must map shared memory appropriately before
+    // Process can be called.
+    virtual void MapSharedMemory() = 0;
+
+    // Called whenever we receive notifications about pending input data.
+    virtual void Process(uint32_t pending_data) = 0;
+
+    base::TimeDelta buffer_duration() const {
+      return audio_parameters_.GetBufferDuration();
+    }
+
+   protected:
+    virtual ~Callback();
+
+    // Protected so that derived classes can access directly.
+    // The variables are 'const' since values are calculated/set in the
+    // constructor and must never change.
+    const AudioParameters audio_parameters_;
+
+    const uint32_t memory_length_;
+    const uint32_t total_segments_;
+    const uint32_t segment_length_;
+
+    // Detached in constructor and attached in InitializeOnAudioThread() which
+    // is called on the audio device thread. Sub-classes can then use it for
+    // various thread checking purposes.
+    base::ThreadChecker thread_checker_;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Callback);
+  };
+
+  // Creates and automatically starts the audio thread.
+  AudioDeviceThread(Callback* callback,
+                    base::SyncSocket::ScopedHandle socket,
+                    const char* thread_name,
+                    base::ThreadPriority thread_priority);
+
+  // This tells the audio thread to stop and clean up the data; this is a
+  // synchronous process and the thread will stop before the method returns.
+  // Blocking call, see base/threading/thread_restrictions.h.
+  ~AudioDeviceThread() override;
+
+ private:
+  base::TimeDelta GetRealtimePeriod() final;
+  void ThreadMain() final;
+
+  Callback* const callback_;
+  const char* thread_name_;
+  base::CancelableSyncSocket socket_;
+  base::PlatformThreadHandle thread_handle_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioDeviceThread);
+};
+
+}  // namespace media.
+
+#endif  // MEDIA_AUDIO_AUDIO_DEVICE_THREAD_H_
diff --git a/third_party/chromium/media/audio/audio_encoders_unittest.cc b/third_party/chromium/media/audio/audio_encoders_unittest.cc
new file mode 100644
index 0000000..0be36ca
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_encoders_unittest.cc
@@ -0,0 +1,323 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "media/audio/audio_opus_encoder.h"
+#include "media/audio/simple_sources.h"
+#include "media/base/audio_encoder.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/status.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/opus/src/include/opus.h"
+
+namespace media {
+
+namespace {
+
+constexpr int kAudioSampleRate = 48000;
+
+// This is the preferred opus buffer duration (60 ms), which corresponds to a
+// value of 2880 frames per buffer (|kOpusFramesPerBuffer|).
+constexpr base::TimeDelta kOpusBufferDuration = base::Milliseconds(60);
+constexpr int kOpusFramesPerBuffer = kOpusBufferDuration.InMicroseconds() *
+                                     kAudioSampleRate /
+                                     base::Time::kMicrosecondsPerSecond;
+
+struct TestAudioParams {
+  const int channels;
+  const int sample_rate;
+};
+
+constexpr TestAudioParams kTestAudioParams[] = {
+    {2, kAudioSampleRate},
+    // Change to mono:
+    {1, kAudioSampleRate},
+    // Different sampling rate as well:
+    {1, 24000},
+    {2, 8000},
+    // Using a non-default Opus sampling rate (48, 24, 16, 12, or 8 kHz).
+    {1, 22050},
+    {2, 44100},
+    {2, 96000},
+    {1, kAudioSampleRate},
+    {2, kAudioSampleRate},
+};
+
+}  // namespace
+
+class AudioEncodersTest : public ::testing::TestWithParam<TestAudioParams> {
+ public:
+  AudioEncodersTest()
+      : audio_source_(GetParam().channels,
+                      /*freq=*/440,
+                      GetParam().sample_rate) {
+    options_.sample_rate = GetParam().sample_rate;
+    options_.channels = GetParam().channels;
+  }
+  AudioEncodersTest(const AudioEncodersTest&) = delete;
+  AudioEncodersTest& operator=(const AudioEncodersTest&) = delete;
+  ~AudioEncodersTest() override = default;
+
+  using MaybeDesc = absl::optional<AudioEncoder::CodecDescription>;
+
+  AudioEncoder* encoder() const { return encoder_.get(); }
+
+  void SetupEncoder(AudioEncoder::OutputCB output_cb) {
+    encoder_ = std::make_unique<AudioOpusEncoder>();
+
+    bool called_done = false;
+    AudioEncoder::StatusCB done_cb =
+        base::BindLambdaForTesting([&](Status error) {
+          if (!error.is_ok())
+            FAIL() << error.message();
+          called_done = true;
+        });
+
+    encoder_->Initialize(options_, std::move(output_cb), std::move(done_cb));
+
+    RunLoop();
+    EXPECT_TRUE(called_done);
+  }
+
+  // Produces an audio data that corresponds to a |buffer_duration_| and the
+  // sample rate of the current |options_|. The produced data is send to
+  // |encoder_| to be encoded, and the number of frames generated is returned.
+  int ProduceAudioAndEncode(
+      base::TimeTicks timestamp = base::TimeTicks::Now()) {
+    DCHECK(encoder_);
+    const int num_frames = options_.sample_rate * buffer_duration_.InSecondsF();
+    auto audio_bus = AudioBus::Create(options_.channels, num_frames);
+    audio_source_.OnMoreData(base::TimeDelta(), timestamp, 0, audio_bus.get());
+
+    bool called_done = false;
+    auto done_cb = base::BindLambdaForTesting([&](Status error) {
+      if (!error.is_ok())
+        FAIL() << error.message();
+      called_done = true;
+    });
+
+    encoder_->Encode(std::move(audio_bus), timestamp, std::move(done_cb));
+    RunLoop();
+    EXPECT_TRUE(called_done);
+    return num_frames;
+  }
+
+  void RunLoop() { task_environment_.RunUntilIdle(); }
+
+  base::test::TaskEnvironment task_environment_;
+
+  // The input params as initialized from the test's parameter.
+  AudioEncoder::Options options_;
+
+  // The audio source used to fill in the data of the |current_audio_bus_|.
+  SineWaveAudioSource audio_source_;
+
+  // The encoder the test is verifying.
+  std::unique_ptr<AudioEncoder> encoder_;
+
+  // The audio bus that was most recently generated and sent to the |encoder_|
+  // by ProduceAudioAndEncode().
+  std::unique_ptr<AudioBus> current_audio_bus_;
+
+  base::TimeDelta buffer_duration_ = base::Milliseconds(10);
+};
+
+TEST_P(AudioEncodersTest, OpusTimestamps) {
+  constexpr int kCount = 12;
+  for (base::TimeDelta duration :
+       {kOpusBufferDuration * 10, kOpusBufferDuration,
+        kOpusBufferDuration * 2 / 3}) {
+    buffer_duration_ = duration;
+    size_t expected_outputs = (buffer_duration_ * kCount) / kOpusBufferDuration;
+    base::TimeTicks current_timestamp;
+    std::vector<base::TimeTicks> timestamps;
+
+    auto output_cb =
+        base::BindLambdaForTesting([&](EncodedAudioBuffer output, MaybeDesc) {
+          timestamps.push_back(output.timestamp);
+        });
+
+    SetupEncoder(std::move(output_cb));
+
+    for (int i = 0; i < kCount; ++i) {
+      ProduceAudioAndEncode(current_timestamp);
+      current_timestamp += buffer_duration_;
+    }
+
+    bool flush_done = false;
+    auto done_cb = base::BindLambdaForTesting([&](Status error) {
+      if (!error.is_ok())
+        FAIL() << error.message();
+      flush_done = true;
+    });
+    encoder()->Flush(std::move(done_cb));
+    RunLoop();
+    EXPECT_TRUE(flush_done);
+    EXPECT_EQ(expected_outputs, timestamps.size());
+
+    current_timestamp = base::TimeTicks();
+    for (auto& ts : timestamps) {
+      auto drift = (current_timestamp - ts).magnitude();
+      EXPECT_LE(drift, base::Microseconds(1));
+      current_timestamp += kOpusBufferDuration;
+    }
+  }
+}
+
+TEST_P(AudioEncodersTest, OpusExtraData) {
+  std::vector<uint8_t> extra;
+  auto output_cb = base::BindLambdaForTesting(
+      [&](EncodedAudioBuffer output, MaybeDesc desc) {
+        DCHECK(desc.has_value());
+        extra = desc.value();
+      });
+
+  SetupEncoder(std::move(output_cb));
+  buffer_duration_ = kOpusBufferDuration;
+  ProduceAudioAndEncode();
+  RunLoop();
+
+  ASSERT_GT(extra.size(), 0u);
+  EXPECT_EQ(extra[0], 'O');
+  EXPECT_EQ(extra[1], 'p');
+  EXPECT_EQ(extra[2], 'u');
+  EXPECT_EQ(extra[3], 's');
+
+  uint16_t* sample_rate_ptr = reinterpret_cast<uint16_t*>(extra.data() + 12);
+  if (options_.sample_rate < std::numeric_limits<uint16_t>::max())
+    EXPECT_EQ(*sample_rate_ptr, options_.sample_rate);
+  else
+    EXPECT_EQ(*sample_rate_ptr, 48000);
+
+  uint8_t* channels_ptr = reinterpret_cast<uint8_t*>(extra.data() + 9);
+  EXPECT_EQ(*channels_ptr, options_.channels);
+
+  uint16_t* skip_ptr = reinterpret_cast<uint16_t*>(extra.data() + 10);
+  EXPECT_GT(*skip_ptr, 0);
+}
+
+// Check how Opus encoder reacts to breaks in continuity of incoming sound.
+// Under normal circumstances capture times are expected to be exactly
+// a buffer's duration apart, but if they are not, the encoder just ignores
+// incoming capture times. In other words the only capture times that matter
+// are
+//   1. timestamp of the first encoded buffer
+//   2. timestamps of buffers coming immediately after Flush() calls.
+TEST_P(AudioEncodersTest, OpusTimeContinuityBreak) {
+  base::TimeTicks current_timestamp = base::TimeTicks::Now();
+  base::TimeDelta gap = base::Microseconds(1500);
+  buffer_duration_ = kOpusBufferDuration;
+  std::vector<base::TimeTicks> timestamps;
+
+  auto output_cb =
+      base::BindLambdaForTesting([&](EncodedAudioBuffer output, MaybeDesc) {
+        timestamps.push_back(output.timestamp);
+      });
+
+  SetupEncoder(std::move(output_cb));
+
+  // Encode first normal buffer and immediately get an output for it.
+  auto ts0 = current_timestamp;
+  ProduceAudioAndEncode(current_timestamp);
+  current_timestamp += buffer_duration_;
+  EXPECT_EQ(1u, timestamps.size());
+  EXPECT_EQ(ts0, timestamps[0]);
+
+  // Encode another buffer after a large gap, output timestamp should
+  // disregard the gap.
+  auto ts1 = current_timestamp;
+  current_timestamp += gap;
+  ProduceAudioAndEncode(current_timestamp);
+  current_timestamp += buffer_duration_;
+  EXPECT_EQ(2u, timestamps.size());
+  EXPECT_EQ(ts1, timestamps[1]);
+
+  // Another buffer without a gap.
+  auto ts2 = ts1 + buffer_duration_;
+  ProduceAudioAndEncode(current_timestamp);
+  EXPECT_EQ(3u, timestamps.size());
+  EXPECT_EQ(ts2, timestamps[2]);
+
+  encoder()->Flush(base::BindOnce([](Status error) {
+    if (!error.is_ok())
+      FAIL() << error.message();
+  }));
+  RunLoop();
+
+  // Reset output timestamp after Flush(), the encoder should start producing
+  // timestamps from new base 0.
+  current_timestamp = base::TimeTicks();
+
+  auto ts3 = current_timestamp;
+  ProduceAudioAndEncode(current_timestamp);
+  current_timestamp += buffer_duration_;
+  EXPECT_EQ(4u, timestamps.size());
+  EXPECT_EQ(ts3, timestamps[3]);
+}
+
+TEST_P(AudioEncodersTest, FullCycleEncodeDecode) {
+  int error;
+  int encode_callback_count = 0;
+  std::vector<float> buffer(kOpusFramesPerBuffer * options_.channels);
+  OpusDecoder* opus_decoder =
+      opus_decoder_create(kAudioSampleRate, options_.channels, &error);
+  ASSERT_TRUE(error == OPUS_OK && opus_decoder);
+  int total_frames = 0;
+
+  auto verify_opus_encoding = [&](EncodedAudioBuffer output, MaybeDesc) {
+    ++encode_callback_count;
+
+    // Use the libopus decoder to decode the |encoded_data| and check we
+    // get the expected number of frames per buffer.
+    EXPECT_EQ(kOpusFramesPerBuffer,
+              opus_decode_float(opus_decoder, output.encoded_data.get(),
+                                output.encoded_data_size, buffer.data(),
+                                kOpusFramesPerBuffer, 0));
+  };
+
+  SetupEncoder(base::BindLambdaForTesting(verify_opus_encoding));
+
+  // The opus encoder encodes in multiple of 60 ms. Wait for the total number of
+  // frames that will be generated in 60 ms at the input sampling rate.
+  const int frames_in_60_ms =
+      kOpusBufferDuration.InSecondsF() * options_.sample_rate;
+
+  base::TimeTicks time;
+  while (total_frames < frames_in_60_ms) {
+    total_frames += ProduceAudioAndEncode(time);
+    time += buffer_duration_;
+  }
+
+  EXPECT_EQ(1, encode_callback_count);
+
+  // If there are remaining frames in the opus encoder FIFO, we need to flush
+  // them before we destroy the encoder. Flushing should trigger the encode
+  // callback and we should be able to decode the resulting encoded frames.
+  if (total_frames > frames_in_60_ms) {
+    encoder()->Flush(base::BindOnce([](Status error) {
+      if (!error.is_ok())
+        FAIL() << error.message();
+    }));
+    RunLoop();
+    EXPECT_EQ(2, encode_callback_count);
+  }
+
+  opus_decoder_destroy(opus_decoder);
+  opus_decoder = nullptr;
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         AudioEncodersTest,
+                         testing::ValuesIn(kTestAudioParams));
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_features.cc b/third_party/chromium/media/audio/audio_features.cc
new file mode 100644
index 0000000..dd1c1f3
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_features.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_features.h"
+#include "base/feature_list.h"
+#include "build/chromeos_buildflags.h"
+
+namespace features {
+
+// When the audio service in a separate process, kill it when a hang is
+// detected. It will be restarted when needed.
+const base::Feature kAudioServiceOutOfProcessKillAtHang{
+  "AudioServiceOutOfProcessKillAtHang",
+#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
+    defined(OS_CHROMEOS)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
+
+// If enabled, base::DumpWithoutCrashing is called whenever an audio service
+// hang is detected.
+const base::Feature kDumpOnAudioServiceHang{"DumpOnAudioServiceHang",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+
+#if defined(OS_ANDROID)
+// Enables loading and using AAudio instead of OpenSLES on compatible devices,
+// for audio output streams.
+const base::Feature kUseAAudioDriver{"UseAAudioDriver",
+                                     base::FEATURE_ENABLED_BY_DEFAULT};
+#endif
+
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+const base::Feature kCrOSSystemAEC{"CrOSSystemAECWithBoardTuningsAllowed",
+                                   base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kCrOSSystemAECDeactivatedGroups{
+    "CrOSSystemAECDeactivatedGroups", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kCrOSEnforceSystemAecNsAgc{
+    "CrOSEnforceSystemAecNsAgc", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kCrOSEnforceSystemAecNs{"CrOSEnforceSystemAecNs",
+                                            base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kCrOSEnforceSystemAecAgc{"CrOSEnforceSystemAecAgc",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kCrOSEnforceSystemAec{"CrOSEnforceSystemAec",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
+#endif
+
+#if defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
+const base::Feature kForceEnableSystemAec{"ForceEnableSystemAec",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
+#if defined(OS_WIN)
+const base::Feature kAllowIAudioClient3{"AllowIAudioClient3",
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
+#endif
+}  // namespace features
diff --git a/third_party/chromium/media/audio/audio_features.h b/third_party/chromium/media/audio/audio_features.h
new file mode 100644
index 0000000..38bcf40
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_features.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_FEATURES_H_
+#define MEDIA_AUDIO_AUDIO_FEATURES_H_
+
+#include "base/feature_list.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+#include "media/base/media_export.h"
+
+namespace features {
+
+MEDIA_EXPORT extern const base::Feature kAudioServiceOutOfProcessKillAtHang;
+MEDIA_EXPORT extern const base::Feature kDumpOnAudioServiceHang;
+
+#if defined(OS_ANDROID)
+MEDIA_EXPORT extern const base::Feature kUseAAudioDriver;
+#endif
+
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+MEDIA_EXPORT extern const base::Feature kCrOSSystemAEC;
+MEDIA_EXPORT extern const base::Feature kCrOSSystemAECDeactivatedGroups;
+MEDIA_EXPORT extern const base::Feature kCrOSEnforceSystemAecNsAgc;
+MEDIA_EXPORT extern const base::Feature kCrOSEnforceSystemAecNs;
+MEDIA_EXPORT extern const base::Feature kCrOSEnforceSystemAecAgc;
+MEDIA_EXPORT extern const base::Feature kCrOSEnforceSystemAec;
+#endif
+
+#if defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
+MEDIA_EXPORT extern const base::Feature kForceEnableSystemAec;
+#endif
+
+#if defined(OS_WIN)
+MEDIA_EXPORT extern const base::Feature kAllowIAudioClient3;
+#endif
+
+}  // namespace features
+
+#endif  // MEDIA_AUDIO_AUDIO_FEATURES_H_
diff --git a/third_party/chromium/media/audio/audio_input_delegate.cc b/third_party/chromium/media/audio/audio_input_delegate.cc
new file mode 100644
index 0000000..1310c00
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_delegate.cc
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_input_delegate.h"
+
+media::AudioInputDelegate::EventHandler::~EventHandler() = default;
+
+media::AudioInputDelegate::~AudioInputDelegate() = default;
diff --git a/third_party/chromium/media/audio/audio_input_delegate.h b/third_party/chromium/media/audio/audio_input_delegate.h
new file mode 100644
index 0000000..203c978
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_delegate.h
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_INPUT_DELEGATE_H_
+#define MEDIA_AUDIO_AUDIO_INPUT_DELEGATE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/base/media_export.h"
+
+namespace base {
+class CancelableSyncSocket;
+class ReadOnlySharedMemoryRegion;
+}  // namespace base
+
+namespace media {
+
+class MEDIA_EXPORT AudioInputDelegate {
+ public:
+  // An AudioInputDelegate must not call back to its EventHandler in its
+  // constructor.
+  class MEDIA_EXPORT EventHandler {
+   public:
+    virtual ~EventHandler() = 0;
+
+    // Called when the underlying stream is ready for recording.
+    virtual void OnStreamCreated(
+        int stream_id,
+        base::ReadOnlySharedMemoryRegion shared_memory_region,
+        std::unique_ptr<base::CancelableSyncSocket> socket,
+        bool initially_muted) = 0;
+
+    // Called when the microphone is muted/unmuted.
+    virtual void OnMuted(int stream_id, bool is_muted) = 0;
+
+    // Called if stream encounters an error and has become unusable.
+    virtual void OnStreamError(int stream_id) = 0;
+  };
+
+  virtual ~AudioInputDelegate() = 0;
+
+  virtual int GetStreamId() = 0;
+
+  // Stream control:
+  virtual void OnRecordStream() = 0;
+  virtual void OnSetVolume(double volume) = 0;
+  virtual void OnSetOutputDeviceForAec(
+      const std::string& raw_output_device_id) = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_INPUT_DELEGATE_H_
diff --git a/third_party/chromium/media/audio/audio_input_device.cc b/third_party/chromium/media/audio/audio_input_device.cc
new file mode 100644
index 0000000..8b238c3
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_device.cc
@@ -0,0 +1,483 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_input_device.h"
+
+#include <stdint.h>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/base/audio_bus.h"
+
+namespace media {
+
+namespace {
+
+// The number of shared memory buffer segments indicated to browser process
+// in order to avoid data overwriting. This number can be any positive number,
+// dependent how fast the renderer process can pick up captured data from
+// shared memory.
+const int kRequestedSharedMemoryCount = 10;
+
+// The number of seconds with missing callbacks before we report a capture
+// error. The value is based on that the Mac audio implementation can defer
+// start for 5 seconds when resuming after standby, and has a startup success
+// check 5 seconds after actually starting, where stats is logged. We must allow
+// enough time for this. See AUAudioInputStream::CheckInputStartupSuccess().
+const int kMissingCallbacksTimeBeforeErrorSeconds = 12;
+
+// The interval for checking missing callbacks.
+const int kCheckMissingCallbacksIntervalSeconds = 5;
+
+// How often AudioInputDevice::AudioThreadCallback informs that it has gotten
+// data from the source.
+const int kGotDataCallbackIntervalSeconds = 1;
+
+base::ThreadPriority ThreadPriorityFromPurpose(
+    AudioInputDevice::Purpose purpose) {
+  switch (purpose) {
+    case AudioInputDevice::Purpose::kUserInput:
+      return base::ThreadPriority::REALTIME_AUDIO;
+    case AudioInputDevice::Purpose::kLoopback:
+      return base::ThreadPriority::NORMAL;
+  }
+}
+
+}  // namespace
+
+// Takes care of invoking the capture callback on the audio thread.
+// An instance of this class is created for each capture stream in
+// OnLowLatencyCreated().
+class AudioInputDevice::AudioThreadCallback
+    : public AudioDeviceThread::Callback {
+ public:
+  AudioThreadCallback(const AudioParameters& audio_parameters,
+                      base::ReadOnlySharedMemoryRegion shared_memory_region,
+                      uint32_t total_segments,
+                      bool enable_uma,
+                      CaptureCallback* capture_callback,
+                      base::RepeatingClosure got_data_callback);
+
+  AudioThreadCallback(const AudioThreadCallback&) = delete;
+  AudioThreadCallback& operator=(const AudioThreadCallback&) = delete;
+
+  ~AudioThreadCallback() override;
+
+  void MapSharedMemory() override;
+
+  // Called whenever we receive notifications about pending data.
+  void Process(uint32_t pending_data) override;
+
+ private:
+  const bool enable_uma_;
+  base::ReadOnlySharedMemoryRegion shared_memory_region_;
+  base::ReadOnlySharedMemoryMapping shared_memory_mapping_;
+  const base::TimeTicks start_time_;
+  size_t current_segment_id_;
+  uint32_t last_buffer_id_;
+  std::vector<std::unique_ptr<const media::AudioBus>> audio_buses_;
+  CaptureCallback* capture_callback_;
+
+  // Used for informing AudioInputDevice that we have gotten data, i.e. the
+  // stream is alive. |got_data_callback_| is run every
+  // |got_data_callback_interval_in_frames_| frames, calculated from
+  // kGotDataCallbackIntervalSeconds.
+  const int got_data_callback_interval_in_frames_;
+  int frames_since_last_got_data_callback_;
+  base::RepeatingClosure got_data_callback_;
+};
+
+AudioInputDevice::AudioInputDevice(std::unique_ptr<AudioInputIPC> ipc,
+                                   Purpose purpose,
+                                   DeadStreamDetection detect_dead_stream)
+    : thread_priority_(ThreadPriorityFromPurpose(purpose)),
+      enable_uma_(purpose == AudioInputDevice::Purpose::kUserInput),
+      callback_(nullptr),
+      ipc_(std::move(ipc)),
+      state_(IDLE),
+      agc_is_enabled_(false),
+      detect_dead_stream_(detect_dead_stream) {
+  CHECK(ipc_);
+
+  // The correctness of the code depends on the relative values assigned in the
+  // State enum.
+  static_assert(IPC_CLOSED < IDLE, "invalid enum value assignment 0");
+  static_assert(IDLE < CREATING_STREAM, "invalid enum value assignment 1");
+  static_assert(CREATING_STREAM < RECORDING, "invalid enum value assignment 2");
+}
+
+void AudioInputDevice::Initialize(const AudioParameters& params,
+                                  CaptureCallback* callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(params.IsValid());
+  DCHECK(!callback_);
+  audio_parameters_ = params;
+  callback_ = callback;
+}
+
+void AudioInputDevice::Start() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(callback_) << "Initialize hasn't been called";
+  TRACE_EVENT0("audio", "AudioInputDevice::Start");
+
+  // Make sure we don't call Start() more than once.
+  if (state_ != IDLE)
+    return;
+
+  state_ = CREATING_STREAM;
+  ipc_->CreateStream(this, audio_parameters_, agc_is_enabled_,
+                     kRequestedSharedMemoryCount);
+}
+
+void AudioInputDevice::Stop() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT0("audio", "AudioInputDevice::Stop");
+
+  if (enable_uma_) {
+    if (detect_dead_stream_ == DeadStreamDetection::kEnabled) {
+      UMA_HISTOGRAM_BOOLEAN(
+          "Media.Audio.Capture.DetectedMissingCallbacks",
+          alive_checker_ ? alive_checker_->DetectedDead() : false);
+    }
+
+    UMA_HISTOGRAM_ENUMERATION("Media.Audio.Capture.StreamCallbackError2",
+                              had_error_);
+  }
+  had_error_ = kNoError;
+
+  // Close the stream, if we haven't already.
+  if (state_ >= CREATING_STREAM) {
+    ipc_->CloseStream();
+    state_ = IDLE;
+    agc_is_enabled_ = false;
+  }
+
+  // We can run into an issue where Stop is called right after
+  // OnStreamCreated is called in cases where Start/Stop are called before we
+  // get the OnStreamCreated callback.  To handle that corner case, we call
+  // audio_thread_.reset(). In most cases, the thread will already be stopped.
+  //
+  // |alive_checker_| must outlive |audio_callback_|.
+  base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_thread_join;
+  audio_thread_.reset();
+  audio_callback_.reset();
+  alive_checker_.reset();
+}
+
+void AudioInputDevice::SetVolume(double volume) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT1("audio", "AudioInputDevice::SetVolume", "volume", volume);
+
+  if (volume < 0 || volume > 1.0) {
+    DLOG(ERROR) << "Invalid volume value specified";
+    return;
+  }
+
+  if (state_ >= CREATING_STREAM)
+    ipc_->SetVolume(volume);
+}
+
+void AudioInputDevice::SetAutomaticGainControl(bool enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT1("audio", "AudioInputDevice::SetAutomaticGainControl", "enabled",
+               enabled);
+
+  if (state_ >= CREATING_STREAM) {
+    DLOG(WARNING) << "The AGC state can not be modified after starting.";
+    return;
+  }
+
+  // We simply store the new AGC setting here. This value will be used when
+  // a new stream is initialized and by GetAutomaticGainControl().
+  agc_is_enabled_ = enabled;
+}
+
+void AudioInputDevice::SetOutputDeviceForAec(
+    const std::string& output_device_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT1("audio", "AudioInputDevice::SetOutputDeviceForAec",
+               "output_device_id", output_device_id);
+
+  output_device_id_for_aec_ = output_device_id;
+  if (state_ > CREATING_STREAM)
+    ipc_->SetOutputDeviceForAec(output_device_id);
+}
+
+void AudioInputDevice::OnStreamCreated(
+    base::ReadOnlySharedMemoryRegion shared_memory_region,
+    base::SyncSocket::ScopedHandle socket_handle,
+    bool initially_muted) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT0("audio", "AudioInputDevice::OnStreamCreated");
+  DCHECK(shared_memory_region.IsValid());
+#if defined(OS_WIN)
+  DCHECK(socket_handle.IsValid());
+#else
+  DCHECK(socket_handle.is_valid());
+#endif
+  DCHECK_GT(shared_memory_region.GetSize(), 0u);
+
+  if (state_ != CREATING_STREAM)
+    return;
+
+  DCHECK(!audio_callback_);
+  DCHECK(!audio_thread_);
+
+  if (initially_muted)
+    callback_->OnCaptureMuted(true);
+
+  if (auto* controls = ipc_->GetProcessorControls())
+    callback_->OnCaptureProcessorCreated(controls);
+
+  if (output_device_id_for_aec_)
+    ipc_->SetOutputDeviceForAec(*output_device_id_for_aec_);
+
+// Set up checker for detecting missing audio data. We pass a callback which
+// holds a reference to this. |alive_checker_| is deleted in
+// Stop() which we expect to always be called (see comment in
+// destructor). Suspend/resume notifications are not supported on Linux and
+// there's a risk of false positives when suspending. So on Linux we only detect
+// missing audio data until the first audio buffer arrives. Note that there's
+// also a risk of false positives if we are suspending when starting the stream
+// here. See comments in AliveChecker and PowerObserverHelper for details and
+// todos.
+  if (detect_dead_stream_ == DeadStreamDetection::kEnabled) {
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+    const bool stop_at_first_alive_notification = true;
+    const bool pause_check_during_suspend = false;
+#else
+  const bool stop_at_first_alive_notification = false;
+  const bool pause_check_during_suspend = true;
+#endif
+    alive_checker_ = std::make_unique<AliveChecker>(
+        base::BindRepeating(&AudioInputDevice::DetectedDeadInputStream, this),
+        base::Seconds(kCheckMissingCallbacksIntervalSeconds),
+        base::Seconds(kMissingCallbacksTimeBeforeErrorSeconds),
+        stop_at_first_alive_notification, pause_check_during_suspend);
+  }
+
+  // Unretained is safe since |alive_checker_| outlives |audio_callback_|.
+  base::RepeatingClosure notify_alive_closure =
+      alive_checker_
+          ? base::BindRepeating(&AliveChecker::NotifyAlive,
+                                base::Unretained(alive_checker_.get()))
+          : base::DoNothing();
+
+  audio_callback_ = std::make_unique<AudioInputDevice::AudioThreadCallback>(
+      audio_parameters_, std::move(shared_memory_region),
+      kRequestedSharedMemoryCount, enable_uma_, callback_,
+      notify_alive_closure);
+  audio_thread_ = std::make_unique<AudioDeviceThread>(
+      audio_callback_.get(), std::move(socket_handle), "AudioInputDevice",
+      thread_priority_);
+
+  state_ = RECORDING;
+  ipc_->RecordStream();
+
+  // Start detecting missing audio data.
+  if (alive_checker_)
+    alive_checker_->Start();
+}
+
+void AudioInputDevice::OnError(AudioCapturerSource::ErrorCode code) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT0("audio", "AudioInputDevice::OnError");
+
+  // Do nothing if the stream has been closed.
+  if (state_ < CREATING_STREAM)
+    return;
+
+  if (state_ == CREATING_STREAM) {
+    // At this point, we haven't attempted to start the audio thread.
+    // Accessing the hardware might have failed or we may have reached
+    // the limit of the number of allowed concurrent streams.
+    // We must report the error to the |callback_| so that a potential
+    // audio source object will enter the correct state (e.g. 'ended' for
+    // a local audio source).
+    had_error_ = kErrorDuringCreation;
+    callback_->OnCaptureError(
+        code, code == AudioCapturerSource::ErrorCode::kSystemPermissions
+                  ? "Unable to open due to failing an OS Permissions check."
+                  : "Maximum allowed input device limit reached or an OS "
+                    "failure occured.");
+  } else {
+    // Don't dereference the callback object if the audio thread
+    // is stopped or stopping.  That could mean that the callback
+    // object has been deleted.
+    // TODO(tommi): Add an explicit contract for clearing the callback
+    // object.  Possibly require calling Initialize again or provide
+    // a callback object via Start() and clear it in Stop().
+    had_error_ = kErrorDuringCapture;
+    if (audio_thread_)
+      callback_->OnCaptureError(code, "IPC delegate state error.");
+  }
+}
+
+void AudioInputDevice::OnMuted(bool is_muted) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT0("audio", "AudioInputDevice::OnMuted");
+
+  // Do nothing if the stream has been closed.
+  if (state_ < CREATING_STREAM)
+    return;
+  callback_->OnCaptureMuted(is_muted);
+}
+
+void AudioInputDevice::OnIPCClosed() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  TRACE_EVENT0("audio", "AudioInputDevice::OnIPCClosed");
+
+  state_ = IPC_CLOSED;
+  ipc_.reset();
+}
+
+AudioInputDevice::~AudioInputDevice() {
+#if DCHECK_IS_ON()
+  // Make sure we've stopped the stream properly before destructing |this|.
+  DCHECK_LE(state_, IDLE);
+  DCHECK(!audio_thread_);
+  DCHECK(!audio_callback_);
+  DCHECK(!alive_checker_);
+#endif  // DCHECK_IS_ON()
+}
+
+void AudioInputDevice::DetectedDeadInputStream() {
+  callback_->OnCaptureError(media::AudioCapturerSource::ErrorCode::kUnknown,
+                            "No audio received from audio capture device.");
+}
+
+// AudioInputDevice::AudioThreadCallback
+AudioInputDevice::AudioThreadCallback::AudioThreadCallback(
+    const AudioParameters& audio_parameters,
+    base::ReadOnlySharedMemoryRegion shared_memory_region,
+    uint32_t total_segments,
+    bool enable_uma,
+    CaptureCallback* capture_callback,
+    base::RepeatingClosure got_data_callback_)
+    : AudioDeviceThread::Callback(
+          audio_parameters,
+          ComputeAudioInputBufferSize(audio_parameters, 1u),
+          total_segments),
+      enable_uma_(enable_uma),
+      shared_memory_region_(std::move(shared_memory_region)),
+      start_time_(base::TimeTicks::Now()),
+      current_segment_id_(0u),
+      last_buffer_id_(UINT32_MAX),
+      capture_callback_(capture_callback),
+      got_data_callback_interval_in_frames_(kGotDataCallbackIntervalSeconds *
+                                            audio_parameters.sample_rate()),
+      frames_since_last_got_data_callback_(0),
+      got_data_callback_(std::move(got_data_callback_)) {
+  // CHECK that the shared memory is large enough. The memory allocated must
+  // be at least as large as expected.
+  CHECK_LE(memory_length_, shared_memory_region_.GetSize());
+}
+
+AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() {
+  if (enable_uma_) {
+    UMA_HISTOGRAM_LONG_TIMES("Media.Audio.Capture.InputStreamDuration",
+                             base::TimeTicks::Now() - start_time_);
+  }
+}
+
+void AudioInputDevice::AudioThreadCallback::MapSharedMemory() {
+  shared_memory_mapping_ = shared_memory_region_.MapAt(0, memory_length_);
+
+  // Create vector of audio buses by wrapping existing blocks of memory.
+  const uint8_t* ptr =
+      static_cast<const uint8_t*>(shared_memory_mapping_.memory());
+  for (uint32_t i = 0; i < total_segments_; ++i) {
+    const media::AudioInputBuffer* buffer =
+        reinterpret_cast<const media::AudioInputBuffer*>(ptr);
+    audio_buses_.push_back(
+        media::AudioBus::WrapReadOnlyMemory(audio_parameters_, buffer->audio));
+    ptr += segment_length_;
+  }
+
+  // Indicate that browser side capture initialization has succeeded and IPC
+  // channel initialized. This effectively completes the
+  // AudioCapturerSource::Start()' phase as far as the caller of that function
+  // is concerned.
+  capture_callback_->OnCaptureStarted();
+}
+
+void AudioInputDevice::AudioThreadCallback::Process(uint32_t pending_data) {
+  TRACE_EVENT_BEGIN0("audio", "AudioInputDevice::AudioThreadCallback::Process");
+  // The shared memory represents parameters, size of the data buffer and the
+  // actual data buffer containing audio data. Map the memory into this
+  // structure and parse out parameters and the data area.
+  const uint8_t* ptr =
+      static_cast<const uint8_t*>(shared_memory_mapping_.memory());
+  ptr += current_segment_id_ * segment_length_;
+  const AudioInputBuffer* buffer =
+      reinterpret_cast<const AudioInputBuffer*>(ptr);
+
+  // Usually this will be equal but in the case of low sample rate (e.g. 8kHz,
+  // the buffer may be bigger (on mac at least)).
+  DCHECK_GE(buffer->params.size,
+            segment_length_ - sizeof(AudioInputBufferParameters));
+
+  // Verify correct sequence.
+  if (buffer->params.id != last_buffer_id_ + 1) {
+    std::string message = base::StringPrintf(
+        "Incorrect buffer sequence. Expected = %u. Actual = %u.",
+        last_buffer_id_ + 1, buffer->params.id);
+    LOG(ERROR) << message;
+    capture_callback_->OnCaptureError(
+        media::AudioCapturerSource::ErrorCode::kUnknown, message);
+  }
+  if (current_segment_id_ != pending_data) {
+    std::string message = base::StringPrintf(
+        "Segment id not matching. Remote = %u. Local = %" PRIuS ".",
+        pending_data, current_segment_id_);
+    LOG(ERROR) << message;
+    capture_callback_->OnCaptureError(
+        media::AudioCapturerSource::ErrorCode::kUnknown, message);
+  }
+  last_buffer_id_ = buffer->params.id;
+
+  // Use pre-allocated audio bus wrapping existing block of shared memory.
+  const media::AudioBus* audio_bus = audio_buses_[current_segment_id_].get();
+
+  // Regularly inform that we have gotten data.
+  frames_since_last_got_data_callback_ += audio_bus->frames();
+  if (frames_since_last_got_data_callback_ >=
+      got_data_callback_interval_in_frames_) {
+    got_data_callback_.Run();
+    frames_since_last_got_data_callback_ = 0;
+  }
+
+  // Deliver captured data to the client in floating point format and update
+  // the audio delay measurement.
+  // TODO(olka, tommi): Take advantage of |capture_time| in the renderer.
+  const base::TimeTicks capture_time =
+      base::TimeTicks() + base::Microseconds(buffer->params.capture_time_us);
+  const base::TimeTicks now_time = base::TimeTicks::Now();
+  DCHECK_GE(now_time, capture_time);
+
+  capture_callback_->Capture(audio_bus, capture_time, buffer->params.volume,
+                             buffer->params.key_pressed);
+
+  if (++current_segment_id_ >= total_segments_)
+    current_segment_id_ = 0u;
+
+  TRACE_EVENT_END2(
+      "audio", "AudioInputDevice::AudioThreadCallback::Process",
+      "capture_time (ms)", (capture_time - base::TimeTicks()).InMillisecondsF(),
+      "now_time (ms)", (now_time - base::TimeTicks()).InMillisecondsF());
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_input_device.h b/third_party/chromium/media/audio/audio_input_device.h
new file mode 100644
index 0000000..ab9d099
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_device.h
@@ -0,0 +1,170 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Low-latency audio capturing class utilizing audio input stream provided
+// by a server process by use of an IPC interface.
+//
+// Relationship of classes:
+//
+//  AudioInputController                 AudioInputDevice
+//           ^                                  ^
+//           |                                  |
+//           v                  IPC             v
+// MojoAudioInputStream    <----------->  AudioInputIPC
+//           ^                            (MojoAudioInputIPC)
+//           |
+//           v
+// AudioInputDeviceManager
+//
+// Transportation of audio samples from the browser to the render process
+// is done by using shared memory in combination with a SyncSocket.
+// The AudioInputDevice user registers an AudioInputDevice::CaptureCallback by
+// calling Initialize().  The callback will be called with recorded audio from
+// the underlying audio layers.
+// The session ID is used by the RenderFrameAudioInputStreamFactory to start
+// the device referenced by this ID.
+//
+// State sequences:
+//
+// Start -> CreateStream ->
+//       <- OnStreamCreated <-
+//       -> RecordStream ->
+//
+// AudioInputDevice::Capture => low latency audio transport on audio thread =>
+//
+// Stop ->  CloseStream -> Close
+//
+// This class depends on the audio transport thread. That thread is responsible
+// for calling the CaptureCallback and feeding it audio samples from the server
+// side audio layer using a socket and shared memory.
+//
+// Implementation notes:
+// - The user must call Stop() before deleting the class instance.
+
+#ifndef MEDIA_AUDIO_AUDIO_INPUT_DEVICE_H_
+#define MEDIA_AUDIO_AUDIO_INPUT_DEVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/sequence_checker.h"
+#include "base/threading/platform_thread.h"
+#include "media/audio/alive_checker.h"
+#include "media/audio/audio_device_thread.h"
+#include "media/audio/audio_input_ipc.h"
+#include "media/base/audio_capturer_source.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+class MEDIA_EXPORT AudioInputDevice : public AudioCapturerSource,
+                                      public AudioInputIPCDelegate {
+ public:
+  enum Purpose : int8_t { kUserInput, kLoopback };
+  enum class DeadStreamDetection : bool { kDisabled = false, kEnabled = true };
+
+  // NOTE: Clients must call Initialize() before using.
+  // |enable_uma| controls logging of UMA stats. It is used to ensure that
+  // stats are not logged for mirroring service streams.
+  // |detect_dead_stream| controls the dead stream detection.
+  AudioInputDevice(std::unique_ptr<AudioInputIPC> ipc,
+                   Purpose purpose,
+                   DeadStreamDetection detect_dead_stream);
+
+  // AudioCapturerSource implementation.
+  void Initialize(const AudioParameters& params,
+                  CaptureCallback* callback) override;
+  void Start() override;
+  void Stop() override;
+  void SetVolume(double volume) override;
+  void SetAutomaticGainControl(bool enabled) override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+ private:
+  friend class base::RefCountedThreadSafe<AudioInputDevice>;
+
+  // Our audio thread callback class.  See source file for details.
+  class AudioThreadCallback;
+
+  // Note: The ordering of members in this enum is critical to correct behavior!
+  enum State {
+    IPC_CLOSED,       // No more IPCs can take place.
+    IDLE,             // Not started.
+    CREATING_STREAM,  // Waiting for OnStreamCreated() to be called back.
+    RECORDING,        // Receiving audio data.
+  };
+
+  // This enum is used for UMA, so the only allowed operation on this definition
+  // is to add new states to the bottom, update kMaxValue, and update the
+  // histogram "Media.Audio.Capture.StreamCallbackError2".
+  enum Error {
+    kNoError = 0,
+    kErrorDuringCreation = 1,
+    kErrorDuringCapture = 2,
+    kMaxValue = kErrorDuringCapture
+  };
+
+  ~AudioInputDevice() override;
+
+  // AudioInputIPCDelegate implementation.
+  void OnStreamCreated(base::ReadOnlySharedMemoryRegion shared_memory_region,
+                       base::SyncSocket::ScopedHandle socket_handle,
+                       bool initially_muted) override;
+  void OnError(AudioCapturerSource::ErrorCode code) override;
+  void OnMuted(bool is_muted) override;
+  void OnIPCClosed() override;
+
+  // This is called by |alive_checker_| if it detects that the input stream is
+  // dead.
+  void DetectedDeadInputStream();
+
+  AudioParameters audio_parameters_;
+
+  const base::ThreadPriority thread_priority_;
+
+  const bool enable_uma_;
+
+  CaptureCallback* callback_;
+
+  // A pointer to the IPC layer that takes care of sending requests over to
+  // the stream implementation.  Only valid when state_ != IPC_CLOSED.
+  std::unique_ptr<AudioInputIPC> ipc_;
+
+  // Current state. See comments for State enum above.
+  State state_;
+
+  // For UMA stats. May only be accessed on the IO thread.
+  Error had_error_ = kNoError;
+
+  // Stores the Automatic Gain Control state. Default is false.
+  bool agc_is_enabled_;
+
+  // Controls the dead stream detection. Only the DSP hotword devices set this
+  // to kDisabled to disable dead stream detection.
+  const DeadStreamDetection detect_dead_stream_;
+
+  // Checks regularly that the input stream is alive and notifies us if it
+  // isn't by calling DetectedDeadInputStream(). Must outlive |audio_callback_|.
+  std::unique_ptr<AliveChecker> alive_checker_;
+
+  std::unique_ptr<AudioInputDevice::AudioThreadCallback> audio_callback_;
+  std::unique_ptr<AudioDeviceThread> audio_thread_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // Cache the output device used for AEC in case it's called before the stream
+  // is created.
+  absl::optional<std::string> output_device_id_for_aec_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AudioInputDevice);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_INPUT_DEVICE_H_
diff --git a/third_party/chromium/media/audio/audio_input_device_unittest.cc b/third_party/chromium/media/audio/audio_input_device_unittest.cc
new file mode 100644
index 0000000..b0c1eba
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_device_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_input_device.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/process/process_handle.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/sync_socket.h"
+#include "base/test/task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::CancelableSyncSocket;
+using base::SyncSocket;
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+
+namespace media {
+
+namespace {
+
+const size_t kMemorySegmentCount = 10u;
+
+class MockAudioInputIPC : public AudioInputIPC {
+ public:
+  MockAudioInputIPC() = default;
+  ~MockAudioInputIPC() override = default;
+
+  MOCK_METHOD4(CreateStream,
+               void(AudioInputIPCDelegate* delegate,
+                    const AudioParameters& params,
+                    bool automatic_gain_control,
+                    uint32_t total_segments));
+  MOCK_METHOD0(RecordStream, void());
+  MOCK_METHOD1(SetVolume, void(double volume));
+  MOCK_METHOD1(SetOutputDeviceForAec, void(const std::string&));
+  MOCK_METHOD0(CloseStream, void());
+};
+
+class MockCaptureCallback : public AudioCapturerSource::CaptureCallback {
+ public:
+  MockCaptureCallback() = default;
+  ~MockCaptureCallback() override = default;
+
+  MOCK_METHOD0(OnCaptureStarted, void());
+  MOCK_METHOD4(Capture,
+               void(const AudioBus* audio_source,
+                    base::TimeTicks audio_capture_time,
+                    double volume,
+                    bool key_pressed));
+
+  MOCK_METHOD2(OnCaptureError,
+               void(AudioCapturerSource::ErrorCode code,
+                    const std::string& message));
+  MOCK_METHOD1(OnCaptureMuted, void(bool is_muted));
+};
+
+}  // namespace.
+
+class AudioInputDeviceTest
+    : public ::testing::TestWithParam<AudioInputDevice::DeadStreamDetection> {};
+
+// Regular construction.
+TEST_P(AudioInputDeviceTest, Noop) {
+  base::test::SingleThreadTaskEnvironment task_environment(
+      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  MockAudioInputIPC* input_ipc = new MockAudioInputIPC();
+  scoped_refptr<AudioInputDevice> device(new AudioInputDevice(
+      base::WrapUnique(input_ipc), AudioInputDevice::Purpose::kUserInput,
+      AudioInputDeviceTest::GetParam()));
+}
+
+ACTION_P(ReportStateChange, device) {
+  static_cast<AudioInputIPCDelegate*>(device)->OnError(
+      media::AudioCapturerSource::ErrorCode::kUnknown);
+}
+
+// Verify that we get an OnCaptureError() callback if CreateStream fails.
+TEST_P(AudioInputDeviceTest, FailToCreateStream) {
+  AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                         CHANNEL_LAYOUT_STEREO, 48000, 480);
+
+  MockCaptureCallback callback;
+  MockAudioInputIPC* input_ipc = new MockAudioInputIPC();
+  scoped_refptr<AudioInputDevice> device(new AudioInputDevice(
+      base::WrapUnique(input_ipc), AudioInputDevice::Purpose::kUserInput,
+      AudioInputDeviceTest::GetParam()));
+  device->Initialize(params, &callback);
+  EXPECT_CALL(*input_ipc, CreateStream(_, _, _, _))
+      .WillOnce(ReportStateChange(device.get()));
+  EXPECT_CALL(callback,
+              OnCaptureError(AudioCapturerSource::ErrorCode::kUnknown, _));
+  EXPECT_CALL(*input_ipc, CloseStream());
+  device->Start();
+  device->Stop();
+}
+
+TEST_P(AudioInputDeviceTest, CreateStream) {
+  AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                         CHANNEL_LAYOUT_STEREO, 48000, 480);
+  base::MappedReadOnlyRegion shared_memory;
+  CancelableSyncSocket browser_socket;
+  CancelableSyncSocket renderer_socket;
+
+  const uint32_t memory_size =
+      media::ComputeAudioInputBufferSize(params, kMemorySegmentCount);
+
+  shared_memory = base::ReadOnlySharedMemoryRegion::Create(memory_size);
+  ASSERT_TRUE(shared_memory.IsValid());
+  memset(shared_memory.mapping.memory(), 0xff, memory_size);
+
+  ASSERT_TRUE(
+      CancelableSyncSocket::CreatePair(&browser_socket, &renderer_socket));
+  base::ReadOnlySharedMemoryRegion duplicated_shared_memory_region =
+      shared_memory.region.Duplicate();
+  ASSERT_TRUE(duplicated_shared_memory_region.IsValid());
+
+  base::test::TaskEnvironment ste;
+  MockCaptureCallback callback;
+  MockAudioInputIPC* input_ipc = new MockAudioInputIPC();
+  scoped_refptr<AudioInputDevice> device(new AudioInputDevice(
+      base::WrapUnique(input_ipc), AudioInputDevice::Purpose::kUserInput,
+      AudioInputDeviceTest::GetParam()));
+  device->Initialize(params, &callback);
+
+  EXPECT_CALL(*input_ipc, CreateStream(_, _, _, _))
+      .WillOnce(InvokeWithoutArgs([&]() {
+        static_cast<AudioInputIPCDelegate*>(device.get())
+            ->OnStreamCreated(std::move(duplicated_shared_memory_region),
+                              renderer_socket.Take(), false);
+      }));
+  EXPECT_CALL(*input_ipc, RecordStream());
+
+  EXPECT_CALL(callback, OnCaptureStarted());
+  device->Start();
+  EXPECT_CALL(*input_ipc, CloseStream());
+  device->Stop();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AudioInputDeviceGroup,
+    AudioInputDeviceTest,
+    ::testing::Values(AudioInputDevice::DeadStreamDetection::kDisabled,
+                      AudioInputDevice::DeadStreamDetection::kEnabled));
+
+}  // namespace media.
diff --git a/third_party/chromium/media/audio/audio_input_ipc.cc b/third_party/chromium/media/audio/audio_input_ipc.cc
new file mode 100644
index 0000000..e18a9b8
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_ipc.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_input_ipc.h"
+
+namespace media {
+
+AudioInputIPCDelegate::~AudioInputIPCDelegate() = default;
+
+AudioInputIPC::~AudioInputIPC() = default;
+
+AudioProcessorControls* AudioInputIPC::GetProcessorControls() {
+  return nullptr;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_input_ipc.h b/third_party/chromium/media/audio/audio_input_ipc.h
new file mode 100644
index 0000000..28663a9
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_ipc.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_INPUT_IPC_H_
+#define MEDIA_AUDIO_AUDIO_INPUT_IPC_H_
+
+#include <stdint.h>
+
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/sync_socket.h"
+#include "media/base/audio_capturer_source.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class AudioProcessorControls;
+
+// Contains IPC notifications for the state of the server side
+// (AudioInputController) audio state changes and when an AudioInputController
+// has been created.  Implemented by AudioInputDevice.
+class MEDIA_EXPORT AudioInputIPCDelegate {
+ public:
+  // Called when an AudioInputController has been created.
+  // See media/mojo/mojom/audio_data_pipe.mojom for documentation of
+  // |handle| and |socket_handle|.
+  virtual void OnStreamCreated(
+      base::ReadOnlySharedMemoryRegion shared_memory_region,
+      base::SyncSocket::ScopedHandle socket_handle,
+      bool initially_muted) = 0;
+
+  // Called when state of an audio stream has changed.
+  virtual void OnError(AudioCapturerSource::ErrorCode code) = 0;
+
+  // Called when an audio stream is muted or unmuted.
+  virtual void OnMuted(bool is_muted) = 0;
+
+  // Called when the AudioInputIPC object is going away and/or when the
+  // IPC channel has been closed and no more IPC requests can be made.
+  // Implementations should delete their owned AudioInputIPC instance
+  // immediately.
+  virtual void OnIPCClosed() = 0;
+
+ protected:
+  virtual ~AudioInputIPCDelegate();
+};
+
+// Provides IPC functionality for an AudioInputIPCDelegate (e.g., an
+// AudioInputDevice).  The implementation should asynchronously deliver the
+// messages to an AudioInputController object (or create one in the case of
+// CreateStream()), that may live in a separate process.
+class MEDIA_EXPORT AudioInputIPC {
+ public:
+  virtual ~AudioInputIPC();
+
+  // Sends a request to create an AudioInputController object in the peer
+  // process, and configures it to use the specified audio |params|.  The
+  // |total_segments| indidates number of equal-lengthed segments in the shared
+  // memory buffer.  Once the stream has been created, the implementation will
+  // notify |delegate| by calling OnStreamCreated().
+  virtual void CreateStream(AudioInputIPCDelegate* delegate,
+                            const AudioParameters& params,
+                            bool automatic_gain_control,
+                            uint32_t total_segments) = 0;
+
+  // Corresponds to a call to AudioInputController::Record() on the server side.
+  virtual void RecordStream() = 0;
+
+  // Sets the volume of the audio stream.
+  virtual void SetVolume(double volume) = 0;
+
+  // Sets the output device from which to cancel echo, if supported. The
+  // |output_device_id| can be gotten from a device enumeration. Must not be
+  // called before the stream has been successfully created.
+  virtual void SetOutputDeviceForAec(const std::string& output_device_id) = 0;
+
+  // If the input has built-in processing, returns a pointer to processing
+  // controls. Valid after the stream has been created.
+  virtual AudioProcessorControls* GetProcessorControls();
+
+  // Closes the audio stream, which should shut down the corresponding
+  // AudioInputController in the peer process.
+  virtual void CloseStream() = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_INPUT_IPC_H_
diff --git a/third_party/chromium/media/audio/audio_input_stream_data_interceptor.cc b/third_party/chromium/media/audio/audio_input_stream_data_interceptor.cc
new file mode 100644
index 0000000..332b3ae
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_stream_data_interceptor.cc
@@ -0,0 +1,100 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_input_stream_data_interceptor.h"
+
+#include <utility>
+
+#include "media/audio/audio_debug_recording_helper.h"
+
+namespace media {
+
+AudioInputStreamDataInterceptor::AudioInputStreamDataInterceptor(
+    CreateDebugRecorderCB create_debug_recorder_cb,
+    AudioInputStream* stream)
+    : create_debug_recorder_cb_(std::move(create_debug_recorder_cb)),
+      stream_(stream) {
+  DCHECK(create_debug_recorder_cb_);
+  DCHECK(stream_);
+}
+
+AudioInputStreamDataInterceptor::~AudioInputStreamDataInterceptor() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+// Implementation of AudioInputStream.
+AudioInputStream::OpenOutcome AudioInputStreamDataInterceptor::Open() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return stream_->Open();
+}
+
+void AudioInputStreamDataInterceptor::Start(
+    AudioInputStream::AudioInputCallback* callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  callback_ = callback;
+  debug_recorder_ = create_debug_recorder_cb_.Run();
+  stream_->Start(this);
+}
+
+void AudioInputStreamDataInterceptor::Stop() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  stream_->Stop();
+  debug_recorder_.reset();
+  callback_ = nullptr;
+}
+
+void AudioInputStreamDataInterceptor::Close() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  stream_->Close();
+  delete this;
+}
+
+double AudioInputStreamDataInterceptor::GetMaxVolume() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return stream_->GetMaxVolume();
+}
+
+void AudioInputStreamDataInterceptor::SetVolume(double volume) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  stream_->SetVolume(volume);
+}
+
+double AudioInputStreamDataInterceptor::GetVolume() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return stream_->GetVolume();
+}
+
+bool AudioInputStreamDataInterceptor::IsMuted() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return stream_->IsMuted();
+}
+
+bool AudioInputStreamDataInterceptor::SetAutomaticGainControl(bool enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return stream_->SetAutomaticGainControl(enabled);
+}
+
+bool AudioInputStreamDataInterceptor::GetAutomaticGainControl() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return stream_->GetAutomaticGainControl();
+}
+
+void AudioInputStreamDataInterceptor::SetOutputDeviceForAec(
+    const std::string& output_device_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return stream_->SetOutputDeviceForAec(output_device_id);
+}
+
+void AudioInputStreamDataInterceptor::OnData(const AudioBus* source,
+                                             base::TimeTicks capture_time,
+                                             double volume) {
+  callback_->OnData(source, capture_time, volume);
+  debug_recorder_->OnData(source);
+}
+
+void AudioInputStreamDataInterceptor::OnError() {
+  callback_->OnError();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_input_stream_data_interceptor.h b/third_party/chromium/media/audio/audio_input_stream_data_interceptor.h
new file mode 100644
index 0000000..3ba937c
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_stream_data_interceptor.h
@@ -0,0 +1,73 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_INPUT_STREAM_DATA_INTERCEPTOR_H_
+#define MEDIA_AUDIO_AUDIO_INPUT_STREAM_DATA_INTERCEPTOR_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+#include "media/audio/audio_io.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class AudioDebugRecorder;
+
+// This class wraps an AudioInputStream to be able to intercept the data for
+// debug recording purposes.
+class MEDIA_EXPORT AudioInputStreamDataInterceptor
+    : public AudioInputStream,
+      public AudioInputStream::AudioInputCallback {
+ public:
+  using CreateDebugRecorderCB =
+      base::RepeatingCallback<std::unique_ptr<AudioDebugRecorder>()>;
+
+  // |stream| is the stream that this object should forward all stream
+  // operations to. It will intercept OnData callbacks and send the audio data
+  // to the debug recorder created by |create_debug_recorder_cb|.
+  AudioInputStreamDataInterceptor(
+      CreateDebugRecorderCB create_debug_recorder_cb,
+      AudioInputStream* stream);
+
+  AudioInputStreamDataInterceptor(const AudioInputStreamDataInterceptor&) =
+      delete;
+  AudioInputStreamDataInterceptor& operator=(
+      const AudioInputStreamDataInterceptor&) = delete;
+
+  ~AudioInputStreamDataInterceptor() override;
+
+  // Implementation of AudioInputStream.
+  OpenOutcome Open() override;
+  void Start(AudioInputStream::AudioInputCallback* callback) override;
+  void Stop() override;
+  void Close() override;
+  double GetMaxVolume() override;
+  void SetVolume(double volume) override;
+  double GetVolume() override;
+  bool IsMuted() override;
+  bool SetAutomaticGainControl(bool enabled) override;
+  bool GetAutomaticGainControl() override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+  // Implementation of AudioInputCallback
+  void OnData(const AudioBus* source,
+              base::TimeTicks capture_time,
+              double volume) override;
+
+  void OnError() override;
+
+ private:
+  const CreateDebugRecorderCB create_debug_recorder_cb_;
+  std::unique_ptr<AudioDebugRecorder> debug_recorder_;
+  AudioInputStream* const stream_;
+  AudioInputStream::AudioInputCallback* callback_;
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_INPUT_STREAM_DATA_INTERCEPTOR_H_
diff --git a/third_party/chromium/media/audio/audio_input_stream_data_interceptor_unittest.cc b/third_party/chromium/media/audio/audio_input_stream_data_interceptor_unittest.cc
new file mode 100644
index 0000000..3a2ec62
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_stream_data_interceptor_unittest.cc
@@ -0,0 +1,314 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_input_stream_data_interceptor.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/ptr_util.h"
+#include "media/audio/audio_debug_recording_helper.h"
+#include "media/audio/audio_io.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+using testing::Return;
+using testing::StrictMock;
+using testing::Mock;
+
+const double kMaxVolume = 0.1234;
+const double kNewVolume = 0.2345;
+const double kVolume = 0.3456;
+
+class MockStream : public AudioInputStream {
+ public:
+  MockStream() = default;
+  ~MockStream() override = default;
+  MOCK_METHOD0(Open, AudioInputStream::OpenOutcome());
+  MOCK_METHOD1(Start, void(AudioInputStream::AudioInputCallback*));
+  MOCK_METHOD0(Stop, void());
+  MOCK_METHOD0(Close, void());
+  MOCK_METHOD0(GetMaxVolume, double());
+  MOCK_METHOD1(SetVolume, void(double));
+  MOCK_METHOD0(GetVolume, double());
+  MOCK_METHOD1(SetAutomaticGainControl, bool(bool));
+  MOCK_METHOD0(GetAutomaticGainControl, bool());
+  MOCK_METHOD0(IsMuted, bool());
+  MOCK_METHOD1(SetOutputDeviceForAec, void(const std::string&));
+};
+
+class MockDebugRecorder : public AudioDebugRecorder {
+ public:
+  MockDebugRecorder() = default;
+  ~MockDebugRecorder() override = default;
+  MOCK_METHOD1(OnData, void(const AudioBus* source));
+};
+
+class MockCallback : public AudioInputStream::AudioInputCallback {
+ public:
+  MockCallback() = default;
+  ~MockCallback() override = default;
+
+  MOCK_METHOD3(OnData, void(const AudioBus*, base::TimeTicks, double));
+  MOCK_METHOD0(OnError, void());
+};
+
+class MockDebugRecorderFactory {
+ public:
+  MockDebugRecorderFactory() = default;
+  ~MockDebugRecorderFactory() { DCHECK(!prepared_recorder_); }
+
+  std::unique_ptr<AudioDebugRecorder> CreateDebugRecorder() {
+    DCHECK(prepared_recorder_);
+    return std::move(prepared_recorder_);
+  }
+
+  void ExpectRecorderCreation(
+      std::unique_ptr<AudioDebugRecorder> recorder_ptr) {
+    DCHECK(!prepared_recorder_);
+    prepared_recorder_ = std::move(recorder_ptr);
+    DCHECK(prepared_recorder_);
+  }
+
+ private:
+  std::unique_ptr<AudioDebugRecorder> prepared_recorder_;
+};
+
+void TestSetAutomaticGainControl(bool enable, bool agc_is_supported) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+
+  EXPECT_CALL(stream, SetAutomaticGainControl(enable))
+      .WillOnce(Return(agc_is_supported));
+  EXPECT_EQ(interceptor->SetAutomaticGainControl(enable), agc_is_supported);
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+}  // namespace
+
+TEST(AudioInputStreamDataInterceptorTest, Open) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+  EXPECT_CALL(stream, Open());
+  interceptor->Open();
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest, Start) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  StrictMock<MockCallback> callback;
+  auto* recorder = new StrictMock<MockDebugRecorder>();
+  std::unique_ptr<AudioBus> audio_bus = AudioBus::Create(1, 1);
+  AudioInputStreamDataInterceptor* interceptor =
+      new AudioInputStreamDataInterceptor(
+          base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                              base::Unretained(&factory)),
+          &stream);
+
+  EXPECT_CALL(stream, Start(interceptor));
+  factory.ExpectRecorderCreation(base::WrapUnique(recorder));
+  interceptor->Start(&callback);
+
+  Mock::VerifyAndClearExpectations(&stream);
+
+  base::TimeTicks time = base::TimeTicks::Now();
+
+  // Audio data should be passed to both callback and recorder.
+  EXPECT_CALL(callback, OnData(audio_bus.get(), time, kVolume));
+  EXPECT_CALL(*recorder, OnData(audio_bus.get()));
+  interceptor->OnData(audio_bus.get(), time, kVolume);
+
+  Mock::VerifyAndClearExpectations(&callback);
+  Mock::VerifyAndClearExpectations(recorder);
+
+  // Errors should be propagated to the renderer
+  EXPECT_CALL(callback, OnError());
+  interceptor->OnError();
+
+  Mock::VerifyAndClearExpectations(&callback);
+
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest, Stop) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+  EXPECT_CALL(stream, Stop());
+  interceptor->Stop();
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest, Close) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest, GetMaxVolume) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+
+  EXPECT_CALL(stream, GetMaxVolume()).WillOnce(Return(kMaxVolume));
+  EXPECT_EQ(interceptor->GetMaxVolume(), kMaxVolume);
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest, SetVolume) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+
+  EXPECT_CALL(stream, SetVolume(kNewVolume));
+  interceptor->SetVolume(kNewVolume);
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest, GetVolume) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+
+  EXPECT_CALL(stream, GetVolume()).WillOnce(Return(kVolume));
+  EXPECT_EQ(interceptor->GetVolume(), kVolume);
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest,
+     SetAutomaticGainControlTrueWhenSupported) {
+  TestSetAutomaticGainControl(true, true);
+}
+
+TEST(AudioInputStreamDataInterceptorTest,
+     SetAutomaticGainControlFalseWhenSupported) {
+  TestSetAutomaticGainControl(false, true);
+}
+
+TEST(AudioInputStreamDataInterceptorTest,
+     SetAutomaticGainControlTrueWhenNotSupported) {
+  TestSetAutomaticGainControl(true, false);
+}
+
+TEST(AudioInputStreamDataInterceptorTest,
+     SetAutomaticGainControlFalseWhenNotSupported) {
+  TestSetAutomaticGainControl(false, false);
+}
+
+TEST(AudioInputStreamDataInterceptorTest, GetAutomaticGainControl_True) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+
+  EXPECT_CALL(stream, GetAutomaticGainControl()).WillOnce(Return(true));
+  EXPECT_EQ(interceptor->GetAutomaticGainControl(), true);
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest, GetAutomaticGainControl_False) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+
+  EXPECT_CALL(stream, GetAutomaticGainControl()).WillOnce(Return(false));
+  EXPECT_EQ(interceptor->GetAutomaticGainControl(), false);
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest, IsMuted_True) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+
+  EXPECT_CALL(stream, IsMuted()).WillOnce(Return(true));
+  EXPECT_EQ(interceptor->IsMuted(), true);
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+TEST(AudioInputStreamDataInterceptorTest, IsMuted_False) {
+  MockDebugRecorderFactory factory;
+  StrictMock<MockStream> stream;
+  AudioInputStream* interceptor = new AudioInputStreamDataInterceptor(
+      base::BindRepeating(&MockDebugRecorderFactory::CreateDebugRecorder,
+                          base::Unretained(&factory)),
+      &stream);
+
+  EXPECT_CALL(stream, IsMuted()).WillOnce(Return(false));
+  EXPECT_EQ(interceptor->IsMuted(), false);
+
+  Mock::VerifyAndClearExpectations(&stream);
+  EXPECT_CALL(stream, Close());
+  interceptor->Close();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_input_unittest.cc b/third_party/chromium/media/audio/audio_input_unittest.cc
new file mode 100644
index 0000000..8756f44
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_input_unittest.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/macros.h"
+#include "base/message_loop/message_pump_type.h"
+#include "base/run_loop.h"
+#include "base/test/test_message_loop.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_device_info_accessor_for_tests.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_unittest_util.h"
+#include "media/audio/test_audio_thread.h"
+#include "media/base/media_switches.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+// This class allows to find out if the callbacks are occurring as
+// expected and if any error has been reported.
+class TestInputCallback : public AudioInputStream::AudioInputCallback {
+ public:
+  TestInputCallback(base::OnceClosure quit_closure)
+      : quit_closure_(std::move(quit_closure)),
+        callback_count_(0),
+        had_error_(0) {}
+  void OnData(const AudioBus* source,
+              base::TimeTicks capture_time,
+              double volume) override {
+    if (!quit_closure_.is_null()) {
+      ++callback_count_;
+      if (callback_count_ >= 2) {
+        std::move(quit_closure_).Run();
+      }
+    }
+  }
+  void OnError() override {
+    if (!quit_closure_.is_null()) {
+      ++had_error_;
+      std::move(quit_closure_).Run();
+    }
+  }
+  // Returns how many times OnData() has been called. This should not be called
+  // until |quit_closure_| has run.
+  int callback_count() const {
+    DCHECK(quit_closure_.is_null());
+    return callback_count_;
+  }
+  // Returns how many times the OnError callback was called. This should not be
+  // called until |quit_closure_| has run.
+  int had_error() const {
+    DCHECK(quit_closure_.is_null());
+    return had_error_;
+  }
+
+ private:
+  base::OnceClosure quit_closure_;
+  int callback_count_;
+  int had_error_;
+};
+
+class AudioInputTest : public testing::Test {
+ public:
+  AudioInputTest()
+      : message_loop_(base::MessagePumpType::UI),
+        audio_manager_(AudioManager::CreateForTesting(
+            std::make_unique<TestAudioThread>())),
+        audio_input_stream_(nullptr) {
+    base::RunLoop().RunUntilIdle();
+  }
+
+  AudioInputTest(const AudioInputTest&) = delete;
+  AudioInputTest& operator=(const AudioInputTest&) = delete;
+
+  ~AudioInputTest() override { audio_manager_->Shutdown(); }
+
+ protected:
+  bool InputDevicesAvailable() {
+#if defined(OS_FUCHSIA)
+    // On Fuchsia HasAudioInputDevices() returns true, but AudioInputStream is
+    // not implemented. Audio input is implemented in
+    // FuchsiaAudioCapturerStream. It implements AudioCapturerStream interface
+    // and runs in the renderer process.
+    return false;
+#elif defined(OS_MAC) && defined(ARCH_CPU_ARM64)
+    // TODO(crbug.com/1128458): macOS on ARM64 says it has devices, but won't
+    // let any of them be opened or listed.
+    return false;
+#else
+    return AudioDeviceInfoAccessorForTests(audio_manager_.get())
+        .HasAudioInputDevices();
+#endif
+  }
+
+  void MakeAudioInputStreamOnAudioThread() {
+    RunOnAudioThread(base::BindOnce(&AudioInputTest::MakeAudioInputStream,
+                                    base::Unretained(this)));
+  }
+
+  void CloseAudioInputStreamOnAudioThread() {
+    RunOnAudioThread(base::BindOnce(&AudioInputStream::Close,
+                                    base::Unretained(audio_input_stream_)));
+    audio_input_stream_ = nullptr;
+  }
+
+  void OpenAndCloseAudioInputStreamOnAudioThread() {
+    RunOnAudioThread(
+        base::BindOnce(&AudioInputTest::OpenAndClose, base::Unretained(this)));
+  }
+
+  void OpenStopAndCloseAudioInputStreamOnAudioThread() {
+    RunOnAudioThread(base::BindOnce(&AudioInputTest::OpenStopAndClose,
+                                    base::Unretained(this)));
+  }
+
+  void OpenAndStartAudioInputStreamOnAudioThread(
+      AudioInputStream::AudioInputCallback* sink) {
+    RunOnAudioThread(base::BindOnce(&AudioInputTest::OpenAndStart,
+                                    base::Unretained(this), sink));
+  }
+
+  void StopAndCloseAudioInputStreamOnAudioThread() {
+    RunOnAudioThread(
+        base::BindOnce(&AudioInputTest::StopAndClose, base::Unretained(this)));
+  }
+
+  void MakeAudioInputStream() {
+    DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+    AudioParameters params =
+        AudioDeviceInfoAccessorForTests(audio_manager_.get())
+            .GetInputStreamParameters(AudioDeviceDescription::kDefaultDeviceId);
+    audio_input_stream_ = audio_manager_->MakeAudioInputStream(
+        params, AudioDeviceDescription::kDefaultDeviceId,
+        base::BindRepeating(&AudioInputTest::OnLogMessage,
+                            base::Unretained(this)));
+    ASSERT_TRUE(audio_input_stream_);
+  }
+
+  void OpenAndClose() {
+    DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+    ASSERT_TRUE(audio_input_stream_);
+    EXPECT_EQ(audio_input_stream_->Open(),
+              AudioInputStream::OpenOutcome::kSuccess);
+    audio_input_stream_->Close();
+    audio_input_stream_ = nullptr;
+  }
+
+  void OpenAndStart(AudioInputStream::AudioInputCallback* sink) {
+    DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+    ASSERT_TRUE(audio_input_stream_);
+    EXPECT_EQ(audio_input_stream_->Open(),
+              AudioInputStream::OpenOutcome::kSuccess);
+    audio_input_stream_->Start(sink);
+  }
+
+  void OpenStopAndClose() {
+    DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+    ASSERT_TRUE(audio_input_stream_);
+    EXPECT_EQ(audio_input_stream_->Open(),
+              AudioInputStream::OpenOutcome::kSuccess);
+    audio_input_stream_->Stop();
+    audio_input_stream_->Close();
+    audio_input_stream_ = nullptr;
+  }
+
+  void StopAndClose() {
+    DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+    ASSERT_TRUE(audio_input_stream_);
+    audio_input_stream_->Stop();
+    audio_input_stream_->Close();
+    audio_input_stream_ = nullptr;
+  }
+
+  // Synchronously runs the provided callback/closure on the audio thread.
+  void RunOnAudioThread(base::OnceClosure closure) {
+    DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+    std::move(closure).Run();
+  }
+
+  void OnLogMessage(const std::string& message) {}
+
+  base::TestMessageLoop message_loop_;
+  std::unique_ptr<AudioManager> audio_manager_;
+  AudioInputStream* audio_input_stream_;
+};
+
+// Test create and close of an AudioInputStream without recording audio.
+TEST_F(AudioInputTest, CreateAndClose) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+  MakeAudioInputStreamOnAudioThread();
+  CloseAudioInputStreamOnAudioThread();
+}
+
+// Test create, open and close of an AudioInputStream without recording audio.
+TEST_F(AudioInputTest, OpenAndClose) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+  MakeAudioInputStreamOnAudioThread();
+  OpenAndCloseAudioInputStreamOnAudioThread();
+}
+
+// Test create, open, stop and close of an AudioInputStream without recording.
+TEST_F(AudioInputTest, OpenStopAndClose) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+  MakeAudioInputStreamOnAudioThread();
+  OpenStopAndCloseAudioInputStreamOnAudioThread();
+}
+
+// Test a normal recording sequence using an AudioInputStream.
+// Very simple test which starts capturing and verifies that recording starts.
+TEST_F(AudioInputTest, Record) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+  MakeAudioInputStreamOnAudioThread();
+
+  base::RunLoop run_loop;
+  TestInputCallback test_callback(run_loop.QuitClosure());
+  OpenAndStartAudioInputStreamOnAudioThread(&test_callback);
+
+  run_loop.Run();
+  EXPECT_GE(test_callback.callback_count(), 2);
+  EXPECT_FALSE(test_callback.had_error());
+
+  StopAndCloseAudioInputStreamOnAudioThread();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_io.h b/third_party/chromium/media/audio/audio_io.h
new file mode 100644
index 0000000..295be09
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_io.h
@@ -0,0 +1,212 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_IO_H_
+#define MEDIA_AUDIO_AUDIO_IO_H_
+
+#include <stdint.h>
+
+#include "base/time/time.h"
+#include "media/base/audio_bus.h"
+#include "media/base/media_export.h"
+
+// Low-level audio output support. To make sound there are 3 objects involved:
+// - AudioSource : produces audio samples on a pull model. Implements
+//   the AudioSourceCallback interface.
+// - AudioOutputStream : uses the AudioSource to render audio on a given
+//   channel, format and sample frequency configuration. Data from the
+//   AudioSource is delivered in a 'pull' model.
+// - AudioManager : factory for the AudioOutputStream objects, manager
+//   of the hardware resources and mixer control.
+//
+// The number and configuration of AudioOutputStream does not need to match the
+// physically available hardware resources. For example you can have:
+//
+//  MonoPCMSource1 --> MonoPCMStream1 --> |       | --> audio left channel
+//  StereoPCMSource -> StereoPCMStream -> | mixer |
+//  MonoPCMSource2 --> MonoPCMStream2 --> |       | --> audio right channel
+//
+// This facility's objective is mix and render audio with low overhead using
+// the OS basic audio support, abstracting as much as possible the
+// idiosyncrasies of each platform. Non-goals:
+// - Positional, 3d audio
+// - Dependence on non-default libraries such as DirectX 9, 10, XAudio
+// - Digital signal processing or effects
+// - Extra features if a specific hardware is installed (EAX, X-fi)
+//
+// The primary client of this facility is audio coming from several tabs.
+// Specifically for this case we avoid supporting complex formats such as MP3
+// or WMA. Complex format decoding should be done by the renderers.
+
+// Models an audio stream that gets rendered to the audio hardware output.
+// Because we support more audio streams than physically available channels
+// a given AudioOutputStream might or might not talk directly to hardware.
+// An audio stream allocates several buffers for audio data and calls
+// AudioSourceCallback::OnMoreData() periodically to fill these buffers,
+// as the data is written to the audio device. Size of each packet is determined
+// by |samples_per_packet| specified in AudioParameters  when the stream is
+// created.
+
+namespace media {
+
+class MEDIA_EXPORT AudioOutputStream {
+ public:
+  // Audio sources must implement AudioSourceCallback. This interface will be
+  // called in a random thread which very likely is a high priority thread. Do
+  // not rely on using this thread TLS or make calls that alter the thread
+  // itself such as creating Windows or initializing COM.
+  class MEDIA_EXPORT AudioSourceCallback {
+   public:
+    virtual ~AudioSourceCallback() {}
+
+    // Provide more data by fully filling |dest|. The source will return the
+    // number of frames it filled. |delay| is the duration of audio written to
+    // |dest| in prior calls to OnMoreData() that has not yet been played out,
+    // and |delay_timestamp| is the time when |delay| was measured. The time
+    // when the first sample added to |dest| is expected to be played out can be
+    // calculated by adding |delay| to |delay_timestamp|. The accuracy of
+    // |delay| and |delay_timestamp| may vary depending on the platform and
+    // implementation. |prior_frames_skipped| is the number of frames skipped by
+    // the consumer.
+    virtual int OnMoreData(base::TimeDelta delay,
+                           base::TimeTicks delay_timestamp,
+                           int prior_frames_skipped,
+                           AudioBus* dest) = 0;
+
+    // There was an error while playing a buffer. Audio source cannot be
+    // destroyed yet. No direct action needed by the AudioStream, but it is
+    // a good place to stop accumulating sound data since is is likely that
+    // playback will not continue.
+    //
+    // An ErrorType may be provided with more information on what went wrong. An
+    // unhandled kDeviceChange type error is likely to result in further errors;
+    // so it's recommended that sources close their existing output stream and
+    // request a new one when this error is sent.
+    enum class ErrorType { kUnknown, kDeviceChange };
+    virtual void OnError(ErrorType type) = 0;
+  };
+
+  virtual ~AudioOutputStream() {}
+
+  // Open the stream. false is returned if the stream cannot be opened.  Open()
+  // must always be followed by a call to Close() even if Open() fails.
+  virtual bool Open() = 0;
+
+  // Starts playing audio and generating AudioSourceCallback::OnMoreData().
+  // Since implementor of AudioOutputStream may have internal buffers, right
+  // after calling this method initial buffers are fetched.
+  //
+  // The output stream does not take ownership of this callback.
+  virtual void Start(AudioSourceCallback* callback) = 0;
+
+  // Stops playing audio.  The operation completes synchronously meaning that
+  // once Stop() has completed executing, no further callbacks will be made to
+  // the callback object that was supplied to Start() and it can be safely
+  // deleted. Stop() may be called in any state, e.g. before Start() or after
+  // Stop().
+  virtual void Stop() = 0;
+
+  // Sets the relative volume, with range [0.0, 1.0] inclusive.
+  virtual void SetVolume(double volume) = 0;
+
+  // Gets the relative volume, with range [0.0, 1.0] inclusive.
+  virtual void GetVolume(double* volume) = 0;
+
+  // Close the stream.
+  // After calling this method, the object should not be used anymore.
+  // After calling this method, no further AudioSourceCallback methods
+  // should be called on the callback object that was supplied to Start()
+  // by the AudioOutputStream implementation.
+  virtual void Close() = 0;
+
+  // Flushes the stream. This should only be called if the stream is not
+  // playing. (i.e. called after Stop or Open)
+  virtual void Flush() = 0;
+};
+
+// Models an audio sink receiving recorded audio from the audio driver.
+class MEDIA_EXPORT AudioInputStream {
+ public:
+  class MEDIA_EXPORT AudioInputCallback {
+   public:
+    // Called by the audio recorder when a full packet of audio data is
+    // available. This is called from a special audio thread and the
+    // implementation should return as soon as possible.
+    //
+    // |capture_time| is the time at which the first sample in |source| was
+    // received. The age of the audio data may be calculated by subtracting
+    // |capture_time| from base::TimeTicks::Now(). |capture_time| is always
+    // monotonically increasing.
+    virtual void OnData(const AudioBus* source,
+                        base::TimeTicks capture_time,
+                        double volume) = 0;
+
+    // There was an error while recording audio. The audio sink cannot be
+    // destroyed yet. No direct action needed by the AudioInputStream, but it
+    // is a good place to stop accumulating sound data since is is likely that
+    // recording will not continue.
+    virtual void OnError() = 0;
+
+   protected:
+    virtual ~AudioInputCallback() {}
+  };
+
+  virtual ~AudioInputStream() {}
+
+  enum class OpenOutcome {
+    kSuccess,
+    kAlreadyOpen,
+    // Failed due to an unknown or unspecified reason.
+    kFailed,
+    // Failed to open due to OS-level System permissions.
+    kFailedSystemPermissions,
+    // Failed to open as the device is exclusively opened by another app.
+    kFailedInUse,
+  };
+
+  // Open the stream and prepares it for recording. Call Start() to actually
+  // begin recording.
+  virtual OpenOutcome Open() = 0;
+
+  // Starts recording audio and generating AudioInputCallback::OnData().
+  // The input stream does not take ownership of this callback.
+  virtual void Start(AudioInputCallback* callback) = 0;
+
+  // Stops recording audio. Effect might not be instantaneous as there could be
+  // pending audio callbacks in the queue which will be issued first before
+  // recording stops.
+  virtual void Stop() = 0;
+
+  // Close the stream. This also generates AudioInputCallback::OnClose(). This
+  // should be the last call made on this object.
+  virtual void Close() = 0;
+
+  // Returns the maximum microphone analog volume or 0.0 if device does not
+  // have volume control.
+  virtual double GetMaxVolume() = 0;
+
+  // Sets the microphone analog volume, with range [0, max_volume] inclusive.
+  virtual void SetVolume(double volume) = 0;
+
+  // Returns the microphone analog volume, with range [0, max_volume] inclusive.
+  virtual double GetVolume() = 0;
+
+  // Sets the Automatic Gain Control (AGC) state.
+  virtual bool SetAutomaticGainControl(bool enabled) = 0;
+
+  // Returns the Automatic Gain Control (AGC) state.
+  virtual bool GetAutomaticGainControl() = 0;
+
+  // Returns the current muting state for the microphone.
+  virtual bool IsMuted() = 0;
+
+  // Sets the output device from which to cancel echo, if echo cancellation is
+  // supported by this stream. E.g. called by WebRTC when it changes playback
+  // devices.
+  virtual void SetOutputDeviceForAec(const std::string& output_device_id) = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_IO_H_
diff --git a/third_party/chromium/media/audio/audio_logging.h b/third_party/chromium/media/audio/audio_logging.h
new file mode 100644
index 0000000..764b8b2
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_logging.h
@@ -0,0 +1,80 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_LOGGING_H_
+#define MEDIA_AUDIO_AUDIO_LOGGING_H_
+
+#include <memory>
+#include <string>
+
+
+namespace media {
+
+class AudioParameters;
+
+// AudioLog logs state information about an active audio component.
+class AudioLog {
+ public:
+  virtual ~AudioLog() {}
+
+  // Called when an audio component is created.  |params| are the parameters of
+  // the created stream.  |device_id| is the id of the audio device opened by
+  // the created stream.
+  virtual void OnCreated(const media::AudioParameters& params,
+                         const std::string& device_id) = 0;
+
+  // Called when an audio component is started, generally this is synonymous
+  // with "playing."
+  virtual void OnStarted() = 0;
+
+  // Called when an audio component is stopped, generally this is synonymous
+  // with "paused."
+  virtual void OnStopped() = 0;
+
+  // Called when an audio component is closed, generally this is synonymous
+  // with "deleted."
+  virtual void OnClosed() = 0;
+
+  // Called when an audio component encounters an error.
+  virtual void OnError() = 0;
+
+  // Called when an audio component changes volume.  |volume| is the new volume.
+  virtual void OnSetVolume(double volume) = 0;
+
+  // Called with information about audio processing set-up for an audio
+  // component.
+  virtual void OnProcessingStateChanged(const std::string& message) = 0;
+
+  // Called when an audio component wants to forward a log message.
+  virtual void OnLogMessage(const std::string& message) = 0;
+};
+
+// AudioLogFactory dispenses AudioLog instances for tracking AudioComponent
+// behavior.
+class AudioLogFactory {
+ public:
+  enum AudioComponent {
+    // Input controllers have a 1:1 mapping with streams, so there's no need to
+    // track both controllers and streams.
+    AUDIO_INPUT_CONTROLLER,
+    // Output controllers may or may not be backed by an active stream, so we
+    // need to track both controllers and streams.
+    AUDIO_OUTPUT_CONTROLLER,
+    AUDIO_OUTPUT_STREAM,
+    AUDIO_COMPONENT_MAX
+  };
+
+  // Create a new AudioLog object for tracking the behavior for one instance of
+  // the given component.  Each instance of an "owning" class must create its
+  // own AudioLog.
+  virtual std::unique_ptr<AudioLog> CreateAudioLog(AudioComponent component,
+                                                   int component_id) = 0;
+
+ protected:
+  virtual ~AudioLogFactory() {}
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_LOGGING_H_
diff --git a/third_party/chromium/media/audio/audio_low_latency_input_output_unittest.cc b/third_party/chromium/media/audio/audio_low_latency_input_output_unittest.cc
new file mode 100644
index 0000000..eca3b36
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_low_latency_input_output_unittest.cc
@@ -0,0 +1,416 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/environment.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_device_info_accessor_for_tests.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_unittest_util.h"
+#include "media/audio/test_audio_thread.h"
+#include "media/base/seekable_buffer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+// Limits the number of delay measurements we can store in an array and
+// then write to file at end of the WASAPIAudioInputOutputFullDuplex test.
+static const size_t kMaxDelayMeasurements = 1000;
+
+// Name of the output text file. The output file will be stored in the
+// directory containing media_unittests.exe.
+// Example: \src\build\Debug\audio_delay_values_ms.txt.
+// See comments for the WASAPIAudioInputOutputFullDuplex test for more details
+// about the file format.
+static const char kDelayValuesFileName[] = "audio_delay_values_ms.txt";
+
+// Contains delay values which are reported during the full-duplex test.
+// Total delay = |buffer_delay_ms| + |input_delay_ms| + |output_delay_ms|.
+struct AudioDelayState {
+  AudioDelayState()
+      : delta_time_ms(0),
+        buffer_delay_ms(0),
+        input_delay_ms(0),
+        output_delay_ms(0) {
+  }
+
+  // Time in milliseconds since last delay report. Typical value is ~10 [ms].
+  int delta_time_ms;
+
+  // Size of internal sync buffer. Typical value is ~0 [ms].
+  int buffer_delay_ms;
+
+  // Reported capture/input delay. Typical value is ~10 [ms].
+  int input_delay_ms;
+
+  // Reported render/output delay. Typical value is ~40 [ms].
+  int output_delay_ms;
+};
+
+void OnLogMessage(const std::string& message) {}
+
+// Test fixture class.
+class AudioLowLatencyInputOutputTest : public testing::Test {
+ protected:
+  AudioLowLatencyInputOutputTest() {
+    audio_manager_ =
+        AudioManager::CreateForTesting(std::make_unique<TestAudioThread>());
+  }
+
+  ~AudioLowLatencyInputOutputTest() override { audio_manager_->Shutdown(); }
+
+  AudioManager* audio_manager() { return audio_manager_.get(); }
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner() {
+    return task_environment_.GetMainThreadTaskRunner();
+  }
+
+ private:
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::UI};
+  std::unique_ptr<AudioManager> audio_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest);
+};
+
+// This audio source/sink implementation should be used for manual tests
+// only since delay measurements are stored on an output text file.
+// All incoming/recorded audio packets are stored in an intermediate media
+// buffer which the renderer reads from when it needs audio for playout.
+// The total effect is that recorded audio is played out in loop back using
+// a sync buffer as temporary storage.
+class FullDuplexAudioSinkSource
+    : public AudioInputStream::AudioInputCallback,
+      public AudioOutputStream::AudioSourceCallback {
+ public:
+  FullDuplexAudioSinkSource(int sample_rate,
+                            int samples_per_packet,
+                            int channels)
+    : sample_rate_(sample_rate),
+      samples_per_packet_(samples_per_packet),
+      channels_(channels),
+      input_elements_to_write_(0),
+      output_elements_to_write_(0),
+      previous_write_time_(base::TimeTicks::Now()) {
+    // Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM).
+    frame_size_ = (16 / 8) * channels_;
+
+    // Start with the smallest possible buffer size. It will be increased
+    // dynamically during the test if required.
+    buffer_ = std::make_unique<media::SeekableBuffer>(
+        0, samples_per_packet_ * frame_size_);
+
+    frames_to_ms_ = static_cast<double>(1000.0 / sample_rate_);
+    delay_states_ = std::make_unique<AudioDelayState[]>(kMaxDelayMeasurements);
+  }
+
+  ~FullDuplexAudioSinkSource() override {
+    // Get complete file path to output file in the directory containing
+    // media_unittests.exe. Example: src/build/Debug/audio_delay_values_ms.txt.
+    base::FilePath file_name;
+    EXPECT_TRUE(base::PathService::Get(base::DIR_EXE, &file_name));
+    file_name = file_name.AppendASCII(kDelayValuesFileName);
+
+    FILE* text_file = base::OpenFile(file_name, "wt");
+    DLOG_IF(ERROR, !text_file) << "Failed to open log file.";
+    VLOG(0) << ">> Output file " << file_name.value() << " has been created.";
+
+    // Write the array which contains time-stamps, buffer size and
+    // audio delays values to a text file.
+    size_t elements_written = 0;
+    while (elements_written <
+        std::min(input_elements_to_write_, output_elements_to_write_)) {
+      const AudioDelayState state = delay_states_[elements_written];
+      fprintf(text_file, "%d %d %d %d\n",
+              state.delta_time_ms,
+              state.buffer_delay_ms,
+              state.input_delay_ms,
+              state.output_delay_ms);
+      ++elements_written;
+    }
+
+    base::CloseFile(text_file);
+  }
+
+  // AudioInputStream::AudioInputCallback.
+  void OnError() override {}
+  void OnData(const AudioBus* src,
+              base::TimeTicks capture_time,
+              double volume) override {
+    base::AutoLock lock(lock_);
+
+    // Update three components in the AudioDelayState for this recorded
+    // audio packet.
+    const base::TimeTicks now_time = base::TimeTicks::Now();
+    const int diff = (now_time - previous_write_time_).InMilliseconds();
+    previous_write_time_ = now_time;
+    if (input_elements_to_write_ < kMaxDelayMeasurements) {
+      delay_states_[input_elements_to_write_].delta_time_ms = diff;
+      delay_states_[input_elements_to_write_].buffer_delay_ms =
+          BytesToMilliseconds(buffer_->forward_bytes());
+      delay_states_[input_elements_to_write_].input_delay_ms =
+          (base::TimeTicks::Now() - capture_time).InMilliseconds();
+      ++input_elements_to_write_;
+    }
+
+    // TODO(henrika): fix this and use AudioFifo instead.
+    // Store the captured audio packet in a seekable media buffer.
+    // if (!buffer_->Append(src, size)) {
+    // An attempt to write outside the buffer limits has been made.
+    // Double the buffer capacity to ensure that we have a buffer large
+    // enough to handle the current sample test scenario.
+    //   buffer_->set_forward_capacity(2 * buffer_->forward_capacity());
+    //   buffer_->Clear();
+    // }
+  }
+
+  // AudioOutputStream::AudioSourceCallback.
+  void OnError(ErrorType type) override {}
+  int OnMoreData(base::TimeDelta delay,
+                 base::TimeTicks /* delay_timestamp */,
+                 int /* prior_frames_skipped */,
+                 AudioBus* dest) override {
+    base::AutoLock lock(lock_);
+
+    // Update one component in the AudioDelayState for the packet
+    // which is about to be played out.
+    if (output_elements_to_write_ < kMaxDelayMeasurements) {
+      delay_states_[output_elements_to_write_].output_delay_ms =
+          delay.InMilliseconds();
+      ++output_elements_to_write_;
+    }
+
+    int size;
+    const uint8_t* source;
+    // Read the data from the seekable media buffer which contains
+    // captured data at the same size and sample rate as the output side.
+    if (buffer_->GetCurrentChunk(&source, &size) && size > 0) {
+      EXPECT_EQ(channels_, dest->channels());
+      size = std::min(dest->frames() * frame_size_, size);
+      EXPECT_EQ(static_cast<size_t>(size) % sizeof(*dest->channel(0)), 0U);
+
+      // We should only have 16 bits per sample.
+      DCHECK_EQ(frame_size_ / channels_, 2);
+      dest->FromInterleaved<SignedInt16SampleTypeTraits>(
+          reinterpret_cast<const int16_t*>(source), size / channels_);
+
+      buffer_->Seek(size);
+      return size / frame_size_;
+    }
+
+    return 0;
+  }
+
+ protected:
+  // Converts from bytes to milliseconds taking the sample rate and size
+  // of an audio frame into account.
+  int BytesToMilliseconds(uint32_t delay_bytes) const {
+    return static_cast<int>((delay_bytes / frame_size_) * frames_to_ms_ + 0.5);
+  }
+
+ private:
+  base::Lock lock_;
+  std::unique_ptr<media::SeekableBuffer> buffer_;
+  int sample_rate_;
+  int samples_per_packet_;
+  int channels_;
+  int frame_size_;
+  double frames_to_ms_;
+  std::unique_ptr<AudioDelayState[]> delay_states_;
+  size_t input_elements_to_write_;
+  size_t output_elements_to_write_;
+  base::TimeTicks previous_write_time_;
+};
+
+class AudioInputStreamTraits {
+ public:
+  typedef AudioInputStream StreamType;
+
+  static AudioParameters GetDefaultAudioStreamParameters(
+      AudioManager* audio_manager) {
+    return AudioDeviceInfoAccessorForTests(audio_manager)
+        .GetInputStreamParameters(AudioDeviceDescription::kDefaultDeviceId);
+  }
+
+  static StreamType* CreateStream(AudioManager* audio_manager,
+      const AudioParameters& params) {
+    return audio_manager->MakeAudioInputStream(
+        params, AudioDeviceDescription::kDefaultDeviceId,
+        base::BindRepeating(&OnLogMessage));
+  }
+};
+
+class AudioOutputStreamTraits {
+ public:
+  typedef AudioOutputStream StreamType;
+
+  static AudioParameters GetDefaultAudioStreamParameters(
+      AudioManager* audio_manager) {
+    return AudioDeviceInfoAccessorForTests(audio_manager)
+        .GetDefaultOutputStreamParameters();
+  }
+
+  static StreamType* CreateStream(AudioManager* audio_manager,
+      const AudioParameters& params) {
+    return audio_manager->MakeAudioOutputStream(
+        params, std::string(), base::BindRepeating(&OnLogMessage));
+  }
+};
+
+// Traits template holding a trait of StreamType. It encapsulates
+// AudioInputStream and AudioOutputStream stream types.
+template <typename StreamTraits>
+class StreamWrapper {
+ public:
+  typedef typename StreamTraits::StreamType StreamType;
+
+  explicit StreamWrapper(AudioManager* audio_manager)
+      : audio_manager_(audio_manager),
+        format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
+#if defined(OS_ANDROID)
+        channel_layout_(CHANNEL_LAYOUT_MONO)
+#else
+        channel_layout_(CHANNEL_LAYOUT_STEREO)
+#endif
+  {
+    // Use the preferred sample rate.
+    const AudioParameters& params =
+        StreamTraits::GetDefaultAudioStreamParameters(audio_manager_);
+    sample_rate_ = params.sample_rate();
+
+    // Use the preferred buffer size. Note that the input side uses the same
+    // size as the output side in this implementation.
+    samples_per_packet_ = params.frames_per_buffer();
+  }
+
+  virtual ~StreamWrapper() = default;
+
+  // Creates an Audio[Input|Output]Stream stream object using default
+  // parameters.
+  StreamType* Create() {
+    return CreateStream();
+  }
+
+  int channels() const {
+    return ChannelLayoutToChannelCount(channel_layout_);
+  }
+  int sample_rate() const { return sample_rate_; }
+  int samples_per_packet() const { return samples_per_packet_; }
+
+ private:
+  StreamType* CreateStream() {
+    StreamType* stream = StreamTraits::CreateStream(
+        audio_manager_, AudioParameters(format_, channel_layout_, sample_rate_,
+                                        samples_per_packet_));
+    EXPECT_TRUE(stream);
+    return stream;
+  }
+
+  AudioManager* audio_manager_;
+  AudioParameters::Format format_;
+  ChannelLayout channel_layout_;
+  int sample_rate_;
+  int samples_per_packet_;
+};
+
+typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper;
+typedef StreamWrapper<AudioOutputStreamTraits> AudioOutputStreamWrapper;
+
+// This test is intended for manual tests and should only be enabled
+// when it is required to make a real-time test of audio in full duplex and
+// at the same time create a text file which contains measured delay values.
+// The file can later be analyzed off line using e.g. MATLAB.
+// MATLAB example:
+//   D=load('audio_delay_values_ms.txt');
+//   x=cumsum(D(:,1));
+//   plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4));
+//   axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]);
+//   legend('buffer delay','input delay','output delay','total delay');
+//   xlabel('time [msec]')
+//   ylabel('delay [msec]')
+//   title('Full-duplex audio delay measurement');
+TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) {
+  AudioDeviceInfoAccessorForTests device_info_accessor(audio_manager());
+  ABORT_AUDIO_TEST_IF_NOT(device_info_accessor.HasAudioInputDevices() &&
+                          device_info_accessor.HasAudioOutputDevices());
+
+  AudioInputStreamWrapper aisw(audio_manager());
+  AudioInputStream* ais = aisw.Create();
+  EXPECT_TRUE(ais);
+
+  AudioOutputStreamWrapper aosw(audio_manager());
+  AudioOutputStream* aos = aosw.Create();
+  EXPECT_TRUE(aos);
+
+  // This test only supports identical parameters in both directions.
+  // TODO(henrika): it is possible to cut delay here by using different
+  // buffer sizes for input and output.
+  if (aisw.sample_rate() != aosw.sample_rate() ||
+      aisw.samples_per_packet() != aosw.samples_per_packet() ||
+      aisw.channels() != aosw.channels()) {
+    LOG(ERROR) << "This test requires symmetric input and output parameters. "
+        "Ensure that sample rate and number of channels are identical in "
+        "both directions";
+    aos->Close();
+    ais->Close();
+    return;
+  }
+
+  EXPECT_EQ(ais->Open(), AudioInputStream::OpenOutcome::kSuccess);
+  EXPECT_TRUE(aos->Open());
+
+  FullDuplexAudioSinkSource full_duplex(
+      aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels());
+
+  VLOG(0) << ">> You should now be able to hear yourself in loopback...";
+  DVLOG(0) << "   sample_rate       : " << aisw.sample_rate();
+  DVLOG(0) << "   samples_per_packet: " << aisw.samples_per_packet();
+  DVLOG(0) << "   channels          : " << aisw.channels();
+
+  ais->Start(&full_duplex);
+  aos->Start(&full_duplex);
+
+  // Wait for approximately 10 seconds. The user will hear their own voice
+  // in loop back during this time. At the same time, delay recordings are
+  // performed and stored in the output text file.
+  base::RunLoop run_loop;
+  task_runner()->PostDelayedTask(
+      FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_timeout());
+  run_loop.Run();
+
+  aos->Stop();
+  ais->Stop();
+
+  // All Close() operations that run on the mocked audio thread,
+  // should be synchronous and not post additional close tasks to
+  // mocked the audio thread. Hence, there is no need to call
+  // message_loop()->RunUntilIdle() after the Close() methods.
+  aos->Close();
+  ais->Close();
+}
+
+}  // namespace
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_manager.cc b/third_party/chromium/media/audio/audio_manager.cc
new file mode 100644
index 0000000..0c33df6
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_manager.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_manager.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_annotations.h"
+#include "build/build_config.h"
+#include "media/audio/fake_audio_log_factory.h"
+#include "media/base/media_switches.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_com_initializer.h"
+#endif
+
+namespace media {
+namespace {
+
+// The singleton instance of AudioManager. This is set when Create() is called.
+AudioManager* g_last_created = nullptr;
+
+// Helper class for managing global AudioManager data.
+class AudioManagerHelper {
+ public:
+  AudioManagerHelper() = default;
+
+  AudioManagerHelper(const AudioManagerHelper&) = delete;
+  AudioManagerHelper& operator=(const AudioManagerHelper&) = delete;
+
+  ~AudioManagerHelper() = default;
+
+  AudioLogFactory* fake_log_factory() { return &fake_log_factory_; }
+
+#if defined(OS_WIN)
+  // This should be called before creating an AudioManager in tests to ensure
+  // that the creating thread is COM initialized.
+  void InitializeCOMForTesting() {
+    com_initializer_for_testing_ =
+        std::make_unique<base::win::ScopedCOMInitializer>();
+  }
+#endif
+
+  void set_app_name(const std::string& app_name) { app_name_ = app_name; }
+  const std::string& app_name() const { return app_name_; }
+
+  FakeAudioLogFactory fake_log_factory_;
+
+#if defined(OS_WIN)
+  std::unique_ptr<base::win::ScopedCOMInitializer> com_initializer_for_testing_;
+#endif
+
+  std::string app_name_;
+};
+
+AudioManagerHelper* GetHelper() {
+  static AudioManagerHelper* helper = new AudioManagerHelper();
+  return helper;
+}
+
+}  // namespace
+
+// Forward declaration of the platform specific AudioManager factory function.
+std::unique_ptr<AudioManager> CreateAudioManager(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory);
+
+void AudioManager::SetMaxStreamCountForTesting(int max_input, int max_output) {
+  NOTREACHED();
+}
+
+AudioManager::AudioManager(std::unique_ptr<AudioThread> audio_thread)
+    : audio_thread_(std::move(audio_thread)) {
+  DCHECK(audio_thread_);
+
+  if (g_last_created) {
+    // We create multiple instances of AudioManager only when testing.
+    // We should not encounter this case in production.
+    LOG(WARNING) << "Multiple instances of AudioManager detected";
+  }
+  // We always override |g_last_created| irrespective of whether it is already
+  // set or not because it represents the last created instance.
+  g_last_created = this;
+}
+
+AudioManager::~AudioManager() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(shutdown_);
+
+  if (g_last_created == this) {
+    g_last_created = nullptr;
+  } else {
+    // We create multiple instances of AudioManager only when testing.
+    // We should not encounter this case in production.
+    LOG(WARNING) << "Multiple instances of AudioManager detected";
+  }
+}
+
+// static
+std::unique_ptr<AudioManager> AudioManager::Create(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory) {
+  std::unique_ptr<AudioManager> manager =
+      CreateAudioManager(std::move(audio_thread), audio_log_factory);
+  manager->InitializeDebugRecording();
+  return manager;
+}
+
+// static
+std::unique_ptr<AudioManager> AudioManager::CreateForTesting(
+    std::unique_ptr<AudioThread> audio_thread) {
+#if defined(OS_WIN)
+  GetHelper()->InitializeCOMForTesting();
+#endif
+  return Create(std::move(audio_thread), GetHelper()->fake_log_factory());
+}
+
+// static
+void AudioManager::SetGlobalAppName(const std::string& app_name) {
+  GetHelper()->set_app_name(app_name);
+}
+
+// static
+const std::string& AudioManager::GetGlobalAppName() {
+  return GetHelper()->app_name();
+}
+
+// static
+AudioManager* AudioManager::Get() {
+  return g_last_created;
+}
+
+bool AudioManager::Shutdown() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (audio_thread_->GetTaskRunner()->BelongsToCurrentThread()) {
+    // If this is the audio thread, there is no need to check if it's hung
+    // (since it's clearly not). https://crbug.com/919854.
+    ShutdownOnAudioThread();
+  } else {
+    // Do not attempt to stop the audio thread if it is hung.
+    // Otherwise the current thread will hang too: https://crbug.com/729494
+    // TODO(olka, grunell): Will be fixed when audio is its own process.
+    if (audio_thread_->IsHung())
+      return false;
+
+    audio_thread_->GetTaskRunner()->PostTask(
+        FROM_HERE, base::BindOnce(&AudioManager::ShutdownOnAudioThread,
+                                  base::Unretained(this)));
+  }
+  audio_thread_->Stop();
+  shutdown_ = true;
+  return true;
+}
+
+void AudioManager::SetDiverterCallbacks(
+    AddDiverterCallback add_callback,
+    RemoveDiverterCallback remove_callback) {
+  add_diverter_callback_ = std::move(add_callback);
+  remove_diverter_callback_ = std::move(remove_callback);
+}
+
+void AudioManager::AddDiverter(const base::UnguessableToken& group_id,
+                               media::AudioSourceDiverter* diverter) {
+  if (!add_diverter_callback_.is_null())
+    add_diverter_callback_.Run(group_id, diverter);
+}
+
+void AudioManager::RemoveDiverter(media::AudioSourceDiverter* diverter) {
+  if (!remove_diverter_callback_.is_null())
+    remove_diverter_callback_.Run(diverter);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_manager.h b/third_party/chromium/media/audio/audio_manager.h
new file mode 100644
index 0000000..bb2fc8b
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_manager.h
@@ -0,0 +1,284 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_MANAGER_H_
+#define MEDIA_AUDIO_AUDIO_MANAGER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "build/build_config.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_logging.h"
+#include "media/audio/audio_thread.h"
+#include "media/base/audio_parameters.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class UnguessableToken;
+}
+
+namespace media {
+
+class AudioDebugRecordingManager;
+class AudioInputStream;
+class AudioManager;
+class AudioOutputStream;
+class AudioSourceDiverter;
+
+// Manages all audio resources.  Provides some convenience functions that avoid
+// the need to provide iterators over the existing streams.
+class MEDIA_EXPORT AudioManager {
+ public:
+  AudioManager(const AudioManager&) = delete;
+  AudioManager& operator=(const AudioManager&) = delete;
+
+  virtual ~AudioManager();
+
+  // Construct the audio manager; only one instance is allowed.
+  //
+  // The manager will forward CreateAudioLog() calls to the provided
+  // AudioLogFactory; as such |audio_log_factory| must outlive the AudioManager.
+  //
+  // The manager will use |audio_thread->GetTaskRunner()| for audio IO.
+  // On OS_MAC, CoreAudio requires that |audio_thread->GetTaskRunner()|
+  // must belong to the main thread of the process, which in our case is sadly
+  // the browser UI thread. Failure to execute calls on the right thread leads
+  // to crashes and odd behavior. See http://crbug.com/158170.
+  //
+  // The manager will use |audio_thread->GetWorkerTaskRunner()| for heavyweight
+  // tasks. The |audio_thread->GetWorkerTaskRunner()| may be the same as
+  // |audio_thread->GetTaskRunner()|.
+  static std::unique_ptr<AudioManager> Create(
+      std::unique_ptr<AudioThread> audio_thread,
+      AudioLogFactory* audio_log_factory);
+
+  // A convenience wrapper of AudioManager::Create for testing.
+  static std::unique_ptr<AudioManager> CreateForTesting(
+      std::unique_ptr<AudioThread> audio_thread);
+
+  // Sets the name of the audio source as seen by external apps.
+  static void SetGlobalAppName(const std::string& app_name);
+
+  // Returns the app name or an empty string if it is not set.
+  static const std::string& GetGlobalAppName();
+
+  // Returns the pointer to the last created instance, or NULL if not yet
+  // created. This is a utility method for the code outside of media directory,
+  // like src/chrome.
+  static AudioManager* Get();
+
+  // Synchronously releases all audio resources.
+  // Must be called before deletion and on the same thread as AudioManager
+  // was created.
+  // Returns true on success but false if AudioManager could not be shutdown.
+  // AudioManager instance must not be deleted if shutdown failed.
+  virtual bool Shutdown();
+
+  // Log callback used for sending log messages from a stream to the object
+  // that manages the stream.
+  using LogCallback = base::RepeatingCallback<void(const std::string&)>;
+
+  // Factory for all the supported stream formats. |params| defines parameters
+  // of the audio stream to be created.
+  //
+  // |params.sample_per_packet| is the requested buffer allocation which the
+  // audio source thinks it can usually fill without blocking. Internally two
+  // or three buffers are created, one will be locked for playback and one will
+  // be ready to be filled in the call to AudioSourceCallback::OnMoreData().
+  //
+  // To create a stream for the default output device, pass an empty string
+  // for |device_id|, otherwise the specified audio device will be opened.
+  //
+  // Returns NULL if the combination of the parameters is not supported, or if
+  // we have reached some other platform specific limit.
+  //
+  // |params.format| can be set to AUDIO_PCM_LOW_LATENCY and that has two
+  // effects:
+  // 1- Instead of triple buffered the audio will be double buffered.
+  // 2- A low latency driver or alternative audio subsystem will be used when
+  //    available.
+  //
+  // Do not free the returned AudioOutputStream. It is owned by AudioManager.
+  virtual AudioOutputStream* MakeAudioOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) = 0;
+
+  // Creates new audio output proxy. A proxy implements
+  // AudioOutputStream interface, but unlike regular output stream
+  // created with MakeAudioOutputStream() it opens device only when a
+  // sound is actually playing.
+  virtual AudioOutputStream* MakeAudioOutputStreamProxy(
+      const AudioParameters& params,
+      const std::string& device_id) = 0;
+
+  // Factory to create audio recording streams.
+  // |channels| can be 1 or 2.
+  // |sample_rate| is in hertz and can be any value supported by the platform.
+  // |samples_per_packet| is in hertz as well and can be 0 to |sample_rate|,
+  // with 0 suggesting that the implementation use a default value for that
+  // platform.
+  // Returns NULL if the combination of the parameters is not supported, or if
+  // we have reached some other platform specific limit.
+  //
+  // Do not free the returned AudioInputStream. It is owned by AudioManager.
+  // When you are done with it, call |Stop()| and |Close()| to release it.
+  virtual AudioInputStream* MakeAudioInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) = 0;
+
+  // Returns the task runner used for audio IO.
+  base::SingleThreadTaskRunner* GetTaskRunner() const {
+    return audio_thread_->GetTaskRunner();
+  }
+
+  // Heavyweight tasks should use GetWorkerTaskRunner() instead of
+  // GetTaskRunner(). On most platforms they are the same, but some share the
+  // UI loop with the audio IO loop.
+  base::SingleThreadTaskRunner* GetWorkerTaskRunner() const {
+    return audio_thread_->GetWorkerTaskRunner();
+  }
+
+  // Allows clients to listen for device state changes; e.g. preferred sample
+  // rate or channel layout changes.  The typical response to receiving this
+  // callback is to recreate the stream.
+  class AudioDeviceListener {
+   public:
+    virtual void OnDeviceChange() = 0;
+  };
+
+  virtual void AddOutputDeviceChangeListener(AudioDeviceListener* listener) = 0;
+  virtual void RemoveOutputDeviceChangeListener(
+      AudioDeviceListener* listener) = 0;
+
+  // Create a new AudioLog object for tracking the behavior for one or more
+  // instances of the given component.  See AudioLogFactory for more details.
+  virtual std::unique_ptr<AudioLog> CreateAudioLog(
+      AudioLogFactory::AudioComponent component,
+      int component_id) = 0;
+
+  // Get debug recording manager. This can only be called on AudioManager's
+  // thread (GetTaskRunner()).
+  virtual AudioDebugRecordingManager* GetAudioDebugRecordingManager() = 0;
+
+  // Gets the name of the audio manager (e.g., Windows, Mac, PulseAudio).
+  virtual const char* GetName() = 0;
+
+  // Limits the number of streams that can be created for testing purposes.
+  virtual void SetMaxStreamCountForTesting(int max_input, int max_output);
+
+  // TODO(crbug/824019): The following are temporary, as a middle-ground step
+  // necessary to resolve a chicken-and-egg problem as we migrate audio
+  // mirroring into the new AudioService. Add/RemoveDiverter() allow
+  // AudioOutputController to (de)register itself as an AudioSourceDiverter,
+  // while SetDiverterCallbacks() allows the entity that is interested in such
+  // notifications to receive them.
+  using AddDiverterCallback =
+      base::RepeatingCallback<void(const base::UnguessableToken&,
+                                   media::AudioSourceDiverter*)>;
+  using RemoveDiverterCallback =
+      base::RepeatingCallback<void(media::AudioSourceDiverter*)>;
+  virtual void SetDiverterCallbacks(AddDiverterCallback add_callback,
+                                    RemoveDiverterCallback remove_callback);
+  virtual void AddDiverter(const base::UnguessableToken& group_id,
+                           media::AudioSourceDiverter* diverter);
+  virtual void RemoveDiverter(media::AudioSourceDiverter* diverter);
+
+ protected:
+  FRIEND_TEST_ALL_PREFIXES(AudioManagerTest, AudioDebugRecording);
+  friend class AudioDeviceInfoAccessorForTests;
+
+  explicit AudioManager(std::unique_ptr<AudioThread> audio_thread);
+
+  virtual void ShutdownOnAudioThread() = 0;
+
+  // Initializes debug recording. Can be called on any thread; will post to the
+  // audio thread if not called on it.
+  virtual void InitializeDebugRecording() = 0;
+
+  // Returns true if the OS reports existence of audio devices. This does not
+  // guarantee that the existing devices support all formats and sample rates.
+  virtual bool HasAudioOutputDevices() = 0;
+
+  // Returns true if the OS reports existence of audio recording devices. This
+  // does not guarantee that the existing devices support all formats and
+  // sample rates.
+  virtual bool HasAudioInputDevices() = 0;
+
+  // Appends a list of available input devices to |device_descriptions|,
+  // which must initially be empty. It is not guaranteed that all the
+  // devices in the list support all formats and sample rates for
+  // recording.
+  //
+  // Not threadsafe; in production this should only be called from the
+  // Audio worker thread (see GetTaskRunner()).
+  virtual void GetAudioInputDeviceDescriptions(
+      AudioDeviceDescriptions* device_descriptions) = 0;
+
+  // Appends a list of available output devices to |device_descriptions|,
+  // which must initially be empty.
+  //
+  // Not threadsafe; in production this should only be called from the
+  // Audio worker thread (see GetTaskRunner()).
+  virtual void GetAudioOutputDeviceDescriptions(
+      AudioDeviceDescriptions* device_descriptions) = 0;
+
+  // Returns the default output hardware audio parameters for opening output
+  // streams. It is a convenience interface to
+  // AudioManagerBase::GetPreferredOutputStreamParameters and each AudioManager
+  // does not need their own implementation to this interface.
+  // TODO(tommi): Remove this method and use GetOutputStreamParameteres instead.
+  virtual AudioParameters GetDefaultOutputStreamParameters() = 0;
+
+  // Returns the output hardware audio parameters for a specific output device.
+  virtual AudioParameters GetOutputStreamParameters(
+      const std::string& device_id) = 0;
+
+  // Returns the input hardware audio parameters of the specific device
+  // for opening input streams. Each AudioManager needs to implement their own
+  // version of this interface.
+  virtual AudioParameters GetInputStreamParameters(
+      const std::string& device_id) = 0;
+
+  // Returns the device id of an output device that belongs to the same hardware
+  // as the specified input device.
+  // If the hardware has only an input device (e.g. a webcam), the return value
+  // will be empty (which the caller can then interpret to be the default output
+  // device).  Implementations that don't yet support this feature, must return
+  // an empty string. Must be called on the audio worker thread (see
+  // GetTaskRunner()).
+  virtual std::string GetAssociatedOutputDeviceID(
+      const std::string& input_device_id) = 0;
+
+  // These functions return the ID of the default/communications audio
+  // input/output devices respectively.
+  // Implementations that do not support this functionality should return an
+  // empty string.
+  virtual std::string GetDefaultInputDeviceID() = 0;
+  virtual std::string GetDefaultOutputDeviceID() = 0;
+  virtual std::string GetCommunicationsInputDeviceID() = 0;
+  virtual std::string GetCommunicationsOutputDeviceID() = 0;
+
+ private:
+  friend class AudioSystemHelper;
+
+  std::unique_ptr<AudioThread> audio_thread_;
+  bool shutdown_ = false;  // True after |this| has been shutdown.
+
+  AddDiverterCallback add_diverter_callback_;
+  RemoveDiverterCallback remove_diverter_callback_;
+
+  THREAD_CHECKER(thread_checker_);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_MANAGER_H_
diff --git a/third_party/chromium/media/audio/audio_manager_base.cc b/third_party/chromium/media/audio/audio_manager_base.cc
new file mode 100644
index 0000000..5400e09
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_manager_base.cc
@@ -0,0 +1,632 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_manager_base.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "build/buildflag.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_output_dispatcher_impl.h"
+#include "media/audio/audio_output_proxy.h"
+#include "media/audio/audio_output_resampler.h"
+#include "media/audio/fake_audio_input_stream.h"
+#include "media/audio/fake_audio_output_stream.h"
+#include "media/base/media_switches.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+#include "base/logging.h"
+#include "build/chromeos_buildflags.h"
+#include "media/audio/audio_input_stream_data_interceptor.h"
+
+namespace media {
+
+namespace {
+
+const int kStreamCloseDelaySeconds = 5;
+
+// Default maximum number of output streams that can be open simultaneously
+// for all platforms.
+const int kDefaultMaxOutputStreams = 16;
+
+// Default maximum number of input streams that can be open simultaneously
+// for all platforms.
+const int kMaxInputStreams = 16;
+
+const int kMaxInputChannels = 3;
+
+// Helper function to pass as callback when the audio debug recording is not
+// enabled.
+std::unique_ptr<AudioDebugRecorder> GetNullptrAudioDebugRecorder(
+    const AudioParameters& params) {
+  return nullptr;
+}
+
+// This enum must match the numbering for AudioOutputProxyStreamFormat in
+// enums.xml. Do not reorder or remove items, only add new items before
+// STREAM_FORMAT_MAX.
+enum StreamFormat {
+  STREAM_FORMAT_BITSTREAM = 0,
+  STREAM_FORMAT_PCM_LINEAR = 1,
+  STREAM_FORMAT_PCM_LOW_LATENCY = 2,
+  STREAM_FORMAT_PCM_LOW_LATENCY_FALLBACK_TO_FAKE = 3,
+  STREAM_FORMAT_FAKE = 4,
+  STREAM_FORMAT_MAX = 4,
+};
+
+PRINTF_FORMAT(2, 3)
+void SendLogMessage(const AudioManagerBase::LogCallback& callback,
+                    const char* format,
+                    ...) {
+  if (callback.is_null())
+    return;
+  va_list args;
+  va_start(args, format);
+  callback.Run("AMB::" + base::StringPrintV(format, args));
+  va_end(args);
+}
+
+}  // namespace
+
+struct AudioManagerBase::DispatcherParams {
+  DispatcherParams(const AudioParameters& input,
+                   const AudioParameters& output,
+                   const std::string& output_device_id)
+      : input_params(input),
+        output_params(output),
+        output_device_id(output_device_id) {}
+  ~DispatcherParams() = default;
+
+  const AudioParameters input_params;
+  const AudioParameters output_params;
+  const std::string output_device_id;
+  std::unique_ptr<AudioOutputDispatcher> dispatcher;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DispatcherParams);
+};
+
+class AudioManagerBase::CompareByParams {
+ public:
+  explicit CompareByParams(const DispatcherParams* dispatcher)
+      : dispatcher_(dispatcher) {}
+  bool operator()(
+      const std::unique_ptr<DispatcherParams>& dispatcher_in) const {
+    // We will reuse the existing dispatcher when:
+    // 1) Unified IO is not used, input_params and output_params of the
+    //    existing dispatcher are the same as the requested dispatcher.
+    // 2) Unified IO is used, input_params and output_params of the existing
+    //    dispatcher are the same as the request dispatcher.
+    return (dispatcher_->input_params.Equals(dispatcher_in->input_params) &&
+            dispatcher_->output_params.Equals(dispatcher_in->output_params) &&
+            dispatcher_->output_device_id == dispatcher_in->output_device_id);
+  }
+
+ private:
+  const DispatcherParams* dispatcher_;
+};
+
+AudioManagerBase::AudioManagerBase(std::unique_ptr<AudioThread> audio_thread,
+                                   AudioLogFactory* audio_log_factory)
+    : AudioManager(std::move(audio_thread)),
+      max_num_output_streams_(kDefaultMaxOutputStreams),
+      num_output_streams_(0),
+      // TODO(dalecurtis): Switch this to an base::ObserverListThreadSafe, so we
+      // don't block the UI thread when swapping devices.
+      output_listeners_(base::ObserverListPolicy::EXISTING_ONLY),
+      audio_log_factory_(audio_log_factory) {}
+
+AudioManagerBase::~AudioManagerBase() {
+  // All the output streams should have been deleted.
+  CHECK_EQ(0, num_output_streams_);
+  // All the input streams should have been deleted.
+  CHECK(input_streams_.empty());
+}
+
+void AudioManagerBase::GetAudioInputDeviceDescriptions(
+    AudioDeviceDescriptions* device_descriptions) {
+  CHECK(GetTaskRunner()->BelongsToCurrentThread());
+  GetAudioDeviceDescriptions(device_descriptions,
+                             &AudioManagerBase::GetAudioInputDeviceNames,
+                             &AudioManagerBase::GetDefaultInputDeviceID,
+                             &AudioManagerBase::GetCommunicationsInputDeviceID,
+                             &AudioManagerBase::GetGroupIDInput);
+}
+
+void AudioManagerBase::GetAudioOutputDeviceDescriptions(
+    AudioDeviceDescriptions* device_descriptions) {
+  CHECK(GetTaskRunner()->BelongsToCurrentThread());
+  GetAudioDeviceDescriptions(device_descriptions,
+                             &AudioManagerBase::GetAudioOutputDeviceNames,
+                             &AudioManagerBase::GetDefaultOutputDeviceID,
+                             &AudioManagerBase::GetCommunicationsOutputDeviceID,
+                             &AudioManagerBase::GetGroupIDOutput);
+}
+
+void AudioManagerBase::GetAudioDeviceDescriptions(
+    AudioDeviceDescriptions* device_descriptions,
+    void (AudioManagerBase::*get_device_names)(AudioDeviceNames*),
+    std::string (AudioManagerBase::*get_default_device_id)(),
+    std::string (AudioManagerBase::*get_communications_device_id)(),
+    std::string (AudioManagerBase::*get_group_id)(const std::string&)) {
+  CHECK(GetTaskRunner()->BelongsToCurrentThread());
+  AudioDeviceNames device_names;
+  (this->*get_device_names)(&device_names);
+  std::string real_default_device_id = (this->*get_default_device_id)();
+  std::string real_communications_device_id =
+      (this->*get_communications_device_id)();
+  std::string real_default_name;
+  std::string real_communications_name;
+
+  // Find the names for the real devices that are mapped to the default and
+  // communications devices.
+  for (const auto& name : device_names) {
+    if (name.unique_id == real_default_device_id)
+      real_default_name = name.device_name;
+    if (name.unique_id == real_communications_device_id)
+      real_communications_name = name.device_name;
+  }
+
+  for (auto& name : device_names) {
+    if (AudioDeviceDescription::IsDefaultDevice(name.unique_id))
+      name.device_name = real_default_name;
+    else if (AudioDeviceDescription::IsCommunicationsDevice(name.unique_id))
+      name.device_name = real_communications_name;
+    std::string group_id = (this->*get_group_id)(name.unique_id);
+    device_descriptions->emplace_back(std::move(name.device_name),
+                                      std::move(name.unique_id),
+                                      std::move(group_id));
+  }
+}
+
+AudioOutputStream* AudioManagerBase::MakeAudioOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  CHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(params.IsValid());
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kFailAudioStreamCreation)) {
+    return nullptr;
+  }
+
+  SendLogMessage(log_callback, "%s({device_id=%s}, {params=[%s]})", __func__,
+                 device_id.c_str(), params.AsHumanReadableString().c_str());
+
+  // Limit the number of audio streams opened. This is to prevent using
+  // excessive resources for a large number of audio streams. More
+  // importantly it prevents instability on certain systems.
+  // See bug: http://crbug.com/30242.
+  if (num_output_streams_ >= max_num_output_streams_) {
+    LOG(ERROR) << "Number of opened output audio streams "
+               << num_output_streams_ << " exceed the max allowed number "
+               << max_num_output_streams_;
+    return nullptr;
+  }
+
+  AudioOutputStream* stream;
+  switch (params.format()) {
+    case AudioParameters::AUDIO_PCM_LINEAR:
+      DCHECK(AudioDeviceDescription::IsDefaultDevice(device_id))
+          << "AUDIO_PCM_LINEAR supports only the default device.";
+      stream = MakeLinearOutputStream(params, log_callback);
+      break;
+    case AudioParameters::AUDIO_PCM_LOW_LATENCY:
+      stream = MakeLowLatencyOutputStream(params, device_id, log_callback);
+      break;
+    case AudioParameters::AUDIO_BITSTREAM_AC3:
+    case AudioParameters::AUDIO_BITSTREAM_EAC3:
+      stream = MakeBitstreamOutputStream(params, device_id, log_callback);
+      break;
+    case AudioParameters::AUDIO_FAKE:
+      stream = FakeAudioOutputStream::MakeFakeStream(this, params);
+      break;
+    default:
+      stream = nullptr;
+      break;
+  }
+
+  if (stream) {
+    ++num_output_streams_;
+    SendLogMessage(log_callback, "%s => (number of streams=%d)", __func__,
+                   output_stream_count());
+  }
+
+  return stream;
+}
+
+AudioOutputStream* AudioManagerBase::MakeBitstreamOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  return nullptr;
+}
+
+AudioInputStream* AudioManagerBase::MakeAudioInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  CHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kFailAudioStreamCreation)) {
+    return nullptr;
+  }
+
+  SendLogMessage(log_callback, "%s({device_id=%s}, {params=[%s]})", __func__,
+                 device_id.c_str(), params.AsHumanReadableString().c_str());
+
+  if (!params.IsValid() || (params.channels() > kMaxInputChannels) ||
+      device_id.empty()) {
+    DLOG(ERROR) << "Audio parameters are invalid for device " << device_id
+                << ", params: " << params.AsHumanReadableString();
+    return nullptr;
+  }
+
+  if (input_stream_count() >= kMaxInputStreams) {
+    LOG(ERROR) << "Number of opened input audio streams "
+               << input_stream_count() << " exceed the max allowed number "
+               << kMaxInputStreams;
+    return nullptr;
+  }
+
+  DVLOG(2) << "Creating a new AudioInputStream with buffer size = "
+           << params.frames_per_buffer();
+
+  AudioInputStream* stream;
+  switch (params.format()) {
+    case AudioParameters::AUDIO_PCM_LINEAR:
+      stream = MakeLinearInputStream(params, device_id, log_callback);
+      break;
+    case AudioParameters::AUDIO_PCM_LOW_LATENCY:
+      stream = MakeLowLatencyInputStream(params, device_id, log_callback);
+      break;
+    case AudioParameters::AUDIO_FAKE:
+      stream = FakeAudioInputStream::MakeFakeStream(this, params);
+      break;
+    default:
+      stream = nullptr;
+      break;
+  }
+
+  if (stream) {
+    input_streams_.insert(stream);
+    if (!log_callback.is_null()) {
+      SendLogMessage(log_callback, "%s => (number of streams=%d)", __func__,
+                     input_stream_count());
+    }
+
+    if (!params.IsBitstreamFormat() && debug_recording_manager_) {
+      // Using unretained for |debug_recording_manager_| is safe since it
+      // outlives the audio thread, on which streams are operated.
+      // Note: The AudioInputStreamDataInterceptor takes ownership of the
+      // created stream and cleans it up when it is Close()d, transparently to
+      // the user of the stream. I the case where the audio manager closes the
+      // stream (Mac), this will result in a dangling pointer.
+      stream = new AudioInputStreamDataInterceptor(
+          base::BindRepeating(
+              &AudioDebugRecordingManager::RegisterDebugRecordingSource,
+              base::Unretained(debug_recording_manager_.get()),
+              AudioDebugRecordingStreamType::kInput, params),
+          stream);
+    }
+  }
+
+  return stream;
+}
+
+AudioOutputStream* AudioManagerBase::MakeAudioOutputStreamProxy(
+    const AudioParameters& params,
+    const std::string& device_id) {
+  CHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(params.IsValid());
+  absl::optional<StreamFormat> uma_stream_format;
+
+  // If the caller supplied an empty device id to select the default device,
+  // we fetch the actual device id of the default device so that the lookup
+  // will find the correct device regardless of whether it was opened as
+  // "default" or via the specific id.
+  // NOTE: Implementations that don't yet support opening non-default output
+  // devices may return an empty string from GetDefaultOutputDeviceID().
+  std::string output_device_id =
+      AudioDeviceDescription::IsDefaultDevice(device_id)
+          ?
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+          // On ChromeOS, it is expected that, if the default device is given,
+          // no specific device ID should be used since the actual output device
+          // should change dynamically if the system default device changes.
+          // See http://crbug.com/750614.
+          std::string()
+#else
+          GetDefaultOutputDeviceID()
+#endif
+          : device_id;
+
+  // If we're not using AudioOutputResampler our output parameters are the same
+  // as our input parameters.
+  AudioParameters output_params = params;
+
+  // If audio has been disabled force usage of a fake audio stream.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableAudioOutput)) {
+    output_params.set_format(AudioParameters::AUDIO_FAKE);
+  }
+
+  if (params.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY &&
+      output_params.format() != AudioParameters::AUDIO_FAKE) {
+    output_params =
+        GetPreferredOutputStreamParameters(output_device_id, params);
+
+    // Ensure we only pass on valid output parameters.
+    if (output_params.IsValid()) {
+      if (params.effects() & AudioParameters::MULTIZONE) {
+        // Never turn off the multizone effect even if it is not preferred.
+        output_params.set_effects(output_params.effects() |
+                                  AudioParameters::MULTIZONE);
+      }
+      if (params.effects() != output_params.effects()) {
+        // Turn off effects that weren't requested.
+        output_params.set_effects(params.effects() & output_params.effects());
+      }
+
+      uma_stream_format = STREAM_FORMAT_PCM_LOW_LATENCY;
+    } else {
+      // We've received invalid audio output parameters, so switch to a mock
+      // output device based on the input parameters.  This may happen if the OS
+      // provided us junk values for the hardware configuration.
+      LOG(ERROR) << "Invalid audio output parameters received; using fake "
+                 << "audio path: " << output_params.AsHumanReadableString();
+
+      // Tell the AudioManager to create a fake output device.
+      output_params = params;
+      output_params.set_format(AudioParameters::AUDIO_FAKE);
+      uma_stream_format = STREAM_FORMAT_PCM_LOW_LATENCY_FALLBACK_TO_FAKE;
+    }
+
+    output_params.set_latency_tag(params.latency_tag());
+  } else {
+    switch (output_params.format()) {
+      case AudioParameters::AUDIO_PCM_LINEAR:
+        uma_stream_format = STREAM_FORMAT_PCM_LINEAR;
+        break;
+      case AudioParameters::AUDIO_FAKE:
+        uma_stream_format = STREAM_FORMAT_FAKE;
+        break;
+      default:
+        if (output_params.IsBitstreamFormat())
+          uma_stream_format = STREAM_FORMAT_BITSTREAM;
+        else
+          NOTREACHED();
+    }
+  }
+
+  if (uma_stream_format) {
+    UMA_HISTOGRAM_ENUMERATION("Media.AudioOutputStreamProxy.StreamFormat",
+                              *uma_stream_format, STREAM_FORMAT_MAX + 1);
+  } else {
+    NOTREACHED();
+  }
+
+  std::unique_ptr<DispatcherParams> dispatcher_params =
+      std::make_unique<DispatcherParams>(params, output_params,
+                                         output_device_id);
+
+  auto it = std::find_if(output_dispatchers_.begin(), output_dispatchers_.end(),
+                         CompareByParams(dispatcher_params.get()));
+  if (it != output_dispatchers_.end())
+    return (*it)->dispatcher->CreateStreamProxy();
+
+  const base::TimeDelta kCloseDelay = base::Seconds(kStreamCloseDelaySeconds);
+  std::unique_ptr<AudioOutputDispatcher> dispatcher;
+  if (output_params.format() != AudioParameters::AUDIO_FAKE &&
+      !output_params.IsBitstreamFormat()) {
+    // Using unretained for |debug_recording_manager_| is safe since it
+    // outlives the dispatchers (cleared in ShutdownOnAudioThread()).
+    dispatcher = std::make_unique<AudioOutputResampler>(
+        this, params, output_params, output_device_id, kCloseDelay,
+        debug_recording_manager_
+            ? base::BindRepeating(
+                  &AudioDebugRecordingManager::RegisterDebugRecordingSource,
+                  base::Unretained(debug_recording_manager_.get()),
+                  AudioDebugRecordingStreamType::kOutput)
+            : base::BindRepeating(&GetNullptrAudioDebugRecorder));
+  } else {
+    dispatcher = std::make_unique<AudioOutputDispatcherImpl>(
+        this, output_params, output_device_id, kCloseDelay);
+  }
+
+  dispatcher_params->dispatcher = std::move(dispatcher);
+  output_dispatchers_.push_back(std::move(dispatcher_params));
+  return output_dispatchers_.back()->dispatcher->CreateStreamProxy();
+}
+
+void AudioManagerBase::GetAudioInputDeviceNames(
+    AudioDeviceNames* device_names) {
+}
+
+void AudioManagerBase::GetAudioOutputDeviceNames(
+    AudioDeviceNames* device_names) {
+}
+
+void AudioManagerBase::ReleaseOutputStream(AudioOutputStream* stream) {
+  CHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(stream);
+  CHECK_GT(num_output_streams_, 0);
+  // TODO(xians) : Have a clearer destruction path for the AudioOutputStream.
+  // For example, pass the ownership to AudioManager so it can delete the
+  // streams.
+  --num_output_streams_;
+  delete stream;
+}
+
+void AudioManagerBase::ReleaseInputStream(AudioInputStream* stream) {
+  CHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(stream);
+  // TODO(xians) : Have a clearer destruction path for the AudioInputStream.
+  CHECK_EQ(1u, input_streams_.erase(stream));
+  delete stream;
+}
+
+void AudioManagerBase::ShutdownOnAudioThread() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  // Close all output streams.
+  output_dispatchers_.clear();
+}
+
+void AudioManagerBase::AddOutputDeviceChangeListener(
+    AudioDeviceListener* listener) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  output_listeners_.AddObserver(listener);
+}
+
+void AudioManagerBase::RemoveOutputDeviceChangeListener(
+    AudioDeviceListener* listener) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  output_listeners_.RemoveObserver(listener);
+}
+
+void AudioManagerBase::NotifyAllOutputDeviceChangeListeners() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DVLOG(1) << "Firing OnDeviceChange() notifications.";
+  for (auto& observer : output_listeners_)
+    observer.OnDeviceChange();
+}
+
+AudioParameters AudioManagerBase::GetDefaultOutputStreamParameters() {
+  return GetPreferredOutputStreamParameters(GetDefaultOutputDeviceID(),
+      AudioParameters());
+}
+
+AudioParameters AudioManagerBase::GetOutputStreamParameters(
+    const std::string& device_id) {
+  return GetPreferredOutputStreamParameters(device_id,
+      AudioParameters());
+}
+
+AudioParameters AudioManagerBase::GetInputStreamParameters(
+    const std::string& device_id) {
+  NOTREACHED();
+  return AudioParameters();
+}
+
+std::string AudioManagerBase::GetAssociatedOutputDeviceID(
+    const std::string& input_device_id) {
+  return std::string();
+}
+
+std::string AudioManagerBase::GetGroupIDOutput(
+    const std::string& output_device_id) {
+  if (output_device_id == AudioDeviceDescription::kDefaultDeviceId) {
+    std::string real_device_id = GetDefaultOutputDeviceID();
+    if (!real_device_id.empty())
+      return real_device_id;
+  } else if (output_device_id ==
+             AudioDeviceDescription::kCommunicationsDeviceId) {
+    std::string real_device_id = GetCommunicationsOutputDeviceID();
+    if (!real_device_id.empty())
+      return real_device_id;
+  }
+  return output_device_id;
+}
+
+std::string AudioManagerBase::GetGroupIDInput(
+    const std::string& input_device_id) {
+  const std::string& real_input_device_id =
+      input_device_id == AudioDeviceDescription::kDefaultDeviceId
+          ? GetDefaultInputDeviceID()
+          : input_device_id == AudioDeviceDescription::kCommunicationsDeviceId
+                ? GetCommunicationsInputDeviceID()
+                : input_device_id;
+  std::string output_device_id =
+      GetAssociatedOutputDeviceID(real_input_device_id);
+  if (output_device_id.empty()) {
+    // Some characters are added to avoid accidentally
+    // giving the input the same group id as an output.
+    return real_input_device_id + "input";
+  }
+  return GetGroupIDOutput(output_device_id);
+}
+
+void AudioManagerBase::CloseAllInputStreams() {
+  for (auto iter = input_streams_.begin(); iter != input_streams_.end();) {
+    // Note: Closing the stream will invalidate the iterator.
+    // Increment the iterator before closing the stream.
+    AudioInputStream* stream = *iter++;
+    stream->Close();
+  }
+  CHECK(input_streams_.empty());
+}
+
+std::string AudioManagerBase::GetDefaultInputDeviceID() {
+  return std::string();
+}
+
+std::string AudioManagerBase::GetDefaultOutputDeviceID() {
+  return std::string();
+}
+
+std::string AudioManagerBase::GetCommunicationsInputDeviceID() {
+  return std::string();
+}
+
+std::string AudioManagerBase::GetCommunicationsOutputDeviceID() {
+  return std::string();
+}
+
+// static
+int AudioManagerBase::GetUserBufferSize() {
+  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+  int buffer_size = 0;
+  std::string buffer_size_str(cmd_line->GetSwitchValueASCII(
+      switches::kAudioBufferSize));
+  if (base::StringToInt(buffer_size_str, &buffer_size) && buffer_size > 0)
+    return buffer_size;
+
+  return 0;
+}
+
+std::unique_ptr<AudioLog> AudioManagerBase::CreateAudioLog(
+    AudioLogFactory::AudioComponent component,
+    int component_id) {
+  return audio_log_factory_->CreateAudioLog(component, component_id);
+}
+
+void AudioManagerBase::InitializeDebugRecording() {
+  if (!GetTaskRunner()->BelongsToCurrentThread()) {
+    // AudioManager is deleted on the audio thread, so it's safe to post
+    // unretained.
+    GetTaskRunner()->PostTask(
+        FROM_HERE, base::BindOnce(&AudioManagerBase::InitializeDebugRecording,
+                                  base::Unretained(this)));
+    return;
+  }
+
+  DCHECK(!debug_recording_manager_);
+  debug_recording_manager_ = CreateAudioDebugRecordingManager(GetTaskRunner());
+}
+
+std::unique_ptr<AudioDebugRecordingManager>
+AudioManagerBase::CreateAudioDebugRecordingManager(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+  return std::make_unique<AudioDebugRecordingManager>(std::move(task_runner));
+}
+
+AudioDebugRecordingManager* AudioManagerBase::GetAudioDebugRecordingManager() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return debug_recording_manager_.get();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_manager_base.h b/third_party/chromium/media/audio/audio_manager_base.h
new file mode 100644
index 0000000..f1e36ca
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_manager_base.h
@@ -0,0 +1,222 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_
+#define MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_
+
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "media/audio/audio_debug_recording_manager.h"
+#include "media/audio/audio_device_name.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_output_dispatcher.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_com_initializer.h"
+#endif
+
+namespace media {
+
+class AudioOutputDispatcher;
+
+// AudioManagerBase provides AudioManager functions common for all platforms.
+class MEDIA_EXPORT AudioManagerBase : public AudioManager {
+ public:
+  enum class VoiceProcessingMode { kDisabled = 0, kEnabled = 1 };
+
+  AudioManagerBase(const AudioManagerBase&) = delete;
+  AudioManagerBase& operator=(const AudioManagerBase&) = delete;
+
+  ~AudioManagerBase() override;
+
+  AudioOutputStream* MakeAudioOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeAudioInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioOutputStream* MakeAudioOutputStreamProxy(
+      const AudioParameters& params,
+      const std::string& device_id) override;
+
+  // Listeners will be notified on the GetTaskRunner() task runner.
+  void AddOutputDeviceChangeListener(AudioDeviceListener* listener) override;
+  void RemoveOutputDeviceChangeListener(AudioDeviceListener* listener) override;
+
+  std::unique_ptr<AudioLog> CreateAudioLog(
+      AudioLogFactory::AudioComponent component,
+      int component_id) override;
+
+  // AudioManagerBase:
+
+  // Called internally by the audio stream when it has been closed.
+  virtual void ReleaseOutputStream(AudioOutputStream* stream);
+  virtual void ReleaseInputStream(AudioInputStream* stream);
+
+  // Creates the output stream for the |AUDIO_PCM_LINEAR| format. The legacy
+  // name is also from |AUDIO_PCM_LINEAR|.
+  virtual AudioOutputStream* MakeLinearOutputStream(
+      const AudioParameters& params,
+      const LogCallback& log_callback) = 0;
+
+  // Creates the output stream for the |AUDIO_PCM_LOW_LATENCY| format.
+  virtual AudioOutputStream* MakeLowLatencyOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) = 0;
+
+  // Creates the output stream for the |AUDIO_BITSTREAM_XXX| format.
+  virtual AudioOutputStream* MakeBitstreamOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback);
+
+  // Creates the input stream for the |AUDIO_PCM_LINEAR| format. The legacy
+  // name is also from |AUDIO_PCM_LINEAR|.
+  virtual AudioInputStream* MakeLinearInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) = 0;
+
+  // Creates the input stream for the |AUDIO_PCM_LOW_LATENCY| format.
+  virtual AudioInputStream* MakeLowLatencyInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) = 0;
+
+  // Get number of input or output streams.
+  int input_stream_count() const {
+    return static_cast<int>(input_streams_.size());
+  }
+  int output_stream_count() const { return num_output_streams_; }
+
+ protected:
+  AudioManagerBase(std::unique_ptr<AudioThread> audio_thread,
+                   AudioLogFactory* audio_log_factory);
+
+  // AudioManager:
+  void ShutdownOnAudioThread() override;
+
+  void GetAudioInputDeviceDescriptions(
+      AudioDeviceDescriptions* device_descriptions) final;
+  void GetAudioOutputDeviceDescriptions(
+      AudioDeviceDescriptions* device_descriptions) final;
+
+  AudioParameters GetDefaultOutputStreamParameters() override;
+  AudioParameters GetOutputStreamParameters(
+      const std::string& device_id) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+  std::string GetAssociatedOutputDeviceID(
+      const std::string& input_device_id) override;
+
+  void SetMaxOutputStreamsAllowed(int max) { max_num_output_streams_ = max; }
+
+  // Called by each platform specific AudioManager to notify output state change
+  // listeners that a state change has occurred.  Must be called from the audio
+  // thread.
+  void NotifyAllOutputDeviceChangeListeners();
+
+  // Returns user buffer size as specified on the command line or 0 if no buffer
+  // size has been specified.
+  static int GetUserBufferSize();
+
+  // Returns the preferred hardware audio output parameters for opening output
+  // streams. If the users inject a valid |input_params|, each AudioManager
+  // will decide if they should return the values from |input_params| or the
+  // default hardware values. If the |input_params| is invalid, it will return
+  // the default hardware audio parameters.
+  // If |output_device_id| is empty, the implementation must treat that as
+  // a request for the default output device.
+  virtual AudioParameters GetPreferredOutputStreamParameters(
+      const std::string& output_device_id,
+      const AudioParameters& input_params) = 0;
+
+  // Appends a list of available input devices to |device_names|,
+  // which must initially be empty.
+  virtual void GetAudioInputDeviceNames(AudioDeviceNames* device_names);
+
+  // Appends a list of available output devices to |device_names|,
+  // which must initially be empty.
+  virtual void GetAudioOutputDeviceNames(AudioDeviceNames* device_names);
+
+  std::string GetDefaultInputDeviceID() override;
+  std::string GetDefaultOutputDeviceID() override;
+  std::string GetCommunicationsInputDeviceID() override;
+  std::string GetCommunicationsOutputDeviceID() override;
+
+  virtual std::unique_ptr<AudioDebugRecordingManager>
+  CreateAudioDebugRecordingManager(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  AudioDebugRecordingManager* GetAudioDebugRecordingManager() final;
+
+  // These functions assign group ids to devices based on their device ids. The
+  // default implementation is an attempt to do this based on
+  // GetAssociatedOutputDeviceID. They may be overridden by subclasses that want
+  // a different logic for assigning group ids. Must be called on the audio
+  // worker thread (see GetTaskRunner()).
+  virtual std::string GetGroupIDOutput(const std::string& output_device_id);
+  virtual std::string GetGroupIDInput(const std::string& input_device_id);
+
+  // Closes all currently open input streams.
+  void CloseAllInputStreams();
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(AudioManagerTest, AudioDebugRecording);
+
+  struct DispatcherParams;
+  typedef std::vector<std::unique_ptr<DispatcherParams>> AudioOutputDispatchers;
+
+  class CompareByParams;
+
+  // AudioManager:
+  void InitializeDebugRecording() final;
+
+  void GetAudioDeviceDescriptions(
+      AudioDeviceDescriptions* descriptions,
+      void (AudioManagerBase::*get_device_names)(AudioDeviceNames*),
+      std::string (AudioManagerBase::*get_default_device_id)(),
+      std::string (AudioManagerBase::*get_communications_device_id)(),
+      std::string (AudioManagerBase::*get_group_id)(const std::string&));
+
+  // Max number of open output streams, modified by
+  // SetMaxOutputStreamsAllowed().
+  int max_num_output_streams_;
+
+  // Number of currently open output streams.
+  int num_output_streams_;
+
+  // Track output state change listeners.
+  base::ObserverList<AudioDeviceListener>::Unchecked output_listeners_;
+
+  // Contains currently open input streams.
+  std::unordered_set<AudioInputStream*> input_streams_;
+
+  // Map of cached AudioOutputDispatcher instances.  Must only be touched
+  // from the audio thread (no locking).
+  AudioOutputDispatchers output_dispatchers_;
+
+  // Proxy for creating AudioLog objects.
+  AudioLogFactory* const audio_log_factory_;
+
+  // Debug recording manager.
+  std::unique_ptr<AudioDebugRecordingManager> debug_recording_manager_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_MANAGER_BASE_H_
diff --git a/third_party/chromium/media/audio/audio_manager_unittest.cc b/third_party/chromium/media/audio/audio_manager_unittest.cc
new file mode 100644
index 0000000..fdf54f8
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_manager_unittest.cc
@@ -0,0 +1,1087 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_manager.h"
+
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/system/sys_info.h"
+#include "base/test/test_message_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_device_info_accessor_for_tests.h"
+#include "media/audio/audio_device_name.h"
+#include "media/audio/audio_output_proxy.h"
+#include "media/audio/audio_unittest_util.h"
+#include "media/audio/fake_audio_log_factory.h"
+#include "media/audio/fake_audio_manager.h"
+#include "media/audio/test_audio_thread.h"
+#include "media/base/limits.h"
+#include "media/base/media_switches.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(USE_ALSA)
+#include "media/audio/alsa/audio_manager_alsa.h"
+#endif  // defined(USE_ALSA)
+
+#if defined(OS_MAC)
+#include "media/audio/mac/audio_manager_mac.h"
+#include "media/base/mac/audio_latency_mac.h"
+#endif
+
+#if defined(OS_WIN)
+#include "base/win/scoped_com_initializer.h"
+#include "media/audio/win/audio_manager_win.h"
+#endif
+
+#if defined(USE_PULSEAUDIO)
+#include "media/audio/pulse/audio_manager_pulse.h"
+#include "media/audio/pulse/pulse_util.h"
+#endif  // defined(USE_PULSEAUDIO)
+
+#if defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/components/audio/audio_devices_pref_handler_stub.h"
+#include "ash/components/audio/cras_audio_handler.h"
+#include "chromeos/dbus/audio/fake_cras_audio_client.h"
+#include "media/audio/cras/audio_manager_chromeos.h"
+#elif defined(USE_CRAS) && (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#include "media/audio/cras/audio_manager_cras.h"
+#endif
+
+namespace media {
+namespace {
+
+#if defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+using ::ash::CrasAudioHandler;
+#endif
+
+template <typename T>
+struct TestAudioManagerFactory {
+  static std::unique_ptr<AudioManager> Create(
+      AudioLogFactory* audio_log_factory) {
+    return std::make_unique<T>(std::make_unique<TestAudioThread>(),
+                               audio_log_factory);
+  }
+};
+
+#if defined(USE_PULSEAUDIO)
+template <>
+struct TestAudioManagerFactory<AudioManagerPulse> {
+  static std::unique_ptr<AudioManager> Create(
+      AudioLogFactory* audio_log_factory) {
+    pa_threaded_mainloop* pa_mainloop = nullptr;
+    pa_context* pa_context = nullptr;
+    if (!pulse::InitPulse(&pa_mainloop, &pa_context))
+      return nullptr;
+    return std::make_unique<AudioManagerPulse>(
+        std::make_unique<TestAudioThread>(), audio_log_factory, pa_mainloop,
+        pa_context);
+  }
+};
+#endif  // defined(USE_PULSEAUDIO)
+
+template <>
+struct TestAudioManagerFactory<std::nullptr_t> {
+  static std::unique_ptr<AudioManager> Create(
+      AudioLogFactory* audio_log_factory) {
+    return AudioManager::CreateForTesting(std::make_unique<TestAudioThread>());
+  }
+};
+
+#if defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+using chromeos::AudioNode;
+using chromeos::AudioNodeList;
+
+const int kDefaultSampleRate = 48000;
+
+const uint64_t kInternalSpeakerId = 10001;
+const uint64_t kInternalSpeakerStableDeviceId = 10001;
+const uint64_t kInternalMicId = 10002;
+const uint64_t kInternalMicStableDeviceId = 10002;
+const uint64_t kJabraSpeaker1Id = 30001;
+const uint64_t kJabraSpeaker1StableDeviceId = 80001;
+const uint64_t kJabraSpeaker2Id = 30002;
+const uint64_t kJabraSpeaker2StableDeviceId = 80002;
+const uint64_t kHDMIOutputId = 30003;
+const uint64_t kHDMIOutputStabeDevicelId = 80003;
+const uint64_t kJabraMic1Id = 40001;
+const uint64_t kJabraMic1StableDeviceId = 90001;
+const uint64_t kJabraMic2Id = 40002;
+const uint64_t kJabraMic2StableDeviceId = 90002;
+const uint64_t kWebcamMicId = 40003;
+const uint64_t kWebcamMicStableDeviceId = 90003;
+
+const AudioNode kInternalSpeaker(false,
+                                 kInternalSpeakerId,
+                                 true,
+                                 kInternalSpeakerStableDeviceId,
+                                 kInternalSpeakerStableDeviceId ^ 0xFF,
+                                 "Internal Speaker",
+                                 "INTERNAL_SPEAKER",
+                                 "Speaker",
+                                 false,
+                                 0,
+                                 2,
+                                 0);
+
+const AudioNode kInternalMic(true,
+                             kInternalMicId,
+                             true,
+                             kInternalMicStableDeviceId,
+                             kInternalMicStableDeviceId ^ 0xFF,
+                             "Internal Mic",
+                             "INTERNAL_MIC",
+                             "Internal Mic",
+                             false,
+                             0,
+                             1,
+                             1);  // EFFECT_TYPE_NOISE_CANCELLATION
+
+const AudioNode kJabraSpeaker1(false,
+                               kJabraSpeaker1Id,
+                               true,
+                               kJabraSpeaker1StableDeviceId,
+                               kJabraSpeaker1StableDeviceId ^ 0xFF,
+                               "Jabra Speaker",
+                               "USB",
+                               "Jabra Speaker 1",
+                               false,
+                               0,
+                               2,  // expects CHANNEL_LAYOUT_STEREO
+                               0);
+
+const AudioNode kJabraSpeaker2(false,
+                               kJabraSpeaker2Id,
+                               true,
+                               kJabraSpeaker2StableDeviceId,
+                               kJabraSpeaker2StableDeviceId ^ 0xFF,
+                               "Jabra Speaker",
+                               "USB",
+                               "Jabra Speaker 2",
+                               false,
+                               0,
+                               6,  // expects CHANNEL_LAYOUT_5_1
+                               0);
+
+const AudioNode kHDMIOutput(false,
+                            kHDMIOutputId,
+                            true,
+                            kHDMIOutputStabeDevicelId,
+                            kHDMIOutputStabeDevicelId ^ 0xFF,
+                            "HDMI output",
+                            "HDMI",
+                            "HDA Intel MID",
+                            false,
+                            0,
+                            8,  // expects CHANNEL_LAYOUT_7_1
+                            0);
+
+const AudioNode kJabraMic1(true,
+                           kJabraMic1Id,
+                           true,
+                           kJabraMic1StableDeviceId,
+                           kJabraMic1StableDeviceId ^ 0xFF,
+                           "Jabra Mic",
+                           "USB",
+                           "Jabra Mic 1",
+                           false,
+                           0,
+                           1,
+                           0);
+
+const AudioNode kJabraMic2(true,
+                           kJabraMic2Id,
+                           true,
+                           kJabraMic2StableDeviceId,
+                           kJabraMic2StableDeviceId ^ 0xFF,
+                           "Jabra Mic",
+                           "USB",
+                           "Jabra Mic 2",
+                           false,
+                           0,
+                           1,
+                           0);
+
+const AudioNode kUSBCameraMic(true,
+                              kWebcamMicId,
+                              true,
+                              kWebcamMicStableDeviceId,
+                              kWebcamMicStableDeviceId ^ 0xFF,
+                              "Webcam Mic",
+                              "USB",
+                              "Logitech Webcam",
+                              false,
+                              0,
+                              1,
+                              0);
+#endif  // defined(USE_CRAS)
+
+const char kRealDefaultInputDeviceID[] = "input2";
+const char kRealDefaultOutputDeviceID[] = "output3";
+const char kRealCommunicationsInputDeviceID[] = "input1";
+const char kRealCommunicationsOutputDeviceID[] = "output1";
+
+void CheckDescriptionLabels(const AudioDeviceDescriptions& descriptions,
+                            const std::string& real_default_id,
+                            const std::string& real_communications_id) {
+  std::string real_default_label;
+  std::string real_communications_label;
+  for (const auto& description : descriptions) {
+    if (description.unique_id == real_default_id)
+      real_default_label = description.device_name;
+    else if (description.unique_id == real_communications_id)
+      real_communications_label = description.device_name;
+  }
+
+  for (const auto& description : descriptions) {
+    if (AudioDeviceDescription::IsDefaultDevice(description.unique_id)) {
+      EXPECT_TRUE(base::EndsWith(description.device_name, real_default_label,
+                                 base::CompareCase::SENSITIVE));
+    } else if (description.unique_id ==
+               AudioDeviceDescription::kCommunicationsDeviceId) {
+      EXPECT_TRUE(base::EndsWith(description.device_name,
+                                 real_communications_label,
+                                 base::CompareCase::SENSITIVE));
+    }
+  }
+}
+
+}  // namespace
+
+// Test fixture which allows us to override the default enumeration API on
+// Windows.
+class AudioManagerTest : public ::testing::Test {
+ public:
+  void HandleDefaultDeviceIDsTest() {
+    AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                           CHANNEL_LAYOUT_STEREO, 48000, 2048);
+
+    // Create a stream with the default device id "".
+    AudioOutputStream* stream =
+        audio_manager_->MakeAudioOutputStreamProxy(params, "");
+    ASSERT_TRUE(stream);
+    AudioOutputDispatcher* dispatcher1 =
+        reinterpret_cast<AudioOutputProxy*>(stream)
+            ->get_dispatcher_for_testing();
+
+    // Closing this stream will put it up for reuse.
+    stream->Close();
+    stream = audio_manager_->MakeAudioOutputStreamProxy(
+        params, AudioDeviceDescription::kDefaultDeviceId);
+
+    // Verify both streams are created with the same dispatcher (which is unique
+    // per device).
+    ASSERT_EQ(dispatcher1, reinterpret_cast<AudioOutputProxy*>(stream)
+                               ->get_dispatcher_for_testing());
+    stream->Close();
+
+    // Create a non-default device and ensure it gets a different dispatcher.
+    stream = audio_manager_->MakeAudioOutputStreamProxy(params, "123456");
+    ASSERT_NE(dispatcher1, reinterpret_cast<AudioOutputProxy*>(stream)
+                               ->get_dispatcher_for_testing());
+    stream->Close();
+  }
+
+  void GetDefaultOutputStreamParameters(media::AudioParameters* params) {
+    *params = device_info_accessor_->GetDefaultOutputStreamParameters();
+  }
+
+  void GetAssociatedOutputDeviceID(const std::string& input_device_id,
+                                   std::string* output_device_id) {
+    *output_device_id =
+        device_info_accessor_->GetAssociatedOutputDeviceID(input_device_id);
+  }
+
+#if defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+  void TearDown() override {
+    CrasAudioHandler::Shutdown();
+    audio_pref_handler_ = nullptr;
+    chromeos::CrasAudioClient::Shutdown();
+  }
+
+  void SetUpCrasAudioHandlerWithTestingNodes(const AudioNodeList& audio_nodes) {
+    chromeos::CrasAudioClient::InitializeFake();
+    chromeos::FakeCrasAudioClient::Get()->SetAudioNodesForTesting(audio_nodes);
+    audio_pref_handler_ = new ash::AudioDevicesPrefHandlerStub();
+    CrasAudioHandler::Initialize(
+        /*media_controller_manager*/ mojo::NullRemote(), audio_pref_handler_);
+    cras_audio_handler_ = CrasAudioHandler::Get();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetActiveOutputNode(uint64_t node_id) {
+    cras_audio_handler_->SwitchToDevice(
+        *cras_audio_handler_->GetDeviceFromId(node_id), true /* notify */,
+        CrasAudioHandler::ACTIVATE_BY_USER /* activate_by */);
+  }
+
+  AudioParameters GetPreferredOutputStreamParameters(
+      ChannelLayout channel_layout, int32_t user_buffer_size = 0) {
+    // Generated AudioParameters should follow the same rule as in
+    // AudioManagerCras::GetPreferredOutputStreamParameters().
+    int sample_rate = kDefaultSampleRate;
+    int32_t buffer_size = user_buffer_size;
+    if (buffer_size == 0)  // Not user-provided.
+      cras_audio_handler_->GetDefaultOutputBufferSize(&buffer_size);
+    return AudioParameters(
+        AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, sample_rate,
+        buffer_size,
+        AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize,
+                                              limits::kMaxAudioBufferSize));
+  }
+#endif  // defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+
+ protected:
+  AudioManagerTest() {
+    CreateAudioManagerForTesting();
+  }
+  ~AudioManagerTest() override { audio_manager_->Shutdown(); }
+
+  // Helper method which verifies that the device list starts with a valid
+  // default record followed by non-default device names.
+  static void CheckDeviceDescriptions(
+      const AudioDeviceDescriptions& device_descriptions) {
+    DVLOG(2) << "Got " << device_descriptions.size() << " audio devices.";
+    if (!device_descriptions.empty()) {
+      auto it = device_descriptions.begin();
+
+      // The first device in the list should always be the default device.
+      EXPECT_EQ(std::string(AudioDeviceDescription::kDefaultDeviceId),
+                it->unique_id);
+      ++it;
+
+      // Other devices should have non-empty name and id and should not contain
+      // default name or id.
+      while (it != device_descriptions.end()) {
+        EXPECT_FALSE(it->device_name.empty());
+        EXPECT_FALSE(it->unique_id.empty());
+        EXPECT_FALSE(it->group_id.empty());
+        DVLOG(2) << "Device ID(" << it->unique_id
+                 << "), label: " << it->device_name
+                 << "group: " << it->group_id;
+        EXPECT_NE(AudioDeviceDescription::GetDefaultDeviceName(),
+                  it->device_name);
+        EXPECT_NE(std::string(AudioDeviceDescription::kDefaultDeviceId),
+                  it->unique_id);
+        ++it;
+      }
+    } else {
+      // Log a warning so we can see the status on the build bots.  No need to
+      // break the test though since this does successfully test the code and
+      // some failure cases.
+      LOG(WARNING) << "No input devices detected";
+    }
+  }
+
+#if defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+  // Helper method for (USE_CRAS) which verifies that the device list starts
+  // with a valid default record followed by physical device names.
+  static void CheckDeviceDescriptionsCras(
+      const AudioDeviceDescriptions& device_descriptions,
+      const std::map<uint64_t, std::string>& expectation) {
+    DVLOG(2) << "Got " << device_descriptions.size() << " audio devices.";
+    if (!device_descriptions.empty()) {
+      AudioDeviceDescriptions::const_iterator it = device_descriptions.begin();
+
+      // The first device in the list should always be the default device.
+      EXPECT_EQ(AudioDeviceDescription::GetDefaultDeviceName(),
+                it->device_name);
+      EXPECT_EQ(std::string(AudioDeviceDescription::kDefaultDeviceId),
+                it->unique_id);
+
+      // |device_descriptions|'size should be |expectation|'s size plus one
+      // because of
+      // default device.
+      EXPECT_EQ(device_descriptions.size(), expectation.size() + 1);
+      ++it;
+      // Check other devices that should have non-empty name and id, and should
+      // be contained in expectation.
+      while (it != device_descriptions.end()) {
+        EXPECT_FALSE(it->device_name.empty());
+        EXPECT_FALSE(it->unique_id.empty());
+        EXPECT_FALSE(it->group_id.empty());
+        DVLOG(2) << "Device ID(" << it->unique_id
+                 << "), label: " << it->device_name
+                 << "group: " << it->group_id;
+        uint64_t key;
+        EXPECT_TRUE(base::StringToUint64(it->unique_id, &key));
+        EXPECT_TRUE(expectation.find(key) != expectation.end());
+        EXPECT_EQ(expectation.find(key)->second, it->device_name);
+        ++it;
+      }
+    } else {
+      // Log a warning so we can see the status on the build bots. No need to
+      // break the test though since this does successfully test the code and
+      // some failure cases.
+      LOG(WARNING) << "No input devices detected";
+    }
+  }
+
+  // Helper method for (USE_CRAS) which returns |group_id| from |device_id|.
+  std::string getGroupID(const AudioDeviceDescriptions& device_descriptions,
+                         const std::string device_id) {
+    AudioDeviceDescriptions::const_iterator it =
+        std::find_if(device_descriptions.begin(), device_descriptions.end(),
+                     [&device_id](const auto& audio_device_desc) {
+                       return audio_device_desc.unique_id == device_id;
+                     });
+
+    EXPECT_NE(it, device_descriptions.end());
+    return it->group_id;
+  }
+#endif  // defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+
+  bool InputDevicesAvailable() {
+#if defined(OS_MAC) && defined(ARCH_CPU_ARM64)
+    // TODO(crbug.com/1128458): macOS on ARM64 says it has devices, but won't
+    // let any of them be opened or listed.
+    return false;
+#else
+    return device_info_accessor_->HasAudioInputDevices();
+#endif
+  }
+  bool OutputDevicesAvailable() {
+    return device_info_accessor_->HasAudioOutputDevices();
+  }
+
+  template <typename T = std::nullptr_t>
+  void CreateAudioManagerForTesting() {
+    // Only one AudioManager may exist at a time, so destroy the one we're
+    // currently holding before creating a new one.
+    // Flush the message loop to run any shutdown tasks posted by AudioManager.
+    if (audio_manager_) {
+      audio_manager_->Shutdown();
+      audio_manager_.reset();
+    }
+
+    audio_manager_ =
+        TestAudioManagerFactory<T>::Create(&fake_audio_log_factory_);
+    // A few AudioManager implementations post initialization tasks to
+    // audio thread. Flush the thread to ensure that |audio_manager_| is
+    // initialized and ready to use before returning from this function.
+    // TODO(alokp): We should perhaps do this in AudioManager::Create().
+    base::RunLoop().RunUntilIdle();
+    device_info_accessor_ =
+        std::make_unique<AudioDeviceInfoAccessorForTests>(audio_manager_.get());
+  }
+
+  base::TestMessageLoop message_loop_;
+  FakeAudioLogFactory fake_audio_log_factory_;
+  std::unique_ptr<AudioManager> audio_manager_;
+  std::unique_ptr<AudioDeviceInfoAccessorForTests> device_info_accessor_;
+
+#if defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+  CrasAudioHandler* cras_audio_handler_ = nullptr;  // Not owned.
+  scoped_refptr<ash::AudioDevicesPrefHandlerStub> audio_pref_handler_;
+#endif  // defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+};
+
+#if defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+TEST_F(AudioManagerTest, EnumerateInputDevicesCras) {
+  // Setup the devices without internal mic, so that it doesn't exist
+  // beamforming capable mic.
+  AudioNodeList audio_nodes;
+  audio_nodes.push_back(kJabraMic1);
+  audio_nodes.push_back(kJabraMic2);
+  audio_nodes.push_back(kUSBCameraMic);
+  audio_nodes.push_back(kHDMIOutput);
+  audio_nodes.push_back(kJabraSpeaker1);
+  SetUpCrasAudioHandlerWithTestingNodes(audio_nodes);
+
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+
+  // Setup expectation with physical devices.
+  std::map<uint64_t, std::string> expectation;
+  expectation[kJabraMic1.id] =
+      cras_audio_handler_->GetDeviceFromId(kJabraMic1.id)->display_name;
+  expectation[kJabraMic2.id] =
+      cras_audio_handler_->GetDeviceFromId(kJabraMic2.id)->display_name;
+  expectation[kUSBCameraMic.id] =
+      cras_audio_handler_->GetDeviceFromId(kUSBCameraMic.id)->display_name;
+
+  DVLOG(2) << "Testing AudioManagerCras.";
+  CreateAudioManagerForTesting<AudioManagerChromeOS>();
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioInputDeviceDescriptions(&device_descriptions);
+  CheckDeviceDescriptionsCras(device_descriptions, expectation);
+}
+
+TEST_F(AudioManagerTest, EnumerateOutputDevicesCras) {
+  // Setup the devices without internal mic, so that it doesn't exist
+  // beamforming capable mic.
+  AudioNodeList audio_nodes;
+  audio_nodes.push_back(kJabraMic1);
+  audio_nodes.push_back(kJabraMic2);
+  audio_nodes.push_back(kUSBCameraMic);
+  audio_nodes.push_back(kHDMIOutput);
+  audio_nodes.push_back(kJabraSpeaker1);
+  SetUpCrasAudioHandlerWithTestingNodes(audio_nodes);
+
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+  // Setup expectation with physical devices.
+  std::map<uint64_t, std::string> expectation;
+  expectation[kHDMIOutput.id] =
+      cras_audio_handler_->GetDeviceFromId(kHDMIOutput.id)->display_name;
+  expectation[kJabraSpeaker1.id] =
+      cras_audio_handler_->GetDeviceFromId(kJabraSpeaker1.id)->display_name;
+
+  DVLOG(2) << "Testing AudioManagerCras.";
+  CreateAudioManagerForTesting<AudioManagerChromeOS>();
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioOutputDeviceDescriptions(&device_descriptions);
+  CheckDeviceDescriptionsCras(device_descriptions, expectation);
+}
+
+TEST_F(AudioManagerTest, CheckOutputStreamParametersCras) {
+  // Setup the devices without internal mic, so that it doesn't exist
+  // beamforming capable mic.
+  AudioNodeList audio_nodes;
+  audio_nodes.push_back(kJabraMic1);
+  audio_nodes.push_back(kJabraMic2);
+  audio_nodes.push_back(kUSBCameraMic);
+  audio_nodes.push_back(kHDMIOutput);
+  audio_nodes.push_back(kJabraSpeaker1);
+  audio_nodes.push_back(kJabraSpeaker2);
+
+  SetUpCrasAudioHandlerWithTestingNodes(audio_nodes);
+
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+  DVLOG(2) << "Testing AudioManagerCras.";
+  CreateAudioManagerForTesting<AudioManagerChromeOS>();
+  AudioParameters params, golden_params;
+
+  // channel_layout:
+  //   JabraSpeaker1 (2-channel): CHANNEL_LAYOUT_STEREO
+  //   JabraSpeaker2 (6-channel): CHANNEL_LAYOUT_5_1
+  //   HDMIOutput (8-channel): CHANNEL_LAYOUT_7_1
+
+  // Check GetOutputStreamParameters() with device ID. The returned parameters
+  // should be reflected to the specific output device.
+  params = device_info_accessor_->GetOutputStreamParameters(
+      base::NumberToString(kJabraSpeaker1Id));
+  golden_params = GetPreferredOutputStreamParameters(
+      ChannelLayout::CHANNEL_LAYOUT_STEREO);
+  EXPECT_TRUE(params.Equals(golden_params));
+  params = device_info_accessor_->GetOutputStreamParameters(
+      base::NumberToString(kJabraSpeaker2Id));
+  golden_params = GetPreferredOutputStreamParameters(
+      ChannelLayout::CHANNEL_LAYOUT_5_1);
+  EXPECT_TRUE(params.Equals(golden_params));
+  params = device_info_accessor_->GetOutputStreamParameters(
+      base::NumberToString(kHDMIOutputId));
+  golden_params = GetPreferredOutputStreamParameters(
+      ChannelLayout::CHANNEL_LAYOUT_7_1);
+  EXPECT_TRUE(params.Equals(golden_params));
+
+  // Set user-provided audio buffer size by command line, then check the buffer
+  // size in stream parameters is equal to the user-provided one.
+  int argc = 2;
+  char const *argv0 = "dummy";
+  char const *argv1 = "--audio-buffer-size=2048";
+  const char* argv[] = {argv0, argv1, 0};
+  base::CommandLine::Reset();
+  EXPECT_TRUE(base::CommandLine::Init(argc, argv));
+
+  // Check GetOutputStreamParameters() with default ID. The returned parameters
+  // should reflect the currently active output device.
+  SetActiveOutputNode(kJabraSpeaker1Id);
+  params = device_info_accessor_->GetOutputStreamParameters(
+      AudioDeviceDescription::kDefaultDeviceId);
+  golden_params = GetPreferredOutputStreamParameters(
+      ChannelLayout::CHANNEL_LAYOUT_STEREO, 2048);
+  EXPECT_TRUE(params.Equals(golden_params));
+  SetActiveOutputNode(kJabraSpeaker2Id);
+  params = device_info_accessor_->GetOutputStreamParameters(
+      AudioDeviceDescription::kDefaultDeviceId);
+  golden_params = GetPreferredOutputStreamParameters(
+      ChannelLayout::CHANNEL_LAYOUT_5_1, 2048);
+  EXPECT_TRUE(params.Equals(golden_params));
+  SetActiveOutputNode(kHDMIOutputId);
+  params = device_info_accessor_->GetOutputStreamParameters(
+      AudioDeviceDescription::kDefaultDeviceId);
+  golden_params = GetPreferredOutputStreamParameters(
+      ChannelLayout::CHANNEL_LAYOUT_7_1, 2048);
+  EXPECT_TRUE(params.Equals(golden_params));
+
+  // Check non-default device again.
+  params = device_info_accessor_->GetOutputStreamParameters(
+      base::NumberToString(kJabraSpeaker1Id));
+  golden_params = GetPreferredOutputStreamParameters(
+      ChannelLayout::CHANNEL_LAYOUT_STEREO, 2048);
+  EXPECT_TRUE(params.Equals(golden_params));
+}
+
+TEST_F(AudioManagerTest, LookupDefaultInputDeviceWithProperGroupId) {
+  // Setup devices with external microphone as active device.
+  // Switch active device to the internal microphone.
+  // Check if default device has the same group id as internal microphone.
+  AudioNodeList audio_nodes;
+  audio_nodes.push_back(kInternalMic);
+  audio_nodes.push_back(kJabraMic1);
+  SetUpCrasAudioHandlerWithTestingNodes(audio_nodes);
+
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+
+  // Setup expectation with physical devices.
+  std::map<uint64_t, std::string> expectation;
+  expectation[kInternalMic.id] =
+      cras_audio_handler_->GetDeviceFromId(kInternalMic.id)->display_name;
+  expectation[kJabraMic1.id] =
+      cras_audio_handler_->GetDeviceFromId(kJabraMic1.id)->display_name;
+
+  CreateAudioManagerForTesting<AudioManagerChromeOS>();
+  auto previous_default_device_id =
+      device_info_accessor_->GetDefaultInputDeviceID();
+  EXPECT_EQ(base::NumberToString(kJabraMic1.id), previous_default_device_id);
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioInputDeviceDescriptions(&device_descriptions);
+
+  CheckDeviceDescriptions(device_descriptions);
+
+  // Set internal microphone as active.
+  ash::AudioDevice internal_microphone(kInternalMic);
+  cras_audio_handler_->SwitchToDevice(internal_microphone, true,
+                                      CrasAudioHandler::ACTIVATE_BY_USER);
+  auto new_default_device_id = device_info_accessor_->GetDefaultInputDeviceID();
+  EXPECT_NE(previous_default_device_id, new_default_device_id);
+
+  auto default_device_group_id =
+      getGroupID(device_descriptions, new_default_device_id);
+  auto mic_group_id =
+      getGroupID(device_descriptions, base::NumberToString(kInternalMic.id));
+
+  EXPECT_EQ(default_device_group_id, mic_group_id);
+  EXPECT_EQ(base::NumberToString(kInternalMic.id), new_default_device_id);
+}
+
+TEST_F(AudioManagerTest, LookupDefaultOutputDeviceWithProperGroupId) {
+  // Setup devices with external speaker as active device.
+  // Switch active device to the internal speaker.
+  // Check if default device has the same group id as internal speaker.
+  AudioNodeList audio_nodes;
+  audio_nodes.push_back(kInternalSpeaker);
+  audio_nodes.push_back(kJabraSpeaker1);
+
+  SetUpCrasAudioHandlerWithTestingNodes(audio_nodes);
+
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+  // Setup expectation with physical devices.
+  std::map<uint64_t, std::string> expectation;
+  expectation[kInternalSpeaker.id] =
+      cras_audio_handler_->GetDeviceFromId(kInternalSpeaker.id)->display_name;
+  expectation[kJabraSpeaker1.id] =
+      cras_audio_handler_->GetDeviceFromId(kJabraSpeaker1.id)->display_name;
+
+  CreateAudioManagerForTesting<AudioManagerChromeOS>();
+  auto previous_default_device_id =
+      device_info_accessor_->GetDefaultOutputDeviceID();
+  EXPECT_EQ(base::NumberToString(kJabraSpeaker1.id),
+            previous_default_device_id);
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioOutputDeviceDescriptions(&device_descriptions);
+
+  CheckDeviceDescriptions(device_descriptions);
+
+  // Set internal speaker as active.
+  ash::AudioDevice internal_speaker(kInternalSpeaker);
+  cras_audio_handler_->SwitchToDevice(internal_speaker, true,
+                                      CrasAudioHandler::ACTIVATE_BY_USER);
+  auto new_default_device_id =
+      device_info_accessor_->GetDefaultOutputDeviceID();
+  EXPECT_NE(previous_default_device_id, new_default_device_id);
+
+  auto default_device_group_id =
+      getGroupID(device_descriptions, new_default_device_id);
+  auto speaker_group_id = getGroupID(device_descriptions,
+                                     base::NumberToString(kInternalSpeaker.id));
+
+  EXPECT_EQ(default_device_group_id, speaker_group_id);
+  EXPECT_EQ(base::NumberToString(kInternalSpeaker.id), new_default_device_id);
+}
+#else  // !(defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH))
+
+TEST_F(AudioManagerTest, HandleDefaultDeviceIDs) {
+  // Use a fake manager so we can makeup device ids, this will still use the
+  // AudioManagerBase code.
+  CreateAudioManagerForTesting<FakeAudioManager>();
+  HandleDefaultDeviceIDsTest();
+  base::RunLoop().RunUntilIdle();
+}
+
+// Test that devices can be enumerated.
+TEST_F(AudioManagerTest, EnumerateInputDevices) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioInputDeviceDescriptions(&device_descriptions);
+  CheckDeviceDescriptions(device_descriptions);
+}
+
+// Test that devices can be enumerated.
+TEST_F(AudioManagerTest, EnumerateOutputDevices) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioOutputDeviceDescriptions(&device_descriptions);
+  CheckDeviceDescriptions(device_descriptions);
+}
+
+// Run additional tests for Windows since enumeration can be done using
+// two different APIs. MMDevice is default for Vista and higher and Wave
+// is default for XP and lower.
+#if defined(OS_WIN)
+
+// Override default enumeration API and force usage of Windows MMDevice.
+// This test will only run on Windows Vista and higher.
+TEST_F(AudioManagerTest, EnumerateInputDevicesWinMMDevice) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioInputDeviceDescriptions(&device_descriptions);
+  CheckDeviceDescriptions(device_descriptions);
+}
+
+TEST_F(AudioManagerTest, EnumerateOutputDevicesWinMMDevice) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioOutputDeviceDescriptions(&device_descriptions);
+  CheckDeviceDescriptions(device_descriptions);
+}
+#endif  // defined(OS_WIN)
+
+#if defined(USE_PULSEAUDIO)
+// On Linux, there are two implementations available and both can
+// sometimes be tested on a single system. These tests specifically
+// test Pulseaudio.
+
+TEST_F(AudioManagerTest, EnumerateInputDevicesPulseaudio) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+
+  CreateAudioManagerForTesting<AudioManagerPulse>();
+  if (audio_manager_.get()) {
+    AudioDeviceDescriptions device_descriptions;
+    device_info_accessor_->GetAudioInputDeviceDescriptions(
+        &device_descriptions);
+    CheckDeviceDescriptions(device_descriptions);
+  } else {
+    LOG(WARNING) << "No pulseaudio on this system.";
+  }
+}
+
+TEST_F(AudioManagerTest, EnumerateOutputDevicesPulseaudio) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+  CreateAudioManagerForTesting<AudioManagerPulse>();
+  if (audio_manager_.get()) {
+    AudioDeviceDescriptions device_descriptions;
+    device_info_accessor_->GetAudioOutputDeviceDescriptions(
+        &device_descriptions);
+    CheckDeviceDescriptions(device_descriptions);
+  } else {
+    LOG(WARNING) << "No pulseaudio on this system.";
+  }
+}
+#endif  // defined(USE_PULSEAUDIO)
+
+#if defined(USE_ALSA)
+// On Linux, there are two implementations available and both can
+// sometimes be tested on a single system. These tests specifically
+// test Alsa.
+
+TEST_F(AudioManagerTest, EnumerateInputDevicesAlsa) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+
+  DVLOG(2) << "Testing AudioManagerAlsa.";
+  CreateAudioManagerForTesting<AudioManagerAlsa>();
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioInputDeviceDescriptions(&device_descriptions);
+  CheckDeviceDescriptions(device_descriptions);
+}
+
+TEST_F(AudioManagerTest, EnumerateOutputDevicesAlsa) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+  DVLOG(2) << "Testing AudioManagerAlsa.";
+  CreateAudioManagerForTesting<AudioManagerAlsa>();
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioOutputDeviceDescriptions(&device_descriptions);
+  CheckDeviceDescriptions(device_descriptions);
+}
+#endif  // defined(USE_ALSA)
+
+TEST_F(AudioManagerTest, GetDefaultOutputStreamParameters) {
+#if defined(OS_WIN) || defined(OS_MAC)
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+
+  AudioParameters params;
+  GetDefaultOutputStreamParameters(&params);
+  EXPECT_TRUE(params.IsValid());
+#endif  // defined(OS_WIN) || defined(OS_MAC)
+}
+
+TEST_F(AudioManagerTest, GetAssociatedOutputDeviceID) {
+#if defined(OS_WIN) || defined(OS_MAC)
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable() && OutputDevicesAvailable());
+
+  AudioDeviceDescriptions device_descriptions;
+  device_info_accessor_->GetAudioInputDeviceDescriptions(&device_descriptions);
+  bool found_an_associated_device = false;
+  for (const auto& description : device_descriptions) {
+    EXPECT_FALSE(description.unique_id.empty());
+    EXPECT_FALSE(description.device_name.empty());
+    EXPECT_FALSE(description.group_id.empty());
+    std::string output_device_id;
+    GetAssociatedOutputDeviceID(description.unique_id, &output_device_id);
+    if (!output_device_id.empty()) {
+      DVLOG(2) << description.unique_id << " matches with " << output_device_id;
+      found_an_associated_device = true;
+    }
+  }
+
+  EXPECT_TRUE(found_an_associated_device);
+#endif  // defined(OS_WIN) || defined(OS_MAC)
+}
+#endif  // defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+
+class TestAudioManager : public FakeAudioManager {
+  // For testing the default implementation of GetGroupId(Input|Output)
+  // input$i is associated to output$i, if both exist.
+  // Default input is input1.
+  // Default output is output2.
+ public:
+  TestAudioManager(std::unique_ptr<AudioThread> audio_thread,
+                   AudioLogFactory* audio_log_factory)
+      : FakeAudioManager(std::move(audio_thread), audio_log_factory) {}
+
+  std::string GetDefaultInputDeviceID() override {
+    return kRealDefaultInputDeviceID;
+  }
+  std::string GetDefaultOutputDeviceID() override {
+    return kRealDefaultOutputDeviceID;
+  }
+  std::string GetCommunicationsInputDeviceID() override {
+    return kRealCommunicationsInputDeviceID;
+  }
+  std::string GetCommunicationsOutputDeviceID() override {
+    return kRealCommunicationsOutputDeviceID;
+  }
+
+  std::string GetAssociatedOutputDeviceID(
+      const std::string& input_id) override {
+    if (input_id == "input1")
+      return "output1";
+    DCHECK_EQ(std::string(kRealDefaultInputDeviceID), "input2");
+    if (input_id == AudioDeviceDescription::kDefaultDeviceId ||
+        input_id == kRealDefaultInputDeviceID)
+      return "output2";
+    return std::string();
+  }
+
+ private:
+  void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override {
+    DCHECK(device_names->empty());
+    device_names->emplace_back(AudioDeviceName::CreateDefault());
+    device_names->emplace_back("Input 1", "input1");
+    device_names->emplace_back("Input 2", "input2");
+    device_names->emplace_back("Input 3", "input3");
+  }
+
+  void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override {
+    DCHECK(device_names->empty());
+    device_names->emplace_back(AudioDeviceName::CreateDefault());
+    device_names->emplace_back("Output 1", "output1");
+    device_names->emplace_back("Output 2", "output2");
+    device_names->emplace_back("Output 3", "output3");
+  }
+};
+
+TEST_F(AudioManagerTest, GroupId) {
+  CreateAudioManagerForTesting<TestAudioManager>();
+  // Groups:
+  // input1, output1
+  // input2, output2, default input
+  // input3
+  // output3, default output
+  AudioDeviceDescriptions inputs;
+  device_info_accessor_->GetAudioInputDeviceDescriptions(&inputs);
+  AudioDeviceDescriptions outputs;
+  device_info_accessor_->GetAudioOutputDeviceDescriptions(&outputs);
+  // default input
+  EXPECT_EQ(inputs[0].group_id, outputs[2].group_id);
+  // default input and default output are not associated
+  EXPECT_NE(inputs[0].group_id, outputs[0].group_id);
+
+  // default output
+  EXPECT_EQ(outputs[0].group_id, outputs[3].group_id);
+
+  // real inputs and outputs that are associated
+  EXPECT_EQ(inputs[1].group_id, outputs[1].group_id);
+  EXPECT_EQ(inputs[2].group_id, outputs[2].group_id);
+
+  // real inputs and outputs that are not associated
+  EXPECT_NE(inputs[3].group_id, outputs[3].group_id);
+
+  // group IDs of different devices should differ.
+  EXPECT_NE(inputs[1].group_id, inputs[2].group_id);
+  EXPECT_NE(inputs[1].group_id, inputs[3].group_id);
+  EXPECT_NE(inputs[2].group_id, inputs[3].group_id);
+  EXPECT_NE(outputs[1].group_id, outputs[2].group_id);
+  EXPECT_NE(outputs[1].group_id, outputs[3].group_id);
+  EXPECT_NE(outputs[2].group_id, outputs[3].group_id);
+}
+
+TEST_F(AudioManagerTest, DefaultCommunicationsLabelsContainRealLabels) {
+  CreateAudioManagerForTesting<TestAudioManager>();
+  std::string default_input_id =
+      device_info_accessor_->GetDefaultInputDeviceID();
+  EXPECT_EQ(default_input_id, kRealDefaultInputDeviceID);
+  std::string default_output_id =
+      device_info_accessor_->GetDefaultOutputDeviceID();
+  EXPECT_EQ(default_output_id, kRealDefaultOutputDeviceID);
+  std::string communications_input_id =
+      device_info_accessor_->GetCommunicationsInputDeviceID();
+  EXPECT_EQ(communications_input_id, kRealCommunicationsInputDeviceID);
+  std::string communications_output_id =
+      device_info_accessor_->GetCommunicationsOutputDeviceID();
+  EXPECT_EQ(communications_output_id, kRealCommunicationsOutputDeviceID);
+  AudioDeviceDescriptions inputs;
+  device_info_accessor_->GetAudioInputDeviceDescriptions(&inputs);
+  CheckDescriptionLabels(inputs, default_input_id, communications_input_id);
+
+  AudioDeviceDescriptions outputs;
+  device_info_accessor_->GetAudioOutputDeviceDescriptions(&outputs);
+  CheckDescriptionLabels(outputs, default_output_id, communications_output_id);
+}
+
+// GetPreferredOutputStreamParameters() can make changes to its input_params,
+// ensure that creating a stream with the default parameters always works.
+TEST_F(AudioManagerTest, CheckMakeOutputStreamWithPreferredParameters) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+  AudioParameters params;
+  GetDefaultOutputStreamParameters(&params);
+  ASSERT_TRUE(params.IsValid());
+
+  AudioOutputStream* stream =
+      audio_manager_->MakeAudioOutputStreamProxy(params, "");
+  ASSERT_TRUE(stream);
+
+  stream->Close();
+}
+
+#if defined(OS_MAC) || defined(USE_CRAS)
+class TestAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
+ public:
+  TestAudioSourceCallback(int expected_frames_per_buffer,
+                          base::WaitableEvent* event)
+      : expected_frames_per_buffer_(expected_frames_per_buffer),
+        event_(event) {}
+
+  TestAudioSourceCallback(const TestAudioSourceCallback&) = delete;
+  TestAudioSourceCallback& operator=(const TestAudioSourceCallback&) = delete;
+
+  ~TestAudioSourceCallback() override {}
+
+  int OnMoreData(base::TimeDelta,
+                 base::TimeTicks,
+                 int,
+                 AudioBus* dest) override {
+    EXPECT_EQ(dest->frames(), expected_frames_per_buffer_);
+    event_->Signal();
+    return 0;
+  }
+
+  void OnError(ErrorType type) override { FAIL(); }
+
+ private:
+  const int expected_frames_per_buffer_;
+  base::WaitableEvent* event_;
+};
+
+// Test that we can create an AudioOutputStream with kMinAudioBufferSize and
+// kMaxAudioBufferSize and that the callback AudioBus is the expected size.
+TEST_F(AudioManagerTest, CheckMinMaxAudioBufferSizeCallbacks) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+#if defined(OS_MAC)
+  CreateAudioManagerForTesting<AudioManagerMac>();
+#elif defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+  CreateAudioManagerForTesting<AudioManagerChromeOS>();
+#endif
+
+  DCHECK(audio_manager_);
+
+  AudioParameters default_params;
+  GetDefaultOutputStreamParameters(&default_params);
+  ASSERT_LT(default_params.frames_per_buffer(),
+            media::limits::kMaxAudioBufferSize);
+
+#if defined(OS_MAC)
+  // On OSX the preferred output buffer size is higher than the minimum
+  // but users may request the minimum size explicitly.
+  ASSERT_GT(default_params.frames_per_buffer(),
+            GetMinAudioBufferSizeMacOS(media::limits::kMinAudioBufferSize,
+                                       default_params.sample_rate()));
+#elif defined(USE_CRAS)
+  // On CRAS the preferred output buffer size varies per board and may be as low
+  // as the minimum for some boards.
+  ASSERT_GE(default_params.frames_per_buffer(),
+            media::limits::kMinAudioBufferSize);
+#else
+  NOTREACHED();
+#endif
+
+  AudioOutputStream* stream;
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  // Create an output stream with the minimum buffer size parameters and ensure
+  // that no errors are returned.
+  AudioParameters min_params = default_params;
+  min_params.set_frames_per_buffer(media::limits::kMinAudioBufferSize);
+  stream = audio_manager_->MakeAudioOutputStreamProxy(min_params, "");
+  ASSERT_TRUE(stream);
+  EXPECT_TRUE(stream->Open());
+  event.Reset();
+  TestAudioSourceCallback min_source(min_params.frames_per_buffer(), &event);
+  stream->Start(&min_source);
+  event.Wait();
+  stream->Stop();
+  stream->Close();
+
+  // Verify the same for the maximum buffer size.
+  AudioParameters max_params = default_params;
+  max_params.set_frames_per_buffer(media::limits::kMaxAudioBufferSize);
+  stream = audio_manager_->MakeAudioOutputStreamProxy(max_params, "");
+  ASSERT_TRUE(stream);
+  EXPECT_TRUE(stream->Open());
+  event.Reset();
+  TestAudioSourceCallback max_source(max_params.frames_per_buffer(), &event);
+  stream->Start(&max_source);
+  event.Wait();
+  stream->Stop();
+  stream->Close();
+}
+#endif
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_opus_encoder.cc b/third_party/chromium/media/audio/audio_opus_encoder.cc
new file mode 100644
index 0000000..22d1cff
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_opus_encoder.cc
@@ -0,0 +1,327 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_opus_encoder.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/numerics/checked_math.h"
+#include "base/strings/stringprintf.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/status.h"
+#include "media/base/status_codes.h"
+#include "media/base/timestamp_constants.h"
+
+namespace media {
+
+namespace {
+
+// Recommended value for opus_encode_float(), according to documentation in
+// third_party/opus/src/include/opus.h, so that the Opus encoder does not
+// degrade the audio due to memory constraints, and is independent of the
+// duration of the encoded buffer.
+constexpr int kOpusMaxDataBytes = 4000;
+
+// Opus preferred sampling rate for encoding. This is also the one WebM likes
+// to have: https://wiki.xiph.org/MatroskaOpus.
+constexpr int kOpusPreferredSamplingRate = 48000;
+
+// For Opus, we try to encode 60ms, the maximum Opus buffer, for quality
+// reasons.
+constexpr int kOpusPreferredBufferDurationMs = 60;
+
+// Deletes the libopus encoder instance pointed to by |encoder_ptr|.
+inline void OpusEncoderDeleter(OpusEncoder* encoder_ptr) {
+  opus_encoder_destroy(encoder_ptr);
+}
+
+AudioParameters CreateInputParams(const AudioEncoder::Options& options) {
+  const int frames_per_buffer = options.sample_rate *
+                                kOpusPreferredBufferDurationMs /
+                                base::Time::kMillisecondsPerSecond;
+  AudioParameters result(media::AudioParameters::AUDIO_PCM_LINEAR,
+                         media::CHANNEL_LAYOUT_DISCRETE, options.sample_rate,
+                         frames_per_buffer);
+  result.set_channels_for_discrete(options.channels);
+  return result;
+}
+
+// Creates the audio parameters of the converted audio format that Opus prefers,
+// which will be used as the input to the libopus encoder.
+AudioParameters CreateOpusCompatibleParams(const AudioParameters& params) {
+  // third_party/libopus supports up to 2 channels (see implementation of
+  // opus_encoder_create()): force |converted_params| to at most those.
+  // Also, the libopus encoder can accept sample rates of 8, 12, 16, 24, and the
+  // default preferred 48 kHz. If the input sample rate is anything else, we'll
+  // use 48 kHz.
+  const int input_rate = params.sample_rate();
+  const int used_rate = (input_rate == 8000 || input_rate == 12000 ||
+                         input_rate == 16000 || input_rate == 24000)
+                            ? input_rate
+                            : kOpusPreferredSamplingRate;
+  const int frames_per_buffer = used_rate * kOpusPreferredBufferDurationMs /
+                                base::Time::kMillisecondsPerSecond;
+
+  AudioParameters result(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                         GuessChannelLayout(std::min(params.channels(), 2)),
+                         used_rate, frames_per_buffer);
+  return result;
+}
+
+// During this object's lifetime, it will use its |audio_bus_| to provide input
+// to its |converter_|.
+class ScopedConverterInputProvider : public AudioConverter::InputCallback {
+ public:
+  ScopedConverterInputProvider(AudioConverter* converter,
+                               const AudioBus* audio_bus)
+      : converter_(converter), audio_bus_(audio_bus) {
+    DCHECK(converter_);
+    DCHECK(audio_bus_);
+    converter_->AddInput(this);
+  }
+  ScopedConverterInputProvider(const ScopedConverterInputProvider&) = delete;
+  ScopedConverterInputProvider& operator=(const ScopedConverterInputProvider&) =
+      delete;
+  ~ScopedConverterInputProvider() override { converter_->RemoveInput(this); }
+
+  // AudioConverted::InputCallback:
+  double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override {
+    audio_bus_->CopyTo(audio_bus);
+    return 1.0f;
+  }
+
+ private:
+  AudioConverter* const converter_;
+  const AudioBus* const audio_bus_;
+};
+
+}  // namespace
+
+// TODO: Remove after switching to C++17
+constexpr int AudioOpusEncoder::kMinBitrate;
+
+AudioOpusEncoder::AudioOpusEncoder()
+    : opus_encoder_(nullptr, OpusEncoderDeleter) {}
+
+void AudioOpusEncoder::Initialize(const Options& options,
+                                  OutputCB output_callback,
+                                  StatusCB done_cb) {
+  DCHECK(!output_callback.is_null());
+  DCHECK(!done_cb.is_null());
+
+  done_cb = BindToCurrentLoop(std::move(done_cb));
+  if (opus_encoder_) {
+    std::move(done_cb).Run(StatusCode::kEncoderInitializeTwice);
+    return;
+  }
+
+  options_ = options;
+  input_params_ = CreateInputParams(options);
+  if (!input_params_.IsValid()) {
+    std::move(done_cb).Run(StatusCode::kEncoderInitializationError);
+    return;
+  }
+
+  converted_params_ = CreateOpusCompatibleParams(input_params_);
+  if (!input_params_.IsValid()) {
+    std::move(done_cb).Run(StatusCode::kEncoderInitializationError);
+    return;
+  }
+
+  converter_ =
+      std::make_unique<AudioConverter>(input_params_, converted_params_,
+                                       /*disable_fifo=*/false);
+  timestamp_tracker_ =
+      std::make_unique<AudioTimestampHelper>(converted_params_.sample_rate());
+  fifo_ = std::make_unique<AudioPushFifo>(base::BindRepeating(
+      &AudioOpusEncoder::OnFifoOutput, base::Unretained(this)));
+  converted_audio_bus_ = AudioBus::Create(
+      converted_params_.channels(), converted_params_.frames_per_buffer());
+  buffer_.resize(converted_params_.channels() *
+                 converted_params_.frames_per_buffer());
+  auto status_or_encoder = CreateOpusEncoder();
+  if (status_or_encoder.has_error()) {
+    std::move(done_cb).Run(std::move(status_or_encoder).error());
+    return;
+  }
+
+  opus_encoder_ = std::move(status_or_encoder).value();
+  converter_->PrimeWithSilence();
+  fifo_->Reset(converter_->GetMaxInputFramesRequested(
+      converted_params_.frames_per_buffer()));
+
+  output_cb_ = BindToCurrentLoop(std::move(output_callback));
+  std::move(done_cb).Run(OkStatus());
+}
+
+AudioOpusEncoder::~AudioOpusEncoder() = default;
+
+AudioOpusEncoder::CodecDescription AudioOpusEncoder::PrepareExtraData() {
+  CodecDescription extra_data;
+  // RFC #7845  Ogg Encapsulation for the Opus Audio Codec
+  // https://tools.ietf.org/html/rfc7845
+  static const uint8_t kExtraDataTemplate[19] = {
+      'O', 'p', 'u', 's', 'H', 'e', 'a', 'd',
+      1,                 // offset 8, version, always 1
+      0,                 // offset 9, channel count
+      0,   0,            // offset 10, pre-skip
+      0,   0,   0,   0,  // offset 12, original input sample rate in Hz
+      0,   0,   0};
+
+  extra_data.assign(kExtraDataTemplate,
+                    kExtraDataTemplate + sizeof(kExtraDataTemplate));
+
+  // Save number of channels
+  base::CheckedNumeric<uint8_t> channels(converted_params_.channels());
+  if (channels.IsValid())
+    extra_data.data()[9] = channels.ValueOrDie();
+
+  // Number of samples to skip from the start of the decoder's output.
+  // Real data begins this many samples late. These samples need to be skipped
+  // only at the very beginning of the audio stream, NOT at beginning of each
+  // decoded output.
+  if (opus_encoder_) {
+    int32_t samples_to_skip = 0;
+
+    opus_encoder_ctl(opus_encoder_.get(), OPUS_GET_LOOKAHEAD(&samples_to_skip));
+    base::CheckedNumeric<uint16_t> samples_to_skip_safe = samples_to_skip;
+    if (samples_to_skip_safe.IsValid())
+      *reinterpret_cast<uint16_t*>(extra_data.data() + 10) =
+          samples_to_skip_safe.ValueOrDie();
+  }
+
+  // Save original sample rate
+  base::CheckedNumeric<uint16_t> sample_rate = input_params_.sample_rate();
+  uint16_t* sample_rate_ptr =
+      reinterpret_cast<uint16_t*>(extra_data.data() + 12);
+  if (sample_rate.IsValid())
+    *sample_rate_ptr = sample_rate.ValueOrDie();
+  else
+    *sample_rate_ptr = uint16_t{kOpusPreferredSamplingRate};
+  return extra_data;
+}
+
+void AudioOpusEncoder::Encode(std::unique_ptr<AudioBus> audio_bus,
+                              base::TimeTicks capture_time,
+                              StatusCB done_cb) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(audio_bus->channels(), input_params_.channels());
+  DCHECK(!done_cb.is_null());
+  DCHECK(timestamp_tracker_);
+
+  current_done_cb_ = BindToCurrentLoop(std::move(done_cb));
+  if (!opus_encoder_) {
+    std::move(current_done_cb_)
+        .Run(StatusCode::kEncoderInitializeNeverCompleted);
+    return;
+  }
+
+  if (timestamp_tracker_->base_timestamp() == kNoTimestamp)
+    timestamp_tracker_->SetBaseTimestamp(capture_time - base::TimeTicks());
+
+  // The |fifo_| won't trigger OnFifoOutput() until we have enough frames
+  // suitable for the converter.
+  fifo_->Push(*audio_bus);
+  if (!current_done_cb_.is_null()) {
+    // Is |current_done_cb_| is null, it means OnFifoOutput() has already
+    // reported an error.
+    std::move(current_done_cb_).Run(OkStatus());
+  }
+}
+
+void AudioOpusEncoder::Flush(StatusCB done_cb) {
+  DCHECK(!done_cb.is_null());
+
+  done_cb = BindToCurrentLoop(std::move(done_cb));
+  if (!opus_encoder_) {
+    std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
+    return;
+  }
+
+  current_done_cb_ = std::move(done_cb);
+  fifo_->Flush();
+  timestamp_tracker_->SetBaseTimestamp(kNoTimestamp);
+  if (!current_done_cb_.is_null()) {
+    // Is |current_done_cb_| is null, it means OnFifoOutput() has already
+    // reported an error.
+    std::move(current_done_cb_).Run(OkStatus());
+  }
+}
+
+void AudioOpusEncoder::OnFifoOutput(const AudioBus& output_bus,
+                                    int frame_delay) {
+  // Provides input to the converter from |output_bus| within this scope only.
+  ScopedConverterInputProvider provider(converter_.get(), &output_bus);
+  converter_->Convert(converted_audio_bus_.get());
+  converted_audio_bus_->ToInterleaved<Float32SampleTypeTraits>(
+      converted_audio_bus_->frames(), buffer_.data());
+
+  std::unique_ptr<uint8_t[]> encoded_data(new uint8_t[kOpusMaxDataBytes]);
+  auto result = opus_encode_float(opus_encoder_.get(), buffer_.data(),
+                                  converted_params_.frames_per_buffer(),
+                                  encoded_data.get(), kOpusMaxDataBytes);
+
+  if (result < 0 && !current_done_cb_.is_null()) {
+    std::move(current_done_cb_)
+        .Run(Status(StatusCode::kEncoderFailedEncode, opus_strerror(result)));
+    return;
+  }
+
+  size_t encoded_data_size = result;
+  // If |result| in {0,1}, do nothing; the documentation says that a return
+  // value of zero or one means the packet does not need to be transmitted.
+  if (encoded_data_size > 1) {
+    absl::optional<CodecDescription> desc;
+    if (need_to_emit_extra_data_) {
+      desc = PrepareExtraData();
+      need_to_emit_extra_data_ = false;
+    }
+
+    auto ts = base::TimeTicks() + timestamp_tracker_->GetTimestamp();
+
+    auto duration = timestamp_tracker_->GetFrameDuration(
+        converted_params_.frames_per_buffer());
+
+    EncodedAudioBuffer encoded_buffer(converted_params_,
+                                      std::move(encoded_data),
+                                      encoded_data_size, ts, duration);
+    output_cb_.Run(std::move(encoded_buffer), desc);
+  }
+  timestamp_tracker_->AddFrames(converted_params_.frames_per_buffer());
+}
+
+// Creates and returns the libopus encoder instance. Returns nullptr if the
+// encoder creation fails.
+StatusOr<OwnedOpusEncoder> AudioOpusEncoder::CreateOpusEncoder() {
+  int opus_result;
+  OwnedOpusEncoder encoder(
+      opus_encoder_create(converted_params_.sample_rate(),
+                          converted_params_.channels(), OPUS_APPLICATION_AUDIO,
+                          &opus_result),
+      OpusEncoderDeleter);
+
+  if (opus_result < 0) {
+    return Status(
+        StatusCode::kEncoderInitializationError,
+        base::StringPrintf(
+            "Couldn't init Opus encoder: %s, sample rate: %d, channels: %d",
+            opus_strerror(opus_result), converted_params_.sample_rate(),
+            converted_params_.channels()));
+  }
+
+  int bitrate =
+      options_.bitrate.has_value() ? options_.bitrate.value() : OPUS_AUTO;
+  if (encoder &&
+      opus_encoder_ctl(encoder.get(), OPUS_SET_BITRATE(bitrate)) != OPUS_OK) {
+    return Status(
+        StatusCode::kEncoderInitializationError,
+        base::StringPrintf("Failed to set Opus bitrate: %d", bitrate));
+  }
+
+  return encoder;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_opus_encoder.h b/third_party/chromium/media/audio/audio_opus_encoder.h
new file mode 100644
index 0000000..c11c5c0
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_opus_encoder.h
@@ -0,0 +1,92 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_OPUS_ENCODER_H_
+#define MEDIA_AUDIO_AUDIO_OPUS_ENCODER_H_
+
+#include <memory>
+#include <vector>
+
+#include "media/base/audio_bus.h"
+#include "media/base/audio_converter.h"
+#include "media/base/audio_encoder.h"
+#include "media/base/audio_push_fifo.h"
+#include "media/base/audio_timestamp_helper.h"
+#include "third_party/opus/src/include/opus.h"
+
+namespace media {
+
+using OpusEncoderDeleterType = void (*)(OpusEncoder* encoder_ptr);
+using OwnedOpusEncoder = std::unique_ptr<OpusEncoder, OpusEncoderDeleterType>;
+
+// Performs Opus encoding of the input audio. The input audio is converted to a
+// a format suitable for Opus before it is passed to the libopus encoder
+// instance to do the actual encoding.
+class MEDIA_EXPORT AudioOpusEncoder : public AudioEncoder {
+ public:
+  AudioOpusEncoder();
+  AudioOpusEncoder(const AudioOpusEncoder&) = delete;
+  AudioOpusEncoder& operator=(const AudioOpusEncoder&) = delete;
+  ~AudioOpusEncoder() override;
+
+  // AudioEncoder:
+  void Initialize(const Options& options,
+                  OutputCB output_callback,
+                  StatusCB done_cb) override;
+
+  void Encode(std::unique_ptr<AudioBus> audio_bus,
+              base::TimeTicks capture_time,
+              StatusCB done_cb) override;
+
+  void Flush(StatusCB done_cb) override;
+
+  static constexpr int kMinBitrate = 6000;
+
+ private:
+  // Called synchronously by |fifo_| once enough audio frames have been
+  // buffered. Calls libopus to do actual encoding.
+  void OnFifoOutput(const AudioBus& output_bus, int frame_delay);
+
+  CodecDescription PrepareExtraData();
+
+  StatusOr<OwnedOpusEncoder> CreateOpusEncoder();
+
+  AudioParameters input_params_;
+
+  // Output parameters after audio conversion. This may differ from the input
+  // params in the number of channels, sample rate, and the frames per buffer.
+  // (See CreateOpusInputParams() in the .cc file for details).
+  AudioParameters converted_params_;
+
+  // Sample rate adapter from the input audio to what OpusEncoder desires.
+  std::unique_ptr<AudioConverter> converter_;
+
+  // Buffer for holding the original input audio before it goes to the
+  // converter.
+  std::unique_ptr<AudioPushFifo> fifo_;
+
+  // This is the destination AudioBus where the |converter_| teh audio into.
+  std::unique_ptr<AudioBus> converted_audio_bus_;
+
+  // Buffer for passing AudioBus data from the converter to the encoder.
+  std::vector<float> buffer_;
+
+  // The actual libopus encoder instance. This is nullptr if creating the
+  // encoder fails.
+  OwnedOpusEncoder opus_encoder_;
+
+  // Keeps track of the timestamps for the each |output_callback_|
+  std::unique_ptr<AudioTimestampHelper> timestamp_tracker_;
+
+  // Callback for reporting completion and status of the current Flush() or
+  // Encoder()
+  StatusCB current_done_cb_;
+
+  // True if the next output needs to have extra_data in it, only happens once.
+  bool need_to_emit_extra_data_ = true;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OPUS_ENCODER_H_
diff --git a/third_party/chromium/media/audio/audio_output_delegate.cc b/third_party/chromium/media/audio/audio_output_delegate.cc
new file mode 100644
index 0000000..98e11e9
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_delegate.cc
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_delegate.h"
+
+media::AudioOutputDelegate::EventHandler::~EventHandler() = default;
+
+media::AudioOutputDelegate::~AudioOutputDelegate() = default;
diff --git a/third_party/chromium/media/audio/audio_output_delegate.h b/third_party/chromium/media/audio/audio_output_delegate.h
new file mode 100644
index 0000000..7cb5e00
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_delegate.h
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DELEGATE_H_
+#define MEDIA_AUDIO_AUDIO_OUTPUT_DELEGATE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/base/media_export.h"
+
+namespace base {
+class UnsafeSharedMemoryRegion;
+class CancelableSyncSocket;
+}
+
+namespace media {
+
+class MEDIA_EXPORT AudioOutputDelegate {
+ public:
+  // An AudioOutputDelegate must not call back to its EventHandler in its
+  // constructor.
+  class MEDIA_EXPORT EventHandler {
+   public:
+    virtual ~EventHandler() = 0;
+
+    // Called when the underlying stream is ready for playout.
+    virtual void OnStreamCreated(
+        int stream_id,
+        base::UnsafeSharedMemoryRegion shared_memory_region,
+        std::unique_ptr<base::CancelableSyncSocket> socket) = 0;
+
+    // Called if stream encounters an error and has become unusable.
+    virtual void OnStreamError(int stream_id) = 0;
+  };
+
+  virtual ~AudioOutputDelegate() = 0;
+
+  virtual int GetStreamId() = 0;
+
+  // Stream control:
+  virtual void OnPlayStream() = 0;
+  virtual void OnPauseStream() = 0;
+  virtual void OnFlushStream() = 0;
+  virtual void OnSetVolume(double volume) = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_DELEGATE_H_
diff --git a/third_party/chromium/media/audio/audio_output_device.cc b/third_party/chromium/media/audio/audio_output_device.cc
new file mode 100644
index 0000000..338d9f6
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_device.cc
@@ -0,0 +1,486 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_device.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cmath>
+#include <memory>
+#include <utility>
+
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/timer/timer.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_output_device_thread_callback.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/limits.h"
+
+namespace media {
+
+AudioOutputDevice::AudioOutputDevice(
+    std::unique_ptr<AudioOutputIPC> ipc,
+    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+    const AudioSinkParameters& sink_params,
+    base::TimeDelta authorization_timeout)
+    : io_task_runner_(io_task_runner),
+      callback_(nullptr),
+      ipc_(std::move(ipc)),
+      state_(IDLE),
+      session_id_(sink_params.session_id),
+      device_id_(sink_params.device_id),
+      processing_id_(sink_params.processing_id),
+      stopping_hack_(false),
+      did_receive_auth_(base::WaitableEvent::ResetPolicy::MANUAL,
+                        base::WaitableEvent::InitialState::NOT_SIGNALED),
+      output_params_(AudioParameters::UnavailableDeviceParams()),
+      device_status_(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL),
+      auth_timeout_(authorization_timeout) {
+  DCHECK(ipc_);
+  DCHECK(io_task_runner_);
+}
+
+void AudioOutputDevice::Initialize(const AudioParameters& params,
+                                   RenderCallback* callback) {
+  io_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputDevice::InitializeOnIOThread, this,
+                                params, callback));
+}
+
+void AudioOutputDevice::InitializeOnIOThread(const AudioParameters& params,
+                                             RenderCallback* callback) {
+  DCHECK(!callback_) << "Calling Initialize() twice?";
+  DCHECK(params.IsValid());
+  DVLOG(1) << __func__ << ": " << params.AsHumanReadableString();
+  audio_parameters_ = params;
+
+  base::AutoLock auto_lock(audio_thread_lock_);
+  // If Stop() has already been called, RenderCallback has already been
+  // destroyed. So |callback| would be a dangling pointer.
+  if (!stopping_hack_)
+    callback_ = callback;
+}
+
+AudioOutputDevice::~AudioOutputDevice() {
+  {
+    // Abort any pending callbacks. Technically we don't need to acquire the
+    // lock here since there should be no other calls outstanding, but because
+    // we've used the GUARDED_BY compiler syntax, we'll get an error without it.
+    base::AutoLock auto_lock(device_info_lock_);
+    if (pending_device_info_cb_) {
+      std::move(pending_device_info_cb_)
+          .Run(OutputDeviceInfo(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL));
+    }
+  }
+
+#if DCHECK_IS_ON()
+  // Make sure we've stopped the stream properly before destructing |this|.
+  DCHECK(audio_thread_lock_.Try());
+  DCHECK_EQ(state_, IDLE);
+  DCHECK(!audio_thread_);
+  DCHECK(!audio_callback_);
+  DCHECK(!stopping_hack_);
+  audio_thread_lock_.Release();
+#endif  // DCHECK_IS_ON()
+}
+
+void AudioOutputDevice::RequestDeviceAuthorization() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::RequestDeviceAuthorization");
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&AudioOutputDevice::RequestDeviceAuthorizationOnIOThread,
+                     this));
+}
+
+void AudioOutputDevice::Start() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::Start");
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&AudioOutputDevice::CreateStreamOnIOThread, this));
+}
+
+void AudioOutputDevice::Stop() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::Stop");
+  {
+    base::AutoLock auto_lock(audio_thread_lock_);
+    audio_thread_.reset();
+    stopping_hack_ = true;
+  }
+  io_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputDevice::ShutDownOnIOThread, this));
+}
+
+void AudioOutputDevice::Play() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::Play");
+  io_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputDevice::PlayOnIOThread, this));
+}
+
+void AudioOutputDevice::Pause() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::Pause");
+  io_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputDevice::PauseOnIOThread, this));
+}
+
+void AudioOutputDevice::Flush() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::Flush");
+  io_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputDevice::FlushOnIOThread, this));
+}
+
+bool AudioOutputDevice::SetVolume(double volume) {
+  TRACE_EVENT1("audio", "AudioOutputDevice::Pause", "volume", volume);
+
+  if (volume < 0 || volume > 1.0)
+    return false;
+
+  return io_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&AudioOutputDevice::SetVolumeOnIOThread, this, volume));
+}
+
+OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::GetOutputDeviceInfo");
+  DCHECK(!io_task_runner_->BelongsToCurrentThread());
+  did_receive_auth_.Wait();
+  return GetOutputDeviceInfo_Signaled();
+}
+
+void AudioOutputDevice::GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) {
+  {
+    // Hold the lock while checking the signal and setting the pending callback
+    // to avoid racing with authorization completion on the IO thread.
+    base::AutoLock auto_lock(device_info_lock_);
+    if (!did_receive_auth_.IsSignaled()) {
+      DCHECK(!pending_device_info_cb_);
+      pending_device_info_cb_ = BindToCurrentLoop(std::move(info_cb));
+      return;
+    }
+  }
+
+  // Always post to avoid the caller being reentrant. Local testing shows even
+  // on a powerful desktop, we haven't received device authorization by this
+  // point when AOD construction and GetOutputDeviceInfoAsync() happen back to
+  // back (which is the most common use case).
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(info_cb), GetOutputDeviceInfo_Signaled()));
+}
+
+bool AudioOutputDevice::IsOptimizedForHardwareParameters() {
+  return true;
+}
+
+bool AudioOutputDevice::CurrentThreadIsRenderingThread() {
+  // Since this function is supposed to be called on the rendering thread,
+  // it's safe to access |audio_callback_| here. It will always be valid when
+  // the rendering thread is running.
+  return audio_callback_->CurrentThreadIsAudioDeviceThread();
+}
+
+void AudioOutputDevice::RequestDeviceAuthorizationOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  DCHECK_EQ(state_, IDLE);
+
+  state_ = AUTHORIZATION_REQUESTED;
+  ipc_->RequestDeviceAuthorization(this, session_id_, device_id_);
+
+  if (auth_timeout_ > base::TimeDelta()) {
+    // Create the timer on the thread it's used on. It's guaranteed to be
+    // deleted on the same thread since users must call Stop() before deleting
+    // AudioOutputDevice; see ShutDownOnIOThread().
+    auth_timeout_action_ = std::make_unique<base::OneShotTimer>();
+    auth_timeout_action_->Start(
+        FROM_HERE, auth_timeout_,
+        base::BindOnce(&AudioOutputDevice::OnDeviceAuthorized, this,
+                       OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT, AudioParameters(),
+                       std::string()));
+  }
+}
+
+void AudioOutputDevice::CreateStreamOnIOThread() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::Create");
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+#if DCHECK_IS_ON()
+  {
+    base::AutoLock auto_lock(audio_thread_lock_);
+    if (!stopping_hack_)
+      DCHECK(callback_) << "Initialize hasn't been called";
+  }
+#endif
+  DCHECK_NE(state_, STREAM_CREATION_REQUESTED);
+
+  if (!ipc_) {
+    NotifyRenderCallbackOfError();
+    return;
+  }
+
+  if (state_ == IDLE && !(did_receive_auth_.IsSignaled() && device_id_.empty()))
+    RequestDeviceAuthorizationOnIOThread();
+
+  ipc_->CreateStream(this, audio_parameters_, processing_id_);
+  // By default, start playing right away.
+  ipc_->PlayStream();
+  state_ = STREAM_CREATION_REQUESTED;
+}
+
+void AudioOutputDevice::PlayOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  if (audio_callback_)
+    audio_callback_->InitializePlayStartTime();
+
+  if (ipc_)
+    ipc_->PlayStream();
+}
+
+void AudioOutputDevice::PauseOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  if (ipc_)
+    ipc_->PauseStream();
+}
+
+void AudioOutputDevice::FlushOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  if (ipc_)
+    ipc_->FlushStream();
+}
+
+void AudioOutputDevice::ShutDownOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  if (ipc_)
+    ipc_->CloseStream();
+
+  state_ = IDLE;
+
+  // Destoy the timer on the thread it's used on.
+  auth_timeout_action_.reset();
+
+  UMA_HISTOGRAM_ENUMERATION("Media.Audio.Render.StreamCallbackError2",
+                            had_error_);
+  had_error_ = kNoError;
+
+  // We can run into an issue where ShutDownOnIOThread is called right after
+  // OnStreamCreated is called in cases where Start/Stop are called before we
+  // get the OnStreamCreated callback.  To handle that corner case, we call
+  // Stop(). In most cases, the thread will already be stopped.
+  //
+  // Another situation is when the IO thread goes away before Stop() is called
+  // in which case, we cannot use the message loop to close the thread handle
+  // and can't rely on the main thread existing either.
+  base::AutoLock auto_lock_(audio_thread_lock_);
+  base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_thread_join;
+  audio_thread_.reset();
+  audio_callback_.reset();
+  stopping_hack_ = false;
+}
+
+void AudioOutputDevice::SetVolumeOnIOThread(double volume) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  if (ipc_)
+    ipc_->SetVolume(volume);
+}
+
+void AudioOutputDevice::OnError() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::OnError");
+
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  // Do nothing if the stream has been closed.
+  if (state_ == IDLE)
+    return;
+
+  // Don't dereference the callback object if the audio thread
+  // is stopped or stopping.  That could mean that the callback
+  // object has been deleted.
+  // TODO(tommi): Add an explicit contract for clearing the callback
+  // object.  Possibly require calling Initialize again or provide
+  // a callback object via Start() and clear it in Stop().
+  NotifyRenderCallbackOfError();
+}
+
+void AudioOutputDevice::OnDeviceAuthorized(
+    OutputDeviceStatus device_status,
+    const AudioParameters& output_params,
+    const std::string& matched_device_id) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  auth_timeout_action_.reset();
+
+  // Do nothing if late authorization is received after timeout.
+  if (!ipc_)
+    return;
+
+  UMA_HISTOGRAM_BOOLEAN("Media.Audio.Render.OutputDeviceAuthorizationTimedOut",
+                        device_status == OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT);
+  LOG_IF(WARNING, device_status == OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT)
+      << "Output device authorization timed out";
+
+  // It may happen that a second authorization is received as a result to a
+  // call to Start() after Stop(). If the status for the second authorization
+  // differs from the first, it will not be reflected in |device_status_|
+  // to avoid a race.
+  // This scenario is unlikely. If it occurs, the new value will be
+  // different from OUTPUT_DEVICE_STATUS_OK, so the AudioOutputDevice
+  // will enter the |ipc_| == nullptr state anyway, which is the safe thing to
+  // do. This is preferable to holding a lock.
+  if (!did_receive_auth_.IsSignaled()) {
+    device_status_ = device_status;
+    UMA_HISTOGRAM_ENUMERATION("Media.Audio.Render.OutputDeviceStatus",
+                              device_status, OUTPUT_DEVICE_STATUS_MAX + 1);
+  }
+
+  if (device_status == OUTPUT_DEVICE_STATUS_OK) {
+    TRACE_EVENT0("audio", "AudioOutputDevice authorized");
+
+    if (!did_receive_auth_.IsSignaled()) {
+      output_params_ = output_params;
+
+      // It's possible to not have a matched device obtained via session id. It
+      // means matching output device through |session_id_| failed and the
+      // default device is used.
+      DCHECK(AudioDeviceDescription::UseSessionIdToSelectDevice(session_id_,
+                                                                device_id_) ||
+             matched_device_id_.empty());
+      matched_device_id_ = matched_device_id;
+
+      DVLOG(1) << "AudioOutputDevice authorized, session_id: " << session_id_
+               << ", device_id: " << device_id_
+               << ", matched_device_id: " << matched_device_id_;
+
+      OnAuthSignal();
+    }
+  } else {
+    TRACE_EVENT1("audio", "AudioOutputDevice not authorized", "auth status",
+                 device_status_);
+
+    // Closing IPC forces a Signal(), so no clients are locked waiting
+    // indefinitely after this method returns.
+    ipc_->CloseStream();
+    OnIPCClosed();
+
+    NotifyRenderCallbackOfError();
+  }
+}
+
+void AudioOutputDevice::OnStreamCreated(
+    base::UnsafeSharedMemoryRegion shared_memory_region,
+    base::SyncSocket::ScopedHandle socket_handle,
+    bool playing_automatically) {
+  TRACE_EVENT0("audio", "AudioOutputDevice::OnStreamCreated");
+
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  DCHECK(shared_memory_region.IsValid());
+#if defined(OS_WIN)
+  DCHECK(socket_handle.IsValid());
+#else
+  DCHECK(socket_handle.is_valid());
+#endif
+  DCHECK_GT(shared_memory_region.GetSize(), 0u);
+
+  if (state_ != STREAM_CREATION_REQUESTED)
+    return;
+
+  // We can receive OnStreamCreated() on the IO thread after the client has
+  // called Stop() but before ShutDownOnIOThread() is processed. In such a
+  // situation |callback_| might point to freed memory. Instead of starting
+  // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called.
+  //
+  // TODO(scherkus): The real fix is to have sane ownership semantics. The fact
+  // that |callback_| (which should own and outlive this object!) can point to
+  // freed memory is a mess. AudioRendererSink should be non-refcounted so that
+  // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and
+  // delete as they see fit. AudioOutputDevice should internally use WeakPtr
+  // to handle teardown and thread hopping. See http://crbug.com/151051 for
+  // details.
+  {
+    base::AutoLock auto_lock(audio_thread_lock_);
+    if (stopping_hack_)
+      return;
+
+    DCHECK(!audio_thread_);
+    DCHECK(!audio_callback_);
+
+    audio_callback_ = std::make_unique<AudioOutputDeviceThreadCallback>(
+        audio_parameters_, std::move(shared_memory_region), callback_);
+    if (playing_automatically)
+      audio_callback_->InitializePlayStartTime();
+    audio_thread_ = std::make_unique<AudioDeviceThread>(
+        audio_callback_.get(), std::move(socket_handle), "AudioOutputDevice",
+        base::ThreadPriority::REALTIME_AUDIO);
+  }
+}
+
+void AudioOutputDevice::OnIPCClosed() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::OnIPCClosed");
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  ipc_.reset();
+  state_ = IDLE;
+
+  OnAuthSignal();
+}
+
+OutputDeviceInfo AudioOutputDevice::GetOutputDeviceInfo_Signaled() {
+  DCHECK(did_receive_auth_.IsSignaled());
+  return OutputDeviceInfo(AudioDeviceDescription::UseSessionIdToSelectDevice(
+                              session_id_, device_id_)
+                              ? matched_device_id_
+                              : device_id_,
+                          device_status_, output_params_);
+}
+
+void AudioOutputDevice::OnAuthSignal() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  // This lock is held while signaling to avoid any thread safety issues while
+  // GetOutputDeviceInfoAsync() may be checking the signal and modifying the
+  // |pending_device_info_cb_| on another thread.
+  //
+  // We might be able to get away with signaling outside of the lock, but this
+  // requires more careful construction for anyone checking the signal and
+  // using the result to set or get the pending callback value. The failure
+  // mode is also more subtle, callbacks will be lost versus a thread hang which
+  // is more easily detectable in the production population.
+  base::AutoLock auto_lock(device_info_lock_);
+
+  // Signal to unblock any blocked threads waiting for parameters.
+  did_receive_auth_.Signal();
+
+  // The callback is always posted by way media::BindToCurrentLoop() usage upon
+  // receipt, so this is safe to run under the lock.
+  if (pending_device_info_cb_)
+    std::move(pending_device_info_cb_).Run(GetOutputDeviceInfo_Signaled());
+}
+
+void AudioOutputDevice::NotifyRenderCallbackOfError() {
+  TRACE_EVENT0("audio", "AudioOutputDevice::NotifyRenderCallbackOfError");
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  base::AutoLock auto_lock(audio_thread_lock_);
+  // Avoid signaling error if Initialize() hasn't been called yet, or if
+  // Stop() has already been called.
+  if (callback_ && !stopping_hack_) {
+    // Update |had_error_| for UMA stats.
+    if (audio_callback_)
+      had_error_ = kErrorDuringRendering;
+    else
+      had_error_ = kErrorDuringCreation;
+    callback_->OnRenderError();
+  }
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_output_device.h b/third_party/chromium/media/audio/audio_output_device.h
new file mode 100644
index 0000000..9770413
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_device.h
@@ -0,0 +1,248 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Audio rendering unit utilizing audio output stream provided by browser
+// process through IPC.
+//
+// Relationship of classes.
+//
+//  AudioOutputController                AudioOutputDevice
+//           ^                                  ^
+//           |                                  |
+//           v                 IPC              v
+//  MojoAudioOutputStream  <---------> AudioOutputIPC (MojoAudioOutputIPC)
+//
+// Transportation of audio samples from the render to the browser process
+// is done by using shared memory in combination with a sync socket pair
+// to generate a low latency transport. The AudioOutputDevice user registers an
+// AudioOutputDevice::RenderCallback at construction and will be polled by the
+// AudioOutputController for audio to be played out by the underlying audio
+// layers.
+//
+// State sequences.
+//
+//            Task [IO thread]                  IPC [IO thread]
+// RequestDeviceAuthorization -> RequestDeviceAuthorizationOnIOThread ------>
+// RequestDeviceAuthorization ->
+//             <- OnDeviceAuthorized <- AudioMsg_NotifyDeviceAuthorized <-
+//
+// Start -> CreateStreamOnIOThread -----> CreateStream ------>
+//       <- OnStreamCreated <- AudioMsg_NotifyStreamCreated <-
+//       ---> PlayOnIOThread -----------> PlayStream -------->
+//
+// Optionally Play() / Pause() sequences may occur:
+// Play -> PlayOnIOThread --------------> PlayStream --------->
+// Pause -> PauseOnIOThread ------------> PauseStream -------->
+// (note that Play() / Pause() sequences before
+// OnStreamCreated are deferred until OnStreamCreated, with the last valid
+// state being used)
+//
+// AudioOutputDevice::Render => audio transport on audio thread =>
+//                               |
+// Stop --> ShutDownOnIOThread -------->  CloseStream -> Close
+//
+// This class utilizes several threads during its lifetime, namely:
+// 1. Creating thread.
+//    Must be the main render thread.
+// 2. Control thread (may be the main render thread or another thread).
+//    The methods: Start(), Stop(), Play(), Pause(), SetVolume()
+//    must be called on the same thread.
+// 3. IO thread (internal implementation detail - not exposed to public API)
+//    The thread within which this class receives all the IPC messages and
+//    IPC communications can only happen in this thread.
+// 4. Audio transport thread (See AudioDeviceThread).
+//    Responsible for calling the AudioOutputDeviceThreadCallback
+//    implementation that in turn calls AudioRendererSink::RenderCallback
+//    which feeds audio samples to the audio layer in the browser process using
+//    sync sockets and shared memory.
+//
+// Implementation notes:
+// - The user must call Stop() before deleting the class instance.
+
+#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_
+#define MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/thread_annotations.h"
+#include "base/time/time.h"
+#include "media/audio/audio_device_thread.h"
+#include "media/audio/audio_output_ipc.h"
+#include "media/audio/audio_sink_parameters.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/audio_renderer_sink.h"
+#include "media/base/media_export.h"
+#include "media/base/output_device_info.h"
+
+namespace base {
+class OneShotTimer;
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+class AudioOutputDeviceThreadCallback;
+
+class MEDIA_EXPORT AudioOutputDevice : public AudioRendererSink,
+                                       public AudioOutputIPCDelegate {
+ public:
+  // NOTE: Clients must call Initialize() before using.
+  AudioOutputDevice(
+      std::unique_ptr<AudioOutputIPC> ipc,
+      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
+      const AudioSinkParameters& sink_params,
+      base::TimeDelta authorization_timeout);
+
+  // Request authorization to use the device specified in the constructor.
+  void RequestDeviceAuthorization();
+
+  // AudioRendererSink implementation.
+  void Initialize(const AudioParameters& params,
+                  RenderCallback* callback) override;
+  void Start() override;
+  void Stop() override;
+  void Play() override;
+  void Pause() override;
+  void Flush() override;
+  bool SetVolume(double volume) override;
+  OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
+  bool IsOptimizedForHardwareParameters() override;
+  bool CurrentThreadIsRenderingThread() override;
+
+  // Methods called on IO thread ----------------------------------------------
+  // AudioOutputIPCDelegate methods.
+  void OnError() override;
+  void OnDeviceAuthorized(OutputDeviceStatus device_status,
+                          const AudioParameters& output_params,
+                          const std::string& matched_device_id) override;
+  void OnStreamCreated(base::UnsafeSharedMemoryRegion shared_memory_region,
+                       base::SyncSocket::ScopedHandle socket_handle,
+                       bool play_automatically) override;
+  void OnIPCClosed() override;
+
+ protected:
+  // Magic required by ref_counted.h to avoid any code deleting the object
+  // accidentally while there are references to it.
+  friend class base::RefCountedThreadSafe<AudioOutputDevice>;
+  ~AudioOutputDevice() override;
+
+ private:
+  enum StartupState {
+    IDLE,                       // Authorization not requested.
+    AUTHORIZATION_REQUESTED,    // Sent (possibly completed) device
+                                // authorization request.
+    STREAM_CREATION_REQUESTED,  // Sent (possibly completed) device creation
+                                // request. Can Play()/Pause()/Stop().
+  };
+
+  // This enum is used for UMA, so the only allowed operation on this definition
+  // is to add new states to the bottom, update kMaxValue, and update the
+  // histogram "Media.Audio.Render.StreamCallbackError2".
+  enum Error {
+    kNoError = 0,
+    kErrorDuringCreation = 1,
+    kErrorDuringRendering = 2,
+    kMaxValue = kErrorDuringRendering
+  };
+
+  // Methods called on IO thread ----------------------------------------------
+  // The following methods are tasks posted on the IO thread that need to
+  // be executed on that thread.  They use AudioOutputIPC to send IPC messages
+  // upon state changes.
+  void RequestDeviceAuthorizationOnIOThread();
+  void InitializeOnIOThread(const AudioParameters& params,
+                            RenderCallback* callback);
+  void CreateStreamOnIOThread();
+  void PlayOnIOThread();
+  void PauseOnIOThread();
+  void FlushOnIOThread();
+  void ShutDownOnIOThread();
+  void SetVolumeOnIOThread(double volume);
+
+  // Process device authorization result on the IO thread.
+  void ProcessDeviceAuthorizationOnIOThread(
+      OutputDeviceStatus device_status,
+      const AudioParameters& output_params,
+      const std::string& matched_device_id,
+      bool timed_out);
+
+  void NotifyRenderCallbackOfError();
+
+  OutputDeviceInfo GetOutputDeviceInfo_Signaled();
+  void OnAuthSignal();
+
+  const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+  AudioParameters audio_parameters_;
+
+  RenderCallback* callback_;
+
+  // A pointer to the IPC layer that takes care of sending requests over to
+  // the implementation. May be set to nullptr after errors.
+  std::unique_ptr<AudioOutputIPC> ipc_;
+
+  // Current state (must only be accessed from the IO thread).  See comments for
+  // State enum above.
+  StartupState state_;
+
+  // For UMA stats. May only be accessed on the IO thread.
+  Error had_error_ = kNoError;
+
+  // Last set volume.
+  double volume_ = 1.0;
+
+  // The media session ID used to identify which input device to be started.
+  // Only used by Unified IO.
+  base::UnguessableToken session_id_;
+
+  // ID of hardware output device to be used (provided |session_id_| is zero)
+  const std::string device_id_;
+
+  // If |device_id_| is empty and |session_id_| is not, |matched_device_id_| is
+  // received in OnDeviceAuthorized().
+  std::string matched_device_id_;
+
+  absl::optional<base::UnguessableToken> processing_id_;
+
+  // In order to avoid a race between OnStreamCreated and Stop(), we use this
+  // guard to control stopping and starting the audio thread.
+  base::Lock audio_thread_lock_;
+  std::unique_ptr<AudioOutputDeviceThreadCallback> audio_callback_;
+  std::unique_ptr<AudioDeviceThread> audio_thread_
+      GUARDED_BY(audio_thread_lock_);
+
+  // Temporary hack to ignore OnStreamCreated() due to the user calling Stop()
+  // so we don't start the audio thread pointing to a potentially freed
+  // |callback_|.
+  //
+  // TODO(scherkus): Replace this by changing AudioRendererSink to either accept
+  // the callback via Start(). See http://crbug.com/151051 for details.
+  bool stopping_hack_ GUARDED_BY(audio_thread_lock_);
+
+  base::WaitableEvent did_receive_auth_;
+  AudioParameters output_params_;
+  OutputDeviceStatus device_status_;
+
+  const base::TimeDelta auth_timeout_;
+  std::unique_ptr<base::OneShotTimer> auth_timeout_action_;
+
+  // Pending callback for OutputDeviceInfo if it has not been received by the
+  // time a call to GetGetOutputDeviceInfoAsync() is called.
+  //
+  // Lock for use ONLY with |pending_device_info_cb_| and |did_receive_auth_|,
+  // if you add more usage of this lock ensure you have not added a deadlock.
+  base::Lock device_info_lock_;
+  OutputDeviceInfoCB pending_device_info_cb_ GUARDED_BY(device_info_lock_);
+
+  DISALLOW_COPY_AND_ASSIGN(AudioOutputDevice);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_
diff --git a/third_party/chromium/media/audio/audio_output_device_thread_callback.cc b/third_party/chromium/media/audio/audio_output_device_thread_callback.cc
new file mode 100644
index 0000000..d64d1c1
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_device_thread_callback.cc
@@ -0,0 +1,99 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_device_thread_callback.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/trace_event/trace_event.h"
+
+namespace media {
+
+AudioOutputDeviceThreadCallback::AudioOutputDeviceThreadCallback(
+    const media::AudioParameters& audio_parameters,
+    base::UnsafeSharedMemoryRegion shared_memory_region,
+    media::AudioRendererSink::RenderCallback* render_callback)
+    : media::AudioDeviceThread::Callback(
+          audio_parameters,
+          ComputeAudioOutputBufferSize(audio_parameters),
+          /*segment count*/ 1),
+      shared_memory_region_(std::move(shared_memory_region)),
+      render_callback_(render_callback),
+      callback_num_(0) {
+  // CHECK that the shared memory is large enough. The memory allocated must be
+  // at least as large as expected.
+  CHECK(memory_length_ <= shared_memory_region_.GetSize());
+}
+
+AudioOutputDeviceThreadCallback::~AudioOutputDeviceThreadCallback() = default;
+
+void AudioOutputDeviceThreadCallback::MapSharedMemory() {
+  CHECK_EQ(total_segments_, 1u);
+  shared_memory_mapping_ = shared_memory_region_.MapAt(0, memory_length_);
+  CHECK(shared_memory_mapping_.IsValid());
+
+  media::AudioOutputBuffer* buffer =
+      reinterpret_cast<media::AudioOutputBuffer*>(
+          shared_memory_mapping_.memory());
+  output_bus_ = media::AudioBus::WrapMemory(audio_parameters_, buffer->audio);
+  output_bus_->set_is_bitstream_format(audio_parameters_.IsBitstreamFormat());
+}
+
+// Called whenever we receive notifications about pending data.
+void AudioOutputDeviceThreadCallback::Process(uint32_t control_signal) {
+  callback_num_++;
+
+  // Read and reset the number of frames skipped.
+  media::AudioOutputBuffer* buffer =
+      reinterpret_cast<media::AudioOutputBuffer*>(
+          shared_memory_mapping_.memory());
+  uint32_t frames_skipped = buffer->params.frames_skipped;
+  buffer->params.frames_skipped = 0;
+
+  TRACE_EVENT_BEGIN2("audio", "AudioOutputDevice::FireRenderCallback",
+                     "callback_num", callback_num_, "frames skipped",
+                     frames_skipped);
+
+  base::TimeDelta delay = base::Microseconds(buffer->params.delay_us);
+
+  base::TimeTicks delay_timestamp =
+      base::TimeTicks() + base::Microseconds(buffer->params.delay_timestamp_us);
+
+  DVLOG(4) << __func__ << " delay:" << delay << " delay_timestamp:" << delay
+           << " frames_skipped:" << frames_skipped;
+
+  // When playback starts, we get an immediate callback to Process to make sure
+  // that we have some data, we'll get another one after the device is awake and
+  // ingesting data, which is what we want to track with this trace.
+  if (callback_num_ == 2)
+    TRACE_EVENT_NESTABLE_ASYNC_END0("audio", "StartingPlayback",
+                                    TRACE_ID_LOCAL(this));
+
+  // Update the audio-delay measurement, inform about the number of skipped
+  // frames, and ask client to render audio.  Since |output_bus_| is wrapping
+  // the shared memory the Render() call is writing directly into the shared
+  // memory.
+  render_callback_->Render(delay, delay_timestamp, frames_skipped,
+                           output_bus_.get());
+
+  if (audio_parameters_.IsBitstreamFormat()) {
+    buffer->params.bitstream_data_size = output_bus_->GetBitstreamDataSize();
+    buffer->params.bitstream_frames = output_bus_->GetBitstreamFrames();
+  }
+
+  TRACE_EVENT_END2("audio", "AudioOutputDevice::FireRenderCallback",
+                   "timestamp (ms)",
+                   (delay_timestamp - base::TimeTicks()).InMillisecondsF(),
+                   "delay (ms)", delay.InMillisecondsF());
+}
+
+bool AudioOutputDeviceThreadCallback::CurrentThreadIsAudioDeviceThread() {
+  return thread_checker_.CalledOnValidThread();
+}
+
+void AudioOutputDeviceThreadCallback::InitializePlayStartTime() {}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_output_device_thread_callback.h b/third_party/chromium/media/audio/audio_output_device_thread_callback.h
new file mode 100644
index 0000000..988187d
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_device_thread_callback.h
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_THREAD_CALLBACK_H_
+#define MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_THREAD_CALLBACK_H_
+
+#include <memory>
+
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "media/audio/audio_device_thread.h"
+#include "media/base/audio_renderer_sink.h"
+
+namespace media {
+
+// Takes care of invoking the render callback on the audio thread.
+// An instance of this class is created for each capture stream on output device
+// stream created.
+class MEDIA_EXPORT AudioOutputDeviceThreadCallback
+    : public media::AudioDeviceThread::Callback {
+ public:
+  AudioOutputDeviceThreadCallback(
+      const media::AudioParameters& audio_parameters,
+      base::UnsafeSharedMemoryRegion shared_memory_region,
+      media::AudioRendererSink::RenderCallback* render_callback);
+
+  AudioOutputDeviceThreadCallback(const AudioOutputDeviceThreadCallback&) =
+      delete;
+  AudioOutputDeviceThreadCallback& operator=(
+      const AudioOutputDeviceThreadCallback&) = delete;
+
+  ~AudioOutputDeviceThreadCallback() override;
+
+  void MapSharedMemory() override;
+
+  // Called whenever we receive notifications about pending data.
+  void Process(uint32_t control_signal) override;
+
+  // Returns whether the current thread is the audio device thread or not.
+  // Will always return true if DCHECKs are not enabled.
+  bool CurrentThreadIsAudioDeviceThread();
+
+  // Sets |first_play_start_time_| to the current time unless it's already set,
+  // in which case it's a no-op. The first call to this method MUST have
+  // completed by the time we recieve our first Process() callback to avoid
+  // data races.
+  void InitializePlayStartTime();
+
+ private:
+  base::UnsafeSharedMemoryRegion shared_memory_region_;
+  base::WritableSharedMemoryMapping shared_memory_mapping_;
+  media::AudioRendererSink::RenderCallback* render_callback_;
+  std::unique_ptr<media::AudioBus> output_bus_;
+  uint64_t callback_num_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_THREAD_CALLBACK_H_
diff --git a/third_party/chromium/media/audio/audio_output_device_unittest.cc b/third_party/chromium/media/audio/audio_output_device_unittest.cc
new file mode 100644
index 0000000..0f7db33
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_device_unittest.cc
@@ -0,0 +1,399 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_device.h"
+
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/shared_memory_mapping.h"
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/single_thread_task_runner.h"
+#include "base/sync_socket.h"
+#include "base/task_runner.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::CancelableSyncSocket;
+using base::UnsafeSharedMemoryRegion;
+using base::WritableSharedMemoryMapping;
+using base::SyncSocket;
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+using testing::WithArg;
+using testing::StrictMock;
+using testing::NiceMock;
+using testing::NotNull;
+using testing::Mock;
+
+namespace media {
+
+namespace {
+
+constexpr char kDefaultDeviceId[] = "";
+constexpr char kNonDefaultDeviceId[] = "valid-nondefault-device-id";
+constexpr char kUnauthorizedDeviceId[] = "unauthorized-device-id";
+constexpr base::TimeDelta kAuthTimeout = base::Milliseconds(10000);
+
+class MockRenderCallback : public AudioRendererSink::RenderCallback {
+ public:
+  MockRenderCallback() = default;
+  ~MockRenderCallback() override = default;
+
+  MOCK_METHOD4(Render,
+               int(base::TimeDelta delay,
+                   base::TimeTicks timestamp,
+                   int prior_frames_skipped,
+                   AudioBus* dest));
+  MOCK_METHOD0(OnRenderError, void());
+};
+
+class MockAudioOutputIPC : public AudioOutputIPC {
+ public:
+  MockAudioOutputIPC() = default;
+  ~MockAudioOutputIPC() override = default;
+
+  MOCK_METHOD3(RequestDeviceAuthorization,
+               void(AudioOutputIPCDelegate* delegate,
+                    const base::UnguessableToken& session_id,
+                    const std::string& device_id));
+  MOCK_METHOD3(
+      CreateStream,
+      void(AudioOutputIPCDelegate* delegate,
+           const AudioParameters& params,
+           const absl::optional<base::UnguessableToken>& processing_id));
+  MOCK_METHOD0(PlayStream, void());
+  MOCK_METHOD0(PauseStream, void());
+  MOCK_METHOD0(FlushStream, void());
+  MOCK_METHOD0(CloseStream, void());
+  MOCK_METHOD1(SetVolume, void(double volume));
+};
+
+}  // namespace.
+
+class AudioOutputDeviceTest : public testing::Test {
+ public:
+  AudioOutputDeviceTest();
+
+  AudioOutputDeviceTest(const AudioOutputDeviceTest&) = delete;
+  AudioOutputDeviceTest& operator=(const AudioOutputDeviceTest&) = delete;
+
+  ~AudioOutputDeviceTest() override;
+
+  void ReceiveAuthorization(OutputDeviceStatus device_status);
+  void StartAudioDevice();
+  void CallOnStreamCreated();
+  void StopAudioDevice();
+  void FlushAudioDevice();
+  void CreateDevice(const std::string& device_id,
+                    base::TimeDelta timeout = kAuthTimeout);
+  void SetDevice(const std::string& device_id);
+
+  MOCK_METHOD1(OnDeviceInfoReceived, void(OutputDeviceInfo));
+
+ protected:
+  base::test::TaskEnvironment task_env_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  AudioParameters default_audio_parameters_;
+  StrictMock<MockRenderCallback> callback_;
+  MockAudioOutputIPC* audio_output_ipc_;  // owned by audio_device_
+  scoped_refptr<AudioOutputDevice> audio_device_;
+  OutputDeviceStatus device_status_;
+
+ private:
+  int CalculateMemorySize();
+
+  UnsafeSharedMemoryRegion shared_memory_region_;
+  WritableSharedMemoryMapping shared_memory_mapping_;
+  CancelableSyncSocket browser_socket_;
+  CancelableSyncSocket renderer_socket_;
+};
+
+AudioOutputDeviceTest::AudioOutputDeviceTest()
+    : device_status_(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL) {
+  default_audio_parameters_.Reset(AudioParameters::AUDIO_PCM_LINEAR,
+                                  CHANNEL_LAYOUT_STEREO, 48000, 1024);
+  SetDevice(kDefaultDeviceId);
+}
+
+AudioOutputDeviceTest::~AudioOutputDeviceTest() {
+  audio_device_ = nullptr;
+}
+
+void AudioOutputDeviceTest::CreateDevice(const std::string& device_id,
+                                         base::TimeDelta timeout) {
+  // Make sure the previous device is properly cleaned up.
+  if (audio_device_)
+    StopAudioDevice();
+
+  audio_output_ipc_ = new NiceMock<MockAudioOutputIPC>();
+  audio_device_ = new AudioOutputDevice(
+      base::WrapUnique(audio_output_ipc_), task_env_.GetMainThreadTaskRunner(),
+      AudioSinkParameters(base::UnguessableToken(), device_id), timeout);
+}
+
+void AudioOutputDeviceTest::SetDevice(const std::string& device_id) {
+  CreateDevice(device_id);
+  EXPECT_CALL(*audio_output_ipc_,
+              RequestDeviceAuthorization(audio_device_.get(),
+                                         base::UnguessableToken(), device_id));
+  audio_device_->RequestDeviceAuthorization();
+  task_env_.FastForwardBy(base::TimeDelta());
+
+  // Simulate response from browser
+  OutputDeviceStatus device_status =
+      (device_id == kUnauthorizedDeviceId)
+          ? OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED
+          : OUTPUT_DEVICE_STATUS_OK;
+  ReceiveAuthorization(device_status);
+
+  audio_device_->Initialize(default_audio_parameters_,
+                            &callback_);
+}
+
+void AudioOutputDeviceTest::ReceiveAuthorization(OutputDeviceStatus status) {
+  device_status_ = status;
+  if (device_status_ != OUTPUT_DEVICE_STATUS_OK)
+    EXPECT_CALL(*audio_output_ipc_, CloseStream());
+
+  audio_device_->OnDeviceAuthorized(device_status_, default_audio_parameters_,
+                                    kDefaultDeviceId);
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+void AudioOutputDeviceTest::StartAudioDevice() {
+  if (device_status_ == OUTPUT_DEVICE_STATUS_OK)
+    EXPECT_CALL(*audio_output_ipc_, CreateStream(audio_device_.get(), _, _));
+  else
+    EXPECT_CALL(callback_, OnRenderError());
+
+  audio_device_->Start();
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+void AudioOutputDeviceTest::CallOnStreamCreated() {
+  const uint32_t kMemorySize =
+      ComputeAudioOutputBufferSize(default_audio_parameters_);
+
+  shared_memory_region_ = base::UnsafeSharedMemoryRegion::Create(kMemorySize);
+  ASSERT_TRUE(shared_memory_region_.IsValid());
+  shared_memory_mapping_ = shared_memory_region_.Map();
+  ASSERT_TRUE(shared_memory_mapping_.IsValid());
+  memset(shared_memory_mapping_.memory(), 0xff, kMemorySize);
+
+  ASSERT_TRUE(CancelableSyncSocket::CreatePair(&browser_socket_,
+                                               &renderer_socket_));
+
+  // Create duplicates of the handles we pass to AudioOutputDevice since
+  // ownership will be transferred and AudioOutputDevice is responsible for
+  // freeing.
+  base::UnsafeSharedMemoryRegion duplicated_memory_region =
+      shared_memory_region_.Duplicate();
+  ASSERT_TRUE(duplicated_memory_region.IsValid());
+
+  audio_device_->OnStreamCreated(std::move(duplicated_memory_region),
+                                 renderer_socket_.Take(),
+                                 /*playing_automatically*/ false);
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+void AudioOutputDeviceTest::StopAudioDevice() {
+  if (device_status_ == OUTPUT_DEVICE_STATUS_OK)
+    EXPECT_CALL(*audio_output_ipc_, CloseStream());
+
+  audio_device_->Stop();
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+void AudioOutputDeviceTest::FlushAudioDevice() {
+  if (device_status_ == OUTPUT_DEVICE_STATUS_OK)
+    EXPECT_CALL(*audio_output_ipc_, FlushStream());
+
+  audio_device_->Flush();
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+TEST_F(AudioOutputDeviceTest, Initialize) {
+  // Tests that the object can be constructed, initialized and destructed
+  // without having ever been started.
+  StopAudioDevice();
+}
+
+// Calls Start() followed by an immediate Stop() and check for the basic message
+// filter messages being sent in that case.
+TEST_F(AudioOutputDeviceTest, StartStop) {
+  StartAudioDevice();
+  StopAudioDevice();
+}
+
+// AudioOutputDevice supports multiple start/stop sequences.
+TEST_F(AudioOutputDeviceTest, StartStopStartStop) {
+  StartAudioDevice();
+  StopAudioDevice();
+  StartAudioDevice();
+  StopAudioDevice();
+}
+
+// Simulate receiving OnStreamCreated() prior to processing ShutDownOnIOThread()
+// on the IO loop.
+TEST_F(AudioOutputDeviceTest, StopBeforeRender) {
+  StartAudioDevice();
+
+  // Call Stop() but don't run the IO loop yet.
+  audio_device_->Stop();
+
+  // Expect us to shutdown IPC but not to render anything despite the stream
+  // getting created.
+  EXPECT_CALL(*audio_output_ipc_, CloseStream());
+  CallOnStreamCreated();
+}
+
+// Multiple start/stop with nondefault device
+TEST_F(AudioOutputDeviceTest, NonDefaultStartStopStartStop) {
+  SetDevice(kNonDefaultDeviceId);
+  StartAudioDevice();
+  StopAudioDevice();
+
+  EXPECT_CALL(*audio_output_ipc_,
+              RequestDeviceAuthorization(audio_device_.get(),
+                                         base::UnguessableToken(), _));
+  StartAudioDevice();
+  // Simulate reply from browser
+  ReceiveAuthorization(OUTPUT_DEVICE_STATUS_OK);
+
+  StopAudioDevice();
+}
+
+TEST_F(AudioOutputDeviceTest, UnauthorizedDevice) {
+  SetDevice(kUnauthorizedDeviceId);
+  StartAudioDevice();
+  StopAudioDevice();
+}
+
+TEST_F(AudioOutputDeviceTest,
+       StartUnauthorizedDeviceAndStopBeforeErrorFires_NoError) {
+  SetDevice(kUnauthorizedDeviceId);
+  audio_device_->Start();
+  // Don't run the runloop. We stop before |audio_device| gets the
+  // authorization error, so it's not allowed to dereference |callback_|.
+  EXPECT_CALL(callback_, OnRenderError()).Times(0);
+  StopAudioDevice();
+}
+
+TEST_F(AudioOutputDeviceTest, AuthorizationFailsBeforeInitialize_NoError) {
+  // Clear audio device set by fixture.
+  StopAudioDevice();
+  audio_output_ipc_ = new NiceMock<MockAudioOutputIPC>();
+  audio_device_ = new AudioOutputDevice(
+      base::WrapUnique(audio_output_ipc_), task_env_.GetMainThreadTaskRunner(),
+      AudioSinkParameters(base::UnguessableToken(), kDefaultDeviceId),
+      kAuthTimeout);
+  EXPECT_CALL(
+      *audio_output_ipc_,
+      RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(),
+                                 kDefaultDeviceId));
+
+  audio_device_->RequestDeviceAuthorization();
+  audio_device_->Initialize(default_audio_parameters_, &callback_);
+  task_env_.FastForwardBy(base::TimeDelta());
+  audio_device_->Stop();
+
+  // We've stopped, so accessing |callback_| isn't ok.
+  EXPECT_CALL(callback_, OnRenderError()).Times(0);
+  audio_device_->OnDeviceAuthorized(OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
+                                    default_audio_parameters_,
+                                    kDefaultDeviceId);
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+TEST_F(AudioOutputDeviceTest, AuthorizationTimedOut) {
+  CreateDevice(kNonDefaultDeviceId);
+  EXPECT_CALL(
+      *audio_output_ipc_,
+      RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(),
+                                 kNonDefaultDeviceId));
+  EXPECT_CALL(*audio_output_ipc_, CloseStream());
+
+  // Request authorization; no reply from the browser.
+  audio_device_->RequestDeviceAuthorization();
+
+  // Advance time until we hit the timeout.
+  task_env_.FastForwardUntilNoTasksRemain();
+
+  audio_device_->Stop();
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+TEST_F(AudioOutputDeviceTest, GetOutputDeviceInfoAsync_Error) {
+  CreateDevice(kUnauthorizedDeviceId, base::TimeDelta());
+  EXPECT_CALL(
+      *audio_output_ipc_,
+      RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(),
+                                 kUnauthorizedDeviceId));
+  audio_device_->RequestDeviceAuthorization();
+  audio_device_->GetOutputDeviceInfoAsync(base::BindOnce(
+      &AudioOutputDeviceTest::OnDeviceInfoReceived, base::Unretained(this)));
+  task_env_.FastForwardBy(base::TimeDelta());
+
+  OutputDeviceInfo info;
+  constexpr auto kExpectedStatus = OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED;
+  EXPECT_CALL(*this, OnDeviceInfoReceived(_))
+      .WillOnce(testing::SaveArg<0>(&info));
+  ReceiveAuthorization(kExpectedStatus);
+
+  task_env_.FastForwardUntilNoTasksRemain();
+  EXPECT_EQ(kExpectedStatus, info.device_status());
+  EXPECT_EQ(kUnauthorizedDeviceId, info.device_id());
+  EXPECT_TRUE(
+      AudioParameters::UnavailableDeviceParams().Equals(info.output_params()));
+
+  audio_device_->Stop();
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+TEST_F(AudioOutputDeviceTest, GetOutputDeviceInfoAsync_Okay) {
+  CreateDevice(kDefaultDeviceId, base::TimeDelta());
+  EXPECT_CALL(
+      *audio_output_ipc_,
+      RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(),
+                                 kDefaultDeviceId));
+  audio_device_->RequestDeviceAuthorization();
+  audio_device_->GetOutputDeviceInfoAsync(base::BindOnce(
+      &AudioOutputDeviceTest::OnDeviceInfoReceived, base::Unretained(this)));
+  task_env_.FastForwardBy(base::TimeDelta());
+
+  OutputDeviceInfo info;
+  constexpr auto kExpectedStatus = OUTPUT_DEVICE_STATUS_OK;
+  EXPECT_CALL(*this, OnDeviceInfoReceived(_))
+      .WillOnce(testing::SaveArg<0>(&info));
+  ReceiveAuthorization(kExpectedStatus);
+
+  task_env_.FastForwardUntilNoTasksRemain();
+  EXPECT_EQ(kExpectedStatus, info.device_status());
+  EXPECT_EQ(kDefaultDeviceId, info.device_id());
+  EXPECT_TRUE(default_audio_parameters_.Equals(info.output_params()));
+
+  audio_device_->Stop();
+  task_env_.FastForwardBy(base::TimeDelta());
+}
+
+TEST_F(AudioOutputDeviceTest, StreamIsFlushed) {
+  StartAudioDevice();
+  FlushAudioDevice();
+  StopAudioDevice();
+}
+
+}  // namespace media.
diff --git a/third_party/chromium/media/audio/audio_output_dispatcher.cc b/third_party/chromium/media/audio/audio_output_dispatcher.cc
new file mode 100644
index 0000000..0e50642
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_dispatcher.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_dispatcher.h"
+
+#include "base/single_thread_task_runner.h"
+#include "media/audio/audio_manager.h"
+
+namespace media {
+
+AudioOutputDispatcher::AudioOutputDispatcher(AudioManager* audio_manager)
+    : audio_manager_(audio_manager) {
+  DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread());
+}
+
+AudioOutputDispatcher::~AudioOutputDispatcher() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_output_dispatcher.h b/third_party/chromium/media/audio/audio_output_dispatcher.h
new file mode 100644
index 0000000..a6aaaf8
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_dispatcher.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// AudioOutputDispatcher is a single-threaded base class that dispatches
+// creation and deletion of audio output streams. AudioOutputProxy objects use
+// this class to allocate and recycle actual audio output streams. When playback
+// is started, the proxy calls StartStream() to get an output stream that it
+// uses to play audio. When playback is stopped, the proxy returns the stream
+// back to the dispatcher by calling StopStream().
+//
+// AudioManagerBase creates one specialization of AudioOutputDispatcher on the
+// audio thread for each possible set of audio parameters. I.e streams with
+// different parameters are managed independently.  The AudioOutputDispatcher
+// instance is then deleted on the audio thread when the AudioManager shuts
+// down.
+
+#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_H_
+#define MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "media/audio/audio_io.h"
+
+namespace media {
+class AudioManager;
+class AudioOutputProxy;
+
+// Lives and must be called on AudioManager device thread.
+class MEDIA_EXPORT AudioOutputDispatcher {
+ public:
+  AudioOutputDispatcher(AudioManager* audio_manager);
+
+  AudioOutputDispatcher(const AudioOutputDispatcher&) = delete;
+  AudioOutputDispatcher& operator=(const AudioOutputDispatcher&) = delete;
+
+  virtual ~AudioOutputDispatcher();
+
+  // Creates an instance of AudioOutputProxy, which uses |this| as dispatcher.
+  // The client owns the returned pointer, which can be deleted using
+  // AudioOutputProxy::Close.
+  virtual AudioOutputProxy* CreateStreamProxy() = 0;
+
+  // Called by AudioOutputProxy to open the stream.
+  // Returns false, if it fails to open it.
+  virtual bool OpenStream() = 0;
+
+  // Called by AudioOutputProxy when the stream is started.
+  // Uses |callback| to get source data and report errors, if any.
+  // Does *not* take ownership of this callback.
+  // Returns true if started successfully, false otherwise.
+  virtual bool StartStream(AudioOutputStream::AudioSourceCallback* callback,
+                           AudioOutputProxy* stream_proxy) = 0;
+
+  // Called by AudioOutputProxy when the stream is stopped.
+  // Ownership of the |stream_proxy| is passed to the dispatcher.
+  virtual void StopStream(AudioOutputProxy* stream_proxy) = 0;
+
+  // Called by AudioOutputProxy when the volume is set.
+  virtual void StreamVolumeSet(AudioOutputProxy* stream_proxy,
+                               double volume) = 0;
+
+  // Called by AudioOutputProxy when the stream is closed.
+  virtual void CloseStream(AudioOutputProxy* stream_proxy) = 0;
+
+  // Called by AudioOutputProxy to flush the stream.  This should only be
+  // called when a stream is stopped.
+  virtual void FlushStream(AudioOutputProxy* stream_proxy) = 0;
+
+ protected:
+  AudioManager* audio_manager() const { return audio_manager_; }
+
+ private:
+  // A no-reference-held pointer (we don't want circular references) back to the
+  // AudioManager that owns this object.
+  AudioManager* const audio_manager_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_H_
diff --git a/third_party/chromium/media/audio/audio_output_dispatcher_impl.cc b/third_party/chromium/media/audio/audio_output_dispatcher_impl.cc
new file mode 100644
index 0000000..a8c428c
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_dispatcher_impl.cc
@@ -0,0 +1,210 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_dispatcher_impl.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/containers/contains.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "media/audio/audio_logging.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_output_proxy.h"
+
+namespace media {
+
+AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
+    AudioManager* audio_manager,
+    const AudioParameters& params,
+    const std::string& output_device_id,
+    base::TimeDelta close_delay)
+    : AudioOutputDispatcher(audio_manager),
+      params_(params),
+      device_id_(output_device_id),
+      idle_proxies_(0),
+      close_timer_(FROM_HERE,
+                   close_delay,
+                   this,
+                   &AudioOutputDispatcherImpl::CloseAllIdleStreams),
+      audio_stream_id_(0) {
+  DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread());
+  audio_manager->AddOutputDeviceChangeListener(this);
+}
+
+AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+
+  // Stop all active streams.
+  for (auto& iter : proxy_to_physical_map_) {
+    StopPhysicalStream(iter.second);
+  }
+
+  // Close all idle streams immediately.  The |close_timer_| will handle
+  // invalidating any outstanding tasks upon its destruction.
+  CloseAllIdleStreams();
+
+  audio_manager()->RemoveOutputDeviceChangeListener(this);
+
+  // All idle physical streams must have been closed during shutdown.
+  CHECK(idle_streams_.empty());
+}
+
+AudioOutputProxy* AudioOutputDispatcherImpl::CreateStreamProxy() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  return new AudioOutputProxy(weak_factory_.GetWeakPtr());
+}
+
+bool AudioOutputDispatcherImpl::OpenStream() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+
+  // Ensure that there is at least one open stream.
+  if (idle_streams_.empty() && !CreateAndOpenStream())
+    return false;
+
+  ++idle_proxies_;
+  close_timer_.Reset();
+  return true;
+}
+
+bool AudioOutputDispatcherImpl::StartStream(
+    AudioOutputStream::AudioSourceCallback* callback,
+    AudioOutputProxy* stream_proxy) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(proxy_to_physical_map_.find(stream_proxy) ==
+         proxy_to_physical_map_.end());
+
+  if (idle_streams_.empty() && !CreateAndOpenStream())
+    return false;
+
+  AudioOutputStream* physical_stream = idle_streams_.back();
+  idle_streams_.pop_back();
+
+  DCHECK_GT(idle_proxies_, 0u);
+  --idle_proxies_;
+
+  double volume = 0;
+  stream_proxy->GetVolume(&volume);
+  physical_stream->SetVolume(volume);
+  DCHECK(base::Contains(audio_logs_, physical_stream));
+  AudioLog* const audio_log = audio_logs_[physical_stream].get();
+  audio_log->OnSetVolume(volume);
+  physical_stream->Start(callback);
+  audio_log->OnStarted();
+  proxy_to_physical_map_[stream_proxy] = physical_stream;
+
+  close_timer_.Reset();
+  return true;
+}
+
+void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  auto it = proxy_to_physical_map_.find(stream_proxy);
+  DCHECK(it != proxy_to_physical_map_.end());
+  StopPhysicalStream(it->second);
+  proxy_to_physical_map_.erase(it);
+  ++idle_proxies_;
+}
+
+void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy,
+                                                double volume) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  auto it = proxy_to_physical_map_.find(stream_proxy);
+  if (it != proxy_to_physical_map_.end()) {
+    AudioOutputStream* physical_stream = it->second;
+    physical_stream->SetVolume(volume);
+    DCHECK(base::Contains(audio_logs_, physical_stream));
+    audio_logs_[physical_stream]->OnSetVolume(volume);
+  }
+}
+
+void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK_GT(idle_proxies_, 0u);
+  --idle_proxies_;
+
+  // Leave at least a single stream running until the close timer fires to help
+  // cycle time when streams are opened and closed repeatedly.
+  CloseIdleStreams(std::max(idle_proxies_, static_cast<size_t>(1)));
+  close_timer_.Reset();
+}
+
+// There is nothing to flush since the phsyical stream is removed during
+// StopStream().
+void AudioOutputDispatcherImpl::FlushStream(AudioOutputProxy* stream_proxy) {}
+
+void AudioOutputDispatcherImpl::OnDeviceChange() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+
+  // We don't want to end up reusing streams which were opened for the wrong
+  // default device. We need to post this task so it runs after device changes
+  // have been sent to all listeners and they've had time to close streams.
+  audio_manager()->GetTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputDispatcherImpl::CloseAllIdleStreams,
+                                weak_factory_.GetWeakPtr()));
+}
+
+bool AudioOutputDispatcherImpl::HasOutputProxies() const {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  return idle_proxies_ || !proxy_to_physical_map_.empty();
+}
+
+bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  const int stream_id = audio_stream_id_++;
+  std::unique_ptr<AudioLog> audio_log = audio_manager()->CreateAudioLog(
+      AudioLogFactory::AUDIO_OUTPUT_STREAM, stream_id);
+  AudioOutputStream* stream = audio_manager()->MakeAudioOutputStream(
+      params_, device_id_,
+      base::BindRepeating(&AudioLog::OnLogMessage,
+                          base::Unretained(audio_log.get())));
+  if (!stream)
+    return false;
+
+  if (!stream->Open()) {
+    stream->Close();
+    return false;
+  }
+
+  audio_log->OnCreated(params_, device_id_);
+  audio_logs_[stream] = std::move(audio_log);
+
+  idle_streams_.push_back(stream);
+  return true;
+}
+
+void AudioOutputDispatcherImpl::CloseAllIdleStreams() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  CloseIdleStreams(0);
+}
+
+void AudioOutputDispatcherImpl::CloseIdleStreams(size_t keep_alive) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  if (idle_streams_.size() <= keep_alive)
+    return;
+  for (size_t i = keep_alive; i < idle_streams_.size(); ++i) {
+    AudioOutputStream* stream = idle_streams_[i];
+    stream->Close();
+
+    auto it = audio_logs_.find(stream);
+    DCHECK(it != audio_logs_.end());
+    it->second->OnClosed();
+    audio_logs_.erase(it);
+  }
+  idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end());
+}
+
+void AudioOutputDispatcherImpl::StopPhysicalStream(AudioOutputStream* stream) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  stream->Stop();
+  DCHECK(base::Contains(audio_logs_, stream));
+  audio_logs_[stream]->OnStopped();
+  idle_streams_.push_back(stream);
+  close_timer_.Reset();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_output_dispatcher_impl.h b/third_party/chromium/media/audio/audio_output_dispatcher_impl.h
new file mode 100644
index 0000000..6d0f6ea
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_dispatcher_impl.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// AudioOutputDispatcherImpl is an implementation of AudioOutputDispatcher.
+//
+// To avoid opening and closing audio devices more frequently than necessary,
+// each dispatcher has a pool of inactive physical streams. A stream is closed
+// only if it hasn't been used for a certain period of time (specified via the
+// constructor).
+//
+
+#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_
+#define MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_output_dispatcher.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+class AudioLog;
+
+class MEDIA_EXPORT AudioOutputDispatcherImpl
+    : public AudioOutputDispatcher,
+      public AudioManager::AudioDeviceListener {
+ public:
+  // |close_delay| specifies delay after the stream is idle until the audio
+  // device is closed.
+  AudioOutputDispatcherImpl(AudioManager* audio_manager,
+                            const AudioParameters& params,
+                            const std::string& output_device_id,
+                            base::TimeDelta close_delay);
+
+  AudioOutputDispatcherImpl(const AudioOutputDispatcherImpl&) = delete;
+  AudioOutputDispatcherImpl& operator=(const AudioOutputDispatcherImpl&) =
+      delete;
+
+  ~AudioOutputDispatcherImpl() override;
+
+  // AudioOutputDispatcher implementation.
+  AudioOutputProxy* CreateStreamProxy() override;
+  bool OpenStream() override;
+  bool StartStream(AudioOutputStream::AudioSourceCallback* callback,
+                   AudioOutputProxy* stream_proxy) override;
+  void StopStream(AudioOutputProxy* stream_proxy) override;
+  void StreamVolumeSet(AudioOutputProxy* stream_proxy, double volume) override;
+  void CloseStream(AudioOutputProxy* stream_proxy) override;
+  void FlushStream(AudioOutputProxy* stream_proxy) override;
+
+  // AudioDeviceListener implementation.
+  void OnDeviceChange() override;
+
+  // Returns true if there are any open AudioOutputProxy objects.
+  bool HasOutputProxies() const;
+
+  // Closes all |idle_streams_|.
+  void CloseAllIdleStreams();
+
+ private:
+  // Creates a new physical output stream, opens it and pushes to
+  // |idle_streams_|.  Returns false if the stream couldn't be created or
+  // opened.
+  bool CreateAndOpenStream();
+
+  // Similar to CloseAllIdleStreams(), but keeps |keep_alive| streams alive.
+  void CloseIdleStreams(size_t keep_alive);
+
+  void StopPhysicalStream(AudioOutputStream* stream);
+
+  // Output parameters.
+  const AudioParameters params_;
+
+  // Output device id.
+  const std::string device_id_;
+
+  size_t idle_proxies_;
+  std::vector<AudioOutputStream*> idle_streams_;
+
+  // When streams are stopped they're added to |idle_streams_|, if no stream is
+  // reused before |close_delay_| elapses |close_timer_| will run
+  // CloseIdleStreams().
+  base::DelayTimer close_timer_;
+
+  typedef base::flat_map<AudioOutputProxy*, AudioOutputStream*> AudioStreamMap;
+  AudioStreamMap proxy_to_physical_map_;
+
+  using AudioLogMap =
+      base::flat_map<AudioOutputStream*, std::unique_ptr<media::AudioLog>>;
+  AudioLogMap audio_logs_;
+  int audio_stream_id_;
+
+  base::WeakPtrFactory<AudioOutputDispatcherImpl> weak_factory_{this};
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_DISPATCHER_IMPL_H_
diff --git a/third_party/chromium/media/audio/audio_output_ipc.cc b/third_party/chromium/media/audio/audio_output_ipc.cc
new file mode 100644
index 0000000..eb07d30
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_ipc.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_ipc.h"
+
+namespace media {
+
+AudioOutputIPCDelegate::~AudioOutputIPCDelegate() = default;
+
+AudioOutputIPC::~AudioOutputIPC() = default;
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_output_ipc.h b/third_party/chromium/media/audio/audio_output_ipc.h
new file mode 100644
index 0000000..a826727
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_ipc.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_IPC_H_
+#define MEDIA_AUDIO_AUDIO_OUTPUT_IPC_H_
+
+#include <string>
+
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/sync_socket.h"
+#include "base/unguessable_token.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+#include "media/base/output_device_info.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+// Contains IPC notifications for the state of the server side
+// (AudioOutputController) audio state changes and when an AudioOutputController
+// has been created.  Implemented by AudioOutputDevice.
+class MEDIA_EXPORT AudioOutputIPCDelegate {
+ public:
+  // Called when state of an audio stream has changed.
+  virtual void OnError() = 0;
+
+  // Called when an authorization request for an output device has been
+  // completed. The AudioOutputIPCDelegate will delete the AudioOutputIPC, if
+  // |device_status| is not OUTPUT_DEVICE_STATUS_OK.
+  virtual void OnDeviceAuthorized(OutputDeviceStatus device_status,
+                                  const media::AudioParameters& output_params,
+                                  const std::string& matched_device_id) = 0;
+
+  // Called when an audio stream has been created.
+  // See media/mojo/mojom/audio_data_pipe.mojom for documentation of
+  // |handle| and |socket_handle|. |playing_automatically| indicates if the
+  // AudioOutputIPCDelegate is playing right away due to an earlier call to
+  // Play();
+  virtual void OnStreamCreated(
+      base::UnsafeSharedMemoryRegion shared_memory_region,
+      base::SyncSocket::ScopedHandle socket_handle,
+      bool playing_automatically) = 0;
+
+  // Called when the AudioOutputIPC object is going away and/or when the IPC
+  // channel has been closed and no more ipc requests can be made.
+  // Implementations should delete their owned AudioOutputIPC instance
+  // immediately.
+  virtual void OnIPCClosed() = 0;
+
+ protected:
+  virtual ~AudioOutputIPCDelegate();
+};
+
+// Provides the IPC functionality for an AudioOutputIPCDelegate (e.g., an
+// AudioOutputDevice).  The implementation should asynchronously deliver the
+// messages to an AudioOutputController object (or create one in the case of
+// CreateStream()), that may live in a separate process.
+class MEDIA_EXPORT AudioOutputIPC {
+ public:
+  virtual ~AudioOutputIPC();
+
+  // Sends a request to authorize the use of a specific audio output device
+  // in the peer process.
+  // If |device_id| is nonempty, the browser selects the device
+  // indicated by |device_id|, regardless of the value of |session_id|.
+  // If |device_id| is empty and |session_id| is nonzero, the browser selects
+  // the output device associated with an opened input device indicated by
+  // |session_id|. If no such device is found, the default device will be
+  // selected.
+  // If |device_id| is empty and |session_id| is zero, the browser selects
+  // the default device.
+  // Once the authorization process is complete, the implementation will
+  // notify |delegate| by calling OnDeviceAuthorized().
+  virtual void RequestDeviceAuthorization(
+      AudioOutputIPCDelegate* delegate,
+      const base::UnguessableToken& session_id,
+      const std::string& device_id) = 0;
+
+  // Sends a request to create an AudioOutputController object in the peer
+  // process and configures it to use the specified audio |params| including
+  // number of synchronized input channels.
+  // If no authorization for an output device has been previously requested,
+  // the default device will be used.
+  // Once the stream has been created, the implementation will notify
+  // |delegate| by calling OnStreamCreated().
+  virtual void CreateStream(
+      AudioOutputIPCDelegate* delegate,
+      const AudioParameters& params,
+      const absl::optional<base::UnguessableToken>& processing_id) = 0;
+
+  // Starts playing the stream.  This should generate a call to
+  // AudioOutputController::Play().
+  virtual void PlayStream() = 0;
+
+  // Pauses an audio stream.  This should generate a call to
+  // AudioOutputController::Pause().
+  virtual void PauseStream() = 0;
+
+  // Flushes an audio stream. This should only be called when the stream is
+  // paused.
+  virtual void FlushStream() = 0;
+
+  // Closes the audio stream which should shut down the corresponding
+  // AudioOutputController in the peer process. Usage of an AudioOutputIPC must
+  // always end with a call to CloseStream(), and the |delegate| passed to other
+  // method must remain valid until then. An exception is if OnIPCClosed is
+  // called first.
+  virtual void CloseStream() = 0;
+
+  // Sets the volume of the audio stream.
+  virtual void SetVolume(double volume) = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_IPC_H_
diff --git a/third_party/chromium/media/audio/audio_output_proxy.cc b/third_party/chromium/media/audio/audio_output_proxy.cc
new file mode 100644
index 0000000..d205d67
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_proxy.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_proxy.h"
+
+#include "base/check_op.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_output_dispatcher.h"
+
+namespace media {
+
+AudioOutputProxy::AudioOutputProxy(
+    base::WeakPtr<AudioOutputDispatcher> dispatcher)
+    : dispatcher_(std::move(dispatcher)), state_(kCreated), volume_(1.0) {
+  DCHECK(dispatcher_);
+}
+
+AudioOutputProxy::~AudioOutputProxy() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(state_ == kCreated || state_ == kClosed) << "State is: " << state_;
+}
+
+bool AudioOutputProxy::Open() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(state_, kCreated);
+
+  if (!dispatcher_ || !dispatcher_->OpenStream()) {
+    state_ = kOpenError;
+    return false;
+  }
+
+  state_ = kOpened;
+  return true;
+}
+
+void AudioOutputProxy::Start(AudioSourceCallback* callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // We need to support both states since the callback may not handle OnError()
+  // immediately (or at all).  It's also possible for subsequent StartStream()
+  // calls to succeed after failing, so we allow it to be called again.
+  DCHECK(state_ == kOpened || state_ == kStartError);
+
+  if (!dispatcher_ || !dispatcher_->StartStream(callback, this)) {
+    state_ = kStartError;
+    callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+    return;
+  }
+  state_ = kPlaying;
+}
+
+void AudioOutputProxy::Stop() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (state_ != kPlaying)
+    return;
+
+  if (dispatcher_)
+    dispatcher_->StopStream(this);
+  state_ = kOpened;
+}
+
+void AudioOutputProxy::SetVolume(double volume) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  volume_ = volume;
+
+  if (dispatcher_)
+    dispatcher_->StreamVolumeSet(this, volume);
+}
+
+void AudioOutputProxy::GetVolume(double* volume) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  *volume = volume_;
+}
+
+void AudioOutputProxy::Close() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(state_ == kCreated || state_ == kOpenError || state_ == kOpened ||
+         state_ == kStartError);
+
+  // kStartError means OpenStream() succeeded and the stream must be closed
+  // before destruction.
+  if (state_ != kCreated && state_ != kOpenError && dispatcher_)
+    dispatcher_->CloseStream(this);
+
+  state_ = kClosed;
+
+  // Delete the object now like is done in the Close() implementation of
+  // physical stream objects.  If we delete the object via DeleteSoon, we
+  // unnecessarily complicate the Shutdown procedure of the
+  // dispatcher+audio manager.
+  delete this;
+}
+
+void AudioOutputProxy::Flush() {
+  DCHECK(state_ != kPlaying);
+
+  if (dispatcher_)
+    dispatcher_->FlushStream(this);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_output_proxy.h b/third_party/chromium/media/audio/audio_output_proxy.h
new file mode 100644
index 0000000..cf6b13d
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_proxy.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_PROXY_H_
+#define MEDIA_AUDIO_AUDIO_OUTPUT_PROXY_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioOutputDispatcher;
+
+// AudioOutputProxy is an audio output stream that uses resources more
+// efficiently than a regular audio output stream: it opens audio
+// device only when sound is playing, i.e. between Start() and Stop()
+// (there is still one physical stream per each audio output proxy in
+// playing state).
+//
+// AudioOutputProxy uses AudioOutputDispatcher to open and close
+// physical output streams.
+class MEDIA_EXPORT AudioOutputProxy : public AudioOutputStream {
+ public:
+  // Caller keeps ownership of |dispatcher|.
+  explicit AudioOutputProxy(base::WeakPtr<AudioOutputDispatcher> dispatcher);
+
+  // AudioOutputStream interface.
+  bool Open() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+  void Close() override;
+  void Flush() override;
+
+  AudioOutputDispatcher* get_dispatcher_for_testing() const {
+    return dispatcher_.get();
+  }
+
+ private:
+  enum State {
+    kCreated,
+    kOpened,
+    kPlaying,
+    kClosed,
+    kOpenError,
+    kStartError,
+  };
+
+  ~AudioOutputProxy() override;
+
+  base::WeakPtr<AudioOutputDispatcher> dispatcher_;
+  State state_;
+
+  // Need to save volume here, so that we can restore it in case the stream
+  // is stopped, and then started again.
+  double volume_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(AudioOutputProxy);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_PROXY_H_
diff --git a/third_party/chromium/media/audio/audio_output_proxy_unittest.cc b/third_party/chromium/media/audio/audio_output_proxy_unittest.cc
new file mode 100644
index 0000000..2ec7ae3
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_proxy_unittest.cc
@@ -0,0 +1,873 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/audio_output_dispatcher_impl.h"
+#include "media/audio/audio_output_proxy.h"
+#include "media/audio/audio_output_resampler.h"
+#include "media/audio/fake_audio_log_factory.h"
+#include "media/audio/fake_audio_output_stream.h"
+#include "media/audio/test_audio_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::DoAll;
+using ::testing::Field;
+using ::testing::Mock;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::SetArrayArgument;
+using media::AudioBus;
+using media::AudioInputStream;
+using media::AudioManager;
+using media::AudioManagerBase;
+using media::AudioOutputDispatcher;
+using media::AudioOutputProxy;
+using media::AudioOutputStream;
+using media::AudioParameters;
+using media::FakeAudioOutputStream;
+using media::TestAudioThread;
+
+namespace {
+
+static const int kTestCloseDelayMs = 10;
+
+// Delay between callbacks to AudioSourceCallback::OnMoreData.
+static const int kOnMoreDataCallbackDelayMs = 10;
+
+// Let start run long enough for many OnMoreData callbacks to occur.
+static const int kStartRunTimeMs = kOnMoreDataCallbackDelayMs * 10;
+
+// Dummy function.
+std::unique_ptr<media::AudioDebugRecorder> RegisterDebugRecording(
+    const media::AudioParameters& params) {
+  return nullptr;
+}
+
+class MockAudioOutputStream : public AudioOutputStream {
+ public:
+  MockAudioOutputStream(AudioManagerBase* manager,
+                        const AudioParameters& params)
+      : start_called_(false),
+        stop_called_(false),
+        params_(params),
+        fake_output_stream_(
+            FakeAudioOutputStream::MakeFakeStream(manager, params_)) {
+  }
+
+  void Start(AudioSourceCallback* callback) override {
+    start_called_ = true;
+    fake_output_stream_->Start(callback);
+  }
+
+  void Stop() override {
+    stop_called_ = true;
+    fake_output_stream_->Stop();
+  }
+
+  ~MockAudioOutputStream() override = default;
+
+  bool start_called() { return start_called_; }
+  bool stop_called() { return stop_called_; }
+
+  MOCK_METHOD0(Open, bool());
+  MOCK_METHOD1(SetVolume, void(double volume));
+  MOCK_METHOD1(GetVolume, void(double* volume));
+  MOCK_METHOD0(Close, void());
+  MOCK_METHOD0(Flush, void());
+
+ private:
+  bool start_called_;
+  bool stop_called_;
+  AudioParameters params_;
+  std::unique_ptr<AudioOutputStream> fake_output_stream_;
+};
+
+class MockAudioManager : public AudioManagerBase {
+ public:
+  MockAudioManager()
+      : AudioManagerBase(std::make_unique<TestAudioThread>(),
+                         &fake_audio_log_factory_) {}
+  ~MockAudioManager() override { Shutdown(); }
+
+  MOCK_METHOD3(MakeAudioOutputStream,
+               AudioOutputStream*(const AudioParameters& params,
+                                  const std::string& device_id,
+                                  const LogCallback& log_callback));
+  MOCK_METHOD2(MakeAudioOutputStreamProxy, AudioOutputStream*(
+      const AudioParameters& params,
+      const std::string& device_id));
+  MOCK_METHOD3(MakeAudioInputStream,
+               AudioInputStream*(const AudioParameters& params,
+                                 const std::string& device_id,
+                                 const LogCallback& log_callback));
+  MOCK_METHOD0(GetTaskRunner, scoped_refptr<base::SingleThreadTaskRunner>());
+  MOCK_METHOD0(GetWorkerTaskRunner,
+               scoped_refptr<base::SingleThreadTaskRunner>());
+  MOCK_METHOD0(GetName, const char*());
+
+  MOCK_METHOD2(MakeLinearOutputStream,
+               AudioOutputStream*(const AudioParameters& params,
+                                  const LogCallback& log_callback));
+  MOCK_METHOD3(MakeLowLatencyOutputStream,
+               AudioOutputStream*(const AudioParameters& params,
+                                  const std::string& device_id,
+                                  const LogCallback& log_callback));
+  MOCK_METHOD3(MakeLinearInputStream,
+               AudioInputStream*(const AudioParameters& params,
+                                 const std::string& device_id,
+                                 const LogCallback& log_callback));
+  MOCK_METHOD3(MakeLowLatencyInputStream,
+               AudioInputStream*(const AudioParameters& params,
+                                 const std::string& device_id,
+                                 const LogCallback& log_callback));
+
+ protected:
+  MOCK_METHOD0(HasAudioOutputDevices, bool());
+  MOCK_METHOD0(HasAudioInputDevices, bool());
+  MOCK_METHOD1(GetAudioInputDeviceNames,
+               void(media::AudioDeviceNames* device_name));
+  MOCK_METHOD2(GetPreferredOutputStreamParameters, AudioParameters(
+      const std::string& device_id, const AudioParameters& params));
+
+ private:
+  media::FakeAudioLogFactory fake_audio_log_factory_;
+};
+
+class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
+ public:
+  int OnMoreData(base::TimeDelta /* delay */,
+                 base::TimeTicks /* delay_timestamp */,
+                 int /* prior_frames_skipped */,
+                 AudioBus* dest) override {
+    dest->Zero();
+    return dest->frames();
+  }
+  MOCK_METHOD1(OnError, void(ErrorType));
+};
+
+}  // namespace
+
+namespace media {
+
+class AudioOutputProxyTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    // Use a low sample rate and large buffer size when testing otherwise the
+    // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e.,
+    // RunUntilIdle() will never terminate.
+    params_ = AudioParameters(AudioParameters::AUDIO_PCM_LINEAR,
+                              CHANNEL_LAYOUT_STEREO, 8000, 2048);
+    InitDispatcher(base::Milliseconds(kTestCloseDelayMs));
+  }
+
+  void TearDown() override {
+    // This is necessary to free all proxy objects that have been
+    // closed by the test.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  virtual void InitDispatcher(base::TimeDelta close_delay) {
+    dispatcher_impl_ = std::make_unique<AudioOutputDispatcherImpl>(
+        &manager(), params_, std::string(), close_delay);
+  }
+
+  virtual void OnStart() {}
+
+  MockAudioManager& manager() {
+    return manager_;
+  }
+
+  void WaitForCloseTimer(MockAudioOutputStream* stream) {
+    base::RunLoop run_loop;
+    EXPECT_CALL(*stream, Close())
+        .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+    run_loop.Run();
+  }
+
+  void CloseAndWaitForCloseTimer(AudioOutputProxy* proxy,
+                                 MockAudioOutputStream* stream) {
+    // Close the stream and verify it doesn't happen immediately.
+    proxy->Close();
+    Mock::VerifyAndClear(stream);
+
+    // Wait for the actual close event to come from the close timer.
+    WaitForCloseTimer(stream);
+  }
+
+  // Basic Open() and Close() test.
+  void OpenAndClose(AudioOutputDispatcher* dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open())
+        .WillOnce(Return(true));
+
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy->Open());
+    CloseAndWaitForCloseTimer(proxy, &stream);
+  }
+
+  // Creates a stream, and then calls Start() and Stop().
+  void StartAndStop(AudioOutputDispatcher* dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open())
+        .WillOnce(Return(true));
+    EXPECT_CALL(stream, SetVolume(_))
+        .Times(1);
+
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy->Open());
+
+    proxy->Start(&callback_);
+    OnStart();
+    proxy->Stop();
+
+    CloseAndWaitForCloseTimer(proxy, &stream);
+    EXPECT_TRUE(stream.stop_called());
+    EXPECT_TRUE(stream.start_called());
+  }
+
+  // Verify that the stream is closed after Stop() is called.
+  void CloseAfterStop(AudioOutputDispatcher* dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open())
+        .WillOnce(Return(true));
+    EXPECT_CALL(stream, SetVolume(_))
+        .Times(1);
+
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy->Open());
+
+    proxy->Start(&callback_);
+    OnStart();
+    proxy->Stop();
+
+    // Wait for the close timer to fire after StopStream().
+    WaitForCloseTimer(&stream);
+    proxy->Close();
+    EXPECT_TRUE(stream.stop_called());
+    EXPECT_TRUE(stream.start_called());
+  }
+
+  // Create two streams, but don't start them.  Only one device must be opened.
+  void TwoStreams(AudioOutputDispatcher* dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open())
+        .WillOnce(Return(true));
+
+    AudioOutputProxy* proxy1 = dispatcher->CreateStreamProxy();
+    AudioOutputProxy* proxy2 = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy1->Open());
+    EXPECT_TRUE(proxy2->Open());
+    proxy1->Close();
+    CloseAndWaitForCloseTimer(proxy2, &stream);
+    EXPECT_FALSE(stream.stop_called());
+    EXPECT_FALSE(stream.start_called());
+  }
+
+  // Open() method failed.
+  void OpenFailed(AudioOutputDispatcher* dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open())
+        .WillOnce(Return(false));
+    EXPECT_CALL(stream, Close())
+        .Times(1);
+
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_FALSE(proxy->Open());
+    proxy->Close();
+    EXPECT_FALSE(stream.stop_called());
+    EXPECT_FALSE(stream.start_called());
+  }
+
+  void CreateAndWait(AudioOutputDispatcher* dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open())
+        .WillOnce(Return(true));
+
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy->Open());
+
+    WaitForCloseTimer(&stream);
+    proxy->Close();
+    EXPECT_FALSE(stream.stop_called());
+    EXPECT_FALSE(stream.start_called());
+  }
+
+  void OneStream_TwoPlays(AudioOutputDispatcher* dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+
+    EXPECT_CALL(stream, Open())
+        .WillOnce(Return(true));
+    EXPECT_CALL(stream, SetVolume(_))
+        .Times(2);
+
+    AudioOutputProxy* proxy1 = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy1->Open());
+
+    proxy1->Start(&callback_);
+    OnStart();
+    proxy1->Stop();
+
+    // The stream should now be idle and get reused by |proxy2|.
+    AudioOutputProxy* proxy2 = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy2->Open());
+    proxy2->Start(&callback_);
+    OnStart();
+    proxy2->Stop();
+
+    proxy1->Close();
+    CloseAndWaitForCloseTimer(proxy2, &stream);
+    EXPECT_TRUE(stream.stop_called());
+    EXPECT_TRUE(stream.start_called());
+  }
+
+  void TwoStreams_BothPlaying(AudioOutputDispatcher* dispatcher) {
+    MockAudioOutputStream stream1(&manager_, params_);
+    MockAudioOutputStream stream2(&manager_, params_);
+
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream1))
+        .WillOnce(Return(&stream2));
+
+    EXPECT_CALL(stream1, Open())
+        .WillOnce(Return(true));
+    EXPECT_CALL(stream1, SetVolume(_))
+        .Times(1);
+
+    EXPECT_CALL(stream2, Open())
+        .WillOnce(Return(true));
+    EXPECT_CALL(stream2, SetVolume(_))
+        .Times(1);
+
+    AudioOutputProxy* proxy1 = dispatcher->CreateStreamProxy();
+    AudioOutputProxy* proxy2 = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy1->Open());
+    EXPECT_TRUE(proxy2->Open());
+
+    proxy1->Start(&callback_);
+    proxy2->Start(&callback_);
+    OnStart();
+    proxy1->Stop();
+    CloseAndWaitForCloseTimer(proxy1, &stream1);
+
+    proxy2->Stop();
+    CloseAndWaitForCloseTimer(proxy2, &stream2);
+
+    EXPECT_TRUE(stream1.stop_called());
+    EXPECT_TRUE(stream1.start_called());
+    EXPECT_TRUE(stream2.stop_called());
+    EXPECT_TRUE(stream2.start_called());
+  }
+
+  void StartFailed(AudioOutputDispatcher* dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open())
+        .WillOnce(Return(true));
+
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy->Open());
+
+    WaitForCloseTimer(&stream);
+
+    // |stream| is closed at this point. Start() should reopen it again.
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .Times(2)
+        .WillRepeatedly(Return(reinterpret_cast<AudioOutputStream*>(NULL)));
+
+    EXPECT_CALL(callback_, OnError(_)).Times(2);
+
+    proxy->Start(&callback_);
+
+    // Double Start() in the error case should be allowed since it's possible a
+    // callback may not have had time to process the OnError() in between.
+    proxy->Stop();
+    proxy->Start(&callback_);
+
+    Mock::VerifyAndClear(&callback_);
+
+    proxy->Close();
+  }
+
+  void DispatcherDestroyed_BeforeOpen(
+      std::unique_ptr<AudioOutputDispatcher> dispatcher) {
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _)).Times(0);
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    dispatcher.reset();
+    EXPECT_FALSE(proxy->Open());
+    proxy->Close();
+  }
+
+  void DispatcherDestroyed_BeforeStart(
+      std::unique_ptr<AudioOutputDispatcher> dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open()).WillOnce(Return(true));
+    EXPECT_CALL(stream, Close()).Times(1);
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy->Open());
+
+    EXPECT_CALL(callback_, OnError(_)).Times(1);
+    dispatcher.reset();
+    proxy->Start(&callback_);
+    proxy->Stop();
+    proxy->Close();
+  }
+
+  void DispatcherDestroyed_BeforeStop(
+      std::unique_ptr<AudioOutputDispatcher> dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open()).WillOnce(Return(true));
+    EXPECT_CALL(stream, Close()).Times(1);
+    EXPECT_CALL(stream, SetVolume(_)).Times(1);
+
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy->Open());
+    proxy->Start(&callback_);
+    dispatcher.reset();
+    proxy->Stop();
+    proxy->Close();
+  }
+
+  void DispatcherDestroyed_AfterStop(
+      std::unique_ptr<AudioOutputDispatcher> dispatcher) {
+    MockAudioOutputStream stream(&manager_, params_);
+    EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+        .WillOnce(Return(&stream));
+    EXPECT_CALL(stream, Open()).WillOnce(Return(true));
+    EXPECT_CALL(stream, Close()).Times(1);
+    EXPECT_CALL(stream, SetVolume(_)).Times(1);
+
+    AudioOutputProxy* proxy = dispatcher->CreateStreamProxy();
+    EXPECT_TRUE(proxy->Open());
+    proxy->Start(&callback_);
+    proxy->Stop();
+    dispatcher.reset();
+    proxy->Close();
+  }
+
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  MockAudioManager manager_;
+  std::unique_ptr<AudioOutputDispatcherImpl> dispatcher_impl_;
+  MockAudioSourceCallback callback_;
+  AudioParameters params_;
+};
+
+class AudioOutputResamplerTest : public AudioOutputProxyTest {
+ public:
+  void InitDispatcher(base::TimeDelta close_delay) override {
+    // Use a low sample rate and large buffer size when testing otherwise the
+    // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e.,
+    // RunUntilIdle() will never terminate.
+    resampler_params_ = AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                                        CHANNEL_LAYOUT_STEREO, 16000, 1024);
+    resampler_ = std::make_unique<AudioOutputResampler>(
+        &manager(), params_, resampler_params_, std::string(), close_delay,
+        base::BindRepeating(&RegisterDebugRecording));
+  }
+
+  void OnStart() override {
+    // Let Start() run for a bit.
+    base::RunLoop run_loop;
+    task_environment_.GetMainThreadTaskRunner()->PostDelayedTask(
+        FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(kStartRunTimeMs));
+    run_loop.Run();
+  }
+
+ protected:
+  AudioParameters resampler_params_;
+  std::unique_ptr<AudioOutputResampler> resampler_;
+};
+
+TEST_F(AudioOutputProxyTest, CreateAndClose) {
+  AudioOutputProxy* proxy = dispatcher_impl_->CreateStreamProxy();
+  proxy->Close();
+}
+
+TEST_F(AudioOutputResamplerTest, CreateAndClose) {
+  AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
+  proxy->Close();
+}
+
+TEST_F(AudioOutputProxyTest, OpenAndClose) {
+  OpenAndClose(dispatcher_impl_.get());
+}
+
+TEST_F(AudioOutputResamplerTest, OpenAndClose) {
+  OpenAndClose(resampler_.get());
+}
+
+// Create a stream, and verify that it is closed after kTestCloseDelayMs.
+// if it doesn't start playing.
+TEST_F(AudioOutputProxyTest, CreateAndWait) {
+  CreateAndWait(dispatcher_impl_.get());
+}
+
+// Create a stream, and verify that it is closed after kTestCloseDelayMs.
+// if it doesn't start playing.
+TEST_F(AudioOutputResamplerTest, CreateAndWait) {
+  CreateAndWait(resampler_.get());
+}
+
+TEST_F(AudioOutputProxyTest, StartAndStop) {
+  StartAndStop(dispatcher_impl_.get());
+}
+
+TEST_F(AudioOutputResamplerTest, StartAndStop) {
+  StartAndStop(resampler_.get());
+}
+
+TEST_F(AudioOutputProxyTest, CloseAfterStop) {
+  CloseAfterStop(dispatcher_impl_.get());
+}
+
+TEST_F(AudioOutputResamplerTest, CloseAfterStop) {
+  CloseAfterStop(resampler_.get());
+}
+
+TEST_F(AudioOutputProxyTest, TwoStreams) {
+  TwoStreams(dispatcher_impl_.get());
+}
+
+TEST_F(AudioOutputResamplerTest, TwoStreams) {
+  TwoStreams(resampler_.get());
+}
+
+// Two streams: verify that second stream is allocated when the first
+// starts playing.
+TEST_F(AudioOutputProxyTest, OneStream_TwoPlays) {
+  OneStream_TwoPlays(dispatcher_impl_.get());
+}
+
+TEST_F(AudioOutputResamplerTest, OneStream_TwoPlays) {
+  OneStream_TwoPlays(resampler_.get());
+}
+
+// Two streams, both are playing. Dispatcher should not open a third stream.
+TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying) {
+  TwoStreams_BothPlaying(dispatcher_impl_.get());
+}
+
+TEST_F(AudioOutputResamplerTest, TwoStreams_BothPlaying) {
+  TwoStreams_BothPlaying(resampler_.get());
+}
+
+TEST_F(AudioOutputProxyTest, OpenFailed) {
+  OpenFailed(dispatcher_impl_.get());
+}
+
+// Start() method failed.
+TEST_F(AudioOutputProxyTest, StartFailed) {
+  StartFailed(dispatcher_impl_.get());
+}
+
+TEST_F(AudioOutputResamplerTest, StartFailed) {
+  StartFailed(resampler_.get());
+}
+
+TEST_F(AudioOutputProxyTest, DispatcherDestroyed_BeforeOpen) {
+  DispatcherDestroyed_BeforeOpen(std::move(dispatcher_impl_));
+}
+
+TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_BeforeOpen) {
+  DispatcherDestroyed_BeforeOpen(std::move(resampler_));
+}
+
+TEST_F(AudioOutputProxyTest, DispatcherDestroyed_BeforeStart) {
+  DispatcherDestroyed_BeforeStart(std::move(dispatcher_impl_));
+}
+
+TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_BeforeStart) {
+  DispatcherDestroyed_BeforeStart(std::move(resampler_));
+}
+
+TEST_F(AudioOutputProxyTest, DispatcherDestroyed_BeforeStop) {
+  DispatcherDestroyed_BeforeStop(std::move(dispatcher_impl_));
+}
+
+TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_BeforeStop) {
+  DispatcherDestroyed_BeforeStop(std::move(resampler_));
+}
+
+TEST_F(AudioOutputProxyTest, DispatcherDestroyed_AfterStop) {
+  DispatcherDestroyed_AfterStop(std::move(dispatcher_impl_));
+}
+
+TEST_F(AudioOutputResamplerTest, DispatcherDestroyed_AfterStop) {
+  DispatcherDestroyed_AfterStop(std::move(resampler_));
+}
+
+TEST_F(AudioOutputProxyTest, DispatcherDeviceChangeClosesIdleStreams) {
+  // Set close delay so long that it triggers a test timeout if relied upon.
+  InitDispatcher(base::Seconds(1000));
+
+  MockAudioOutputStream stream(&manager_, params_);
+
+  EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+      .WillOnce(Return(&stream));
+  EXPECT_CALL(stream, Open()).WillOnce(Return(true));
+
+  AudioOutputProxy* proxy = dispatcher_impl_->CreateStreamProxy();
+  EXPECT_TRUE(proxy->Open());
+
+  // Close the stream and verify it doesn't happen immediately.
+  proxy->Close();
+  Mock::VerifyAndClear(&stream);
+
+  // This should trigger a true close on the stream.
+  dispatcher_impl_->OnDeviceChange();
+
+  base::RunLoop run_loop;
+  EXPECT_CALL(stream, Close())
+      .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
+  run_loop.Run();
+}
+
+// Simulate AudioOutputStream::Create() failure with a low latency stream and
+// ensure AudioOutputResampler falls back to the high latency path.
+TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) {
+  MockAudioOutputStream stream(&manager_, params_);
+  EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+      .Times(2)
+      .WillOnce(Return(static_cast<AudioOutputStream*>(NULL)))
+      .WillRepeatedly(Return(&stream));
+  EXPECT_CALL(stream, Open())
+      .WillOnce(Return(true));
+
+  AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
+  EXPECT_TRUE(proxy->Open());
+  CloseAndWaitForCloseTimer(proxy, &stream);
+}
+
+// Simulate AudioOutputStream::Open() failure with a low latency stream and
+// ensure AudioOutputResampler falls back to the high latency path.
+TEST_F(AudioOutputResamplerTest, LowLatencyOpenFailedFallback) {
+  MockAudioOutputStream failed_stream(&manager_, params_);
+  MockAudioOutputStream okay_stream(&manager_, params_);
+  EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+      .Times(2)
+      .WillOnce(Return(&failed_stream))
+      .WillRepeatedly(Return(&okay_stream));
+  EXPECT_CALL(failed_stream, Open())
+      .WillOnce(Return(false));
+  EXPECT_CALL(failed_stream, Close())
+      .Times(1);
+  EXPECT_CALL(okay_stream, Open())
+      .WillOnce(Return(true));
+
+  AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
+  EXPECT_TRUE(proxy->Open());
+  CloseAndWaitForCloseTimer(proxy, &okay_stream);
+}
+
+// Simulate failures to open both the low latency and the fallback high latency
+// stream and ensure AudioOutputResampler falls back to a fake stream.
+TEST_F(AudioOutputResamplerTest, HighLatencyFallbackFailed) {
+  MockAudioOutputStream okay_stream(&manager_, params_);
+
+// Only Windows has a high latency output driver that is not the same as the low
+// latency path.
+#if defined(OS_WIN)
+  static const int kFallbackCount = 2;
+#else
+  static const int kFallbackCount = 1;
+#endif
+  EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+      .Times(kFallbackCount)
+      .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));
+
+  // To prevent shared memory issues the sample rate and buffer size should
+  // match the input stream parameters.
+  EXPECT_CALL(manager(),
+              MakeAudioOutputStream(
+                  AllOf(testing::Property(&AudioParameters::format,
+                                          AudioParameters::AUDIO_FAKE),
+                        testing::Property(&AudioParameters::sample_rate,
+                                          params_.sample_rate()),
+                        testing::Property(&AudioParameters::frames_per_buffer,
+                                          params_.frames_per_buffer())),
+                  _, _))
+      .Times(1)
+      .WillOnce(Return(&okay_stream));
+  EXPECT_CALL(okay_stream, Open())
+      .WillOnce(Return(true));
+
+  AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
+  EXPECT_TRUE(proxy->Open());
+  CloseAndWaitForCloseTimer(proxy, &okay_stream);
+}
+
+// Simulate failures to open both the low latency, the fallback high latency
+// stream, and the fake audio output stream and ensure AudioOutputResampler
+// terminates normally.
+TEST_F(AudioOutputResamplerTest, AllFallbackFailed) {
+// Only Windows has a high latency output driver that is not the same as the low
+// latency path.
+#if defined(OS_WIN)
+  static const int kFallbackCount = 3;
+#else
+  static const int kFallbackCount = 2;
+#endif
+  EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+      .Times(kFallbackCount)
+      .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));
+
+  AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
+  EXPECT_FALSE(proxy->Open());
+  proxy->Close();
+}
+
+// Simulate an eventual OpenStream() failure; i.e. successful OpenStream() calls
+// eventually followed by one which fails; root cause of http://crbug.com/150619
+TEST_F(AudioOutputResamplerTest, LowLatencyOpenEventuallyFails) {
+  MockAudioOutputStream stream1(&manager_, params_);
+  MockAudioOutputStream stream2(&manager_, params_);
+
+  // Setup the mock such that all three streams are successfully created.
+  EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+      .WillOnce(Return(&stream1))
+      .WillOnce(Return(&stream2))
+      .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));
+
+  // Stream1 should be able to successfully open and start.
+  EXPECT_CALL(stream1, Open())
+      .WillOnce(Return(true));
+  EXPECT_CALL(stream1, SetVolume(_))
+      .Times(1);
+
+  // Stream2 should also be able to successfully open and start.
+  EXPECT_CALL(stream2, Open())
+      .WillOnce(Return(true));
+  EXPECT_CALL(stream2, SetVolume(_))
+      .Times(1);
+
+  // Open and start the first proxy and stream.
+  AudioOutputProxy* proxy1 = resampler_->CreateStreamProxy();
+  EXPECT_TRUE(proxy1->Open());
+  proxy1->Start(&callback_);
+  OnStart();
+
+  // Open and start the second proxy and stream.
+  AudioOutputProxy* proxy2 = resampler_->CreateStreamProxy();
+  EXPECT_TRUE(proxy2->Open());
+  proxy2->Start(&callback_);
+  OnStart();
+
+  // Attempt to open the third stream which should fail.
+  AudioOutputProxy* proxy3 = resampler_->CreateStreamProxy();
+  EXPECT_FALSE(proxy3->Open());
+  proxy3->Close();
+
+  // Perform the required Stop()/Close() shutdown dance for each proxy.  Under
+  // the hood each proxy should correctly call CloseStream() if OpenStream()
+  // succeeded or not.
+  proxy2->Stop();
+  CloseAndWaitForCloseTimer(proxy2, &stream2);
+
+  proxy1->Stop();
+  CloseAndWaitForCloseTimer(proxy1, &stream1);
+
+  EXPECT_TRUE(stream1.stop_called());
+  EXPECT_TRUE(stream1.start_called());
+  EXPECT_TRUE(stream2.stop_called());
+  EXPECT_TRUE(stream2.start_called());
+}
+
+// Simulate failures to open both the low latency and the fallback high latency
+// stream and ensure AudioOutputResampler falls back to a fake stream.  Ensure
+// that after the close delay elapses, opening another stream succeeds with a
+// non-fake stream.
+TEST_F(AudioOutputResamplerTest, FallbackRecovery) {
+  MockAudioOutputStream fake_stream(&manager_, params_);
+
+  // Trigger the fallback mechanism until a fake output stream is created.
+#if defined(OS_WIN)
+  static const int kFallbackCount = 2;
+#else
+  static const int kFallbackCount = 1;
+#endif
+  EXPECT_CALL(manager(), MakeAudioOutputStream(_, _, _))
+      .Times(kFallbackCount)
+      .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL)));
+  EXPECT_CALL(manager(),
+              MakeAudioOutputStream(
+                  AllOf(testing::Property(&AudioParameters::format,
+                                          AudioParameters::AUDIO_FAKE),
+                        testing::Property(&AudioParameters::sample_rate,
+                                          params_.sample_rate()),
+                        testing::Property(&AudioParameters::frames_per_buffer,
+                                          params_.frames_per_buffer())),
+                  _, _))
+      .WillOnce(Return(&fake_stream));
+  EXPECT_CALL(fake_stream, Open()).WillOnce(Return(true));
+  AudioOutputProxy* proxy = resampler_->CreateStreamProxy();
+  EXPECT_TRUE(proxy->Open());
+  CloseAndWaitForCloseTimer(proxy, &fake_stream);
+
+  // Once all proxies have been closed, AudioOutputResampler will start the
+  // reinitialization timer and execute it after the close delay elapses.
+  base::RunLoop run_loop;
+  task_environment_.GetMainThreadTaskRunner()->PostDelayedTask(
+      FROM_HERE, run_loop.QuitClosure(),
+      base::Milliseconds(2 * kTestCloseDelayMs));
+  run_loop.Run();
+
+  // Verify a non-fake stream can be created.
+  MockAudioOutputStream real_stream(&manager_, params_);
+  EXPECT_CALL(manager(),
+              MakeAudioOutputStream(
+                  testing::Property(&AudioParameters::format,
+                                    testing::Ne(AudioParameters::AUDIO_FAKE)),
+                  _, _))
+      .WillOnce(Return(&real_stream));
+
+  // Stream1 should be able to successfully open and start.
+  EXPECT_CALL(real_stream, Open()).WillOnce(Return(true));
+  proxy = resampler_->CreateStreamProxy();
+  EXPECT_TRUE(proxy->Open());
+  CloseAndWaitForCloseTimer(proxy, &real_stream);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_output_resampler.cc b/third_party/chromium/media/audio/audio_output_resampler.cc
new file mode 100644
index 0000000..8b75d98
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_resampler.cc
@@ -0,0 +1,502 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_resampler.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_output_dispatcher_impl.h"
+#include "media/audio/audio_output_proxy.h"
+#include "media/base/audio_converter.h"
+#include "media/base/audio_timestamp_helper.h"
+#include "media/base/limits.h"
+#include "media/base/sample_rates.h"
+
+namespace media {
+
+class OnMoreDataConverter
+    : public AudioOutputStream::AudioSourceCallback,
+      public AudioConverter::InputCallback {
+ public:
+  OnMoreDataConverter(const AudioParameters& input_params,
+                      const AudioParameters& output_params,
+                      std::unique_ptr<AudioDebugRecorder> debug_recorder);
+
+  OnMoreDataConverter(const OnMoreDataConverter&) = delete;
+  OnMoreDataConverter& operator=(const OnMoreDataConverter&) = delete;
+
+  ~OnMoreDataConverter() override;
+
+  // AudioSourceCallback interface.
+  int OnMoreData(base::TimeDelta delay,
+                 base::TimeTicks delay_timestamp,
+                 int prior_frames_skipped,
+                 AudioBus* dest) override;
+  void OnError(ErrorType type) override;
+
+  // Sets |source_callback_|.  If this is not a new object, then Stop() must be
+  // called before Start().
+  void Start(AudioOutputStream::AudioSourceCallback* callback);
+
+  // Clears |source_callback_| and flushes the resampler.
+  void Stop();
+
+  bool started() const { return source_callback_ != nullptr; }
+
+  bool error_occurred() const { return error_occurred_; }
+
+ private:
+  // AudioConverter::InputCallback implementation.
+  double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override;
+
+  // Source callback.
+  AudioOutputStream::AudioSourceCallback* source_callback_;
+
+  // Last |delay| and |delay_timestamp| received via OnMoreData(). Used to
+  // correct playback delay in ProvideInput() before calling |source_callback_|.
+  base::TimeDelta current_delay_;
+  base::TimeTicks current_delay_timestamp_;
+
+  const int input_samples_per_second_;
+
+  // Handles resampling, buffering, and channel mixing between input and output
+  // parameters.
+  AudioConverter audio_converter_;
+
+  // True if OnError() was ever called.  Should only be read if the underlying
+  // stream has been stopped.
+  bool error_occurred_;
+
+  // Information about input and output buffer sizes to be traced.
+  const int input_buffer_size_;
+  const int output_buffer_size_;
+
+  // For audio debug recordings.
+  std::unique_ptr<AudioDebugRecorder> debug_recorder_;
+};
+
+namespace {
+
+// Record UMA statistics for hardware output configuration.
+static void RecordStats(const AudioParameters& output_params) {
+  base::UmaHistogramEnumeration(
+      "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
+      static_cast<ChannelLayout>(CHANNEL_LAYOUT_MAX + 1));
+  base::UmaHistogramExactLinear("Media.HardwareAudioChannelCount",
+                                output_params.channels(),
+                                static_cast<int>(limits::kMaxChannels));
+
+  AudioSampleRate asr;
+  if (!ToAudioSampleRate(output_params.sample_rate(), &asr))
+    return;
+
+  base::UmaHistogramEnumeration(
+      "Media.HardwareAudioSamplesPerSecond", asr,
+      static_cast<AudioSampleRate>(kAudioSampleRateMax + 1));
+}
+
+// Only Windows has a high latency output driver that is not the same as the low
+// latency path.
+#if defined(OS_WIN)
+// Converts low latency based |output_params| into high latency appropriate
+// output parameters in error situations.
+AudioParameters GetFallbackOutputParams(
+    const AudioParameters& original_output_params) {
+  DCHECK_EQ(original_output_params.format(),
+            AudioParameters::AUDIO_PCM_LOW_LATENCY);
+  // Choose AudioParameters appropriate for opening the device in high latency
+  // mode.  |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
+  // MAXIMUM frame size for low latency.
+  static const int kMinLowLatencyFrameSize = 2048;
+  const int frames_per_buffer = std::max(
+      original_output_params.frames_per_buffer(), kMinLowLatencyFrameSize);
+
+  return AudioParameters(AudioParameters::AUDIO_PCM_LINEAR,
+                         original_output_params.channel_layout(),
+                         original_output_params.sample_rate(),
+                         frames_per_buffer);
+}
+#endif
+
+// This enum must match the numbering for
+// AudioOutputResamplerOpenLowLatencyStreamResult in enums.xml. Do not reorder
+// or remove items, only add new items before OPEN_STREAM_MAX.
+enum class OpenStreamResult {
+  kFail = 0,
+  kFallbackToFake = 1,
+  kFallbackToLinear = 2,
+  kSuccess = 3,
+  kFallbackToFakeFail = 4,
+  kFallbackToFakeSuccess = 5,
+  kFallbackToLinearFail = 6,
+  kFallbackToLinearSuccess = 7,
+  kSubsequentFail = 8,
+  kSubsequentSuccess = 9,
+  kMaxValue = kSubsequentSuccess,
+};
+
+OpenStreamResult GetSubsequentStreamCreationResultBucket(
+    const AudioParameters& current_params,
+    bool success) {
+  switch (current_params.format()) {
+    case AudioParameters::AUDIO_PCM_LOW_LATENCY:
+      return success ? OpenStreamResult::kSubsequentSuccess
+                     : OpenStreamResult::kSubsequentFail;
+    case AudioParameters::AUDIO_PCM_LINEAR:
+      return success ? OpenStreamResult::kFallbackToLinearSuccess
+                     : OpenStreamResult::kFallbackToLinearFail;
+    case AudioParameters::AUDIO_FAKE:
+      return success ? OpenStreamResult::kFallbackToFakeSuccess
+                     : OpenStreamResult::kFallbackToFakeFail;
+    default:
+      NOTREACHED();
+      return OpenStreamResult::kFail;
+  }
+}
+
+}  // namespace
+
+AudioOutputResampler::AudioOutputResampler(
+    AudioManager* audio_manager,
+    const AudioParameters& input_params,
+    const AudioParameters& output_params,
+    const std::string& output_device_id,
+    base::TimeDelta close_delay,
+    const RegisterDebugRecordingSourceCallback&
+        register_debug_recording_source_callback)
+    : AudioOutputDispatcher(audio_manager),
+      close_delay_(close_delay),
+      input_params_(input_params),
+      output_params_(output_params),
+      original_output_params_(output_params),
+      device_id_(output_device_id),
+      reinitialize_timer_(
+          FROM_HERE,
+          close_delay_,
+          base::BindRepeating(&AudioOutputResampler::Reinitialize,
+                              base::Unretained(this))),
+      register_debug_recording_source_callback_(
+          register_debug_recording_source_callback) {
+  DCHECK(audio_manager->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(input_params.IsValid());
+  DCHECK(output_params.IsValid());
+  DCHECK(output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY ||
+         output_params_.format() == AudioParameters::AUDIO_PCM_LINEAR);
+  DCHECK(register_debug_recording_source_callback_);
+
+  // Record UMA statistics for the hardware configuration.
+  RecordStats(output_params);
+}
+
+AudioOutputResampler::~AudioOutputResampler() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  for (const auto& item : callbacks_) {
+    if (item.second->started())
+      StopStreamInternal(item);
+  }
+}
+
+void AudioOutputResampler::Reinitialize() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+
+  // We can only reinitialize the dispatcher if it has no active proxies. Check
+  // if one has been created since the reinitialization timer was started.
+  if (dispatcher_ && dispatcher_->HasOutputProxies())
+    return;
+
+  DCHECK(callbacks_.empty());
+
+  // Log a trace event so we can get feedback in the field when this happens.
+  TRACE_EVENT0("audio", "AudioOutputResampler::Reinitialize");
+
+  output_params_ = original_output_params_;
+  dispatcher_.reset();
+}
+
+std::unique_ptr<AudioOutputDispatcherImpl> AudioOutputResampler::MakeDispatcher(
+    const std::string& output_device_id,
+    const AudioParameters& params) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(callbacks_.empty());
+  return std::make_unique<AudioOutputDispatcherImpl>(
+      audio_manager(), params, output_device_id, close_delay_);
+}
+
+AudioOutputProxy* AudioOutputResampler::CreateStreamProxy() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  return new AudioOutputProxy(weak_factory_.GetWeakPtr());
+}
+
+bool AudioOutputResampler::OpenStream() {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+
+  bool first_stream = false;
+  if (!dispatcher_) {
+    first_stream = true;
+    // No open streams => no fallback has happened.
+    DCHECK(original_output_params_.Equals(output_params_));
+    DCHECK(callbacks_.empty());
+    dispatcher_ = MakeDispatcher(device_id_, output_params_);
+  }
+
+  constexpr char kFallbackHistogramName[] =
+      "Media.FallbackToHighLatencyAudioPath";
+  constexpr char kOpenLowLatencyHistogramName[] =
+      "Media.AudioOutputResampler.OpenLowLatencyStream";
+
+  if (dispatcher_->OpenStream()) {
+    // Only record the UMA statistic if we didn't fallback during construction
+    // and only for the first stream we open.
+    if (original_output_params_.format() ==
+        AudioParameters::AUDIO_PCM_LOW_LATENCY) {
+      if (first_stream)
+        base::UmaHistogramBoolean(kFallbackHistogramName, false);
+
+      base::UmaHistogramEnumeration(
+          kOpenLowLatencyHistogramName,
+          first_stream
+              ? OpenStreamResult::kSuccess
+              : GetSubsequentStreamCreationResultBucket(output_params_, true));
+    }
+    return true;
+  }
+
+  // Fallback is available for low latency streams only.
+  if (original_output_params_.format() !=
+      AudioParameters::AUDIO_PCM_LOW_LATENCY) {
+    return false;
+  }
+
+  // If we have successfully opened a stream previously, there's nothing more to
+  // be done.
+  if (!first_stream) {
+    base::UmaHistogramEnumeration(
+        kOpenLowLatencyHistogramName,
+        GetSubsequentStreamCreationResultBucket(output_params_, false));
+    return false;
+  }
+
+  base::UmaHistogramBoolean(kFallbackHistogramName, true);
+
+  // Only Windows has a high latency output driver that is not the same as the
+  // low latency path.
+#if defined(OS_WIN)
+  DLOG(ERROR) << "Unable to open audio device in low latency mode.  Falling "
+              << "back to high latency audio output.";
+
+  output_params_ = GetFallbackOutputParams(original_output_params_);
+  const std::string fallback_device_id = "";
+  dispatcher_ = MakeDispatcher(fallback_device_id, output_params_);
+  if (dispatcher_->OpenStream()) {
+    base::UmaHistogramEnumeration(kOpenLowLatencyHistogramName,
+                                  OpenStreamResult::kFallbackToLinear);
+    return true;
+  }
+#endif
+
+  DLOG(ERROR) << "Unable to open audio device in high latency mode.  Falling "
+              << "back to fake audio output.";
+
+  // Finally fall back to a fake audio output device.
+  output_params_ = input_params_;
+  output_params_.set_format(AudioParameters::AUDIO_FAKE);
+  dispatcher_ = MakeDispatcher(device_id_, output_params_);
+  if (dispatcher_->OpenStream()) {
+    base::UmaHistogramEnumeration(kOpenLowLatencyHistogramName,
+                                  OpenStreamResult::kFallbackToFake);
+    return true;
+  }
+
+  // Resetting the malfunctioning dispatcher.
+  Reinitialize();
+
+  base::UmaHistogramEnumeration(kOpenLowLatencyHistogramName,
+                                OpenStreamResult::kFail);
+  return false;
+}
+
+bool AudioOutputResampler::StartStream(
+    AudioOutputStream::AudioSourceCallback* callback,
+    AudioOutputProxy* stream_proxy) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(dispatcher_);
+
+  OnMoreDataConverter* resampler_callback = nullptr;
+  auto it = callbacks_.find(stream_proxy);
+  if (it == callbacks_.end()) {
+    // If a register callback has been given, register and pass the returned
+    // recoder to the converter. Data is fed to same recorder for the lifetime
+    // of the converter, which is until the stream is closed.
+    resampler_callback = new OnMoreDataConverter(
+        input_params_, output_params_,
+        register_debug_recording_source_callback_.Run(output_params_));
+    callbacks_[stream_proxy] =
+        base::WrapUnique<OnMoreDataConverter>(resampler_callback);
+  } else {
+    resampler_callback = it->second.get();
+  }
+
+  resampler_callback->Start(callback);
+  bool result = dispatcher_->StartStream(resampler_callback, stream_proxy);
+  if (!result)
+    resampler_callback->Stop();
+  return result;
+}
+
+void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
+                                           double volume) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(dispatcher_);
+  dispatcher_->StreamVolumeSet(stream_proxy, volume);
+}
+
+void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+
+  auto it = callbacks_.find(stream_proxy);
+  DCHECK(it != callbacks_.end());
+  StopStreamInternal(*it);
+}
+
+void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(dispatcher_);
+
+  dispatcher_->CloseStream(stream_proxy);
+
+  // We assume that StopStream() is always called prior to CloseStream(), so
+  // that it is safe to delete the OnMoreDataConverter here.
+  callbacks_.erase(stream_proxy);
+
+  // Start the reinitialization timer if there are no active proxies and we're
+  // not using the originally requested output parameters.  This allows us to
+  // recover from transient output creation errors.
+  if (!dispatcher_->HasOutputProxies() && callbacks_.empty() &&
+      !output_params_.Equals(original_output_params_)) {
+    reinitialize_timer_.Reset();
+  }
+}
+
+void AudioOutputResampler::FlushStream(AudioOutputProxy* stream_proxy) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(dispatcher_);
+
+  dispatcher_->FlushStream(stream_proxy);
+}
+
+void AudioOutputResampler::StopStreamInternal(
+    const CallbackMap::value_type& item) {
+  DCHECK(audio_manager()->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(dispatcher_);
+  AudioOutputProxy* stream_proxy = item.first;
+  OnMoreDataConverter* callback = item.second.get();
+  DCHECK(callback->started());
+
+  // Stop the underlying physical stream.
+  dispatcher_->StopStream(stream_proxy);
+
+  // Now that StopStream() has completed the underlying physical stream should
+  // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
+  // OnMoreDataConverter.
+  callback->Stop();
+
+  // Destroy idle streams if any errors occurred during output; this ensures
+  // bad streams will not be reused.  Note: Errors may occur during the Stop()
+  // call above.
+  if (callback->error_occurred())
+    dispatcher_->CloseAllIdleStreams();
+}
+
+OnMoreDataConverter::OnMoreDataConverter(
+    const AudioParameters& input_params,
+    const AudioParameters& output_params,
+    std::unique_ptr<AudioDebugRecorder> debug_recorder)
+    : source_callback_(nullptr),
+      input_samples_per_second_(input_params.sample_rate()),
+      audio_converter_(input_params, output_params, false),
+      error_occurred_(false),
+      input_buffer_size_(input_params.frames_per_buffer()),
+      output_buffer_size_(output_params.frames_per_buffer()),
+      debug_recorder_(std::move(debug_recorder)) {}
+
+OnMoreDataConverter::~OnMoreDataConverter() {
+  // Ensure Stop() has been called so we don't end up with an AudioOutputStream
+  // calling back into OnMoreData() after destruction.
+  CHECK(!source_callback_);
+}
+
+void OnMoreDataConverter::Start(
+    AudioOutputStream::AudioSourceCallback* callback) {
+  CHECK(!source_callback_);
+  CHECK(callback);
+  source_callback_ = callback;
+
+  // While AudioConverter can handle multiple inputs, we're using it only with
+  // a single input currently.  Eventually this may be the basis for a browser
+  // side mixer.
+  audio_converter_.AddInput(this);
+}
+
+void OnMoreDataConverter::Stop() {
+  CHECK(source_callback_);
+  audio_converter_.RemoveInput(this);
+  source_callback_ = nullptr;
+}
+
+int OnMoreDataConverter::OnMoreData(base::TimeDelta delay,
+                                    base::TimeTicks delay_timestamp,
+                                    int /* prior_frames_skipped */,
+                                    AudioBus* dest) {
+  TRACE_EVENT2("audio", "OnMoreDataConverter::OnMoreData", "input buffer size",
+               input_buffer_size_, "output buffer size", output_buffer_size_);
+  current_delay_ = delay;
+  current_delay_timestamp_ = delay_timestamp;
+  audio_converter_.Convert(dest);
+
+  if (debug_recorder_)
+    debug_recorder_->OnData(dest);
+
+  // Always return the full number of frames requested, ProvideInput()
+  // will pad with silence if it wasn't able to acquire enough data.
+  return dest->frames();
+}
+
+double OnMoreDataConverter::ProvideInput(AudioBus* dest,
+                                         uint32_t frames_delayed) {
+  base::TimeDelta new_delay =
+      current_delay_ + AudioTimestampHelper::FramesToTime(
+                           frames_delayed, input_samples_per_second_);
+  // Retrieve data from the original callback.
+  const int frames = source_callback_->OnMoreData(
+      new_delay, current_delay_timestamp_, 0, dest);
+
+  // Zero any unfilled frames if anything was filled, otherwise we'll just
+  // return a volume of zero and let AudioConverter drop the output.
+  if (frames > 0 && frames < dest->frames())
+    dest->ZeroFramesPartial(frames, dest->frames() - frames);
+  return frames > 0 ? 1 : 0;
+}
+
+void OnMoreDataConverter::OnError(ErrorType type) {
+  error_occurred_ = true;
+  source_callback_->OnError(type);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_output_resampler.h b/third_party/chromium/media/audio/audio_output_resampler.h
new file mode 100644
index 0000000..dc79475
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_resampler.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_RESAMPLER_H_
+#define MEDIA_AUDIO_AUDIO_OUTPUT_RESAMPLER_H_
+
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "media/audio/audio_debug_recording_helper.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_output_dispatcher.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioManager;
+class AudioOutputDispatcherImpl;
+class OnMoreDataConverter;
+
+// AudioOutputResampler is a browser-side resampling and buffering solution
+// which ensures audio data is always output at given parameters.  See the
+// AudioConverter class for details on the conversion process.
+//
+// AOR works by intercepting the AudioSourceCallback provided to StartStream()
+// and redirecting it through an AudioConverter instance.
+//
+// AOR will automatically fall back from AUDIO_PCM_LOW_LATENCY to
+// AUDIO_PCM_LINEAR if the output device fails to open at the requested output
+// parameters. If opening still fails, it will fallback to AUDIO_FAKE.
+class MEDIA_EXPORT AudioOutputResampler : public AudioOutputDispatcher {
+ public:
+  // Callback type to register an AudioDebugRecorder.
+  using RegisterDebugRecordingSourceCallback =
+      base::RepeatingCallback<std::unique_ptr<AudioDebugRecorder>(
+          const AudioParameters&)>;
+
+  AudioOutputResampler(AudioManager* audio_manager,
+                       const AudioParameters& input_params,
+                       const AudioParameters& output_params,
+                       const std::string& output_device_id,
+                       base::TimeDelta close_delay,
+                       const RegisterDebugRecordingSourceCallback&
+                           register_debug_recording_source_callback);
+
+  AudioOutputResampler(const AudioOutputResampler&) = delete;
+  AudioOutputResampler& operator=(const AudioOutputResampler&) = delete;
+
+  ~AudioOutputResampler() override;
+
+  // AudioOutputDispatcher interface.
+  AudioOutputProxy* CreateStreamProxy() override;
+  bool OpenStream() override;
+  bool StartStream(AudioOutputStream::AudioSourceCallback* callback,
+                   AudioOutputProxy* stream_proxy) override;
+  void StopStream(AudioOutputProxy* stream_proxy) override;
+  void StreamVolumeSet(AudioOutputProxy* stream_proxy, double volume) override;
+  void CloseStream(AudioOutputProxy* stream_proxy) override;
+  void FlushStream(AudioOutputProxy* stream_proxy) override;
+
+ private:
+  using CallbackMap =
+      base::flat_map<AudioOutputProxy*, std::unique_ptr<OnMoreDataConverter>>;
+
+  // Used to reinitialize |dispatcher_| upon timeout if there are no open
+  // streams.
+  void Reinitialize();
+
+  // Used to initialize |dispatcher_|.
+  std::unique_ptr<AudioOutputDispatcherImpl> MakeDispatcher(
+      const std::string& output_device_id,
+      const AudioParameters& params);
+
+  // Stops the stream corresponding to the |item| in |callbacks_|.
+  void StopStreamInternal(const CallbackMap::value_type& item);
+
+  // Dispatcher to proxy all AudioOutputDispatcher calls too.
+  // Lazily initialized on a first stream open request.
+  std::unique_ptr<AudioOutputDispatcherImpl> dispatcher_;
+
+  // Map of outstanding OnMoreDataConverter objects.  A new object is created
+  // on every StartStream() call and destroyed on CloseStream().
+  CallbackMap callbacks_;
+
+  // Used by AudioOutputDispatcherImpl; kept so we can reinitialize on the fly.
+  const base::TimeDelta close_delay_;
+
+  // Source AudioParameters.
+  const AudioParameters input_params_;
+
+  // AudioParameters used to setup the output stream; changed upon fallback.
+  AudioParameters output_params_;
+
+  // The original AudioParameters we were constructed with.
+  const AudioParameters original_output_params_;
+
+  // Output device id.
+  const std::string device_id_;
+
+  // The reinitialization timer provides a way to recover from temporary failure
+  // states by clearing the dispatcher if all proxies have been closed and none
+  // have been created within |close_delay_|.  Without this, audio may be lost
+  // to a fake stream indefinitely for transient errors.
+  base::RetainingOneShotTimer reinitialize_timer_;
+
+  // Callback for registering a debug recording source.
+  RegisterDebugRecordingSourceCallback
+      register_debug_recording_source_callback_;
+
+  base::WeakPtrFactory<AudioOutputResampler> weak_factory_{this};
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_RESAMPLER_H_
diff --git a/third_party/chromium/media/audio/audio_output_stream_sink.cc b/third_party/chromium/media/audio/audio_output_stream_sink.cc
new file mode 100644
index 0000000..1dff40e
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_stream_sink.cc
@@ -0,0 +1,182 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_output_stream_sink.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "media/audio/audio_manager.h"
+#include "media/base/audio_timestamp_helper.h"
+
+namespace media {
+
+AudioOutputStreamSink::AudioOutputStreamSink()
+    : initialized_(false),
+      started_(false),
+      render_callback_(nullptr),
+      active_render_callback_(nullptr),
+      audio_task_runner_(AudioManager::Get()->GetTaskRunner()),
+      stream_(nullptr) {}
+
+AudioOutputStreamSink::~AudioOutputStreamSink() = default;
+
+void AudioOutputStreamSink::Initialize(const AudioParameters& params,
+                                       RenderCallback* callback) {
+  DCHECK(callback);
+  DCHECK(!started_);
+  params_ = params;
+  render_callback_ = callback;
+  initialized_ = true;
+}
+
+void AudioOutputStreamSink::Start() {
+  DCHECK(initialized_);
+  DCHECK(!started_);
+  {
+    base::AutoLock al(callback_lock_);
+    active_render_callback_ = render_callback_;
+  }
+  started_ = true;
+  audio_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&AudioOutputStreamSink::DoStart, this, params_));
+}
+
+void AudioOutputStreamSink::Stop() {
+  ClearCallback();
+  started_ = false;
+  audio_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputStreamSink::DoStop, this));
+}
+
+void AudioOutputStreamSink::Pause() {
+  ClearCallback();
+  audio_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputStreamSink::DoPause, this));
+}
+
+void AudioOutputStreamSink::Flush() {
+  audio_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputStreamSink::DoFlush, this));
+}
+
+void AudioOutputStreamSink::Play() {
+  {
+    base::AutoLock al(callback_lock_);
+    active_render_callback_ = render_callback_;
+  }
+  audio_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AudioOutputStreamSink::DoPlay, this));
+}
+
+bool AudioOutputStreamSink::SetVolume(double volume) {
+  audio_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&AudioOutputStreamSink::DoSetVolume, this, volume));
+  return true;
+}
+
+OutputDeviceInfo AudioOutputStreamSink::GetOutputDeviceInfo() {
+  return OutputDeviceInfo(OUTPUT_DEVICE_STATUS_OK);
+}
+
+void AudioOutputStreamSink::GetOutputDeviceInfoAsync(
+    OutputDeviceInfoCB info_cb) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(info_cb), GetOutputDeviceInfo()));
+}
+
+bool AudioOutputStreamSink::IsOptimizedForHardwareParameters() {
+  return true;
+}
+
+bool AudioOutputStreamSink::CurrentThreadIsRenderingThread() {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+int AudioOutputStreamSink::OnMoreData(base::TimeDelta delay,
+                                      base::TimeTicks delay_timestamp,
+                                      int prior_frames_skipped,
+                                      AudioBus* dest) {
+  // Note: Runs on the audio thread created by the OS.
+  base::AutoLock al(callback_lock_);
+  if (!active_render_callback_)
+    return 0;
+
+  return active_render_callback_->Render(delay, delay_timestamp,
+                                         prior_frames_skipped, dest);
+}
+
+void AudioOutputStreamSink::OnError(ErrorType type) {
+  // Note: Runs on the audio thread created by the OS.
+  base::AutoLock al(callback_lock_);
+  if (active_render_callback_)
+    active_render_callback_->OnRenderError();
+}
+
+void AudioOutputStreamSink::DoStart(const AudioParameters& params) {
+  DCHECK(audio_task_runner_->BelongsToCurrentThread());
+
+  // Create an AudioOutputStreamProxy which will handle any and all resampling
+  // necessary to generate a low latency output stream.
+  active_params_ = params;
+  stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(active_params_,
+                                                            std::string());
+  if (!stream_ || !stream_->Open()) {
+    {
+      base::AutoLock al(callback_lock_);
+      if (active_render_callback_)
+        active_render_callback_->OnRenderError();
+    }
+    if (stream_)
+      stream_->Close();
+    stream_ = nullptr;
+  }
+}
+
+void AudioOutputStreamSink::DoStop() {
+  DCHECK(audio_task_runner_->BelongsToCurrentThread());
+
+  if (!stream_)
+    return;
+
+  DoPause();
+  stream_->Close();
+  stream_ = nullptr;
+}
+
+void AudioOutputStreamSink::DoPause() {
+  DCHECK(audio_task_runner_->BelongsToCurrentThread());
+  stream_->Stop();
+}
+
+void AudioOutputStreamSink::DoFlush() {
+  DCHECK(audio_task_runner_->BelongsToCurrentThread());
+  if (stream_) {
+    stream_->Flush();
+  }
+}
+
+void AudioOutputStreamSink::DoPlay() {
+  DCHECK(audio_task_runner_->BelongsToCurrentThread());
+  stream_->Start(this);
+}
+
+void AudioOutputStreamSink::DoSetVolume(double volume) {
+  DCHECK(audio_task_runner_->BelongsToCurrentThread());
+  stream_->SetVolume(volume);
+}
+
+void AudioOutputStreamSink::ClearCallback() {
+  base::AutoLock al(callback_lock_);
+  active_render_callback_ = nullptr;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_output_stream_sink.h b/third_party/chromium/media/audio/audio_output_stream_sink.h
new file mode 100644
index 0000000..fcc8bc1
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_stream_sink.h
@@ -0,0 +1,96 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_STREAM_SINK_H_
+#define MEDIA_AUDIO_AUDIO_OUTPUT_STREAM_SINK_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "base/time/time.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_renderer_sink.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Wrapper which exposes the browser side audio interface (AudioOutputStream) as
+// if it were a renderer side audio interface (AudioRendererSink). Note: This
+// will not work for sandboxed renderers.
+//
+// TODO(dalecurtis): Delete this class once we have a proper mojo audio service;
+// tracked by http://crbug.com/425368
+class MEDIA_EXPORT AudioOutputStreamSink
+    : public RestartableAudioRendererSink,
+      public AudioOutputStream::AudioSourceCallback {
+ public:
+  AudioOutputStreamSink();
+
+  // RestartableAudioRendererSink implementation.
+  void Initialize(const AudioParameters& params,
+                  RenderCallback* callback) override;
+  void Start() override;
+  void Stop() override;
+  void Pause() override;
+  void Play() override;
+  bool SetVolume(double volume) override;
+  OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
+  bool IsOptimizedForHardwareParameters() override;
+  bool CurrentThreadIsRenderingThread() override;
+
+  // AudioSourceCallback implementation.
+  int OnMoreData(base::TimeDelta delay,
+                 base::TimeTicks delay_timestamp,
+                 int prior_frames_skipped,
+                 AudioBus* dest) override;
+  void OnError(ErrorType type) override;
+  void Flush() override;
+
+ private:
+  ~AudioOutputStreamSink() override;
+  void ClearCallback();
+
+  // Helper methods for running AudioManager methods on the audio thread.
+  void DoStart(const AudioParameters& params);
+  void DoStop();
+  void DoPause();
+  void DoFlush();
+  void DoPlay();
+  void DoSetVolume(double volume);
+
+  bool initialized_;
+  bool started_;
+
+  // Parameters provided by Initialize().
+  AudioParameters params_;
+  RenderCallback* render_callback_;
+
+  // State latched for the audio thread.
+  // |active_render_callback_| allows Stop()/Pause() to synchronously prevent
+  // callbacks. Access is synchronized by |callback_lock_|.
+  // |active_params_| is set on the audio thread and therefore does not need
+  // synchronization.
+  AudioParameters active_params_;
+  RenderCallback* active_render_callback_ GUARDED_BY(callback_lock_);
+
+  // Lock to synchronize setting and clearing of |active_render_callback_|.
+  base::Lock callback_lock_;
+
+  // The task runner for the audio thread.
+  const scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
+
+  // The actual AudioOutputStream, must only be accessed on the audio thread.
+  AudioOutputStream* stream_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioOutputStreamSink);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_STREAM_SINK_H_
diff --git a/third_party/chromium/media/audio/audio_output_unittest.cc b/third_party/chromium/media/audio/audio_output_unittest.cc
new file mode 100644
index 0000000..c69ba66
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_output_unittest.cc
@@ -0,0 +1,214 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/command_line.h"
+#include "base/memory/aligned_memory.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "media/audio/audio_device_info_accessor_for_tests.h"
+#include "media/audio/audio_features.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_unittest_util.h"
+#include "media/audio/simple_sources.h"
+#include "media/audio/test_audio_thread.h"
+#include "media/base/limits.h"
+#include "media/base/media_switches.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_ANDROID)
+#include "media/audio/android/audio_manager_android.h"
+#endif
+
+namespace media {
+
+class AudioOutputTest : public testing::TestWithParam<bool> {
+ public:
+  AudioOutputTest() {
+    audio_manager_ =
+        AudioManager::CreateForTesting(std::make_unique<TestAudioThread>());
+    audio_manager_device_info_ =
+        std::make_unique<AudioDeviceInfoAccessorForTests>(audio_manager_.get());
+#if defined(OS_ANDROID)
+    // The only parameter is used to enable/disable AAudio.
+    should_use_aaudio_ = GetParam();
+    if (should_use_aaudio_) {
+      features_.InitAndEnableFeature(features::kUseAAudioDriver);
+
+      aaudio_is_supported_ =
+          reinterpret_cast<AudioManagerAndroid*>(audio_manager_.get())
+              ->IsUsingAAudioForTesting();
+    }
+#endif
+    base::RunLoop().RunUntilIdle();
+  }
+  ~AudioOutputTest() override {
+    if (stream_)
+      stream_->Close();
+    audio_manager_->Shutdown();
+  }
+
+  void CreateWithDefaultParameters() {
+    stream_params_ =
+        audio_manager_device_info_->GetDefaultOutputStreamParameters();
+    stream_ = audio_manager_->MakeAudioOutputStream(
+        stream_params_, std::string(), AudioManager::LogCallback());
+  }
+
+  // Runs message loop for the specified amount of time.
+  void RunMessageLoop(base::TimeDelta delay) {
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, run_loop.QuitClosure(), delay);
+    run_loop.Run();
+  }
+
+ protected:
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
+  std::unique_ptr<AudioManager> audio_manager_;
+  std::unique_ptr<AudioDeviceInfoAccessorForTests> audio_manager_device_info_;
+  AudioParameters stream_params_;
+  AudioOutputStream* stream_ = nullptr;
+  bool should_use_aaudio_ = false;
+  bool aaudio_is_supported_ = false;
+#if defined(OS_ANDROID)
+  base::test::ScopedFeatureList features_;
+#endif
+};
+
+// Test that can it be created and closed.
+TEST_P(AudioOutputTest, GetAndClose) {
+  if (should_use_aaudio_ && !aaudio_is_supported_)
+    return;
+
+  ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
+  CreateWithDefaultParameters();
+  ASSERT_TRUE(stream_);
+}
+
+// Test that it can be opened and closed.
+TEST_P(AudioOutputTest, OpenAndClose) {
+  if (should_use_aaudio_ && !aaudio_is_supported_)
+    return;
+
+  ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
+
+  CreateWithDefaultParameters();
+  ASSERT_TRUE(stream_);
+  EXPECT_TRUE(stream_->Open());
+}
+
+// Verify that Stop() can be called before Start().
+TEST_P(AudioOutputTest, StopBeforeStart) {
+  if (should_use_aaudio_ && !aaudio_is_supported_)
+    return;
+
+  ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
+  CreateWithDefaultParameters();
+  EXPECT_TRUE(stream_->Open());
+  stream_->Stop();
+}
+
+// Verify that Stop() can be called more than once.
+TEST_P(AudioOutputTest, StopTwice) {
+  if (should_use_aaudio_ && !aaudio_is_supported_)
+    return;
+
+  ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
+  CreateWithDefaultParameters();
+  EXPECT_TRUE(stream_->Open());
+  SineWaveAudioSource source(1, 200.0, stream_params_.sample_rate());
+
+  stream_->Start(&source);
+  stream_->Stop();
+  stream_->Stop();
+}
+
+// This test produces actual audio for .25 seconds on the default device.
+TEST_P(AudioOutputTest, Play200HzTone) {
+  if (should_use_aaudio_ && !aaudio_is_supported_)
+    return;
+
+  ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
+
+  stream_params_ =
+      audio_manager_device_info_->GetDefaultOutputStreamParameters();
+  stream_ = audio_manager_->MakeAudioOutputStream(stream_params_, std::string(),
+                                                  AudioManager::LogCallback());
+  ASSERT_TRUE(stream_);
+
+  SineWaveAudioSource source(1, 200.0, stream_params_.sample_rate());
+
+  // Play for 100ms.
+  const int samples_to_play = stream_params_.sample_rate() / 10;
+
+  EXPECT_TRUE(stream_->Open());
+  stream_->SetVolume(1.0);
+
+  // Play the stream until position gets past |samples_to_play|.
+  base::RunLoop run_loop;
+  source.set_on_more_data_callback(
+      base::BindLambdaForTesting([&source, &run_loop, samples_to_play]() {
+        if (source.pos_samples() >= samples_to_play)
+          run_loop.Quit();
+      }));
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_timeout());
+
+  stream_->Start(&source);
+  run_loop.Run();
+
+  stream_->Stop();
+
+  EXPECT_FALSE(source.errors());
+  EXPECT_GE(source.callbacks(), 1);
+  EXPECT_GE(source.pos_samples(), samples_to_play);
+}
+
+// Test that SetVolume() and GetVolume() work as expected.
+TEST_P(AudioOutputTest, VolumeControl) {
+  if (should_use_aaudio_ && !aaudio_is_supported_)
+    return;
+
+  ABORT_AUDIO_TEST_IF_NOT(audio_manager_device_info_->HasAudioOutputDevices());
+
+  CreateWithDefaultParameters();
+  ASSERT_TRUE(stream_);
+  EXPECT_TRUE(stream_->Open());
+
+  double volume = 0.0;
+
+  stream_->GetVolume(&volume);
+  EXPECT_EQ(volume, 1.0);
+
+  stream_->SetVolume(0.5);
+
+  stream_->GetVolume(&volume);
+  EXPECT_LT(volume, 0.51);
+  EXPECT_GT(volume, 0.49);
+  stream_->Stop();
+}
+
+// The test parameter is only relevant on Android. It controls whether or not we
+// allow the use of AAudio.
+INSTANTIATE_TEST_SUITE_P(Base, AudioOutputTest, testing::Values(false));
+
+#if defined(OS_ANDROID)
+// Run tests with AAudio enabled. On Android P and below, these tests should not
+// run, as we only use AAudio on Q+.
+INSTANTIATE_TEST_SUITE_P(AAudio, AudioOutputTest, testing::Values(true));
+#endif
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_sink_parameters.cc b/third_party/chromium/media/audio/audio_sink_parameters.cc
new file mode 100644
index 0000000..8287fa0
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_sink_parameters.cc
@@ -0,0 +1,18 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_sink_parameters.h"
+
+namespace media {
+
+AudioSinkParameters::AudioSinkParameters() = default;
+AudioSinkParameters::AudioSinkParameters(
+    const base::UnguessableToken& session_id,
+    const std::string& device_id)
+    : session_id(session_id), device_id(device_id) {}
+AudioSinkParameters::AudioSinkParameters(const AudioSinkParameters& params) =
+    default;
+AudioSinkParameters::~AudioSinkParameters() = default;
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_sink_parameters.h b/third_party/chromium/media/audio/audio_sink_parameters.h
new file mode 100644
index 0000000..45b89b3
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_sink_parameters.h
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_SINK_PARAMETERS_H_
+#define MEDIA_AUDIO_AUDIO_SINK_PARAMETERS_H_
+
+#include <string>
+
+#include "base/unguessable_token.h"
+#include "media/base/media_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+// The set of parameters used to create an AudioOutputDevice.
+// |session_id| and |device_id| are used to select which device to
+// use. |device_id| is preferred over |session_id| if both are set
+// (i.e. session_id is nonzero).  If neither is set, the default output device
+// will be selected. This is the state when default constructed.
+// If the optional |processing_id| is provided, it is used to indicate that this
+// stream is to be used as the reference signal during audio processing. An
+// audio source must be constructed with the same processing id to complete the
+// association.
+struct MEDIA_EXPORT AudioSinkParameters final {
+  AudioSinkParameters();
+  AudioSinkParameters(const base::UnguessableToken& session_id,
+                      const std::string& device_id);
+  AudioSinkParameters(const AudioSinkParameters& params);
+  ~AudioSinkParameters();
+
+  base::UnguessableToken session_id;
+  std::string device_id;
+  absl::optional<base::UnguessableToken> processing_id;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SINK_PARAMETERS_H_
diff --git a/third_party/chromium/media/audio/audio_source_diverter.h b/third_party/chromium/media/audio/audio_source_diverter.h
new file mode 100644
index 0000000..6ac55a6
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_source_diverter.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_SOURCE_DIVERTER_H_
+#define MEDIA_AUDIO_AUDIO_SOURCE_DIVERTER_H_
+
+#include "base/time/time.h"
+#include "media/base/audio_bus.h"
+#include "media/base/media_export.h"
+
+// Audio sources may optionally implement AudioSourceDiverter to temporarily
+// divert audio data to an alternate AudioOutputStream.  This allows the audio
+// data to be plumbed to an alternate consumer; for example, a loopback
+// mechanism for audio mirroring.
+
+namespace media {
+
+class AudioOutputStream;
+class AudioParameters;
+
+class MEDIA_EXPORT AudioPushSink {
+ public:
+  // Call this function to push audio data into the sink.
+  virtual void OnData(std::unique_ptr<AudioBus> source,
+                      base::TimeTicks reference_time) = 0;
+
+  // Close the stream.
+  // After calling this method, the object should not be used anymore.
+  virtual void Close() = 0;
+};
+
+class MEDIA_EXPORT AudioSourceDiverter {
+public:
+  // Returns the audio parameters of the divertable audio data.
+  virtual const AudioParameters& GetAudioParameters() = 0;
+
+  // Start providing audio data to the given |to_stream|, which is in an
+  // unopened state.  |to_stream| remains under the control of the
+  // AudioSourceDiverter.
+  virtual void StartDiverting(AudioOutputStream* to_stream) = 0;
+
+  // Stops diverting audio data to the stream.  The AudioSourceDiverter is
+  // responsible for making sure the stream is closed, perhaps asynchronously.
+  virtual void StopDiverting() = 0;
+
+  // Start duplicating the current audio stream, and push the copied data into
+  // |sink|.
+  virtual void StartDuplicating(AudioPushSink* sink) = 0;
+
+  // Stop duplicating for the specified |sink|.  The AudioSourceDiverter is
+  // responsible for making sure the sink is closed, perhaps asynchronously.
+  virtual void StopDuplicating(AudioPushSink* sink) = 0;
+
+ protected:
+  virtual ~AudioSourceDiverter() {}
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SOURCE_DIVERTER_H_
diff --git a/third_party/chromium/media/audio/audio_source_parameters.cc b/third_party/chromium/media/audio/audio_source_parameters.cc
new file mode 100644
index 0000000..b8c631af
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_source_parameters.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_source_parameters.h"
+
+namespace media {
+
+AudioSourceParameters::AudioSourceParameters() = default;
+AudioSourceParameters::AudioSourceParameters(
+    const base::UnguessableToken& session_id)
+    : session_id(session_id) {}
+AudioSourceParameters::AudioSourceParameters(
+    const AudioSourceParameters& params) = default;
+AudioSourceParameters::~AudioSourceParameters() = default;
+
+AudioSourceParameters::ProcessingConfig::ProcessingConfig(
+    base::UnguessableToken id,
+    AudioProcessingSettings settings)
+    : id(id), settings(settings) {
+  DCHECK(!id.is_empty());
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_source_parameters.h b/third_party/chromium/media/audio/audio_source_parameters.h
new file mode 100644
index 0000000..efd2da3
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_source_parameters.h
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_SOURCE_PARAMETERS_H_
+#define MEDIA_AUDIO_AUDIO_SOURCE_PARAMETERS_H_
+
+#include "base/unguessable_token.h"
+#include "media/base/audio_processing.h"
+#include "media/base/media_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+// The set of parameters used to create an AudioInputDevice.
+// If |session_id| is nonzero, it is used by the browser
+// to select the correct input device ID. If |session_id| is zero, the default
+// input device will be selected. This is the state when default constructed.
+struct MEDIA_EXPORT AudioSourceParameters final {
+  AudioSourceParameters();
+  explicit AudioSourceParameters(const base::UnguessableToken& session_id);
+  AudioSourceParameters(const AudioSourceParameters& params);
+  ~AudioSourceParameters();
+
+  base::UnguessableToken session_id;
+
+  struct MEDIA_EXPORT ProcessingConfig {
+    ProcessingConfig(base::UnguessableToken id,
+                     AudioProcessingSettings settings);
+    base::UnguessableToken id;
+    AudioProcessingSettings settings;
+  };
+
+  absl::optional<ProcessingConfig> processing;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SOURCE_PARAMETERS_H_
diff --git a/third_party/chromium/media/audio/audio_system.cc b/third_party/chromium/media/audio/audio_system.cc
new file mode 100644
index 0000000..81c58aa
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_system.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_system.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "media/audio/audio_device_description.h"
+
+namespace media {
+
+// static
+AudioSystem::OnDeviceDescriptionsCallback
+AudioSystem::WrapCallbackWithDeviceNameLocalization(
+    OnDeviceDescriptionsCallback callback) {
+  return base::BindOnce(
+      [](OnDeviceDescriptionsCallback cb,
+         media::AudioDeviceDescriptions descriptions) {
+        media::AudioDeviceDescription::LocalizeDeviceDescriptions(
+            &descriptions);
+        std::move(cb).Run(std::move(descriptions));
+      },
+      std::move(callback));
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_system.h b/third_party/chromium/media/audio/audio_system.h
new file mode 100644
index 0000000..9701570
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_system.h
@@ -0,0 +1,88 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_SYSTEM_H_
+#define MEDIA_AUDIO_AUDIO_SYSTEM_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "media/audio/audio_device_description.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+// Provides asynchronous interface to access audio device information
+class MEDIA_EXPORT AudioSystem {
+ public:
+  // Replies are sent asynchronously to the thread the calls are issued on.
+  // Instance is bound to the thread it's called on the first time.
+  // Attention! Audio system thread may outlive the client
+  // objects; bind callbacks with care.
+
+  // Non-empty optional AudioParameters are guaranteed to be valid.
+  // If optional AudioParameters are empty, it means the specified device is not
+  // found. This is best-effort: non-empty parameters do not guarantee existence
+  // of the device.
+  // TODO(olka,tommi): fix all AudioManager implementations to always report
+  // when a device is not found, instead of returning sub parameter values.
+  // Non-empty optional matched output device id is guaranteed to be a non-empty
+  // std::string. If optional matched output device id is empty, it means there
+  // is no associated output device.
+  using OnAudioParamsCallback =
+      base::OnceCallback<void(const absl::optional<AudioParameters>&)>;
+  using OnDeviceIdCallback =
+      base::OnceCallback<void(const absl::optional<std::string>&)>;
+  using OnInputDeviceInfoCallback =
+      base::OnceCallback<void(const absl::optional<AudioParameters>&,
+                              const absl::optional<std::string>&)>;
+
+  using OnBoolCallback = base::OnceCallback<void(bool)>;
+  using OnDeviceDescriptionsCallback =
+      base::OnceCallback<void(AudioDeviceDescriptions)>;
+
+  virtual ~AudioSystem() = default;
+
+  virtual void GetInputStreamParameters(const std::string& device_id,
+                                        OnAudioParamsCallback on_params_cb) = 0;
+
+  virtual void GetOutputStreamParameters(
+      const std::string& device_id,
+      OnAudioParamsCallback on_params_cb) = 0;
+
+  virtual void HasInputDevices(OnBoolCallback on_has_devices_cb) = 0;
+
+  virtual void HasOutputDevices(OnBoolCallback on_has_devices_cb) = 0;
+
+  // Replies with device descriptions of input audio devices if |for_input| is
+  // true, and of output audio devices otherwise.
+  virtual void GetDeviceDescriptions(
+      bool for_input,
+      OnDeviceDescriptionsCallback on_descriptions_cb) = 0;
+
+  // Replies with an empty optional if there is no associated output device
+  // found and a non-empty string otherwise.
+  virtual void GetAssociatedOutputDeviceID(
+      const std::string& input_device_id,
+      OnDeviceIdCallback on_device_id_cb) = 0;
+
+  // Replies with audio parameters for the specified input device and
+  // device ID of the associated output device, if any (otherwise
+  // the associated output device ID is an empty optional).
+  virtual void GetInputDeviceInfo(
+      const std::string& input_device_id,
+      OnInputDeviceInfoCallback on_input_device_info_cb) = 0;
+
+  // This function wraps |callback| with a call to
+  // AudioDeviceDescription::LocalizeDeviceDescriptions for convenience. This is
+  // typically used by AudioSystem implementations, not AudioSystem clients.
+  static OnDeviceDescriptionsCallback WrapCallbackWithDeviceNameLocalization(
+      OnDeviceDescriptionsCallback callback);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SYSTEM_H_
diff --git a/third_party/chromium/media/audio/audio_system_helper.cc b/third_party/chromium/media/audio/audio_system_helper.cc
new file mode 100644
index 0000000..ad026bc
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_system_helper.cc
@@ -0,0 +1,145 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_system_helper.h"
+
+#include "base/single_thread_task_runner.h"
+#include "media/audio/audio_manager.h"
+#include "media/base/limits.h"
+
+namespace media {
+
+namespace {
+
+absl::optional<AudioParameters> TryToFixChannels(
+    const AudioParameters& params) {
+  DCHECK(!params.IsValid());
+  AudioParameters params_copy(params);
+
+  // If the number of output channels is greater than the maximum, use the
+  // maximum allowed value. Hardware channels are ignored upstream, so it is
+  // better to report a valid value if this is the only problem.
+  if (params.channels() > limits::kMaxChannels) {
+    DCHECK(params.channel_layout() == CHANNEL_LAYOUT_DISCRETE);
+    params_copy.set_channels_for_discrete(limits::kMaxChannels);
+  }
+
+  return params_copy.IsValid() ? params_copy
+                               : absl::optional<AudioParameters>();
+}
+
+}  // namespace
+
+AudioSystemHelper::AudioSystemHelper(AudioManager* audio_manager)
+    : audio_manager_(audio_manager) {
+  DCHECK(audio_manager_);
+}
+
+AudioSystemHelper::~AudioSystemHelper() = default;
+
+void AudioSystemHelper::GetInputStreamParameters(
+    const std::string& device_id,
+    AudioSystem::OnAudioParamsCallback on_params_cb) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  std::move(on_params_cb).Run(ComputeInputParameters(device_id));
+}
+
+void AudioSystemHelper::GetOutputStreamParameters(
+    const std::string& device_id,
+    AudioSystem::OnAudioParamsCallback on_params_cb) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  std::move(on_params_cb).Run(ComputeOutputParameters(device_id));
+}
+
+void AudioSystemHelper::HasInputDevices(
+    AudioSystem::OnBoolCallback on_has_devices_cb) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  std::move(on_has_devices_cb).Run(audio_manager_->HasAudioInputDevices());
+}
+
+void AudioSystemHelper::HasOutputDevices(
+    AudioSystem::OnBoolCallback on_has_devices_cb) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  std::move(on_has_devices_cb).Run(audio_manager_->HasAudioOutputDevices());
+}
+
+void AudioSystemHelper::GetDeviceDescriptions(
+    bool for_input,
+    AudioSystem::OnDeviceDescriptionsCallback on_descriptions_cb) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  AudioDeviceDescriptions descriptions;
+  if (for_input)
+    audio_manager_->GetAudioInputDeviceDescriptions(&descriptions);
+  else
+    audio_manager_->GetAudioOutputDeviceDescriptions(&descriptions);
+  std::move(on_descriptions_cb).Run(std::move(descriptions));
+}
+
+void AudioSystemHelper::GetAssociatedOutputDeviceID(
+    const std::string& input_device_id,
+    AudioSystem::OnDeviceIdCallback on_device_id_cb) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  const std::string associated_output_device_id =
+      audio_manager_->GetAssociatedOutputDeviceID(input_device_id);
+  std::move(on_device_id_cb)
+      .Run(associated_output_device_id.empty() ? absl::optional<std::string>()
+                                               : associated_output_device_id);
+}
+
+void AudioSystemHelper::GetInputDeviceInfo(
+    const std::string& input_device_id,
+    AudioSystem::OnInputDeviceInfoCallback on_input_device_info_cb) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  const std::string associated_output_device_id =
+      audio_manager_->GetAssociatedOutputDeviceID(input_device_id);
+  std::move(on_input_device_info_cb)
+      .Run(ComputeInputParameters(input_device_id),
+           associated_output_device_id.empty() ? absl::optional<std::string>()
+                                               : associated_output_device_id);
+}
+
+absl::optional<AudioParameters> AudioSystemHelper::ComputeInputParameters(
+    const std::string& device_id) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+
+  // TODO(olka): remove this when AudioManager::GetInputStreamParameters()
+  // returns invalid parameters if the device is not found.
+  if (AudioDeviceDescription::IsLoopbackDevice(device_id)) {
+    // For system audio capture, we need an output device (namely speaker)
+    // instead of an input device (namely microphone) to work.
+    // AudioManager::GetInputStreamParameters will check |device_id| and
+    // query the correct device for audio parameters by itself.
+    if (!audio_manager_->HasAudioOutputDevices())
+      return absl::optional<AudioParameters>();
+  } else {
+    if (!audio_manager_->HasAudioInputDevices())
+      return absl::optional<AudioParameters>();
+  }
+
+  AudioParameters params = audio_manager_->GetInputStreamParameters(device_id);
+  return params.IsValid() ? params : TryToFixChannels(params);
+}
+
+absl::optional<AudioParameters> AudioSystemHelper::ComputeOutputParameters(
+    const std::string& device_id) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+
+  // TODO(olka): remove this when
+  // AudioManager::Get[Default]OutputStreamParameters() returns invalid
+  // parameters if the device is not found.
+  if (!audio_manager_->HasAudioOutputDevices())
+    return absl::optional<AudioParameters>();
+
+  AudioParameters params =
+      AudioDeviceDescription::IsDefaultDevice(device_id)
+          ? audio_manager_->GetDefaultOutputStreamParameters()
+          : audio_manager_->GetOutputStreamParameters(device_id);
+
+  if (params.IsValid())
+    return params;
+
+  return TryToFixChannels(params);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_system_helper.h b/third_party/chromium/media/audio/audio_system_helper.h
new file mode 100644
index 0000000..30897d9
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_system_helper.h
@@ -0,0 +1,62 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_SYSTEM_HELPER_H_
+#define MEDIA_AUDIO_AUDIO_SYSTEM_HELPER_H_
+
+#include "media/audio/audio_system.h"
+#include "media/base/media_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+class AudioManager;
+
+// Helper class wrapping AudioManager functionality. Methods to be called on
+// audio thread only. Only audio system implementations are allowed to use it.
+// See AudioSystem interface for method descriptions.
+class MEDIA_EXPORT AudioSystemHelper {
+ public:
+  AudioSystemHelper(AudioManager* audio_manager);
+
+  AudioSystemHelper(const AudioSystemHelper&) = delete;
+  AudioSystemHelper& operator=(const AudioSystemHelper&) = delete;
+
+  ~AudioSystemHelper();
+
+  void GetInputStreamParameters(
+      const std::string& device_id,
+      AudioSystem::OnAudioParamsCallback on_params_cb);
+
+  void GetOutputStreamParameters(
+      const std::string& device_id,
+      AudioSystem::OnAudioParamsCallback on_params_cb);
+
+  void HasInputDevices(AudioSystem::OnBoolCallback on_has_devices_cb);
+
+  void HasOutputDevices(AudioSystem::OnBoolCallback on_has_devices_cb);
+
+  void GetDeviceDescriptions(
+      bool for_input,
+      AudioSystem::OnDeviceDescriptionsCallback on_descriptions_cp);
+
+  void GetAssociatedOutputDeviceID(
+      const std::string& input_device_id,
+      AudioSystem::OnDeviceIdCallback on_device_id_cb);
+
+  void GetInputDeviceInfo(
+      const std::string& input_device_id,
+      AudioSystem::OnInputDeviceInfoCallback on_input_device_info_cb);
+
+ private:
+  absl::optional<AudioParameters> ComputeInputParameters(
+      const std::string& device_id);
+  absl::optional<AudioParameters> ComputeOutputParameters(
+      const std::string& device_id);
+
+  AudioManager* const audio_manager_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SYSTEM_HELPER_H_
diff --git a/third_party/chromium/media/audio/audio_system_impl.cc b/third_party/chromium/media/audio/audio_system_impl.cc
new file mode 100644
index 0000000..7a3efa5
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_system_impl.cc
@@ -0,0 +1,181 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_system_impl.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task_runner_util.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_manager.h"
+#include "media/base/bind_to_current_loop.h"
+
+// Using base::Unretained for |audio_manager_| is safe since AudioManager is
+// deleted after audio thread is stopped.
+
+// No need to bind the callback to the current loop if we are on the audio
+// thread. However, the client still expects to receive the reply
+// asynchronously, so we always post the helper function, which will
+// syncronously call the (bound to current loop or not) callback. Thus the
+// client always receives the callback on the thread it accesses AudioSystem on.
+
+namespace media {
+
+namespace {
+
+void GetInputStreamParametersOnAudioThread(
+    AudioManager* audio_manager,
+    const std::string& device_id,
+    AudioSystem::OnAudioParamsCallback on_params_cb) {
+  AudioSystemHelper(audio_manager)
+      .GetInputStreamParameters(device_id, std::move(on_params_cb));
+}
+
+void GetOutputStreamParametersOnAudioThread(
+    AudioManager* audio_manager,
+    const std::string& device_id,
+    AudioSystem::OnAudioParamsCallback on_params_cb) {
+  AudioSystemHelper(audio_manager)
+      .GetOutputStreamParameters(device_id, std::move(on_params_cb));
+}
+
+void HasInputDevicesOnAudioThread(
+    AudioManager* audio_manager,
+    AudioSystem::OnBoolCallback on_has_devices_cb) {
+  AudioSystemHelper(audio_manager)
+      .HasInputDevices(std::move(on_has_devices_cb));
+}
+
+void HasOutputDevicesOnAudioThread(
+    AudioManager* audio_manager,
+    AudioSystem::OnBoolCallback on_has_devices_cb) {
+  AudioSystemHelper(audio_manager)
+      .HasOutputDevices(std::move(on_has_devices_cb));
+}
+
+void GetDeviceDescriptionsOnAudioThread(
+    AudioManager* audio_manager,
+    bool for_input,
+    AudioSystem::OnDeviceDescriptionsCallback on_descriptions_cb) {
+  AudioSystemHelper(audio_manager)
+      .GetDeviceDescriptions(for_input, std::move(on_descriptions_cb));
+}
+
+void GetAssociatedOutputDeviceIDOnAudioThread(
+    AudioManager* audio_manager,
+    const std::string& input_device_id,
+    AudioSystem::OnDeviceIdCallback on_device_id_cb) {
+  AudioSystemHelper(audio_manager)
+      .GetAssociatedOutputDeviceID(input_device_id, std::move(on_device_id_cb));
+}
+
+void GetInputDeviceInfoOnAudioThread(
+    AudioManager* audio_manager,
+    const std::string& input_device_id,
+    AudioSystem::OnInputDeviceInfoCallback on_input_device_info_cb) {
+  AudioSystemHelper(audio_manager)
+      .GetInputDeviceInfo(input_device_id, std::move(on_input_device_info_cb));
+}
+
+}  // namespace
+
+template <typename... Args>
+inline base::OnceCallback<void(Args...)>
+AudioSystemImpl::MaybeBindToCurrentLoop(
+    base::OnceCallback<void(Args...)> callback) {
+  return audio_manager_->GetTaskRunner()->BelongsToCurrentThread()
+             ? std::move(callback)
+             : media::BindToCurrentLoop(std::move(callback));
+}
+
+// static
+std::unique_ptr<AudioSystem> AudioSystemImpl::CreateInstance() {
+  DCHECK(AudioManager::Get()) << "AudioManager instance is not created";
+  return std::make_unique<AudioSystemImpl>(AudioManager::Get());
+}
+
+AudioSystemImpl::AudioSystemImpl(AudioManager* audio_manager)
+    : audio_manager_(audio_manager) {
+  DETACH_FROM_THREAD(thread_checker_);
+}
+
+void AudioSystemImpl::GetInputStreamParameters(
+    const std::string& device_id,
+    OnAudioParamsCallback on_params_cb) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_manager_->GetTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&GetInputStreamParametersOnAudioThread,
+                     base::Unretained(audio_manager_), device_id,
+                     MaybeBindToCurrentLoop(std::move(on_params_cb))));
+}
+
+void AudioSystemImpl::GetOutputStreamParameters(
+    const std::string& device_id,
+    OnAudioParamsCallback on_params_cb) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_manager_->GetTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&GetOutputStreamParametersOnAudioThread,
+                     base::Unretained(audio_manager_), device_id,
+                     MaybeBindToCurrentLoop(std::move(on_params_cb))));
+}
+
+void AudioSystemImpl::HasInputDevices(OnBoolCallback on_has_devices_cb) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_manager_->GetTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&HasInputDevicesOnAudioThread,
+                     base::Unretained(audio_manager_),
+                     MaybeBindToCurrentLoop(std::move(on_has_devices_cb))));
+}
+
+void AudioSystemImpl::HasOutputDevices(OnBoolCallback on_has_devices_cb) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_manager_->GetTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&HasOutputDevicesOnAudioThread,
+                     base::Unretained(audio_manager_),
+                     MaybeBindToCurrentLoop(std::move(on_has_devices_cb))));
+}
+
+void AudioSystemImpl::GetDeviceDescriptions(
+    bool for_input,
+    OnDeviceDescriptionsCallback on_descriptions_cb) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_manager_->GetTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&GetDeviceDescriptionsOnAudioThread,
+                                base::Unretained(audio_manager_), for_input,
+                                MaybeBindToCurrentLoop(
+                                    WrapCallbackWithDeviceNameLocalization(
+                                        std::move(on_descriptions_cb)))));
+}
+
+void AudioSystemImpl::GetAssociatedOutputDeviceID(
+    const std::string& input_device_id,
+    OnDeviceIdCallback on_device_id_cb) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_manager_->GetTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&GetAssociatedOutputDeviceIDOnAudioThread,
+                     base::Unretained(audio_manager_), input_device_id,
+                     MaybeBindToCurrentLoop(std::move(on_device_id_cb))));
+}
+
+void AudioSystemImpl::GetInputDeviceInfo(
+    const std::string& input_device_id,
+    OnInputDeviceInfoCallback on_input_device_info_cb) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_manager_->GetTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &GetInputDeviceInfoOnAudioThread, base::Unretained(audio_manager_),
+          input_device_id,
+          MaybeBindToCurrentLoop(std::move(on_input_device_info_cb))));
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_system_impl.h b/third_party/chromium/media/audio/audio_system_impl.h
new file mode 100644
index 0000000..59fc179
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_system_impl.h
@@ -0,0 +1,64 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_SYSTEM_IMPL_H_
+#define MEDIA_AUDIO_AUDIO_SYSTEM_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/audio_system.h"
+#include "media/audio/audio_system_helper.h"
+
+namespace media {
+class AudioManager;
+
+class MEDIA_EXPORT AudioSystemImpl : public AudioSystem {
+ public:
+  // Creates AudioSystem using the global AudioManager instance, which must be
+  // created prior to that.
+  static std::unique_ptr<AudioSystem> CreateInstance();
+
+  explicit AudioSystemImpl(AudioManager* audio_manager);
+
+  // AudioSystem implementation.
+  void GetInputStreamParameters(const std::string& device_id,
+                                OnAudioParamsCallback on_params_cb) override;
+
+  void GetOutputStreamParameters(const std::string& device_id,
+                                 OnAudioParamsCallback on_params_cb) override;
+
+  void HasInputDevices(OnBoolCallback on_has_devices_cb) override;
+
+  void HasOutputDevices(OnBoolCallback on_has_devices_cb) override;
+
+  void GetDeviceDescriptions(
+      bool for_input,
+      OnDeviceDescriptionsCallback on_descriptions_cp) override;
+
+  void GetAssociatedOutputDeviceID(const std::string& input_device_id,
+                                   OnDeviceIdCallback on_device_id_cb) override;
+
+  void GetInputDeviceInfo(
+      const std::string& input_device_id,
+      OnInputDeviceInfoCallback on_input_device_info_cb) override;
+
+ private:
+  // No-op if called on helper_.GetTaskRunner() thread, otherwise binds
+  // |callback| to the current loop.
+  template <typename... Args>
+  base::OnceCallback<void(Args...)> MaybeBindToCurrentLoop(
+      base::OnceCallback<void(Args...)> callback);
+
+  THREAD_CHECKER(thread_checker_);
+  AudioManager* const audio_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioSystemImpl);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SYSTEM_IMPL_H_
diff --git a/third_party/chromium/media/audio/audio_system_impl_unittest.cc b/third_party/chromium/media/audio/audio_system_impl_unittest.cc
new file mode 100644
index 0000000..8375d35
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_system_impl_unittest.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_system_impl.h"
+
+#include "base/test/task_environment.h"
+#include "media/audio/audio_system_test_util.h"
+#include "media/audio/audio_thread_impl.h"
+#include "media/audio/mock_audio_manager.h"
+#include "media/audio/test_audio_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+// TODO(olka): These are the only tests for AudioSystemHelper. Make sure that
+// AudioSystemHelper is tested if AudioSystemImpl goes away.
+
+// Typed tests cannot be parametrized, so using template parameter instead of
+// inheriting from TestWithParams<>
+template <bool use_audio_thread>
+class AudioSystemImplTestBase : public testing::Test {
+ public:
+  AudioSystemImplTestBase() = default;
+
+  ~AudioSystemImplTestBase() override = default;
+
+  void SetUp() override {
+    audio_manager_ = std::make_unique<MockAudioManager>(
+        std::make_unique<TestAudioThread>(use_audio_thread));
+    audio_system_ = std::make_unique<AudioSystemImpl>(audio_manager_.get());
+  }
+  void TearDown() override { audio_manager_->Shutdown(); }
+
+ protected:
+  MockAudioManager* audio_manager() { return audio_manager_.get(); }
+  AudioSystem* audio_system() { return audio_system_.get(); }
+
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  std::unique_ptr<MockAudioManager> audio_manager_;
+  std::unique_ptr<AudioSystem> audio_system_;
+  // AudioSystemTester tester_;
+};
+
+using AudioSystemTestBaseVariations =
+    testing::Types<AudioSystemImplTestBase<false>,
+                   AudioSystemImplTestBase<true>>;
+
+INSTANTIATE_TYPED_TEST_SUITE_P(AudioSystemImpl,
+                               AudioSystemTestTemplate,
+                               AudioSystemTestBaseVariations);
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_system_test_util.cc b/third_party/chromium/media/audio/audio_system_test_util.cc
new file mode 100644
index 0000000..7e31a7f
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_system_test_util.cc
@@ -0,0 +1,151 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_system_test_util.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+
+namespace media {
+
+bool operator==(const AudioDeviceDescription& lhs,
+                const AudioDeviceDescription& rhs) {
+  return lhs.device_name == rhs.device_name && lhs.unique_id == rhs.unique_id &&
+         lhs.group_id == rhs.group_id;
+}
+
+AudioSystem::OnAudioParamsCallback
+AudioSystemCallbackExpectations::GetAudioParamsCallback(
+    const base::Location& location,
+    base::OnceClosure on_cb_received,
+    const absl::optional<AudioParameters>& expected_params) {
+  return base::BindOnce(&AudioSystemCallbackExpectations::OnAudioParams,
+                        base::Unretained(this), location.ToString(),
+                        std::move(on_cb_received), expected_params);
+}
+
+AudioSystem::OnBoolCallback AudioSystemCallbackExpectations::GetBoolCallback(
+    const base::Location& location,
+    base::OnceClosure on_cb_received,
+    bool expected) {
+  return base::BindOnce(&AudioSystemCallbackExpectations::OnBool,
+                        base::Unretained(this), location.ToString(),
+                        std::move(on_cb_received), expected);
+}
+
+AudioSystem::OnDeviceDescriptionsCallback
+AudioSystemCallbackExpectations::GetDeviceDescriptionsCallback(
+    const base::Location& location,
+    base::OnceClosure on_cb_received,
+    const AudioDeviceDescriptions& expected_descriptions) {
+  return base::BindOnce(&AudioSystemCallbackExpectations::OnDeviceDescriptions,
+                        base::Unretained(this), location.ToString(),
+                        std::move(on_cb_received), expected_descriptions);
+}
+
+AudioSystem::OnInputDeviceInfoCallback
+AudioSystemCallbackExpectations::GetInputDeviceInfoCallback(
+    const base::Location& location,
+    base::OnceClosure on_cb_received,
+    const absl::optional<AudioParameters>& expected_input,
+    const absl::optional<std::string>& expected_associated_device_id) {
+  return base::BindOnce(&AudioSystemCallbackExpectations::OnInputDeviceInfo,
+                        base::Unretained(this), location.ToString(),
+                        std::move(on_cb_received), expected_input,
+                        expected_associated_device_id);
+}
+
+AudioSystem::OnDeviceIdCallback
+AudioSystemCallbackExpectations::GetDeviceIdCallback(
+    const base::Location& location,
+    base::OnceClosure on_cb_received,
+    const absl::optional<std::string>& expected_id) {
+  return base::BindOnce(&AudioSystemCallbackExpectations::OnDeviceId,
+                        base::Unretained(this), location.ToString(),
+                        std::move(on_cb_received), expected_id);
+}
+
+void AudioSystemCallbackExpectations::OnAudioParams(
+    const std::string& from_here,
+    base::OnceClosure on_cb_received,
+    const absl::optional<AudioParameters>& expected,
+    const absl::optional<AudioParameters>& received) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_, from_here);
+  if (expected) {
+    EXPECT_TRUE(received) << from_here;
+    EXPECT_EQ(expected->AsHumanReadableString(),
+              received->AsHumanReadableString())
+        << from_here;
+  } else {
+    EXPECT_FALSE(received) << from_here;
+  }
+  std::move(on_cb_received).Run();
+}
+
+void AudioSystemCallbackExpectations::OnBool(const std::string& from_here,
+                                             base::OnceClosure on_cb_received,
+                                             bool expected,
+                                             bool result) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_, from_here);
+  EXPECT_EQ(expected, result) << from_here;
+  std::move(on_cb_received).Run();
+}
+
+void AudioSystemCallbackExpectations::OnDeviceDescriptions(
+    const std::string& from_here,
+    base::OnceClosure on_cb_received,
+    const AudioDeviceDescriptions& expected_descriptions,
+    AudioDeviceDescriptions descriptions) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  EXPECT_EQ(expected_descriptions, descriptions);
+  std::move(on_cb_received).Run();
+}
+
+void AudioSystemCallbackExpectations::OnInputDeviceInfo(
+    const std::string& from_here,
+    base::OnceClosure on_cb_received,
+    const absl::optional<AudioParameters>& expected_input,
+    const absl::optional<std::string>& expected_associated_device_id,
+    const absl::optional<AudioParameters>& input,
+    const absl::optional<std::string>& associated_device_id) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_, from_here);
+  EXPECT_TRUE(!input || input->IsValid());
+  if (expected_input) {
+    EXPECT_TRUE(input) << from_here;
+    EXPECT_EQ(expected_input->AsHumanReadableString(),
+              input->AsHumanReadableString())
+        << from_here;
+  } else {
+    EXPECT_FALSE(input) << from_here;
+  }
+  EXPECT_TRUE(!associated_device_id || !associated_device_id->empty());
+  if (expected_associated_device_id) {
+    EXPECT_TRUE(associated_device_id) << from_here;
+    EXPECT_EQ(expected_associated_device_id, associated_device_id) << from_here;
+  } else {
+    EXPECT_FALSE(associated_device_id) << from_here;
+  }
+  std::move(on_cb_received).Run();
+}
+
+void AudioSystemCallbackExpectations::OnDeviceId(
+    const std::string& from_here,
+    base::OnceClosure on_cb_received,
+    const absl::optional<std::string>& expected_id,
+    const absl::optional<std::string>& result_id) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_, from_here);
+  EXPECT_TRUE(!result_id || !result_id->empty());
+  if (expected_id) {
+    EXPECT_TRUE(result_id) << from_here;
+    EXPECT_EQ(expected_id, result_id) << from_here;
+  } else {
+    EXPECT_FALSE(result_id) << from_here;
+  }
+  std::move(on_cb_received).Run();
+}
+
+// This suite is instantiated in binaries that use //media:test_support.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioSystemTestTemplate);
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_system_test_util.h b/third_party/chromium/media/audio/audio_system_test_util.h
new file mode 100644
index 0000000..b14c1cc
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_system_test_util.h
@@ -0,0 +1,366 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_SYSTEM_TEST_UTIL_H_
+#define MEDIA_AUDIO_AUDIO_SYSTEM_TEST_UTIL_H_
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_system.h"
+#include "media/audio/mock_audio_manager.h"
+#include "media/base/audio_parameters.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+// For tests only. Creates AudioSystem callbacks to be passed to AudioSystem
+// methods. When AudioSystem calls such a callback, it verifies treading
+// expectations and checks recieved parameters against expected values passed
+// during its creation. After that it calls |on_cb_received| closure.
+// Note AudioSystemCallbackExpectations object must outlive all the callbacks
+// it produced, since they contain raw pointers to it.
+class AudioSystemCallbackExpectations {
+ public:
+  AudioSystemCallbackExpectations() = default;
+  AudioSystem::OnAudioParamsCallback GetAudioParamsCallback(
+      const base::Location& location,
+      base::OnceClosure on_cb_received,
+      const absl::optional<AudioParameters>& expected_params);
+
+  AudioSystem::OnBoolCallback GetBoolCallback(const base::Location& location,
+                                              base::OnceClosure on_cb_received,
+                                              bool expected);
+
+  AudioSystem::OnDeviceDescriptionsCallback GetDeviceDescriptionsCallback(
+      const base::Location& location,
+      base::OnceClosure on_cb_received,
+      const AudioDeviceDescriptions& expected_descriptions);
+
+  AudioSystem::OnInputDeviceInfoCallback GetInputDeviceInfoCallback(
+      const base::Location& location,
+      base::OnceClosure on_cb_received,
+      const absl::optional<AudioParameters>& expected_input,
+      const absl::optional<std::string>& expected_associated_device_id);
+
+  AudioSystem::OnDeviceIdCallback GetDeviceIdCallback(
+      const base::Location& location,
+      base::OnceClosure on_cb_received,
+      const absl::optional<std::string>& expected_id);
+
+ private:
+  // Methods to verify correctness of received data.
+  void OnAudioParams(const std::string& from_here,
+                     base::OnceClosure on_cb_received,
+                     const absl::optional<AudioParameters>& expected,
+                     const absl::optional<AudioParameters>& received);
+
+  void OnBool(const std::string& from_here,
+              base::OnceClosure on_cb_received,
+              bool expected,
+              bool result);
+
+  void OnDeviceDescriptions(
+      const std::string& from_here,
+      base::OnceClosure on_cb_received,
+      const AudioDeviceDescriptions& expected_descriptions,
+      AudioDeviceDescriptions descriptions);
+
+  void OnInputDeviceInfo(
+      const std::string& from_here,
+      base::OnceClosure on_cb_received,
+      const absl::optional<AudioParameters>& expected_input,
+      const absl::optional<std::string>& expected_associated_device_id,
+      const absl::optional<AudioParameters>& input,
+      const absl::optional<std::string>& associated_device_id);
+
+  void OnDeviceId(const std::string& from_here,
+                  base::OnceClosure on_cb_received,
+                  const absl::optional<std::string>& expected_id,
+                  const absl::optional<std::string>& result_id);
+
+  THREAD_CHECKER(thread_checker_);
+  DISALLOW_COPY_AND_ASSIGN(AudioSystemCallbackExpectations);
+};
+
+// Template test case to test AudioSystem implementations.
+template <class T>
+class AudioSystemTestTemplate : public T {
+ public:
+  AudioSystemTestTemplate() {}
+
+  AudioSystemTestTemplate(const AudioSystemTestTemplate&) = delete;
+  AudioSystemTestTemplate& operator=(const AudioSystemTestTemplate&) = delete;
+
+  ~AudioSystemTestTemplate() override {}
+
+  void SetUp() override {
+    T::SetUp();
+    input_params_ =
+        AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
+                        AudioParameters::kTelephoneSampleRate,
+                        AudioParameters::kTelephoneSampleRate / 10);
+    output_params_ =
+        AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
+                        AudioParameters::kTelephoneSampleRate,
+                        AudioParameters::kTelephoneSampleRate / 20);
+    default_output_params_ =
+        AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
+                        AudioParameters::kTelephoneSampleRate,
+                        AudioParameters::kTelephoneSampleRate / 30);
+    audio_manager()->SetInputStreamParameters(input_params_);
+    audio_manager()->SetOutputStreamParameters(output_params_);
+    audio_manager()->SetDefaultOutputStreamParameters(default_output_params_);
+
+    auto get_device_descriptions = [](const AudioDeviceDescriptions* source,
+                                      AudioDeviceDescriptions* destination) {
+      destination->insert(destination->end(), source->begin(), source->end());
+    };
+
+    audio_manager()->SetInputDeviceDescriptionsCallback(
+        base::BindRepeating(get_device_descriptions,
+                            base::Unretained(&input_device_descriptions_)));
+    audio_manager()->SetOutputDeviceDescriptionsCallback(
+        base::BindRepeating(get_device_descriptions,
+                            base::Unretained(&output_device_descriptions_)));
+  }
+
+ protected:
+  MockAudioManager* audio_manager() { return T::audio_manager(); }
+  AudioSystem* audio_system() { return T::audio_system(); }
+
+  AudioSystemCallbackExpectations expectations_;
+  AudioParameters input_params_;
+  AudioParameters output_params_;
+  AudioParameters default_output_params_;
+  AudioDeviceDescriptions input_device_descriptions_;
+  AudioDeviceDescriptions output_device_descriptions_;
+};
+
+TYPED_TEST_SUITE_P(AudioSystemTestTemplate);
+
+TYPED_TEST_P(AudioSystemTestTemplate, GetInputStreamParametersNormal) {
+  base::RunLoop wait_loop;
+  this->audio_system()->GetInputStreamParameters(
+      AudioDeviceDescription::kDefaultDeviceId,
+      this->expectations_.GetAudioParamsCallback(
+          FROM_HERE, wait_loop.QuitClosure(), this->input_params_));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, GetInputStreamParametersNoDevice) {
+  this->audio_manager()->SetHasInputDevices(false);
+
+  base::RunLoop wait_loop;
+  this->audio_system()->GetInputStreamParameters(
+      AudioDeviceDescription::kDefaultDeviceId,
+      this->expectations_.GetAudioParamsCallback(
+          FROM_HERE, wait_loop.QuitClosure(),
+          absl::optional<AudioParameters>()));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, GetOutputStreamParameters) {
+  base::RunLoop wait_loop;
+  this->audio_system()->GetOutputStreamParameters(
+      "non-default-device-id",
+      this->expectations_.GetAudioParamsCallback(
+          FROM_HERE, wait_loop.QuitClosure(), this->output_params_));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, GetDefaultOutputStreamParameters) {
+  base::RunLoop wait_loop;
+  this->audio_system()->GetOutputStreamParameters(
+      AudioDeviceDescription::kDefaultDeviceId,
+      this->expectations_.GetAudioParamsCallback(
+          FROM_HERE, wait_loop.QuitClosure(), this->default_output_params_));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate,
+             GetOutputStreamParametersForDefaultDeviceNoDevices) {
+  this->audio_manager()->SetHasOutputDevices(false);
+  base::RunLoop wait_loop;
+  this->audio_system()->GetOutputStreamParameters(
+      AudioDeviceDescription::kDefaultDeviceId,
+      this->expectations_.GetAudioParamsCallback(
+          FROM_HERE, wait_loop.QuitClosure(),
+          absl::optional<AudioParameters>()));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate,
+             GetOutputStreamParametersForNonDefaultDeviceNoDevices) {
+  this->audio_manager()->SetHasOutputDevices(false);
+  base::RunLoop wait_loop;
+  this->audio_system()->GetOutputStreamParameters(
+      "non-default-device-id", this->expectations_.GetAudioParamsCallback(
+                                   FROM_HERE, wait_loop.QuitClosure(),
+                                   absl::optional<AudioParameters>()));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, HasInputDevices) {
+  base::RunLoop wait_loop;
+  this->audio_system()->HasInputDevices(this->expectations_.GetBoolCallback(
+      FROM_HERE, wait_loop.QuitClosure(), true));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, HasNoInputDevices) {
+  this->audio_manager()->SetHasInputDevices(false);
+  base::RunLoop wait_loop;
+  this->audio_system()->HasInputDevices(this->expectations_.GetBoolCallback(
+      FROM_HERE, wait_loop.QuitClosure(), false));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, HasOutputDevices) {
+  base::RunLoop wait_loop;
+  this->audio_system()->HasOutputDevices(this->expectations_.GetBoolCallback(
+      FROM_HERE, wait_loop.QuitClosure(), true));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, HasNoOutputDevices) {
+  this->audio_manager()->SetHasOutputDevices(false);
+  base::RunLoop wait_loop;
+  this->audio_system()->HasOutputDevices(this->expectations_.GetBoolCallback(
+      FROM_HERE, wait_loop.QuitClosure(), false));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate,
+             GetInputDeviceDescriptionsNoInputDevices) {
+  this->output_device_descriptions_.emplace_back(
+      "output_device_name", "output_device_id", "group_id");
+  EXPECT_EQ(0, static_cast<int>(this->input_device_descriptions_.size()));
+  EXPECT_EQ(1, static_cast<int>(this->output_device_descriptions_.size()));
+
+  base::RunLoop wait_loop;
+  this->audio_system()->GetDeviceDescriptions(
+      true, this->expectations_.GetDeviceDescriptionsCallback(
+                FROM_HERE, wait_loop.QuitClosure(),
+                this->input_device_descriptions_));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, GetInputDeviceDescriptions) {
+  this->output_device_descriptions_.emplace_back(
+      "output_device_name", "output_device_id", "group_id");
+  this->input_device_descriptions_.emplace_back(
+      "input_device_name1", "input_device_id1", "group_id1");
+  this->input_device_descriptions_.emplace_back(
+      "input_device_name2", "input_device_id2", "group_id2");
+  EXPECT_EQ(2, static_cast<int>(this->input_device_descriptions_.size()));
+  EXPECT_EQ(1, static_cast<int>(this->output_device_descriptions_.size()));
+
+  base::RunLoop wait_loop;
+  this->audio_system()->GetDeviceDescriptions(
+      true, this->expectations_.GetDeviceDescriptionsCallback(
+                FROM_HERE, wait_loop.QuitClosure(),
+                this->input_device_descriptions_));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate,
+             GetOutputDeviceDescriptionsNoInputDevices) {
+  this->input_device_descriptions_.emplace_back("input_device_name",
+                                                "input_device_id", "group_id");
+  EXPECT_EQ(0, static_cast<int>(this->output_device_descriptions_.size()));
+  EXPECT_EQ(1, static_cast<int>(this->input_device_descriptions_.size()));
+
+  base::RunLoop wait_loop;
+  this->audio_system()->GetDeviceDescriptions(
+      false, this->expectations_.GetDeviceDescriptionsCallback(
+                 FROM_HERE, wait_loop.QuitClosure(),
+                 this->output_device_descriptions_));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, GetOutputDeviceDescriptions) {
+  this->input_device_descriptions_.emplace_back("input_device_name",
+                                                "input_device_id", "group_id");
+  this->output_device_descriptions_.emplace_back(
+      "output_device_name1", "output_device_id1", "group_id1");
+  this->output_device_descriptions_.emplace_back(
+      "output_device_name2", "output_device_id2", "group_id2");
+  EXPECT_EQ(2, static_cast<int>(this->output_device_descriptions_.size()));
+  EXPECT_EQ(1, static_cast<int>(this->input_device_descriptions_.size()));
+
+  base::RunLoop wait_loop;
+  this->audio_system()->GetDeviceDescriptions(
+      false, this->expectations_.GetDeviceDescriptionsCallback(
+                 FROM_HERE, wait_loop.QuitClosure(),
+                 this->output_device_descriptions_));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, GetAssociatedOutputDeviceID) {
+  const std::string associated_id("associated_id");
+  this->audio_manager()->SetAssociatedOutputDeviceIDCallback(
+      base::BindRepeating([](const std::string& result, const std::string&)
+                              -> std::string { return result; },
+                          associated_id));
+
+  base::RunLoop wait_loop;
+  this->audio_system()->GetAssociatedOutputDeviceID(
+      std::string(), this->expectations_.GetDeviceIdCallback(
+                         FROM_HERE, wait_loop.QuitClosure(), associated_id));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, GetInputDeviceInfoNoAssociation) {
+  base::RunLoop wait_loop;
+  this->audio_system()->GetInputDeviceInfo(
+      "non-default-device-id",
+      this->expectations_.GetInputDeviceInfoCallback(
+          FROM_HERE, wait_loop.QuitClosure(), this->input_params_,
+          absl::optional<std::string>()));
+  wait_loop.Run();
+}
+
+TYPED_TEST_P(AudioSystemTestTemplate, GetInputDeviceInfoWithAssociation) {
+  const std::string associated_id("associated_id");
+  this->audio_manager()->SetAssociatedOutputDeviceIDCallback(
+      base::BindRepeating([](const std::string& result, const std::string&)
+                              -> std::string { return result; },
+                          associated_id));
+
+  base::RunLoop wait_loop;
+  this->audio_system()->GetInputDeviceInfo(
+      "non-default-device-id", this->expectations_.GetInputDeviceInfoCallback(
+                                   FROM_HERE, wait_loop.QuitClosure(),
+                                   this->input_params_, associated_id));
+  wait_loop.Run();
+}
+
+REGISTER_TYPED_TEST_SUITE_P(
+    AudioSystemTestTemplate,
+    GetInputStreamParametersNormal,
+    GetInputStreamParametersNoDevice,
+    GetOutputStreamParameters,
+    GetDefaultOutputStreamParameters,
+    GetOutputStreamParametersForDefaultDeviceNoDevices,
+    GetOutputStreamParametersForNonDefaultDeviceNoDevices,
+    HasInputDevices,
+    HasNoInputDevices,
+    HasOutputDevices,
+    HasNoOutputDevices,
+    GetInputDeviceDescriptionsNoInputDevices,
+    GetInputDeviceDescriptions,
+    GetOutputDeviceDescriptionsNoInputDevices,
+    GetOutputDeviceDescriptions,
+    GetAssociatedOutputDeviceID,
+    GetInputDeviceInfoNoAssociation,
+    GetInputDeviceInfoWithAssociation);
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_SYSTEM_TEST_UTIL_H_
diff --git a/third_party/chromium/media/audio/audio_thread.h b/third_party/chromium/media/audio/audio_thread.h
new file mode 100644
index 0000000..b014b89
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_thread.h
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_THREAD_H_
+#define MEDIA_AUDIO_AUDIO_THREAD_H_
+
+#include "media/base/media_export.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace media {
+
+// This class encapulates the logic for the thread and task runners that the
+// AudioManager and related classes run on.
+class MEDIA_EXPORT AudioThread {
+ public:
+  virtual ~AudioThread() {}
+
+  // Synchronously stops all underlying threads.
+  virtual void Stop() = 0;
+
+  // Indicates whether the audio thread is responsive. If false, calling Stop()
+  // will likely block forever.
+  virtual bool IsHung() const = 0;
+
+  // Returns the task runner used for audio IO.
+  // It always returns a non-null task runner (even after Stop has been called).
+  virtual base::SingleThreadTaskRunner* GetTaskRunner() = 0;
+
+  // Heavyweight tasks should use GetWorkerTaskRunner() instead of
+  // GetTaskRunner(). On most platforms they are the same, but some share the
+  // UI loop with the audio IO loop.
+  // It always returns a non-null task runner (even after Stop has been called).
+  virtual base::SingleThreadTaskRunner* GetWorkerTaskRunner() = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_THREAD_H_
diff --git a/third_party/chromium/media/audio/audio_thread_hang_monitor.cc b/third_party/chromium/media/audio/audio_thread_hang_monitor.cc
new file mode 100644
index 0000000..f69a79b
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_thread_hang_monitor.cc
@@ -0,0 +1,205 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_thread_hang_monitor.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/process/process.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/tick_clock.h"
+
+namespace media {
+
+namespace {
+
+// Maximum number of failed pings to the audio thread allowed. A UMA will be
+// recorded once this count is reached. We require at least three failed pings
+// before recording to ensure unobservable power events aren't mistakenly
+// caught (e.g., the system suspends before a OnSuspend() event can be fired).
+constexpr int kMaxFailedPingsCount = 3;
+
+// The default deadline after which we consider the audio thread hung.
+constexpr base::TimeDelta kDefaultHangDeadline = base::Minutes(3);
+
+}  // namespace
+
+AudioThreadHangMonitor::SharedAtomicFlag::SharedAtomicFlag() {}
+AudioThreadHangMonitor::SharedAtomicFlag::~SharedAtomicFlag() {}
+
+// static
+AudioThreadHangMonitor::Ptr AudioThreadHangMonitor::Create(
+    HangAction hang_action,
+    absl::optional<base::TimeDelta> hang_deadline,
+    const base::TickClock* clock,
+    scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> monitor_task_runner) {
+  if (!monitor_task_runner)
+    monitor_task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
+
+  auto monitor =
+      Ptr(new AudioThreadHangMonitor(hang_action, hang_deadline, clock,
+                                     std::move(audio_thread_task_runner)),
+          base::OnTaskRunnerDeleter(monitor_task_runner));
+
+  // |monitor| is destroyed on |monitor_task_runner|, so Unretained is safe.
+  monitor_task_runner->PostTask(
+      FROM_HERE, base::BindOnce(&AudioThreadHangMonitor::StartTimer,
+                                base::Unretained(monitor.get())));
+  return monitor;
+}
+
+AudioThreadHangMonitor::~AudioThreadHangMonitor() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(monitor_sequence_);
+}
+
+bool AudioThreadHangMonitor::IsAudioThreadHung() const {
+  return audio_thread_status_ == ThreadStatus::kHung;
+}
+
+AudioThreadHangMonitor::AudioThreadHangMonitor(
+    HangAction hang_action,
+    absl::optional<base::TimeDelta> hang_deadline,
+    const base::TickClock* clock,
+    scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner)
+    : clock_(clock),
+      alive_flag_(base::MakeRefCounted<SharedAtomicFlag>()),
+      audio_task_runner_(std::move(audio_thread_task_runner)),
+      hang_action_(hang_action),
+      ping_interval_((hang_deadline ? hang_deadline.value().is_zero()
+                                          ? kDefaultHangDeadline
+                                          : hang_deadline.value()
+                                    : kDefaultHangDeadline) /
+                     kMaxFailedPingsCount),
+      timer_(clock_) {
+  DETACH_FROM_SEQUENCE(monitor_sequence_);
+}
+
+void AudioThreadHangMonitor::StartTimer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(monitor_sequence_);
+
+  // Set the flag to true so that the first run doesn't detect a hang.
+  alive_flag_->flag_ = true;
+
+  last_check_time_ = clock_->NowTicks();
+
+  LogHistogramThreadStatus();
+
+  // |this| owns |timer_|, so Unretained is safe.
+  timer_.Start(
+      FROM_HERE, ping_interval_,
+      base::BindRepeating(&AudioThreadHangMonitor::CheckIfAudioThreadIsAlive,
+                          base::Unretained(this)));
+}
+
+bool AudioThreadHangMonitor::NeverLoggedThreadHung() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(monitor_sequence_);
+  return audio_thread_status_ == ThreadStatus::kStarted;
+}
+
+bool AudioThreadHangMonitor::NeverLoggedThreadRecoveredAfterHung() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(monitor_sequence_);
+  return audio_thread_status_ == ThreadStatus::kHung;
+}
+
+void AudioThreadHangMonitor::CheckIfAudioThreadIsAlive() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(monitor_sequence_);
+
+  const base::TimeDelta time_since_last_check =
+      clock_->NowTicks() - last_check_time_;
+
+  // An unexpected |time_since_last_check| may indicate that the system has been
+  // in sleep mode, in which case the audio thread may have had insufficient
+  // time to respond to the ping. In such a case, skip the check for now.
+  if (time_since_last_check > ping_interval_ + base::Seconds(1))
+    return;
+
+  const bool audio_thread_responded_to_last_ping = alive_flag_->flag_;
+  if (audio_thread_responded_to_last_ping) {
+    recent_ping_state_ = std::max(recent_ping_state_, 0) + 1;
+
+    // Update the thread status if it was previously hung. Will only log
+    // "recovered" once for the lifetime of this object.
+    if (NeverLoggedThreadRecoveredAfterHung() &&
+        recent_ping_state_ >= kMaxFailedPingsCount) {
+      // Require just as many successful pings to recover from failure.
+      audio_thread_status_ = ThreadStatus::kRecovered;
+      LogHistogramThreadStatus();
+    }
+  } else {
+    recent_ping_state_ = std::min(recent_ping_state_, 0) - 1;
+
+    // Update the thread status if it was previously live and has never been
+    // considered hung before. Will only log "hung" once for the lifetime of
+    // this object.
+    if (-recent_ping_state_ >= kMaxFailedPingsCount &&
+        NeverLoggedThreadHung()) {
+      LOG(ERROR)
+          << "Audio thread hang has been detected. You may need to restart "
+             "your browser. Please file a bug at https://crbug.com/new";
+
+      audio_thread_status_ = ThreadStatus::kHung;
+      LogHistogramThreadStatus();
+
+      if (hang_action_ == HangAction::kDump ||
+          hang_action_ == HangAction::kDumpAndTerminateCurrentProcess) {
+        DumpWithoutCrashing();
+      }
+      if (hang_action_ == HangAction::kTerminateCurrentProcess ||
+          hang_action_ == HangAction::kDumpAndTerminateCurrentProcess) {
+        TerminateCurrentProcess();
+      }
+    }
+  }
+
+  alive_flag_->flag_ = false;
+  last_check_time_ = clock_->NowTicks();
+  audio_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](scoped_refptr<SharedAtomicFlag> flag) { flag->flag_ = true; },
+          alive_flag_));
+}
+
+void AudioThreadHangMonitor::LogHistogramThreadStatus() {
+  UMA_HISTOGRAM_ENUMERATION("Media.AudioThreadStatus",
+                            audio_thread_status_.load());
+}
+
+void AudioThreadHangMonitor::SetHangActionCallbacksForTesting(
+    base::RepeatingClosure dump_callback,
+    base::RepeatingClosure terminate_process_callback) {
+  dump_callback_ = std::move(dump_callback);
+  terminate_process_callback_ = std::move(terminate_process_callback);
+}
+
+void AudioThreadHangMonitor::DumpWithoutCrashing() {
+  LOG(ERROR) << "Creating non-crash dump for audio thread hang.";
+  if (!dump_callback_.is_null())
+    dump_callback_.Run();
+  else
+    base::debug::DumpWithoutCrashing();
+}
+
+void AudioThreadHangMonitor::TerminateCurrentProcess() {
+  LOG(ERROR) << "Terminating process for audio thread hang.";
+  if (!terminate_process_callback_.is_null())
+    terminate_process_callback_.Run();
+  else
+    base::Process::TerminateCurrentProcessImmediately(1);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_thread_hang_monitor.h b/third_party/chromium/media/audio/audio_thread_hang_monitor.h
new file mode 100644
index 0000000..f625545
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_thread_hang_monitor.h
@@ -0,0 +1,172 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_THREAD_HANG_MONITOR_H_
+#define MEDIA_AUDIO_AUDIO_THREAD_HANG_MONITOR_H_
+
+#include "media/audio/audio_manager.h"
+
+#include <atomic>
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "media/base/media_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace base {
+class TickClock;
+class SingleThreadTaskRunner;
+}  // namespace base
+
+namespace media {
+
+// This class detects if the audio manager thread is hung. It logs a histogram,
+// and can optionally (if |dump_on_hang| is set) upload a crash dump when a hang
+// is detected. It runs on a task runner from the task scheduler. It works by
+// posting a task to the audio thread every minute and checking that it was
+// executed. If three consecutive such pings are missed, the thread is
+// considered hung.
+class MEDIA_EXPORT AudioThreadHangMonitor final {
+ public:
+  using Ptr =
+      std::unique_ptr<AudioThreadHangMonitor, base::OnTaskRunnerDeleter>;
+
+  // These values are histogrammed over time; do not change their ordinal
+  // values.
+  enum class ThreadStatus {
+    // kNone = 0, obsolete.
+    kStarted = 1,
+    kHung,
+    kRecovered,
+    kMaxValue = kRecovered
+  };
+
+  enum class HangAction {
+    // Do nothing. (UMA logging is always done.)
+    kDoNothing,
+    // A crash dump will be collected the first time the thread is detected as
+    // hung (note that no actual crashing is involved).
+    kDump,
+    // Terminate the current process with exit code 0.
+    kTerminateCurrentProcess,
+    // Terminate the current process with exit code 1, which yields a crash
+    // dump.
+    kDumpAndTerminateCurrentProcess
+  };
+
+  // |monitor_task_runner| may be set explicitly by tests only. Other callers
+  // should use the default. If |hang_deadline| is not provided, or if it's
+  // zero, a default value is used.
+  static Ptr Create(
+      HangAction hang_action,
+      absl::optional<base::TimeDelta> hang_deadline,
+      const base::TickClock* clock,
+      scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner,
+      scoped_refptr<base::SequencedTaskRunner> monitor_task_runner = nullptr);
+
+  AudioThreadHangMonitor(const AudioThreadHangMonitor&) = delete;
+  AudioThreadHangMonitor& operator=(const AudioThreadHangMonitor&) = delete;
+
+  ~AudioThreadHangMonitor();
+
+  // Thread-safe.
+  bool IsAudioThreadHung() const;
+
+ private:
+  friend class AudioThreadHangMonitorTest;
+
+  class SharedAtomicFlag final
+      : public base::RefCountedThreadSafe<SharedAtomicFlag> {
+   public:
+    SharedAtomicFlag();
+
+    std::atomic_bool flag_ = {false};
+
+   private:
+    friend class base::RefCountedThreadSafe<SharedAtomicFlag>;
+    ~SharedAtomicFlag();
+  };
+
+  AudioThreadHangMonitor(
+      HangAction hang_action,
+      absl::optional<base::TimeDelta> hang_deadline,
+      const base::TickClock* clock,
+      scoped_refptr<base::SingleThreadTaskRunner> audio_thread_task_runner);
+
+  void StartTimer();
+
+  bool NeverLoggedThreadHung() const;
+  bool NeverLoggedThreadRecoveredAfterHung() const;
+
+  // This function is run by the |timer_|. It checks if the audio thread has
+  // shown signs of life since the last time it was called (by checking the
+  // |alive_flag_|) and updates the value of |successful_pings_| and
+  // |failed_pings_| as appropriate. It also changes the thread status and logs
+  // its value to a histogram.
+  void CheckIfAudioThreadIsAlive();
+
+  // LogHistogramThreadStatus logs |thread_status_| to a histogram.
+  void LogHistogramThreadStatus();
+
+  // For tests. See below functions.
+  void SetHangActionCallbacksForTesting(
+      base::RepeatingClosure dump_callback,
+      base::RepeatingClosure terminate_process_callback);
+
+  // Thin wrapper functions that either executes the default or runs a callback
+  // set with SetHangActioncallbacksForTesting(), for testing purposes.
+  void DumpWithoutCrashing();
+  void TerminateCurrentProcess();
+
+  const base::TickClock* const clock_;
+
+  // This flag is set to false on the monitor sequence and then set to true on
+  // the audio thread to indicate that the audio thread is alive.
+  const scoped_refptr<SharedAtomicFlag> alive_flag_;
+
+  // |audio_task_runner_| is the task runner of the audio thread.
+  const scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
+
+  // Which action(s) to take when detected hung thread.
+  const HangAction hang_action_;
+
+  // At which interval to ping and see if the thread is running.
+  const base::TimeDelta ping_interval_;
+
+  // For testing. See DumpWithoutCrashing() and TerminateCurrentProcess().
+  base::RepeatingClosure dump_callback_;
+  base::RepeatingClosure terminate_process_callback_;
+
+  std::atomic<ThreadStatus> audio_thread_status_ = {ThreadStatus::kStarted};
+
+  // All fields below are accessed on |monitor_sequence|.
+  SEQUENCE_CHECKER(monitor_sequence_);
+
+  // Timer to check |alive_flag_| regularly.
+  base::RepeatingTimer timer_;
+
+  // This variable is used to check to detect suspend/resume cycles.
+  // If a long time has passed since the timer was last fired, it is likely due
+  // to the machine being suspended. In such a case, we want to avoid falsely
+  // detecting the audio thread as hung.
+  base::TimeTicks last_check_time_ = base::TimeTicks();
+
+  // |recent_ping_state_| tracks the recent life signs from the audio thread. If
+  // the most recent ping was successful, the number indicates the number of
+  // successive successful pings. If the most recent ping was failed, the number
+  // is the negative of the number of successive failed pings.
+  int recent_ping_state_ = 0;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_THREAD_HANG_MONITOR_H_
diff --git a/third_party/chromium/media/audio/audio_thread_hang_monitor_unittest.cc b/third_party/chromium/media/audio/audio_thread_hang_monitor_unittest.cc
new file mode 100644
index 0000000..0412cfb
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_thread_hang_monitor_unittest.cc
@@ -0,0 +1,307 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_thread_hang_monitor.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/post_task.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+using testing::ElementsAre;
+using testing::Test;
+using HangAction = media::AudioThreadHangMonitor::HangAction;
+
+namespace media {
+
+namespace {
+
+constexpr int kStarted =
+    static_cast<int>(AudioThreadHangMonitor::ThreadStatus::kStarted);
+constexpr int kHung =
+    static_cast<int>(AudioThreadHangMonitor::ThreadStatus::kHung);
+constexpr int kRecovered =
+    static_cast<int>(AudioThreadHangMonitor::ThreadStatus::kRecovered);
+
+constexpr base::TimeDelta kShortHangDeadline = base::Seconds(5);
+constexpr base::TimeDelta kLongHangDeadline = base::Minutes(30);
+
+}  // namespace
+
+class AudioThreadHangMonitorTest : public Test {
+ public:
+  AudioThreadHangMonitorTest()
+      : task_env_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+        histograms_(),
+        audio_thread_("Audio thread"),
+        hang_monitor_({nullptr, base::OnTaskRunnerDeleter(nullptr)}) {
+    CHECK(audio_thread_.Start());
+    // We must inject the main thread task runner as the hang monitor task
+    // runner since TaskEnvironment::FastForwardBy only works for the main
+    // thread.
+    hang_monitor_ = AudioThreadHangMonitor::Create(
+        HangAction::kDoNothing, absl::nullopt, task_env_.GetMockTickClock(),
+        audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  }
+
+  ~AudioThreadHangMonitorTest() override {
+    hang_monitor_.reset();
+    task_env_.RunUntilIdle();
+  }
+
+  void SetHangActionCallbacksForTesting() {
+    hang_monitor_->SetHangActionCallbacksForTesting(
+        base::BindRepeating(&AudioThreadHangMonitorTest::HangActionDump,
+                            base::Unretained(this)),
+        base::BindRepeating(&AudioThreadHangMonitorTest::HangActionTerminate,
+                            base::Unretained(this)));
+  }
+
+  void RunUntilIdle() { task_env_.RunUntilIdle(); }
+
+  void FlushAudioThread() {
+    base::WaitableEvent ev;
+    audio_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&ev)));
+    ev.Wait();
+  }
+
+  void BlockAudioThreadUntilEvent() {
+    // We keep |event_| as a member of the test fixture to make sure that the
+    // audio thread terminates before |event_| is destructed.
+    event_.Reset();
+    audio_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&base::WaitableEvent::Wait, base::Unretained(&event_)));
+  }
+
+  MOCK_METHOD0(HangActionDump, void());
+  MOCK_METHOD0(HangActionTerminate, void());
+
+  base::WaitableEvent event_;
+  base::test::TaskEnvironment task_env_;
+  base::HistogramTester histograms_;
+  base::Thread audio_thread_;
+  AudioThreadHangMonitor::Ptr hang_monitor_;
+};
+
+TEST_F(AudioThreadHangMonitorTest, LogsThreadStarted) {
+  RunUntilIdle();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, DoesNotLogThreadHungWhenOk) {
+  RunUntilIdle();
+
+  for (int i = 0; i < 10; ++i) {
+    // Flush the audio thread, then advance the clock. The audio thread should
+    // register as "alive" every time.
+    FlushAudioThread();
+    task_env_.FastForwardBy(base::Minutes(1));
+  }
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, LogsHungWhenAudioThreadIsBlocked) {
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::Minutes(10));
+  event_.Signal();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 1), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, DoesNotLogThreadHungWithShortDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, kShortHangDeadline, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(kShortHangDeadline / 2);
+  event_.Signal();
+
+  // Two started events, one for the originally created hang monitor and one for
+  // the new created here.
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, LogsThreadHungWithShortDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, kShortHangDeadline, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(kShortHangDeadline * 2);
+  event_.Signal();
+
+  // Two started events, one for the originally created hang monitor and one for
+  // the new created here.
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, DoesNotLogThreadHungWithLongDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, kLongHangDeadline, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(kLongHangDeadline / 2);
+  event_.Signal();
+
+  // Two started events, one for the originally created hang monitor and one for
+  // the new created here.
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, LogsThreadHungWithLongDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, kLongHangDeadline, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(kLongHangDeadline * 2);
+  event_.Signal();
+
+  // Two started events, one for the originally created hang monitor and one for
+  // the new created here.
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+// Zero deadline means that the default deadline should be used.
+TEST_F(AudioThreadHangMonitorTest, ZeroDeadlineMeansDefaultDeadline) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDoNothing, base::TimeDelta(), task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  RunUntilIdle();
+
+  for (int i = 0; i < 10; ++i) {
+    // Flush the audio thread, then advance the clock. The audio thread should
+    // register as "alive" every time.
+    FlushAudioThread();
+    task_env_.FastForwardBy(base::Minutes(1));
+  }
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2)));
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::Minutes(10));
+  event_.Signal();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest,
+       LogsRecoveredWhenAudioThreadIsBlockedThenRecovers) {
+  RunUntilIdle();
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::Minutes(10));
+  event_.Signal();
+
+  for (int i = 0; i < 10; ++i) {
+    // Flush the audio thread, then advance the clock. The audio thread should
+    // register as "alive" every time.
+    FlushAudioThread();
+    task_env_.FastForwardBy(base::Minutes(1));
+  }
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 1), base::Bucket(kHung, 1),
+                          base::Bucket(kRecovered, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, NoHangActionWhenOk) {
+  SetHangActionCallbacksForTesting();
+  RunUntilIdle();
+
+  for (int i = 0; i < 10; ++i) {
+    // Flush the audio thread, then advance the clock. The audio thread should
+    // register as "alive" every time.
+    FlushAudioThread();
+    task_env_.FastForwardBy(base::Minutes(1));
+  }
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, DumpsWhenAudioThreadIsBlocked) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDump, absl::nullopt, task_env_.GetMockTickClock(),
+      audio_thread_.task_runner(), task_env_.GetMainThreadTaskRunner());
+  SetHangActionCallbacksForTesting();
+  RunUntilIdle();
+
+  EXPECT_CALL(*this, HangActionDump).Times(1);
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::Minutes(10));
+  event_.Signal();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest, TerminatesProcessWhenAudioThreadIsBlocked) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kTerminateCurrentProcess, absl::nullopt,
+      task_env_.GetMockTickClock(), audio_thread_.task_runner(),
+      task_env_.GetMainThreadTaskRunner());
+  SetHangActionCallbacksForTesting();
+  RunUntilIdle();
+
+  EXPECT_CALL(*this, HangActionTerminate).Times(1);
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::Minutes(10));
+  event_.Signal();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+TEST_F(AudioThreadHangMonitorTest,
+       DumpsAndTerminatesProcessWhenAudioThreadIsBlocked) {
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      HangAction::kDumpAndTerminateCurrentProcess, absl::nullopt,
+      task_env_.GetMockTickClock(), audio_thread_.task_runner(),
+      task_env_.GetMainThreadTaskRunner());
+  SetHangActionCallbacksForTesting();
+  RunUntilIdle();
+
+  EXPECT_CALL(*this, HangActionDump).Times(1);
+  EXPECT_CALL(*this, HangActionTerminate).Times(1);
+
+  BlockAudioThreadUntilEvent();
+  task_env_.FastForwardBy(base::Minutes(10));
+  event_.Signal();
+
+  EXPECT_THAT(histograms_.GetAllSamples("Media.AudioThreadStatus"),
+              ElementsAre(base::Bucket(kStarted, 2), base::Bucket(kHung, 1)));
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_thread_impl.cc b/third_party/chromium/media/audio/audio_thread_impl.cc
new file mode 100644
index 0000000..f1cd692
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_thread_impl.cc
@@ -0,0 +1,76 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_thread_impl.h"
+
+#include "base/message_loop/message_pump_type.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/default_tick_clock.h"
+#include "build/build_config.h"
+#include "media/audio/audio_thread_hang_monitor.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+AudioThreadImpl::AudioThreadImpl()
+    : thread_("AudioThread"),
+      hang_monitor_(nullptr, base::OnTaskRunnerDeleter(nullptr)) {
+  base::Thread::Options thread_options;
+#if defined(OS_WIN)
+  thread_.init_com_with_mta(true);
+#elif defined(OS_FUCHSIA)
+  // FIDL-based APIs require async_t, which is initialized on IO thread.
+  thread_options.message_pump_type = base::MessagePumpType::IO;
+  thread_options.priority = base::ThreadPriority::REALTIME_AUDIO;
+#endif
+  CHECK(thread_.StartWithOptions(std::move(thread_options)));
+
+#if defined(OS_MAC)
+  // On Mac, the audio task runner must belong to the main thread.
+  // See http://crbug.com/158170.
+  task_runner_ = base::ThreadTaskRunnerHandle::Get();
+#else
+  task_runner_ = thread_.task_runner();
+#endif
+  worker_task_runner_ = thread_.task_runner();
+
+#if !defined(OS_MAC) && !defined(OS_ANDROID)
+  // Since we run on the main thread on Mac, we don't need a hang monitor.
+  // https://crbug.com/946968: The hang monitor possibly causes crashes on
+  // Android
+  hang_monitor_ = AudioThreadHangMonitor::Create(
+      AudioThreadHangMonitor::HangAction::kDoNothing, absl::nullopt,
+      base::DefaultTickClock::GetInstance(), task_runner_);
+#endif
+}
+
+AudioThreadImpl::~AudioThreadImpl() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void AudioThreadImpl::Stop() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  hang_monitor_.reset();
+
+  // Note that on MACOSX, we can still have tasks posted on the |task_runner_|,
+  // since it is the main thread task runner and we do not stop the main thread.
+  // But this is fine because none of those tasks will actually run.
+  thread_.Stop();
+}
+
+bool AudioThreadImpl::IsHung() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return hang_monitor_ ? hang_monitor_->IsAudioThreadHung() : false;
+}
+
+base::SingleThreadTaskRunner* AudioThreadImpl::GetTaskRunner() {
+  return task_runner_.get();
+}
+
+base::SingleThreadTaskRunner* AudioThreadImpl::GetWorkerTaskRunner() {
+  return worker_task_runner_.get();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_thread_impl.h b/third_party/chromium/media/audio/audio_thread_impl.h
new file mode 100644
index 0000000..a63a40f
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_thread_impl.h
@@ -0,0 +1,44 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_THREAD_IMPL_H_
+#define MEDIA_AUDIO_AUDIO_THREAD_IMPL_H_
+
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/audio_thread.h"
+#include "media/audio/audio_thread_hang_monitor.h"
+
+namespace media {
+
+class MEDIA_EXPORT AudioThreadImpl final : public AudioThread {
+ public:
+  AudioThreadImpl();
+
+  AudioThreadImpl(const AudioThreadImpl&) = delete;
+  AudioThreadImpl& operator=(const AudioThreadImpl&) = delete;
+
+  ~AudioThreadImpl() final;
+
+  // AudioThread implementation.
+  void Stop() final;
+  bool IsHung() const final;
+  base::SingleThreadTaskRunner* GetTaskRunner() final;
+  base::SingleThreadTaskRunner* GetWorkerTaskRunner() final;
+
+ private:
+  base::Thread thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner_;
+
+  // Null on Mac OS, initialized in the constructor on other platforms.
+  AudioThreadHangMonitor::Ptr hang_monitor_;
+
+  THREAD_CHECKER(thread_checker_);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_THREAD_IMPL_H_
diff --git a/third_party/chromium/media/audio/audio_unittest_util.cc b/third_party/chromium/media/audio/audio_unittest_util.cc
new file mode 100644
index 0000000..0ddf94a
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_unittest_util.cc
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/audio_unittest_util.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "media/base/media_switches.h"
+
+namespace media {
+
+// For macro ABORT_AUDIO_TEST_IF_NOT.
+bool ShouldAbortAudioTest(bool requirements_satisfied,
+                          const char* requirements_expression,
+                          bool* should_fail) {
+  bool fail_if_unsatisfied = base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kRequireAudioHardwareForTesting);
+  if (!requirements_satisfied) {
+    LOG(WARNING) << "Requirement(s) not satisfied (" << requirements_expression
+                 << ")";
+    *should_fail = fail_if_unsatisfied;
+    return true;
+  }
+  *should_fail = false;
+  return false;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/audio_unittest_util.h b/third_party/chromium/media/audio/audio_unittest_util.h
new file mode 100644
index 0000000..3bd0dfa
--- /dev/null
+++ b/third_party/chromium/media/audio/audio_unittest_util.h
@@ -0,0 +1,34 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_AUDIO_UNITTEST_UTIL_H_
+#define MEDIA_AUDIO_AUDIO_UNITTEST_UTIL_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+// Use in tests to either skip or fail a test when the system is missing a
+// required audio device or library. If the --require-audio-hardware-for-testing
+// flag is set, missing requirements will cause the test to fail. Otherwise it
+// will be skipped.
+#define ABORT_AUDIO_TEST_IF_NOT(requirements_satisfied)                       \
+  do {                                                                        \
+    bool fail = false;                                                        \
+    if (ShouldAbortAudioTest(requirements_satisfied, #requirements_satisfied, \
+                             &fail)) {                                        \
+      if (fail)                                                               \
+        FAIL();                                                               \
+      else                                                                    \
+        return;                                                               \
+    }                                                                         \
+  } while (false)
+
+bool ShouldAbortAudioTest(bool requirements_satisfied,
+                          const char* requirements_expression,
+                          bool* should_fail);
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_AUDIO_UNITTEST_UTIL_H_
diff --git a/third_party/chromium/media/audio/clockless_audio_sink.cc b/third_party/chromium/media/audio/clockless_audio_sink.cc
new file mode 100644
index 0000000..1de786b
--- /dev/null
+++ b/third_party/chromium/media/audio/clockless_audio_sink.cc
@@ -0,0 +1,173 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/clockless_audio_sink.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/simple_thread.h"
+#include "media/base/audio_hash.h"
+
+namespace media {
+
+// Internal to ClocklessAudioSink. Class is used to call Render() on a seperate
+// thread, running as fast as it can read the data.
+class ClocklessAudioSinkThread : public base::DelegateSimpleThread::Delegate {
+ public:
+  ClocklessAudioSinkThread(const AudioParameters& params,
+                           AudioRendererSink::RenderCallback* callback,
+                           bool hashing)
+      : callback_(callback),
+        audio_bus_(AudioBus::Create(params)),
+        stop_event_(new base::WaitableEvent(
+            base::WaitableEvent::ResetPolicy::AUTOMATIC,
+            base::WaitableEvent::InitialState::NOT_SIGNALED)) {
+    if (hashing)
+      audio_hash_ = std::make_unique<AudioHash>();
+  }
+
+  void Start() {
+    stop_event_->Reset();
+    thread_ = std::make_unique<base::DelegateSimpleThread>(
+        this, "ClocklessAudioSink");
+    thread_->Start();
+  }
+
+  // Generate a signal to stop calling Render().
+  base::TimeDelta Stop() {
+    stop_event_->Signal();
+    thread_->Join();
+    return playback_time_;
+  }
+
+  std::string GetAudioHash() {
+    DCHECK(audio_hash_);
+    return audio_hash_->ToString();
+  }
+
+ private:
+  // Call Render() repeatedly, keeping track of the rendering time.
+  void Run() override {
+    base::TimeTicks start;
+    while (!stop_event_->IsSignaled()) {
+      const int frames_received = callback_->Render(
+          base::TimeDelta(), base::TimeTicks::Now(), 0, audio_bus_.get());
+      DCHECK_GE(frames_received, 0);
+      if (audio_hash_)
+        audio_hash_->Update(audio_bus_.get(), frames_received);
+      if (!frames_received) {
+        // No data received, so let other threads run to provide data.
+        base::PlatformThread::YieldCurrentThread();
+      } else if (start.is_null()) {
+        // First time we processed some audio, so record the starting time.
+        start = base::TimeTicks::Now();
+      } else {
+        // Keep track of the last time data was rendered.
+        playback_time_ = base::TimeTicks::Now() - start;
+      }
+    }
+  }
+
+  AudioRendererSink::RenderCallback* callback_;
+  std::unique_ptr<AudioBus> audio_bus_;
+  std::unique_ptr<base::WaitableEvent> stop_event_;
+  std::unique_ptr<base::DelegateSimpleThread> thread_;
+  base::TimeDelta playback_time_;
+  std::unique_ptr<AudioHash> audio_hash_;
+};
+
+ClocklessAudioSink::ClocklessAudioSink()
+    : ClocklessAudioSink(OutputDeviceInfo()) {}
+
+ClocklessAudioSink::ClocklessAudioSink(const OutputDeviceInfo& device_info)
+    : device_info_(device_info),
+      initialized_(false),
+      playing_(false),
+      hashing_(false),
+      is_optimized_for_hw_params_(true) {}
+
+ClocklessAudioSink::~ClocklessAudioSink() = default;
+
+void ClocklessAudioSink::Initialize(const AudioParameters& params,
+                                    RenderCallback* callback) {
+  DCHECK(!initialized_);
+  thread_ =
+      std::make_unique<ClocklessAudioSinkThread>(params, callback, hashing_);
+  initialized_ = true;
+}
+
+void ClocklessAudioSink::Start() {
+  DCHECK(initialized_);
+  DCHECK(!playing_);
+}
+
+void ClocklessAudioSink::Stop() {
+  if (initialized_)
+    Pause();
+}
+
+void ClocklessAudioSink::Flush() {}
+
+void ClocklessAudioSink::Play() {
+  DCHECK(initialized_);
+
+  if (playing_)
+    return;
+
+  playing_ = true;
+  thread_->Start();
+}
+
+void ClocklessAudioSink::Pause() {
+  DCHECK(initialized_);
+
+  if (!playing_)
+    return;
+
+  playing_ = false;
+  playback_time_ = thread_->Stop();
+}
+
+bool ClocklessAudioSink::SetVolume(double volume) {
+  // Audio is always muted.
+  return volume == 0.0;
+}
+
+OutputDeviceInfo ClocklessAudioSink::GetOutputDeviceInfo() {
+  return device_info_;
+}
+
+void ClocklessAudioSink::GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(info_cb), device_info_));
+}
+
+bool ClocklessAudioSink::IsOptimizedForHardwareParameters() {
+  return is_optimized_for_hw_params_;
+}
+
+bool ClocklessAudioSink::CurrentThreadIsRenderingThread() {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void ClocklessAudioSink::StartAudioHashForTesting() {
+  DCHECK(!initialized_);
+  hashing_ = true;
+}
+
+std::string ClocklessAudioSink::GetAudioHashForTesting() {
+  return thread_ && hashing_ ? thread_->GetAudioHash() : std::string();
+}
+
+void ClocklessAudioSink::SetIsOptimizedForHardwareParametersForTesting(
+    bool value) {
+  is_optimized_for_hw_params_ = value;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/clockless_audio_sink.h b/third_party/chromium/media/audio/clockless_audio_sink.h
new file mode 100644
index 0000000..cf6d6a6
--- /dev/null
+++ b/third_party/chromium/media/audio/clockless_audio_sink.h
@@ -0,0 +1,69 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_CLOCKLESS_AUDIO_SINK_H_
+#define MEDIA_AUDIO_CLOCKLESS_AUDIO_SINK_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "media/base/audio_renderer_sink.h"
+
+namespace media {
+class ClocklessAudioSinkThread;
+
+// Implementation of an AudioRendererSink that consumes the audio as fast as
+// possible. This class does not support multiple Play()/Pause() events.
+class MEDIA_EXPORT ClocklessAudioSink : public AudioRendererSink {
+ public:
+  ClocklessAudioSink();
+  explicit ClocklessAudioSink(const OutputDeviceInfo& device_info);
+
+  // AudioRendererSink implementation.
+  void Initialize(const AudioParameters& params,
+                  RenderCallback* callback) override;
+  void Start() override;
+  void Stop() override;
+  void Flush() override;
+  void Pause() override;
+  void Play() override;
+  bool SetVolume(double volume) override;
+  OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
+  bool IsOptimizedForHardwareParameters() override;
+  bool CurrentThreadIsRenderingThread() override;
+
+  // Returns the time taken to consume all the audio.
+  base::TimeDelta render_time() { return playback_time_; }
+
+  // Enables audio frame hashing.  Must be called prior to Initialize().
+  void StartAudioHashForTesting();
+
+  // Returns the hash of all audio frames seen since construction.
+  std::string GetAudioHashForTesting();
+
+  void SetIsOptimizedForHardwareParametersForTesting(bool value);
+
+ protected:
+  ~ClocklessAudioSink() override;
+
+ private:
+  const OutputDeviceInfo device_info_;
+  std::unique_ptr<ClocklessAudioSinkThread> thread_;
+  bool initialized_;
+  bool playing_;
+  bool hashing_;
+  bool is_optimized_for_hw_params_;
+
+  // Time taken in last set of Render() calls.
+  base::TimeDelta playback_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClocklessAudioSink);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_CLOCKLESS_AUDIO_SINK_H_
diff --git a/third_party/chromium/media/audio/cras/audio_manager_chromeos.cc b/third_party/chromium/media/audio/cras/audio_manager_chromeos.cc
new file mode 100644
index 0000000..e18f874
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/audio_manager_chromeos.cc
@@ -0,0 +1,605 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/cras/audio_manager_chromeos.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+
+#include "ash/components/audio/audio_device.h"
+#include "ash/components/audio/cras_audio_handler.h"
+#include "base/bind.h"
+#include "base/check_op.h"
+#include "base/command_line.h"
+#include "base/cxx17_backports.h"
+#include "base/environment.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/nix/xdg_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/system/sys_info.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_features.h"
+#include "media/audio/cras/cras_input.h"
+#include "media/audio/cras/cras_unified.h"
+#include "media/base/channel_layout.h"
+#include "media/base/limits.h"
+#include "media/base/localized_strings.h"
+
+namespace media {
+namespace {
+
+using ::ash::AudioDevice;
+using ::ash::AudioDeviceList;
+using ::ash::CrasAudioHandler;
+
+// Default sample rate for input and output streams.
+const int kDefaultSampleRate = 48000;
+
+// Default input buffer size.
+const int kDefaultInputBufferSize = 1024;
+
+const char kInternalInputVirtualDevice[] = "Built-in mic";
+const char kInternalOutputVirtualDevice[] = "Built-in speaker";
+const char kHeadphoneLineOutVirtualDevice[] = "Headphone/Line Out";
+
+// Used for the Media.CrosBeamformingDeviceState histogram, currently not used
+// since beamforming is disabled.
+enum CrosBeamformingDeviceState {
+  BEAMFORMING_DEFAULT_ENABLED = 0,
+  BEAMFORMING_USER_ENABLED,
+  BEAMFORMING_DEFAULT_DISABLED,
+  BEAMFORMING_USER_DISABLED,
+  BEAMFORMING_STATE_MAX = BEAMFORMING_USER_DISABLED
+};
+
+bool HasKeyboardMic(const AudioDeviceList& devices) {
+  for (const auto& device : devices) {
+    if (device.is_input &&
+        device.type == chromeos::AudioDeviceType::kKeyboardMic) {
+      return true;
+    }
+  }
+  return false;
+}
+
+const AudioDevice* GetDeviceFromId(const AudioDeviceList& devices,
+                                   uint64_t id) {
+  for (const auto& device : devices) {
+    if (device.id == id) {
+      return &device;
+    }
+  }
+  return nullptr;
+}
+
+// Process |device_list| that two shares the same dev_index by creating a
+// virtual device name for them.
+void ProcessVirtualDeviceName(AudioDeviceNames* device_names,
+                              const AudioDeviceList& device_list) {
+  DCHECK_EQ(2U, device_list.size());
+  if (device_list[0].type == chromeos::AudioDeviceType::kLineout ||
+      device_list[1].type == chromeos::AudioDeviceType::kLineout) {
+    device_names->emplace_back(kHeadphoneLineOutVirtualDevice,
+                               base::NumberToString(device_list[0].id));
+  } else if (device_list[0].type ==
+                 chromeos::AudioDeviceType::kInternalSpeaker ||
+             device_list[1].type ==
+                 chromeos::AudioDeviceType::kInternalSpeaker) {
+    device_names->emplace_back(kInternalOutputVirtualDevice,
+                               base::NumberToString(device_list[0].id));
+  } else {
+    DCHECK(device_list[0].IsInternalMic() || device_list[1].IsInternalMic());
+    device_names->emplace_back(kInternalInputVirtualDevice,
+                               base::NumberToString(device_list[0].id));
+  }
+}
+
+// Collects flags values for whether, and in what way, the AEC, NS or AGC
+// effects should be enforced in spite of them not being flagged as supported by
+// the board.
+void RetrieveSystemEffectFeatures(bool& enforce_system_aec,
+                                  bool& enforce_system_ns,
+                                  bool& enforce_system_agc,
+                                  bool& tuned_system_aec_allowed) {
+  const bool enforce_system_aec_ns_agc_feature =
+      base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecNsAgc);
+  const bool enforce_system_aec_ns_feature =
+      base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecNs);
+  const bool enforce_system_aec_agc_feature =
+      base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAecAgc);
+  const bool enforce_system_aec_feature =
+      base::FeatureList::IsEnabled(features::kCrOSEnforceSystemAec);
+
+  enforce_system_aec =
+      enforce_system_aec_feature || enforce_system_aec_ns_agc_feature ||
+      enforce_system_aec_ns_feature || enforce_system_aec_agc_feature;
+  enforce_system_ns =
+      enforce_system_aec_ns_agc_feature || enforce_system_aec_ns_feature;
+  enforce_system_agc =
+      enforce_system_aec_ns_agc_feature || enforce_system_aec_agc_feature;
+
+  tuned_system_aec_allowed =
+      base::FeatureList::IsEnabled(features::kCrOSSystemAEC);
+}
+
+// Checks if a system AEC with a specific group ID is flagged to be deactivated
+// by the field trial.
+bool IsSystemAecDeactivated(int aec_group_id) {
+  return base::GetFieldTrialParamByFeatureAsBool(
+      features::kCrOSSystemAECDeactivatedGroups, std::to_string(aec_group_id),
+      false);
+}
+
+}  // namespace
+
+bool AudioManagerChromeOS::HasAudioOutputDevices() {
+  return true;
+}
+
+bool AudioManagerChromeOS::HasAudioInputDevices() {
+  AudioDeviceList devices;
+  GetAudioDevices(&devices);
+  for (size_t i = 0; i < devices.size(); ++i) {
+    if (devices[i].is_input && devices[i].is_for_simple_usage())
+      return true;
+  }
+  return false;
+}
+
+AudioManagerChromeOS::AudioManagerChromeOS(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory)
+    : AudioManagerCrasBase(std::move(audio_thread), audio_log_factory),
+      on_shutdown_(base::WaitableEvent::ResetPolicy::MANUAL,
+                   base::WaitableEvent::InitialState::NOT_SIGNALED),
+      main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      weak_ptr_factory_(this) {
+  weak_this_ = weak_ptr_factory_.GetWeakPtr();
+}
+
+AudioManagerChromeOS::~AudioManagerChromeOS() = default;
+
+void AudioManagerChromeOS::GetAudioDeviceNamesImpl(
+    bool is_input,
+    AudioDeviceNames* device_names) {
+  DCHECK(device_names->empty());
+
+  device_names->push_back(AudioDeviceName::CreateDefault());
+
+  AudioDeviceList devices;
+  GetAudioDevices(&devices);
+
+  // |dev_idx_map| is a map of dev_index and their audio devices.
+  std::map<int, AudioDeviceList> dev_idx_map;
+  for (const auto& device : devices) {
+    if (device.is_input != is_input || !device.is_for_simple_usage())
+      continue;
+
+    dev_idx_map[dev_index_of(device.id)].push_back(device);
+  }
+
+  for (const auto& item : dev_idx_map) {
+    if (1 == item.second.size()) {
+      const AudioDevice& device = item.second.front();
+      device_names->emplace_back(device.display_name,
+                                 base::NumberToString(device.id));
+    } else {
+      // Create virtual device name for audio nodes that share the same device
+      // index.
+      ProcessVirtualDeviceName(device_names, item.second);
+    }
+  }
+}
+
+void AudioManagerChromeOS::GetAudioInputDeviceNames(
+    AudioDeviceNames* device_names) {
+  GetAudioDeviceNamesImpl(true, device_names);
+}
+
+void AudioManagerChromeOS::GetAudioOutputDeviceNames(
+    AudioDeviceNames* device_names) {
+  GetAudioDeviceNamesImpl(false, device_names);
+}
+
+AudioParameters AudioManagerChromeOS::GetInputStreamParameters(
+    const std::string& device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  // Check if the device has keyboard.
+  AudioDeviceList devices;
+  GetAudioDevices(&devices);
+  const bool has_keyboard = HasKeyboardMic(devices);
+
+  // Retrieve buffer size.
+  int user_buffer_size = GetUserBufferSize();
+  user_buffer_size =
+      user_buffer_size != 0 ? user_buffer_size : kDefaultInputBufferSize;
+
+  // Retrieve the board support in terms of APM effects and properties.
+  const SystemAudioProcessingInfo system_apm_info =
+      GetSystemApmEffectsSupportedPerBoard();
+
+  // TODO(hshi): Fine-tune audio parameters based on |device_id|. The optimal
+  // parameters for the loopback stream may differ from the default.
+  return GetStreamParametersForSystem(user_buffer_size, has_keyboard,
+                                      system_apm_info);
+}
+
+std::string AudioManagerChromeOS::GetAssociatedOutputDeviceID(
+    const std::string& input_device_id) {
+  AudioDeviceList devices;
+  GetAudioDevices(&devices);
+
+  if (input_device_id == AudioDeviceDescription::kDefaultDeviceId) {
+    // Note: the default input should not be associated to any output, as this
+    // may lead to accidental uses of a pinned stream.
+    return "";
+  }
+
+  const std::string device_name =
+      GetHardwareDeviceFromDeviceId(devices, true, input_device_id);
+
+  if (device_name.empty())
+    return "";
+
+  // Now search for an output device with the same device name.
+  auto output_device_it = std::find_if(
+      devices.begin(), devices.end(), [device_name](const AudioDevice& device) {
+        return !device.is_input && device.device_name == device_name;
+      });
+  return output_device_it == devices.end()
+             ? ""
+             : base::NumberToString(output_device_it->id);
+}
+
+std::string AudioManagerChromeOS::GetDefaultInputDeviceID() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return base::NumberToString(GetPrimaryActiveInputNode());
+}
+
+std::string AudioManagerChromeOS::GetDefaultOutputDeviceID() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return base::NumberToString(GetPrimaryActiveOutputNode());
+}
+
+std::string AudioManagerChromeOS::GetGroupIDOutput(
+    const std::string& output_device_id) {
+  AudioDeviceList devices;
+  GetAudioDevices(&devices);
+
+  return GetHardwareDeviceFromDeviceId(devices, false, output_device_id);
+}
+
+std::string AudioManagerChromeOS::GetGroupIDInput(
+    const std::string& input_device_id) {
+  AudioDeviceList devices;
+  GetAudioDevices(&devices);
+
+  return GetHardwareDeviceFromDeviceId(devices, true, input_device_id);
+}
+
+bool AudioManagerChromeOS::Shutdown() {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  on_shutdown_.Signal();
+  return AudioManager::Shutdown();
+}
+
+int AudioManagerChromeOS::GetDefaultOutputBufferSizePerBoard() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  int32_t buffer_size = 512;
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  if (main_task_runner_->BelongsToCurrentThread()) {
+    // Unittest may use the same thread for audio thread.
+    GetDefaultOutputBufferSizeOnMainThread(&buffer_size, &event);
+  } else {
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &AudioManagerChromeOS::GetDefaultOutputBufferSizeOnMainThread,
+            weak_this_, base::Unretained(&buffer_size),
+            base::Unretained(&event)));
+  }
+  WaitEventOrShutdown(&event);
+  return static_cast<int>(buffer_size);
+}
+
+AudioManagerChromeOS::SystemAudioProcessingInfo
+AudioManagerChromeOS::GetSystemApmEffectsSupportedPerBoard() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  SystemAudioProcessingInfo system_apm_info;
+  if (main_task_runner_->BelongsToCurrentThread()) {
+    // Unittest may use the same thread for audio thread.
+    GetSystemApmEffectsSupportedOnMainThread(&system_apm_info, &event);
+  } else {
+    // Using base::Unretained is safe here because we wait for callback be
+    // executed in main thread before local variables are destructed.
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &AudioManagerChromeOS::GetSystemApmEffectsSupportedOnMainThread,
+            weak_this_, base::Unretained(&system_apm_info),
+            base::Unretained(&event)));
+  }
+  WaitEventOrShutdown(&event);
+  return system_apm_info;
+}
+
+AudioParameters AudioManagerChromeOS::GetPreferredOutputStreamParameters(
+    const std::string& output_device_id,
+    const AudioParameters& input_params) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
+  int sample_rate = kDefaultSampleRate;
+  int buffer_size = GetUserBufferSize();
+  if (input_params.IsValid()) {
+    channel_layout = input_params.channel_layout();
+    sample_rate = input_params.sample_rate();
+    if (!buffer_size)  // Not user-provided.
+      buffer_size =
+          std::min(static_cast<int>(limits::kMaxAudioBufferSize),
+                   std::max(static_cast<int>(limits::kMinAudioBufferSize),
+                            input_params.frames_per_buffer()));
+    return AudioParameters(
+        AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, sample_rate,
+        buffer_size,
+        AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize,
+                                              limits::kMaxAudioBufferSize));
+  }
+
+  // Get max supported channels from |output_device_id| or the primary active
+  // one if |output_device_id| is the default device.
+  uint64_t preferred_device_id;
+  if (AudioDeviceDescription::IsDefaultDevice(output_device_id)) {
+    preferred_device_id = GetPrimaryActiveOutputNode();
+  } else {
+    if (!base::StringToUint64(output_device_id, &preferred_device_id))
+      preferred_device_id = 0;  // 0 represents invalid |output_device_id|.
+  }
+
+  if (preferred_device_id) {
+    AudioDeviceList devices;
+    GetAudioDevices(&devices);
+    const AudioDevice* device = GetDeviceFromId(devices, preferred_device_id);
+    if (device && device->is_input == false) {
+      channel_layout =
+          GuessChannelLayout(static_cast<int>(device->max_supported_channels));
+      // Fall-back to old fashion: always fixed to STEREO layout.
+      if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) {
+        channel_layout = CHANNEL_LAYOUT_STEREO;
+      }
+    }
+  }
+
+  if (!buffer_size)  // Not user-provided.
+    buffer_size = GetDefaultOutputBufferSizePerBoard();
+
+  return AudioParameters(
+      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, sample_rate,
+      buffer_size,
+      AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize,
+                                            limits::kMaxAudioBufferSize));
+}
+
+bool AudioManagerChromeOS::IsDefault(const std::string& device_id,
+                                     bool is_input) {
+  AudioDeviceNames device_names;
+  GetAudioDeviceNamesImpl(is_input, &device_names);
+  DCHECK(!device_names.empty());
+  const AudioDeviceName& device_name = device_names.front();
+  return device_name.unique_id == device_id;
+}
+
+std::string AudioManagerChromeOS::GetHardwareDeviceFromDeviceId(
+    const AudioDeviceList& devices,
+    bool is_input,
+    const std::string& device_id) {
+  uint64_t u64_device_id = 0;
+  if (AudioDeviceDescription::IsDefaultDevice(device_id)) {
+    u64_device_id =
+        is_input ? GetPrimaryActiveInputNode() : GetPrimaryActiveOutputNode();
+  } else {
+    if (!base::StringToUint64(device_id, &u64_device_id))
+      return "";
+  }
+
+  const AudioDevice* device = GetDeviceFromId(devices, u64_device_id);
+
+  return device ? device->device_name : "";
+}
+
+void AudioManagerChromeOS::GetAudioDevices(AudioDeviceList* devices) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  if (main_task_runner_->BelongsToCurrentThread()) {
+    GetAudioDevicesOnMainThread(devices, &event);
+  } else {
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&AudioManagerChromeOS::GetAudioDevicesOnMainThread,
+                       weak_this_, base::Unretained(devices),
+                       base::Unretained(&event)));
+  }
+  WaitEventOrShutdown(&event);
+}
+
+void AudioManagerChromeOS::GetAudioDevicesOnMainThread(
+    AudioDeviceList* devices,
+    base::WaitableEvent* event) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  // CrasAudioHandler is shut down before AudioManagerChromeOS.
+  if (CrasAudioHandler::Get())
+    CrasAudioHandler::Get()->GetAudioDevices(devices);
+  event->Signal();
+}
+
+uint64_t AudioManagerChromeOS::GetPrimaryActiveInputNode() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  uint64_t device_id = 0;
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  if (main_task_runner_->BelongsToCurrentThread()) {
+    GetPrimaryActiveInputNodeOnMainThread(&device_id, &event);
+  } else {
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &AudioManagerChromeOS::GetPrimaryActiveInputNodeOnMainThread,
+            weak_this_, &device_id, &event));
+  }
+  WaitEventOrShutdown(&event);
+  return device_id;
+}
+
+uint64_t AudioManagerChromeOS::GetPrimaryActiveOutputNode() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  uint64_t device_id = 0;
+  if (main_task_runner_->BelongsToCurrentThread()) {
+    // Unittest may use the same thread for audio thread.
+    GetPrimaryActiveOutputNodeOnMainThread(&device_id, &event);
+  } else {
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &AudioManagerChromeOS::GetPrimaryActiveOutputNodeOnMainThread,
+            weak_this_, base::Unretained(&device_id),
+            base::Unretained(&event)));
+  }
+  WaitEventOrShutdown(&event);
+  return device_id;
+}
+
+void AudioManagerChromeOS::GetPrimaryActiveInputNodeOnMainThread(
+    uint64_t* active_input_node_id,
+    base::WaitableEvent* event) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  if (CrasAudioHandler::Get()) {
+    *active_input_node_id =
+        CrasAudioHandler::Get()->GetPrimaryActiveInputNode();
+  }
+  event->Signal();
+}
+
+void AudioManagerChromeOS::GetPrimaryActiveOutputNodeOnMainThread(
+    uint64_t* active_output_node_id,
+    base::WaitableEvent* event) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  if (CrasAudioHandler::Get()) {
+    *active_output_node_id =
+        CrasAudioHandler::Get()->GetPrimaryActiveOutputNode();
+  }
+  event->Signal();
+}
+
+void AudioManagerChromeOS::GetDefaultOutputBufferSizeOnMainThread(
+    int32_t* buffer_size,
+    base::WaitableEvent* event) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  if (CrasAudioHandler::Get())
+    CrasAudioHandler::Get()->GetDefaultOutputBufferSize(buffer_size);
+  event->Signal();
+}
+
+void AudioManagerChromeOS::GetSystemApmEffectsSupportedOnMainThread(
+    SystemAudioProcessingInfo* system_apm_info,
+    base::WaitableEvent* event) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  if (CrasAudioHandler::Get()) {
+    system_apm_info->aec_supported =
+        CrasAudioHandler::Get()->system_aec_supported();
+    system_apm_info->aec_group_id =
+        CrasAudioHandler::Get()->system_aec_group_id();
+    system_apm_info->ns_supported =
+        CrasAudioHandler::Get()->system_ns_supported();
+    system_apm_info->agc_supported =
+        CrasAudioHandler::Get()->system_agc_supported();
+  }
+  event->Signal();
+}
+
+void AudioManagerChromeOS::WaitEventOrShutdown(base::WaitableEvent* event) {
+  base::WaitableEvent* waitables[] = {event, &on_shutdown_};
+  base::WaitableEvent::WaitMany(waitables, base::size(waitables));
+}
+
+enum CRAS_CLIENT_TYPE AudioManagerChromeOS::GetClientType() {
+  return CRAS_CLIENT_TYPE_CHROME;
+}
+
+AudioParameters AudioManagerChromeOS::GetStreamParametersForSystem(
+    int user_buffer_size,
+    bool has_keyboard,
+    const AudioManagerChromeOS::SystemAudioProcessingInfo& system_apm_info) {
+  AudioParameters params(
+      AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
+      kDefaultSampleRate, user_buffer_size,
+      AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize,
+                                            limits::kMaxAudioBufferSize));
+  if (has_keyboard)
+    params.set_effects(AudioParameters::KEYBOARD_MIC);
+
+  bool enforce_system_aec;
+  bool enforce_system_ns;
+  bool enforce_system_agc;
+  bool tuned_system_aec_allowed;
+  RetrieveSystemEffectFeatures(enforce_system_aec, enforce_system_ns,
+                               enforce_system_agc, tuned_system_aec_allowed);
+
+  // Activation of the system AEC. Allow experimentation with system AEC with
+  // all devices, but enable it by default on devices that actually support it.
+  params.set_effects(params.effects() |
+                     AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
+
+  // Rephrase the field aec_supported to properly reflect its meaning in this
+  // context (since it currently signals whether an CrAS APM with tuned settings
+  // is available).
+  const bool tuned_system_apm_available = system_apm_info.aec_supported;
+
+  // Don't use the system AEC if it is deactivated for this group ID. Also never
+  // activate NS nor AGC for this board if the AEC is not activated, since this
+  // will cause issues for the Browser AEC.
+  bool use_system_aec =
+      (tuned_system_apm_available && tuned_system_aec_allowed) ||
+      enforce_system_aec;
+
+  if (!use_system_aec || IsSystemAecDeactivated(system_apm_info.aec_group_id)) {
+    return params;
+  }
+
+  // Activation of the system AEC.
+  params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER);
+
+  // Don't use system NS or AGC if the AEC has board-specific tunings.
+  if (tuned_system_apm_available) {
+    return params;
+  }
+
+  // Activation of the system NS.
+  if (system_apm_info.ns_supported || enforce_system_ns) {
+    params.set_effects(params.effects() | AudioParameters::NOISE_SUPPRESSION);
+  }
+
+  // Activation of the system AGC.
+  if (system_apm_info.agc_supported || enforce_system_agc) {
+    params.set_effects(params.effects() |
+                       AudioParameters::AUTOMATIC_GAIN_CONTROL);
+  }
+
+  return params;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/cras/audio_manager_chromeos.h b/third_party/chromium/media/audio/cras/audio_manager_chromeos.h
new file mode 100644
index 0000000..1006644
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/audio_manager_chromeos.h
@@ -0,0 +1,119 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_CRAS_AUDIO_MANAGER_CHROMEOS_H_
+#define MEDIA_AUDIO_CRAS_AUDIO_MANAGER_CHROMEOS_H_
+
+#include <cras_types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "ash/components/audio/audio_device.h"
+#include "ash/components/audio/cras_audio_handler.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/cras/audio_manager_cras_base.h"
+
+namespace media {
+
+class MEDIA_EXPORT AudioManagerChromeOS : public AudioManagerCrasBase {
+ public:
+  AudioManagerChromeOS(std::unique_ptr<AudioThread> audio_thread,
+                   AudioLogFactory* audio_log_factory);
+
+  AudioManagerChromeOS(const AudioManagerChromeOS&) = delete;
+  AudioManagerChromeOS& operator=(const AudioManagerChromeOS&) = delete;
+
+  ~AudioManagerChromeOS() override;
+
+  // AudioManager implementation.
+  bool HasAudioOutputDevices() override;
+  bool HasAudioInputDevices() override;
+  void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override;
+  void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+  std::string GetAssociatedOutputDeviceID(
+      const std::string& input_device_id) override;
+  std::string GetDefaultInputDeviceID() override;
+  std::string GetDefaultOutputDeviceID() override;
+  std::string GetGroupIDOutput(const std::string& output_device_id) override;
+  std::string GetGroupIDInput(const std::string& input_device_id) override;
+  bool Shutdown() override;
+
+  // AudioManagerCrasBase implementation.
+  bool IsDefault(const std::string& device_id, bool is_input) override;
+  enum CRAS_CLIENT_TYPE GetClientType() override;
+
+  // Stores information about the system audio processing effects and
+  // properties that are provided by the system audio processing module (APM).
+  struct SystemAudioProcessingInfo {
+    bool aec_supported = false;
+    int32_t aec_group_id = ash::CrasAudioHandler::kSystemAecGroupIdNotAvailable;
+    bool ns_supported = false;
+    bool agc_supported = false;
+  };
+
+  // Produces AudioParameters for the system, including audio processing
+  // capabilities tailored for the system,
+  static AudioParameters GetStreamParametersForSystem(
+      int user_buffer_size,
+      bool has_keyboard,
+      const AudioManagerChromeOS::SystemAudioProcessingInfo& system_apm_info);
+
+ protected:
+  AudioParameters GetPreferredOutputStreamParameters(
+      const std::string& output_device_id,
+      const AudioParameters& input_params) override;
+
+ private:
+  // Get default output buffer size for this board.
+  int GetDefaultOutputBufferSizePerBoard();
+
+  // Get any system APM effects that are supported for this board.
+  SystemAudioProcessingInfo GetSystemApmEffectsSupportedPerBoard();
+
+  void GetAudioDeviceNamesImpl(bool is_input, AudioDeviceNames* device_names);
+
+  std::string GetHardwareDeviceFromDeviceId(const ash::AudioDeviceList& devices,
+                                            bool is_input,
+                                            const std::string& device_id);
+
+  void GetAudioDevices(ash::AudioDeviceList* devices);
+  void GetAudioDevicesOnMainThread(ash::AudioDeviceList* devices,
+                                   base::WaitableEvent* event);
+  uint64_t GetPrimaryActiveInputNode();
+  uint64_t GetPrimaryActiveOutputNode();
+  void GetPrimaryActiveInputNodeOnMainThread(uint64_t* active_input_node_id,
+                                             base::WaitableEvent* event);
+  void GetPrimaryActiveOutputNodeOnMainThread(uint64_t* active_output_node_id,
+                                              base::WaitableEvent* event);
+  void GetDefaultOutputBufferSizeOnMainThread(int32_t* buffer_size,
+                                              base::WaitableEvent* event);
+  void GetSystemApmEffectsSupportedOnMainThread(
+      SystemAudioProcessingInfo* system_apm_info,
+      base::WaitableEvent* event);
+
+  void WaitEventOrShutdown(base::WaitableEvent* event);
+
+  // Signaled if AudioManagerCras is shutting down.
+  base::WaitableEvent on_shutdown_;
+
+  // Task runner of browser main thread. CrasAudioHandler should be only
+  // accessed on this thread.
+  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+  // For posting tasks from audio thread to |main_task_runner_|.
+  base::WeakPtr<AudioManagerChromeOS> weak_this_;
+
+  base::WeakPtrFactory<AudioManagerChromeOS> weak_ptr_factory_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_CRAS_AUDIO_MANAGER_CHROMEOS_H_
diff --git a/third_party/chromium/media/audio/cras/audio_manager_chromeos_unittest.cc b/third_party/chromium/media/audio/cras/audio_manager_chromeos_unittest.cc
new file mode 100644
index 0000000..b11144a
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/audio_manager_chromeos_unittest.cc
@@ -0,0 +1,229 @@
+// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "media/audio/cras/audio_manager_chromeos.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "media/audio/audio_features.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+constexpr int kAecTestGroupId = 9;
+constexpr int kNoAecFlaggedGroupId = 0;
+
+// Chosen to be the same as in `audio_manager_chromeos.cc`, but any size should
+// work since this should not affect the testing done herein.
+constexpr int kDefaultInputBufferSize = 1024;
+
+bool ExperimentalAecActive(const AudioParameters& params) {
+  return params.effects() & AudioParameters::EXPERIMENTAL_ECHO_CANCELLER;
+}
+
+bool AecActive(const AudioParameters& params) {
+  return params.effects() & AudioParameters::ECHO_CANCELLER;
+}
+
+bool NsActive(const AudioParameters& params) {
+  return params.effects() & AudioParameters::NOISE_SUPPRESSION;
+}
+
+bool AgcActive(const AudioParameters& params) {
+  return params.effects() & AudioParameters::AUTOMATIC_GAIN_CONTROL;
+}
+
+}  // namespace
+
+class GetStreamParametersForSystem
+    : public ::testing::Test,
+      public ::testing::WithParamInterface<
+          std::tuple<bool, int, bool, int32_t, bool, bool>> {
+ protected:
+  // Retrieve test parameter values.
+  void GetTestParameters() {
+    has_keyboard_ = std::get<0>(GetParam());
+    user_buffer_size_ = std::get<1>(GetParam());
+    system_apm_info_.aec_supported = std::get<2>(GetParam());
+    system_apm_info_.aec_group_id = std::get<3>(GetParam());
+    system_apm_info_.ns_supported = std::get<4>(GetParam());
+    system_apm_info_.agc_supported = std::get<5>(GetParam());
+  }
+
+  AudioManagerChromeOS::SystemAudioProcessingInfo system_apm_info_;
+  size_t has_keyboard_;
+  size_t user_buffer_size_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    AllInputParameters,
+    GetStreamParametersForSystem,
+    ::testing::Combine(::testing::Values(false, true),
+                       ::testing::Values(512, kDefaultInputBufferSize),
+                       ::testing::Values(false, true),
+                       ::testing::Values(kNoAecFlaggedGroupId, kAecTestGroupId),
+                       ::testing::Values(false, true),
+                       ::testing::Values(false, true)));
+
+TEST_P(GetStreamParametersForSystem, DefaultBehavior) {
+  GetTestParameters();
+  AudioParameters params = AudioManagerChromeOS::GetStreamParametersForSystem(
+      user_buffer_size_, has_keyboard_, system_apm_info_);
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_EQ(AecActive(params), system_apm_info_.aec_supported);
+  if (system_apm_info_.aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  }
+}
+
+TEST_P(GetStreamParametersForSystem,
+       BehaviorWithCrOSEnforceSystemAecDisallowed) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(features::kCrOSSystemAEC);
+
+  GetTestParameters();
+  AudioParameters params = AudioManagerChromeOS::GetStreamParametersForSystem(
+      user_buffer_size_, has_keyboard_, system_apm_info_);
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_FALSE(AecActive(params));
+  EXPECT_FALSE(NsActive(params));
+  EXPECT_FALSE(AgcActive(params));
+}
+
+TEST_P(GetStreamParametersForSystem, BehaviorWithCrOSEnforceSystemAecNsAgc) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kCrOSEnforceSystemAecNsAgc);
+
+  GetTestParameters();
+  AudioParameters params = AudioManagerChromeOS::GetStreamParametersForSystem(
+      user_buffer_size_, has_keyboard_, system_apm_info_);
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_TRUE(AecActive(params));
+  if (system_apm_info_.aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_TRUE(NsActive(params));
+    EXPECT_TRUE(AgcActive(params));
+  }
+}
+
+// TODO(crbug.com/1216273): DCHECKs are disabled during automated testing on
+// CrOS and this test failed when tested on an experimental builder with
+// DCHECKs. Revert https://crrev.com/c/2959990 to re-enable it.
+// See go/chrome-dcheck-on-cros or http://crbug.com/1113456 for more details.
+#if !DCHECK_IS_ON()
+TEST_P(GetStreamParametersForSystem,
+       BehaviorWithCrOSEnforceSystemAecNsAndAecAgc) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kCrOSEnforceSystemAecNs);
+  feature_list.InitAndEnableFeature(features::kCrOSEnforceSystemAecAgc);
+
+  GetTestParameters();
+  AudioParameters params = AudioManagerChromeOS::GetStreamParametersForSystem(
+      user_buffer_size_, has_keyboard_, system_apm_info_);
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_TRUE(AecActive(params));
+  if (system_apm_info_.aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_TRUE(NsActive(params));
+    EXPECT_TRUE(AgcActive(params));
+  }
+}
+#endif
+
+// TODO(crbug.com/1216273): DCHECKs are disabled during automated testing on
+// CrOS and this test failed when tested on an experimental builder with
+// DCHECKs. Revert https://crrev.com/c/2959990 to re-enable it.
+// See go/chrome-dcheck-on-cros or http://crbug.com/1113456 for more details.
+#if !DCHECK_IS_ON()
+TEST_P(GetStreamParametersForSystem,
+       BehaviorWithCrOSEnforceSystemAecNsAgcAndDisallowedSystemAec) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kCrOSEnforceSystemAecNsAgc);
+  feature_list.InitAndDisableFeature(features::kCrOSSystemAEC);
+
+  GetTestParameters();
+  AudioParameters params = AudioManagerChromeOS::GetStreamParametersForSystem(
+      user_buffer_size_, has_keyboard_, system_apm_info_);
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_TRUE(AecActive(params));
+  if (system_apm_info_.aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_TRUE(NsActive(params));
+    EXPECT_TRUE(AgcActive(params));
+  }
+}
+#endif
+
+TEST_P(GetStreamParametersForSystem, BehaviorWithCrOSEnforceSystemAecNs) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kCrOSEnforceSystemAecNs);
+
+  GetTestParameters();
+  AudioParameters params = AudioManagerChromeOS::GetStreamParametersForSystem(
+      user_buffer_size_, has_keyboard_, system_apm_info_);
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_TRUE(AecActive(params));
+  if (system_apm_info_.aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_TRUE(NsActive(params));
+    EXPECT_EQ(AgcActive(params), system_apm_info_.agc_supported);
+  }
+}
+
+TEST_P(GetStreamParametersForSystem, BehaviorWithCrOSEnforceSystemAecAgc) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kCrOSEnforceSystemAecAgc);
+
+  GetTestParameters();
+  AudioParameters params = AudioManagerChromeOS::GetStreamParametersForSystem(
+      user_buffer_size_, has_keyboard_, system_apm_info_);
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_TRUE(AecActive(params));
+  if (system_apm_info_.aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_EQ(NsActive(params), system_apm_info_.ns_supported);
+    EXPECT_TRUE(AgcActive(params));
+  }
+}
+
+TEST_P(GetStreamParametersForSystem, BehaviorWithCrOSEnforceSystemAec) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kCrOSEnforceSystemAec);
+
+  GetTestParameters();
+  AudioParameters params = AudioManagerChromeOS::GetStreamParametersForSystem(
+      user_buffer_size_, has_keyboard_, system_apm_info_);
+
+  EXPECT_TRUE(ExperimentalAecActive(params));
+  EXPECT_TRUE(AecActive(params));
+  if (system_apm_info_.aec_supported) {
+    EXPECT_FALSE(NsActive(params));
+    EXPECT_FALSE(AgcActive(params));
+  } else {
+    EXPECT_EQ(NsActive(params), system_apm_info_.ns_supported);
+    EXPECT_EQ(AgcActive(params), system_apm_info_.agc_supported);
+  }
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/cras/audio_manager_cras.cc b/third_party/chromium/media/audio/cras/audio_manager_cras.cc
new file mode 100644
index 0000000..5044a50
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/audio_manager_cras.cc
@@ -0,0 +1,226 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/cras/audio_manager_cras.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check_op.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/nix/xdg_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/system/sys_info.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_features.h"
+#include "media/audio/cras/cras_input.h"
+#include "media/audio/cras/cras_unified.h"
+#include "media/audio/cras/cras_util.h"
+#include "media/base/channel_layout.h"
+#include "media/base/limits.h"
+#include "media/base/localized_strings.h"
+
+namespace media {
+namespace {
+
+// Default sample rate for input and output streams.
+const int kDefaultSampleRate = 48000;
+
+// Default input buffer size.
+const int kDefaultInputBufferSize = 1024;
+
+// Default output buffer size.
+const int kDefaultOutputBufferSize = 512;
+
+}  // namespace
+
+bool AudioManagerCras::HasAudioOutputDevices() {
+  return true;
+}
+
+bool AudioManagerCras::HasAudioInputDevices() {
+  return !CrasGetAudioDevices(DeviceType::kInput).empty();
+}
+
+AudioManagerCras::AudioManagerCras(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory)
+    : AudioManagerCrasBase(std::move(audio_thread), audio_log_factory),
+      main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      weak_ptr_factory_(this) {
+  weak_this_ = weak_ptr_factory_.GetWeakPtr();
+}
+
+AudioManagerCras::~AudioManagerCras() = default;
+
+void AudioManagerCras::GetAudioInputDeviceNames(
+    AudioDeviceNames* device_names) {
+  device_names->push_back(AudioDeviceName::CreateDefault());
+  for (const auto& device : CrasGetAudioDevices(DeviceType::kInput)) {
+    device_names->emplace_back(device.name, base::NumberToString(device.id));
+  }
+}
+
+void AudioManagerCras::GetAudioOutputDeviceNames(
+    AudioDeviceNames* device_names) {
+  device_names->push_back(AudioDeviceName::CreateDefault());
+  for (const auto& device : CrasGetAudioDevices(DeviceType::kOutput)) {
+    device_names->emplace_back(device.name, base::NumberToString(device.id));
+  }
+}
+
+AudioParameters AudioManagerCras::GetInputStreamParameters(
+    const std::string& device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+
+  int user_buffer_size = GetUserBufferSize();
+  int buffer_size =
+      user_buffer_size ? user_buffer_size : kDefaultInputBufferSize;
+
+  AudioParameters params(
+      AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
+      kDefaultSampleRate, buffer_size,
+      AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize,
+                                            limits::kMaxAudioBufferSize));
+
+  if (CrasHasKeyboardMic())
+    params.set_effects(AudioParameters::KEYBOARD_MIC);
+
+  // Allow experimentation with system echo cancellation with all devices,
+  // but enable it by default on devices that actually support it.
+  params.set_effects(params.effects() |
+                     AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
+  if (base::FeatureList::IsEnabled(features::kCrOSSystemAEC)) {
+    if (CrasGetAecSupported()) {
+      const int32_t aec_group_id = CrasGetAecGroupId();
+
+      // Check if the system AEC has a group ID which is flagged to be
+      // deactivated by the field trial.
+      const bool system_aec_deactivated =
+          base::GetFieldTrialParamByFeatureAsBool(
+              features::kCrOSSystemAECDeactivatedGroups,
+              base::NumberToString(aec_group_id), false);
+
+      if (!system_aec_deactivated) {
+        params.set_effects(params.effects() | AudioParameters::ECHO_CANCELLER);
+      }
+    }
+  }
+
+  return params;
+}
+
+std::string AudioManagerCras::GetDefaultInputDeviceID() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return base::NumberToString(GetPrimaryActiveInputNode());
+}
+
+std::string AudioManagerCras::GetDefaultOutputDeviceID() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return base::NumberToString(GetPrimaryActiveOutputNode());
+}
+
+std::string AudioManagerCras::GetGroupIDInput(const std::string& device_id) {
+  for (const auto& device : CrasGetAudioDevices(DeviceType::kInput)) {
+    if (base::NumberToString(device.id) == device_id ||
+        (AudioDeviceDescription::IsDefaultDevice(device_id) && device.active)) {
+      return device.dev_name;
+    }
+  }
+  return "";
+}
+
+std::string AudioManagerCras::GetGroupIDOutput(const std::string& device_id) {
+  for (const auto& device : CrasGetAudioDevices(DeviceType::kOutput)) {
+    if (base::NumberToString(device.id) == device_id ||
+        (AudioDeviceDescription::IsDefaultDevice(device_id) && device.active)) {
+      return device.dev_name;
+    }
+  }
+  return "";
+}
+
+std::string AudioManagerCras::GetAssociatedOutputDeviceID(
+    const std::string& input_device_id) {
+  if (AudioDeviceDescription::IsDefaultDevice(input_device_id)) {
+    // Note: the default input should not be associated to any output, as this
+    // may lead to accidental uses of a pinned stream.
+    return "";
+  }
+
+  std::string device_name = GetGroupIDInput(input_device_id);
+
+  if (device_name.empty())
+    return "";
+
+  // Now search for an output device with the same device name.
+  for (const auto& device : CrasGetAudioDevices(DeviceType::kOutput)) {
+    if (device.dev_name == device_name)
+      return base::NumberToString(device.id);
+  }
+  return "";
+}
+
+AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters(
+    const std::string& output_device_id,
+    const AudioParameters& input_params) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
+  int sample_rate = kDefaultSampleRate;
+  int buffer_size = GetUserBufferSize();
+  if (input_params.IsValid()) {
+    channel_layout = input_params.channel_layout();
+    sample_rate = input_params.sample_rate();
+    if (!buffer_size)  // Not user-provided.
+      buffer_size =
+          std::min(static_cast<int>(limits::kMaxAudioBufferSize),
+                   std::max(static_cast<int>(limits::kMinAudioBufferSize),
+                            input_params.frames_per_buffer()));
+  }
+
+  if (!buffer_size)  // Not user-provided.
+    buffer_size = CrasGetDefaultOutputBufferSize();
+
+  if (buffer_size <= 0)
+    buffer_size = kDefaultOutputBufferSize;
+
+  return AudioParameters(
+      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, sample_rate,
+      buffer_size,
+      AudioParameters::HardwareCapabilities(limits::kMinAudioBufferSize,
+                                            limits::kMaxAudioBufferSize));
+}
+
+uint64_t AudioManagerCras::GetPrimaryActiveInputNode() {
+  for (const auto& device : CrasGetAudioDevices(DeviceType::kInput)) {
+    if (device.active)
+      return device.id;
+  }
+  return 0;
+}
+
+uint64_t AudioManagerCras::GetPrimaryActiveOutputNode() {
+  for (const auto& device : CrasGetAudioDevices(DeviceType::kOutput)) {
+    if (device.active)
+      return device.id;
+  }
+  return 0;
+}
+
+bool AudioManagerCras::IsDefault(const std::string& device_id, bool is_input) {
+  return AudioDeviceDescription::IsDefaultDevice(device_id);
+}
+
+enum CRAS_CLIENT_TYPE AudioManagerCras::GetClientType() {
+  return CRAS_CLIENT_TYPE_LACROS;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/cras/audio_manager_cras.h b/third_party/chromium/media/audio/cras/audio_manager_cras.h
new file mode 100644
index 0000000..ffd8656
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/audio_manager_cras.h
@@ -0,0 +1,76 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_CRAS_AUDIO_MANAGER_CRAS_H_
+#define MEDIA_AUDIO_CRAS_AUDIO_MANAGER_CRAS_H_
+
+#include <cras_types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/audio/cras/audio_manager_cras_base.h"
+
+namespace media {
+
+class MEDIA_EXPORT AudioManagerCras : public AudioManagerCrasBase {
+ public:
+  AudioManagerCras(std::unique_ptr<AudioThread> audio_thread,
+                   AudioLogFactory* audio_log_factory);
+
+  AudioManagerCras(const AudioManagerCras&) = delete;
+  AudioManagerCras& operator=(const AudioManagerCras&) = delete;
+
+  ~AudioManagerCras() override;
+
+  // AudioManager implementation.
+  bool HasAudioOutputDevices() override;
+  bool HasAudioInputDevices() override;
+  void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override;
+  void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+  std::string GetDefaultInputDeviceID() override;
+  std::string GetDefaultOutputDeviceID() override;
+  std::string GetGroupIDInput(const std::string& device_id) override;
+  std::string GetGroupIDOutput(const std::string& device_id) override;
+  std::string GetAssociatedOutputDeviceID(
+      const std::string& input_device_id) override;
+
+  // AudioManagerCrasBase implementation.
+  bool IsDefault(const std::string& device_id, bool is_input) override;
+  enum CRAS_CLIENT_TYPE GetClientType() override;
+
+ protected:
+  AudioParameters GetPreferredOutputStreamParameters(
+      const std::string& output_device_id,
+      const AudioParameters& input_params) override;
+
+ private:
+  uint64_t GetPrimaryActiveInputNode();
+  uint64_t GetPrimaryActiveOutputNode();
+  void GetPrimaryActiveInputNodeOnMainThread(uint64_t* active_input_node_id,
+                                             base::WaitableEvent* event);
+  void GetPrimaryActiveOutputNodeOnMainThread(uint64_t* active_output_node_id,
+                                              base::WaitableEvent* event);
+  void GetDefaultOutputBufferSizeOnMainThread(int32_t* buffer_size,
+                                              base::WaitableEvent* event);
+
+  // Task runner of browser main thread. CrasAudioHandler should be only
+  // accessed on this thread.
+  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+  // For posting tasks from audio thread to |main_task_runner_|.
+  base::WeakPtr<AudioManagerCras> weak_this_;
+
+  base::WeakPtrFactory<AudioManagerCras> weak_ptr_factory_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_CRAS_AUDIO_MANAGER_CRAS_H_
diff --git a/third_party/chromium/media/audio/cras/audio_manager_cras_base.cc b/third_party/chromium/media/audio/cras/audio_manager_cras_base.cc
new file mode 100644
index 0000000..b7eecf4
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/audio_manager_cras_base.cc
@@ -0,0 +1,95 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/cras/audio_manager_cras_base.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check_op.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/nix/xdg_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/system/sys_info.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_features.h"
+#include "media/audio/cras/cras_input.h"
+#include "media/audio/cras/cras_unified.h"
+#include "media/base/channel_layout.h"
+#include "media/base/limits.h"
+#include "media/base/localized_strings.h"
+
+namespace media {
+namespace {
+
+// Maximum number of output streams that can be open simultaneously.
+const int kMaxOutputStreams = 50;
+
+}  // namespace
+
+AudioManagerCrasBase::AudioManagerCrasBase(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory)
+    : AudioManagerBase(std::move(audio_thread), audio_log_factory) {
+  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
+}
+
+AudioManagerCrasBase::~AudioManagerCrasBase() = default;
+
+const char* AudioManagerCrasBase::GetName() {
+  return "CRAS";
+}
+
+AudioOutputStream* AudioManagerCrasBase::MakeLinearOutputStream(
+    const AudioParameters& params,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+  // Pinning stream is not supported for MakeLinearOutputStream.
+  return MakeOutputStream(params, AudioDeviceDescription::kDefaultDeviceId);
+}
+
+AudioOutputStream* AudioManagerCrasBase::MakeLowLatencyOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+  return MakeOutputStream(params, device_id);
+}
+
+AudioInputStream* AudioManagerCrasBase::MakeLinearInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+  return MakeInputStream(params, device_id);
+}
+
+AudioInputStream* AudioManagerCrasBase::MakeLowLatencyInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+  return MakeInputStream(params, device_id);
+}
+
+AudioOutputStream* AudioManagerCrasBase::MakeOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id) {
+  return new CrasUnifiedStream(params, this, device_id);
+}
+
+AudioInputStream* AudioManagerCrasBase::MakeInputStream(
+    const AudioParameters& params, const std::string& device_id) {
+  return new CrasInputStream(params, this, device_id);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/cras/audio_manager_cras_base.h b/third_party/chromium/media/audio/cras/audio_manager_cras_base.h
new file mode 100644
index 0000000..4f67626
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/audio_manager_cras_base.h
@@ -0,0 +1,70 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_CRAS_AUDIO_MANAGER_CRAS_BASE_H_
+#define MEDIA_AUDIO_CRAS_AUDIO_MANAGER_CRAS_BASE_H_
+
+#include <cras_types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/audio/audio_manager_base.h"
+
+namespace media {
+
+class MEDIA_EXPORT AudioManagerCrasBase : public AudioManagerBase {
+ public:
+  AudioManagerCrasBase(std::unique_ptr<AudioThread> audio_thread,
+                   AudioLogFactory* audio_log_factory);
+
+  AudioManagerCrasBase(const AudioManagerCrasBase&) = delete;
+  AudioManagerCrasBase& operator=(const AudioManagerCrasBase&) = delete;
+
+  ~AudioManagerCrasBase() override;
+
+  // AudioManager implementation.
+  const char* GetName() override;
+
+  // AudioManagerBase implementation.
+  AudioOutputStream* MakeLinearOutputStream(
+      const AudioParameters& params,
+      const LogCallback& log_callback) override;
+  AudioOutputStream* MakeLowLatencyOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLinearInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLowLatencyInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+
+  // Checks if |device_id| corresponds to the default device.
+  // Set |is_input| to true for capture devices, false for output.
+  virtual bool IsDefault(const std::string& device_id, bool is_input) = 0;
+
+  // Returns CRAS client type.
+  virtual enum CRAS_CLIENT_TYPE GetClientType() = 0;
+
+ protected:
+  // Called by MakeLinearOutputStream and MakeLowLatencyOutputStream.
+  AudioOutputStream* MakeOutputStream(const AudioParameters& params,
+                                      const std::string& device_id);
+
+  // Called by MakeLinearInputStream and MakeLowLatencyInputStream.
+  AudioInputStream* MakeInputStream(const AudioParameters& params,
+                                    const std::string& device_id);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_CRAS_AUDIO_MANAGER_CRAS_BASE_H_
diff --git a/third_party/chromium/media/audio/cras/cras_input.cc b/third_party/chromium/media/audio/cras/cras_input.cc
new file mode 100644
index 0000000..dc98f23
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/cras_input.cc
@@ -0,0 +1,361 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/cras/cras_input.h"
+
+#include <math.h>
+#include <algorithm>
+
+#include "base/cxx17_backports.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/cras/audio_manager_cras_base.h"
+
+namespace media {
+
+CrasInputStream::CrasInputStream(const AudioParameters& params,
+                                 AudioManagerCrasBase* manager,
+                                 const std::string& device_id)
+    : audio_manager_(manager),
+      callback_(NULL),
+      client_(NULL),
+      params_(params),
+      started_(false),
+      stream_id_(0),
+      stream_direction_(CRAS_STREAM_INPUT),
+      pin_device_(NO_DEVICE),
+      is_loopback_(AudioDeviceDescription::IsLoopbackDevice(device_id)),
+      mute_system_audio_(device_id ==
+                         AudioDeviceDescription::kLoopbackWithMuteDeviceId),
+      mute_done_(false),
+      input_volume_(1.0f) {
+  DCHECK(audio_manager_);
+  audio_bus_ = AudioBus::Create(params_);
+  if (!audio_manager_->IsDefault(device_id, true)) {
+    uint64_t cras_node_id;
+    base::StringToUint64(device_id, &cras_node_id);
+    pin_device_ = dev_index_of(cras_node_id);
+  }
+}
+
+CrasInputStream::~CrasInputStream() {
+  DCHECK(!client_);
+}
+
+AudioInputStream::OpenOutcome CrasInputStream::Open() {
+  if (client_) {
+    NOTREACHED() << "CrasInputStream already open";
+    return OpenOutcome::kAlreadyOpen;
+  }
+
+  // Sanity check input values.
+  if (params_.sample_rate() <= 0) {
+    DLOG(WARNING) << "Unsupported audio frequency.";
+    return OpenOutcome::kFailed;
+  }
+
+  if (AudioParameters::AUDIO_PCM_LINEAR != params_.format() &&
+      AudioParameters::AUDIO_PCM_LOW_LATENCY != params_.format()) {
+    DLOG(WARNING) << "Unsupported audio format.";
+    return OpenOutcome::kFailed;
+  }
+
+  // Create the client and connect to the CRAS server.
+  client_ = libcras_client_create();
+  if (!client_) {
+    DLOG(WARNING) << "Couldn't create CRAS client.\n";
+    client_ = NULL;
+    return OpenOutcome::kFailed;
+  }
+
+  if (libcras_client_connect(client_)) {
+    DLOG(WARNING) << "Couldn't connect CRAS client.\n";
+    libcras_client_destroy(client_);
+    client_ = NULL;
+    return OpenOutcome::kFailed;
+  }
+
+  // Then start running the client.
+  if (libcras_client_run_thread(client_)) {
+    DLOG(WARNING) << "Couldn't run CRAS client.\n";
+    libcras_client_destroy(client_);
+    client_ = NULL;
+    return OpenOutcome::kFailed;
+  }
+
+  if (is_loopback_) {
+    if (libcras_client_connected_wait(client_) < 0) {
+      DLOG(WARNING) << "Couldn't synchronize data.";
+      // TODO(chinyue): Add a DestroyClientOnError method to de-duplicate the
+      // cleanup code.
+      libcras_client_destroy(client_);
+      client_ = NULL;
+      return OpenOutcome::kFailed;
+    }
+
+    int rc = libcras_client_get_loopback_dev_idx(client_, &pin_device_);
+    if (rc < 0) {
+      DLOG(WARNING) << "Couldn't find CRAS loopback device.";
+      libcras_client_destroy(client_);
+      client_ = NULL;
+      return OpenOutcome::kFailed;
+    }
+  }
+
+  return OpenOutcome::kSuccess;
+}
+
+void CrasInputStream::Close() {
+  Stop();
+
+  if (client_) {
+    libcras_client_stop(client_);
+    libcras_client_destroy(client_);
+    client_ = NULL;
+  }
+
+  // Signal to the manager that we're closed and can be removed.
+  // Should be last call in the method as it deletes "this".
+  audio_manager_->ReleaseInputStream(this);
+}
+
+inline bool CrasInputStream::UseCrasAec() const {
+  return params_.effects() & AudioParameters::ECHO_CANCELLER;
+}
+
+inline bool CrasInputStream::UseCrasNs() const {
+  return params_.effects() & AudioParameters::NOISE_SUPPRESSION;
+}
+
+inline bool CrasInputStream::UseCrasAgc() const {
+  return params_.effects() & AudioParameters::AUTOMATIC_GAIN_CONTROL;
+}
+
+void CrasInputStream::Start(AudioInputCallback* callback) {
+  DCHECK(client_);
+  DCHECK(callback);
+
+  // Channel map to CRAS_CHANNEL, values in the same order of
+  // corresponding source in Chromium defined Channels.
+  static const int kChannelMap[] = {
+    CRAS_CH_FL,
+    CRAS_CH_FR,
+    CRAS_CH_FC,
+    CRAS_CH_LFE,
+    CRAS_CH_RL,
+    CRAS_CH_RR,
+    CRAS_CH_FLC,
+    CRAS_CH_FRC,
+    CRAS_CH_RC,
+    CRAS_CH_SL,
+    CRAS_CH_SR
+  };
+  static_assert(base::size(kChannelMap) == CHANNELS_MAX + 1,
+                "kChannelMap array size should match");
+
+  // If already playing, stop before re-starting.
+  if (started_)
+    return;
+
+  StartAgc();
+
+  callback_ = callback;
+
+  CRAS_STREAM_TYPE type = CRAS_STREAM_TYPE_DEFAULT;
+  uint32_t flags = 0;
+  if (params_.effects() & AudioParameters::PlatformEffectsMask::HOTWORD) {
+    flags = HOTWORD_STREAM;
+    type = CRAS_STREAM_TYPE_SPEECH_RECOGNITION;
+  }
+
+  unsigned int frames_per_packet = params_.frames_per_buffer();
+  struct libcras_stream_params* stream_params = libcras_stream_params_create();
+  if (!stream_params) {
+    DLOG(ERROR) << "Error creating stream params";
+    callback_->OnError();
+    callback_ = NULL;
+    return;
+  }
+
+  int rc = libcras_stream_params_set(
+      stream_params, stream_direction_, frames_per_packet, frames_per_packet,
+      type, audio_manager_->GetClientType(), flags, this,
+      CrasInputStream::SamplesReady, CrasInputStream::StreamError,
+      params_.sample_rate(), SND_PCM_FORMAT_S16, params_.channels());
+
+  if (rc) {
+    DLOG(WARNING) << "Error setting up stream parameters.";
+    callback_->OnError();
+    callback_ = NULL;
+    libcras_stream_params_destroy(stream_params);
+    return;
+  }
+
+  // Initialize channel layout to all -1 to indicate that none of
+  // the channels is set in the layout.
+  int8_t layout[CRAS_CH_MAX];
+  for (size_t i = 0; i < base::size(layout); ++i)
+    layout[i] = -1;
+
+  // Converts to CRAS defined channels. ChannelOrder will return -1
+  // for channels that are not present in params_.channel_layout().
+  for (size_t i = 0; i < base::size(kChannelMap); ++i) {
+    layout[kChannelMap[i]] = ChannelOrder(params_.channel_layout(),
+                                          static_cast<Channels>(i));
+  }
+
+  rc = libcras_stream_params_set_channel_layout(stream_params, CRAS_CH_MAX,
+                                                layout);
+  if (rc) {
+    DLOG(WARNING) << "Error setting up the channel layout.";
+    callback_->OnError();
+    callback_ = NULL;
+    libcras_stream_params_destroy(stream_params);
+    return;
+  }
+
+  if (UseCrasAec())
+    libcras_stream_params_enable_aec(stream_params);
+
+  if (UseCrasNs())
+    libcras_stream_params_enable_ns(stream_params);
+
+  if (UseCrasAgc())
+    libcras_stream_params_enable_agc(stream_params);
+
+  // Adding the stream will start the audio callbacks.
+  if (libcras_client_add_pinned_stream(client_, pin_device_, &stream_id_,
+                                       stream_params)) {
+    DLOG(WARNING) << "Failed to add the stream.";
+    callback_->OnError();
+    callback_ = NULL;
+  }
+
+  // Mute system audio if requested.
+  if (mute_system_audio_) {
+    int muted;
+    libcras_client_get_system_muted(client_, &muted);
+    if (!muted)
+      libcras_client_set_system_mute(client_, 1);
+    mute_done_ = true;
+  }
+
+  // Done with config params.
+  libcras_stream_params_destroy(stream_params);
+
+  started_ = true;
+}
+
+void CrasInputStream::Stop() {
+  if (!client_)
+    return;
+
+  if (!callback_ || !started_)
+    return;
+
+  if (mute_system_audio_ && mute_done_) {
+    libcras_client_set_system_mute(client_, 0);
+    mute_done_ = false;
+  }
+
+  StopAgc();
+
+  // Removing the stream from the client stops audio.
+  libcras_client_rm_stream(client_, stream_id_);
+
+  started_ = false;
+  callback_ = NULL;
+}
+
+// Static callback asking for samples.  Run on high priority thread.
+int CrasInputStream::SamplesReady(struct libcras_stream_cb_data* data) {
+  unsigned int frames;
+  uint8_t* buf;
+  struct timespec latency;
+  void* usr_arg;
+  libcras_stream_cb_data_get_frames(data, &frames);
+  libcras_stream_cb_data_get_buf(data, &buf);
+  libcras_stream_cb_data_get_latency(data, &latency);
+  libcras_stream_cb_data_get_usr_arg(data, &usr_arg);
+  CrasInputStream* me = static_cast<CrasInputStream*>(usr_arg);
+  me->ReadAudio(frames, buf, &latency);
+  return frames;
+}
+
+// Static callback for stream errors.
+int CrasInputStream::StreamError(cras_client* client,
+                                 cras_stream_id_t stream_id,
+                                 int err,
+                                 void* arg) {
+  CrasInputStream* me = static_cast<CrasInputStream*>(arg);
+  me->NotifyStreamError(err);
+  return 0;
+}
+
+void CrasInputStream::ReadAudio(size_t frames,
+                                uint8_t* buffer,
+                                const timespec* latency_ts) {
+  DCHECK(callback_);
+
+  // Update the AGC volume level once every second. Note that, |volume| is
+  // also updated each time SetVolume() is called through IPC by the
+  // render-side AGC.
+  double normalized_volume = 0.0;
+  GetAgcVolume(&normalized_volume);
+
+  const base::TimeDelta delay =
+      std::max(base::TimeDelta::FromTimeSpec(*latency_ts), base::TimeDelta());
+
+  // The delay says how long ago the capture was, so we subtract the delay from
+  // Now() to find the capture time.
+  const base::TimeTicks capture_time = base::TimeTicks::Now() - delay;
+
+  audio_bus_->FromInterleaved<SignedInt16SampleTypeTraits>(
+      reinterpret_cast<int16_t*>(buffer), audio_bus_->frames());
+  callback_->OnData(audio_bus_.get(), capture_time, normalized_volume);
+}
+
+void CrasInputStream::NotifyStreamError(int err) {
+  if (callback_)
+    callback_->OnError();
+}
+
+double CrasInputStream::GetMaxVolume() {
+  return 1.0f;
+}
+
+void CrasInputStream::SetVolume(double volume) {
+  DCHECK(client_);
+
+  // Set the volume ratio to CRAS's softare and stream specific gain.
+  input_volume_ = volume;
+  libcras_client_set_stream_volume(client_, stream_id_, input_volume_);
+
+  // Update the AGC volume level based on the last setting above. Note that,
+  // the volume-level resolution is not infinite and it is therefore not
+  // possible to assume that the volume provided as input parameter can be
+  // used directly. Instead, a new query to the audio hardware is required.
+  // This method does nothing if AGC is disabled.
+  UpdateAgcVolume();
+}
+
+double CrasInputStream::GetVolume() {
+  if (!client_)
+    return 0.0;
+
+  return input_volume_;
+}
+
+bool CrasInputStream::IsMuted() {
+  return false;
+}
+
+void CrasInputStream::SetOutputDeviceForAec(
+    const std::string& output_device_id) {
+  // Not supported. Do nothing.
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/cras/cras_input.h b/third_party/chromium/media/audio/cras/cras_input.h
new file mode 100644
index 0000000..4acbc0c
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/cras_input.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_CRAS_CRAS_INPUT_H_
+#define MEDIA_AUDIO_CRAS_CRAS_INPUT_H_
+
+#include <cras_client.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "media/audio/agc_audio_stream.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class AudioManagerCrasBase;
+
+// Provides an input stream for audio capture based on CRAS, the ChromeOS Audio
+// Server.  This object is not thread safe and all methods should be invoked in
+// the thread that created the object.
+class MEDIA_EXPORT CrasInputStream : public AgcAudioStream<AudioInputStream> {
+ public:
+  // The ctor takes all the usual parameters, plus |manager| which is the
+  // audio manager who is creating this object.
+  CrasInputStream(const AudioParameters& params,
+                  AudioManagerCrasBase* manager,
+                  const std::string& device_id);
+
+  CrasInputStream(const CrasInputStream&) = delete;
+  CrasInputStream& operator=(const CrasInputStream&) = delete;
+
+  // The dtor is typically called by the AudioManager only and it is usually
+  // triggered by calling AudioOutputStream::Close().
+  ~CrasInputStream() override;
+
+  // Implementation of AudioInputStream.
+  AudioInputStream::OpenOutcome Open() override;
+  void Start(AudioInputCallback* callback) override;
+  void Stop() override;
+  void Close() override;
+  double GetMaxVolume() override;
+  void SetVolume(double volume) override;
+  double GetVolume() override;
+  bool IsMuted() override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+ private:
+  // Handles requests to get samples from the provided buffer.  This will be
+  // called by the audio server when it has samples ready.
+  static int SamplesReady(struct libcras_stream_cb_data* data);
+
+  // Handles notification that there was an error with the playback stream.
+  static int StreamError(cras_client* client,
+                         cras_stream_id_t stream_id,
+                         int err,
+                         void* arg);
+
+  // Reads one or more buffers of audio from the device, passes on to the
+  // registered callback. Called from SamplesReady().
+  void ReadAudio(size_t frames, uint8_t* buffer, const timespec* latency_ts);
+
+  // Deals with an error that occured in the stream.  Called from StreamError().
+  void NotifyStreamError(int err);
+
+  // Convert from dB * 100 to a volume ratio.
+  double GetVolumeRatioFromDecibels(double dB) const;
+
+  // Convert from a volume ratio to dB.
+  double GetDecibelsFromVolumeRatio(double volume_ratio) const;
+
+  // Return true to use AEC in CRAS for this input stream.
+  inline bool UseCrasAec() const;
+
+  // Return true to use NS in CRAS for this input stream.
+  inline bool UseCrasNs() const;
+
+  // Return true to use AGC in CRAS for this input stream.
+  inline bool UseCrasAgc() const;
+
+  // Non-refcounted pointer back to the audio manager.
+  // The AudioManager indirectly holds on to stream objects, so we don't
+  // want circular references.  Additionally, stream objects live on the audio
+  // thread, which is owned by the audio manager and we don't want to addref
+  // the manager from that thread.
+  AudioManagerCrasBase* const audio_manager_;
+
+  // Callback to pass audio samples too, valid while recording.
+  AudioInputCallback* callback_;
+
+  // The client used to communicate with the audio server.
+  struct libcras_client* client_;
+
+  // PCM parameters for the stream.
+  const AudioParameters params_;
+
+  // True if the stream has been started.
+  bool started_;
+
+  // ID of the playing stream.
+  cras_stream_id_t stream_id_;
+
+  // Direction of the stream.
+  const CRAS_STREAM_DIRECTION stream_direction_;
+
+  // Index of the CRAS device to stream input from.
+  int pin_device_;
+
+  // True if the stream is a system-wide loopback stream.
+  bool is_loopback_;
+
+  // True if we want to mute system audio during capturing.
+  bool mute_system_audio_;
+  bool mute_done_;
+
+  // Value of input stream volume, between 0.0 - 1.0.
+  double input_volume_;
+
+  std::unique_ptr<AudioBus> audio_bus_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_CRAS_CRAS_INPUT_H_
diff --git a/third_party/chromium/media/audio/cras/cras_input_unittest.cc b/third_party/chromium/media/audio/cras/cras_input_unittest.cc
new file mode 100644
index 0000000..5bda316
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/cras_input_unittest.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "ash/components/audio/cras_audio_handler.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_message_loop.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chromeos/dbus/audio/fake_cras_audio_client.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/cras/audio_manager_chromeos.h"
+#include "media/audio/fake_audio_log_factory.h"
+#include "media/audio/test_audio_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// cras_util.h defines custom min/max macros which break compilation, so ensure
+// it's not included until last.  #if avoids presubmit errors.
+#if defined(USE_CRAS)
+#include "media/audio/cras/cras_input.h"
+#endif
+
+using testing::_;
+using testing::AtLeast;
+using testing::Ge;
+using testing::InvokeWithoutArgs;
+using testing::StrictMock;
+
+namespace media {
+
+class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
+ public:
+  MOCK_METHOD3(OnData, void(const AudioBus*, base::TimeTicks, double));
+  MOCK_METHOD0(OnError, void());
+};
+
+class MockAudioManagerCrasInput : public AudioManagerChromeOS {
+ public:
+  MockAudioManagerCrasInput()
+      : AudioManagerChromeOS(std::make_unique<TestAudioThread>(),
+                             &fake_audio_log_factory_) {}
+
+  // We need to override this function in order to skip checking the number
+  // of active output streams. It is because the number of active streams
+  // is managed inside MakeAudioInputStream, and we don't use
+  // MakeAudioInputStream to create the stream in the tests.
+  void ReleaseInputStream(AudioInputStream* stream) override {
+    DCHECK(stream);
+    delete stream;
+  }
+
+ private:
+  FakeAudioLogFactory fake_audio_log_factory_;
+};
+
+class CrasInputStreamTest : public testing::Test {
+ protected:
+  CrasInputStreamTest() {
+    chromeos::CrasAudioClient::InitializeFake();
+    ash::CrasAudioHandler::InitializeForTesting();
+    mock_manager_.reset(new StrictMock<MockAudioManagerCrasInput>());
+    base::RunLoop().RunUntilIdle();
+  }
+
+  ~CrasInputStreamTest() override {
+    mock_manager_->Shutdown();
+    ash::CrasAudioHandler::Shutdown();
+    chromeos::CrasAudioClient::Shutdown();
+  }
+
+  CrasInputStream* CreateStream(ChannelLayout layout) {
+    return CreateStream(layout, kTestFramesPerPacket);
+  }
+
+  CrasInputStream* CreateStream(ChannelLayout layout,
+                                int32_t samples_per_packet) {
+    return CreateStream(layout, samples_per_packet,
+                        AudioDeviceDescription::kDefaultDeviceId);
+  }
+
+  CrasInputStream* CreateStream(ChannelLayout layout,
+                                int32_t samples_per_packet,
+                                const std::string& device_id) {
+    AudioParameters params(kTestFormat,
+                           layout,
+                           kTestSampleRate,
+                           samples_per_packet);
+    return new CrasInputStream(params, mock_manager_.get(), device_id);
+  }
+
+  void CaptureSomeFrames(const AudioParameters &params,
+                         unsigned int duration_ms) {
+    CrasInputStream* test_stream = new CrasInputStream(
+        params, mock_manager_.get(), AudioDeviceDescription::kDefaultDeviceId);
+
+    EXPECT_EQ(test_stream->Open(), AudioInputStream::OpenOutcome::kSuccess);
+
+    // Allow 8 frames variance for SRC in the callback.  Different numbers of
+    // samples can be provided when doing non-integer SRC.  For example
+    // converting from 192k to 44.1k is a ratio of 4.35 to 1.
+    MockAudioInputCallback mock_callback;
+    base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                              base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+    EXPECT_CALL(mock_callback, OnData(_, _, _))
+        .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal));
+
+    test_stream->Start(&mock_callback);
+
+    // Wait for samples to be captured.
+    EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout()));
+
+    test_stream->Stop();
+    test_stream->Close();
+  }
+
+  static const unsigned int kTestCaptureDurationMs;
+  static const ChannelLayout kTestChannelLayout;
+  static const AudioParameters::Format kTestFormat;
+  static const uint32_t kTestFramesPerPacket;
+  static const int kTestSampleRate;
+
+  base::TestMessageLoop message_loop_;
+  std::unique_ptr<StrictMock<MockAudioManagerCrasInput>> mock_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CrasInputStreamTest);
+};
+
+const unsigned int CrasInputStreamTest::kTestCaptureDurationMs = 250;
+const ChannelLayout CrasInputStreamTest::kTestChannelLayout =
+    CHANNEL_LAYOUT_STEREO;
+const AudioParameters::Format CrasInputStreamTest::kTestFormat =
+    AudioParameters::AUDIO_PCM_LINEAR;
+const uint32_t CrasInputStreamTest::kTestFramesPerPacket = 1000;
+const int CrasInputStreamTest::kTestSampleRate = 44100;
+
+TEST_F(CrasInputStreamTest, OpenMono) {
+  CrasInputStream* test_stream = CreateStream(CHANNEL_LAYOUT_MONO);
+  EXPECT_EQ(test_stream->Open(), AudioInputStream::OpenOutcome::kSuccess);
+  test_stream->Close();
+}
+
+TEST_F(CrasInputStreamTest, OpenStereo) {
+  CrasInputStream* test_stream = CreateStream(CHANNEL_LAYOUT_STEREO);
+  EXPECT_EQ(test_stream->Open(), AudioInputStream::OpenOutcome::kSuccess);
+  test_stream->Close();
+}
+
+TEST_F(CrasInputStreamTest, BadSampleRate) {
+  AudioParameters bad_rate_params(kTestFormat,
+                                  kTestChannelLayout,
+                                  0,
+                                  kTestFramesPerPacket);
+  CrasInputStream* test_stream =
+      new CrasInputStream(bad_rate_params, mock_manager_.get(),
+                          AudioDeviceDescription::kDefaultDeviceId);
+  EXPECT_EQ(test_stream->Open(), AudioInputStream::OpenOutcome::kFailed);
+  test_stream->Close();
+}
+
+TEST_F(CrasInputStreamTest, SetGetVolume) {
+  CrasInputStream* test_stream = CreateStream(CHANNEL_LAYOUT_MONO);
+  EXPECT_EQ(test_stream->Open(), AudioInputStream::OpenOutcome::kSuccess);
+
+  double max_volume = test_stream->GetMaxVolume();
+  EXPECT_GE(max_volume, 1.0);
+
+  test_stream->SetVolume(max_volume / 2);
+
+  double new_volume = test_stream->GetVolume();
+
+  EXPECT_GE(new_volume, 0.0);
+  EXPECT_LE(new_volume, max_volume);
+
+  test_stream->Close();
+}
+
+TEST_F(CrasInputStreamTest, CaptureFrames) {
+  const unsigned int rates[] =
+      {8000, 16000, 22050, 32000, 44100, 48000, 96000, 192000};
+
+  for (unsigned int i = 0; i < ARRAY_SIZE(rates); i++) {
+    SCOPED_TRACE(testing::Message() << "Mono " << rates[i] << "Hz");
+    AudioParameters params_mono(kTestFormat,
+                                CHANNEL_LAYOUT_MONO,
+                                rates[i],
+                                kTestFramesPerPacket);
+    CaptureSomeFrames(params_mono, kTestCaptureDurationMs);
+  }
+
+  for (unsigned int i = 0; i < ARRAY_SIZE(rates); i++) {
+    SCOPED_TRACE(testing::Message() << "Stereo " << rates[i] << "Hz");
+    AudioParameters params_stereo(kTestFormat,
+                                  CHANNEL_LAYOUT_STEREO,
+                                  rates[i],
+                                  kTestFramesPerPacket);
+    CaptureSomeFrames(params_stereo, kTestCaptureDurationMs);
+  }
+}
+
+TEST_F(CrasInputStreamTest, CaptureLoopback) {
+  CrasInputStream* test_stream =
+      CreateStream(CHANNEL_LAYOUT_STEREO, kTestFramesPerPacket,
+                   AudioDeviceDescription::kLoopbackInputDeviceId);
+  EXPECT_EQ(test_stream->Open(), AudioInputStream::OpenOutcome::kSuccess);
+  test_stream->Close();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/cras/cras_unified.cc b/third_party/chromium/media/audio/cras/cras_unified.cc
new file mode 100644
index 0000000..d4a5069
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/cras_unified.cc
@@ -0,0 +1,292 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/cras/cras_unified.h"
+
+#include <algorithm>
+
+#include "base/cxx17_backports.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "media/audio/cras/audio_manager_cras_base.h"
+
+namespace media {
+
+namespace {
+
+int GetDevicePin(AudioManagerCrasBase* manager, const std::string& device_id) {
+  if (!manager->IsDefault(device_id, false)) {
+    uint64_t cras_node_id;
+    base::StringToUint64(device_id, &cras_node_id);
+    return dev_index_of(cras_node_id);
+  }
+  return NO_DEVICE;
+}
+
+}  // namespace
+
+// Overview of operation:
+// 1) An object of CrasUnifiedStream is created by the AudioManager
+// factory: audio_man->MakeAudioStream().
+// 2) Next some thread will call Open(), at that point a client is created and
+// configured for the correct format and sample rate.
+// 3) Then Start(source) is called and a stream is added to the CRAS client
+// which will create its own thread that periodically calls the source for more
+// data as buffers are being consumed.
+// 4) When finished Stop() is called, which is handled by stopping the stream.
+// 5) Finally Close() is called. It cleans up and notifies the audio manager,
+// which likely will destroy this object.
+//
+// Simplified data flow for output only streams:
+//
+//   +-------------+                  +------------------+
+//   | CRAS Server |                  | Chrome Client    |
+//   +------+------+    Add Stream    +---------+--------+
+//          |<----------------------------------|
+//          |                                   |
+//          | Near out of samples, request more |
+//          |---------------------------------->|
+//          |                                   |  UnifiedCallback()
+//          |                                   |  WriteAudio()
+//          |                                   |
+//          |  buffer_frames written to shm     |
+//          |<----------------------------------|
+//          |                                   |
+//         ...  Repeats for each block.        ...
+//          |                                   |
+//          |                                   |
+//          |  Remove stream                    |
+//          |<----------------------------------|
+//          |                                   |
+//
+// For Unified streams the Chrome client is notified whenever buffer_frames have
+// been captured.  For Output streams the client is notified a few milliseconds
+// before the hardware buffer underruns and fills the buffer with another block
+// of audio.
+
+CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params,
+                                     AudioManagerCrasBase* manager,
+                                     const std::string& device_id)
+    : client_(NULL),
+      stream_id_(0),
+      params_(params),
+      is_playing_(false),
+      volume_(1.0),
+      manager_(manager),
+      source_callback_(NULL),
+      output_bus_(AudioBus::Create(params)),
+      stream_direction_(CRAS_STREAM_OUTPUT),
+      pin_device_(GetDevicePin(manager, device_id)) {
+  DCHECK(manager_);
+  DCHECK_GT(params_.channels(), 0);
+}
+
+CrasUnifiedStream::~CrasUnifiedStream() {
+  DCHECK(!is_playing_);
+}
+
+bool CrasUnifiedStream::Open() {
+  // Sanity check input values.
+  if (params_.sample_rate() <= 0) {
+    LOG(WARNING) << "Unsupported audio frequency.";
+    return false;
+  }
+
+  // Create the client and connect to the CRAS server.
+  client_ = libcras_client_create();
+  if (!client_) {
+    LOG(WARNING) << "Couldn't create CRAS client.\n";
+    client_ = NULL;
+    return false;
+  }
+
+  if (libcras_client_connect(client_)) {
+    LOG(WARNING) << "Couldn't connect CRAS client.\n";
+    libcras_client_destroy(client_);
+    client_ = NULL;
+    return false;
+  }
+
+  // Then start running the client.
+  if (libcras_client_run_thread(client_)) {
+    LOG(WARNING) << "Couldn't run CRAS client.\n";
+    libcras_client_destroy(client_);
+    client_ = NULL;
+    return false;
+  }
+
+  return true;
+}
+
+void CrasUnifiedStream::Close() {
+  if (client_) {
+    libcras_client_stop(client_);
+    libcras_client_destroy(client_);
+    client_ = NULL;
+  }
+
+  // Signal to the manager that we're closed and can be removed.
+  // Should be last call in the method as it deletes "this".
+  manager_->ReleaseOutputStream(this);
+}
+
+// This stream is always used with sub second buffer sizes, where it's
+// sufficient to simply always flush upon Start().
+void CrasUnifiedStream::Flush() {}
+
+void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
+  CHECK(callback);
+
+  // Channel map to CRAS_CHANNEL, values in the same order of
+  // corresponding source in Chromium defined Channels.
+  static const int kChannelMap[] = {
+    CRAS_CH_FL,
+    CRAS_CH_FR,
+    CRAS_CH_FC,
+    CRAS_CH_LFE,
+    CRAS_CH_RL,
+    CRAS_CH_RR,
+    CRAS_CH_FLC,
+    CRAS_CH_FRC,
+    CRAS_CH_RC,
+    CRAS_CH_SL,
+    CRAS_CH_SR
+  };
+
+  source_callback_ = callback;
+
+  // Only start if we can enter the playing state.
+  if (is_playing_)
+    return;
+
+  struct libcras_stream_params* stream_params = libcras_stream_params_create();
+  if (!stream_params) {
+    DLOG(ERROR) << "Error creating stream params.";
+    callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+  }
+
+  unsigned int frames_per_packet = params_.frames_per_buffer();
+  int rc = libcras_stream_params_set(
+      stream_params, stream_direction_, frames_per_packet * 2,
+      frames_per_packet, CRAS_STREAM_TYPE_DEFAULT, manager_->GetClientType(), 0,
+      this, CrasUnifiedStream::UnifiedCallback, CrasUnifiedStream::StreamError,
+      params_.sample_rate(), SND_PCM_FORMAT_S16, params_.channels());
+
+  if (rc) {
+    LOG(WARNING) << "Error setting up stream parameters.";
+    callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+    libcras_stream_params_destroy(stream_params);
+    return;
+  }
+
+  // Initialize channel layout to all -1 to indicate that none of
+  // the channels is set in the layout.
+  int8_t layout[CRAS_CH_MAX] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+
+  // Converts to CRAS defined channels. ChannelOrder will return -1
+  // for channels that does not present in params_.channel_layout().
+  for (size_t i = 0; i < base::size(kChannelMap); ++i)
+    layout[kChannelMap[i]] = ChannelOrder(params_.channel_layout(),
+                                          static_cast<Channels>(i));
+
+  rc = libcras_stream_params_set_channel_layout(stream_params, CRAS_CH_MAX,
+                                                layout);
+  if (rc) {
+    DLOG(WARNING) << "Error setting up the channel layout.";
+    callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+    libcras_stream_params_destroy(stream_params);
+    return;
+  }
+
+  // Adding the stream will start the audio callbacks requesting data.
+  if (libcras_client_add_pinned_stream(client_, pin_device_, &stream_id_,
+                                       stream_params)) {
+    LOG(WARNING) << "Failed to add the stream.";
+    callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+    libcras_stream_params_destroy(stream_params);
+    return;
+  }
+
+  // Set initial volume.
+  libcras_client_set_stream_volume(client_, stream_id_, volume_);
+
+  // Done with config params.
+  libcras_stream_params_destroy(stream_params);
+
+  is_playing_ = true;
+}
+
+void CrasUnifiedStream::Stop() {
+  if (!client_)
+    return;
+
+  // Removing the stream from the client stops audio.
+  libcras_client_rm_stream(client_, stream_id_);
+
+  is_playing_ = false;
+}
+
+void CrasUnifiedStream::SetVolume(double volume) {
+  if (!client_)
+    return;
+  volume_ = static_cast<float>(volume);
+  libcras_client_set_stream_volume(client_, stream_id_, volume_);
+}
+
+void CrasUnifiedStream::GetVolume(double* volume) {
+  *volume = volume_;
+}
+
+// Static callback asking for samples.
+int CrasUnifiedStream::UnifiedCallback(struct libcras_stream_cb_data* data) {
+  unsigned int frames;
+  uint8_t* buf;
+  struct timespec latency;
+  void* usr_arg;
+  libcras_stream_cb_data_get_frames(data, &frames);
+  libcras_stream_cb_data_get_buf(data, &buf);
+  libcras_stream_cb_data_get_latency(data, &latency);
+  libcras_stream_cb_data_get_usr_arg(data, &usr_arg);
+  CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(usr_arg);
+  return me->WriteAudio(frames, buf, &latency);
+}
+
+// Static callback for stream errors.
+int CrasUnifiedStream::StreamError(cras_client* client,
+                                   cras_stream_id_t stream_id,
+                                   int err,
+                                   void* arg) {
+  CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
+  me->NotifyStreamError(err);
+  return 0;
+}
+
+uint32_t CrasUnifiedStream::WriteAudio(size_t frames,
+                                       uint8_t* buffer,
+                                       const timespec* latency_ts) {
+  DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
+
+  // Treat negative latency (if we are too slow to render) as 0.
+  const base::TimeDelta delay =
+      std::max(base::TimeDelta::FromTimeSpec(*latency_ts), base::TimeDelta());
+
+  int frames_filled = source_callback_->OnMoreData(
+      delay, base::TimeTicks::Now(), 0, output_bus_.get());
+
+  // Note: If this ever changes to output raw float the data must be clipped and
+  // sanitized since it may come from an untrusted source such as NaCl.
+  output_bus_->ToInterleaved<SignedInt16SampleTypeTraits>(
+      frames_filled, reinterpret_cast<int16_t*>(buffer));
+
+  return frames_filled;
+}
+
+void CrasUnifiedStream::NotifyStreamError(int err) {
+  // This will remove the stream from the client.
+  // TODO(dalecurtis): Consider sending a translated |err| code.
+  if (source_callback_)
+    source_callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/cras/cras_unified.h b/third_party/chromium/media/audio/cras/cras_unified.h
new file mode 100644
index 0000000..9aeca27
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/cras_unified.h
@@ -0,0 +1,108 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Creates a unified stream based on the cras (ChromeOS audio server) interface.
+//
+// CrasUnifiedStream object is *not* thread-safe and should only be used
+// from the audio thread.
+
+#ifndef MEDIA_AUDIO_CRAS_CRAS_UNIFIED_H_
+#define MEDIA_AUDIO_CRAS_CRAS_UNIFIED_H_
+
+#include <cras_client.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioManagerCrasBase;
+
+// Implementation of AudioOuputStream for Chrome OS using the Chrome OS audio
+// server.
+// TODO(dgreid): This class is used for only output, either remove all the
+// relevant input code and change the class to CrasOutputStream or merge
+// cras_input.cc into this unified implementation.
+class MEDIA_EXPORT CrasUnifiedStream : public AudioOutputStream {
+ public:
+  // The ctor takes all the usual parameters, plus |manager| which is the
+  // audio manager who is creating this object.
+  CrasUnifiedStream(const AudioParameters& params,
+                    AudioManagerCrasBase* manager,
+                    const std::string& device_id);
+
+  CrasUnifiedStream(const CrasUnifiedStream&) = delete;
+  CrasUnifiedStream& operator=(const CrasUnifiedStream&) = delete;
+
+  // The dtor is typically called by the AudioManager only and it is usually
+  // triggered by calling AudioUnifiedStream::Close().
+  ~CrasUnifiedStream() override;
+
+  // Implementation of AudioOutputStream.
+  bool Open() override;
+  void Close() override;
+  void Flush() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+
+ private:
+  // Handles captured audio and fills the output with audio to be played.
+  static int UnifiedCallback(struct libcras_stream_cb_data* data);
+
+  // Handles notification that there was an error with the playback stream.
+  static int StreamError(cras_client* client,
+                         cras_stream_id_t stream_id,
+                         int err,
+                         void* arg);
+
+  // Writes audio for a playback stream.
+  uint32_t WriteAudio(size_t frames,
+                      uint8_t* buffer,
+                      const timespec* latency_ts);
+
+  // Deals with an error that occured in the stream.  Called from StreamError().
+  void NotifyStreamError(int err);
+
+  // The client used to communicate with the audio server.
+  struct libcras_client* client_;
+
+  // ID of the playing stream.
+  cras_stream_id_t stream_id_;
+
+  // PCM parameters for the stream.
+  AudioParameters params_;
+
+  // True if stream is playing.
+  bool is_playing_;
+
+  // Volume level from 0.0 to 1.0.
+  float volume_;
+
+  // Audio manager that created us.  Used to report that we've been closed.
+  AudioManagerCrasBase* manager_;
+
+  // Callback to get audio samples.
+  AudioSourceCallback* source_callback_;
+
+  // Container for exchanging data with AudioSourceCallback::OnMoreData().
+  std::unique_ptr<AudioBus> output_bus_;
+
+  // Direciton of the stream.
+  CRAS_STREAM_DIRECTION stream_direction_;
+
+  // Index of the CRAS device to stream output to.
+  const int pin_device_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_CRAS_CRAS_UNIFIED_H_
diff --git a/third_party/chromium/media/audio/cras/cras_unified_unittest.cc b/third_party/chromium/media/audio/cras/cras_unified_unittest.cc
new file mode 100644
index 0000000..cd0c366
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/cras_unified_unittest.cc
@@ -0,0 +1,160 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "ash/components/audio/cras_audio_handler.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_message_loop.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chromeos/dbus/audio/cras_audio_client.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/cras/audio_manager_chromeos.h"
+#include "media/audio/fake_audio_log_factory.h"
+#include "media/audio/mock_audio_source_callback.h"
+#include "media/audio/test_audio_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// cras_util.h defines custom min/max macros which break compilation, so ensure
+// it's not included until last.  #if avoids presubmit errors.
+#if defined(USE_CRAS)
+#include "media/audio/cras/cras_unified.h"
+#endif
+
+using testing::_;
+using testing::DoAll;
+using testing::InvokeWithoutArgs;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::StrictMock;
+
+namespace media {
+
+class MockAudioManagerCras : public AudioManagerChromeOS {
+ public:
+  MockAudioManagerCras()
+      : AudioManagerChromeOS(std::make_unique<TestAudioThread>(),
+                             &fake_audio_log_factory_) {}
+
+  // We need to override this function in order to skip the checking the number
+  // of active output streams. It is because the number of active streams
+  // is managed inside MakeAudioOutputStream, and we don't use
+  // MakeAudioOutputStream to create the stream in the tests.
+  void ReleaseOutputStream(AudioOutputStream* stream) override {
+    DCHECK(stream);
+    delete stream;
+  }
+
+ private:
+  FakeAudioLogFactory fake_audio_log_factory_;
+};
+
+class CrasUnifiedStreamTest : public testing::Test {
+ protected:
+  CrasUnifiedStreamTest() {
+    chromeos::CrasAudioClient::InitializeFake();
+    ash::CrasAudioHandler::InitializeForTesting();
+    mock_manager_.reset(new StrictMock<MockAudioManagerCras>());
+    base::RunLoop().RunUntilIdle();
+  }
+
+  ~CrasUnifiedStreamTest() override {
+    mock_manager_->Shutdown();
+    ash::CrasAudioHandler::Shutdown();
+    chromeos::CrasAudioClient::Shutdown();
+  }
+
+  CrasUnifiedStream* CreateStream(ChannelLayout layout) {
+    return CreateStream(layout, kTestFramesPerPacket);
+  }
+
+  CrasUnifiedStream* CreateStream(ChannelLayout layout,
+                                  int32_t samples_per_packet) {
+    AudioParameters params(kTestFormat, layout, kTestSampleRate,
+                           samples_per_packet);
+    return new CrasUnifiedStream(params, mock_manager_.get(),
+                                 AudioDeviceDescription::kDefaultDeviceId);
+  }
+
+  MockAudioManagerCras& mock_manager() {
+    return *(mock_manager_.get());
+  }
+
+  static const ChannelLayout kTestChannelLayout;
+  static const int kTestSampleRate;
+  static const AudioParameters::Format kTestFormat;
+  static const uint32_t kTestFramesPerPacket;
+
+  base::TestMessageLoop message_loop_;
+  std::unique_ptr<StrictMock<MockAudioManagerCras>> mock_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CrasUnifiedStreamTest);
+};
+
+const ChannelLayout CrasUnifiedStreamTest::kTestChannelLayout =
+    CHANNEL_LAYOUT_STEREO;
+const int CrasUnifiedStreamTest::kTestSampleRate =
+    AudioParameters::kAudioCDSampleRate;
+const AudioParameters::Format CrasUnifiedStreamTest::kTestFormat =
+    AudioParameters::AUDIO_PCM_LINEAR;
+const uint32_t CrasUnifiedStreamTest::kTestFramesPerPacket = 1000;
+
+TEST_F(CrasUnifiedStreamTest, ConstructedState) {
+  CrasUnifiedStream* test_stream = CreateStream(kTestChannelLayout);
+  EXPECT_TRUE(test_stream->Open());
+  test_stream->Close();
+
+  // Should support mono.
+  test_stream = CreateStream(CHANNEL_LAYOUT_MONO);
+  EXPECT_TRUE(test_stream->Open());
+  test_stream->Close();
+
+  // Should support multi-channel.
+  test_stream = CreateStream(CHANNEL_LAYOUT_SURROUND);
+  EXPECT_TRUE(test_stream->Open());
+  test_stream->Close();
+
+  // Bad sample rate.
+  AudioParameters bad_rate_params(kTestFormat, kTestChannelLayout, 0,
+                                  kTestFramesPerPacket);
+  test_stream = new CrasUnifiedStream(bad_rate_params, mock_manager_.get(),
+                                      AudioDeviceDescription::kDefaultDeviceId);
+  EXPECT_FALSE(test_stream->Open());
+  test_stream->Close();
+}
+
+TEST_F(CrasUnifiedStreamTest, RenderFrames) {
+  CrasUnifiedStream* test_stream = CreateStream(CHANNEL_LAYOUT_MONO);
+  MockAudioSourceCallback mock_callback;
+
+  ASSERT_TRUE(test_stream->Open());
+
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+
+  EXPECT_CALL(mock_callback, OnMoreData(_, _, 0, _))
+      .WillRepeatedly(
+          DoAll(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal),
+                Return(kTestFramesPerPacket)));
+
+  test_stream->Start(&mock_callback);
+
+  // Wait for samples to be captured.
+  EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout()));
+
+  test_stream->Stop();
+
+  test_stream->Close();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/cras/cras_util.cc b/third_party/chromium/media/audio/cras/cras_util.cc
new file mode 100644
index 0000000..2198db3
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/cras_util.cc
@@ -0,0 +1,270 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/cras/cras_util.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/cras/audio_manager_cras_base.h"
+
+namespace media {
+
+namespace {
+
+constexpr char kInternalInputVirtualDevice[] = "Built-in mic";
+constexpr char kInternalOutputVirtualDevice[] = "Built-in speaker";
+constexpr char kHeadphoneLineOutVirtualDevice[] = "Headphone/Line Out";
+
+// Names below are from the node_type_to_str function in CRAS server.
+// https://chromium.googlesource.com/chromiumos/third_party/adhd/+/refs/heads/main/cras/src/server/cras_iodev_list.c
+constexpr char kInternalSpeaker[] = "INTERNAL_SPEAKER";
+constexpr char kHeadphone[] = "HEADPHONE";
+constexpr char kHDMI[] = "HDMI";
+constexpr char kLineout[] = "LINEOUT";
+constexpr char kMic[] = "MIC";
+constexpr char kInternalMic[] = "INTERNAL_MIC";
+constexpr char kFrontMic[] = "FRONT_MIC";
+constexpr char kRearMic[] = "REAR_MIC";
+constexpr char kKeyBoardMic[] = "KEYBOARD_MIC";
+constexpr char kBluetoothNBMic[] = "BLUETOOTH_NB_MIC";
+constexpr char kUSB[] = "USB";
+constexpr char kBluetooth[] = "BLUETOOTH";
+constexpr char kAlsaLoopback[] = "ALSA_LOOPBACK";
+
+// Returns if that an input or output audio device is for simple usage like
+// playback or recording for user. In contrast, audio device such as loopback,
+// always on keyword recognition (HOTWORD), and keyboard mic are not for simple
+// usage.
+// One special case is ALSA loopback device, which will only exist under
+// testing. We want it visible to users for e2e tests.
+bool IsForSimpleUsage(std::string type) {
+  return type == kInternalMic || type == kHeadphone || type == kHDMI ||
+         type == kLineout || type == kMic || type == kInternalMic ||
+         type == kFrontMic || type == kRearMic || type == kBluetoothNBMic ||
+         type == kUSB || type == kBluetooth || type == kAlsaLoopback;
+}
+
+bool IsInternalMic(std::string type) {
+  return type == kInternalMic || type == kFrontMic || type == kRearMic;
+}
+
+// Connects to the CRAS server.
+libcras_client* CrasConnect() {
+  libcras_client* client;
+
+  client = libcras_client_create();
+  if (!client) {
+    LOG(ERROR) << "Couldn't create CRAS client.\n";
+    return nullptr;
+  }
+  if (libcras_client_connect(client)) {
+    LOG(ERROR) << "Couldn't connect CRAS client.\n";
+    libcras_client_destroy(client);
+    return nullptr;
+  }
+  return client;
+}
+
+// Disconnects from the CRAS server.
+void CrasDisconnect(libcras_client** client) {
+  if (*client) {
+    libcras_client_stop(*client);
+    libcras_client_destroy(*client);
+    *client = nullptr;
+  }
+}
+
+}  // namespace
+
+CrasDevice::CrasDevice() = default;
+
+CrasDevice::CrasDevice(struct libcras_node_info* node, DeviceType type)
+    : type(type) {
+  int rc;
+  rc = libcras_node_info_get_id(node, &id);
+  if (rc) {
+    LOG(ERROR) << "Failed to get the node id: " << rc;
+    id = 0;
+  }
+
+  rc = libcras_node_info_get_dev_idx(node, &dev_idx);
+  if (rc) {
+    LOG(ERROR) << "Failed to get the dev idx: " << rc;
+    dev_idx = 0;
+  }
+
+  rc = libcras_node_info_is_plugged(node, &plugged);
+  if (rc) {
+    LOG(ERROR) << "Failed to get if the node is plugged: " << rc;
+    plugged = false;
+  }
+
+  rc = libcras_node_info_is_active(node, &active);
+  if (rc) {
+    LOG(ERROR) << "Failed to get if the node is active: " << rc;
+    active = false;
+  }
+
+  char* type_str;
+  rc = libcras_node_info_get_type(node, &type_str);
+  if (rc) {
+    LOG(ERROR) << "Failed to get the node type: " << rc;
+    node_type = nullptr;
+  }
+  node_type = type_str;
+
+  char* node_name;
+  rc = libcras_node_info_get_node_name(node, &node_name);
+  if (rc) {
+    LOG(ERROR) << "Failed to get the node name: " << rc;
+    node_name = nullptr;
+  }
+
+  char* device_name;
+  rc = libcras_node_info_get_dev_name(node, &device_name);
+  if (rc) {
+    LOG(ERROR) << "Failed to get the dev name: " << rc;
+    device_name = nullptr;
+  }
+
+  name = std::string(node_name);
+  if (name.empty() || name == "(default)")
+    name = device_name;
+  dev_name = device_name;
+}
+
+void mergeDevices(CrasDevice& old_dev, CrasDevice& new_dev) {
+  if (old_dev.node_type == kLineout || new_dev.node_type == kLineout) {
+    old_dev.name = kHeadphoneLineOutVirtualDevice;
+    old_dev.node_type = "";
+  } else if (old_dev.node_type == kInternalSpeaker ||
+             new_dev.node_type == kInternalSpeaker) {
+    old_dev.name = kInternalOutputVirtualDevice;
+    old_dev.node_type = "";
+  } else if (IsInternalMic(old_dev.node_type) ||
+             IsInternalMic(new_dev.node_type)) {
+    old_dev.name = kInternalInputVirtualDevice;
+    old_dev.node_type = "";
+  } else {
+    LOG(WARNING) << "Failed to create virtual device for " << old_dev.name;
+  }
+  old_dev.active |= new_dev.active;
+}
+
+std::vector<CrasDevice> CrasGetAudioDevices(DeviceType type) {
+  std::vector<CrasDevice> devices;
+
+  libcras_client* client = CrasConnect();
+  if (!client)
+    return devices;
+
+  int rc;
+
+  struct libcras_node_info** nodes;
+  size_t num_nodes;
+
+  if (type == DeviceType::kInput) {
+    rc =
+        libcras_client_get_nodes(client, CRAS_STREAM_INPUT, &nodes, &num_nodes);
+  } else {
+    rc = libcras_client_get_nodes(client, CRAS_STREAM_OUTPUT, &nodes,
+                                  &num_nodes);
+  }
+
+  if (rc < 0) {
+    LOG(ERROR) << "Failed to get devices: " << std::strerror(rc);
+    CrasDisconnect(&client);
+    return devices;
+  }
+
+  for (size_t i = 0; i < num_nodes; i++) {
+    auto new_dev = CrasDevice(nodes[i], type);
+    if (!new_dev.plugged || !IsForSimpleUsage(new_dev.node_type))
+      continue;
+    bool added = false;
+    for (auto& dev : devices) {
+      if (dev.dev_idx == new_dev.dev_idx) {
+        mergeDevices(dev, new_dev);
+        added = true;
+        break;
+      }
+    }
+    if (!added)
+      devices.emplace_back(new_dev);
+  }
+
+  libcras_node_info_array_destroy(nodes, num_nodes);
+
+  CrasDisconnect(&client);
+  return devices;
+}
+
+bool CrasHasKeyboardMic() {
+  libcras_client* client = CrasConnect();
+  if (!client)
+    return false;
+
+  struct libcras_node_info** nodes;
+  size_t num_nodes;
+  int rc =
+      libcras_client_get_nodes(client, CRAS_STREAM_INPUT, &nodes, &num_nodes);
+  int ret = false;
+
+  if (rc < 0) {
+    LOG(ERROR) << "Failed to get devices: " << std::strerror(rc);
+    CrasDisconnect(&client);
+    return false;
+  }
+
+  for (size_t i = 0; i < num_nodes; i++) {
+    auto device = CrasDevice(nodes[i], DeviceType::kInput);
+    if (device.node_type == kKeyBoardMic)
+      ret = true;
+  }
+
+  libcras_node_info_array_destroy(nodes, num_nodes);
+
+  CrasDisconnect(&client);
+  return ret;
+}
+
+int CrasGetAecSupported() {
+  libcras_client* client = CrasConnect();
+  if (!client)
+    return 0;
+
+  int supported;
+  libcras_client_get_aec_supported(client, &supported);
+  CrasDisconnect(&client);
+
+  return supported;
+}
+
+int CrasGetAecGroupId() {
+  libcras_client* client = CrasConnect();
+  if (!client)
+    return -1;
+
+  int id;
+  int rc = libcras_client_get_aec_group_id(client, &id);
+  CrasDisconnect(&client);
+
+  return rc < 0 ? rc : id;
+}
+
+int CrasGetDefaultOutputBufferSize() {
+  libcras_client* client = CrasConnect();
+  if (!client)
+    return -1;
+
+  int size;
+  int rc = libcras_client_get_default_output_buffer_size(client, &size);
+  CrasDisconnect(&client);
+
+  return rc < 0 ? rc : size;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/cras/cras_util.h b/third_party/chromium/media/audio/cras/cras_util.h
new file mode 100644
index 0000000..14d1d8f
--- /dev/null
+++ b/third_party/chromium/media/audio/cras/cras_util.h
@@ -0,0 +1,50 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_CRAS_CRAS_UTIL_H_
+#define MEDIA_AUDIO_CRAS_CRAS_UTIL_H_
+
+#include <cras_client.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace media {
+
+enum class DeviceType { kInput, kOutput };
+
+struct CrasDevice {
+  CrasDevice();
+  explicit CrasDevice(struct libcras_node_info* node, DeviceType type);
+
+  DeviceType type;
+  uint64_t id;
+  uint32_t dev_idx;
+  bool plugged;
+  bool active;
+  std::string node_type;
+  std::string name;
+  std::string dev_name;
+};
+
+// Enumerates all devices of |type|.
+std::vector<CrasDevice> CrasGetAudioDevices(DeviceType type);
+
+// Returns if there is a keyboard mic in CRAS.
+bool CrasHasKeyboardMic();
+
+// Returns if system AEC is supported in CRAS.
+int CrasGetAecSupported();
+
+// Returns the system AEC group ID. If no group ID is specified, -1 is
+// returned.
+int CrasGetAecGroupId();
+
+// Returns the default output buffer size.
+int CrasGetDefaultOutputBufferSize();
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_CRAS_CRAS_UTIL_H_
diff --git a/third_party/chromium/media/audio/fake_audio_input_stream.cc b/third_party/chromium/media/audio/fake_audio_input_stream.cc
new file mode 100644
index 0000000..7d50030
--- /dev/null
+++ b/third_party/chromium/media/audio/fake_audio_input_stream.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/fake_audio_input_stream.h"
+
+#include <memory>
+#include <string>
+
+#include "base/atomicops.h"
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_split.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/simple_sources.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/media_switches.h"
+
+namespace media {
+
+namespace {
+base::subtle::AtomicWord g_fake_input_streams_are_muted = 0;
+}
+
+AudioInputStream* FakeAudioInputStream::MakeFakeStream(
+    AudioManagerBase* manager,
+    const AudioParameters& params) {
+  return new FakeAudioInputStream(manager, params);
+}
+
+FakeAudioInputStream::FakeAudioInputStream(AudioManagerBase* manager,
+                                           const AudioParameters& params)
+    : audio_manager_(manager),
+      callback_(nullptr),
+      params_(params),
+      audio_bus_(AudioBus::Create(params)),
+      capture_thread_(
+          nullptr,
+          base::OnTaskRunnerDeleter(manager->GetWorkerTaskRunner())) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+}
+
+FakeAudioInputStream::~FakeAudioInputStream() {
+  // |worker_| should be null as Stop() should have been called before.
+  DCHECK(!capture_thread_);
+  DCHECK(!callback_);
+  DCHECK(!fake_audio_worker_);
+}
+
+AudioInputStream::OpenOutcome FakeAudioInputStream::Open() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  audio_bus_->Zero();
+
+  return OpenOutcome::kSuccess;
+}
+
+void FakeAudioInputStream::Start(AudioInputCallback* callback) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(!capture_thread_);
+  DCHECK(callback);
+  DCHECK(!fake_audio_worker_);
+
+  capture_thread_.reset(new base::Thread("FakeAudioInput"));
+  base::Thread::Options options;
+  // REALTIME_AUDIO priority is needed to avoid audio playout delays.
+  // See crbug.com/971265
+  options.priority = base::ThreadPriority::REALTIME_AUDIO;
+  CHECK(capture_thread_->StartWithOptions(std::move(options)));
+
+  {
+    base::AutoLock lock(callback_lock_);
+    DCHECK(!callback_);
+    callback_ = callback;
+  }
+
+  fake_audio_worker_ = std::make_unique<FakeAudioWorker>(
+      capture_thread_->task_runner(), params_);
+  fake_audio_worker_->Start(base::BindRepeating(
+      &FakeAudioInputStream::ReadAudioFromSource, base::Unretained(this)));
+}
+
+void FakeAudioInputStream::Stop() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  // Start has not been called yet.
+  if (!capture_thread_) {
+    return;
+  }
+
+  {
+    base::AutoLock lock(callback_lock_);
+    DCHECK(callback_);
+    callback_ = nullptr;
+  }
+
+  DCHECK(fake_audio_worker_);
+  fake_audio_worker_->Stop();
+  fake_audio_worker_.reset();
+
+  capture_thread_.reset();
+}
+
+void FakeAudioInputStream::Close() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  Stop();
+  audio_manager_->ReleaseInputStream(this);
+}
+
+double FakeAudioInputStream::GetMaxVolume() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return 1.0;
+}
+
+void FakeAudioInputStream::SetVolume(double volume) {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+}
+
+double FakeAudioInputStream::GetVolume() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return 1.0;
+}
+
+bool FakeAudioInputStream::IsMuted() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  return base::subtle::NoBarrier_Load(&g_fake_input_streams_are_muted) != 0;
+}
+
+bool FakeAudioInputStream::SetAutomaticGainControl(bool enabled) {
+  return false;
+}
+
+bool FakeAudioInputStream::GetAutomaticGainControl() {
+  return false;
+}
+
+void FakeAudioInputStream::SetOutputDeviceForAec(
+    const std::string& output_device_id) {
+  // Not supported. Do nothing.
+}
+
+void FakeAudioInputStream::ReadAudioFromSource(base::TimeTicks ideal_time,
+                                               base::TimeTicks now) {
+  DCHECK(capture_thread_->task_runner()->BelongsToCurrentThread());
+
+  if (!audio_source_)
+    audio_source_ = ChooseSource();
+
+  // This OnMoreData()/OnData() timing would never happen in a real system:
+  //
+  //   1. Real AudioSources would never be asked to generate audio that should
+  //      already be playing-out exactly at this very moment; they are asked to
+  //      do so for audio to be played-out in the future.
+  //   2. Real AudioInputStreams could never provide audio that is striking a
+  //      microphone element exactly at this very moment; they provide audio
+  //      that happened in the recent past.
+  //
+  // However, it would be pointless to add a FIFO queue here to delay the signal
+  // in this "fake" implementation. So, just hack the timing and carry-on.
+  {
+    base::AutoLock lock(callback_lock_);
+    if (audio_bus_ && callback_) {
+      audio_source_->OnMoreData(base::TimeDelta(), ideal_time, 0,
+                                audio_bus_.get());
+      callback_->OnData(audio_bus_.get(), ideal_time, 1.0);
+    }
+  }
+}
+
+using AudioSourceCallback = AudioOutputStream::AudioSourceCallback;
+std::unique_ptr<AudioSourceCallback> FakeAudioInputStream::ChooseSource() {
+  DCHECK(capture_thread_->task_runner()->BelongsToCurrentThread());
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kUseFileForFakeAudioCapture)) {
+    base::CommandLine::StringType switch_value =
+        base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
+            switches::kUseFileForFakeAudioCapture);
+    base::CommandLine::StringVector parameters =
+        base::SplitString(switch_value, FILE_PATH_LITERAL("%"),
+                          base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    CHECK(parameters.size() > 0) << "You must pass <file>[%noloop] to  --"
+                                 << switches::kUseFileForFakeAudioCapture
+                                 << ".";
+    base::FilePath path_to_wav_file = base::FilePath(parameters[0]);
+    bool looping = true;
+    if (parameters.size() == 2) {
+      CHECK(parameters[1] == FILE_PATH_LITERAL("noloop"))
+          << "Unknown parameter " << parameters[1] << " to "
+          << switches::kUseFileForFakeAudioCapture << ".";
+      looping = false;
+    }
+    return std::make_unique<FileSource>(params_, path_to_wav_file, looping);
+  }
+  return std::make_unique<BeepingSource>(params_);
+}
+
+void FakeAudioInputStream::BeepOnce() {
+  BeepingSource::BeepOnce();
+}
+
+void FakeAudioInputStream::SetGlobalMutedState(bool is_muted) {
+  base::subtle::NoBarrier_Store(&g_fake_input_streams_are_muted,
+                                (is_muted ? 1 : 0));
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/fake_audio_input_stream.h b/third_party/chromium/media/audio/fake_audio_input_stream.h
new file mode 100644
index 0000000..809edfb
--- /dev/null
+++ b/third_party/chromium/media/audio/fake_audio_input_stream.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A fake implementation of AudioInputStream, useful for testing purpose.
+
+#ifndef MEDIA_AUDIO_FAKE_AUDIO_INPUT_STREAM_H_
+#define MEDIA_AUDIO_FAKE_AUDIO_INPUT_STREAM_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/threading/thread.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/fake_audio_worker.h"
+
+namespace media {
+
+class AudioBus;
+class AudioManagerBase;
+
+// This class acts as a fake audio input stream. The default is to generate a
+// beeping sound unless --use-file-for-fake-audio-capture=<file> is specified,
+// in which case the indicated .wav file will be read and played into the
+// stream.
+class MEDIA_EXPORT FakeAudioInputStream : public AudioInputStream {
+ public:
+  static AudioInputStream* MakeFakeStream(AudioManagerBase* manager,
+                                          const AudioParameters& params);
+
+  OpenOutcome Open() override;
+  void Start(AudioInputCallback* callback) override;
+  void Stop() override;
+  void Close() override;
+  double GetMaxVolume() override;
+  void SetVolume(double volume) override;
+  double GetVolume() override;
+  bool IsMuted() override;
+  bool SetAutomaticGainControl(bool enabled) override;
+  bool GetAutomaticGainControl() override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+  // Generate one beep sound. This method is called by FakeVideoCaptureDevice to
+  // test audio/video synchronization. This is a static method because
+  // FakeVideoCaptureDevice is disconnected from an audio device. This means
+  // only one instance of this class gets to respond, which is okay because we
+  // assume there's only one stream for this testing purpose. Furthermore this
+  // method will do nothing if --use-file-for-fake-audio-capture is specified
+  // since the input stream will be playing from a file instead of beeping.
+  // TODO(hclam): Make this non-static. To do this we'll need to fix
+  // crbug.com/159053 such that video capture device is aware of audio
+  // input stream.
+  static void BeepOnce();
+
+  // Set the muted state for _all_ FakeAudioInputStreams. The value is global,
+  // so it can be set before any FakeAudioInputStreams have been created.
+  static void SetGlobalMutedState(bool is_muted);
+
+ private:
+  FakeAudioInputStream(AudioManagerBase* manager,
+                       const AudioParameters& params);
+  ~FakeAudioInputStream() override;
+
+  std::unique_ptr<AudioOutputStream::AudioSourceCallback> ChooseSource();
+  void ReadAudioFromSource(base::TimeTicks ideal_time, base::TimeTicks now);
+
+  AudioManagerBase* audio_manager_;
+  // |callback_| needs the lock as ReadAudioFromSource reads callback_
+  // on the capture thread, while callback_ is set on the audio thread.
+  base::Lock callback_lock_;
+  AudioInputCallback* callback_ GUARDED_BY(callback_lock_);
+  AudioParameters params_;
+
+  std::unique_ptr<FakeAudioWorker> fake_audio_worker_;
+  std::unique_ptr<AudioOutputStream::AudioSourceCallback> audio_source_;
+  std::unique_ptr<media::AudioBus> audio_bus_;
+  // We will delete the capture thread on the AudioManager worker task runner
+  // since the audio thread is the main UI thread on Mac.
+  std::unique_ptr<base::Thread, base::OnTaskRunnerDeleter> capture_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeAudioInputStream);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_FAKE_AUDIO_INPUT_STREAM_H_
diff --git a/third_party/chromium/media/audio/fake_audio_log_factory.cc b/third_party/chromium/media/audio/fake_audio_log_factory.cc
new file mode 100644
index 0000000..caf08f5
--- /dev/null
+++ b/third_party/chromium/media/audio/fake_audio_log_factory.cc
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/fake_audio_log_factory.h"
+
+#include <string>
+
+namespace media {
+
+class FakeAudioLogImpl : public AudioLog {
+ public:
+  FakeAudioLogImpl() = default;
+  ~FakeAudioLogImpl() override = default;
+  void OnCreated(const media::AudioParameters& params,
+                 const std::string& device_id) override {}
+  void OnStarted() override {}
+  void OnStopped() override {}
+  void OnClosed() override {}
+  void OnError() override {}
+  void OnSetVolume(double volume) override {}
+  void OnProcessingStateChanged(const std::string& message) override {}
+  void OnLogMessage(const std::string& message) override {}
+};
+
+FakeAudioLogFactory::FakeAudioLogFactory() = default;
+FakeAudioLogFactory::~FakeAudioLogFactory() = default;
+
+std::unique_ptr<AudioLog> FakeAudioLogFactory::CreateAudioLog(
+    AudioComponent component,
+    int component_id) {
+  return std::make_unique<FakeAudioLogImpl>();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/fake_audio_log_factory.h b/third_party/chromium/media/audio/fake_audio_log_factory.h
new file mode 100644
index 0000000..9e2eff8
--- /dev/null
+++ b/third_party/chromium/media/audio/fake_audio_log_factory.h
@@ -0,0 +1,32 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_FAKE_AUDIO_LOG_FACTORY_H_
+#define MEDIA_AUDIO_FAKE_AUDIO_LOG_FACTORY_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "media/audio/audio_logging.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Creates stub AudioLog instances, for testing, which do nothing.
+class MEDIA_EXPORT FakeAudioLogFactory : public AudioLogFactory {
+ public:
+  FakeAudioLogFactory();
+
+  FakeAudioLogFactory(const FakeAudioLogFactory&) = delete;
+  FakeAudioLogFactory& operator=(const FakeAudioLogFactory&) = delete;
+
+  ~FakeAudioLogFactory() override;
+  std::unique_ptr<AudioLog> CreateAudioLog(AudioComponent component,
+                                           int component_id) override;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_FAKE_AUDIO_LOG_FACTORY_H_
diff --git a/third_party/chromium/media/audio/fake_audio_manager.cc b/third_party/chromium/media/audio/fake_audio_manager.cc
new file mode 100644
index 0000000..cc6ea93
--- /dev/null
+++ b/third_party/chromium/media/audio/fake_audio_manager.cc
@@ -0,0 +1,87 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/fake_audio_manager.h"
+
+#include <algorithm>
+#include <utility>
+
+namespace media {
+
+namespace {
+
+const int kDefaultInputBufferSize = 1024;
+const int kDefaultSampleRate = 48000;
+
+}  // namespace
+
+FakeAudioManager::FakeAudioManager(std::unique_ptr<AudioThread> audio_thread,
+                                   AudioLogFactory* audio_log_factory)
+    : AudioManagerBase(std::move(audio_thread), audio_log_factory) {}
+
+FakeAudioManager::~FakeAudioManager() = default;
+
+// Implementation of AudioManager.
+bool FakeAudioManager::HasAudioOutputDevices() { return false; }
+
+bool FakeAudioManager::HasAudioInputDevices() { return false; }
+
+const char* FakeAudioManager::GetName() {
+  return "Fake";
+}
+
+// Implementation of AudioManagerBase.
+AudioOutputStream* FakeAudioManager::MakeLinearOutputStream(
+    const AudioParameters& params,
+    const LogCallback& log_callback) {
+  return FakeAudioOutputStream::MakeFakeStream(this, params);
+}
+
+AudioOutputStream* FakeAudioManager::MakeLowLatencyOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  return FakeAudioOutputStream::MakeFakeStream(this, params);
+}
+
+AudioInputStream* FakeAudioManager::MakeLinearInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  return FakeAudioInputStream::MakeFakeStream(this, params);
+}
+
+AudioInputStream* FakeAudioManager::MakeLowLatencyInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  return FakeAudioInputStream::MakeFakeStream(this, params);
+}
+
+AudioParameters FakeAudioManager::GetPreferredOutputStreamParameters(
+    const std::string& output_device_id,
+    const AudioParameters& input_params) {
+  static const int kDefaultOutputBufferSize = 2048;
+  static const int kDefaultSampleRate = 48000;
+  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
+  int sample_rate = kDefaultSampleRate;
+  int buffer_size = kDefaultOutputBufferSize;
+  if (input_params.IsValid()) {
+    sample_rate = input_params.sample_rate();
+    channel_layout = input_params.channel_layout();
+    buffer_size = std::min(input_params.frames_per_buffer(), buffer_size);
+  }
+
+  return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
+                         sample_rate, buffer_size);
+}
+
+AudioParameters FakeAudioManager::GetInputStreamParameters(
+    const std::string& device_id) {
+  return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                         CHANNEL_LAYOUT_STEREO, kDefaultSampleRate,
+                         kDefaultInputBufferSize);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/fake_audio_manager.h b/third_party/chromium/media/audio/fake_audio_manager.h
new file mode 100644
index 0000000..1979afa
--- /dev/null
+++ b/third_party/chromium/media/audio/fake_audio_manager.h
@@ -0,0 +1,59 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_FAKE_AUDIO_MANAGER_H_
+#define MEDIA_AUDIO_FAKE_AUDIO_MANAGER_H_
+
+#include <string>
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/fake_audio_input_stream.h"
+#include "media/audio/fake_audio_output_stream.h"
+
+namespace media {
+
+class MEDIA_EXPORT FakeAudioManager : public AudioManagerBase {
+ public:
+  FakeAudioManager(std::unique_ptr<AudioThread> audio_thread,
+                   AudioLogFactory* audio_log_factory);
+
+  FakeAudioManager(const FakeAudioManager&) = delete;
+  FakeAudioManager& operator=(const FakeAudioManager&) = delete;
+
+  ~FakeAudioManager() override;
+
+  // Implementation of AudioManager.
+  bool HasAudioOutputDevices() override;
+  bool HasAudioInputDevices() override;
+  const char* GetName() override;
+
+  // Implementation of AudioManagerBase.
+  AudioOutputStream* MakeLinearOutputStream(
+      const AudioParameters& params,
+      const LogCallback& log_callback) override;
+  AudioOutputStream* MakeLowLatencyOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLinearInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLowLatencyInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+
+ protected:
+  AudioParameters GetPreferredOutputStreamParameters(
+      const std::string& output_device_id,
+      const AudioParameters& input_params) override;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_FAKE_AUDIO_MANAGER_H_
diff --git a/third_party/chromium/media/audio/fake_audio_output_stream.cc b/third_party/chromium/media/audio/fake_audio_output_stream.cc
new file mode 100644
index 0000000..22282b3
--- /dev/null
+++ b/third_party/chromium/media/audio/fake_audio_output_stream.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/fake_audio_output_stream.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/check.h"
+#include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
+#include "media/audio/audio_manager_base.h"
+
+namespace media {
+
+// static
+AudioOutputStream* FakeAudioOutputStream::MakeFakeStream(
+    AudioManagerBase* manager, const AudioParameters& params) {
+  return new FakeAudioOutputStream(manager, params);
+}
+
+FakeAudioOutputStream::FakeAudioOutputStream(AudioManagerBase* manager,
+                                             const AudioParameters& params)
+    : audio_manager_(manager),
+      fixed_data_delay_(FakeAudioWorker::ComputeFakeOutputDelay(params)),
+      callback_(nullptr),
+      fake_worker_(manager->GetWorkerTaskRunner(), params),
+      audio_bus_(AudioBus::Create(params)) {}
+
+FakeAudioOutputStream::~FakeAudioOutputStream() {
+  DCHECK(!callback_);
+}
+
+bool FakeAudioOutputStream::Open() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  audio_bus_->Zero();
+  return true;
+}
+
+void FakeAudioOutputStream::Start(AudioSourceCallback* callback)  {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  callback_ = callback;
+  fake_worker_.Start(base::BindRepeating(&FakeAudioOutputStream::CallOnMoreData,
+                                         base::Unretained(this)));
+}
+
+void FakeAudioOutputStream::Stop() {
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  fake_worker_.Stop();
+  callback_ = nullptr;
+}
+
+void FakeAudioOutputStream::Close() {
+  DCHECK(!callback_);
+  DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
+  audio_manager_->ReleaseOutputStream(this);
+}
+
+void FakeAudioOutputStream::Flush() {}
+
+void FakeAudioOutputStream::SetVolume(double volume) {}
+
+void FakeAudioOutputStream::GetVolume(double* volume) {
+  *volume = 0;
+}
+
+void FakeAudioOutputStream::CallOnMoreData(base::TimeTicks ideal_time,
+                                           base::TimeTicks now) {
+  DCHECK(audio_manager_->GetWorkerTaskRunner()->BelongsToCurrentThread());
+  // Real streams provide small tweaks to their delay values, alongside the
+  // current system time; and so the same is done here.
+  const auto delay =
+      fixed_data_delay_ + std::max(base::TimeDelta(), ideal_time - now);
+  callback_->OnMoreData(delay, now, 0, audio_bus_.get());
+}
+
+void FakeAudioOutputStream::SetMute(bool muted) {}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/fake_audio_output_stream.h b/third_party/chromium/media/audio/fake_audio_output_stream.h
new file mode 100644
index 0000000..3f97020
--- /dev/null
+++ b/third_party/chromium/media/audio/fake_audio_output_stream.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_FAKE_AUDIO_OUTPUT_STREAM_H_
+#define MEDIA_AUDIO_FAKE_AUDIO_OUTPUT_STREAM_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "media/audio/android/muteable_audio_output_stream.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/fake_audio_worker.h"
+
+namespace media {
+
+class AudioManagerBase;
+
+// A fake implementation of AudioOutputStream.  Used for testing and when a real
+// audio output device is unavailable or refusing output (e.g. remote desktop).
+// Callbacks are driven on the AudioManager's message loop.
+class MEDIA_EXPORT FakeAudioOutputStream : public MuteableAudioOutputStream {
+ public:
+  static AudioOutputStream* MakeFakeStream(AudioManagerBase* manager,
+                                           const AudioParameters& params);
+
+  // AudioOutputStream implementation.
+  bool Open() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+  void Close() override;
+  void Flush() override;
+  void SetMute(bool muted) override;
+
+ private:
+  FakeAudioOutputStream(AudioManagerBase* manager,
+                        const AudioParameters& params);
+  ~FakeAudioOutputStream() override;
+
+  // Task that periodically calls OnMoreData() to consume audio data.
+  void CallOnMoreData(base::TimeTicks ideal_time, base::TimeTicks now);
+
+  AudioManagerBase* const audio_manager_;
+  const base::TimeDelta fixed_data_delay_;
+  AudioSourceCallback* callback_;
+  FakeAudioWorker fake_worker_;
+  const std::unique_ptr<AudioBus> audio_bus_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeAudioOutputStream);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_FAKE_AUDIO_OUTPUT_STREAM_H_
diff --git a/third_party/chromium/media/audio/fuchsia/DIR_METADATA b/third_party/chromium/media/audio/fuchsia/DIR_METADATA
new file mode 100644
index 0000000..5b3985e
--- /dev/null
+++ b/third_party/chromium/media/audio/fuchsia/DIR_METADATA
@@ -0,0 +1,10 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+mixins: "//build/fuchsia/COMMON_METADATA"
+os: FUCHSIA
\ No newline at end of file
diff --git a/third_party/chromium/media/audio/fuchsia/OWNERS b/third_party/chromium/media/audio/fuchsia/OWNERS
new file mode 100644
index 0000000..e7034ea
--- /dev/null
+++ b/third_party/chromium/media/audio/fuchsia/OWNERS
@@ -0,0 +1 @@
+file://build/fuchsia/OWNERS
diff --git a/third_party/chromium/media/audio/fuchsia/audio_manager_fuchsia.cc b/third_party/chromium/media/audio/fuchsia/audio_manager_fuchsia.cc
new file mode 100644
index 0000000..4ff1fd9
--- /dev/null
+++ b/third_party/chromium/media/audio/fuchsia/audio_manager_fuchsia.cc
@@ -0,0 +1,143 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/fuchsia/audio_manager_fuchsia.h"
+
+#include <memory>
+
+#include "base/command_line.h"
+#include "base/fuchsia/scheduler.h"
+#include "media/audio/fuchsia/audio_output_stream_fuchsia.h"
+#include "media/base/audio_timestamp_helper.h"
+#include "media/base/media_switches.h"
+
+namespace media {
+
+AudioManagerFuchsia::AudioManagerFuchsia(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory)
+    : AudioManagerBase(std::move(audio_thread), audio_log_factory) {}
+
+AudioManagerFuchsia::~AudioManagerFuchsia() = default;
+
+bool AudioManagerFuchsia::HasAudioOutputDevices() {
+  // TODO(crbug.com/852834): Fuchsia currently doesn't provide an API for device
+  // enumeration. Update this method when that functionality is implemented.
+  return true;
+}
+
+bool AudioManagerFuchsia::HasAudioInputDevices() {
+  // TODO(crbug.com/852834): Fuchsia currently doesn't provide an API for device
+  // enumeration. Update this method when that functionality is implemented.
+  return true;
+}
+
+void AudioManagerFuchsia::GetAudioInputDeviceNames(
+    AudioDeviceNames* device_names) {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableAudioInput)) {
+    return;
+  }
+
+  // TODO(crbug.com/852834): Fuchsia currently doesn't provide an API for device
+  // enumeration. Update this method when that functionality is implemented.
+  *device_names = {AudioDeviceName::CreateDefault()};
+}
+
+void AudioManagerFuchsia::GetAudioOutputDeviceNames(
+    AudioDeviceNames* device_names) {
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableAudioOutput)) {
+    return;
+  }
+
+  // TODO(crbug.com/852834): Fuchsia currently doesn't provide an API for device
+  // enumeration. Update this method when that functionality is implemented.
+  *device_names = {AudioDeviceName::CreateDefault()};
+}
+
+AudioParameters AudioManagerFuchsia::GetInputStreamParameters(
+    const std::string& device_id) {
+  // TODO(crbug.com/852834): Fuchsia currently doesn't provide an API to get
+  // device configuration and supported effects. Update this method when that
+  // functionality is implemented.
+  //
+  // Use 16kHz sample rate with 10ms buffer, which is consistent with
+  // the default configuration used in the AudioCapturer implementation.
+  // Assume that the system-provided AudioConsumer supports echo cancellation,
+  // noise suppression and automatic gain control.
+  const size_t kSampleRate = 16000;
+  const size_t kPeriodSamples = AudioTimestampHelper::TimeToFrames(
+      base::kAudioSchedulingPeriod, kSampleRate);
+  AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                         CHANNEL_LAYOUT_MONO, kSampleRate, kPeriodSamples);
+  params.set_effects(AudioParameters::ECHO_CANCELLER |
+                     AudioParameters::NOISE_SUPPRESSION |
+                     AudioParameters::AUTOMATIC_GAIN_CONTROL);
+
+  return params;
+}
+
+AudioParameters AudioManagerFuchsia::GetPreferredOutputStreamParameters(
+    const std::string& output_device_id,
+    const AudioParameters& input_params) {
+  // TODO(crbug.com/852834): Fuchsia currently doesn't provide an API to get
+  // device configuration. Update this method when that functionality is
+  // implemented.
+  const size_t kSampleRate = 48000;
+  const size_t kPeriodFrames = AudioTimestampHelper::TimeToFrames(
+      base::kAudioSchedulingPeriod, kSampleRate);
+  return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                         CHANNEL_LAYOUT_STEREO, kSampleRate, kPeriodFrames);
+}
+
+const char* AudioManagerFuchsia::GetName() {
+  return "Fuchsia";
+}
+
+AudioOutputStream* AudioManagerFuchsia::MakeLinearOutputStream(
+    const AudioParameters& params,
+    const LogCallback& log_callback) {
+  NOTREACHED();
+  return nullptr;
+}
+
+AudioOutputStream* AudioManagerFuchsia::MakeLowLatencyOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+
+  if (!device_id.empty() &&
+      device_id != AudioDeviceDescription::kDefaultDeviceId) {
+    return nullptr;
+  }
+
+  return new AudioOutputStreamFuchsia(this, params);
+}
+
+AudioInputStream* AudioManagerFuchsia::MakeLinearInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  NOTREACHED();
+  return nullptr;
+}
+
+AudioInputStream* AudioManagerFuchsia::MakeLowLatencyInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  NOTREACHED();
+  return nullptr;
+}
+
+std::unique_ptr<AudioManager> CreateAudioManager(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory) {
+  return std::make_unique<AudioManagerFuchsia>(std::move(audio_thread),
+                                               audio_log_factory);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/fuchsia/audio_manager_fuchsia.h b/third_party/chromium/media/audio/fuchsia/audio_manager_fuchsia.h
new file mode 100644
index 0000000..105aca4
--- /dev/null
+++ b/third_party/chromium/media/audio/fuchsia/audio_manager_fuchsia.h
@@ -0,0 +1,56 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_FUCHSIA_AUDIO_MANAGER_FUCHSIA_H_
+#define MEDIA_AUDIO_FUCHSIA_AUDIO_MANAGER_FUCHSIA_H_
+
+#include "media/audio/audio_manager_base.h"
+
+namespace media {
+
+class AudioManagerFuchsia : public AudioManagerBase {
+ public:
+  AudioManagerFuchsia(std::unique_ptr<AudioThread> audio_thread,
+                      AudioLogFactory* audio_log_factory);
+
+  AudioManagerFuchsia(const AudioManagerFuchsia&) = delete;
+  AudioManagerFuchsia& operator=(const AudioManagerFuchsia&) = delete;
+
+  ~AudioManagerFuchsia() override;
+
+  // Implementation of AudioManager.
+  bool HasAudioOutputDevices() override;
+  bool HasAudioInputDevices() override;
+  void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override;
+  void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+  const char* GetName() override;
+
+  // Implementation of AudioManagerBase.
+  AudioOutputStream* MakeLinearOutputStream(
+      const AudioParameters& params,
+      const LogCallback& log_callback) override;
+  AudioOutputStream* MakeLowLatencyOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLinearInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLowLatencyInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+
+ protected:
+  AudioParameters GetPreferredOutputStreamParameters(
+      const std::string& output_device_id,
+      const AudioParameters& input_params) override;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_FUCHSIA_AUDIO_MANAGER_FUCHSIA_H_
diff --git a/third_party/chromium/media/audio/fuchsia/audio_output_stream_fuchsia.cc b/third_party/chromium/media/audio/fuchsia/audio_output_stream_fuchsia.cc
new file mode 100644
index 0000000..d872b08
--- /dev/null
+++ b/third_party/chromium/media/audio/fuchsia/audio_output_stream_fuchsia.cc
@@ -0,0 +1,301 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/fuchsia/audio_output_stream_fuchsia.h"
+
+#include <lib/sys/cpp/component_context.h>
+#include <zircon/syscalls.h>
+
+#include "base/bind.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/fuchsia/process_context.h"
+#include "base/logging.h"
+#include "base/memory/writable_shared_memory_region.h"
+#include "media/audio/fuchsia/audio_manager_fuchsia.h"
+#include "media/base/audio_sample_types.h"
+#include "media/base/audio_timestamp_helper.h"
+
+namespace media {
+
+namespace {
+
+// Current AudioRenderer implementation allows only one buffer with id=0.
+// TODO(crbug.com/1131179): Replace with an incrementing buffer id now that
+// AddPayloadBuffer() and RemovePayloadBuffer() are implemented properly in
+// AudioRenderer.
+const uint32_t kBufferId = 0;
+
+fuchsia::media::AudioRenderUsage GetStreamUsage(
+    const AudioParameters& parameters) {
+  if (parameters.latency_tag() == AudioLatency::LATENCY_RTC)
+    return fuchsia::media::AudioRenderUsage::COMMUNICATION;
+  return fuchsia::media::AudioRenderUsage::MEDIA;
+}
+
+}  // namespace
+
+AudioOutputStreamFuchsia::AudioOutputStreamFuchsia(
+    AudioManagerFuchsia* manager,
+    const AudioParameters& parameters)
+    : manager_(manager),
+      parameters_(parameters),
+      audio_bus_(AudioBus::Create(parameters)) {}
+
+AudioOutputStreamFuchsia::~AudioOutputStreamFuchsia() {
+  // Close() must be called first.
+  DCHECK(!audio_renderer_);
+}
+
+bool AudioOutputStreamFuchsia::Open() {
+  DCHECK(!audio_renderer_);
+
+  // Connect |audio_renderer_| to the audio service.
+  fuchsia::media::AudioPtr audio_server =
+      base::ComponentContextForProcess()
+          ->svc()
+          ->Connect<fuchsia::media::Audio>();
+  audio_server->CreateAudioRenderer(audio_renderer_.NewRequest());
+  audio_renderer_.set_error_handler(
+      fit::bind_member(this, &AudioOutputStreamFuchsia::OnRendererError));
+
+  audio_renderer_->SetUsage(GetStreamUsage(parameters_));
+
+  // Inform the |audio_renderer_| of the format required by the caller.
+  fuchsia::media::AudioStreamType format;
+  format.sample_format = fuchsia::media::AudioSampleFormat::FLOAT;
+  format.channels = parameters_.channels();
+  format.frames_per_second = parameters_.sample_rate();
+  audio_renderer_->SetPcmStreamType(std::move(format));
+
+  // Use number of samples to specify media position.
+  audio_renderer_->SetPtsUnits(parameters_.sample_rate(), 1);
+
+  // Setup OnMinLeadTimeChanged event listener. This event is used to get
+  // |min_lead_time_|, which indicates how far ahead audio samples need to be
+  // sent to the renderer.
+  audio_renderer_.events().OnMinLeadTimeChanged =
+      fit::bind_member(this, &AudioOutputStreamFuchsia::OnMinLeadTimeChanged);
+  audio_renderer_->EnableMinLeadTimeEvents(true);
+
+  // The renderer may fail initialization asynchronously, which is handled in
+  // OnRendererError().
+  return true;
+}
+
+void AudioOutputStreamFuchsia::Start(AudioSourceCallback* callback) {
+  DCHECK(!callback_);
+  DCHECK(reference_time_.is_null());
+  DCHECK(!timer_.IsRunning());
+  callback_ = callback;
+
+  // Delay PumpSamples() until OnMinLeadTimeChanged is received and Pause() is
+  // not pending.
+  if (!min_lead_time_.has_value() || pause_pending_)
+    return;
+
+  PumpSamples();
+}
+
+void AudioOutputStreamFuchsia::Stop() {
+  callback_ = nullptr;
+  timer_.Stop();
+
+  // Nothing to do if playback is not started or being stopped.
+  if (reference_time_.is_null() || pause_pending_)
+    return;
+
+  reference_time_ = base::TimeTicks();
+  pause_pending_ = true;
+  audio_renderer_->Pause(
+      fit::bind_member(this, &AudioOutputStreamFuchsia::OnPauseComplete));
+  audio_renderer_->DiscardAllPacketsNoReply();
+}
+
+// This stream is always used with sub second buffer sizes, where it's
+// sufficient to simply always flush upon Start().
+void AudioOutputStreamFuchsia::Flush() {}
+
+void AudioOutputStreamFuchsia::SetVolume(double volume) {
+  DCHECK(0.0 <= volume && volume <= 1.0) << volume;
+  volume_ = volume;
+}
+
+void AudioOutputStreamFuchsia::GetVolume(double* volume) {
+  *volume = volume_;
+}
+
+void AudioOutputStreamFuchsia::Close() {
+  Stop();
+  audio_renderer_.Unbind();
+
+  // Signal to the manager that we're closed and can be removed. This should be
+  // the last call in the function as it deletes |this|.
+  manager_->ReleaseOutputStream(this);
+}
+
+base::TimeTicks AudioOutputStreamFuchsia::GetCurrentStreamTime() {
+  DCHECK(!reference_time_.is_null());
+  return reference_time_ +
+         AudioTimestampHelper::FramesToTime(stream_position_samples_,
+                                            parameters_.sample_rate());
+}
+
+size_t AudioOutputStreamFuchsia::GetMinBufferSize() {
+  // Ensure that |payload_buffer_| fits enough packets to cover min_lead_time_
+  // plus one extra packet.
+  int min_packets = (AudioTimestampHelper::TimeToFrames(
+                         min_lead_time_.value(), parameters_.sample_rate()) +
+                     parameters_.frames_per_buffer() - 1) /
+                        parameters_.frames_per_buffer() +
+                    1;
+
+  return parameters_.GetBytesPerBuffer(kSampleFormatF32) * min_packets;
+}
+
+bool AudioOutputStreamFuchsia::InitializePayloadBuffer() {
+  size_t buffer_size = GetMinBufferSize();
+  auto region = base::WritableSharedMemoryRegion::Create(buffer_size);
+  payload_buffer_ = region.Map();
+  if (!payload_buffer_.IsValid()) {
+    LOG(WARNING) << "Failed to allocate VMO of size " << buffer_size;
+    return false;
+  }
+
+  payload_buffer_pos_ = 0;
+  audio_renderer_->AddPayloadBuffer(
+      kBufferId, base::WritableSharedMemoryRegion::TakeHandleForSerialization(
+                     std::move(region))
+                     .PassPlatformHandle());
+
+  return true;
+}
+
+void AudioOutputStreamFuchsia::OnMinLeadTimeChanged(int64_t min_lead_time) {
+  bool min_lead_time_was_unknown = !min_lead_time_.has_value();
+
+  min_lead_time_ = base::Nanoseconds(min_lead_time);
+
+  // When min_lead_time_ increases we may need to reallocate |payload_buffer_|.
+  // Code below just unmaps the current buffer. The new buffer will be allocated
+  // lated in PumpSamples(). This is necessary because VMO allocation may fail
+  // and it's not possible to report that error here - OnMinLeadTimeChanged()
+  // may be invoked before Start().
+  if (payload_buffer_.IsValid() &&
+      GetMinBufferSize() > payload_buffer_.size()) {
+    payload_buffer_ = {};
+
+    // Discard all packets currently in flight. This is required because
+    // AddPayloadBuffer() will fail if there are any packets in flight.
+    audio_renderer_->DiscardAllPacketsNoReply();
+  }
+
+  // If playback was started but we were waiting for MinLeadTime, then start
+  // pumping samples now.
+  if (is_started() && min_lead_time_was_unknown) {
+    DCHECK(!timer_.IsRunning());
+    PumpSamples();
+  }
+}
+
+void AudioOutputStreamFuchsia::OnRendererError(zx_status_t status) {
+  ZX_LOG(WARNING, status) << "AudioRenderer has failed";
+  ReportError();
+}
+
+void AudioOutputStreamFuchsia::ReportError() {
+  reference_time_ = base::TimeTicks();
+  timer_.Stop();
+  if (callback_)
+    callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+}
+
+void AudioOutputStreamFuchsia::OnPauseComplete(int64_t reference_time,
+                                               int64_t media_time) {
+  DCHECK(pause_pending_);
+  pause_pending_ = false;
+
+  // If the stream was restarted while Pause() was pending then we can start
+  // pumping samples again.
+  if (is_started())
+    PumpSamples();
+}
+
+void AudioOutputStreamFuchsia::PumpSamples() {
+  DCHECK(is_started());
+  DCHECK(audio_renderer_);
+
+  // Allocate payload buffer if necessary.
+  if (!payload_buffer_.IsValid() && !InitializePayloadBuffer()) {
+    ReportError();
+    return;
+  }
+
+  base::TimeTicks now = base::TimeTicks::Now();
+
+  base::TimeDelta delay;
+  if (reference_time_.is_null()) {
+    delay = min_lead_time_.value() + parameters_.GetBufferDuration() / 2;
+    stream_position_samples_ = 0;
+  } else {
+    auto stream_time = GetCurrentStreamTime();
+
+    // Adjust stream position if we missed timer deadline.
+    if (now + min_lead_time_.value() > stream_time) {
+      stream_position_samples_ += AudioTimestampHelper::TimeToFrames(
+          now + min_lead_time_.value() - stream_time,
+          parameters_.sample_rate());
+    }
+
+    delay = stream_time - now;
+  }
+
+  // Request more samples from |callback_|.
+  int frames_filled = callback_->OnMoreData(delay, now, 0, audio_bus_.get());
+  DCHECK_EQ(frames_filled, audio_bus_->frames());
+
+  audio_bus_->Scale(volume_);
+
+  // Save samples to the |payload_buffer_|.
+  size_t packet_size = parameters_.GetBytesPerBuffer(kSampleFormatF32);
+  DCHECK_LE(payload_buffer_pos_ + packet_size, payload_buffer_.size());
+
+  // We skip clipping since that occurs at the shared memory boundary.
+  audio_bus_->ToInterleaved<Float32SampleTypeTraitsNoClip>(
+      audio_bus_->frames(),
+      reinterpret_cast<float*>(static_cast<uint8_t*>(payload_buffer_.memory()) +
+                               payload_buffer_pos_));
+
+  // Send a new packet.
+  fuchsia::media::StreamPacket packet;
+  packet.pts = stream_position_samples_;
+  packet.payload_buffer_id = kBufferId;
+  packet.payload_offset = payload_buffer_pos_;
+  packet.payload_size = packet_size;
+  packet.flags = 0;
+  audio_renderer_->SendPacketNoReply(std::move(packet));
+
+  // Start playback if the stream was previously stopped.
+  if (reference_time_.is_null()) {
+    reference_time_ = now + delay;
+    audio_renderer_->PlayNoReply(reference_time_.ToZxTime(),
+                                 stream_position_samples_);
+  }
+
+  stream_position_samples_ += frames_filled;
+  payload_buffer_pos_ =
+      (payload_buffer_pos_ + packet_size) % payload_buffer_.size();
+
+  SchedulePumpSamples(now);
+}
+
+void AudioOutputStreamFuchsia::SchedulePumpSamples(base::TimeTicks now) {
+  base::TimeTicks next_pump_time = GetCurrentStreamTime() -
+                                   min_lead_time_.value() -
+                                   parameters_.GetBufferDuration() / 2;
+  timer_.Start(FROM_HERE, next_pump_time - now,
+               base::BindOnce(&AudioOutputStreamFuchsia::PumpSamples,
+                              base::Unretained(this)));
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/fuchsia/audio_output_stream_fuchsia.h b/third_party/chromium/media/audio/fuchsia/audio_output_stream_fuchsia.h
new file mode 100644
index 0000000..5f5bfa8
--- /dev/null
+++ b/third_party/chromium/media/audio/fuchsia/audio_output_stream_fuchsia.h
@@ -0,0 +1,105 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_FUCHSIA_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
+#define MEDIA_AUDIO_FUCHSIA_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
+
+#include <fuchsia/media/cpp/fidl.h>
+
+#include "base/memory/shared_memory_mapping.h"
+#include "base/timer/timer.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+class AudioManagerFuchsia;
+
+class AudioOutputStreamFuchsia : public AudioOutputStream {
+ public:
+  // Caller must ensure that manager outlives the stream.
+  AudioOutputStreamFuchsia(AudioManagerFuchsia* manager,
+                           const AudioParameters& parameters);
+
+  // AudioOutputStream interface.
+  bool Open() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void Flush() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+  void Close() override;
+
+ private:
+  ~AudioOutputStreamFuchsia() override;
+
+  bool is_started() { return callback_ != nullptr; }
+
+  // Returns minimum |payload_buffer_| size for the current |min_lead_time_|.
+  size_t GetMinBufferSize();
+
+  // Allocates and maps |payload_buffer_|.
+  bool InitializePayloadBuffer();
+
+  base::TimeTicks GetCurrentStreamTime();
+
+  // Event handler for |audio_out_|.
+  void OnMinLeadTimeChanged(int64_t min_lead_time);
+
+  // Error handler for |audio_out_|.
+  void OnRendererError(zx_status_t status);
+
+  // Resets internal state and reports an error to |callback_|.
+  void ReportError();
+
+  // Callback for AudioRenderer::Pause().
+  void OnPauseComplete(int64_t reference_time, int64_t media_time);
+
+  // Requests data from AudioSourceCallback, passes it to the mixer and
+  // schedules |timer_| for the next call.
+  void PumpSamples();
+
+  // Schedules |timer_| to call PumpSamples() when appropriate for the next
+  // packet.
+  void SchedulePumpSamples(base::TimeTicks now);
+
+  AudioManagerFuchsia* manager_;
+  AudioParameters parameters_;
+
+  fuchsia::media::AudioRendererPtr audio_renderer_;
+
+  // |audio_bus_| is used only in PumpSamples(). It is kept here to avoid
+  // reallocating the memory every time.
+  std::unique_ptr<AudioBus> audio_bus_;
+
+  base::WritableSharedMemoryMapping payload_buffer_;
+  size_t payload_buffer_pos_ = 0;
+
+  AudioSourceCallback* callback_ = nullptr;
+
+  double volume_ = 1.0;
+
+  // Set to true when Pause() call is pending. AudioRenderer handles Pause()
+  // asynchronously, so Play() should not be called again until Pause() is
+  // complete.
+  bool pause_pending_ = false;
+
+  base::TimeTicks reference_time_;
+
+  int64_t stream_position_samples_;
+
+  // Current min lead time for the stream. This value is not set until the first
+  // AudioRenderer::OnMinLeadTimeChanged event.
+  absl::optional<base::TimeDelta> min_lead_time_;
+
+  // Timer that's scheduled to call PumpSamples().
+  base::OneShotTimer timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioOutputStreamFuchsia);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_FUCHSIA_AUDIO_OUTPUT_STREAM_FUCHSIA_H_
diff --git a/third_party/chromium/media/audio/linux/audio_manager_linux.cc b/third_party/chromium/media/audio/linux/audio_manager_linux.cc
new file mode 100644
index 0000000..ed7a396
--- /dev/null
+++ b/third_party/chromium/media/audio/linux/audio_manager_linux.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "build/chromeos_buildflags.h"
+#include "media/audio/fake_audio_manager.h"
+#include "media/base/media_switches.h"
+
+#if defined(USE_ALSA)
+#include "media/audio/alsa/audio_manager_alsa.h"
+#endif
+
+#if defined(USE_CRAS) && BUILDFLAG(IS_CHROMEOS_ASH)
+#include "media/audio/cras/audio_manager_chromeos.h"
+#elif defined(USE_CRAS)
+#include "media/audio/cras/audio_manager_cras.h"
+#endif
+
+#if defined(USE_PULSEAUDIO)
+#include "media/audio/pulse/audio_manager_pulse.h"
+#include "media/audio/pulse/pulse_util.h"
+#endif
+
+namespace media {
+
+std::unique_ptr<media::AudioManager> CreateAudioManager(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory) {
+  // For testing allow audio output to be disabled.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableAudioOutput)) {
+    return std::make_unique<FakeAudioManager>(std::move(audio_thread),
+                                              audio_log_factory);
+  }
+
+#if defined(USE_CRAS)
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseCras)) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    return std::make_unique<AudioManagerChromeOS>(std::move(audio_thread),
+                                                  audio_log_factory);
+#else
+    return std::make_unique<AudioManagerCras>(std::move(audio_thread),
+                                                   audio_log_factory);
+#endif
+  }
+#endif // defined(USE_CRAS)
+
+#if defined(USE_PULSEAUDIO)
+  pa_threaded_mainloop* pa_mainloop = nullptr;
+  pa_context* pa_context = nullptr;
+  if (pulse::InitPulse(&pa_mainloop, &pa_context)) {
+    return std::make_unique<AudioManagerPulse>(
+        std::move(audio_thread), audio_log_factory, pa_mainloop, pa_context);
+  }
+  LOG(WARNING) << "Falling back to ALSA for audio output. PulseAudio is not "
+                  "available or could not be initialized.";
+#endif
+
+#if defined(USE_ALSA)
+  return std::make_unique<AudioManagerAlsa>(std::move(audio_thread),
+                                            audio_log_factory);
+#else
+  return std::make_unique<FakeAudioManager>(std::move(audio_thread),
+                                            audio_log_factory);
+#endif
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/audio_auhal_mac.cc b/third_party/chromium/media/audio/mac/audio_auhal_mac.cc
new file mode 100644
index 0000000..0c7c8d3
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_auhal_mac.cc
@@ -0,0 +1,525 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mac/audio_auhal_mac.h"
+
+#include <CoreServices/CoreServices.h>
+
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/mac/mac_logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event.h"
+#include "media/audio/mac/audio_manager_mac.h"
+#include "media/base/audio_pull_fifo.h"
+#include "media/base/audio_timestamp_helper.h"
+
+namespace media {
+
+// Mapping from Chrome's channel layout to CoreAudio layout. This must match the
+// layout of the Channels enum in |channel_layout.h|
+static const AudioChannelLabel kCoreAudioChannelMapping[] = {
+    kAudioChannelLabel_Left,
+    kAudioChannelLabel_Right,
+    kAudioChannelLabel_Center,
+    kAudioChannelLabel_LFEScreen,
+    kAudioChannelLabel_LeftSurround,
+    kAudioChannelLabel_RightSurround,
+    kAudioChannelLabel_LeftCenter,
+    kAudioChannelLabel_RightCenter,
+    kAudioChannelLabel_CenterSurround,
+    kAudioChannelLabel_LeftSurroundDirect,
+    kAudioChannelLabel_RightSurroundDirect,
+};
+static_assert(0 == LEFT && 1 == RIGHT && 2 == CENTER && 3 == LFE &&
+                  4 == BACK_LEFT &&
+                  5 == BACK_RIGHT &&
+                  6 == LEFT_OF_CENTER &&
+                  7 == RIGHT_OF_CENTER &&
+                  8 == BACK_CENTER &&
+                  9 == SIDE_LEFT &&
+                  10 == SIDE_RIGHT &&
+                  10 == CHANNELS_MAX,
+              "Channel positions must match CoreAudio channel order.");
+
+static void WrapBufferList(AudioBufferList* buffer_list,
+                           AudioBus* bus,
+                           int frames) {
+  const int channels = bus->channels();
+  const int buffer_list_channels = buffer_list->mNumberBuffers;
+  CHECK_EQ(channels, buffer_list_channels);
+
+  // Copy pointers from AudioBufferList.
+  for (int i = 0; i < channels; ++i)
+    bus->SetChannelData(i, static_cast<float*>(buffer_list->mBuffers[i].mData));
+
+  // Finally set the actual length.
+  bus->set_frames(frames);
+}
+
+// Sets the stream format on the AUHAL to PCM Float32 non-interleaved for the
+// given number of channels on the given scope and element. The created stream
+// description will be stored in |desc|.
+static bool SetStreamFormat(int channels,
+                            int sample_rate,
+                            AudioUnit audio_unit,
+                            AudioStreamBasicDescription* format) {
+  format->mSampleRate = sample_rate;
+  format->mFormatID = kAudioFormatLinearPCM;
+  format->mFormatFlags =
+      kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
+  format->mBytesPerPacket = sizeof(Float32);
+  format->mFramesPerPacket = 1;
+  format->mBytesPerFrame = sizeof(Float32);
+  format->mChannelsPerFrame = channels;
+  format->mBitsPerChannel = 32;
+  format->mReserved = 0;
+
+  // Set stream formats. See Apple's tech note for details on the peculiar way
+  // that inputs and outputs are handled in the AUHAL concerning scope and bus
+  // (element) numbers:
+  // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
+  return AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat,
+                              kAudioUnitScope_Input, AUElement::OUTPUT, format,
+                              sizeof(*format)) == noErr;
+}
+
+// Converts |channel_layout| into CoreAudio format and sets up the AUHAL with
+// our layout information so it knows how to remap the channels.
+static void SetAudioChannelLayout(int channels,
+                                  ChannelLayout channel_layout,
+                                  AudioUnit audio_unit) {
+  DCHECK(audio_unit);
+  DCHECK_GT(channels, 0);
+  DCHECK_GT(channel_layout, CHANNEL_LAYOUT_UNSUPPORTED);
+
+  // AudioChannelLayout is structure ending in a variable length array, so we
+  // can't directly allocate one. Instead compute the size and and allocate one
+  // inside of a byte array.
+  //
+  // Code modeled after example from Apple documentation here:
+  // https://developer.apple.com/library/content/qa/qa1627/_index.html
+  const size_t layout_size =
+      offsetof(AudioChannelLayout, mChannelDescriptions[channels]);
+  std::unique_ptr<uint8_t[]> layout_storage(new uint8_t[layout_size]);
+  memset(layout_storage.get(), 0, layout_size);
+  AudioChannelLayout* coreaudio_layout =
+      reinterpret_cast<AudioChannelLayout*>(layout_storage.get());
+
+  coreaudio_layout->mNumberChannelDescriptions = channels;
+  coreaudio_layout->mChannelLayoutTag =
+      kAudioChannelLayoutTag_UseChannelDescriptions;
+  AudioChannelDescription* descriptions =
+      coreaudio_layout->mChannelDescriptions;
+
+  if (channel_layout == CHANNEL_LAYOUT_DISCRETE) {
+    // For the discrete case just assume common input mappings; once we run out
+    // of known channels mark them as unknown.
+    for (int ch = 0; ch < channels; ++ch) {
+      descriptions[ch].mChannelLabel = ch > CHANNELS_MAX
+                                           ? kAudioChannelLabel_Unknown
+                                           : kCoreAudioChannelMapping[ch];
+      descriptions[ch].mChannelFlags = kAudioChannelFlags_AllOff;
+    }
+  } else if (channel_layout == CHANNEL_LAYOUT_MONO) {
+    // CoreAudio has a special label for mono.
+    DCHECK_EQ(channels, 1);
+    descriptions[0].mChannelLabel = kAudioChannelLabel_Mono;
+    descriptions[0].mChannelFlags = kAudioChannelFlags_AllOff;
+  } else {
+    for (int ch = 0; ch <= CHANNELS_MAX; ++ch) {
+      const int order = ChannelOrder(channel_layout, static_cast<Channels>(ch));
+      if (order == -1)
+        continue;
+      descriptions[order].mChannelLabel = kCoreAudioChannelMapping[ch];
+      descriptions[order].mChannelFlags = kAudioChannelFlags_AllOff;
+    }
+  }
+
+  OSStatus result = AudioUnitSetProperty(
+      audio_unit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input,
+      AUElement::OUTPUT, coreaudio_layout, layout_size);
+  if (result != noErr) {
+    OSSTATUS_DLOG(ERROR, result)
+        << "Failed to set audio channel layout. Using default layout.";
+  }
+}
+
+AUHALStream::AUHALStream(AudioManagerMac* manager,
+                         const AudioParameters& params,
+                         AudioDeviceID device,
+                         const AudioManager::LogCallback& log_callback)
+    : manager_(manager),
+      params_(params),
+      number_of_frames_(params_.frames_per_buffer()),
+      number_of_frames_requested_(0),
+      source_(NULL),
+      device_(device),
+      volume_(1),
+      stopped_(true),
+      current_lost_frames_(0),
+      last_sample_time_(0.0),
+      last_number_of_frames_(0),
+      total_lost_frames_(0),
+      largest_glitch_frames_(0),
+      glitches_detected_(0),
+      log_callback_(log_callback) {
+  // We must have a manager.
+  DCHECK(manager_);
+  DCHECK(params_.IsValid());
+  DCHECK_NE(device, kAudioObjectUnknown);
+}
+
+AUHALStream::~AUHALStream() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  CHECK(!audio_unit_);
+
+  base::AutoLock al(lock_);
+  ReportAndResetStats();
+}
+
+bool AUHALStream::Open() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(!output_bus_);
+  DCHECK(!audio_unit_);
+
+  // The output bus will wrap the AudioBufferList given to us in
+  // the Render() callback.
+  output_bus_ = AudioBus::CreateWrapper(params_.channels());
+
+  bool configured = ConfigureAUHAL();
+  if (configured) {
+    DCHECK(audio_unit_);
+    DCHECK(audio_unit_->is_valid());
+    hardware_latency_ = AudioManagerMac::GetHardwareLatency(
+        audio_unit_->audio_unit(), device_, kAudioDevicePropertyScopeOutput,
+        params_.sample_rate());
+  }
+
+  return configured;
+}
+
+void AUHALStream::Close() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (audio_unit_) {
+    Stop();
+
+    // Clear the render callback to try and prevent any callbacks from coming
+    // in after we've called stop. https://crbug.com/737527.
+    AURenderCallbackStruct callback = {0};
+    auto result = AudioUnitSetProperty(
+        audio_unit_->audio_unit(), kAudioUnitProperty_SetRenderCallback,
+        kAudioUnitScope_Input, AUElement::OUTPUT, &callback, sizeof(callback));
+    OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+        << "Failed to clear input callback.";
+  }
+
+  audio_unit_.reset();
+  // Inform the audio manager that we have been closed. This will cause our
+  // destruction. Also include the device ID as a signal to the audio manager
+  // that it should try to increase the native I/O buffer size after the stream
+  // has been closed.
+  manager_->ReleaseOutputStreamUsingRealDevice(this, device_);
+}
+
+void AUHALStream::Start(AudioSourceCallback* callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(callback);
+  if (!audio_unit_) {
+    DLOG(ERROR) << "Open() has not been called successfully";
+    return;
+  }
+
+  if (!stopped_) {
+    base::AutoLock al(lock_);
+    CHECK_EQ(source_, callback);
+    return;
+  }
+
+  // Check if we should defer Start() for http://crbug.com/160920.
+  if (manager_->ShouldDeferStreamStart()) {
+    // Use a cancellable closure so that if Stop() is called before Start()
+    // actually runs, we can cancel the pending start.
+    deferred_start_cb_.Reset(
+        base::BindOnce(&AUHALStream::Start, base::Unretained(this), callback));
+    manager_->GetTaskRunner()->PostDelayedTask(
+        FROM_HERE, deferred_start_cb_.callback(),
+        base::Seconds(AudioManagerMac::kStartDelayInSecsForPowerEvents));
+    return;
+  }
+
+  stopped_ = false;
+
+  {
+    base::AutoLock al(lock_);
+    audio_fifo_.reset();
+    source_ = callback;
+  }
+
+  OSStatus result = AudioOutputUnitStart(audio_unit_->audio_unit());
+  if (result == noErr)
+    return;
+
+  Stop();
+  OSSTATUS_DLOG(ERROR, result) << "AudioOutputUnitStart() failed.";
+  callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+}
+
+// This stream is always used with sub second buffer sizes, where it's
+// sufficient to simply always flush upon Start().
+void AUHALStream::Flush() {}
+
+void AUHALStream::Stop() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  deferred_start_cb_.Cancel();
+  if (stopped_)
+    return;
+
+  OSStatus result = AudioOutputUnitStop(audio_unit_->audio_unit());
+  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+      << "AudioOutputUnitStop() failed.";
+
+  {
+    base::AutoLock al(lock_);
+    if (result != noErr)
+      source_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+
+    ReportAndResetStats();
+    source_ = nullptr;
+  }
+
+  stopped_ = true;
+}
+
+void AUHALStream::SetVolume(double volume) {
+  volume_ = static_cast<float>(volume);
+}
+
+void AUHALStream::GetVolume(double* volume) {
+  *volume = volume_;
+}
+
+// Pulls on our provider to get rendered audio stream.
+// Note to future hackers of this function: Do not add locks which can
+// be contended in the middle of stream processing here (starting and stopping
+// the stream are ok) because this is running on a real-time thread.
+OSStatus AUHALStream::Render(AudioUnitRenderActionFlags* flags,
+                             const AudioTimeStamp* output_time_stamp,
+                             UInt32 bus_number,
+                             UInt32 number_of_frames,
+                             AudioBufferList* data) {
+  TRACE_EVENT2("audio", "AUHALStream::Render", "input buffer size",
+               number_of_frames_, "output buffer size", number_of_frames);
+
+  base::AutoLock al(lock_);
+
+  // There's no documentation on what we should return here, but if we're here
+  // something is wrong so just return an AudioUnit error that looks reasonable.
+  if (!source_)
+    return kAudioUnitErr_Uninitialized;
+
+  UpdatePlayoutTimestamp(output_time_stamp);
+
+  // If the stream parameters change for any reason, we need to insert a FIFO
+  // since the OnMoreData() pipeline can't handle frame size changes.
+  if (number_of_frames != number_of_frames_) {
+    // Create a FIFO on the fly to handle any discrepancies in callback rates.
+    if (!audio_fifo_) {
+      // TODO(grunell): We'll only care about the first buffer size change,
+      // any further changes will be ignored. It would be nice to have all
+      // changes reflected in UMA stats.
+      number_of_frames_requested_ = number_of_frames;
+      DVLOG(1) << "Audio frame size changed from " << number_of_frames_
+               << " to " << number_of_frames << " adding FIFO to compensate.";
+      audio_fifo_ = std::make_unique<AudioPullFifo>(
+          params_.channels(), number_of_frames_,
+          base::BindRepeating(&AUHALStream::ProvideInput,
+                              base::Unretained(this)));
+    }
+  }
+
+  // Make |output_bus_| wrap the output AudioBufferList.
+  WrapBufferList(data, output_bus_.get(), number_of_frames);
+
+  current_playout_time_ = GetPlayoutTime(output_time_stamp);
+
+  if (audio_fifo_)
+    audio_fifo_->Consume(output_bus_.get(), output_bus_->frames());
+  else
+    ProvideInput(0, output_bus_.get());
+
+  last_number_of_frames_ = number_of_frames;
+
+  return noErr;
+}
+
+void AUHALStream::ProvideInput(int frame_delay, AudioBus* dest) {
+  lock_.AssertAcquired();
+  DCHECK(source_);
+
+  const base::TimeTicks playout_time =
+      current_playout_time_ +
+      AudioTimestampHelper::FramesToTime(frame_delay, params_.sample_rate());
+  const base::TimeTicks now = base::TimeTicks::Now();
+  const base::TimeDelta delay = playout_time - now;
+
+  // Supply the input data and render the output data.
+  source_->OnMoreData(delay, now, current_lost_frames_, dest);
+  dest->Scale(volume_);
+  current_lost_frames_ = 0;
+}
+
+// AUHAL callback.
+OSStatus AUHALStream::InputProc(void* user_data,
+                                AudioUnitRenderActionFlags* flags,
+                                const AudioTimeStamp* output_time_stamp,
+                                UInt32 bus_number,
+                                UInt32 number_of_frames,
+                                AudioBufferList* io_data) {
+  // Dispatch to our class method.
+  AUHALStream* audio_output = static_cast<AUHALStream*>(user_data);
+  if (!audio_output)
+    return -1;
+
+  return audio_output->Render(flags, output_time_stamp, bus_number,
+                              number_of_frames, io_data);
+}
+
+base::TimeTicks AUHALStream::GetPlayoutTime(
+    const AudioTimeStamp* output_time_stamp) {
+  // A platform bug has been observed where the platform sometimes reports that
+  // the next frames will be output at an invalid time or a time in the past.
+  // Because the target playout time cannot be invalid or in the past, return
+  // "now" in these cases.
+  if ((output_time_stamp->mFlags & kAudioTimeStampHostTimeValid) == 0)
+    return base::TimeTicks::Now();
+
+  return std::max(base::TimeTicks::FromMachAbsoluteTime(
+                      output_time_stamp->mHostTime),
+                  base::TimeTicks::Now()) +
+         hardware_latency_;
+}
+
+void AUHALStream::UpdatePlayoutTimestamp(const AudioTimeStamp* timestamp) {
+  lock_.AssertAcquired();
+
+  if ((timestamp->mFlags & kAudioTimeStampSampleTimeValid) == 0)
+    return;
+
+  if (last_sample_time_) {
+    DCHECK_NE(0U, last_number_of_frames_);
+    UInt32 diff =
+        static_cast<UInt32>(timestamp->mSampleTime - last_sample_time_);
+    if (diff != last_number_of_frames_) {
+      DCHECK_GT(diff, last_number_of_frames_);
+      // We're being asked to render samples post what we expected. Update the
+      // glitch count etc and keep a record of the largest glitch.
+      auto lost_frames = diff - last_number_of_frames_;
+      total_lost_frames_ += lost_frames;
+      current_lost_frames_ += lost_frames;
+      if (lost_frames > largest_glitch_frames_)
+        largest_glitch_frames_ = lost_frames;
+      ++glitches_detected_;
+    }
+  }
+
+  // Store the last sample time for use next time we get called back.
+  last_sample_time_ = timestamp->mSampleTime;
+}
+
+void AUHALStream::ReportAndResetStats() {
+  lock_.AssertAcquired();
+
+  if (!last_sample_time_)
+    return;  // No stats gathered to report.
+
+  // A value of 0 indicates that we got the buffer size we asked for.
+  UMA_HISTOGRAM_COUNTS_1M("Media.Audio.Render.FramesRequested",
+                          number_of_frames_requested_);
+  // Even if there aren't any glitches, we want to record it to get a feel for
+  // how often we get no glitches vs the alternative.
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Media.Audio.Render.Glitches", glitches_detected_,
+                              1, 999999, 100);
+
+  auto lost_frames_ms = (total_lost_frames_ * 1000) / params_.sample_rate();
+
+  std::string log_message = base::StringPrintf(
+      "AU out: Total glitches=%d. Total frames lost=%d (%d ms).",
+      glitches_detected_, total_lost_frames_, lost_frames_ms);
+
+  if (!log_callback_.is_null())
+    log_callback_.Run(log_message);
+
+  if (glitches_detected_ != 0) {
+    UMA_HISTOGRAM_COUNTS_1M("Media.Audio.Render.LostFramesInMs",
+                            lost_frames_ms);
+    auto largest_glitch_ms =
+        (largest_glitch_frames_ * 1000) / params_.sample_rate();
+    UMA_HISTOGRAM_COUNTS_1M("Media.Audio.Render.LargestGlitchMs",
+                            largest_glitch_ms);
+    DLOG(WARNING) << log_message;
+  }
+
+  number_of_frames_requested_ = 0;
+  glitches_detected_ = 0;
+  last_sample_time_ = 0;
+  last_number_of_frames_ = 0;
+  total_lost_frames_ = 0;
+  largest_glitch_frames_ = 0;
+}
+
+bool AUHALStream::ConfigureAUHAL() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  std::unique_ptr<ScopedAudioUnit> local_audio_unit(
+      new ScopedAudioUnit(device_, AUElement::OUTPUT));
+  if (!local_audio_unit->is_valid())
+    return false;
+
+  if (!SetStreamFormat(params_.channels(), params_.sample_rate(),
+                       local_audio_unit->audio_unit(), &output_format_)) {
+    return false;
+  }
+
+  bool size_was_changed = false;
+  size_t io_buffer_frame_size = 0;
+  if (!manager_->MaybeChangeBufferSize(device_, local_audio_unit->audio_unit(),
+                                       0, number_of_frames_, &size_was_changed,
+                                       &io_buffer_frame_size)) {
+    return false;
+  }
+
+  // Setup callback.
+  AURenderCallbackStruct callback;
+  callback.inputProc = InputProc;
+  callback.inputProcRefCon = this;
+  OSStatus result = AudioUnitSetProperty(
+      local_audio_unit->audio_unit(), kAudioUnitProperty_SetRenderCallback,
+      kAudioUnitScope_Input, AUElement::OUTPUT, &callback, sizeof(callback));
+  if (result != noErr)
+    return false;
+
+  SetAudioChannelLayout(params_.channels(), params_.channel_layout(),
+                        local_audio_unit->audio_unit());
+
+  result = AudioUnitInitialize(local_audio_unit->audio_unit());
+  if (result != noErr) {
+    OSSTATUS_DLOG(ERROR, result) << "AudioUnitInitialize() failed.";
+    return false;
+  }
+
+  audio_unit_ = std::move(local_audio_unit);
+  return true;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/audio_auhal_mac.h b/third_party/chromium/media/audio/mac/audio_auhal_mac.h
new file mode 100644
index 0000000..376ca46
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_auhal_mac.h
@@ -0,0 +1,221 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation notes:
+//
+// - It is recommended to first acquire the native sample rate of the default
+//   output device and then use the same rate when creating this object.
+//   Use AudioManagerMac::HardwareSampleRate() to retrieve the sample rate.
+// - Calling Close() also leads to self destruction.
+// - The latency consists of two parts:
+//   1) Hardware latency, which includes Audio Unit latency, audio device
+//      latency;
+//   2) The delay between the moment getting the callback and the scheduled time
+//      stamp that tells when the data is going to be played out.
+//
+#ifndef MEDIA_AUDIO_MAC_AUDIO_AUHAL_MAC_H_
+#define MEDIA_AUDIO_MAC_AUDIO_AUHAL_MAC_H_
+
+#include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/CoreAudio.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <atomic>
+#include <memory>
+
+#include "base/cancelable_callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/mac/scoped_audio_unit.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioManagerMac;
+class AudioPullFifo;
+
+// Implementation of AudioOuputStream for Mac OS X using the
+// AUHAL Audio Unit present in OS 10.4 and later.
+// It is useful for low-latency output.
+//
+// Overview of operation:
+// 1) An object of AUHALStream is created by the AudioManager factory on the
+//    object's main thread via audio_man->MakeAudioStream().  Calls to the
+//    control routines (Open/Close/Start/Stop), must be made on this thread.
+// 2) Next Open() will be called. At that point the underlying AUHAL Audio Unit
+//    is created and configured to use the |device|.
+// 3) Then Start(source) is called and the device is started which creates its
+//    own thread (or uses an existing background thread) on which the AUHAL's
+//    callback will periodically ask for more data as buffers are being
+//    consumed.
+//    Note that all AUHAL instances receive callbacks on that very same
+//    thread, so avoid any contention in the callback as to not cause delays for
+//    other instances.
+// 4) At some point Stop() will be called, which we handle by stopping the
+//    output Audio Unit.
+// 6) Lastly, Close() will be called where we cleanup and notify the audio
+//    manager, which will delete the object.
+
+// TODO(tommi): Since the callback audio thread is shared for all instances of
+// AUHALStream, one stream blocking, can cause others to be delayed.  Several
+// occurrances of this can cause a buildup of delay which forces the OS
+// to skip rendering frames. One known cause of this is the synchronzation
+// between the browser and render process in AudioSyncReader.
+// We need to fix this.
+
+class AUHALStream : public AudioOutputStream {
+ public:
+  // |manager| creates this object.
+  // |device| is the CoreAudio device to use for the stream.
+  // It will often be the default output device.
+  AUHALStream(AudioManagerMac* manager,
+              const AudioParameters& params,
+              AudioDeviceID device,
+              const AudioManager::LogCallback& log_callback);
+
+  AUHALStream(const AUHALStream&) = delete;
+  AUHALStream& operator=(const AUHALStream&) = delete;
+
+  // The dtor is typically called by the AudioManager only and it is usually
+  // triggered by calling AudioOutputStream::Close().
+  ~AUHALStream() override;
+
+  // Implementation of AudioOutputStream.
+  bool Open() override;
+  void Close() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void Flush() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+
+  AudioDeviceID device_id() const { return device_; }
+  size_t requested_buffer_size() const { return number_of_frames_; }
+  AudioUnit audio_unit() const {
+    return audio_unit_ ? audio_unit_->audio_unit() : nullptr;
+  }
+
+ private:
+  // AUHAL callback.
+  static OSStatus InputProc(void* user_data,
+                            AudioUnitRenderActionFlags* flags,
+                            const AudioTimeStamp* time_stamp,
+                            UInt32 bus_number,
+                            UInt32 number_of_frames,
+                            AudioBufferList* io_data);
+
+  OSStatus Render(AudioUnitRenderActionFlags* flags,
+                  const AudioTimeStamp* output_time_stamp,
+                  UInt32 bus_number,
+                  UInt32 number_of_frames,
+                  AudioBufferList* io_data);
+
+  // Called by either |audio_fifo_| or Render() to provide audio data.
+  void ProvideInput(int frame_delay, AudioBus* dest);
+
+  // Creates the AUHAL, sets its stream format, buffer-size, etc.
+  bool ConfigureAUHAL();
+
+  // Creates the input and output busses.
+  void CreateIOBusses();
+
+  // Returns the playout time for a given AudioTimeStamp.
+  base::TimeTicks GetPlayoutTime(const AudioTimeStamp* output_time_stamp);
+
+  // Updates playout timestamp, current lost frames, and total lost frames and
+  // glitches.
+  void UpdatePlayoutTimestamp(const AudioTimeStamp* timestamp);
+
+  // Called from the dtor and when the stream is reset.
+  void ReportAndResetStats();
+
+  // Our creator, the audio manager needs to be notified when we close.
+  AudioManagerMac* const manager_;
+
+  const AudioParameters params_;
+
+  // We may get some callbacks after AudioUnitStop() has been called.
+  base::Lock lock_;
+
+  // Size of audio buffer requested at construction. The actual buffer size
+  // is given by |actual_io_buffer_frame_size_| and it can differ from the
+  // requested size.
+  const size_t number_of_frames_;
+
+  // Stores the number of frames that we actually get callbacks for.
+  // This may be different from what we ask for, so we use this for stats in
+  // order to understand how often this happens and what are the typical values.
+  size_t number_of_frames_requested_ GUARDED_BY(lock_);
+
+  // Pointer to the object that will provide the audio samples.
+  AudioSourceCallback* source_ GUARDED_BY(lock_);
+
+  // Holds the stream format details such as bitrate.
+  AudioStreamBasicDescription output_format_;
+
+  // The audio device to use with the AUHAL.
+  // We can potentially handle both input and output with this device.
+  const AudioDeviceID device_;
+
+  // The AUHAL Audio Unit which talks to |device_|.
+  std::unique_ptr<ScopedAudioUnit> audio_unit_;
+
+  // Volume level from 0 to 1.
+  std::atomic<float> volume_;
+
+  // Fixed playout hardware latency.
+  base::TimeDelta hardware_latency_;
+
+  // This flag will be set to false while we're actively receiving callbacks.
+  bool stopped_;
+
+  // Container for retrieving data from AudioSourceCallback::OnMoreData().
+  std::unique_ptr<AudioBus> output_bus_;
+
+  // Dynamically allocated FIFO used when CoreAudio asks for unexpected frame
+  // sizes.
+  std::unique_ptr<AudioPullFifo> audio_fifo_ GUARDED_BY(lock_);
+
+  // Current playout time.  Set by Render().
+  base::TimeTicks current_playout_time_;
+
+  // Lost frames not yet reported to the provider. Increased in
+  // UpdatePlayoutTimestamp() if any lost frame since last time. Forwarded to
+  // the provider and reset in ProvideInput().
+  uint32_t current_lost_frames_;
+
+  // Stores the timestamp of the previous audio buffer requested by the OS.
+  // We use this in combination with |last_number_of_frames_| to detect when
+  // the OS has decided to skip rendering frames (i.e. a glitch).
+  // This can happen in case of high CPU load or excessive blocking on the
+  // callback audio thread.
+  // These variables are only touched on the callback thread and then read
+  // in the dtor (when no longer receiving callbacks).
+  // NOTE: Float64 and UInt32 types are used for native API compatibility.
+  Float64 last_sample_time_ GUARDED_BY(lock_);
+  UInt32 last_number_of_frames_ GUARDED_BY(lock_);
+  UInt32 total_lost_frames_ GUARDED_BY(lock_);
+  UInt32 largest_glitch_frames_ GUARDED_BY(lock_);
+  int glitches_detected_ GUARDED_BY(lock_);
+
+  // Used to defer Start() to workaround http://crbug.com/160920.
+  base::CancelableOnceClosure deferred_start_cb_;
+
+  // Callback to send statistics info.
+  AudioManager::LogCallback log_callback_;
+
+  // Used to make sure control functions (Start(), Stop() etc) are called on the
+  // right thread.
+  base::ThreadChecker thread_checker_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_MAC_AUDIO_AUHAL_MAC_H_
diff --git a/third_party/chromium/media/audio/mac/audio_auhal_mac_unittest.cc b/third_party/chromium/media/audio/mac/audio_auhal_mac_unittest.cc
new file mode 100644
index 0000000..8ba6e39
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_auhal_mac_unittest.cc
@@ -0,0 +1,123 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_pump_type.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_message_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "media/audio/audio_device_info_accessor_for_tests.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/audio_unittest_util.h"
+#include "media/audio/mock_audio_source_callback.h"
+#include "media/audio/test_audio_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+
+// TODO(crogers): Most of these tests can be made platform agnostic.
+// http://crbug.com/223242
+
+namespace media {
+
+ACTION(ZeroBuffer) {
+  arg3->Zero();
+}
+
+ACTION_P3(MaybeSignalEvent, counter, signal_at_count, event) {
+  if (++(*counter) == signal_at_count)
+    event->Signal();
+}
+
+class AUHALStreamTest : public testing::Test {
+ public:
+  AUHALStreamTest()
+      : message_loop_(base::MessagePumpType::UI),
+        manager_(AudioManager::CreateForTesting(
+            std::make_unique<TestAudioThread>())),
+        manager_device_info_(manager_.get()) {
+    // Wait for the AudioManager to finish any initialization on the audio loop.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  AUHALStreamTest(const AUHALStreamTest&) = delete;
+  AUHALStreamTest& operator=(const AUHALStreamTest&) = delete;
+
+  ~AUHALStreamTest() override { manager_->Shutdown(); }
+
+  AudioOutputStream* Create() {
+    return manager_->MakeAudioOutputStream(
+        manager_device_info_.GetDefaultOutputStreamParameters(), "",
+        base::BindRepeating(&AUHALStreamTest::OnLogMessage,
+                            base::Unretained(this)));
+  }
+
+  bool OutputDevicesAvailable() {
+    return manager_device_info_.HasAudioOutputDevices();
+  }
+
+  void OnLogMessage(const std::string& message) { log_message_ = message; }
+
+ protected:
+  base::TestMessageLoop message_loop_;
+  std::unique_ptr<AudioManager> manager_;
+  AudioDeviceInfoAccessorForTests manager_device_info_;
+  MockAudioSourceCallback source_;
+  std::string log_message_;
+};
+
+TEST_F(AUHALStreamTest, HardwareSampleRate) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+  const AudioParameters preferred_params =
+      manager_device_info_.GetDefaultOutputStreamParameters();
+  EXPECT_GE(preferred_params.sample_rate(), 16000);
+  EXPECT_LE(preferred_params.sample_rate(), 192000);
+}
+
+TEST_F(AUHALStreamTest, CreateClose) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+  Create()->Close();
+}
+
+TEST_F(AUHALStreamTest, CreateOpenClose) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+  AudioOutputStream* stream = Create();
+  EXPECT_TRUE(stream->Open());
+  stream->Close();
+}
+
+TEST_F(AUHALStreamTest, CreateOpenStartStopClose) {
+  ABORT_AUDIO_TEST_IF_NOT(OutputDevicesAvailable());
+
+  AudioOutputStream* stream = Create();
+  EXPECT_TRUE(stream->Open());
+
+  // Wait for the first two data callback from the OS.
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  int callback_counter = 0;
+  const int number_of_callbacks = 2;
+  EXPECT_CALL(source_, OnMoreData(_, _, _, _))
+      .Times(number_of_callbacks)
+      .WillRepeatedly(DoAll(
+          ZeroBuffer(),
+          MaybeSignalEvent(&callback_counter, number_of_callbacks, &event),
+          Return(0)));
+  EXPECT_CALL(source_, OnError(_)).Times(0);
+  stream->Start(&source_);
+  event.Wait();
+
+  stream->Stop();
+  stream->Close();
+
+  EXPECT_FALSE(log_message_.empty());
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/audio_device_listener_mac.cc b/third_party/chromium/media/audio/mac/audio_device_listener_mac.cc
new file mode 100644
index 0000000..3476082
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_device_listener_mac.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mac/audio_device_listener_mac.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/mac/mac_logging.h"
+#include "base/single_thread_task_runner.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/mac/core_audio_util_mac.h"
+#include "media/base/bind_to_current_loop.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+const AudioObjectPropertyAddress
+    AudioDeviceListenerMac::kDefaultOutputDeviceChangePropertyAddress = {
+        kAudioHardwarePropertyDefaultOutputDevice,
+        kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
+
+const AudioObjectPropertyAddress
+    AudioDeviceListenerMac::kDefaultInputDeviceChangePropertyAddress = {
+        kAudioHardwarePropertyDefaultInputDevice,
+        kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
+
+const AudioObjectPropertyAddress
+    AudioDeviceListenerMac::kDevicesPropertyAddress = {
+        kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
+        kAudioObjectPropertyElementMaster};
+
+const AudioObjectPropertyAddress kPropertyOutputSourceChanged = {
+    kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput,
+    kAudioObjectPropertyElementMaster};
+
+const AudioObjectPropertyAddress kPropertyInputSourceChanged = {
+    kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeInput,
+    kAudioObjectPropertyElementMaster};
+
+class AudioDeviceListenerMac::PropertyListener {
+ public:
+  PropertyListener(AudioObjectID monitored_object,
+                   const AudioObjectPropertyAddress* property,
+                   base::RepeatingClosure callback)
+      : monitored_object_(monitored_object),
+        address_(property),
+        callback_(std::move(callback)) {}
+
+  AudioObjectID monitored_object() const { return monitored_object_; }
+  const base::RepeatingClosure& callback() const { return callback_; }
+  const AudioObjectPropertyAddress* property() const { return address_; }
+
+ private:
+  AudioObjectID monitored_object_;
+  const AudioObjectPropertyAddress* address_;
+  base::RepeatingClosure callback_;
+};
+
+// Callback from the system when an event occurs; this must be called on the
+// MessageLoop that created the AudioManager.
+// static
+OSStatus AudioDeviceListenerMac::OnEvent(
+    AudioObjectID object,
+    UInt32 num_addresses,
+    const AudioObjectPropertyAddress addresses[],
+    void* context) {
+  PropertyListener* listener = static_cast<PropertyListener*>(context);
+  if (object != listener->monitored_object())
+    return noErr;
+
+  for (UInt32 i = 0; i < num_addresses; ++i) {
+    if (addresses[i].mSelector == listener->property()->mSelector &&
+        addresses[i].mScope == listener->property()->mScope &&
+        addresses[i].mElement == listener->property()->mElement && context) {
+      listener->callback().Run();
+      break;
+    }
+  }
+
+  return noErr;
+}
+
+AudioDeviceListenerMac::AudioDeviceListenerMac(
+    const base::RepeatingClosure listener_cb,
+    bool monitor_default_input,
+    bool monitor_addition_removal,
+    bool monitor_sources)
+    : weak_factory_(this) {
+  listener_cb_ = std::move(listener_cb);
+
+  // Changes to the default output device are always monitored.
+  default_output_listener_ = std::make_unique<PropertyListener>(
+      kAudioObjectSystemObject, &kDefaultOutputDeviceChangePropertyAddress,
+      listener_cb_);
+  if (!AddPropertyListener(default_output_listener_.get()))
+    default_output_listener_.reset();
+
+  if (monitor_default_input) {
+    default_input_listener_ = std::make_unique<PropertyListener>(
+        kAudioObjectSystemObject, &kDefaultInputDeviceChangePropertyAddress,
+        listener_cb_);
+    if (!AddPropertyListener(default_input_listener_.get()))
+      default_input_listener_.reset();
+  }
+  if (monitor_addition_removal) {
+    addition_removal_listener_ = std::make_unique<PropertyListener>(
+        kAudioObjectSystemObject, &kDevicesPropertyAddress,
+        monitor_sources ? media::BindToCurrentLoop(base::BindRepeating(
+                              &AudioDeviceListenerMac::OnDevicesAddedOrRemoved,
+                              weak_factory_.GetWeakPtr()))
+                        : listener_cb_);
+    if (!AddPropertyListener(addition_removal_listener_.get()))
+      addition_removal_listener_.reset();
+
+    // Sources can be monitored only if addition/removal is monitored.
+    if (monitor_sources)
+      UpdateSourceListeners();
+  }
+}
+
+AudioDeviceListenerMac::~AudioDeviceListenerMac() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // Since we're running on the same CFRunLoop, there can be no outstanding
+  // callbacks in flight.
+  if (default_output_listener_)
+    RemovePropertyListener(default_output_listener_.get());
+  if (default_input_listener_)
+    RemovePropertyListener(default_input_listener_.get());
+  if (addition_removal_listener_)
+    RemovePropertyListener(addition_removal_listener_.get());
+  for (const auto& entry : source_listeners_)
+    RemovePropertyListener(entry.second.get());
+}
+
+bool AudioDeviceListenerMac::AddPropertyListener(
+    AudioDeviceListenerMac::PropertyListener* property_listener) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  OSStatus result = AudioObjectAddPropertyListener(
+      property_listener->monitored_object(), property_listener->property(),
+      &AudioDeviceListenerMac::OnEvent, property_listener);
+  bool success = result == noErr;
+  if (!success)
+    OSSTATUS_DLOG(ERROR, result) << "AudioObjectAddPropertyListener() failed!";
+
+  return success;
+}
+
+void AudioDeviceListenerMac::RemovePropertyListener(
+    AudioDeviceListenerMac::PropertyListener* property_listener) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  OSStatus result = AudioObjectRemovePropertyListener(
+      property_listener->monitored_object(), property_listener->property(),
+      &AudioDeviceListenerMac::OnEvent, property_listener);
+  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+      << "AudioObjectRemovePropertyListener() failed!";
+}
+
+void AudioDeviceListenerMac::OnDevicesAddedOrRemoved() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  UpdateSourceListeners();
+  listener_cb_.Run();
+}
+
+void AudioDeviceListenerMac::UpdateSourceListeners() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  std::vector<AudioObjectID> device_ids =
+      core_audio_mac::GetAllAudioDeviceIDs();
+  for (bool is_input : {true, false}) {
+    for (auto device_id : device_ids) {
+      const AudioObjectPropertyAddress* property_address =
+          is_input ? &kPropertyInputSourceChanged
+                   : &kPropertyOutputSourceChanged;
+      SourceListenerKey key = {device_id, is_input};
+      auto it_key = source_listeners_.find(key);
+      bool is_monitored = it_key != source_listeners_.end();
+      if (core_audio_mac::GetDeviceSource(device_id, is_input)) {
+        if (!is_monitored) {
+          // Start monitoring if the device has source and is not currently
+          // being monitored.
+          std::unique_ptr<PropertyListener> source_listener =
+              std::make_unique<PropertyListener>(device_id, property_address,
+                                                 listener_cb_);
+          if (AddPropertyListener(source_listener.get())) {
+            source_listeners_[key] = std::move(source_listener);
+          } else {
+            source_listener.reset();
+          }
+        }
+      } else if (is_monitored) {
+        // Stop monitoring if the device has no source but is currently being
+        // monitored.
+        RemovePropertyListener(it_key->second.get());
+        source_listeners_.erase(it_key);
+      }
+    }
+  }
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/audio_device_listener_mac.h b/third_party/chromium/media/audio/mac/audio_device_listener_mac.h
new file mode 100644
index 0000000..0c6dced
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_device_listener_mac.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_MAC_AUDIO_DEVICE_LISTENER_MAC_H_
+#define MEDIA_AUDIO_MAC_AUDIO_DEVICE_LISTENER_MAC_H_
+
+#include <CoreAudio/AudioHardware.h>
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// AudioDeviceListenerMac facilitates execution of device listener callbacks
+// issued via CoreAudio.
+class MEDIA_EXPORT AudioDeviceListenerMac {
+ public:
+  // |listener_cb| will be called when a device change occurs; it's a permanent
+  // callback and must outlive AudioDeviceListenerMac.  Note that |listener_cb|
+  // might not be executed on the same thread as construction.
+  AudioDeviceListenerMac(base::RepeatingClosure listener_cb,
+                         bool monitor_default_input = false,
+                         bool monitor_addition_removal = false,
+                         bool monitor_sources = false);
+
+  AudioDeviceListenerMac(const AudioDeviceListenerMac&) = delete;
+  AudioDeviceListenerMac& operator=(const AudioDeviceListenerMac&) = delete;
+
+  ~AudioDeviceListenerMac();
+
+ private:
+  friend class AudioDeviceListenerMacTest;
+  class PropertyListener;
+  static const AudioObjectPropertyAddress
+      kDefaultOutputDeviceChangePropertyAddress;
+  static const AudioObjectPropertyAddress
+      kDefaultInputDeviceChangePropertyAddress;
+  static const AudioObjectPropertyAddress kDevicesPropertyAddress;
+
+  static OSStatus OnEvent(AudioObjectID object,
+                          UInt32 num_addresses,
+                          const AudioObjectPropertyAddress addresses[],
+                          void* context);
+
+  bool AddPropertyListener(PropertyListener* property_listener);
+  void RemovePropertyListener(PropertyListener* property_listener);
+  void OnDevicesAddedOrRemoved();
+  void UpdateSourceListeners();
+
+  base::RepeatingClosure listener_cb_;
+  std::unique_ptr<PropertyListener> default_output_listener_;
+  std::unique_ptr<PropertyListener> default_input_listener_;
+  std::unique_ptr<PropertyListener> addition_removal_listener_;
+
+  using SourceListenerKey = std::pair<AudioObjectID, bool>;
+  using SourceListenerMap =
+      base::flat_map<SourceListenerKey, std::unique_ptr<PropertyListener>>;
+  SourceListenerMap source_listeners_;
+
+  // AudioDeviceListenerMac must be constructed and destructed on the same
+  // thread.
+  THREAD_CHECKER(thread_checker_);
+
+  base::WeakPtrFactory<AudioDeviceListenerMac> weak_factory_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_MAC_AUDIO_DEVICE_LISTENER_MAC_H_
diff --git a/third_party/chromium/media/audio/mac/audio_device_listener_mac_unittest.cc b/third_party/chromium/media/audio/mac/audio_device_listener_mac_unittest.cc
new file mode 100644
index 0000000..0a4599b
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_device_listener_mac_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mac/audio_device_listener_mac.h"
+
+#include <CoreAudio/AudioHardware.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/task_environment.h"
+#include "media/base/bind_to_current_loop.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class AudioDeviceListenerMacTest : public testing::Test {
+ public:
+  AudioDeviceListenerMacTest() {
+    // It's important to create the device listener from the message loop in
+    // order to ensure we don't end up with unbalanced TaskObserver calls.
+    task_environment_.GetMainThreadTaskRunner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&AudioDeviceListenerMacTest::CreateDeviceListener,
+                       base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  AudioDeviceListenerMacTest(const AudioDeviceListenerMacTest&) = delete;
+  AudioDeviceListenerMacTest& operator=(const AudioDeviceListenerMacTest&) =
+      delete;
+
+  virtual ~AudioDeviceListenerMacTest() {
+    // It's important to destroy the device listener from the message loop in
+    // order to ensure we don't end up with unbalanced TaskObserver calls.
+    task_environment_.GetMainThreadTaskRunner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&AudioDeviceListenerMacTest::DestroyDeviceListener,
+                       base::Unretained(this)));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void CreateDeviceListener() {
+    // Force a post task using BindToCurrentLoop() to ensure device listener
+    // internals are working correctly.
+    device_listener_ = std::make_unique<AudioDeviceListenerMac>(
+        BindToCurrentLoop(
+            base::BindRepeating(&AudioDeviceListenerMacTest::OnDeviceChange,
+                                base::Unretained(this))),
+        true /* monitor_default_input */, true /* monitor_addition_removal */);
+  }
+
+  void DestroyDeviceListener() { device_listener_.reset(); }
+
+  bool ListenerIsValid() { return !device_listener_->listener_cb_.is_null(); }
+
+  bool SimulateEvent(const AudioObjectPropertyAddress& address) {
+    // Include multiple addresses to ensure only a single device change event
+    // occurs.
+    const AudioObjectPropertyAddress addresses[] = {
+        address,
+        {kAudioHardwarePropertySleepingIsAllowed,
+         kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}};
+
+    OSStatus status = device_listener_->OnEvent(
+        kAudioObjectSystemObject, 2, addresses,
+        device_listener_->default_output_listener_.get());
+    if (status != noErr)
+      return false;
+
+    device_listener_->OnEvent(kAudioObjectSystemObject, 2, addresses,
+                              device_listener_->default_input_listener_.get());
+    if (status != noErr)
+      return false;
+
+    device_listener_->OnEvent(
+        kAudioObjectSystemObject, 2, addresses,
+        device_listener_->addition_removal_listener_.get());
+    return status == noErr;
+  }
+
+  bool SimulateDefaultOutputDeviceChange() {
+    return SimulateEvent(
+        AudioDeviceListenerMac::kDefaultOutputDeviceChangePropertyAddress);
+  }
+
+  bool SimulateDefaultInputDeviceChange() {
+    return SimulateEvent(
+        AudioDeviceListenerMac::kDefaultInputDeviceChangePropertyAddress);
+  }
+
+  bool SimulateDeviceAdditionRemoval() {
+    return SimulateEvent(AudioDeviceListenerMac::kDevicesPropertyAddress);
+  }
+
+  MOCK_METHOD0(OnDeviceChange, void());
+
+ protected:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  std::unique_ptr<AudioDeviceListenerMac> device_listener_;
+};
+
+// Simulate a device change event and ensure we get the right callback.
+TEST_F(AudioDeviceListenerMacTest, Events) {
+  ASSERT_TRUE(ListenerIsValid());
+  EXPECT_CALL(*this, OnDeviceChange()).Times(1);
+  ASSERT_TRUE(SimulateDefaultOutputDeviceChange());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_CALL(*this, OnDeviceChange()).Times(1);
+  ASSERT_TRUE(SimulateDefaultInputDeviceChange());
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_CALL(*this, OnDeviceChange()).Times(1);
+  ASSERT_TRUE(SimulateDeviceAdditionRemoval());
+  base::RunLoop().RunUntilIdle();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/audio_input_mac.cc b/third_party/chromium/media/audio/mac/audio_input_mac.cc
new file mode 100644
index 0000000..275d284
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_input_mac.cc
@@ -0,0 +1,328 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mac/audio_input_mac.h"
+
+#include <CoreServices/CoreServices.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/mac/mac_logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/trace_event/trace_event.h"
+#include "media/audio/mac/audio_manager_mac.h"
+#include "media/base/audio_bus.h"
+
+namespace media {
+
+namespace {
+// A one-shot timer is created and started in Start() and it triggers
+// CheckInputStartupSuccess() after this amount of time. UMA stats marked
+// Media.Audio.InputStartupSuccessMacHighLatency is then updated where true is
+// added if input callbacks have started, and false otherwise. This constant
+// should ideally be set to about the same value as in
+// audio_low_latency_input_mac.cc, to make comparing them reasonable.
+const int kInputCallbackStartTimeoutInSeconds = 8;
+}  // namespace
+
+PCMQueueInAudioInputStream::PCMQueueInAudioInputStream(
+    AudioManagerMac* manager,
+    const AudioParameters& params)
+    : manager_(manager),
+      callback_(NULL),
+      audio_queue_(NULL),
+      buffer_size_bytes_(0),
+      started_(false),
+      input_callback_is_active_(false),
+      audio_bus_(media::AudioBus::Create(params)) {
+  // We must have a manager.
+  DCHECK(manager_);
+
+  const SampleFormat kSampleFormat = kSampleFormatS16;
+
+  // A frame is one sample across all channels. In interleaved audio the per
+  // frame fields identify the set of n |channels|. In uncompressed audio, a
+  // packet is always one frame.
+  format_.mSampleRate = params.sample_rate();
+  format_.mFormatID = kAudioFormatLinearPCM;
+  format_.mFormatFlags =
+      kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
+  format_.mBitsPerChannel = SampleFormatToBitsPerChannel(kSampleFormat);
+  format_.mChannelsPerFrame = params.channels();
+  format_.mFramesPerPacket = 1;
+  format_.mBytesPerPacket = format_.mBytesPerFrame =
+      params.GetBytesPerFrame(kSampleFormat);
+  format_.mReserved = 0;
+
+  buffer_size_bytes_ = params.GetBytesPerBuffer(kSampleFormat);
+}
+
+PCMQueueInAudioInputStream::~PCMQueueInAudioInputStream() {
+  DCHECK(!callback_);
+  DCHECK(!audio_queue_);
+}
+
+AudioInputStream::OpenOutcome PCMQueueInAudioInputStream::Open() {
+  OSStatus err = AudioQueueNewInput(&format_,
+                                    &HandleInputBufferStatic,
+                                    this,
+                                    NULL,  // Use OS CFRunLoop for |callback|
+                                    kCFRunLoopCommonModes,
+                                    0,  // Reserved
+                                    &audio_queue_);
+  if (err != noErr) {
+    HandleError(err);
+    return AudioInputStream::OpenOutcome::kFailed;
+  }
+  return SetupBuffers() ? AudioInputStream::OpenOutcome::kSuccess
+                        : AudioInputStream::OpenOutcome::kFailed;
+}
+
+void PCMQueueInAudioInputStream::Start(AudioInputCallback* callback) {
+  DCHECK(callback);
+  DLOG_IF(ERROR, !audio_queue_) << "Open() has not been called successfully";
+  if (callback_ || !audio_queue_)
+    return;
+
+  // Check if we should defer Start() for http://crbug.com/160920.
+  if (manager_->ShouldDeferStreamStart()) {
+    // Use a cancellable closure so that if Stop() is called before Start()
+    // actually runs, we can cancel the pending start.
+    deferred_start_cb_.Reset(base::BindOnce(&PCMQueueInAudioInputStream::Start,
+                                            base::Unretained(this), callback));
+    manager_->GetTaskRunner()->PostDelayedTask(
+        FROM_HERE, deferred_start_cb_.callback(),
+        base::Seconds(AudioManagerMac::kStartDelayInSecsForPowerEvents));
+    return;
+  }
+
+  callback_ = callback;
+  OSStatus err = AudioQueueStart(audio_queue_, NULL);
+  if (err != noErr) {
+    HandleError(err);
+  } else {
+    started_ = true;
+  }
+
+  // For UMA stat purposes, start a one-shot timer which detects when input
+  // callbacks starts indicating if input audio recording starts as intended.
+  // CheckInputStartupSuccess() will check if |input_callback_is_active_| is
+  // true when the timer expires.
+  input_callback_timer_ = std::make_unique<base::OneShotTimer>();
+  input_callback_timer_->Start(
+      FROM_HERE, base::Seconds(kInputCallbackStartTimeoutInSeconds), this,
+      &PCMQueueInAudioInputStream::CheckInputStartupSuccess);
+  DCHECK(input_callback_timer_->IsRunning());
+}
+
+void PCMQueueInAudioInputStream::Stop() {
+  deferred_start_cb_.Cancel();
+  if (input_callback_timer_ != nullptr) {
+    input_callback_timer_->Stop();
+    input_callback_timer_.reset();
+  }
+  if (!audio_queue_ || !started_)
+    return;
+
+  // We request a synchronous stop, so the next call can take some time. In
+  // the windows implementation we block here as well.
+  OSStatus err = AudioQueueStop(audio_queue_, true);
+  if (err != noErr)
+    HandleError(err);
+
+  SetInputCallbackIsActive(false);
+  started_ = false;
+  callback_ = NULL;
+}
+
+void PCMQueueInAudioInputStream::Close() {
+  Stop();
+
+  // It is valid to call Close() before calling Open() or Start(), thus
+  // |audio_queue_| and |callback_| might be NULL.
+  if (audio_queue_) {
+    OSStatus err = AudioQueueDispose(audio_queue_, true);
+    audio_queue_ = NULL;
+    if (err != noErr)
+      HandleError(err);
+  }
+
+  manager_->ReleaseInputStream(this);
+  // CARE: This object may now be destroyed.
+}
+
+double PCMQueueInAudioInputStream::GetMaxVolume() {
+  NOTREACHED() << "Only supported for low-latency mode.";
+  return 0.0;
+}
+
+void PCMQueueInAudioInputStream::SetVolume(double volume) {
+  NOTREACHED() << "Only supported for low-latency mode.";
+}
+
+double PCMQueueInAudioInputStream::GetVolume() {
+  NOTREACHED() << "Only supported for low-latency mode.";
+  return 0.0;
+}
+
+bool PCMQueueInAudioInputStream::IsMuted() {
+  NOTREACHED() << "Only supported for low-latency mode.";
+  return false;
+}
+
+bool PCMQueueInAudioInputStream::SetAutomaticGainControl(bool enabled) {
+  NOTREACHED() << "Only supported for low-latency mode.";
+  return false;
+}
+
+bool PCMQueueInAudioInputStream::GetAutomaticGainControl() {
+  NOTREACHED() << "Only supported for low-latency mode.";
+  return false;
+}
+
+void PCMQueueInAudioInputStream::SetOutputDeviceForAec(
+    const std::string& output_device_id) {
+  // Not supported. Do nothing.
+}
+
+void PCMQueueInAudioInputStream::HandleError(OSStatus err) {
+  if (callback_)
+    callback_->OnError();
+  // This point should never be reached.
+  OSSTATUS_DCHECK(0, err);
+}
+
+bool PCMQueueInAudioInputStream::SetupBuffers() {
+  DCHECK(buffer_size_bytes_);
+  for (int i = 0; i < kNumberBuffers; ++i) {
+    AudioQueueBufferRef buffer;
+    OSStatus err = AudioQueueAllocateBuffer(audio_queue_,
+                                            buffer_size_bytes_,
+                                            &buffer);
+    if (err == noErr)
+      err = QueueNextBuffer(buffer);
+    if (err != noErr) {
+      HandleError(err);
+      return false;
+    }
+    // |buffer| will automatically be freed when |audio_queue_| is released.
+  }
+  return true;
+}
+
+OSStatus PCMQueueInAudioInputStream::QueueNextBuffer(
+    AudioQueueBufferRef audio_buffer) {
+  // Only the first 2 params are needed for recording.
+  return AudioQueueEnqueueBuffer(audio_queue_, audio_buffer, 0, NULL);
+}
+
+// static
+void PCMQueueInAudioInputStream::HandleInputBufferStatic(
+    void* data,
+    AudioQueueRef audio_queue,
+    AudioQueueBufferRef audio_buffer,
+    const AudioTimeStamp* start_time,
+    UInt32 num_packets,
+    const AudioStreamPacketDescription* desc) {
+  reinterpret_cast<PCMQueueInAudioInputStream*>(data)->
+      HandleInputBuffer(audio_queue, audio_buffer, start_time,
+                        num_packets, desc);
+}
+
+void PCMQueueInAudioInputStream::HandleInputBuffer(
+    AudioQueueRef audio_queue,
+    AudioQueueBufferRef audio_buffer,
+    const AudioTimeStamp* start_time,
+    UInt32 num_packets,
+    const AudioStreamPacketDescription* packet_desc) {
+  DCHECK_EQ(audio_queue_, audio_queue);
+  DCHECK(audio_buffer->mAudioData);
+  TRACE_EVENT0("audio", "PCMQueueInAudioInputStream::HandleInputBuffer");
+  if (!callback_) {
+    // This can happen if Stop() was called without start.
+    DCHECK_EQ(0U, audio_buffer->mAudioDataByteSize);
+    return;
+  }
+
+  // Indicate that input callbacks have started.
+  SetInputCallbackIsActive(true);
+
+  if (audio_buffer->mAudioDataByteSize) {
+    // The AudioQueue API may use a large internal buffer and repeatedly call us
+    // back to back once that internal buffer is filled.  When this happens the
+    // renderer client does not have enough time to read data back from the
+    // shared memory before the next write comes along.  If HandleInputBuffer()
+    // is called too frequently, Sleep() at least 5ms to ensure the shared
+    // memory doesn't get trampled.
+    // TODO(dalecurtis): This is a HACK.  Long term the AudioQueue path is going
+    // away in favor of the AudioUnit based AUAudioInputStream().  Tracked by
+    // http://crbug.com/161383.
+    // TODO(dalecurtis): Delete all this. It shouldn't be necessary now that we
+    // have a ring buffer and FIFO on the actual shared memory.
+    base::TimeDelta elapsed = base::TimeTicks::Now() - last_fill_;
+    const base::TimeDelta kMinDelay = base::Milliseconds(5);
+    if (elapsed < kMinDelay) {
+      TRACE_EVENT0("audio",
+                   "PCMQueueInAudioInputStream::HandleInputBuffer sleep");
+      base::PlatformThread::Sleep(kMinDelay - elapsed);
+    }
+
+    // TODO(dalecurtis): This should be updated to include the device latency,
+    // but really since Pepper (which ignores the delay value) is on the only
+    // one creating AUDIO_PCM_LINEAR input devices, it doesn't matter.
+    // https://lists.apple.com/archives/coreaudio-api/2017/Jul/msg00035.html
+    const base::TimeTicks capture_time =
+        start_time->mFlags & kAudioTimeStampHostTimeValid
+            ? base::TimeTicks::FromMachAbsoluteTime(start_time->mHostTime)
+            : base::TimeTicks::Now();
+
+    uint8_t* audio_data = reinterpret_cast<uint8_t*>(audio_buffer->mAudioData);
+    DCHECK_EQ(format_.mBitsPerChannel, 16u);
+    audio_bus_->FromInterleaved<SignedInt16SampleTypeTraits>(
+        reinterpret_cast<int16_t*>(audio_data), audio_bus_->frames());
+    callback_->OnData(audio_bus_.get(), capture_time, 0.0);
+
+    last_fill_ = base::TimeTicks::Now();
+  }
+  // Recycle the buffer.
+  OSStatus err = QueueNextBuffer(audio_buffer);
+  if (err != noErr) {
+    if (err == kAudioQueueErr_EnqueueDuringReset) {
+      // This is the error you get if you try to enqueue a buffer and the
+      // queue has been closed. Not really a problem if indeed the queue
+      // has been closed.
+      // TODO(joth): PCMQueueOutAudioOutputStream uses callback_ to provide an
+      // extra guard for this situation, but it seems to introduce more
+      // complications than it solves (memory barrier issues accessing it from
+      // multiple threads, looses the means to indicate OnClosed to client).
+      // Should determine if we need to do something equivalent here.
+      return;
+    }
+    HandleError(err);
+  }
+}
+
+void PCMQueueInAudioInputStream::SetInputCallbackIsActive(bool enabled) {
+  base::subtle::Release_Store(&input_callback_is_active_, enabled);
+}
+
+bool PCMQueueInAudioInputStream::GetInputCallbackIsActive() {
+  return (base::subtle::Acquire_Load(&input_callback_is_active_) != false);
+}
+
+void PCMQueueInAudioInputStream::CheckInputStartupSuccess() {
+  // Check if we have called Start() and input callbacks have actually
+  // started in time as they should. If that is not the case, we have a
+  // problem and the stream is considered dead.
+  const bool input_callback_is_active = GetInputCallbackIsActive();
+  UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartupSuccessMac_HighLatency",
+                        input_callback_is_active);
+  DVLOG(1) << "high_latency_input_callback_is_active: "
+           << input_callback_is_active;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/audio_input_mac.h b/third_party/chromium/media/audio/mac/audio_input_mac.h
new file mode 100644
index 0000000..7cab433
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_input_mac.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_MAC_AUDIO_INPUT_MAC_H_
+#define MEDIA_AUDIO_MAC_AUDIO_INPUT_MAC_H_
+
+#include <AudioToolbox/AudioFormat.h>
+#include <AudioToolbox/AudioQueue.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/atomicops.h"
+#include "base/cancelable_callback.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioBus;
+class AudioManagerMac;
+
+// Implementation of AudioInputStream for Mac OS X using the audio queue service
+// present in OS 10.5 and later. Design reflects PCMQueueOutAudioOutputStream.
+class PCMQueueInAudioInputStream : public AudioInputStream {
+ public:
+  // Parameters as per AudioManager::MakeAudioInputStream.
+  PCMQueueInAudioInputStream(AudioManagerMac* manager,
+                             const AudioParameters& params);
+
+  PCMQueueInAudioInputStream(const PCMQueueInAudioInputStream&) = delete;
+  PCMQueueInAudioInputStream& operator=(const PCMQueueInAudioInputStream&) =
+      delete;
+
+  ~PCMQueueInAudioInputStream() override;
+
+  // Implementation of AudioInputStream.
+  AudioInputStream::OpenOutcome Open() override;
+  void Start(AudioInputCallback* callback) override;
+  void Stop() override;
+  void Close() override;
+  double GetMaxVolume() override;
+  void SetVolume(double volume) override;
+  double GetVolume() override;
+  bool SetAutomaticGainControl(bool enabled) override;
+  bool GetAutomaticGainControl() override;
+  bool IsMuted() override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+ private:
+  // Issue the OnError to |callback_|;
+  void HandleError(OSStatus err);
+
+  // Allocates and prepares the memory that will be used for recording.
+  bool SetupBuffers();
+
+  // Sends a buffer to the audio driver for recording.
+  OSStatus QueueNextBuffer(AudioQueueBufferRef audio_buffer);
+
+  // Callback from OS, delegates to non-static version below.
+  static void HandleInputBufferStatic(
+      void* data,
+      AudioQueueRef audio_queue,
+      AudioQueueBufferRef audio_buffer,
+      const AudioTimeStamp* start_time,
+      UInt32 num_packets,
+      const AudioStreamPacketDescription* desc);
+
+  // Handles callback from OS. Will be called on OS internal thread.
+  void HandleInputBuffer(AudioQueueRef audio_queue,
+                         AudioQueueBufferRef audio_buffer,
+                         const AudioTimeStamp* start_time,
+                         UInt32 num_packets,
+                         const AudioStreamPacketDescription* packet_desc);
+
+  static const int kNumberBuffers = 3;
+
+  // Helper methods to set and get atomic |input_callback_is_active_|.
+  void SetInputCallbackIsActive(bool active);
+  bool GetInputCallbackIsActive();
+
+  // Checks if a stream was started successfully and the audio unit also starts
+  // to call InputProc() as it should. This method is called once when a timer
+  // expires, a few seconds after calling Start().
+  void CheckInputStartupSuccess();
+
+  // Manager that owns this stream, used for closing down.
+  AudioManagerMac* manager_;
+  // We use the callback mostly to periodically supply the recorded audio data.
+  AudioInputCallback* callback_;
+  // Structure that holds the stream format details such as bitrate.
+  AudioStreamBasicDescription format_;
+  // Handle to the OS audio queue object.
+  AudioQueueRef audio_queue_;
+  // Size of each of the buffers in |audio_buffers_|
+  uint32_t buffer_size_bytes_;
+  // True iff Start() has been called successfully.
+  bool started_;
+  // Used to determine if we need to slow down |callback_| calls.
+  base::TimeTicks last_fill_;
+  // Used to defer Start() to workaround http://crbug.com/160920.
+  base::CancelableOnceClosure deferred_start_cb_;
+
+  // Is set to true on the internal AUHAL IO thread in the first input callback
+  // after Start() has bee called.
+  base::subtle::Atomic32 input_callback_is_active_;
+
+  // Timer which triggers CheckInputStartupSuccess() to verify that input
+  // callbacks have started as intended after a successful call to Start().
+  // This timer lives on the main browser thread.
+  std::unique_ptr<base::OneShotTimer> input_callback_timer_;
+
+  std::unique_ptr<media::AudioBus> audio_bus_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_MAC_AUDIO_INPUT_MAC_H_
diff --git a/third_party/chromium/media/audio/mac/audio_low_latency_input_mac.cc b/third_party/chromium/media/audio/mac/audio_low_latency_input_mac.cc
new file mode 100644
index 0000000..e28d374
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_low_latency_input_mac.cc
@@ -0,0 +1,1485 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "media/audio/mac/audio_low_latency_input_mac.h"
+
+#include <CoreAudio/AudioHardware.h>
+#include <CoreServices/CoreServices.h>
+#include <dlfcn.h>
+#include <mach-o/loader.h>
+#include <mach/mach.h>
+#include <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/strcat.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/system/sys_info.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "media/audio/mac/core_audio_util_mac.h"
+#include "media/audio/mac/scoped_audio_unit.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_timestamp_helper.h"
+#include "media/base/data_buffer.h"
+
+namespace {
+extern "C" {
+// See:
+// https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/PAL/pal/spi/cf/CoreAudioSPI.h?rev=228264
+OSStatus AudioDeviceDuck(AudioDeviceID inDevice,
+                         Float32 inDuckedLevel,
+                         const AudioTimeStamp* __nullable inStartTime,
+                         Float32 inRampDuration) __attribute__((weak_import));
+}
+
+void UndoDucking(AudioDeviceID output_device_id) {
+  if (AudioDeviceDuck != nullptr) {
+    // Ramp the volume back up over half a second.
+    AudioDeviceDuck(output_device_id, 1.0, nullptr, 0.5);
+  }
+}
+
+}  // namespace
+
+namespace media {
+
+// Number of blocks of buffers used in the |fifo_|.
+const int kNumberOfBlocksBufferInFifo = 2;
+
+// Max length of sequence of TooManyFramesToProcessError errors.
+// The stream will be stopped as soon as this time limit is passed.
+const int kMaxErrorTimeoutInSeconds = 1;
+
+// A one-shot timer is created and started in Start() and it triggers
+// CheckInputStartupSuccess() after this amount of time. UMA stats marked
+// Media.Audio.InputStartupSuccessMac is then updated where true is added
+// if input callbacks have started, and false otherwise.
+const int kInputCallbackStartTimeoutInSeconds = 5;
+
+// Returns true if the format flags in |format_flags| has the "non-interleaved"
+// flag (kAudioFormatFlagIsNonInterleaved) cleared (set to 0).
+static bool FormatIsInterleaved(UInt32 format_flags) {
+  return !(format_flags & kAudioFormatFlagIsNonInterleaved);
+}
+
+// Converts the 32-bit non-terminated 4 byte string into an std::string.
+// Example: code=1735354734 <=> 'goin' <=> kAudioDevicePropertyDeviceIsRunning.
+static std::string FourCharFormatCodeToString(UInt32 code) {
+  char code_string[5];
+  // Converts a 32-bit integer from the host’s native byte order to big-endian.
+  UInt32 code_id = CFSwapInt32HostToBig(code);
+  bcopy(&code_id, code_string, 4);
+  code_string[4] = '\0';
+  return std::string(code_string);
+}
+
+static std::ostream& operator<<(std::ostream& os,
+                                const AudioStreamBasicDescription& format) {
+  std::string format_string = FourCharFormatCodeToString(format.mFormatID);
+  os << "sample rate       : " << format.mSampleRate << std::endl
+     << "format ID         : " << format_string << std::endl
+     << "format flags      : " << format.mFormatFlags << std::endl
+     << "bytes per packet  : " << format.mBytesPerPacket << std::endl
+     << "frames per packet : " << format.mFramesPerPacket << std::endl
+     << "bytes per frame   : " << format.mBytesPerFrame << std::endl
+     << "channels per frame: " << format.mChannelsPerFrame << std::endl
+     << "bits per channel  : " << format.mBitsPerChannel << std::endl
+     << "reserved          : " << format.mReserved << std::endl
+     << "interleaved       : "
+     << (FormatIsInterleaved(format.mFormatFlags) ? "yes" : "no");
+  return os;
+}
+
+static OSStatus OnGetPlayoutData(void* in_ref_con,
+                                 AudioUnitRenderActionFlags* flags,
+                                 const AudioTimeStamp* time_stamp,
+                                 UInt32 bus_number,
+                                 UInt32 num_frames,
+                                 AudioBufferList* io_data) {
+  *flags |= kAudioUnitRenderAction_OutputIsSilence;
+  return noErr;
+}
+
+static OSStatus GetInputDeviceStreamFormat(
+    AudioUnit audio_unit,
+    AudioStreamBasicDescription* format) {
+  DCHECK(audio_unit);
+  UInt32 property_size = sizeof(*format);
+  // Get the audio stream data format on the input scope of the input element
+  // since it is connected to the current input device.
+  OSStatus result =
+      AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat,
+                           kAudioUnitScope_Input, 1, format, &property_size);
+  DVLOG(1) << "Input device stream format: " << *format;
+  return result;
+}
+
+// Returns the number of physical processors on the device.
+static int NumberOfPhysicalProcessors() {
+  base::mac::ScopedMachSendRight mach_host(mach_host_self());
+  host_basic_info hbi = {};
+  mach_msg_type_number_t info_count = HOST_BASIC_INFO_COUNT;
+  kern_return_t kr =
+      host_info(mach_host.get(), HOST_BASIC_INFO,
+                reinterpret_cast<host_info_t>(&hbi), &info_count);
+
+  int n_physical_cores = 0;
+  if (kr != KERN_SUCCESS) {
+    n_physical_cores = 1;
+    LOG(ERROR) << "Failed to determine number of physical cores, assuming 1";
+  } else {
+    n_physical_cores = hbi.physical_cpu;
+  }
+  DCHECK_EQ(HOST_BASIC_INFO_COUNT, info_count);
+  return n_physical_cores;
+}
+
+// Adds extra system information to Media.AudioXXXMac UMA statistics.
+// Only called when it has been detected that audio callbacks does not start
+// as expected.
+static void AddSystemInfoToUMA() {
+  // Number of logical processors/cores on the current machine.
+  UMA_HISTOGRAM_COUNTS_1M("Media.Audio.LogicalProcessorsMac",
+                          base::SysInfo::NumberOfProcessors());
+  // Number of physical processors/cores on the current machine.
+  UMA_HISTOGRAM_COUNTS_1M("Media.Audio.PhysicalProcessorsMac",
+                          NumberOfPhysicalProcessors());
+  DVLOG(1) << "logical processors: " << base::SysInfo::NumberOfProcessors();
+  DVLOG(1) << "physical processors: " << NumberOfPhysicalProcessors();
+}
+
+// Finds the first subdevice, in an aggregate device, with output streams.
+static AudioDeviceID FindFirstOutputSubdevice(
+    AudioDeviceID aggregate_device_id) {
+  const AudioObjectPropertyAddress property_address = {
+      kAudioAggregateDevicePropertyFullSubDeviceList,
+      kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
+  base::ScopedCFTypeRef<CFArrayRef> subdevices;
+  UInt32 size = sizeof(subdevices);
+  OSStatus result = AudioObjectGetPropertyData(
+      aggregate_device_id, &property_address, 0 /* inQualifierDataSize */,
+      nullptr /* inQualifierData */, &size, subdevices.InitializeInto());
+
+  if (result != noErr) {
+    OSSTATUS_LOG(WARNING, result)
+        << "Failed to read property "
+        << kAudioAggregateDevicePropertyFullSubDeviceList << " for device "
+        << aggregate_device_id;
+    return kAudioObjectUnknown;
+  }
+
+  AudioDeviceID output_subdevice_id = kAudioObjectUnknown;
+  DCHECK_EQ(CFGetTypeID(subdevices), CFArrayGetTypeID());
+  const CFIndex count = CFArrayGetCount(subdevices);
+  for (CFIndex i = 0; i != count; ++i) {
+    CFStringRef value =
+        base::mac::CFCast<CFStringRef>(CFArrayGetValueAtIndex(subdevices, i));
+    if (value) {
+      std::string uid = base::SysCFStringRefToUTF8(value);
+      output_subdevice_id = AudioManagerMac::GetAudioDeviceIdByUId(false, uid);
+      if (output_subdevice_id != kAudioObjectUnknown &&
+          core_audio_mac::GetNumStreams(output_subdevice_id, false) > 0) {
+        break;
+      }
+    }
+  }
+
+  return output_subdevice_id;
+}
+
+// See "Technical Note TN2091 - Device input using the HAL Output Audio Unit"
+// http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
+// for more details and background regarding this implementation.
+
+AUAudioInputStream::AUAudioInputStream(
+    AudioManagerMac* manager,
+    const AudioParameters& input_params,
+    AudioDeviceID audio_device_id,
+    const AudioManager::LogCallback& log_callback,
+    AudioManagerBase::VoiceProcessingMode voice_processing_mode)
+    : manager_(manager),
+      input_params_(input_params),
+      number_of_frames_provided_(0),
+      io_buffer_frame_size_(0),
+      sink_(nullptr),
+      audio_unit_(0),
+      input_device_id_(audio_device_id),
+      number_of_channels_in_frame_(0),
+      fifo_(input_params.channels(),
+            input_params.frames_per_buffer(),
+            kNumberOfBlocksBufferInFifo),
+      got_input_callback_(false),
+      input_callback_is_active_(false),
+      buffer_size_was_changed_(false),
+      audio_unit_render_has_worked_(false),
+      noise_reduction_suppressed_(false),
+      use_voice_processing_(voice_processing_mode ==
+                            AudioManagerBase::VoiceProcessingMode::kEnabled),
+      output_device_id_for_aec_(kAudioObjectUnknown),
+      last_sample_time_(0.0),
+      last_number_of_frames_(0),
+      total_lost_frames_(0),
+      largest_glitch_frames_(0),
+      glitches_detected_(0),
+      log_callback_(log_callback) {
+  DCHECK(manager_);
+  CHECK(log_callback_ != AudioManager::LogCallback());
+  if (use_voice_processing_) {
+    DCHECK(input_params.channels() == 1 || input_params.channels() == 2);
+    const bool got_default_device =
+        AudioManagerMac::GetDefaultOutputDevice(&output_device_id_for_aec_);
+    DCHECK(got_default_device);
+  }
+
+  const SampleFormat kSampleFormat = kSampleFormatS16;
+
+  // Set up the desired (output) format specified by the client.
+  format_.mSampleRate = input_params.sample_rate();
+  format_.mFormatID = kAudioFormatLinearPCM;
+  format_.mFormatFlags =
+      kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
+  DCHECK(FormatIsInterleaved(format_.mFormatFlags));
+  format_.mBitsPerChannel = SampleFormatToBitsPerChannel(kSampleFormat);
+  format_.mChannelsPerFrame = input_params.channels();
+  format_.mFramesPerPacket = 1;  // uncompressed audio
+  format_.mBytesPerPacket = format_.mBytesPerFrame =
+      input_params.GetBytesPerFrame(kSampleFormat);
+  format_.mReserved = 0;
+
+  DVLOG(1) << "ctor";
+  DVLOG(1) << "device ID: 0x" << std::hex << audio_device_id;
+  DVLOG(1) << "buffer size : " << input_params.frames_per_buffer();
+  DVLOG(1) << "channels : " << input_params.channels();
+  DVLOG(1) << "desired output format: " << format_;
+
+  // Derive size (in bytes) of the buffers that we will render to.
+  UInt32 data_byte_size =
+      input_params.frames_per_buffer() * format_.mBytesPerFrame;
+  DVLOG(1) << "size of data buffer in bytes : " << data_byte_size;
+
+  // Allocate AudioBuffers to be used as storage for the received audio.
+  // The AudioBufferList structure works as a placeholder for the
+  // AudioBuffer structure, which holds a pointer to the actual data buffer.
+  audio_data_buffer_.reset(new uint8_t[data_byte_size]);
+  // We ask for noninterleaved audio.
+  audio_buffer_list_.mNumberBuffers = 1;
+
+  AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
+  audio_buffer->mNumberChannels = input_params.channels();
+  audio_buffer->mDataByteSize = data_byte_size;
+  audio_buffer->mData = audio_data_buffer_.get();
+}
+
+AUAudioInputStream::~AUAudioInputStream() {
+  DVLOG(1) << "~dtor";
+  ReportAndResetStats();
+}
+
+// Obtain and open the AUHAL AudioOutputUnit for recording.
+AudioInputStream::OpenOutcome AUAudioInputStream::Open() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DVLOG(1) << "Open";
+  DCHECK(!audio_unit_);
+
+  // Verify that we have a valid device. Send appropriate error code to
+  // HandleError() to ensure that the error type is added to UMA stats.
+  if (input_device_id_ == kAudioObjectUnknown) {
+    NOTREACHED() << "Device ID is unknown";
+    HandleError(kAudioUnitErr_InvalidElement);
+    return OpenOutcome::kFailed;
+  }
+
+  // The requested sample-rate must match the hardware sample-rate.
+  const int sample_rate =
+      AudioManagerMac::HardwareSampleRateForDevice(input_device_id_);
+  DCHECK_EQ(sample_rate, format_.mSampleRate);
+
+  log_callback_.Run(base::StrCat(
+      {"AU in: Open using ", use_voice_processing_ ? "VPAU" : "AUHAL"}));
+
+  const bool success =
+      use_voice_processing_ ? OpenVoiceProcessingAU() : OpenAUHAL();
+
+  if (!success)
+    return OpenOutcome::kFailed;
+
+  // The hardware latency is fixed and will not change during the call.
+  hardware_latency_ = AudioManagerMac::GetHardwareLatency(
+      audio_unit_, input_device_id_, kAudioDevicePropertyScopeInput,
+      format_.mSampleRate);
+
+  // The master channel is 0, Left and right are channels 1 and 2.
+  // And the master channel is not counted in |number_of_channels_in_frame_|.
+  number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();
+
+  return OpenOutcome::kSuccess;
+}
+
+bool AUAudioInputStream::OpenAUHAL() {
+  // Start by obtaining an AudioOuputUnit using an AUHAL component description.
+
+  // Description for the Audio Unit we want to use (AUHAL in this case).
+  // The kAudioUnitSubType_HALOutput audio unit interfaces to any audio device.
+  // The user specifies which audio device to track. The audio unit can do
+  // input from the device as well as output to the device. Bus 0 is used for
+  // the output side, bus 1 is used to get audio input from the device.
+  AudioComponentDescription desc = {kAudioUnitType_Output,
+                                    kAudioUnitSubType_HALOutput,
+                                    kAudioUnitManufacturer_Apple, 0, 0};
+
+  // Find a component that meets the description in |desc|.
+  AudioComponent comp = AudioComponentFindNext(nullptr, &desc);
+  DCHECK(comp);
+  if (!comp) {
+    HandleError(kAudioUnitErr_NoConnection);
+    return false;
+  }
+
+  // Get access to the service provided by the specified Audio Unit.
+  OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
+  if (result) {
+    HandleError(result);
+    return false;
+  }
+
+  // Initialize the AUHAL before making any changes or using it. The audio
+  // unit will be initialized once more as last operation in this method but
+  // that is intentional. This approach is based on a comment in the
+  // CAPlayThrough example from Apple, which states that "AUHAL needs to be
+  // initialized *before* anything is done to it".
+  // TODO(henrika): remove this extra call if we are unable to see any
+  // positive effects of it in our UMA stats.
+  result = AudioUnitInitialize(audio_unit_);
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  // Enable IO on the input scope of the Audio Unit.
+  // Note that, these changes must be done *before* setting the AUHAL's
+  // current device.
+
+  // After creating the AUHAL object, we must enable IO on the input scope
+  // of the Audio Unit to obtain the device input. Input must be explicitly
+  // enabled with the kAudioOutputUnitProperty_EnableIO property on Element 1
+  // of the AUHAL. Because the AUHAL can be used for both input and output,
+  // we must also disable IO on the output scope.
+
+  // kAudioOutputUnitProperty_EnableIO is not a writable property of the
+  // voice processing unit (we'd get kAudioUnitErr_PropertyNotWritable returned
+  // back to us). IO is always enabled.
+
+  // Enable input on the AUHAL.
+  {
+    const UInt32 enableIO = 1;
+    result = AudioUnitSetProperty(
+        audio_unit_, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,
+        AUElement::INPUT, &enableIO, sizeof(enableIO));
+    if (result != noErr) {
+      HandleError(result);
+      return false;
+    }
+  }
+
+  // Disable output on the AUHAL.
+  {
+    const UInt32 disableIO = 0;
+    result = AudioUnitSetProperty(
+        audio_unit_, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
+        AUElement::OUTPUT, &disableIO, sizeof(disableIO));
+    if (result != noErr) {
+      HandleError(result);
+      return false;
+    }
+  }
+
+  // Next, set the audio device to be the Audio Unit's current device.
+  // Note that, devices can only be set to the AUHAL after enabling IO.
+  result =
+      AudioUnitSetProperty(audio_unit_, kAudioOutputUnitProperty_CurrentDevice,
+                           kAudioUnitScope_Global, AUElement::OUTPUT,
+                           &input_device_id_, sizeof(input_device_id_));
+
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  // Register the input procedure for the AUHAL. This procedure will be called
+  // when the AUHAL has received new data from the input device.
+  AURenderCallbackStruct callback;
+  callback.inputProc = &DataIsAvailable;
+  callback.inputProcRefCon = this;
+  result = AudioUnitSetProperty(
+      audio_unit_, kAudioOutputUnitProperty_SetInputCallback,
+      kAudioUnitScope_Global, AUElement::OUTPUT, &callback, sizeof(callback));
+
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  // Get the stream format for the selected input device and ensure that the
+  // sample rate of the selected input device matches the desired (given at
+  // construction) sample rate. We should not rely on sample rate conversion
+  // in the AUHAL, only *simple* conversions, e.g., 32-bit float to 16-bit
+  // signed integer format.
+  AudioStreamBasicDescription input_device_format = {0};
+  GetInputDeviceStreamFormat(audio_unit_, &input_device_format);
+  if (input_device_format.mSampleRate != format_.mSampleRate) {
+    LOG(ERROR) << "Input device's sample rate does not match the client's "
+                  "sample rate; input_device_format="
+               << input_device_format;
+    result = kAudioUnitErr_FormatNotSupported;
+    HandleError(result);
+    return false;
+  }
+
+  // Modify the IO buffer size if not already set correctly for the selected
+  // device. The status of other active audio input and output streams is
+  // involved in the final setting.
+  // TODO(henrika): we could make io_buffer_frame_size a member and add it to
+  // the UMA stats tied to the Media.Audio.InputStartupSuccessMac record.
+  size_t io_buffer_frame_size = 0;
+  if (!manager_->MaybeChangeBufferSize(
+          input_device_id_, audio_unit_, 1, input_params_.frames_per_buffer(),
+          &buffer_size_was_changed_, &io_buffer_frame_size)) {
+    result = kAudioUnitErr_FormatNotSupported;
+    HandleError(result);
+    return false;
+  }
+
+  // Store current I/O buffer frame size for UMA stats stored in combination
+  // with failing input callbacks.
+  DCHECK(!io_buffer_frame_size_);
+  io_buffer_frame_size_ = io_buffer_frame_size;
+
+  // If the requested number of frames is out of range, the closest valid buffer
+  // size will be set instead. Check the current setting and log a warning for a
+  // non perfect match. Any such mismatch will be compensated for in
+  // OnDataIsAvailable().
+  UInt32 buffer_frame_size = 0;
+  UInt32 property_size = sizeof(buffer_frame_size);
+  result = AudioUnitGetProperty(
+      audio_unit_, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global,
+      AUElement::OUTPUT, &buffer_frame_size, &property_size);
+  LOG_IF(WARNING, buffer_frame_size !=
+                      static_cast<UInt32>(input_params_.frames_per_buffer()))
+      << "AUHAL is using best match of IO buffer size: " << buffer_frame_size;
+
+  // Channel mapping should be supported but add a warning just in case.
+  // TODO(henrika): perhaps add to UMA stat to track if this can happen.
+  DLOG_IF(WARNING,
+          input_device_format.mChannelsPerFrame != format_.mChannelsPerFrame)
+      << "AUHAL's audio converter must do channel conversion";
+
+  // Set up the the desired (output) format.
+  // For obtaining input from a device, the device format is always expressed
+  // on the output scope of the AUHAL's Element 1.
+  result = AudioUnitSetProperty(audio_unit_, kAudioUnitProperty_StreamFormat,
+                                kAudioUnitScope_Output, 1, &format_,
+                                sizeof(format_));
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  // Finally, initialize the audio unit and ensure that it is ready to render.
+  // Allocates memory according to the maximum number of audio frames
+  // it can produce in response to a single render call.
+  result = AudioUnitInitialize(audio_unit_);
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  return true;
+}
+
+bool AUAudioInputStream::OpenVoiceProcessingAU() {
+  // Start by obtaining an AudioOuputUnit using an AUHAL component description.
+
+  // Description for the Audio Unit we want to use (AUHAL in this case).
+  // The kAudioUnitSubType_HALOutput audio unit interfaces to any audio device.
+  // The user specifies which audio device to track. The audio unit can do
+  // input from the device as well as output to the device. Bus 0 is used for
+  // the output side, bus 1 is used to get audio input from the device.
+  AudioComponentDescription desc = {kAudioUnitType_Output,
+                                    kAudioUnitSubType_VoiceProcessingIO,
+                                    kAudioUnitManufacturer_Apple, 0, 0};
+
+  // Find a component that meets the description in |desc|.
+  AudioComponent comp = AudioComponentFindNext(nullptr, &desc);
+  DCHECK(comp);
+  if (!comp) {
+    HandleError(kAudioUnitErr_NoConnection);
+    return false;
+  }
+
+  // Get access to the service provided by the specified Audio Unit.
+  OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
+  if (result) {
+    HandleError(result);
+    return false;
+  }
+
+  // Next, set the audio device to be the Audio Unit's input device.
+  result =
+      AudioUnitSetProperty(audio_unit_, kAudioOutputUnitProperty_CurrentDevice,
+                           kAudioUnitScope_Global, AUElement::INPUT,
+                           &input_device_id_, sizeof(input_device_id_));
+
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  // Followed by the audio device to be the Audio Unit's output device.
+  result = AudioUnitSetProperty(
+      audio_unit_, kAudioOutputUnitProperty_CurrentDevice,
+      kAudioUnitScope_Global, AUElement::OUTPUT, &output_device_id_for_aec_,
+      sizeof(output_device_id_for_aec_));
+
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  // Register the input procedure for the AUHAL. This procedure will be called
+  // when the AUHAL has received new data from the input device.
+  AURenderCallbackStruct callback;
+  callback.inputProc = &DataIsAvailable;
+  callback.inputProcRefCon = this;
+
+  result = AudioUnitSetProperty(
+      audio_unit_, kAudioOutputUnitProperty_SetInputCallback,
+      kAudioUnitScope_Global, AUElement::INPUT, &callback, sizeof(callback));
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  callback.inputProc = OnGetPlayoutData;
+  callback.inputProcRefCon = this;
+  result = AudioUnitSetProperty(
+      audio_unit_, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
+      AUElement::OUTPUT, &callback, sizeof(callback));
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  // Get the stream format for the selected input device and ensure that the
+  // sample rate of the selected input device matches the desired (given at
+  // construction) sample rate. We should not rely on sample rate conversion
+  // in the AUHAL, only *simple* conversions, e.g., 32-bit float to 16-bit
+  // signed integer format.
+  AudioStreamBasicDescription input_device_format = {0};
+  GetInputDeviceStreamFormat(audio_unit_, &input_device_format);
+  if (input_device_format.mSampleRate != format_.mSampleRate) {
+    LOG(ERROR)
+        << "Input device's sample rate does not match the client's sample rate";
+    result = kAudioUnitErr_FormatNotSupported;
+    HandleError(result);
+    return false;
+  }
+
+  // Modify the IO buffer size if not already set correctly for the selected
+  // device. The status of other active audio input and output streams is
+  // involved in the final setting.
+  // TODO(henrika): we could make io_buffer_frame_size a member and add it to
+  // the UMA stats tied to the Media.Audio.InputStartupSuccessMac record.
+  size_t io_buffer_frame_size = 0;
+  if (!manager_->MaybeChangeBufferSize(
+          input_device_id_, audio_unit_, 1, input_params_.frames_per_buffer(),
+          &buffer_size_was_changed_, &io_buffer_frame_size)) {
+    result = kAudioUnitErr_FormatNotSupported;
+    HandleError(result);
+    return false;
+  }
+
+  // Store current I/O buffer frame size for UMA stats stored in combination
+  // with failing input callbacks.
+  DCHECK(!io_buffer_frame_size_);
+  io_buffer_frame_size_ = io_buffer_frame_size;
+
+  // If the requested number of frames is out of range, the closest valid buffer
+  // size will be set instead. Check the current setting and log a warning for a
+  // non perfect match. Any such mismatch will be compensated for in
+  // OnDataIsAvailable().
+  UInt32 buffer_frame_size = 0;
+  UInt32 property_size = sizeof(buffer_frame_size);
+  result = AudioUnitGetProperty(
+      audio_unit_, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global,
+      AUElement::OUTPUT, &buffer_frame_size, &property_size);
+  LOG_IF(WARNING, buffer_frame_size !=
+                      static_cast<UInt32>(input_params_.frames_per_buffer()))
+      << "AUHAL is using best match of IO buffer size: " << buffer_frame_size;
+
+  // The built-in device claims to be stereo. VPAU claims 5 channels (for me)
+  // but refuses to work in stereo. Just accept stero for now, use mono
+  // internally and upmix.
+  AudioStreamBasicDescription mono_format = format_;
+  if (format_.mChannelsPerFrame == 2) {
+    mono_format.mChannelsPerFrame = 1;
+    mono_format.mBytesPerPacket = mono_format.mBitsPerChannel / 8;
+    mono_format.mBytesPerFrame = mono_format.mBytesPerPacket;
+  }
+
+  // Set up the the desired (output) format.
+  // For obtaining input from a device, the device format is always expressed
+  // on the output scope of the AUHAL's Element 1.
+  result = AudioUnitSetProperty(audio_unit_, kAudioUnitProperty_StreamFormat,
+                                kAudioUnitScope_Output, AUElement::INPUT,
+                                &mono_format, sizeof(mono_format));
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  result = AudioUnitSetProperty(audio_unit_, kAudioUnitProperty_StreamFormat,
+                                kAudioUnitScope_Input, AUElement::OUTPUT,
+                                &mono_format, sizeof(mono_format));
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  // Finally, initialize the audio unit and ensure that it is ready to render.
+  // Allocates memory according to the maximum number of audio frames
+  // it can produce in response to a single render call.
+  result = AudioUnitInitialize(audio_unit_);
+  if (result != noErr) {
+    HandleError(result);
+    return false;
+  }
+
+  UndoDucking(output_device_id_for_aec_);
+
+  return true;
+}
+
+void AUAudioInputStream::Start(AudioInputCallback* callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DVLOG(1) << "Start";
+  DCHECK(callback);
+  DCHECK(!sink_);
+  DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully";
+  if (IsRunning())
+    return;
+
+  // Check if we should defer Start() for http://crbug.com/160920.
+  if (manager_->ShouldDeferStreamStart()) {
+    LOG(WARNING) << "Start of input audio is deferred";
+    // Use a cancellable closure so that if Stop() is called before Start()
+    // actually runs, we can cancel the pending start.
+    deferred_start_cb_.Reset(base::BindOnce(&AUAudioInputStream::Start,
+                                            base::Unretained(this), callback));
+    manager_->GetTaskRunner()->PostDelayedTask(
+        FROM_HERE, deferred_start_cb_.callback(),
+        base::Seconds(AudioManagerMac::kStartDelayInSecsForPowerEvents));
+    return;
+  }
+
+  sink_ = callback;
+  last_success_time_ = base::TimeTicks::Now();
+  audio_unit_render_has_worked_ = false;
+
+  // Don't disable built-in noise suppression when using VPAU.
+  if (!use_voice_processing_ &&
+      !(input_params_.effects() & AudioParameters::NOISE_SUPPRESSION) &&
+      manager_->DeviceSupportsAmbientNoiseReduction(input_device_id_)) {
+    noise_reduction_suppressed_ =
+        manager_->SuppressNoiseReduction(input_device_id_);
+  }
+  StartAgc();
+  OSStatus result = AudioOutputUnitStart(audio_unit_);
+  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+      << "Failed to start acquiring data";
+  if (result != noErr) {
+    Stop();
+    return;
+  }
+  DCHECK(IsRunning()) << "Audio unit started OK but is not yet running";
+
+  // For UMA stat purposes, start a one-shot timer which detects when input
+  // callbacks starts indicating if input audio recording starts as intended.
+  // CheckInputStartupSuccess() will check if |input_callback_is_active_| is
+  // true when the timer expires.
+  input_callback_timer_ = std::make_unique<base::OneShotTimer>();
+  input_callback_timer_->Start(
+      FROM_HERE, base::Seconds(kInputCallbackStartTimeoutInSeconds), this,
+      &AUAudioInputStream::CheckInputStartupSuccess);
+  DCHECK(input_callback_timer_->IsRunning());
+}
+
+void AUAudioInputStream::Stop() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  deferred_start_cb_.Cancel();
+  DVLOG(1) << "Stop";
+  StopAgc();
+  if (noise_reduction_suppressed_) {
+    manager_->UnsuppressNoiseReduction(input_device_id_);
+    noise_reduction_suppressed_ = false;
+  }
+  if (input_callback_timer_ != nullptr) {
+    input_callback_timer_->Stop();
+    input_callback_timer_.reset();
+  }
+
+  if (audio_unit_ != nullptr) {
+    // Stop the I/O audio unit.
+    OSStatus result = AudioOutputUnitStop(audio_unit_);
+    DCHECK_EQ(result, noErr);
+    // Add a DCHECK here just in case. AFAIK, the call to AudioOutputUnitStop()
+    // seems to set this state synchronously, hence it should always report
+    // false after a successful call.
+    DCHECK(!IsRunning()) << "Audio unit is stopped but still running";
+
+    // Reset the audio unit’s render state. This function clears memory.
+    // It does not allocate or free memory resources.
+    result = AudioUnitReset(audio_unit_, kAudioUnitScope_Global, 0);
+    DCHECK_EQ(result, noErr);
+    OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+        << "Failed to stop acquiring data";
+  }
+
+  SetInputCallbackIsActive(false);
+  ReportAndResetStats();
+  sink_ = nullptr;
+  fifo_.Clear();
+  io_buffer_frame_size_ = 0;
+  got_input_callback_ = false;
+}
+
+void AUAudioInputStream::Close() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DVLOG(1) << "Close";
+
+  // It is valid to call Close() before calling open or Start().
+  // It is also valid to call Close() after Start() has been called.
+  if (IsRunning()) {
+    Stop();
+  }
+
+  // Uninitialize and dispose the audio unit.
+  CloseAudioUnit();
+
+  // Inform the audio manager that we have been closed. This will cause our
+  // destruction.
+  manager_->ReleaseInputStream(this);
+}
+
+double AUAudioInputStream::GetMaxVolume() {
+  // Verify that we have a valid device.
+  if (input_device_id_ == kAudioObjectUnknown) {
+    NOTREACHED() << "Device ID is unknown";
+    return 0.0;
+  }
+
+  // Query if any of the master, left or right channels has volume control.
+  for (int i = 0; i <= number_of_channels_in_frame_; ++i) {
+    // If the volume is settable, the  valid volume range is [0.0, 1.0].
+    if (IsVolumeSettableOnChannel(i))
+      return 1.0;
+  }
+
+  // Volume control is not available for the audio stream.
+  return 0.0;
+}
+
+void AUAudioInputStream::SetVolume(double volume) {
+  DVLOG(1) << "SetVolume(volume=" << volume << ")";
+  DCHECK_GE(volume, 0.0);
+  DCHECK_LE(volume, 1.0);
+
+  // Verify that we have a valid device.
+  if (input_device_id_ == kAudioObjectUnknown) {
+    NOTREACHED() << "Device ID is unknown";
+    return;
+  }
+
+  Float32 volume_float32 = static_cast<Float32>(volume);
+  AudioObjectPropertyAddress property_address = {
+      kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
+      kAudioObjectPropertyElementMaster};
+
+  // Try to set the volume for master volume channel.
+  if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) {
+    OSStatus result = AudioObjectSetPropertyData(
+        input_device_id_, &property_address, 0, nullptr, sizeof(volume_float32),
+        &volume_float32);
+    if (result != noErr) {
+      DLOG(WARNING) << "Failed to set volume to " << volume_float32;
+    }
+    return;
+  }
+
+  // There is no master volume control, try to set volume for each channel.
+  int successful_channels = 0;
+  for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
+    property_address.mElement = static_cast<UInt32>(i);
+    if (IsVolumeSettableOnChannel(i)) {
+      OSStatus result = AudioObjectSetPropertyData(
+          input_device_id_, &property_address, 0, NULL, sizeof(volume_float32),
+          &volume_float32);
+      if (result == noErr)
+        ++successful_channels;
+    }
+  }
+
+  DLOG_IF(WARNING, successful_channels == 0)
+      << "Failed to set volume to " << volume_float32;
+
+  // Update the AGC volume level based on the last setting above. Note that,
+  // the volume-level resolution is not infinite and it is therefore not
+  // possible to assume that the volume provided as input parameter can be
+  // used directly. Instead, a new query to the audio hardware is required.
+  // This method does nothing if AGC is disabled.
+  UpdateAgcVolume();
+}
+
+double AUAudioInputStream::GetVolume() {
+  // Verify that we have a valid device.
+  if (input_device_id_ == kAudioObjectUnknown) {
+    NOTREACHED() << "Device ID is unknown";
+    return 0.0;
+  }
+
+  AudioObjectPropertyAddress property_address = {
+      kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
+      kAudioObjectPropertyElementMaster};
+
+  if (AudioObjectHasProperty(input_device_id_, &property_address)) {
+    // The device supports master volume control, get the volume from the
+    // master channel.
+    Float32 volume_float32 = 0.0;
+    UInt32 size = sizeof(volume_float32);
+    OSStatus result =
+        AudioObjectGetPropertyData(input_device_id_, &property_address, 0,
+                                   nullptr, &size, &volume_float32);
+    if (result == noErr)
+      return static_cast<double>(volume_float32);
+  } else {
+    // There is no master volume control, try to get the average volume of
+    // all the channels.
+    Float32 volume_float32 = 0.0;
+    int successful_channels = 0;
+    for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
+      property_address.mElement = static_cast<UInt32>(i);
+      if (AudioObjectHasProperty(input_device_id_, &property_address)) {
+        Float32 channel_volume = 0;
+        UInt32 size = sizeof(channel_volume);
+        OSStatus result =
+            AudioObjectGetPropertyData(input_device_id_, &property_address, 0,
+                                       nullptr, &size, &channel_volume);
+        if (result == noErr) {
+          volume_float32 += channel_volume;
+          ++successful_channels;
+        }
+      }
+    }
+
+    // Get the average volume of the channels.
+    if (successful_channels != 0)
+      return static_cast<double>(volume_float32 / successful_channels);
+  }
+
+  DLOG(WARNING) << "Failed to get volume";
+  return 0.0;
+}
+
+bool AUAudioInputStream::IsMuted() {
+  // Verify that we have a valid device.
+  DCHECK_NE(input_device_id_, kAudioObjectUnknown) << "Device ID is unknown";
+
+  AudioObjectPropertyAddress property_address = {
+      kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput,
+      kAudioObjectPropertyElementMaster};
+
+  if (!AudioObjectHasProperty(input_device_id_, &property_address)) {
+    DLOG(ERROR) << "Device does not support checking master mute state";
+    return false;
+  }
+
+  UInt32 muted = 0;
+  UInt32 size = sizeof(muted);
+  OSStatus result = AudioObjectGetPropertyData(
+      input_device_id_, &property_address, 0, nullptr, &size, &muted);
+  DLOG_IF(WARNING, result != noErr) << "Failed to get mute state";
+  return result == noErr && muted != 0;
+}
+
+void AUAudioInputStream::SetOutputDeviceForAec(
+    const std::string& output_device_id) {
+  if (!use_voice_processing_)
+    return;
+
+  AudioDeviceID audio_device_id =
+      AudioManagerMac::GetAudioDeviceIdByUId(false, output_device_id);
+  if (audio_device_id == output_device_id_for_aec_)
+    return;
+
+  if (audio_device_id == kAudioObjectUnknown) {
+    log_callback_.Run(
+        base::StringPrintf("AU in: Unable to resolve output device id '%s'",
+                           output_device_id.c_str()));
+    return;
+  }
+
+  // If the selected device is an aggregate device, try to use the first output
+  // device of the aggregate device instead.
+  if (core_audio_mac::GetDeviceTransportType(audio_device_id) ==
+      kAudioDeviceTransportTypeAggregate) {
+    const AudioDeviceID output_subdevice_id =
+        FindFirstOutputSubdevice(audio_device_id);
+
+    if (output_subdevice_id == kAudioObjectUnknown) {
+      log_callback_.Run(base::StringPrintf(
+          "AU in: Unable to find an output subdevice in aggregate device '%s'",
+          output_device_id.c_str()));
+      return;
+    }
+    audio_device_id = output_subdevice_id;
+  }
+
+  if (audio_device_id != output_device_id_for_aec_) {
+    output_device_id_for_aec_ = audio_device_id;
+    log_callback_.Run(base::StringPrintf(
+        "AU in: Output device for AEC changed to '%s' (%d)",
+        output_device_id.c_str(), output_device_id_for_aec_));
+    // Only restart the stream if it has previously been started.
+    if (audio_unit_)
+      ReinitializeVoiceProcessingAudioUnit();
+  }
+}
+
+void AUAudioInputStream::ReinitializeVoiceProcessingAudioUnit() {
+  DCHECK(use_voice_processing_);
+  DCHECK(audio_unit_);
+
+  const bool was_running = IsRunning();
+  OSStatus result = noErr;
+
+  if (was_running) {
+    result = AudioOutputUnitStop(audio_unit_);
+    DCHECK_EQ(result, noErr);
+  }
+
+  CloseAudioUnit();
+
+  // Reset things to a state similar to before the audio unit was opened.
+  // Most of these will be no-ops if the audio unit was opened but not started.
+  SetInputCallbackIsActive(false);
+  ReportAndResetStats();
+  io_buffer_frame_size_ = 0;
+  got_input_callback_ = false;
+
+  OpenVoiceProcessingAU();
+
+  if (was_running) {
+    result = AudioOutputUnitStart(audio_unit_);
+    if (result != noErr) {
+      OSSTATUS_DLOG(ERROR, result) << "Failed to start acquiring data";
+      Stop();
+      return;
+    }
+  }
+
+  log_callback_.Run(base::StringPrintf(
+      "AU in: Successfully reinitialized AEC for output device id=%d.",
+      output_device_id_for_aec_));
+}
+
+// static
+OSStatus AUAudioInputStream::DataIsAvailable(void* context,
+                                             AudioUnitRenderActionFlags* flags,
+                                             const AudioTimeStamp* time_stamp,
+                                             UInt32 bus_number,
+                                             UInt32 number_of_frames,
+                                             AudioBufferList* io_data) {
+  DCHECK(context);
+  // Recorded audio is always on the input bus (=1).
+  DCHECK_EQ(bus_number, 1u);
+  // No data buffer should be allocated at this stage.
+  DCHECK(!io_data);
+  AUAudioInputStream* self = reinterpret_cast<AUAudioInputStream*>(context);
+  // Propagate render action flags, time stamp, bus number and number
+  // of frames requested to the AudioUnitRender() call where the actual data
+  // is received from the input device via the output scope of the audio unit.
+  return self->OnDataIsAvailable(flags, time_stamp, bus_number,
+                                 number_of_frames);
+}
+
+OSStatus AUAudioInputStream::OnDataIsAvailable(
+    AudioUnitRenderActionFlags* flags,
+    const AudioTimeStamp* time_stamp,
+    UInt32 bus_number,
+    UInt32 number_of_frames) {
+  TRACE_EVENT0("audio", "AUAudioInputStream::OnDataIsAvailable");
+
+  // Indicate that input callbacks have started.
+  if (!got_input_callback_) {
+    got_input_callback_ = true;
+    SetInputCallbackIsActive(true);
+  }
+
+  // Update the |mDataByteSize| value in the audio_buffer_list() since
+  // |number_of_frames| can be changed on the fly.
+  // |mDataByteSize| needs to be exactly mapping to |number_of_frames|,
+  // otherwise it will put CoreAudio into bad state and results in
+  // AudioUnitRender() returning -50 for the new created stream.
+  // We have also seen kAudioUnitErr_TooManyFramesToProcess (-10874) and
+  // kAudioUnitErr_CannotDoInCurrentContext (-10863) as error codes.
+  // See crbug/428706 for details.
+  UInt32 new_size = number_of_frames * format_.mBytesPerFrame;
+  AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
+  bool new_buffer_size_detected = false;
+  if (new_size != audio_buffer->mDataByteSize) {
+    new_buffer_size_detected = true;
+    DVLOG(1) << "New size of number_of_frames detected: " << number_of_frames;
+    io_buffer_frame_size_ = static_cast<size_t>(number_of_frames);
+    if (new_size > audio_buffer->mDataByteSize) {
+      // This can happen if the device is unplugged during recording. We
+      // allocate enough memory here to avoid depending on how CoreAudio
+      // handles it.
+      // See See http://www.crbug.com/434681 for one example when we can enter
+      // this scope.
+      audio_data_buffer_.reset(new uint8_t[new_size]);
+      audio_buffer->mData = audio_data_buffer_.get();
+    }
+
+    // Update the |mDataByteSize| to match |number_of_frames|.
+    audio_buffer->mDataByteSize = new_size;
+  }
+
+  // Obtain the recorded audio samples by initiating a rendering cycle.
+  // Since it happens on the input bus, the |&audio_buffer_list_| parameter is
+  // a reference to the preallocated audio buffer list that the audio unit
+  // renders into.
+  OSStatus result;
+  if (use_voice_processing_ && format_.mChannelsPerFrame != 1) {
+    // Use the first part of the output buffer for mono data...
+    AudioBufferList mono_buffer_list;
+    mono_buffer_list.mNumberBuffers = 1;
+    AudioBuffer* mono_buffer = mono_buffer_list.mBuffers;
+    mono_buffer->mNumberChannels = 1;
+    mono_buffer->mDataByteSize =
+        audio_buffer->mDataByteSize / audio_buffer->mNumberChannels;
+    mono_buffer->mData = audio_buffer->mData;
+
+    TRACE_EVENT_BEGIN0("audio", "AudioUnitRender");
+    result = AudioUnitRender(audio_unit_, flags, time_stamp, bus_number,
+                             number_of_frames, &mono_buffer_list);
+    TRACE_EVENT_END0("audio", "AudioUnitRender");
+    // ... then upmix it by copying it out to two channels.
+    UpmixMonoToStereoInPlace(audio_buffer, format_.mBitsPerChannel / 8);
+  } else {
+    TRACE_EVENT_BEGIN0("audio", "AudioUnitRender");
+    result = AudioUnitRender(audio_unit_, flags, time_stamp, bus_number,
+                             number_of_frames, &audio_buffer_list_);
+    TRACE_EVENT_END0("audio", "AudioUnitRender");
+  }
+
+  if (result == noErr) {
+    audio_unit_render_has_worked_ = true;
+  }
+  if (result) {
+    TRACE_EVENT_INSTANT0("audio", "AudioUnitRender error",
+                         TRACE_EVENT_SCOPE_THREAD);
+    // Only upload UMA histograms for the case when AGC is enabled. The reason
+    // is that we want to compare these stats with others in this class and
+    // they are only stored for "AGC streams", e.g. WebRTC audio streams.
+    const bool add_uma_histogram = GetAutomaticGainControl();
+    if (add_uma_histogram) {
+      base::UmaHistogramSparse("Media.AudioInputCbErrorMac", result);
+    }
+    OSSTATUS_LOG(ERROR, result) << "AudioUnitRender() failed ";
+    if (result == kAudioUnitErr_TooManyFramesToProcess ||
+        result == kAudioUnitErr_CannotDoInCurrentContext) {
+      DCHECK(!last_success_time_.is_null());
+      // We delay stopping the stream for kAudioUnitErr_TooManyFramesToProcess
+      // since it has been observed that some USB headsets can cause this error
+      // but only for a few initial frames at startup and then then the stream
+      // returns to a stable state again. See b/19524368 for details.
+      // Instead, we measure time since last valid audio frame and call
+      // HandleError() only if a too long error sequence is detected. We do
+      // this to avoid ending up in a non recoverable bad core audio state.
+      // Also including kAudioUnitErr_CannotDoInCurrentContext since long
+      // sequences can be produced in combination with e.g. sample-rate changes
+      // for input devices.
+      base::TimeDelta time_since_last_success =
+          base::TimeTicks::Now() - last_success_time_;
+      if ((time_since_last_success >
+           base::Seconds(kMaxErrorTimeoutInSeconds))) {
+        const char* err = (result == kAudioUnitErr_TooManyFramesToProcess)
+                              ? "kAudioUnitErr_TooManyFramesToProcess"
+                              : "kAudioUnitErr_CannotDoInCurrentContext";
+        LOG(ERROR) << "Too long sequence of " << err << " errors!";
+        HandleError(result);
+      }
+
+      // Add some extra UMA stat to track down if we see this particular error
+      // in combination with a previous change of buffer size "on the fly".
+      if (result == kAudioUnitErr_CannotDoInCurrentContext &&
+          add_uma_histogram) {
+        UMA_HISTOGRAM_BOOLEAN("Media.Audio.RenderFailsWhenBufferSizeChangesMac",
+                              new_buffer_size_detected);
+        UMA_HISTOGRAM_BOOLEAN("Media.Audio.AudioUnitRenderHasWorkedMac",
+                              audio_unit_render_has_worked_);
+      }
+    } else {
+      // We have also seen kAudioUnitErr_NoConnection in some cases. Bailing
+      // out for this error for now.
+      HandleError(result);
+    }
+    return result;
+  }
+  // Update time of successful call to AudioUnitRender().
+  last_success_time_ = base::TimeTicks::Now();
+
+  // Deliver recorded data to the consumer as a callback.
+  return Provide(number_of_frames, &audio_buffer_list_, time_stamp);
+}
+
+OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
+                                     AudioBufferList* io_data,
+                                     const AudioTimeStamp* time_stamp) {
+  TRACE_EVENT1("audio", "AUAudioInputStream::Provide", "number_of_frames",
+               number_of_frames);
+  UpdateCaptureTimestamp(time_stamp);
+  last_number_of_frames_ = number_of_frames;
+
+  // TODO(grunell): We'll only care about the first buffer size change, any
+  // further changes will be ignored. This is in line with output side stats.
+  // It would be nice to have all changes reflected in UMA stats.
+  if (number_of_frames !=
+          static_cast<UInt32>(input_params_.frames_per_buffer()) &&
+      number_of_frames_provided_ == 0)
+    number_of_frames_provided_ = number_of_frames;
+
+  base::TimeTicks capture_time = GetCaptureTime(time_stamp);
+
+  // The AGC volume level is updated once every second on a separate thread.
+  // Note that, |volume| is also updated each time SetVolume() is called
+  // through IPC by the render-side AGC.
+  double normalized_volume = 0.0;
+  GetAgcVolume(&normalized_volume);
+
+  AudioBuffer& buffer = io_data->mBuffers[0];
+  uint8_t* audio_data = reinterpret_cast<uint8_t*>(buffer.mData);
+  DCHECK(audio_data);
+  if (!audio_data)
+    return kAudioUnitErr_InvalidElement;
+
+  // Dynamically increase capacity of the FIFO to handle larger buffers from
+  // CoreAudio. This can happen in combination with Apple Thunderbolt Displays
+  // when the Display Audio is used as capture source and the cable is first
+  // remove and then inserted again.
+  // See http://www.crbug.com/434681 for details.
+  if (static_cast<int>(number_of_frames) > fifo_.GetUnfilledFrames()) {
+    // Derive required increase in number of FIFO blocks. The increase is
+    // typically one block.
+    const int blocks =
+        static_cast<int>((number_of_frames - fifo_.GetUnfilledFrames()) /
+                         input_params_.frames_per_buffer()) +
+        1;
+    DLOG(WARNING) << "Increasing FIFO capacity by " << blocks << " blocks";
+    TRACE_EVENT_INSTANT1("audio", "Increasing FIFO capacity",
+                         TRACE_EVENT_SCOPE_THREAD, "increased by", blocks);
+    fifo_.IncreaseCapacity(blocks);
+  }
+
+  // Compensate the capture time for the FIFO before pushing an new frames.
+  capture_time -= AudioTimestampHelper::FramesToTime(fifo_.GetAvailableFrames(),
+                                                     format_.mSampleRate);
+
+  // Copy captured (and interleaved) data into FIFO.
+  fifo_.Push(audio_data, number_of_frames, format_.mBitsPerChannel / 8);
+
+  // Consume and deliver the data when the FIFO has a block of available data.
+  while (fifo_.available_blocks()) {
+    const AudioBus* audio_bus = fifo_.Consume();
+    DCHECK_EQ(audio_bus->frames(),
+              static_cast<int>(input_params_.frames_per_buffer()));
+
+    sink_->OnData(audio_bus, capture_time, normalized_volume);
+
+    // Move the capture time forward for each vended block.
+    capture_time += AudioTimestampHelper::FramesToTime(audio_bus->frames(),
+                                                       format_.mSampleRate);
+  }
+
+  return noErr;
+}
+
+int AUAudioInputStream::HardwareSampleRate() {
+  // Determine the default input device's sample-rate.
+  AudioDeviceID device_id = kAudioObjectUnknown;
+  UInt32 info_size = sizeof(device_id);
+
+  AudioObjectPropertyAddress default_input_device_address = {
+      kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
+      kAudioObjectPropertyElementMaster};
+  OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                               &default_input_device_address, 0,
+                                               0, &info_size, &device_id);
+  if (result != noErr)
+    return 0.0;
+
+  Float64 nominal_sample_rate;
+  info_size = sizeof(nominal_sample_rate);
+
+  AudioObjectPropertyAddress nominal_sample_rate_address = {
+      kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal,
+      kAudioObjectPropertyElementMaster};
+  result = AudioObjectGetPropertyData(device_id, &nominal_sample_rate_address,
+                                      0, 0, &info_size, &nominal_sample_rate);
+  if (result != noErr)
+    return 0.0;
+
+  return static_cast<int>(nominal_sample_rate);
+}
+
+base::TimeTicks AUAudioInputStream::GetCaptureTime(
+    const AudioTimeStamp* input_time_stamp) {
+  // Total latency is composed by the dynamic latency and the fixed
+  // hardware latency.
+  // https://lists.apple.com/archives/coreaudio-api/2017/Jul/msg00035.html
+  return (input_time_stamp->mFlags & kAudioTimeStampHostTimeValid
+              ? base::TimeTicks::FromMachAbsoluteTime(
+                    input_time_stamp->mHostTime)
+              : base::TimeTicks::Now()) -
+         hardware_latency_;
+}
+
+int AUAudioInputStream::GetNumberOfChannelsFromStream() {
+  // Get the stream format, to be able to read the number of channels.
+  AudioObjectPropertyAddress property_address = {
+      kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput,
+      kAudioObjectPropertyElementMaster};
+  AudioStreamBasicDescription stream_format;
+  UInt32 size = sizeof(stream_format);
+  OSStatus result = AudioObjectGetPropertyData(
+      input_device_id_, &property_address, 0, nullptr, &size, &stream_format);
+  if (result != noErr) {
+    DLOG(WARNING) << "Could not get stream format";
+    return 0;
+  }
+
+  return static_cast<int>(stream_format.mChannelsPerFrame);
+}
+
+bool AUAudioInputStream::IsRunning() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  if (!audio_unit_)
+    return false;
+  UInt32 is_running = 0;
+  UInt32 size = sizeof(is_running);
+  OSStatus error =
+      AudioUnitGetProperty(audio_unit_, kAudioOutputUnitProperty_IsRunning,
+                           kAudioUnitScope_Global, 0, &is_running, &size);
+  OSSTATUS_DLOG_IF(ERROR, error != noErr, error)
+      << "AudioUnitGetProperty(kAudioOutputUnitProperty_IsRunning) failed";
+  DVLOG(1) << "IsRunning: " << is_running;
+  return (error == noErr && is_running);
+}
+
+void AUAudioInputStream::HandleError(OSStatus err) {
+  // Log the latest OSStatus error message and also change the sign of the
+  // error if no callbacks are active. I.e., the sign of the error message
+  // carries one extra level of information.
+  base::UmaHistogramSparse("Media.InputErrorMac",
+                           GetInputCallbackIsActive() ? err : (err * -1));
+  NOTREACHED() << "error " << logging::DescriptionFromOSStatus(err) << " ("
+               << err << ")";
+  if (sink_)
+    sink_->OnError();
+}
+
+bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
+  Boolean is_settable = false;
+  AudioObjectPropertyAddress property_address = {
+      kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
+      static_cast<UInt32>(channel)};
+  OSStatus result = AudioObjectIsPropertySettable(
+      input_device_id_, &property_address, &is_settable);
+  return (result == noErr) ? is_settable : false;
+}
+
+void AUAudioInputStream::SetInputCallbackIsActive(bool enabled) {
+  base::subtle::Release_Store(&input_callback_is_active_, enabled);
+}
+
+bool AUAudioInputStream::GetInputCallbackIsActive() {
+  return (base::subtle::Acquire_Load(&input_callback_is_active_) != false);
+}
+
+void AUAudioInputStream::CheckInputStartupSuccess() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(IsRunning());
+  // Only add UMA stat related to failing input audio for streams where
+  // the AGC has been enabled, e.g. WebRTC audio input streams.
+  if (GetAutomaticGainControl()) {
+    // Check if we have called Start() and input callbacks have actually
+    // started in time as they should. If that is not the case, we have a
+    // problem and the stream is considered dead.
+    const bool input_callback_is_active = GetInputCallbackIsActive();
+    UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartupSuccessMac",
+                          input_callback_is_active);
+    DVLOG(1) << "input_callback_is_active: " << input_callback_is_active;
+    if (!input_callback_is_active) {
+      // Now when we know that startup has failed for some reason, add extra
+      // UMA stats in an attempt to figure out the exact reason.
+      AddHistogramsForFailedStartup();
+    }
+  }
+}
+
+void AUAudioInputStream::CloseAudioUnit() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DVLOG(1) << "CloseAudioUnit";
+  if (!audio_unit_)
+    return;
+  OSStatus result = AudioUnitUninitialize(audio_unit_);
+  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+      << "AudioUnitUninitialize() failed.";
+  result = AudioComponentInstanceDispose(audio_unit_);
+  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+      << "AudioComponentInstanceDispose() failed.";
+  audio_unit_ = 0;
+}
+
+void AUAudioInputStream::AddHistogramsForFailedStartup() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedMac",
+                        buffer_size_was_changed_);
+  // |input_params_.frames_per_buffer()| is set at construction and corresponds
+  // to the requested (by the client) number of audio frames per I/O buffer
+  // connected to the selected input device. Ideally, this size will be the same
+  // as the native I/O buffer size given by |io_buffer_frame_size_|.
+  base::UmaHistogramSparse("Media.Audio.RequestedInputBufferFrameSizeMac",
+                           input_params_.frames_per_buffer());
+  DVLOG(1) << "number_of_frames_: " << input_params_.frames_per_buffer();
+  // This value indicates the number of frames in the IO buffers connected to
+  // the selected input device. It has been set by the audio manger in Open()
+  // and can be the same as |input_params_.frames_per_buffer()|, which is the
+  // desired buffer size. These two values might differ if other streams are
+  // using the same device and any of these streams have asked for a smaller
+  // buffer size.
+  base::UmaHistogramSparse("Media.Audio.ActualInputBufferFrameSizeMac",
+                           io_buffer_frame_size_);
+  DVLOG(1) << "io_buffer_frame_size_: " << io_buffer_frame_size_;
+  // Add information about things like number of logical processors etc.
+  AddSystemInfoToUMA();
+}
+
+void AUAudioInputStream::UpdateCaptureTimestamp(
+    const AudioTimeStamp* timestamp) {
+  if ((timestamp->mFlags & kAudioTimeStampSampleTimeValid) == 0)
+    return;
+
+  if (last_sample_time_) {
+    DCHECK_NE(0U, last_number_of_frames_);
+    UInt32 diff =
+        static_cast<UInt32>(timestamp->mSampleTime - last_sample_time_);
+    if (diff != last_number_of_frames_) {
+      DCHECK_GT(diff, last_number_of_frames_);
+      // We were given samples post what we expected. Update the glitch count
+      // etc. and keep a record of the largest glitch.
+      auto lost_frames = diff - last_number_of_frames_;
+      total_lost_frames_ += lost_frames;
+      if (lost_frames > largest_glitch_frames_)
+        largest_glitch_frames_ = lost_frames;
+      ++glitches_detected_;
+    }
+  }
+
+  // Store the last sample time for use next time we get called back.
+  last_sample_time_ = timestamp->mSampleTime;
+}
+
+void AUAudioInputStream::ReportAndResetStats() {
+  if (last_sample_time_ == 0)
+    return;  // No stats gathered to report.
+
+  // A value of 0 indicates that we got the buffer size we asked for.
+  UMA_HISTOGRAM_COUNTS_10000("Media.Audio.Capture.FramesProvided",
+                             number_of_frames_provided_);
+  // Even if there aren't any glitches, we want to record it to get a feel for
+  // how often we get no glitches vs the alternative.
+  UMA_HISTOGRAM_COUNTS_1M("Media.Audio.Capture.Glitches", glitches_detected_);
+
+  auto lost_frames_ms = (total_lost_frames_ * 1000) / format_.mSampleRate;
+  std::string log_message = base::StringPrintf(
+      "AU in: Total glitches=%d. Total frames lost=%d (%.0lf ms).",
+      glitches_detected_, total_lost_frames_, lost_frames_ms);
+  log_callback_.Run(log_message);
+
+  if (glitches_detected_ != 0) {
+    UMA_HISTOGRAM_LONG_TIMES("Media.Audio.Capture.LostFramesInMs",
+                             base::Milliseconds(lost_frames_ms));
+    auto largest_glitch_ms =
+        (largest_glitch_frames_ * 1000) / format_.mSampleRate;
+    UMA_HISTOGRAM_CUSTOM_TIMES("Media.Audio.Capture.LargestGlitchMs",
+                               base::Milliseconds(largest_glitch_ms),
+                               base::Milliseconds(1), base::Minutes(1), 50);
+    DLOG(WARNING) << log_message;
+  }
+
+  number_of_frames_provided_ = 0;
+  glitches_detected_ = 0;
+  last_sample_time_ = 0;
+  last_number_of_frames_ = 0;
+  total_lost_frames_ = 0;
+  largest_glitch_frames_ = 0;
+}
+
+// TODO(ossu): Ideally, we'd just use the mono stream directly. However, since
+// mono or stereo (may) depend on if we want to run the echo canceller, and
+// since we can't provide two sets of AudioParameters for a device, this is the
+// best we can do right now.
+//
+// The algorithm works by copying a sample at offset N to 2*N and 2*N + 1, e.g.:
+//  ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
+// | a1 | a2 | a3 | b1 | b2 | b3 | c1 | c2 | c3 | -- | -- | -- | -- | -- | ...
+//  ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
+//  into
+//  ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
+// | a1 | a2 | a3 | a1 | a2 | a3 | b1 | b2 | b3 | b1 | b2 | b3 | c1 | c2 | ...
+//  ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
+//
+// To support various different sample sizes, this is done byte-by-byte. Only
+// the first half of the buffer will be used as input. It is expected to contain
+// mono audio. The second half is output only. Since the data is expanding, the
+// algorithm starts copying from the last sample. Otherwise it would overwrite
+// data not already copied.
+void AUAudioInputStream::UpmixMonoToStereoInPlace(AudioBuffer* audio_buffer,
+                                                  int bytes_per_sample) {
+  constexpr int channels = 2;
+  DCHECK_EQ(audio_buffer->mNumberChannels, static_cast<UInt32>(channels));
+  const int total_bytes = audio_buffer->mDataByteSize;
+  const int frames = total_bytes / bytes_per_sample / channels;
+  char* byte_ptr = reinterpret_cast<char*>(audio_buffer->mData);
+  for (int i = frames - 1; i >= 0; --i) {
+    int in_offset = (bytes_per_sample * i);
+    int out_offset = (channels * bytes_per_sample * i);
+    for (int b = 0; b < bytes_per_sample; ++b) {
+      const char byte = byte_ptr[in_offset + b];
+      byte_ptr[out_offset + b] = byte;
+      byte_ptr[out_offset + bytes_per_sample + b] = byte;
+    }
+  }
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/audio_low_latency_input_mac.h b/third_party/chromium/media/audio/mac/audio_low_latency_input_mac.h
new file mode 100644
index 0000000..5ea6c83
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_low_latency_input_mac.h
@@ -0,0 +1,282 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of AudioInputStream for Mac OS X using the special AUHAL
+// input Audio Unit present in OS 10.4 and later.
+// The AUHAL input Audio Unit is for low-latency audio I/O.
+//
+// Overview of operation:
+//
+// - An object of AUAudioInputStream is created by the AudioManager
+//   factory: audio_man->MakeAudioInputStream().
+// - Next some thread will call Open(), at that point the underlying
+//   AUHAL output Audio Unit is created and configured.
+// - Then some thread will call Start(sink).
+//   Then the Audio Unit is started which creates its own thread which
+//   periodically will provide the sink with more data as buffers are being
+//   produced/recorded.
+// - At some point some thread will call Stop(), which we handle by directly
+//   stopping the AUHAL output Audio Unit.
+// - The same thread that called stop will call Close() where we cleanup
+//   and notify the audio manager, which likely will destroy this object.
+//
+// Implementation notes:
+//
+// - It is recommended to first acquire the native sample rate of the default
+//   input device and then use the same rate when creating this object.
+//   Use AUAudioInputStream::HardwareSampleRate() to retrieve the sample rate.
+// - Calling Close() also leads to self destruction.
+// - The latency consists of two parts:
+//   1) Hardware latency, which includes Audio Unit latency, audio device
+//      latency;
+//   2) The delay between the actual recording instant and the time when the
+//      data packet is provided as a callback.
+//
+#ifndef MEDIA_AUDIO_MAC_AUDIO_LOW_LATENCY_INPUT_MAC_H_
+#define MEDIA_AUDIO_MAC_AUDIO_LOW_LATENCY_INPUT_MAC_H_
+
+#include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/CoreAudio.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/atomicops.h"
+#include "base/cancelable_callback.h"
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "media/audio/agc_audio_stream.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/mac/audio_manager_mac.h"
+#include "media/base/audio_block_fifo.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class MEDIA_EXPORT AUAudioInputStream
+    : public AgcAudioStream<AudioInputStream> {
+ public:
+  // The ctor takes all the usual parameters, plus |manager| which is the
+  // the audio manager who is creating this object.
+  AUAudioInputStream(
+      AudioManagerMac* manager,
+      const AudioParameters& input_params,
+      AudioDeviceID audio_device_id,
+      const AudioManager::LogCallback& log_callback,
+      AudioManagerBase::VoiceProcessingMode voice_processing_mode);
+
+  AUAudioInputStream(const AUAudioInputStream&) = delete;
+  AUAudioInputStream& operator=(const AUAudioInputStream&) = delete;
+
+  // The dtor is typically called by the AudioManager only and it is usually
+  // triggered by calling AudioInputStream::Close().
+  ~AUAudioInputStream() override;
+
+  // Implementation of AudioInputStream.
+  AudioInputStream::OpenOutcome Open() override;
+  void Start(AudioInputCallback* callback) override;
+  void Stop() override;
+  void Close() override;
+  double GetMaxVolume() override;
+  void SetVolume(double volume) override;
+  double GetVolume() override;
+  bool IsMuted() override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+  // Returns the current hardware sample rate for the default input device.
+  static int HardwareSampleRate();
+
+  // Returns true if the audio unit is active/running.
+  // The result is based on the kAudioOutputUnitProperty_IsRunning property
+  // which exists for output units.
+  bool IsRunning();
+
+  AudioDeviceID device_id() const { return input_device_id_; }
+  size_t requested_buffer_size() const {
+    return input_params_.frames_per_buffer();
+  }
+  AudioUnit audio_unit() const { return audio_unit_; }
+
+  // Fan out the data from the first half of audio_buffer into interleaved
+  // stereo across the whole of audio_buffer. Public for testing only.
+  static void UpmixMonoToStereoInPlace(AudioBuffer* audio_buffer,
+                                       int bytes_per_sample);
+
+ private:
+  bool OpenAUHAL();
+  bool OpenVoiceProcessingAU();
+
+  // Callback functions called on a real-time priority I/O thread from the audio
+  // unit. These methods are called when recorded audio is available.
+  static OSStatus DataIsAvailable(void* context,
+                                  AudioUnitRenderActionFlags* flags,
+                                  const AudioTimeStamp* time_stamp,
+                                  UInt32 bus_number,
+                                  UInt32 number_of_frames,
+                                  AudioBufferList* io_data);
+  OSStatus OnDataIsAvailable(AudioUnitRenderActionFlags* flags,
+                             const AudioTimeStamp* time_stamp,
+                             UInt32 bus_number,
+                             UInt32 number_of_frames);
+
+  // Pushes recorded data to consumer of the input audio stream.
+  OSStatus Provide(UInt32 number_of_frames,
+                   AudioBufferList* io_data,
+                   const AudioTimeStamp* time_stamp);
+
+  // Gets the current capture time.
+  base::TimeTicks GetCaptureTime(const AudioTimeStamp* input_time_stamp);
+
+  // Gets the number of channels for a stream of audio data.
+  int GetNumberOfChannelsFromStream();
+
+  // Issues the OnError() callback to the |sink_|.
+  void HandleError(OSStatus err);
+
+  // Helper function to check if the volume control is avialable on specific
+  // channel.
+  bool IsVolumeSettableOnChannel(int channel);
+
+  // Helper methods to set and get atomic |input_callback_is_active_|.
+  void SetInputCallbackIsActive(bool active);
+  bool GetInputCallbackIsActive();
+
+  // Checks if a stream was started successfully and the audio unit also starts
+  // to call InputProc() as it should. This method is called once when a timer
+  // expires some time after calling Start().
+  void CheckInputStartupSuccess();
+
+  // Uninitializes the audio unit if needed.
+  void CloseAudioUnit();
+
+  // Reinitializes the AudioUnit to use a new output device.
+  void ReinitializeVoiceProcessingAudioUnit();
+
+  // Adds extra UMA stats when it has been detected that startup failed.
+  void AddHistogramsForFailedStartup();
+
+  // Updates capture timestamp, current lost frames, and total lost frames and
+  // glitches.
+  void UpdateCaptureTimestamp(const AudioTimeStamp* timestamp);
+
+  // Called from the dtor and when the stream is reset.
+  void ReportAndResetStats();
+
+  // Verifies that Open(), Start(), Stop() and Close() are all called on the
+  // creating thread which is the main browser thread (CrBrowserMain) on Mac.
+  base::ThreadChecker thread_checker_;
+
+  // Our creator, the audio manager needs to be notified when we close.
+  AudioManagerMac* const manager_;
+
+  // The audio parameters requested when creating the stream.
+  const AudioParameters input_params_;
+
+  // Stores the number of frames that we actually get callbacks for.
+  // This may be different from what we ask for, so we use this for stats in
+  // order to understand how often this happens and what are the typical values.
+  size_t number_of_frames_provided_;
+
+  // The actual I/O buffer size for the input device connected to the active
+  // AUHAL audio unit.
+  size_t io_buffer_frame_size_;
+
+  // Pointer to the object that will receive the recorded audio samples.
+  AudioInputCallback* sink_;
+
+  // Structure that holds the desired output format of the stream.
+  // Note that, this format can differ from the device(=input) format.
+  AudioStreamBasicDescription format_;
+
+  // The special Audio Unit called AUHAL, which allows us to pass audio data
+  // directly from a microphone, through the HAL, and to our application.
+  // The AUHAL also enables selection of non default devices.
+  AudioUnit audio_unit_;
+
+  // The UID refers to the current input audio device.
+  const AudioDeviceID input_device_id_;
+
+  // Provides a mechanism for encapsulating one or more buffers of audio data.
+  AudioBufferList audio_buffer_list_;
+
+  // Temporary storage for recorded data. The InputProc() renders into this
+  // array as soon as a frame of the desired buffer size has been recorded.
+  std::unique_ptr<uint8_t[]> audio_data_buffer_;
+
+  // Fixed capture hardware latency.
+  base::TimeDelta hardware_latency_;
+
+  // The number of channels in each frame of audio data, which is used
+  // when querying the volume of each channel.
+  int number_of_channels_in_frame_;
+
+  // FIFO used to accumulates recorded data.
+  media::AudioBlockFifo fifo_;
+
+  // Used to defer Start() to workaround http://crbug.com/160920.
+  base::CancelableOnceClosure deferred_start_cb_;
+
+  // Contains time of last successful call to AudioUnitRender().
+  // Initialized first time in Start() and then updated for each valid
+  // audio buffer. Used to detect long error sequences and to take actions
+  // if length of error sequence is above a certain limit.
+  base::TimeTicks last_success_time_;
+
+  // Flags to indicate if we have gotten an input callback.
+  // |got_input_callback_| is only accessed on the OS audio thread in
+  // OnDataIsAvailable() and is set to true when the first callback comes. It
+  // acts as a gate to only set |input_callback_is_active_| atomically once.
+  // |got_input_callback_| is reset to false in Stop() on the main thread. This
+  // is safe since after stopping the audio unit there is no current callback
+  // ongoing and no further callbacks coming.
+  bool got_input_callback_;
+  base::subtle::Atomic32 input_callback_is_active_;
+
+  // Timer which triggers CheckInputStartupSuccess() to verify that input
+  // callbacks have started as intended after a successful call to Start().
+  // This timer lives on the main browser thread.
+  std::unique_ptr<base::OneShotTimer> input_callback_timer_;
+
+  // Set to true if the audio unit's IO buffer was changed when Open() was
+  // called.
+  bool buffer_size_was_changed_;
+
+  // Set to true once when AudioUnitRender() succeeds for the first time.
+  bool audio_unit_render_has_worked_;
+
+  // Set to true when we've successfully called SuppressNoiseReduction to
+  // disable ambient noise reduction.
+  bool noise_reduction_suppressed_;
+
+  // Controls whether or not we use the kAudioUnitSubType_VoiceProcessingIO
+  // voice processing component that provides echo cancellation, ducking
+  // and gain control on Sierra and later.
+  const bool use_voice_processing_;
+
+  // The of the output device to cancel echo from.
+  AudioDeviceID output_device_id_for_aec_;
+
+  // Stores the timestamp of the previous audio buffer provided by the OS.
+  // We use this in combination with |last_number_of_frames_| to detect when
+  // the OS has decided to skip providing frames (i.e. a glitch).
+  // This can happen in case of high CPU load or excessive blocking on the
+  // callback audio thread.
+  // These variables are only touched on the callback thread and then read
+  // in the dtor (when no longer receiving callbacks).
+  // NOTE: Float64 and UInt32 types are used for native API compatibility.
+  Float64 last_sample_time_;
+  UInt32 last_number_of_frames_;
+  UInt32 total_lost_frames_;
+  UInt32 largest_glitch_frames_;
+  int glitches_detected_;
+
+  // Callback to send statistics info.
+  AudioManager::LogCallback log_callback_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_MAC_AUDIO_LOW_LATENCY_INPUT_MAC_H_
diff --git a/third_party/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc b/third_party/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc
new file mode 100644
index 0000000..96287c9
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_low_latency_input_mac_unittest.cc
@@ -0,0 +1,352 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/environment.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_device_info_accessor_for_tests.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/audio_unittest_util.h"
+#include "media/audio/mac/audio_low_latency_input_mac.h"
+#include "media/audio/test_audio_thread.h"
+#include "media/base/seekable_buffer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::Ge;
+using ::testing::NotNull;
+
+namespace media {
+
+ACTION_P4(CheckCountAndPostQuitTask, count, limit, task_runner, closure) {
+  if (++*count >= limit) {
+    task_runner->PostTask(FROM_HERE, closure);
+  }
+}
+
+class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
+ public:
+  MOCK_METHOD3(OnData,
+               void(const AudioBus* src,
+                    base::TimeTicks capture_time,
+                    double volume));
+  MOCK_METHOD0(OnError, void());
+};
+
+// This audio sink implementation should be used for manual tests only since
+// the recorded data is stored on a raw binary data file.
+// The last test (WriteToFileAudioSink) - which is disabled by default -
+// can use this audio sink to store the captured data on a file for offline
+// analysis.
+class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
+ public:
+  // Allocate space for ~10 seconds of data @ 48kHz in stereo:
+  // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
+  static const int kMaxBufferSize = 2 * 2 * 480 * 100 * 10;
+
+  explicit WriteToFileAudioSink(const char* file_name)
+      : buffer_(0, kMaxBufferSize),
+        file_(fopen(file_name, "wb")),
+        bytes_to_write_(0) {
+  }
+
+  ~WriteToFileAudioSink() override {
+    int bytes_written = 0;
+    while (bytes_written < bytes_to_write_) {
+      const uint8_t* chunk;
+      int chunk_size;
+
+      // Stop writing if no more data is available.
+      if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
+        break;
+
+      // Write recorded data chunk to the file and prepare for next chunk.
+      fwrite(chunk, 1, chunk_size, file_);
+      buffer_.Seek(chunk_size);
+      bytes_written += chunk_size;
+    }
+    fclose(file_);
+  }
+
+  // AudioInputStream::AudioInputCallback implementation.
+  void OnData(const AudioBus* src,
+              base::TimeTicks capture_time,
+              double volume) override {
+    const int num_samples = src->frames() * src->channels();
+    std::unique_ptr<int16_t> interleaved(new int16_t[num_samples]);
+    src->ToInterleaved<SignedInt16SampleTypeTraits>(src->frames(),
+                                                    interleaved.get());
+
+    // Store data data in a temporary buffer to avoid making blocking
+    // fwrite() calls in the audio callback. The complete buffer will be
+    // written to file in the destructor.
+    const int bytes_per_sample = sizeof(*interleaved);
+    const int size = bytes_per_sample * num_samples;
+    if (buffer_.Append((const uint8_t*)interleaved.get(), size)) {
+      bytes_to_write_ += size;
+    }
+  }
+
+  void OnError() override {}
+
+ private:
+  media::SeekableBuffer buffer_;
+  FILE* file_;
+  int bytes_to_write_;
+};
+
+class MacAudioInputTest : public testing::Test {
+ protected:
+  MacAudioInputTest()
+      : task_environment_(
+            base::test::SingleThreadTaskEnvironment::MainThreadType::UI),
+        audio_manager_(AudioManager::CreateForTesting(
+            std::make_unique<TestAudioThread>())) {
+    // Wait for the AudioManager to finish any initialization on the audio loop.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  ~MacAudioInputTest() override { audio_manager_->Shutdown(); }
+
+  bool InputDevicesAvailable() {
+#if defined(OS_MAC) && defined(ARCH_CPU_ARM64)
+    // TODO(crbug.com/1128458): macOS on ARM64 says it has devices, but won't
+    // let any of them be opened or listed.
+    return false;
+#else
+    return AudioDeviceInfoAccessorForTests(audio_manager_.get())
+        .HasAudioInputDevices();
+#endif
+  }
+
+  // Convenience method which creates a default AudioInputStream object using
+  // a 10ms frame size and a sample rate which is set to the hardware sample
+  // rate.
+  AudioInputStream* CreateDefaultAudioInputStream() {
+    int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
+    int samples_per_packet = fs / 100;
+    AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
+        AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                        CHANNEL_LAYOUT_STEREO, fs, samples_per_packet),
+        AudioDeviceDescription::kDefaultDeviceId,
+        base::BindRepeating(&MacAudioInputTest::OnLogMessage,
+                            base::Unretained(this)));
+    EXPECT_TRUE(ais);
+    return ais;
+  }
+
+  // Convenience method which creates an AudioInputStream object with a
+  // specified channel layout.
+  AudioInputStream* CreateAudioInputStream(ChannelLayout channel_layout) {
+    int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
+    int samples_per_packet = fs / 100;
+    AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
+        AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
+                        fs, samples_per_packet),
+        AudioDeviceDescription::kDefaultDeviceId,
+        base::BindRepeating(&MacAudioInputTest::OnLogMessage,
+                            base::Unretained(this)));
+    EXPECT_TRUE(ais);
+    return ais;
+  }
+
+  void OnLogMessage(const std::string& message) { log_message_ = message; }
+
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  std::unique_ptr<AudioManager> audio_manager_;
+  std::string log_message_;
+};
+
+// Test Create(), Close().
+TEST_F(MacAudioInputTest, AUAudioInputStreamCreateAndClose) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+  AudioInputStream* ais = CreateDefaultAudioInputStream();
+  ais->Close();
+}
+
+// Test Open(), Close().
+TEST_F(MacAudioInputTest, AUAudioInputStreamOpenAndClose) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+  AudioInputStream* ais = CreateDefaultAudioInputStream();
+  EXPECT_EQ(ais->Open(), AudioInputStream::OpenOutcome::kSuccess);
+  ais->Close();
+}
+
+// Test Open(), Start(), Close().
+TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartAndClose) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+  AudioInputStream* ais = CreateDefaultAudioInputStream();
+  EXPECT_EQ(ais->Open(), AudioInputStream::OpenOutcome::kSuccess);
+  MockAudioInputCallback sink;
+  ais->Start(&sink);
+  ais->Close();
+}
+
+// Test Open(), Start(), Stop(), Close().
+TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartStopAndClose) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+  AudioInputStream* ais = CreateDefaultAudioInputStream();
+  EXPECT_EQ(ais->Open(), AudioInputStream::OpenOutcome::kSuccess);
+  MockAudioInputCallback sink;
+  ais->Start(&sink);
+  ais->Stop();
+  ais->Close();
+}
+
+// Verify that recording starts and stops correctly in mono using mocked sink.
+TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyMonoRecording) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+
+  int count = 0;
+
+  // Create an audio input stream which records in mono.
+  AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_MONO);
+  EXPECT_EQ(ais->Open(), AudioInputStream::OpenOutcome::kSuccess);
+
+  MockAudioInputCallback sink;
+
+  // We use 10ms packets and will run the test until ten packets are received.
+  // All should contain valid packets of the same size and a valid delay
+  // estimate.
+  base::RunLoop run_loop;
+  EXPECT_CALL(sink, OnData(NotNull(), _, _))
+      .Times(AtLeast(10))
+      .WillRepeatedly(CheckCountAndPostQuitTask(
+          &count, 10, task_environment_.GetMainThreadTaskRunner(),
+          run_loop.QuitClosure()));
+  ais->Start(&sink);
+  run_loop.Run();
+  ais->Stop();
+  ais->Close();
+
+  EXPECT_FALSE(log_message_.empty());
+}
+
+// Verify that recording starts and stops correctly in mono using mocked sink.
+TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyStereoRecording) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+
+  int count = 0;
+
+  // Create an audio input stream which records in stereo.
+  AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_STEREO);
+  EXPECT_EQ(ais->Open(), AudioInputStream::OpenOutcome::kSuccess);
+
+  MockAudioInputCallback sink;
+
+  // We use 10ms packets and will run the test until ten packets are received.
+  // All should contain valid packets of the same size and a valid delay
+  // estimate.
+  // TODO(henrika): http://crbug.com/154352 forced us to run the capture side
+  // using a native buffer size of 128 audio frames and combine it with a FIFO
+  // to match the requested size by the client. This change might also have
+  // modified the delay estimates since the existing Ge(bytes_per_packet) for
+  // parameter #4 does no longer pass. I am removing this restriction here to
+  // ensure that we can land the patch but will revisit this test again when
+  // more analysis of the delay estimates are done.
+  base::RunLoop run_loop;
+  EXPECT_CALL(sink, OnData(NotNull(), _, _))
+      .Times(AtLeast(10))
+      .WillRepeatedly(CheckCountAndPostQuitTask(
+          &count, 10, task_environment_.GetMainThreadTaskRunner(),
+          run_loop.QuitClosure()));
+  ais->Start(&sink);
+  run_loop.Run();
+  ais->Stop();
+  ais->Close();
+
+  EXPECT_FALSE(log_message_.empty());
+}
+
+// This test is intended for manual tests and should only be enabled
+// when it is required to store the captured data on a local file.
+// By default, GTest will print out YOU HAVE 1 DISABLED TEST.
+// To include disabled tests in test execution, just invoke the test program
+// with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
+// environment variable to a value greater than 0.
+TEST_F(MacAudioInputTest, DISABLED_AUAudioInputStreamRecordToFile) {
+  ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
+  const char* file_name = "out_stereo_10sec.pcm";
+
+  int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
+  AudioInputStream* ais = CreateDefaultAudioInputStream();
+  EXPECT_EQ(ais->Open(), AudioInputStream::OpenOutcome::kSuccess);
+
+  fprintf(stderr, "               File name  : %s\n", file_name);
+  fprintf(stderr, "               Sample rate: %d\n", fs);
+  WriteToFileAudioSink file_sink(file_name);
+  fprintf(stderr, "               >> Speak into the mic while recording...\n");
+  ais->Start(&file_sink);
+  base::PlatformThread::Sleep(TestTimeouts::action_timeout());
+  ais->Stop();
+  fprintf(stderr, "               >> Recording has stopped.\n");
+  ais->Close();
+}
+
+TEST(MacAudioInputUpmixerTest, Upmix16bit) {
+  constexpr int kNumFrames = 512;
+  constexpr int kBytesPerSample = sizeof(int16_t);
+  int16_t mono[kNumFrames];
+  int16_t stereo[kNumFrames * 2];
+
+  // Fill the mono buffer and the first half of the stereo buffer with data
+  for (int i = 0; i != kNumFrames; ++i) {
+    mono[i] = i;
+    stereo[i] = i;
+  }
+
+  AudioBuffer audio_buffer;
+  audio_buffer.mNumberChannels = 2;
+  audio_buffer.mDataByteSize = kNumFrames * kBytesPerSample * 2;
+  audio_buffer.mData = stereo;
+  AUAudioInputStream::UpmixMonoToStereoInPlace(&audio_buffer, kBytesPerSample);
+
+  // Assert that the samples have been distributed properly
+  for (int i = 0; i != kNumFrames; ++i) {
+    ASSERT_EQ(mono[i], stereo[i * 2]);
+    ASSERT_EQ(mono[i], stereo[i * 2 + 1]);
+  }
+}
+
+TEST(MacAudioInputUpmixerTest, Upmix32bit) {
+  constexpr int kNumFrames = 512;
+  constexpr int kBytesPerSample = sizeof(int32_t);
+  int32_t mono[kNumFrames];
+  int32_t stereo[kNumFrames * 2];
+
+  // Fill the mono buffer and the first half of the stereo buffer with data
+  for (int i = 0; i != kNumFrames; ++i) {
+    mono[i] = i;
+    stereo[i] = i;
+  }
+
+  AudioBuffer audio_buffer;
+  audio_buffer.mNumberChannels = 2;
+  audio_buffer.mDataByteSize = kNumFrames * kBytesPerSample * 2;
+  audio_buffer.mData = stereo;
+  AUAudioInputStream::UpmixMonoToStereoInPlace(&audio_buffer, kBytesPerSample);
+
+  // Assert that the samples have been distributed properly
+  for (int i = 0; i != kNumFrames; ++i) {
+    ASSERT_EQ(mono[i], stereo[i * 2]);
+    ASSERT_EQ(mono[i], stereo[i * 2 + 1]);
+  }
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/audio_manager_mac.cc b/third_party/chromium/media/audio/mac/audio_manager_mac.cc
new file mode 100644
index 0000000..2066a12
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_manager_mac.cc
@@ -0,0 +1,1252 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mac/audio_manager_mac.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/containers/flat_set.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/macros.h"
+#include "base/memory/free_deleter.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_observer.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/mac/audio_auhal_mac.h"
+#include "media/audio/mac/audio_input_mac.h"
+#include "media/audio/mac/audio_low_latency_input_mac.h"
+#include "media/audio/mac/core_audio_util_mac.h"
+#include "media/audio/mac/coreaudio_dispatch_override.h"
+#include "media/audio/mac/scoped_audio_unit.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/audio_timestamp_helper.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/channel_layout.h"
+#include "media/base/limits.h"
+#include "media/base/mac/audio_latency_mac.h"
+#include "media/base/media_switches.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+// Maximum number of output streams that can be open simultaneously.
+static const int kMaxOutputStreams = 50;
+
+// Default sample-rate on most Apple hardware.
+static const int kFallbackSampleRate = 44100;
+
+static bool GetDeviceChannels(AudioUnit audio_unit,
+                              AUElement element,
+                              int* channels);
+
+// Helper method to construct AudioObjectPropertyAddress structure given
+// property selector and scope. The property element is always set to
+// kAudioObjectPropertyElementMaster.
+static AudioObjectPropertyAddress GetAudioObjectPropertyAddress(
+    AudioObjectPropertySelector selector,
+    bool is_input) {
+  AudioObjectPropertyScope scope = is_input ? kAudioObjectPropertyScopeInput
+                                            : kAudioObjectPropertyScopeOutput;
+  AudioObjectPropertyAddress property_address = {
+      selector, scope, kAudioObjectPropertyElementMaster};
+  return property_address;
+}
+
+static const AudioObjectPropertyAddress kNoiseReductionPropertyAddress = {
+    'nzca', kAudioDevicePropertyScopeInput, kAudioObjectPropertyElementMaster};
+
+// Get IO buffer size range from HAL given device id and scope.
+static OSStatus GetIOBufferFrameSizeRange(AudioDeviceID device_id,
+                                          bool is_input,
+                                          UInt32* minimum,
+                                          UInt32* maximum) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  AudioObjectPropertyAddress address = GetAudioObjectPropertyAddress(
+      kAudioDevicePropertyBufferFrameSizeRange, is_input);
+  AudioValueRange range = {0, 0};
+  UInt32 data_size = sizeof(AudioValueRange);
+  OSStatus result = AudioObjectGetPropertyData(device_id, &address, 0, NULL,
+                                               &data_size, &range);
+  if (result != noErr) {
+    OSSTATUS_DLOG(WARNING, result)
+        << "Failed to query IO buffer size range for device: " << std::hex
+        << device_id;
+  } else {
+    *minimum = range.mMinimum;
+    *maximum = range.mMaximum;
+  }
+  return result;
+}
+
+static bool HasAudioHardware(AudioObjectPropertySelector selector) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  AudioDeviceID output_device_id = kAudioObjectUnknown;
+  const AudioObjectPropertyAddress property_address = {
+    selector,
+    kAudioObjectPropertyScopeGlobal,            // mScope
+    kAudioObjectPropertyElementMaster           // mElement
+  };
+  UInt32 output_device_id_size = static_cast<UInt32>(sizeof(output_device_id));
+  OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                            &property_address,
+                                            0,     // inQualifierDataSize
+                                            NULL,  // inQualifierData
+                                            &output_device_id_size,
+                                            &output_device_id);
+  return err == kAudioHardwareNoError &&
+      output_device_id != kAudioObjectUnknown;
+}
+
+static std::string GetAudioDeviceNameFromDeviceId(AudioDeviceID device_id,
+                                                  bool is_input) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  CFStringRef device_name = nullptr;
+  UInt32 data_size = sizeof(device_name);
+  AudioObjectPropertyAddress property_address = GetAudioObjectPropertyAddress(
+      kAudioDevicePropertyDeviceNameCFString, is_input);
+  OSStatus result = AudioObjectGetPropertyData(
+      device_id, &property_address, 0, nullptr, &data_size, &device_name);
+  std::string device;
+  if (result == noErr) {
+    device = base::SysCFStringRefToUTF8(device_name);
+    CFRelease(device_name);
+  }
+  return device;
+}
+
+// Retrieves information on audio devices, and prepends the default
+// device to the list if the list is non-empty.
+static void GetAudioDeviceInfo(bool is_input,
+                               media::AudioDeviceNames* device_names) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  std::vector<AudioObjectID> device_ids =
+      core_audio_mac::GetAllAudioDeviceIDs();
+  for (AudioObjectID device_id : device_ids) {
+    const bool is_valid_for_direction =
+        (is_input ? core_audio_mac::IsInputDevice(device_id)
+                  : core_audio_mac::IsOutputDevice(device_id));
+
+    if (!is_valid_for_direction)
+      continue;
+
+    absl::optional<std::string> unique_id =
+        core_audio_mac::GetDeviceUniqueID(device_id);
+    if (!unique_id)
+      continue;
+
+    absl::optional<std::string> label =
+        core_audio_mac::GetDeviceLabel(device_id, is_input);
+    if (!label)
+      continue;
+
+    // Filter out aggregate devices, e.g. those that get created by using
+    // kAudioUnitSubType_VoiceProcessingIO.
+    if (core_audio_mac::IsPrivateAggregateDevice(device_id))
+      continue;
+
+    device_names->emplace_back(std::move(*label), std::move(*unique_id));
+  }
+
+  if (!device_names->empty()) {
+    // Prepend the default device to the list since we always want it to be
+    // on the top of the list for all platforms. There is no duplicate
+    // counting here since the default device has been abstracted out before.
+    device_names->push_front(media::AudioDeviceName::CreateDefault());
+  }
+}
+
+AudioDeviceID AudioManagerMac::GetAudioDeviceIdByUId(
+    bool is_input,
+    const std::string& device_id) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  AudioObjectPropertyAddress property_address = {
+    kAudioHardwarePropertyDevices,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMaster
+  };
+  AudioDeviceID audio_device_id = kAudioObjectUnknown;
+  UInt32 device_size = sizeof(audio_device_id);
+  OSStatus result = -1;
+
+  if (AudioDeviceDescription::IsDefaultDevice(device_id)) {
+    // Default Device.
+    property_address.mSelector = is_input ?
+        kAudioHardwarePropertyDefaultInputDevice :
+        kAudioHardwarePropertyDefaultOutputDevice;
+
+    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                        &property_address,
+                                        0,
+                                        0,
+                                        &device_size,
+                                        &audio_device_id);
+  } else {
+    // Non-default device.
+    base::ScopedCFTypeRef<CFStringRef> uid(
+        base::SysUTF8ToCFStringRef(device_id));
+    AudioValueTranslation value;
+    value.mInputData = &uid;
+    value.mInputDataSize = sizeof(CFStringRef);
+    value.mOutputData = &audio_device_id;
+    value.mOutputDataSize = device_size;
+    UInt32 translation_size = sizeof(AudioValueTranslation);
+
+    property_address.mSelector = kAudioHardwarePropertyDeviceForUID;
+    result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                        &property_address,
+                                        0,
+                                        0,
+                                        &translation_size,
+                                        &value);
+  }
+
+  if (result) {
+    OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id
+                                   << " for AudioDeviceID";
+  }
+
+  return audio_device_id;
+}
+
+static bool GetDefaultDevice(AudioDeviceID* device, bool input) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  CHECK(device);
+
+  // Obtain the AudioDeviceID of the default input or output AudioDevice.
+  AudioObjectPropertyAddress pa;
+  pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice
+                       : kAudioHardwarePropertyDefaultOutputDevice;
+  pa.mScope = kAudioObjectPropertyScopeGlobal;
+  pa.mElement = kAudioObjectPropertyElementMaster;
+
+  UInt32 size = sizeof(*device);
+  OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0,
+                                               0, &size, device);
+  if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) {
+    DLOG(ERROR) << "Error getting default AudioDevice.";
+    return false;
+  }
+  return true;
+}
+
+bool AudioManagerMac::GetDefaultOutputDevice(AudioDeviceID* device) {
+  return GetDefaultDevice(device, false);
+}
+
+// Returns the total number of channels on a device; regardless of what the
+// device's preferred rendering layout looks like. Should only be used for the
+// channel count when a device has more than kMaxConcurrentChannels.
+static bool GetDeviceTotalChannelCount(AudioDeviceID device,
+                                       AudioObjectPropertyScope scope,
+                                       int* channels) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  CHECK(channels);
+
+  // Get the stream configuration of the device in an AudioBufferList (with the
+  // buffer pointers set to nullptr) which describes the list of streams and the
+  // number of channels in each stream.
+  AudioObjectPropertyAddress pa = {kAudioDevicePropertyStreamConfiguration,
+                                   scope, kAudioObjectPropertyElementMaster};
+
+  UInt32 size;
+  OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size);
+  if (result != noErr || !size)
+    return false;
+
+  std::unique_ptr<uint8_t[]> list_storage(new uint8_t[size]);
+  AudioBufferList* buffer_list =
+      reinterpret_cast<AudioBufferList*>(list_storage.get());
+
+  result = AudioObjectGetPropertyData(device, &pa, 0, 0, &size, buffer_list);
+  if (result != noErr)
+    return false;
+
+  // Determine number of channels based on the AudioBufferList.
+  // |mNumberBuffers] is the  number of interleaved channels in the buffer.
+  // If the number is 1, the buffer is noninterleaved.
+  *channels = 0;
+  for (UInt32 i = 0; i < buffer_list->mNumberBuffers; ++i)
+    *channels += buffer_list->mBuffers[i].mNumberChannels;
+
+  DVLOG(1) << (scope == kAudioDevicePropertyScopeInput ? "Input" : "Output")
+           << " total channels: " << *channels;
+  return true;
+}
+
+// Returns the channel count from the |audio_unit|'s stream format for input
+// scope / input element or output scope / output element.
+static bool GetAudioUnitStreamFormatChannelCount(AudioUnit audio_unit,
+                                                 AUElement element,
+                                                 int* channels) {
+  AudioStreamBasicDescription stream_format;
+  UInt32 size = sizeof(stream_format);
+  OSStatus result =
+      AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat,
+                           element == AUElement::OUTPUT ? kAudioUnitScope_Output
+                                                        : kAudioUnitScope_Input,
+                           element, &stream_format, &size);
+  if (result != noErr) {
+    OSSTATUS_DLOG(ERROR, result) << "Failed to get AudioUnit stream format.";
+    return false;
+  }
+
+  *channels = stream_format.mChannelsPerFrame;
+  return true;
+}
+
+// Returns the channel layout for |device| as provided by the AudioUnit attached
+// to that device matching |element|. Returns true if the count could be pulled
+// from the AudioUnit successfully, false otherwise.
+static bool GetDeviceChannels(AudioDeviceID device,
+                              AUElement element,
+                              int* channels) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  CHECK(channels);
+
+  // For input, get the channel count directly from the AudioUnit's stream
+  // format.
+  // TODO(https://crbug.com/796163): Find out if we can use channel layout on
+  // input element, or confirm that we can't.
+  if (element == AUElement::INPUT) {
+    ScopedAudioUnit au(device, element);
+    if (!au.is_valid())
+      return false;
+
+    if (!GetAudioUnitStreamFormatChannelCount(au.audio_unit(), element,
+                                              channels)) {
+      return false;
+    }
+
+    DVLOG(1) << "Input channels: " << *channels;
+    return true;
+  }
+
+  // For output, use the channel layout to determine channel count.
+  DCHECK(element == AUElement::OUTPUT);
+
+  // If the device has more channels than possible for layouts to express, use
+  // the total count of channels on the device; as of this writing, macOS will
+  // only return up to 8 channels in any layout. To allow WebAudio to work with
+  // > 8 channel devices, we must use the total channel count instead of the
+  // channel count of the preferred layout.
+  int total_channel_count = 0;
+  if (GetDeviceTotalChannelCount(device,
+                                 element == AUElement::OUTPUT
+                                     ? kAudioDevicePropertyScopeOutput
+                                     : kAudioDevicePropertyScopeInput,
+                                 &total_channel_count) &&
+      total_channel_count > kMaxConcurrentChannels) {
+    *channels = total_channel_count;
+    return true;
+  }
+
+  ScopedAudioUnit au(device, element);
+  if (!au.is_valid())
+    return false;
+
+  return GetDeviceChannels(au.audio_unit(), element, channels);
+}
+
+static bool GetDeviceChannels(AudioUnit audio_unit,
+                              AUElement element,
+                              int* channels) {
+  // Attempt to retrieve the channel layout from the AudioUnit.
+  //
+  // Note: We don't use kAudioDevicePropertyPreferredChannelLayout on the device
+  // because it is not available on all devices.
+  UInt32 size;
+  Boolean writable;
+  OSStatus result = AudioUnitGetPropertyInfo(
+      audio_unit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output,
+      element, &size, &writable);
+  if (result != noErr) {
+    OSSTATUS_DLOG(ERROR, result)
+        << "Failed to get property info for AudioUnit channel layout.";
+  }
+
+  std::unique_ptr<uint8_t[]> layout_storage(new uint8_t[size]);
+  AudioChannelLayout* layout =
+      reinterpret_cast<AudioChannelLayout*>(layout_storage.get());
+
+  result =
+      AudioUnitGetProperty(audio_unit, kAudioUnitProperty_AudioChannelLayout,
+                           kAudioUnitScope_Output, element, layout, &size);
+  if (result != noErr) {
+    OSSTATUS_LOG(ERROR, result) << "Failed to get AudioUnit channel layout.";
+    return false;
+  }
+
+  // We don't want to have to know about all channel layout tags, so force OSX
+  // to give us the channel descriptions from the bitmap or tag if necessary.
+  const AudioChannelLayoutTag tag = layout->mChannelLayoutTag;
+  if (tag != kAudioChannelLayoutTag_UseChannelDescriptions) {
+    const bool is_bitmap = tag == kAudioChannelLayoutTag_UseChannelBitmap;
+    const AudioFormatPropertyID fa =
+        is_bitmap ? kAudioFormatProperty_ChannelLayoutForBitmap
+                  : kAudioFormatProperty_ChannelLayoutForTag;
+
+    if (is_bitmap) {
+      result = AudioFormatGetPropertyInfo(fa, sizeof(UInt32),
+                                          &layout->mChannelBitmap, &size);
+    } else {
+      result = AudioFormatGetPropertyInfo(fa, sizeof(AudioChannelLayoutTag),
+                                          &tag, &size);
+    }
+    if (result != noErr || !size) {
+      OSSTATUS_DLOG(ERROR, result)
+          << "Failed to get AudioFormat property info, size=" << size;
+      return false;
+    }
+
+    layout_storage.reset(new uint8_t[size]);
+    layout = reinterpret_cast<AudioChannelLayout*>(layout_storage.get());
+    if (is_bitmap) {
+      result = AudioFormatGetProperty(fa, sizeof(UInt32),
+                                      &layout->mChannelBitmap, &size, layout);
+    } else {
+      result = AudioFormatGetProperty(fa, sizeof(AudioChannelLayoutTag), &tag,
+                                      &size, layout);
+    }
+    if (result != noErr) {
+      OSSTATUS_DLOG(ERROR, result) << "Failed to get AudioFormat property.";
+      return false;
+    }
+  }
+
+  // There is no channel info for stereo, assume so for mono as well.
+  if (layout->mNumberChannelDescriptions <= 2) {
+    *channels = layout->mNumberChannelDescriptions;
+  } else {
+    *channels = 0;
+    for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
+      if (layout->mChannelDescriptions[i].mChannelLabel !=
+          kAudioChannelLabel_Unknown)
+        (*channels)++;
+    }
+  }
+
+  DVLOG(1) << "Output channels: " << *channels;
+  return true;
+}
+
+class AudioManagerMac::AudioPowerObserver : public base::PowerSuspendObserver {
+ public:
+  AudioPowerObserver()
+      : is_suspending_(false),
+        is_monitoring_(base::PowerMonitor::IsInitialized()),
+        num_resume_notifications_(0) {
+    // The PowerMonitor requires significant setup (a CFRunLoop and preallocated
+    // IO ports) so it's not available under unit tests.  See the OSX impl of
+    // base::PowerMonitorDeviceSource for more details.
+    if (!is_monitoring_)
+      return;
+    base::PowerMonitor::AddPowerSuspendObserver(this);
+  }
+
+  AudioPowerObserver(const AudioPowerObserver&) = delete;
+  AudioPowerObserver& operator=(const AudioPowerObserver&) = delete;
+
+  ~AudioPowerObserver() override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    if (!is_monitoring_)
+      return;
+    base::PowerMonitor::RemovePowerSuspendObserver(this);
+  }
+
+  bool IsSuspending() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return is_suspending_;
+  }
+
+  size_t num_resume_notifications() const { return num_resume_notifications_; }
+
+  bool ShouldDeferStreamStart() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    // Start() should be deferred if the system is in the middle of a suspend or
+    // has recently started the process of resuming.
+    return is_suspending_ || base::TimeTicks::Now() < earliest_start_time_;
+  }
+
+  bool IsOnBatteryPower() const {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    return base::PowerMonitor::IsOnBatteryPower();
+  }
+
+ private:
+  void OnSuspend() override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    DVLOG(1) << "OnSuspend";
+    is_suspending_ = true;
+  }
+
+  void OnResume() override {
+    DCHECK(thread_checker_.CalledOnValidThread());
+    DVLOG(1) << "OnResume";
+    ++num_resume_notifications_;
+    is_suspending_ = false;
+    earliest_start_time_ =
+        base::TimeTicks::Now() + base::Seconds(kStartDelayInSecsForPowerEvents);
+  }
+
+  bool is_suspending_;
+  const bool is_monitoring_;
+  base::TimeTicks earliest_start_time_;
+  base::ThreadChecker thread_checker_;
+  size_t num_resume_notifications_;
+};
+
+AudioManagerMac::AudioManagerMac(std::unique_ptr<AudioThread> audio_thread,
+                                 AudioLogFactory* audio_log_factory)
+    : AudioManagerBase(std::move(audio_thread), audio_log_factory),
+      current_sample_rate_(0),
+      current_output_device_(kAudioDeviceUnknown),
+      in_shutdown_(false),
+      weak_ptr_factory_(this) {
+  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
+
+  // PostTask since AudioManager creation may be on the startup path and this
+  // may be slow.
+  GetTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&AudioManagerMac::InitializeOnAudioThread,
+                                weak_ptr_factory_.GetWeakPtr()));
+}
+
+AudioManagerMac::~AudioManagerMac() = default;
+
+void AudioManagerMac::ShutdownOnAudioThread() {
+  // We are now in shutdown mode. This flag disables MaybeChangeBufferSize()
+  // and IncreaseIOBufferSizeIfPossible() which both touches native Core Audio
+  // APIs and they can fail and disrupt tests during shutdown.
+  in_shutdown_ = true;
+
+  // Even if tasks to close the streams are enqueued, they would not run
+  // leading to CHECKs getting hit in the destructor about open streams. Close
+  // them explicitly here. crbug.com/608049.
+  CloseAllInputStreams();
+  CHECK(basic_input_streams_.empty());
+  CHECK(low_latency_input_streams_.empty());
+
+  // Deinitialize power observer on audio thread, since it's initialized on the
+  // audio thread. Typically, constructor/destructor and
+  // InitializeOnAudioThread/ShutdownOnAudioThread are all run on the main
+  // thread, but this might not be true in testing.
+  power_observer_.reset();
+
+  AudioManagerBase::ShutdownOnAudioThread();
+}
+
+bool AudioManagerMac::HasAudioOutputDevices() {
+  return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
+}
+
+bool AudioManagerMac::HasAudioInputDevices() {
+  return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice);
+}
+
+// static
+int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) {
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  Float64 nominal_sample_rate;
+  UInt32 info_size = sizeof(nominal_sample_rate);
+
+  static const AudioObjectPropertyAddress kNominalSampleRateAddress = {
+      kAudioDevicePropertyNominalSampleRate,
+      kAudioObjectPropertyScopeGlobal,
+      kAudioObjectPropertyElementMaster
+  };
+  OSStatus result = AudioObjectGetPropertyData(device_id,
+                                               &kNominalSampleRateAddress,
+                                               0,
+                                               0,
+                                               &info_size,
+                                               &nominal_sample_rate);
+  if (result != noErr) {
+    OSSTATUS_DLOG(WARNING, result)
+        << "Could not get default sample rate for device: " << device_id;
+    return 0;
+  }
+
+  return static_cast<int>(nominal_sample_rate);
+}
+
+// static
+int AudioManagerMac::HardwareSampleRate() {
+  // Determine the default output device's sample-rate.
+  AudioDeviceID device_id = kAudioObjectUnknown;
+  if (!GetDefaultOutputDevice(&device_id))
+    return kFallbackSampleRate;
+
+  return HardwareSampleRateForDevice(device_id);
+}
+
+void AudioManagerMac::GetAudioInputDeviceNames(
+    media::AudioDeviceNames* device_names) {
+  DCHECK(device_names->empty());
+  GetAudioDeviceInfo(true, device_names);
+}
+
+void AudioManagerMac::GetAudioOutputDeviceNames(
+    media::AudioDeviceNames* device_names) {
+  DCHECK(device_names->empty());
+  GetAudioDeviceInfo(false, device_names);
+}
+
+AudioParameters AudioManagerMac::GetInputStreamParameters(
+    const std::string& device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id);
+  if (device == kAudioObjectUnknown) {
+    DLOG(ERROR) << "Invalid device " << device_id;
+    return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                           CHANNEL_LAYOUT_STEREO, kFallbackSampleRate,
+                           ChooseBufferSize(true, kFallbackSampleRate));
+  }
+
+  int channels = 0;
+  ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
+  if (GetDeviceChannels(device, AUElement::INPUT, &channels) && channels <= 2) {
+    channel_layout = GuessChannelLayout(channels);
+  } else {
+    DLOG(ERROR) << "Failed to get the device channels, use stereo as default "
+                << "for device " << device_id;
+  }
+
+  int sample_rate = HardwareSampleRateForDevice(device);
+  if (!sample_rate)
+    sample_rate = kFallbackSampleRate;
+
+  // Due to the sharing of the input and output buffer sizes, we need to choose
+  // the input buffer size based on the output sample rate.  See
+  // http://crbug.com/154352.
+  const int buffer_size = ChooseBufferSize(true, sample_rate);
+
+  // TODO(grunell): query the native channel layout for the specific device.
+  AudioParameters params(
+      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, sample_rate,
+      buffer_size,
+      AudioParameters::HardwareCapabilities(
+          GetMinAudioBufferSizeMacOS(limits::kMinAudioBufferSize, sample_rate),
+          limits::kMaxAudioBufferSize));
+
+  if (DeviceSupportsAmbientNoiseReduction(device)) {
+    params.set_effects(AudioParameters::NOISE_SUPPRESSION);
+  }
+
+  // VoiceProcessingIO is only supported on MacOS 10.12 and cannot be used on
+  // aggregate devices, since it creates an aggregate device itself.  It also
+  // only runs in mono, but we allow upmixing to stereo since we can't claim a
+  // device works either in stereo without echo cancellation or mono with echo
+  // cancellation.
+  if (base::mac::IsAtLeastOS10_12() &&
+      (params.channel_layout() == CHANNEL_LAYOUT_MONO ||
+       params.channel_layout() == CHANNEL_LAYOUT_STEREO) &&
+      core_audio_mac::GetDeviceTransportType(device) !=
+          kAudioDeviceTransportTypeAggregate) {
+    params.set_effects(params.effects() |
+                       AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
+  }
+
+  return params;
+}
+
+std::string AudioManagerMac::GetAssociatedOutputDeviceID(
+    const std::string& input_device_unique_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  AudioObjectID input_device_id =
+      GetAudioDeviceIdByUId(true, input_device_unique_id);
+  if (input_device_id == kAudioObjectUnknown)
+    return std::string();
+
+  std::vector<AudioObjectID> related_device_ids =
+      core_audio_mac::GetRelatedDeviceIDs(input_device_id);
+
+  // Defined as a set as device IDs might be duplicated in
+  // GetRelatedDeviceIDs().
+  base::flat_set<AudioObjectID> related_output_device_ids;
+  for (AudioObjectID device_id : related_device_ids) {
+    if (core_audio_mac::GetNumStreams(device_id, false /* is_input */) > 0)
+      related_output_device_ids.insert(device_id);
+  }
+
+  // Return the device ID if there is only one associated device.
+  // When there are multiple associated devices, we currently do not have a way
+  // to detect if a device (e.g. a digital output device) is actually connected
+  // to an endpoint, so we cannot randomly pick a device.
+  if (related_output_device_ids.size() == 1) {
+    absl::optional<std::string> related_unique_id =
+        core_audio_mac::GetDeviceUniqueID(*related_output_device_ids.begin());
+    if (related_unique_id)
+      return std::move(*related_unique_id);
+  }
+
+  return std::string();
+}
+
+const char* AudioManagerMac::GetName() {
+  return "Mac";
+}
+
+AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
+    const AudioParameters& params,
+    const LogCallback& log_callback) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return MakeLowLatencyOutputStream(params, std::string(), log_callback);
+}
+
+AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  bool device_listener_first_init = false;
+  // Lazily create the audio device listener on the first stream creation,
+  // even if getting an audio device fails. Otherwise, if we have 0 audio
+  // devices, the listener will never be initialized, and new valid devices
+  // will never be detected.
+  if (!output_device_listener_) {
+    // NOTE: Use BindToCurrentLoop() to ensure the callback is always PostTask'd
+    // even if OSX calls us on the right thread.  Some CoreAudio drivers will
+    // fire the callbacks during stream creation, leading to re-entrancy issues
+    // otherwise.  See http://crbug.com/349604
+    output_device_listener_ = std::make_unique<AudioDeviceListenerMac>(
+        BindToCurrentLoop(base::BindRepeating(
+            &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))));
+    device_listener_first_init = true;
+  }
+
+  AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id);
+  if (device == kAudioObjectUnknown) {
+    DLOG(ERROR) << "Failed to open output device: " << device_id;
+    return NULL;
+  }
+
+  // Only set the device and sample rate if we just initialized the device
+  // listener.
+  if (device_listener_first_init) {
+    // Only set the current output device for the default device.
+    if (AudioDeviceDescription::IsDefaultDevice(device_id))
+      current_output_device_ = device;
+    // Just use the current sample rate since we don't allow non-native sample
+    // rates on OSX.
+    current_sample_rate_ = params.sample_rate();
+  }
+
+  AUHALStream* stream = new AUHALStream(this, params, device, log_callback);
+  output_streams_.push_back(stream);
+  return stream;
+}
+
+std::string AudioManagerMac::GetDefaultOutputDeviceID() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return GetDefaultDeviceID(false /* is_input */);
+}
+
+std::string AudioManagerMac::GetDefaultInputDeviceID() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return GetDefaultDeviceID(true /* is_input */);
+}
+
+std::string AudioManagerMac::GetDefaultDeviceID(bool is_input) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  AudioDeviceID device_id = kAudioObjectUnknown;
+  if (!GetDefaultDevice(&device_id, is_input))
+    return std::string();
+
+  const AudioObjectPropertyAddress property_address = {
+    kAudioDevicePropertyDeviceUID,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMaster
+  };
+  CFStringRef device_uid = NULL;
+  UInt32 size = sizeof(device_uid);
+  OSStatus status = AudioObjectGetPropertyData(device_id,
+                                               &property_address,
+                                               0,
+                                               NULL,
+                                               &size,
+                                               &device_uid);
+  if (status != kAudioHardwareNoError || !device_uid)
+    return std::string();
+
+  std::string ret(base::SysCFStringRefToUTF8(device_uid));
+  CFRelease(device_uid);
+
+  return ret;
+}
+
+AudioInputStream* AudioManagerMac::MakeLinearInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+  AudioInputStream* stream = new PCMQueueInAudioInputStream(this, params);
+  basic_input_streams_.push_back(stream);
+  return stream;
+}
+
+AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+  // Gets the AudioDeviceID that refers to the AudioInputDevice with the device
+  // unique id. This AudioDeviceID is used to set the device for Audio Unit.
+  AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id);
+  if (audio_device_id == kAudioObjectUnknown) {
+    return nullptr;
+  }
+
+  VoiceProcessingMode voice_processing_mode =
+      (params.effects() & AudioParameters::ECHO_CANCELLER)
+          ? VoiceProcessingMode::kEnabled
+          : VoiceProcessingMode::kDisabled;
+
+  auto* stream = new AUAudioInputStream(this, params, audio_device_id,
+                                        log_callback, voice_processing_mode);
+  low_latency_input_streams_.push_back(stream);
+  return stream;
+}
+
+AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters(
+    const std::string& output_device_id,
+    const AudioParameters& input_params) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  const AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id);
+  if (device == kAudioObjectUnknown) {
+    DLOG(ERROR) << "Invalid output device " << output_device_id;
+    return input_params.IsValid()
+               ? input_params
+               : AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                                 CHANNEL_LAYOUT_STEREO, kFallbackSampleRate,
+                                 ChooseBufferSize(false, kFallbackSampleRate));
+  }
+
+  const bool has_valid_input_params = input_params.IsValid();
+  const int hardware_sample_rate = HardwareSampleRateForDevice(device);
+
+  // Allow pass through buffer sizes.  If concurrent input and output streams
+  // exist, they will use the smallest buffer size amongst them.  As such, each
+  // stream must be able to FIFO requests appropriately when this happens.
+  int buffer_size;
+  if (has_valid_input_params) {
+    // Ensure the latency asked for is maintained, even if the sample rate is
+    // changed here.
+    const int scaled_buffer_size = input_params.frames_per_buffer() *
+                                   hardware_sample_rate /
+                                   input_params.sample_rate();
+    // If passed in via the input_params we allow buffer sizes to go as
+    // low as the the kMinAudioBufferSize, ignoring what
+    // ChooseBufferSize() normally returns.
+    buffer_size =
+        std::min(static_cast<int>(limits::kMaxAudioBufferSize),
+                 std::max(scaled_buffer_size,
+                          static_cast<int>(limits::kMinAudioBufferSize)));
+  } else {
+    buffer_size = ChooseBufferSize(false, hardware_sample_rate);
+  }
+
+  int hardware_channels;
+  if (!GetDeviceChannels(device, AUElement::OUTPUT, &hardware_channels))
+    hardware_channels = 2;
+
+  // Use the input channel count and channel layout if possible.  Let OSX take
+  // care of remapping the channels; this lets user specified channel layouts
+  // work correctly.
+  int output_channels = input_params.channels();
+  ChannelLayout channel_layout = input_params.channel_layout();
+  if (!has_valid_input_params || output_channels > hardware_channels) {
+    output_channels = hardware_channels;
+    channel_layout = GuessChannelLayout(output_channels);
+    if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED)
+      channel_layout = CHANNEL_LAYOUT_DISCRETE;
+  }
+
+  AudioParameters params(
+      AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
+      hardware_sample_rate, buffer_size,
+      AudioParameters::HardwareCapabilities(
+          GetMinAudioBufferSizeMacOS(limits::kMinAudioBufferSize,
+                                     hardware_sample_rate),
+          limits::kMaxAudioBufferSize));
+  params.set_channels_for_discrete(output_channels);
+  return params;
+}
+
+void AudioManagerMac::InitializeOnAudioThread() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  InitializeCoreAudioDispatchOverride();
+  power_observer_ = std::make_unique<AudioPowerObserver>();
+}
+
+void AudioManagerMac::HandleDeviceChanges() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  const int new_sample_rate = HardwareSampleRate();
+  AudioDeviceID new_output_device;
+  GetDefaultOutputDevice(&new_output_device);
+
+  if (current_sample_rate_ == new_sample_rate &&
+      current_output_device_ == new_output_device) {
+    return;
+  }
+
+  current_sample_rate_ = new_sample_rate;
+  current_output_device_ = new_output_device;
+  NotifyAllOutputDeviceChangeListeners();
+}
+
+int AudioManagerMac::ChooseBufferSize(bool is_input, int sample_rate) {
+  // kMinAudioBufferSize is too small for the output side because
+  // CoreAudio can get into under-run if the renderer fails delivering data
+  // to the browser within the allowed time by the OS. The workaround is to
+  // use 256 samples as the default output buffer size for sample rates
+  // smaller than 96KHz.
+  // TODO(xians): Remove this workaround after WebAudio supports user defined
+  // buffer size.  See https://github.com/WebAudio/web-audio-api/issues/348
+  // for details.
+  int buffer_size =
+      is_input ? limits::kMinAudioBufferSize : 2 * limits::kMinAudioBufferSize;
+  const int user_buffer_size = GetUserBufferSize();
+  buffer_size = user_buffer_size
+                    ? user_buffer_size
+                    : GetMinAudioBufferSizeMacOS(buffer_size, sample_rate);
+  return buffer_size;
+}
+
+bool AudioManagerMac::IsSuspending() const {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return power_observer_->IsSuspending();
+}
+
+bool AudioManagerMac::ShouldDeferStreamStart() const {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return power_observer_->ShouldDeferStreamStart();
+}
+
+bool AudioManagerMac::IsOnBatteryPower() const {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return power_observer_->IsOnBatteryPower();
+}
+
+size_t AudioManagerMac::GetNumberOfResumeNotifications() const {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return power_observer_->num_resume_notifications();
+}
+
+bool AudioManagerMac::MaybeChangeBufferSize(AudioDeviceID device_id,
+                                            AudioUnit audio_unit,
+                                            AudioUnitElement element,
+                                            size_t desired_buffer_size,
+                                            bool* size_was_changed,
+                                            size_t* io_buffer_frame_size) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  if (in_shutdown_) {
+    DVLOG(1) << "Disabled since we are shutting down";
+    return false;
+  }
+  const bool is_input = (element == 1);
+  DVLOG(1) << "MaybeChangeBufferSize(id=0x" << std::hex << device_id
+           << ", is_input=" << is_input << ", desired_buffer_size=" << std::dec
+           << desired_buffer_size << ")";
+
+  *size_was_changed = false;
+  *io_buffer_frame_size = 0;
+
+  // Log the device name (and id) for debugging purposes.
+  std::string device_name = GetAudioDeviceNameFromDeviceId(device_id, is_input);
+  DVLOG(1) << "name: " << device_name << " (ID: 0x" << std::hex << device_id
+           << ")";
+
+  // Get the current size of the I/O buffer for the specified device. The
+  // property is read on a global scope, hence using element 0. The default IO
+  // buffer size on Mac OSX for OS X 10.9 and later is 512 audio frames.
+  UInt32 buffer_size = 0;
+  UInt32 property_size = sizeof(buffer_size);
+  OSStatus result = AudioUnitGetProperty(
+      audio_unit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global,
+      0, &buffer_size, &property_size);
+  if (result != noErr) {
+    OSSTATUS_DLOG(ERROR, result)
+        << "AudioUnitGetProperty(kAudioDevicePropertyBufferFrameSize) failed.";
+    return false;
+  }
+  // Store the currently used (not changed yet) I/O buffer frame size.
+  *io_buffer_frame_size = buffer_size;
+
+  DVLOG(1) << "current IO buffer size: " << buffer_size;
+  DVLOG(1) << "#output streams: " << output_streams_.size();
+  DVLOG(1) << "#input streams: " << low_latency_input_streams_.size();
+
+  // Check if a buffer size change is required. If the caller asks for a
+  // reduced size (|desired_buffer_size| < |buffer_size|), the new lower size
+  // will be set. For larger buffer sizes, we have to perform some checks to
+  // see if the size can actually be changed. If there is any other active
+  // streams on the same device, either input or output, a larger size than
+  // their requested buffer size can't be set. The reason is that an existing
+  // stream can't handle buffer size larger than its requested buffer size.
+  // See http://crbug.com/428706 for a reason why.
+
+  if (buffer_size == desired_buffer_size)
+    return true;
+
+  if (desired_buffer_size > buffer_size) {
+    // Do NOT set the buffer size if there is another output stream using
+    // the same device with a smaller requested buffer size.
+    // Note, for the caller stream, its requested_buffer_size() will be the same
+    // as |desired_buffer_size|, so it won't return true due to comparing with
+    // itself.
+    for (auto* stream : output_streams_) {
+      if (stream->device_id() == device_id &&
+          stream->requested_buffer_size() < desired_buffer_size) {
+        return true;
+      }
+    }
+
+    // Do NOT set the buffer size if there is another input stream using
+    // the same device with a smaller buffer size.
+    for (auto* stream : low_latency_input_streams_) {
+      if (stream->device_id() == device_id &&
+          stream->requested_buffer_size() < desired_buffer_size) {
+        return true;
+      }
+    }
+  }
+
+  // In this scope we know that the IO buffer size should be modified. But
+  // first, verify that |desired_buffer_size| is within the valid range and
+  // modify the desired buffer size if it is outside this range.
+  // Note that, we have found that AudioUnitSetProperty(PropertyBufferFrameSize)
+  // does in fact do this limitation internally and report noErr even if the
+  // user tries to set an invalid size. As an example, asking for a size of
+  // 4410 will on most devices be limited to 4096 without any further notice.
+  UInt32 minimum = buffer_size;
+  UInt32 maximum = buffer_size;
+  result = GetIOBufferFrameSizeRange(device_id, is_input, &minimum, &maximum);
+  if (result != noErr) {
+    // OS error is logged in GetIOBufferFrameSizeRange().
+    return false;
+  }
+  DVLOG(1) << "valid IO buffer size range: [" << minimum << ", " << maximum
+           << "]";
+  buffer_size = desired_buffer_size;
+  if (buffer_size < minimum)
+    buffer_size = minimum;
+  else if (buffer_size > maximum)
+    buffer_size = maximum;
+  DVLOG(1) << "validated desired buffer size: " << buffer_size;
+
+  // Set new (and valid) I/O buffer size for the specified device. The property
+  // is set on a global scope, hence using element 0.
+  result = AudioUnitSetProperty(audio_unit, kAudioDevicePropertyBufferFrameSize,
+                                kAudioUnitScope_Global, 0, &buffer_size,
+                                sizeof(buffer_size));
+  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+      << "AudioUnitSetProperty(kAudioDevicePropertyBufferFrameSize) failed.  "
+      << "Size:: " << buffer_size;
+  *size_was_changed = (result == noErr);
+  DVLOG_IF(1, result == noErr) << "IO buffer size changed to: " << buffer_size;
+  // Store the currently used (after a change) I/O buffer frame size.
+  *io_buffer_frame_size = buffer_size;
+  return result == noErr;
+}
+
+// static
+base::TimeDelta AudioManagerMac::GetHardwareLatency(
+    AudioUnit audio_unit,
+    AudioDeviceID device_id,
+    AudioObjectPropertyScope scope,
+    int sample_rate) {
+  if (!audio_unit || device_id == kAudioObjectUnknown) {
+    DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
+    return base::TimeDelta();
+  }
+
+  // Get audio unit latency.
+  Float64 audio_unit_latency_sec = 0.0;
+  UInt32 size = sizeof(audio_unit_latency_sec);
+  OSStatus result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_Latency,
+                                         kAudioUnitScope_Global, 0,
+                                         &audio_unit_latency_sec, &size);
+  OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
+      << "Could not get audio unit latency";
+
+  // Get audio device latency.
+  AudioObjectPropertyAddress property_address = {
+      kAudioDevicePropertyLatency, scope, kAudioObjectPropertyElementMaster};
+  UInt32 device_latency_frames = 0;
+  size = sizeof(device_latency_frames);
+  result = AudioObjectGetPropertyData(device_id, &property_address, 0, nullptr,
+                                      &size, &device_latency_frames);
+  OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
+      << "Could not get audio device latency.";
+
+  // Retrieve stream ids and take the stream latency from the first stream.
+  // There may be multiple streams with different latencies, but since we're
+  // likely using this delay information for a/v sync we must choose one of
+  // them; Apple recommends just taking the first entry.
+  //
+  // TODO(dalecurtis): Refactor all these "get data size" + "get data" calls
+  // into a common utility function that just returns a std::unique_ptr.
+  UInt32 stream_latency_frames = 0;
+  property_address.mSelector = kAudioDevicePropertyStreams;
+  result = AudioObjectGetPropertyDataSize(device_id, &property_address, 0,
+                                          nullptr, &size);
+  if (result == noErr && size >= sizeof(AudioStreamID)) {
+    std::unique_ptr<uint8_t[]> stream_id_storage(new uint8_t[size]);
+    AudioStreamID* stream_ids =
+        reinterpret_cast<AudioStreamID*>(stream_id_storage.get());
+    result = AudioObjectGetPropertyData(device_id, &property_address, 0,
+                                        nullptr, &size, stream_ids);
+    if (result == noErr) {
+      property_address.mSelector = kAudioStreamPropertyLatency;
+      size = sizeof(stream_latency_frames);
+      result =
+          AudioObjectGetPropertyData(stream_ids[0], &property_address, 0,
+                                     nullptr, &size, &stream_latency_frames);
+      OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
+          << "Could not get stream latency for stream #0.";
+    } else {
+      OSSTATUS_DLOG(WARNING, result)
+          << "Could not get audio device stream ids.";
+    }
+  } else {
+    OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
+        << "Could not get audio device stream ids size.";
+  }
+
+  return base::Seconds(audio_unit_latency_sec) +
+         AudioTimestampHelper::FramesToTime(
+             device_latency_frames + stream_latency_frames, sample_rate);
+}
+
+bool AudioManagerMac::DeviceSupportsAmbientNoiseReduction(
+    AudioDeviceID device_id) {
+  return AudioObjectHasProperty(device_id, &kNoiseReductionPropertyAddress);
+}
+
+bool AudioManagerMac::SuppressNoiseReduction(AudioDeviceID device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(DeviceSupportsAmbientNoiseReduction(device_id));
+  NoiseReductionState& state = device_noise_reduction_states_[device_id];
+  if (state.suppression_count == 0) {
+    UInt32 initially_enabled = 0;
+    UInt32 size = sizeof(initially_enabled);
+    OSStatus result =
+        AudioObjectGetPropertyData(device_id, &kNoiseReductionPropertyAddress,
+                                   0, nullptr, &size, &initially_enabled);
+    if (result != noErr)
+      return false;
+
+    if (initially_enabled) {
+      const UInt32 disable = 0;
+      OSStatus result =
+          AudioObjectSetPropertyData(device_id, &kNoiseReductionPropertyAddress,
+                                     0, nullptr, sizeof(disable), &disable);
+      if (result != noErr) {
+        OSSTATUS_DLOG(WARNING, result)
+            << "Failed to disable ambient noise reduction for device: "
+            << std::hex << device_id;
+      }
+      state.initial_state = NoiseReductionState::ENABLED;
+    } else {
+      state.initial_state = NoiseReductionState::DISABLED;
+    }
+  }
+
+  // Only increase the counter if suppression succeeded or is already active.
+  ++state.suppression_count;
+  return true;
+}
+
+void AudioManagerMac::UnsuppressNoiseReduction(AudioDeviceID device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  NoiseReductionState& state = device_noise_reduction_states_[device_id];
+  DCHECK_NE(state.suppression_count, 0);
+  --state.suppression_count;
+  if (state.suppression_count == 0) {
+    if (state.initial_state == NoiseReductionState::ENABLED) {
+      const UInt32 enable = 1;
+      OSStatus result =
+          AudioObjectSetPropertyData(device_id, &kNoiseReductionPropertyAddress,
+                                     0, nullptr, sizeof(enable), &enable);
+      if (result != noErr) {
+        OSSTATUS_DLOG(WARNING, result)
+            << "Failed to re-enable ambient noise reduction for device: "
+            << std::hex << device_id;
+      }
+    }
+  }
+}
+
+bool AudioManagerMac::AudioDeviceIsUsedForInput(AudioDeviceID device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  if (!basic_input_streams_.empty()) {
+    // For Audio Queues and in the default case (Mac OS X), the audio comes
+    // from the system’s default audio input device as set by a user in System
+    // Preferences.
+    AudioDeviceID default_id;
+    GetDefaultDevice(&default_id, true);
+    if (default_id == device_id)
+      return true;
+  }
+
+  // Each low latency streams has its own device ID.
+  for (auto* stream : low_latency_input_streams_) {
+    if (stream->device_id() == device_id)
+      return true;
+  }
+  return false;
+}
+
+void AudioManagerMac::ReleaseOutputStream(AudioOutputStream* stream) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  output_streams_.remove(static_cast<AUHALStream*>(stream));
+  AudioManagerBase::ReleaseOutputStream(stream);
+}
+
+void AudioManagerMac::ReleaseOutputStreamUsingRealDevice(
+    AudioOutputStream* stream,
+    AudioDeviceID device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  DVLOG(1) << "Closing output stream with id=0x" << std::hex << device_id;
+  DVLOG(1) << "requested_buffer_size: "
+           << static_cast<AUHALStream*>(stream)->requested_buffer_size();
+
+  // Start by closing down the specified output stream.
+  output_streams_.remove(static_cast<AUHALStream*>(stream));
+  AudioManagerBase::ReleaseOutputStream(stream);
+}
+
+void AudioManagerMac::ReleaseInputStream(AudioInputStream* stream) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  auto stream_it = std::find(basic_input_streams_.begin(),
+                             basic_input_streams_.end(),
+                             stream);
+  if (stream_it == basic_input_streams_.end())
+    low_latency_input_streams_.remove(static_cast<AUAudioInputStream*>(stream));
+  else
+    basic_input_streams_.erase(stream_it);
+
+  AudioManagerBase::ReleaseInputStream(stream);
+}
+
+std::unique_ptr<AudioManager> CreateAudioManager(
+    std::unique_ptr<AudioThread> audio_thread,
+    AudioLogFactory* audio_log_factory) {
+  return std::make_unique<AudioManagerMac>(std::move(audio_thread),
+                                           audio_log_factory);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/audio_manager_mac.h b/third_party/chromium/media/audio/mac/audio_manager_mac.h
new file mode 100644
index 0000000..8abe189
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/audio_manager_mac.h
@@ -0,0 +1,209 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_MAC_AUDIO_MANAGER_MAC_H_
+#define MEDIA_AUDIO_MAC_AUDIO_MANAGER_MAC_H_
+
+#include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/AudioHardware.h>
+#include <stddef.h>
+
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/mac/audio_device_listener_mac.h"
+
+namespace media {
+
+class AUAudioInputStream;
+class AUHALStream;
+
+// Mac OS X implementation of the AudioManager singleton. This class is internal
+// to the audio output and only internal users can call methods not exposed by
+// the AudioManager class.
+class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase {
+ public:
+  AudioManagerMac(std::unique_ptr<AudioThread> audio_thread,
+                  AudioLogFactory* audio_log_factory);
+
+  AudioManagerMac(const AudioManagerMac&) = delete;
+  AudioManagerMac& operator=(const AudioManagerMac&) = delete;
+
+  ~AudioManagerMac() override;
+
+  // Implementation of AudioManager.
+  bool HasAudioOutputDevices() override;
+  bool HasAudioInputDevices() override;
+  void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override;
+  void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+  std::string GetAssociatedOutputDeviceID(
+      const std::string& input_device_id) override;
+  const char* GetName() override;
+
+  // Implementation of AudioManagerBase.
+  AudioOutputStream* MakeLinearOutputStream(
+      const AudioParameters& params,
+      const LogCallback& log_callback) override;
+  AudioOutputStream* MakeLowLatencyOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLinearInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLowLatencyInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+
+  std::string GetDefaultInputDeviceID() override;
+  std::string GetDefaultOutputDeviceID() override;
+
+  // Used to track destruction of input and output streams.
+  void ReleaseOutputStream(AudioOutputStream* stream) override;
+  void ReleaseInputStream(AudioInputStream* stream) override;
+
+  // Called by AUHALStream::Close() before releasing the stream.
+  // This method is a special contract between the real stream and the audio
+  // manager and it ensures that we only try to increase the IO buffer size
+  // for real streams and not for fake or mocked streams.
+  void ReleaseOutputStreamUsingRealDevice(AudioOutputStream* stream,
+                                          AudioDeviceID device_id);
+
+  static int HardwareSampleRateForDevice(AudioDeviceID device_id);
+  static int HardwareSampleRate();
+  static bool GetDefaultOutputDevice(AudioDeviceID* device);
+  static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
+                                             const std::string& device_id);
+
+  // OSX has issues with starting streams as the system goes into suspend and
+  // immediately after it wakes up from resume.  See http://crbug.com/160920.
+  // As a workaround we delay Start() when it occurs after suspend and for a
+  // small amount of time after resume.
+  //
+  // Streams should consult ShouldDeferStreamStart() and if true check the value
+  // again after |kStartDelayInSecsForPowerEvents| has elapsed. If false, the
+  // stream may be started immediately.
+  // TODO(henrika): track UMA statistics related to defer start to come up with
+  // a suitable delay value.
+  enum { kStartDelayInSecsForPowerEvents = 5 };
+  bool ShouldDeferStreamStart() const;
+
+  // True if the device is on battery power.
+  bool IsOnBatteryPower() const;
+
+  // Number of times the device has resumed from power suspension.
+  size_t GetNumberOfResumeNotifications() const;
+
+  // True if the device is suspending.
+  bool IsSuspending() const;
+
+  // Changes the I/O buffer size for |device_id| if |desired_buffer_size| is
+  // lower than the current device buffer size. The buffer size can also be
+  // modified under other conditions. See comments in the corresponding cc-file
+  // for more details.
+  // |size_was_changed| is set to true if the device's buffer size was changed
+  // and |io_buffer_frame_size| contains the new buffer size.
+  // Returns false if an error occurred.
+  bool MaybeChangeBufferSize(AudioDeviceID device_id,
+                             AudioUnit audio_unit,
+                             AudioUnitElement element,
+                             size_t desired_buffer_size,
+                             bool* size_was_changed,
+                             size_t* io_buffer_frame_size);
+
+  // Returns the latency for the given audio unit and device. Total latency is
+  // the sum of the latency of the AudioUnit, device, and stream. If any one
+  // component of the latency can't be retrieved it is considered as zero.
+  static base::TimeDelta GetHardwareLatency(AudioUnit audio_unit,
+                                            AudioDeviceID device_id,
+                                            AudioObjectPropertyScope scope,
+                                            int sample_rate);
+
+  // Number of constructed output and input streams.
+  size_t output_streams() const { return output_streams_.size(); }
+  size_t low_latency_input_streams() const {
+    return low_latency_input_streams_.size();
+  }
+  size_t basic_input_streams() const { return basic_input_streams_.size(); }
+
+  bool DeviceSupportsAmbientNoiseReduction(AudioDeviceID device_id);
+  bool SuppressNoiseReduction(AudioDeviceID device_id);
+  void UnsuppressNoiseReduction(AudioDeviceID device_id);
+
+  // The state of a single device for which we've tried to disable Ambient Noise
+  // Reduction. If the device initially has ANR enabled, it will be turned off
+  // as the suppression count goes from 0 to 1 and turned on again as the count
+  // returns to 0.
+  struct NoiseReductionState {
+    enum State { DISABLED, ENABLED };
+    State initial_state = DISABLED;
+    int suppression_count = 0;
+  };
+
+  // Keep track of the devices that we've changed the Ambient Noise Reduction
+  // setting on.
+  std::map<AudioDeviceID, NoiseReductionState> device_noise_reduction_states_;
+
+ protected:
+  AudioParameters GetPreferredOutputStreamParameters(
+      const std::string& output_device_id,
+      const AudioParameters& input_params) override;
+  void ShutdownOnAudioThread() override;
+
+ private:
+  void InitializeOnAudioThread();
+
+  int ChooseBufferSize(bool is_input, int sample_rate);
+
+  // Notify streams of a device change if the default output device or its
+  // sample rate has changed, otherwise does nothing.
+  void HandleDeviceChanges();
+
+  // Returns true if any active input stream is using the specified |device_id|.
+  bool AudioDeviceIsUsedForInput(AudioDeviceID device_id);
+
+  std::string GetDefaultDeviceID(bool is_input);
+
+  std::unique_ptr<AudioDeviceListenerMac> output_device_listener_;
+
+  // Track the output sample-rate and the default output device
+  // so we can intelligently handle device notifications only when necessary.
+  int current_sample_rate_;
+  AudioDeviceID current_output_device_;
+
+  // Helper class which monitors power events to determine if output streams
+  // should defer Start() calls.  Required to workaround an OSX bug.  See
+  // http://crbug.com/160920 for more details.
+  class AudioPowerObserver;
+  std::unique_ptr<AudioPowerObserver> power_observer_;
+
+  // Tracks all constructed input and output streams.
+  // TODO(alokp): We used to track these streams to close before destruction.
+  // We no longer close the streams, so we may be able to get rid of these
+  // member variables. They are currently used by MaybeChangeBufferSize().
+  // Investigate if we can remove these.
+  std::list<AudioInputStream*> basic_input_streams_;
+  std::list<AUAudioInputStream*> low_latency_input_streams_;
+  std::list<AUHALStream*> output_streams_;
+
+  // Set to true in the destructor. Ensures that methods that touches native
+  // Core Audio APIs are not executed during shutdown.
+  bool in_shutdown_;
+
+  base::WeakPtrFactory<AudioManagerMac> weak_ptr_factory_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_MAC_AUDIO_MANAGER_MAC_H_
diff --git a/third_party/chromium/media/audio/mac/core_audio_util_mac.cc b/third_party/chromium/media/audio/mac/core_audio_util_mac.cc
new file mode 100644
index 0000000..8283c1a
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/core_audio_util_mac.cc
@@ -0,0 +1,358 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mac/core_audio_util_mac.h"
+
+#include <IOKit/audio/IOAudioTypes.h>
+
+#include <utility>
+
+#include "base/mac/mac_logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+
+namespace media {
+namespace core_audio_mac {
+
+namespace {
+
+AudioObjectPropertyScope InputOutputScope(bool is_input) {
+  return is_input ? kAudioObjectPropertyScopeInput
+                  : kAudioObjectPropertyScopeOutput;
+}
+
+absl::optional<std::string> GetDeviceStringProperty(
+    AudioObjectID device_id,
+    AudioObjectPropertySelector property_selector) {
+  CFStringRef property_value = nullptr;
+  UInt32 size = sizeof(property_value);
+  AudioObjectPropertyAddress property_address = {
+      property_selector, kAudioObjectPropertyScopeGlobal,
+      kAudioObjectPropertyElementMaster};
+
+  OSStatus result = AudioObjectGetPropertyData(
+      device_id, &property_address, 0 /* inQualifierDataSize */,
+      nullptr /* inQualifierData */, &size, &property_value);
+  if (result != noErr) {
+    OSSTATUS_DLOG(WARNING, result)
+        << "Failed to read string property " << property_selector
+        << " for device " << device_id;
+    return absl::nullopt;
+  }
+
+  if (!property_value)
+    return absl::nullopt;
+
+  std::string device_property = base::SysCFStringRefToUTF8(property_value);
+  CFRelease(property_value);
+
+  return device_property;
+}
+
+absl::optional<uint32_t> GetDeviceUint32Property(
+    AudioObjectID device_id,
+    AudioObjectPropertySelector property_selector,
+    AudioObjectPropertyScope property_scope) {
+  AudioObjectPropertyAddress property_address = {
+      property_selector, property_scope, kAudioObjectPropertyElementMaster};
+  UInt32 property_value;
+  UInt32 size = sizeof(property_value);
+  OSStatus result = AudioObjectGetPropertyData(
+      device_id, &property_address, 0 /* inQualifierDataSize */,
+      nullptr /* inQualifierData */, &size, &property_value);
+  if (result != noErr)
+    return absl::nullopt;
+
+  return property_value;
+}
+
+uint32_t GetDevicePropertySize(AudioObjectID device_id,
+                               AudioObjectPropertySelector property_selector,
+                               AudioObjectPropertyScope property_scope) {
+  AudioObjectPropertyAddress property_address = {
+      property_selector, property_scope, kAudioObjectPropertyElementMaster};
+  UInt32 size = 0;
+  OSStatus result = AudioObjectGetPropertyDataSize(
+      device_id, &property_address, 0 /* inQualifierDataSize */,
+      nullptr /* inQualifierData */, &size);
+  if (result != noErr) {
+    OSSTATUS_DLOG(WARNING, result)
+        << "Failed to read size of property " << property_selector
+        << " for device " << device_id;
+    return 0;
+  }
+  return size;
+}
+
+std::vector<AudioObjectID> GetAudioObjectIDs(
+    AudioObjectID audio_object_id,
+    AudioObjectPropertySelector property_selector) {
+  AudioObjectPropertyAddress property_address = {
+      property_selector, kAudioObjectPropertyScopeGlobal,
+      kAudioObjectPropertyElementMaster};
+  UInt32 size = 0;
+  OSStatus result = AudioObjectGetPropertyDataSize(
+      audio_object_id, &property_address, 0 /* inQualifierDataSize */,
+      nullptr /* inQualifierData */, &size);
+  if (result != noErr) {
+    OSSTATUS_DLOG(WARNING, result)
+        << "Failed to read size of property " << property_selector
+        << " for device/object " << audio_object_id;
+    return {};
+  }
+
+  if (size == 0)
+    return {};
+
+  size_t device_count = size / sizeof(AudioObjectID);
+  // Get the array of device ids for all the devices, which includes both
+  // input devices and output devices.
+  std::vector<AudioObjectID> device_ids(device_count);
+  result = AudioObjectGetPropertyData(
+      audio_object_id, &property_address, 0 /* inQualifierDataSize */,
+      nullptr /* inQualifierData */, &size, device_ids.data());
+  if (result != noErr) {
+    OSSTATUS_DLOG(WARNING, result)
+        << "Failed to read object IDs from property " << property_selector
+        << " for device/object " << audio_object_id;
+    return {};
+  }
+
+  return device_ids;
+}
+
+absl::optional<std::string> GetDeviceName(AudioObjectID device_id) {
+  return GetDeviceStringProperty(device_id, kAudioObjectPropertyName);
+}
+
+absl::optional<std::string> GetDeviceModel(AudioObjectID device_id) {
+  return GetDeviceStringProperty(device_id, kAudioDevicePropertyModelUID);
+}
+
+bool ModelContainsVidPid(const std::string& model) {
+  return model.size() > 10 && model[model.size() - 5] == ':' &&
+         model[model.size() - 10] == ':';
+}
+
+std::string UsbVidPidFromModel(const std::string& model) {
+  return ModelContainsVidPid(model)
+             ? base::ToLowerASCII(model.substr(model.size() - 9))
+             : std::string();
+}
+
+std::string TransportTypeToString(uint32_t transport_type) {
+  switch (transport_type) {
+    case kAudioDeviceTransportTypeBuiltIn:
+      return "Built-in";
+    case kAudioDeviceTransportTypeAggregate:
+      return "Aggregate";
+    case kAudioDeviceTransportTypeAutoAggregate:
+      return "AutoAggregate";
+    case kAudioDeviceTransportTypeVirtual:
+      return "Virtual";
+    case kAudioDeviceTransportTypePCI:
+      return "PCI";
+    case kAudioDeviceTransportTypeUSB:
+      return "USB";
+    case kAudioDeviceTransportTypeFireWire:
+      return "FireWire";
+    case kAudioDeviceTransportTypeBluetooth:
+      return "Bluetooth";
+    case kAudioDeviceTransportTypeBluetoothLE:
+      return "Bluetooth LE";
+    case kAudioDeviceTransportTypeHDMI:
+      return "HDMI";
+    case kAudioDeviceTransportTypeDisplayPort:
+      return "DisplayPort";
+    case kAudioDeviceTransportTypeAirPlay:
+      return "AirPlay";
+    case kAudioDeviceTransportTypeAVB:
+      return "AVB";
+    case kAudioDeviceTransportTypeThunderbolt:
+      return "Thunderbolt";
+    case kAudioDeviceTransportTypeUnknown:
+    default:
+      return std::string();
+  }
+}
+
+absl::optional<std::string> TranslateDeviceSource(AudioObjectID device_id,
+                                                  UInt32 source_id,
+                                                  bool is_input) {
+  CFStringRef source_name = nullptr;
+  AudioValueTranslation translation;
+  translation.mInputData = &source_id;
+  translation.mInputDataSize = sizeof(source_id);
+  translation.mOutputData = &source_name;
+  translation.mOutputDataSize = sizeof(source_name);
+
+  UInt32 translation_size = sizeof(AudioValueTranslation);
+  AudioObjectPropertyAddress property_address = {
+      kAudioDevicePropertyDataSourceNameForIDCFString,
+      InputOutputScope(is_input), kAudioObjectPropertyElementMaster};
+
+  OSStatus result = AudioObjectGetPropertyData(
+      device_id, &property_address, 0 /* inQualifierDataSize */,
+      nullptr /* inQualifierData */, &translation_size, &translation);
+  if (result)
+    return absl::nullopt;
+
+  std::string ret = base::SysCFStringRefToUTF8(source_name);
+  CFRelease(source_name);
+
+  return ret;
+}
+
+}  // namespace
+
+std::vector<AudioObjectID> GetAllAudioDeviceIDs() {
+  return GetAudioObjectIDs(kAudioObjectSystemObject,
+                           kAudioHardwarePropertyDevices);
+}
+
+std::vector<AudioObjectID> GetRelatedDeviceIDs(AudioObjectID device_id) {
+  return GetAudioObjectIDs(device_id, kAudioDevicePropertyRelatedDevices);
+}
+
+absl::optional<std::string> GetDeviceUniqueID(AudioObjectID device_id) {
+  return GetDeviceStringProperty(device_id, kAudioDevicePropertyDeviceUID);
+}
+
+absl::optional<std::string> GetDeviceLabel(AudioObjectID device_id,
+                                           bool is_input) {
+  absl::optional<std::string> device_label;
+  absl::optional<uint32_t> source = GetDeviceSource(device_id, is_input);
+  if (source) {
+    device_label = TranslateDeviceSource(device_id, *source, is_input);
+  }
+
+  if (!device_label) {
+    device_label = GetDeviceName(device_id);
+    if (!device_label)
+      return absl::nullopt;
+  }
+
+  std::string suffix;
+  absl::optional<uint32_t> transport_type = GetDeviceTransportType(device_id);
+  if (transport_type) {
+    if (*transport_type == kAudioDeviceTransportTypeUSB) {
+      absl::optional<std::string> model = GetDeviceModel(device_id);
+      if (model) {
+        suffix = UsbVidPidFromModel(*model);
+      }
+    } else {
+      suffix = TransportTypeToString(*transport_type);
+    }
+  }
+
+  DCHECK(device_label);
+  if (!suffix.empty())
+    *device_label += " (" + suffix + ")";
+
+  return device_label;
+}
+
+uint32_t GetNumStreams(AudioObjectID device_id, bool is_input) {
+  return GetDevicePropertySize(device_id, kAudioDevicePropertyStreams,
+                               InputOutputScope(is_input));
+}
+
+absl::optional<uint32_t> GetDeviceSource(AudioObjectID device_id,
+                                         bool is_input) {
+  return GetDeviceUint32Property(device_id, kAudioDevicePropertyDataSource,
+                                 InputOutputScope(is_input));
+}
+
+absl::optional<uint32_t> GetDeviceTransportType(AudioObjectID device_id) {
+  return GetDeviceUint32Property(device_id, kAudioDevicePropertyTransportType,
+                                 kAudioObjectPropertyScopeGlobal);
+}
+
+bool IsPrivateAggregateDevice(AudioObjectID device_id) {
+  // Don't try to access aggregate device properties unless |device_id| is
+  // really an aggregate device.
+  if (GetDeviceTransportType(device_id) != kAudioDeviceTransportTypeAggregate)
+    return false;
+
+  const AudioObjectPropertyAddress property_address = {
+      kAudioAggregateDevicePropertyComposition, kAudioObjectPropertyScopeGlobal,
+      kAudioObjectPropertyElementMaster};
+  CFDictionaryRef dictionary = nullptr;
+  UInt32 size = sizeof(dictionary);
+  OSStatus result = AudioObjectGetPropertyData(
+      device_id, &property_address, 0 /* inQualifierDataSize */,
+      nullptr /* inQualifierData */, &size, &dictionary);
+
+  if (result != noErr) {
+    OSSTATUS_LOG(WARNING, result) << "Failed to read property "
+                                  << kAudioAggregateDevicePropertyComposition
+                                  << " for device " << device_id;
+    return false;
+  }
+
+  DCHECK_EQ(CFGetTypeID(dictionary), CFDictionaryGetTypeID());
+  bool is_private = false;
+  CFTypeRef value = CFDictionaryGetValue(
+      dictionary, CFSTR(kAudioAggregateDeviceIsPrivateKey));
+
+  if (value && CFGetTypeID(value) == CFNumberGetTypeID()) {
+    int number = 0;
+    if (CFNumberGetValue(reinterpret_cast<CFNumberRef>(value), kCFNumberIntType,
+                         &number)) {
+      is_private = number != 0;
+    }
+  }
+  CFRelease(dictionary);
+
+  return is_private;
+}
+
+bool IsInputDevice(AudioObjectID device_id) {
+  std::vector<AudioObjectID> streams =
+      GetAudioObjectIDs(device_id, kAudioDevicePropertyStreams);
+
+  int num_undefined_input_streams = 0;
+  int num_defined_input_streams = 0;
+  int num_output_streams = 0;
+
+  for (auto stream_id : streams) {
+    auto direction =
+        GetDeviceUint32Property(stream_id, kAudioStreamPropertyDirection,
+                                kAudioObjectPropertyScopeGlobal);
+    DCHECK(direction.has_value());
+    const UInt32 kDirectionOutput = 0;
+    const UInt32 kDirectionInput = 1;
+    if (direction == kDirectionOutput) {
+      ++num_output_streams;
+    } else if (direction == kDirectionInput) {
+      // Filter input streams based on what terminal it claims to be attached
+      // to. Note that INPUT_UNDEFINED comes from a set of terminals declared
+      // in IOKit. CoreAudio defines a number of terminals in
+      // AudioHardwareBase.h but none of them match any of the values I've
+      // seen used in practice, though I've only tested a few devices.
+      auto terminal =
+          GetDeviceUint32Property(stream_id, kAudioStreamPropertyTerminalType,
+                                  kAudioObjectPropertyScopeGlobal);
+      if (terminal.has_value() && terminal == INPUT_UNDEFINED) {
+        ++num_undefined_input_streams;
+      } else {
+        ++num_defined_input_streams;
+      }
+    }
+  }
+
+  // I've only seen INPUT_UNDEFINED introduced by the VoiceProcessing AudioUnit,
+  // but to err on the side of caution, let's allow a device with only undefined
+  // input streams and no output streams as well.
+  return num_defined_input_streams > 0 ||
+         (num_undefined_input_streams > 0 && num_output_streams == 0);
+}
+
+bool IsOutputDevice(AudioObjectID device_id) {
+  return GetNumStreams(device_id, false) > 0;
+}
+
+}  // namespace core_audio_mac
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/core_audio_util_mac.h b/third_party/chromium/media/audio/mac/core_audio_util_mac.h
new file mode 100644
index 0000000..b46243a
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/core_audio_util_mac.h
@@ -0,0 +1,68 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_MAC_CORE_AUDIO_UTIL_MAC_H_
+#define MEDIA_AUDIO_MAC_CORE_AUDIO_UTIL_MAC_H_
+
+#include <CoreAudio/AudioHardware.h>
+
+#include <string>
+#include <vector>
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+namespace core_audio_mac {
+
+// Returns a vector with the IDs of all audio devices in the system.
+// The vector is empty if there are no devices or if there is an error.
+std::vector<AudioObjectID> GetAllAudioDeviceIDs();
+
+// Returns a vector with the IDs of all devices related to the given
+// |device_id|. The vector is empty if there are no related devices or
+// if there is an error.
+std::vector<AudioObjectID> GetRelatedDeviceIDs(AudioObjectID device_id);
+
+// Returns a string with a unique device ID for the given |device_id|, or no
+// value if there is an error.
+absl::optional<std::string> GetDeviceUniqueID(AudioObjectID device_id);
+
+// Returns a string with a descriptive label for the given |device_id|, or no
+// value if there is an error. The returned label is based on several
+// characteristics of the device.
+absl::optional<std::string> GetDeviceLabel(AudioObjectID device_id,
+                                           bool is_input);
+
+// Returns the number of input or output streams associated with the given
+// |device_id|. Returns zero if there are no streams or if there is an error.
+uint32_t GetNumStreams(AudioObjectID device_id, bool is_input);
+
+// Returns the source associated with the given |device_id|, or no value if
+// |device_id| has no source or if there is an error.
+absl::optional<uint32_t> GetDeviceSource(AudioObjectID device_id,
+                                         bool is_input);
+
+// Returns the transport type of the given |device_id|, or no value if
+// |device_id| has no source or if there is an error.
+absl::optional<uint32_t> GetDeviceTransportType(AudioObjectID device_id);
+
+// Returns whether or not the |device_id| corresponds to a private, aggregate
+// device. Such a device gets created by instantiating a VoiceProcessingIO
+// AudioUnit.
+bool IsPrivateAggregateDevice(AudioObjectID device_id);
+
+// Returns whether or not the |device_id| corresponds to a device that has valid
+// input streams. When the VoiceProcessing AudioUnit is active, some output
+// devices get an input stream as well. This function tries to filter those out,
+// based on the value of the stream's kAudioStreamPropertyTerminalType value.
+bool IsInputDevice(AudioObjectID device_id);
+
+// Returns whether or not the |device_id| corresponds to a device with output
+// streams.
+bool IsOutputDevice(AudioObjectID device_id);
+
+}  // namespace core_audio_mac
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_MAC_CORE_AUDIO_UTIL_MAC_H_
diff --git a/third_party/chromium/media/audio/mac/coreaudio_dispatch_override.cc b/third_party/chromium/media/audio/mac/coreaudio_dispatch_override.cc
new file mode 100644
index 0000000..57a700e
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/coreaudio_dispatch_override.cc
@@ -0,0 +1,207 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mac/coreaudio_dispatch_override.h"
+
+#include <dispatch/dispatch.h>
+#include <dlfcn.h>
+#include <mach-o/loader.h>
+
+#include "base/atomicops.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
+
+namespace {
+struct dyld_interpose_tuple {
+  template <typename T>
+  dyld_interpose_tuple(T* replacement, T* replacee)
+      : replacement(reinterpret_cast<const void*>(replacement)),
+        replacee(reinterpret_cast<const void*>(replacee)) {}
+  const void* replacement;
+  const void* replacee;
+};
+
+using DispatchGetGlobalQueueFunc = dispatch_queue_t (*)(long id,
+                                                        unsigned long flags);
+}  // namespace
+
+// This method, and the tuple above, is defined in dyld_priv.h; see:
+// https://github.com/opensource-apple/dyld/blob/master/include/mach-o/dyld_priv.h
+extern "C" void dyld_dynamic_interpose(
+    const struct mach_header* mh,
+    const struct dyld_interpose_tuple array[],
+    size_t count) __attribute__((weak_import));
+
+namespace media {
+namespace {
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum DispatchOverrideInitResult {
+  RESULT_NOT_SUPPORTED = 0,
+  RESULT_INITIALIZED = 1,
+  RESULT_DYNAMIC_INTERPOSE_NOT_FOUND = 2,
+  RESULT_COREAUDIO_DLOPEN_FAILED = 3,
+  RESULT_COREAUDIO_SYMBOL_NOT_FOUND = 4,
+  RESULT_COREAUDIO_MACH_HEADER_NOT_FOUND = 5,
+  RESULT_MAX = RESULT_COREAUDIO_MACH_HEADER_NOT_FOUND
+};
+
+void LogInitResult(DispatchOverrideInitResult result) {
+  UMA_HISTOGRAM_ENUMERATION("Media.Audio.CoreAudioDispatchOverrideInitResult",
+                            result, RESULT_MAX + 1);
+}
+
+enum CallsiteLookupEvent {
+  LOOKUP_MISS = 0,
+  LOOKUP_RESUMEIO_CALLSITE_FOUND = 1,
+  LOOKUP_PAUSEIO_CALLSITE_FOUND = 2,
+  LOOKUP_MAX = LOOKUP_PAUSEIO_CALLSITE_FOUND
+};
+
+#if defined(ARCH_CPU_X86_64)
+void LogCallsiteLookupEvent(CallsiteLookupEvent event) {
+  UMA_HISTOGRAM_ENUMERATION("Media.Audio.CoreAudioDispatchOverrideLookupEvent",
+                            event, LOOKUP_MAX + 1);
+}
+#endif
+
+const char kCoreAudioPath[] =
+    "/System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio";
+
+dispatch_queue_t g_pause_resume_queue = nullptr;
+bool g_dispatch_override_installed = false;
+
+#if defined(ARCH_CPU_X86_64)
+base::subtle::AtomicWord g_resumeio_callsite = 0;
+base::subtle::AtomicWord g_pauseio_callsite = 0;
+
+bool AddressIsPauseOrResume(intptr_t address) {
+  if (address == 0)
+    return false;
+
+  intptr_t resumeio_callsite =
+      base::subtle::NoBarrier_Load(&g_resumeio_callsite);
+
+  if (address == resumeio_callsite)
+    return true;
+
+  intptr_t pauseio_callsite = base::subtle::NoBarrier_Load(&g_pauseio_callsite);
+  if (address == pauseio_callsite)
+    return true;
+
+  if (resumeio_callsite && pauseio_callsite)
+    return false;
+
+  // We don't know both callsites yet, so try to look up the caller.
+  Dl_info info;
+  if (!dladdr(reinterpret_cast<const void*>(address), &info))
+    return false;
+
+  DCHECK_EQ(strcmp(info.dli_fname, kCoreAudioPath), 0);
+
+  // Before Mac OSX 10.10, this code is not applied because dyld is not
+  // available.
+  // From Mac OSX 10.10 to 10.15 (excluded) the target functions that trigger
+  // the interposition are HALC_IOContext_ResumeIO and HALC_IOContext_PauseIO
+  // for respectively resume and pause.
+  // With MacOSX 10.15 the target functions have changed to _XIOContext_ResumeIO
+  // and _XIOContext_PauseIO for respectively resume and pause.
+  if (!resumeio_callsite && info.dli_sname &&
+      (strcmp(info.dli_sname, "HALC_IOContext_ResumeIO") == 0 ||
+       strcmp(info.dli_sname, "_XIOContext_ResumeIO") == 0)) {
+    resumeio_callsite = address;
+    base::subtle::NoBarrier_CompareAndSwap(&g_resumeio_callsite, 0,
+                                           resumeio_callsite);
+    LogCallsiteLookupEvent(LOOKUP_RESUMEIO_CALLSITE_FOUND);
+  } else if (!pauseio_callsite && info.dli_sname &&
+             (strcmp(info.dli_sname, "HALC_IOContext_PauseIO") == 0 ||
+              strcmp(info.dli_sname, "_XIOContext_PauseIO") == 0)) {
+    pauseio_callsite = address;
+    base::subtle::NoBarrier_CompareAndSwap(&g_pauseio_callsite, 0,
+                                           pauseio_callsite);
+    LogCallsiteLookupEvent(LOOKUP_PAUSEIO_CALLSITE_FOUND);
+  } else {
+    LogCallsiteLookupEvent(LOOKUP_MISS);
+  }
+
+  return address == pauseio_callsite || address == resumeio_callsite;
+}
+
+dispatch_queue_t GetGlobalQueueOverride(long identifier, unsigned long flags) {
+  // Get the return address.
+  const intptr_t* rbp = 0;
+  asm("movq %%rbp, %0;" : "=r"(rbp));
+  const intptr_t caller = rbp[1];
+
+  // Check if it's one we should override.
+  if (identifier == DISPATCH_QUEUE_PRIORITY_HIGH &&
+      AddressIsPauseOrResume(caller)) {
+    return g_pause_resume_queue;
+  }
+
+  return dispatch_get_global_queue(identifier, flags);
+}
+#endif  // defined(ARCH_CPU_X86_64)
+
+}  // namespace
+
+bool InitializeCoreAudioDispatchOverride() {
+  if (g_dispatch_override_installed)
+    return true;
+
+  DCHECK_EQ(g_pause_resume_queue, nullptr);
+
+  if (dyld_dynamic_interpose == nullptr) {
+    LOG(ERROR) << "Unable to resolve dyld_dynamic_interpose()";
+    LogInitResult(RESULT_DYNAMIC_INTERPOSE_NOT_FOUND);
+    return false;
+  }
+  // Get CoreAudio handle
+  void* coreaudio = dlopen(kCoreAudioPath, RTLD_LAZY);
+  if (!coreaudio) {
+    LOG(ERROR) << "Could not load CoreAudio while trying to initialize "
+                  "dispatch override";
+    LogInitResult(RESULT_COREAUDIO_DLOPEN_FAILED);
+    return false;
+  }
+  // Retrieve the base address (also address of Mach header). For this
+  // we need any external symbol to look up.
+  const void* symbol = dlsym(coreaudio, "AudioObjectGetPropertyData");
+  if (!symbol) {
+    LOG(ERROR) << "Unable to resolve AudioObjectGetPropertyData in "
+                  "CoreAudio library";
+    LogInitResult(RESULT_COREAUDIO_SYMBOL_NOT_FOUND);
+    return false;
+  }
+  // From the address of that symbol, we can get the address of the library's
+  // header.
+  Dl_info info = {};
+  if (!dladdr(symbol, &info)) {
+    LOG(ERROR) << "Unable to find Mach header for CoreAudio library.";
+    LogInitResult(RESULT_COREAUDIO_MACH_HEADER_NOT_FOUND);
+    return false;
+  }
+
+#if defined(ARCH_CPU_X86_64)
+  const auto* header = reinterpret_cast<const mach_header*>(info.dli_fbase);
+  g_pause_resume_queue =
+      dispatch_queue_create("org.chromium.CoreAudioPauseResumeQueue", nullptr);
+  // The reinterpret_cast<> is needed because in the macOS 10.14 SDK, the return
+  // type of dispatch_get_global_queue changed to return a subtype of
+  // dispatch_queue_t* instead of dispatch_queue_t* itself, and T(*)(...) isn't
+  // automatically converted to U(*)(...) even if U is a superclass of T.
+  dyld_interpose_tuple interposition(
+      &GetGlobalQueueOverride,
+      reinterpret_cast<DispatchGetGlobalQueueFunc>(&dispatch_get_global_queue));
+  dyld_dynamic_interpose(header, &interposition, 1);
+#endif  // defined(ARCH_CPU_X86_64)
+
+  g_dispatch_override_installed = true;
+  LogInitResult(RESULT_INITIALIZED);
+  return true;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/coreaudio_dispatch_override.h b/third_party/chromium/media/audio/mac/coreaudio_dispatch_override.h
new file mode 100644
index 0000000..efd9f8c
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/coreaudio_dispatch_override.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_MAC_COREAUDIO_DISPATCH_OVERRIDE_H_
+#define MEDIA_AUDIO_MAC_COREAUDIO_DISPATCH_OVERRIDE_H_
+
+namespace media {
+// Initializes a CoreAudio hotfix, if supported (macOS >= 10.10).
+// See: http://crbug.com/772410
+// The hotfix overrides calls to dispatch_get_global_queue() from two CoreAudio
+// functions: HALC_IOContext_PauseIO and HALC_IOContext_ResumeIO. These dispatch
+// blocks that should execute in-order, but the global queue does not guarantee
+// this. When the calls execute out-of-order, we stop receiving callbacks for
+// audio streams on one or more devices.
+//
+// To circumvent this problem, these two functions get handed an internal serial
+// queue instead. For all other callers, the override will just defer to the
+// normal dispatch_get_global_queue() implementation.
+//
+// Calls to this function must be serialized. Will do nothing if called when
+// already initialized.
+//
+// Returns true if the hotfix is supported and initialization succeeded, or if
+// it was already initialized; false otherwise.
+bool InitializeCoreAudioDispatchOverride();
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_MAC_COREAUDIO_DISPATCH_OVERRIDE_H_
diff --git a/third_party/chromium/media/audio/mac/scoped_audio_unit.cc b/third_party/chromium/media/audio/mac/scoped_audio_unit.cc
new file mode 100644
index 0000000..e063346
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/scoped_audio_unit.cc
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mac/scoped_audio_unit.h"
+
+#include "base/mac/mac_logging.h"
+
+namespace media {
+
+constexpr AudioComponentDescription desc = {kAudioUnitType_Output,
+                                            kAudioUnitSubType_HALOutput,
+                                            kAudioUnitManufacturer_Apple, 0, 0};
+
+static void DestroyAudioUnit(AudioUnit audio_unit) {
+  OSStatus result = AudioUnitUninitialize(audio_unit);
+  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+      << "AudioUnitUninitialize() failed : " << audio_unit;
+  result = AudioComponentInstanceDispose(audio_unit);
+  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+      << "AudioComponentInstanceDispose() failed : " << audio_unit;
+}
+
+ScopedAudioUnit::ScopedAudioUnit(AudioDeviceID device, AUElement element) {
+  AudioComponent comp = AudioComponentFindNext(0, &desc);
+  if (!comp)
+    return;
+
+  AudioUnit audio_unit;
+  OSStatus result = AudioComponentInstanceNew(comp, &audio_unit);
+  if (result != noErr) {
+    OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed.";
+    return;
+  }
+
+  const UInt32 enable_input_io = element == AUElement::INPUT ? 1 : 0;
+  result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO,
+                                kAudioUnitScope_Input, AUElement::INPUT,
+                                &enable_input_io, sizeof(enable_input_io));
+  if (result != noErr) {
+    OSSTATUS_DLOG(ERROR, result)
+        << "Failed to set input enable IO for audio unit.";
+    DestroyAudioUnit(audio_unit);
+    return;
+  }
+
+  const UInt32 enable_output_io = !enable_input_io;
+  result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO,
+                                kAudioUnitScope_Output, AUElement::OUTPUT,
+                                &enable_output_io, sizeof(enable_output_io));
+  if (result != noErr) {
+    OSSTATUS_DLOG(ERROR, result)
+        << "Failed to set output enable IO for audio unit.";
+    DestroyAudioUnit(audio_unit);
+    return;
+  }
+
+  result = AudioUnitSetProperty(
+      audio_unit, kAudioOutputUnitProperty_CurrentDevice,
+      kAudioUnitScope_Global, 0, &device, sizeof(AudioDeviceID));
+  if (result != noErr) {
+    OSSTATUS_DLOG(ERROR, result)
+        << "Failed to set current device for audio unit.";
+    DestroyAudioUnit(audio_unit);
+    return;
+  }
+
+  audio_unit_ = audio_unit;
+}
+
+ScopedAudioUnit::~ScopedAudioUnit() {
+  if (audio_unit_)
+    DestroyAudioUnit(audio_unit_);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mac/scoped_audio_unit.h b/third_party/chromium/media/audio/mac/scoped_audio_unit.h
new file mode 100644
index 0000000..d688399
--- /dev/null
+++ b/third_party/chromium/media/audio/mac/scoped_audio_unit.h
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_MAC_SCOPED_AUDIO_UNIT_H_
+#define MEDIA_AUDIO_MAC_SCOPED_AUDIO_UNIT_H_
+
+#include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/CoreAudio.h>
+
+#include "base/macros.h"
+
+namespace media {
+
+// For whatever reason Apple doesn't have constants defined for these; per the
+// documentation, we use bus 0 for output and bus 1 for input:
+// http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
+enum AUElement : AudioUnitElement { OUTPUT = 0, INPUT = 1 };
+
+// A helper class that ensures AudioUnits are properly disposed of.
+class ScopedAudioUnit {
+ public:
+  // Creates a new AudioUnit and sets its device for |element| to |device|. If
+  // the operation fails, is_valid() will return false and audio_unit() will
+  // return nullptr.
+  ScopedAudioUnit(AudioDeviceID device, AUElement element);
+
+  ScopedAudioUnit(const ScopedAudioUnit&) = delete;
+  ScopedAudioUnit& operator=(const ScopedAudioUnit&) = delete;
+
+  ~ScopedAudioUnit();
+
+  bool is_valid() const { return audio_unit_ != nullptr; }
+  AudioUnit audio_unit() const { return audio_unit_; }
+
+ private:
+  AudioUnit audio_unit_ = nullptr;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_MAC_SCOPED_AUDIO_UNIT_H_
diff --git a/third_party/chromium/media/audio/mock_audio_debug_recording_manager.cc b/third_party/chromium/media/audio/mock_audio_debug_recording_manager.cc
new file mode 100644
index 0000000..1a38ba2
--- /dev/null
+++ b/third_party/chromium/media/audio/mock_audio_debug_recording_manager.cc
@@ -0,0 +1,17 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mock_audio_debug_recording_manager.h"
+
+#include <utility>
+
+namespace media {
+
+MockAudioDebugRecordingManager::MockAudioDebugRecordingManager(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : AudioDebugRecordingManager(std::move(task_runner)) {}
+
+MockAudioDebugRecordingManager::~MockAudioDebugRecordingManager() = default;
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mock_audio_debug_recording_manager.h b/third_party/chromium/media/audio/mock_audio_debug_recording_manager.h
new file mode 100644
index 0000000..1ef5983
--- /dev/null
+++ b/third_party/chromium/media/audio/mock_audio_debug_recording_manager.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_MOCK_AUDIO_DEBUG_RECORDING_MANAGER_H_
+#define MEDIA_AUDIO_MOCK_AUDIO_DEBUG_RECORDING_MANAGER_H_
+
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "media/audio/audio_debug_recording_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+
+class MockAudioDebugRecordingManager : public AudioDebugRecordingManager {
+ public:
+  explicit MockAudioDebugRecordingManager(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+  MockAudioDebugRecordingManager(const MockAudioDebugRecordingManager&) =
+      delete;
+  MockAudioDebugRecordingManager& operator=(
+      const MockAudioDebugRecordingManager&) = delete;
+
+  ~MockAudioDebugRecordingManager() override;
+
+  MOCK_METHOD1(EnableDebugRecording,
+               void(AudioDebugRecordingManager::CreateWavFileCallback
+                        create_file_callback));
+  MOCK_METHOD0(DisableDebugRecording, void());
+};
+
+}  // namespace media.
+
+#endif  // MEDIA_AUDIO_MOCK_AUDIO_DEBUG_RECORDING_MANAGER_H_
diff --git a/third_party/chromium/media/audio/mock_audio_manager.cc b/third_party/chromium/media/audio/mock_audio_manager.cc
new file mode 100644
index 0000000..a4056b2
--- /dev/null
+++ b/third_party/chromium/media/audio/mock_audio_manager.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mock_audio_manager.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/check.h"
+#include "media/audio/mock_audio_debug_recording_manager.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+MockAudioManager::MockAudioManager(std::unique_ptr<AudioThread> audio_thread)
+    : AudioManager(std::move(audio_thread)) {}
+
+MockAudioManager::~MockAudioManager() = default;
+
+void MockAudioManager::ShutdownOnAudioThread() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+}
+
+bool MockAudioManager::HasAudioOutputDevices() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return has_output_devices_;
+}
+
+bool MockAudioManager::HasAudioInputDevices() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return has_input_devices_;
+}
+
+void MockAudioManager::GetAudioInputDeviceDescriptions(
+    AudioDeviceDescriptions* device_descriptions) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  if (!get_input_device_descriptions_cb_)
+    return;
+  get_input_device_descriptions_cb_.Run(device_descriptions);
+}
+
+void MockAudioManager::GetAudioOutputDeviceDescriptions(
+    AudioDeviceDescriptions* device_descriptions) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  if (!get_output_device_descriptions_cb_)
+    return;
+  get_output_device_descriptions_cb_.Run(device_descriptions);
+}
+
+media::AudioOutputStream* MockAudioManager::MakeAudioOutputStream(
+    const media::AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  return MakeAudioOutputStreamProxy(params, device_id);
+}
+
+media::AudioOutputStream* MockAudioManager::MakeAudioOutputStreamProxy(
+    const media::AudioParameters& params,
+    const std::string& device_id) {
+  return make_output_stream_cb_ ? make_output_stream_cb_.Run(params, device_id)
+                                : nullptr;
+}
+
+media::AudioInputStream* MockAudioManager::MakeAudioInputStream(
+    const media::AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  return make_input_stream_cb_ ? make_input_stream_cb_.Run(params, device_id)
+                               : nullptr;
+}
+
+void MockAudioManager::AddOutputDeviceChangeListener(
+    AudioDeviceListener* listener) {
+}
+
+void MockAudioManager::RemoveOutputDeviceChangeListener(
+    AudioDeviceListener* listener) {
+}
+
+AudioParameters MockAudioManager::GetDefaultOutputStreamParameters() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return default_output_params_;
+}
+
+AudioParameters MockAudioManager::GetOutputStreamParameters(
+      const std::string& device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return output_params_;
+}
+
+AudioParameters MockAudioManager::GetInputStreamParameters(
+    const std::string& device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return input_params_;
+}
+
+std::string MockAudioManager::GetAssociatedOutputDeviceID(
+    const std::string& input_device_id) {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return get_associated_output_device_id_cb_
+             ? get_associated_output_device_id_cb_.Run(input_device_id)
+             : std::string();
+}
+
+std::string MockAudioManager::GetDefaultInputDeviceID() {
+  return std::string();
+}
+std::string MockAudioManager::GetDefaultOutputDeviceID() {
+  return std::string();
+}
+std::string MockAudioManager::GetCommunicationsInputDeviceID() {
+  return std::string();
+}
+std::string MockAudioManager::GetCommunicationsOutputDeviceID() {
+  return std::string();
+}
+
+std::unique_ptr<AudioLog> MockAudioManager::CreateAudioLog(
+    AudioLogFactory::AudioComponent component,
+    int component_id) {
+  return nullptr;
+}
+
+void MockAudioManager::InitializeDebugRecording() {
+  if (!GetTaskRunner()->BelongsToCurrentThread()) {
+    GetTaskRunner()->PostTask(
+        FROM_HERE, base::BindOnce(&MockAudioManager::InitializeDebugRecording,
+                                  base::Unretained(this)));
+    return;
+  }
+
+  DCHECK(!debug_recording_manager_);
+  debug_recording_manager_ =
+      std::make_unique<MockAudioDebugRecordingManager>(GetTaskRunner());
+}
+
+AudioDebugRecordingManager* MockAudioManager::GetAudioDebugRecordingManager() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  return debug_recording_manager_.get();
+}
+
+const char* MockAudioManager::GetName() {
+  return nullptr;
+}
+
+void MockAudioManager::SetMakeOutputStreamCB(MakeOutputStreamCallback cb) {
+  make_output_stream_cb_ = std::move(cb);
+}
+
+void MockAudioManager::SetMakeInputStreamCB(MakeInputStreamCallback cb) {
+  make_input_stream_cb_ = std::move(cb);
+}
+
+void MockAudioManager::SetInputStreamParameters(const AudioParameters& params) {
+  input_params_ = params;
+}
+
+void MockAudioManager::SetOutputStreamParameters(
+    const AudioParameters& params) {
+  output_params_ = params;
+}
+
+void MockAudioManager::SetDefaultOutputStreamParameters(
+    const AudioParameters& params) {
+  default_output_params_ = params;
+}
+
+void MockAudioManager::SetHasInputDevices(bool has_input_devices) {
+  has_input_devices_ = has_input_devices;
+}
+
+void MockAudioManager::SetHasOutputDevices(bool has_output_devices) {
+  has_output_devices_ = has_output_devices;
+}
+
+void MockAudioManager::SetInputDeviceDescriptionsCallback(
+    GetDeviceDescriptionsCallback callback) {
+  get_input_device_descriptions_cb_ = std::move(callback);
+}
+
+void MockAudioManager::SetOutputDeviceDescriptionsCallback(
+    GetDeviceDescriptionsCallback callback) {
+  get_output_device_descriptions_cb_ = std::move(callback);
+}
+
+void MockAudioManager::SetAssociatedOutputDeviceIDCallback(
+    GetAssociatedOutputDeviceIDCallback callback) {
+  get_associated_output_device_id_cb_ = std::move(callback);
+}
+
+}  // namespace media.
diff --git a/third_party/chromium/media/audio/mock_audio_manager.h b/third_party/chromium/media/audio/mock_audio_manager.h
new file mode 100644
index 0000000..b40bb43
--- /dev/null
+++ b/third_party/chromium/media/audio/mock_audio_manager.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_MOCK_AUDIO_MANAGER_H_
+#define MEDIA_AUDIO_MOCK_AUDIO_MANAGER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "media/audio/audio_debug_recording_manager.h"
+#include "media/audio/audio_manager.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+
+// This class is a simple mock around AudioManager, used exclusively for tests,
+// which avoids to use the actual (system and platform dependent) AudioManager.
+// Some bots do not have input devices, thus using the actual AudioManager
+// would causing failures on classes which expect that.
+class MockAudioManager : public AudioManager {
+ public:
+  using GetDeviceDescriptionsCallback =
+      base::RepeatingCallback<void(AudioDeviceDescriptions*)>;
+  using GetAssociatedOutputDeviceIDCallback =
+      base::RepeatingCallback<std::string(const std::string&)>;
+  using MakeOutputStreamCallback =
+      base::RepeatingCallback<media::AudioOutputStream*(
+          const media::AudioParameters& params,
+          const std::string& device_id)>;
+  using MakeInputStreamCallback =
+      base::RepeatingCallback<media::AudioInputStream*(
+          const media::AudioParameters& params,
+          const std::string& device_id)>;
+
+  explicit MockAudioManager(std::unique_ptr<AudioThread> audio_thread);
+
+  MockAudioManager(const MockAudioManager&) = delete;
+  MockAudioManager& operator=(const MockAudioManager&) = delete;
+
+  ~MockAudioManager() override;
+
+  AudioOutputStream* MakeAudioOutputStream(
+      const media::AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+
+  AudioOutputStream* MakeAudioOutputStreamProxy(
+      const media::AudioParameters& params,
+      const std::string& device_id) override;
+
+  AudioInputStream* MakeAudioInputStream(
+      const media::AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+
+  void AddOutputDeviceChangeListener(AudioDeviceListener* listener) override;
+  void RemoveOutputDeviceChangeListener(AudioDeviceListener* listener) override;
+
+  std::unique_ptr<AudioLog> CreateAudioLog(
+      AudioLogFactory::AudioComponent component,
+      int component_id) override;
+
+  void InitializeDebugRecording() override;
+  AudioDebugRecordingManager* GetAudioDebugRecordingManager() override;
+
+  const char* GetName() override;
+
+  // Setters to emulate desired in-test behavior.
+  void SetMakeOutputStreamCB(MakeOutputStreamCallback cb);
+  void SetMakeInputStreamCB(MakeInputStreamCallback cb);
+  void SetInputStreamParameters(const AudioParameters& params);
+  void SetOutputStreamParameters(const AudioParameters& params);
+  void SetDefaultOutputStreamParameters(const AudioParameters& params);
+  void SetHasInputDevices(bool has_input_devices);
+  void SetHasOutputDevices(bool has_output_devices);
+  void SetInputDeviceDescriptionsCallback(
+      GetDeviceDescriptionsCallback callback);
+  void SetOutputDeviceDescriptionsCallback(
+      GetDeviceDescriptionsCallback callback);
+  void SetAssociatedOutputDeviceIDCallback(
+      GetAssociatedOutputDeviceIDCallback callback);
+
+ protected:
+  void ShutdownOnAudioThread() override;
+
+  bool HasAudioOutputDevices() override;
+
+  bool HasAudioInputDevices() override;
+
+  void GetAudioInputDeviceDescriptions(
+      media::AudioDeviceDescriptions* device_descriptions) override;
+
+  void GetAudioOutputDeviceDescriptions(
+      media::AudioDeviceDescriptions* device_descriptions) override;
+
+  AudioParameters GetDefaultOutputStreamParameters() override;
+  AudioParameters GetOutputStreamParameters(
+      const std::string& device_id) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+  std::string GetAssociatedOutputDeviceID(
+      const std::string& input_device_id) override;
+  std::string GetDefaultInputDeviceID() override;
+  std::string GetDefaultOutputDeviceID() override;
+  std::string GetCommunicationsInputDeviceID() override;
+  std::string GetCommunicationsOutputDeviceID() override;
+
+ private:
+  AudioParameters input_params_;
+  AudioParameters output_params_;
+  AudioParameters default_output_params_;
+  bool has_input_devices_ = true;
+  bool has_output_devices_ = true;
+  MakeOutputStreamCallback make_output_stream_cb_;
+  MakeInputStreamCallback make_input_stream_cb_;
+  GetDeviceDescriptionsCallback get_input_device_descriptions_cb_;
+  GetDeviceDescriptionsCallback get_output_device_descriptions_cb_;
+  GetAssociatedOutputDeviceIDCallback get_associated_output_device_id_cb_;
+  std::unique_ptr<AudioDebugRecordingManager> debug_recording_manager_;
+};
+
+}  // namespace media.
+
+#endif  // MEDIA_AUDIO_MOCK_AUDIO_MANAGER_H_
diff --git a/third_party/chromium/media/audio/mock_audio_source_callback.cc b/third_party/chromium/media/audio/mock_audio_source_callback.cc
new file mode 100644
index 0000000..106b755
--- /dev/null
+++ b/third_party/chromium/media/audio/mock_audio_source_callback.cc
@@ -0,0 +1,12 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/mock_audio_source_callback.h"
+
+namespace media {
+
+MockAudioSourceCallback::MockAudioSourceCallback() = default;
+MockAudioSourceCallback::~MockAudioSourceCallback() = default;
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/mock_audio_source_callback.h b/third_party/chromium/media/audio/mock_audio_source_callback.h
new file mode 100644
index 0000000..a01e067
--- /dev/null
+++ b/third_party/chromium/media/audio/mock_audio_source_callback.h
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_MOCK_AUDIO_SOURCE_CALLBACK_H_
+#define MEDIA_AUDIO_MOCK_AUDIO_SOURCE_CALLBACK_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "media/audio/audio_io.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace media {
+
+class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback {
+ public:
+  MockAudioSourceCallback();
+
+  MockAudioSourceCallback(const MockAudioSourceCallback&) = delete;
+  MockAudioSourceCallback& operator=(const MockAudioSourceCallback&) = delete;
+
+  ~MockAudioSourceCallback() override;
+
+  MOCK_METHOD4(OnMoreData,
+               int(base::TimeDelta, base::TimeTicks, int, AudioBus*));
+  MOCK_METHOD1(OnError, void(ErrorType));
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_MOCK_AUDIO_SOURCE_CALLBACK_H_
diff --git a/third_party/chromium/media/audio/null_audio_sink.cc b/third_party/chromium/media/audio/null_audio_sink.cc
new file mode 100644
index 0000000..98c1acd
--- /dev/null
+++ b/third_party/chromium/media/audio/null_audio_sink.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/null_audio_sink.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "media/base/audio_hash.h"
+#include "media/base/fake_audio_worker.h"
+
+namespace media {
+
+NullAudioSink::NullAudioSink(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+    : initialized_(false),
+      started_(false),
+      playing_(false),
+      callback_(nullptr),
+      task_runner_(task_runner) {}
+
+NullAudioSink::~NullAudioSink() = default;
+
+void NullAudioSink::Initialize(const AudioParameters& params,
+                               RenderCallback* callback) {
+  DCHECK(!started_);
+  fake_worker_ = std::make_unique<FakeAudioWorker>(task_runner_, params);
+  fixed_data_delay_ = FakeAudioWorker::ComputeFakeOutputDelay(params);
+  audio_bus_ = AudioBus::Create(params);
+  callback_ = callback;
+  initialized_ = true;
+}
+
+void NullAudioSink::Start() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(initialized_);
+  DCHECK(!started_);
+  started_ = true;
+}
+
+void NullAudioSink::Stop() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  started_ = false;
+  // Stop may be called at any time, so we have to check before stopping.
+  if (fake_worker_)
+    fake_worker_->Stop();
+}
+
+void NullAudioSink::Play() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(started_);
+
+  if (playing_)
+    return;
+
+  fake_worker_->Start(
+      base::BindRepeating(&NullAudioSink::CallRender, base::Unretained(this)));
+
+  playing_ = true;
+}
+
+void NullAudioSink::Pause() {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  DCHECK(started_);
+
+  if (!playing_)
+    return;
+
+  fake_worker_->Stop();
+  playing_ = false;
+}
+
+void NullAudioSink::Flush() {}
+
+bool NullAudioSink::SetVolume(double volume) {
+  // Audio is always muted.
+  return volume == 0.0;
+}
+
+OutputDeviceInfo NullAudioSink::GetOutputDeviceInfo() {
+  return OutputDeviceInfo(OUTPUT_DEVICE_STATUS_OK);
+}
+
+void NullAudioSink::GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) {
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(info_cb), GetOutputDeviceInfo()));
+}
+
+bool NullAudioSink::IsOptimizedForHardwareParameters() {
+  return false;
+}
+
+bool NullAudioSink::CurrentThreadIsRenderingThread() {
+  return task_runner_->BelongsToCurrentThread();
+}
+
+void NullAudioSink::SwitchOutputDevice(const std::string& device_id,
+                                       OutputDeviceStatusCB callback) {
+  std::move(callback).Run(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL);
+}
+
+void NullAudioSink::CallRender(base::TimeTicks ideal_time,
+                               base::TimeTicks now) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+
+  // Since NullAudioSink is only used for cases where a real audio sink was not
+  // available, provide "idealized" delay-timing arguments. This will drive the
+  // smoothest playback (since video is sync'ed to audio). See
+  // content::AudioRendererImpl and media::AudioClock for further details.
+  int frames_received =
+      callback_->Render(fixed_data_delay_, ideal_time, 0, audio_bus_.get());
+  if (!audio_hash_ || frames_received <= 0)
+    return;
+
+  audio_hash_->Update(audio_bus_.get(), frames_received);
+}
+
+void NullAudioSink::StartAudioHashForTesting() {
+  DCHECK(!initialized_);
+  audio_hash_ = std::make_unique<AudioHash>();
+}
+
+std::string NullAudioSink::GetAudioHashForTesting() {
+  return audio_hash_ ? audio_hash_->ToString() : std::string();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/null_audio_sink.h b/third_party/chromium/media/audio/null_audio_sink.h
new file mode 100644
index 0000000..1db35b9
--- /dev/null
+++ b/third_party/chromium/media/audio/null_audio_sink.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_NULL_AUDIO_SINK_H_
+#define MEDIA_AUDIO_NULL_AUDIO_SINK_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "media/base/audio_renderer_sink.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+class AudioBus;
+class AudioHash;
+class FakeAudioWorker;
+
+class MEDIA_EXPORT NullAudioSink : public SwitchableAudioRendererSink {
+ public:
+  explicit NullAudioSink(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+
+  // AudioRendererSink implementation.
+  void Initialize(const AudioParameters& params,
+                  RenderCallback* callback) override;
+  void Start() override;
+  void Stop() override;
+  void Pause() override;
+  void Play() override;
+  void Flush() override;
+  bool SetVolume(double volume) override;
+  OutputDeviceInfo GetOutputDeviceInfo() override;
+  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
+  bool IsOptimizedForHardwareParameters() override;
+  bool CurrentThreadIsRenderingThread() override;
+  void SwitchOutputDevice(const std::string& device_id,
+                          OutputDeviceStatusCB callback) override;
+
+  // Enables audio frame hashing.  Must be called prior to Initialize().
+  void StartAudioHashForTesting();
+
+  // Returns the hash of all audio frames seen since construction.
+  std::string GetAudioHashForTesting();
+
+ protected:
+  ~NullAudioSink() override;
+
+ private:
+  // Task that periodically calls Render() to consume audio data.
+  void CallRender(base::TimeTicks ideal_time, base::TimeTicks now);
+
+  bool initialized_;
+  bool started_;
+  bool playing_;
+  RenderCallback* callback_;
+
+  // Controls whether or not a running hash is computed for audio frames.
+  std::unique_ptr<AudioHash> audio_hash_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  std::unique_ptr<FakeAudioWorker> fake_worker_;
+  base::TimeDelta fixed_data_delay_;
+  std::unique_ptr<AudioBus> audio_bus_;
+
+  DISALLOW_COPY_AND_ASSIGN(NullAudioSink);
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_NULL_AUDIO_SINK_H_
diff --git a/third_party/chromium/media/audio/power_observer_helper.cc b/third_party/chromium/media/audio/power_observer_helper.cc
new file mode 100644
index 0000000..187ca85
--- /dev/null
+++ b/third_party/chromium/media/audio/power_observer_helper.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/power_observer_helper.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/power_monitor/power_monitor.h"
+
+namespace media {
+
+PowerObserverHelper::PowerObserverHelper(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    base::RepeatingClosure suspend_callback,
+    base::RepeatingClosure resume_callback)
+    : task_runner_(std::move(task_runner)),
+      suspend_callback_(std::move(suspend_callback)),
+      resume_callback_(std::move(resume_callback)) {
+  DCHECK(!suspend_callback_.is_null());
+  DCHECK(!resume_callback_.is_null());
+
+  // The PowerMonitor requires significant setup (a CFRunLoop and preallocated
+  // IO ports) so it's not available under unit tests.  See the OSX impl of
+  // base::PowerMonitorDeviceSource for more details.
+  // TODO(grunell): We could be suspending when adding this as observer, and
+  // we won't be notified about that. See if we can add
+  // PowerMonitorSource::IsSuspending() so that this can be checked here.
+  base::PowerMonitor::AddPowerSuspendObserver(this);
+}
+
+PowerObserverHelper::~PowerObserverHelper() {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  base::PowerMonitor::RemovePowerSuspendObserver(this);
+}
+
+bool PowerObserverHelper::IsSuspending() const {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  return is_suspending_;
+}
+
+void PowerObserverHelper::OnSuspend() {
+  DVLOG(1) << "OnSuspend";
+  if (!task_runner_->RunsTasksInCurrentSequence()) {
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(&PowerObserverHelper::OnSuspend,
+                                          weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  is_suspending_ = true;
+  suspend_callback_.Run();
+}
+
+void PowerObserverHelper::OnResume() {
+  DVLOG(1) << "OnResume";
+  if (!task_runner_->RunsTasksInCurrentSequence()) {
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(&PowerObserverHelper::OnResume,
+                                          weak_factory_.GetWeakPtr()));
+    return;
+  }
+
+  is_suspending_ = false;
+  resume_callback_.Run();
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/power_observer_helper.h b/third_party/chromium/media/audio/power_observer_helper.h
new file mode 100644
index 0000000..beb263c
--- /dev/null
+++ b/third_party/chromium/media/audio/power_observer_helper.h
@@ -0,0 +1,76 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_POWER_OBSERVER_HELPER_H_
+#define MEDIA_AUDIO_POWER_OBSERVER_HELPER_H_
+
+#include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/power_monitor/power_observer.h"
+#include "base/sequenced_task_runner.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Helper class that implements PowerSuspendObserver and handles threading. A
+// task runner is given, on which suspend and resume notification callbacks are
+// run. It also provides a function to check if we are suspending on the task
+// runner.
+// Note that on Linux suspend/resume information is not supported.
+class MEDIA_EXPORT PowerObserverHelper : public base::PowerSuspendObserver {
+ public:
+  PowerObserverHelper(scoped_refptr<base::SequencedTaskRunner> task_runner,
+                      base::RepeatingClosure suspend_callback,
+                      base::RepeatingClosure resume_callback);
+
+  PowerObserverHelper(const PowerObserverHelper&) = delete;
+  PowerObserverHelper& operator=(const PowerObserverHelper&) = delete;
+
+  ~PowerObserverHelper() override;
+
+  // Must be called on |task_runner|.
+  virtual bool IsSuspending() const;
+
+ protected:
+  base::SequencedTaskRunner* TaskRunnerForTesting() const {
+    return task_runner_.get();
+  }
+
+  base::RepeatingClosure* SuspendCallbackForTesting() {
+    return &suspend_callback_;
+  }
+
+  base::RepeatingClosure* ResumeCallbackForTesting() {
+    return &resume_callback_;
+  }
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(PowerObserverHelperTest,
+                           SuspendAndResumeNotificationsTwice);
+  FRIEND_TEST_ALL_PREFIXES(PowerObserverHelperTest,
+                           TwoSuspendAndTwoResumeNotifications);
+
+  // The task runner on which |is_suspending_| should live and the callbacks
+  // should be run on.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  // Suspend and resume callbacks. Run on |task_runner_|.
+  base::RepeatingClosure suspend_callback_;
+  base::RepeatingClosure resume_callback_;
+
+  // base::PowerSuspendObserver implementation.
+  void OnSuspend() override;
+  void OnResume() override;
+
+  // Flag if we are suspending.
+  bool is_suspending_ = false;
+
+  base::WeakPtrFactory<PowerObserverHelper> weak_factory_{this};
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_POWER_OBSERVER_HELPER_H_
diff --git a/third_party/chromium/media/audio/power_observer_helper_unittest.cc b/third_party/chromium/media/audio/power_observer_helper_unittest.cc
new file mode 100644
index 0000000..0d68d4e
--- /dev/null
+++ b/third_party/chromium/media/audio/power_observer_helper_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/task_environment.h"
+#include "base/threading/thread.h"
+#include "media/audio/power_observer_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+class PowerObserverHelperTest : public testing::Test {
+ public:
+  PowerObserverHelperTest()
+      : power_observer_helper_thread_("AliveCheckerThread"),
+        suspend_event_(base::WaitableEvent::ResetPolicy::MANUAL,
+                       base::WaitableEvent::InitialState::NOT_SIGNALED),
+        resume_event_(base::WaitableEvent::ResetPolicy::MANUAL,
+                      base::WaitableEvent::InitialState::NOT_SIGNALED) {
+    power_observer_helper_thread_.StartAndWaitForTesting();
+  }
+
+  void OnSuspend() {
+    EXPECT_TRUE(
+        power_observer_helper_thread_.task_runner()->BelongsToCurrentThread());
+    suspend_event_.Signal();
+  }
+
+  void OnResume() {
+    EXPECT_TRUE(
+        power_observer_helper_thread_.task_runner()->BelongsToCurrentThread());
+    resume_event_.Signal();
+  }
+
+ protected:
+  ~PowerObserverHelperTest() override {
+    base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
+                             base::WaitableEvent::InitialState::NOT_SIGNALED);
+    power_observer_helper_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&PowerObserverHelperTest::
+                           ResetPowerObserverHelperOnPowerObserverHelperThread,
+                       base::Unretained(this), &done));
+    done.Wait();
+  }
+
+  void CreatePowerObserverHelper() {
+    DCHECK(!power_observer_helper_);
+    power_observer_helper_ = std::make_unique<PowerObserverHelper>(
+        power_observer_helper_thread_.task_runner(),
+        base::BindRepeating(&PowerObserverHelperTest::OnSuspend,
+                            base::Unretained(this)),
+        base::BindRepeating(&PowerObserverHelperTest::OnResume,
+                            base::Unretained(this)));
+  }
+
+  void WaitUntilSuspendNotification() {
+    suspend_event_.Wait();
+    suspend_event_.Reset();
+  }
+
+  void WaitUntilResumeNotification() {
+    resume_event_.Wait();
+    resume_event_.Reset();
+  }
+
+  bool IsSuspending() {
+    bool is_suspending = false;
+    base::WaitableEvent did_check(
+        base::WaitableEvent::ResetPolicy::MANUAL,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    power_observer_helper_thread_.task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&PowerObserverHelperTest::CheckIfSuspending,
+                       base::Unretained(this), &is_suspending, &did_check));
+    did_check.Wait();
+    return is_suspending;
+  }
+
+  PowerObserverHelper* power_observer_helper() const {
+    return power_observer_helper_.get();
+  }
+
+ private:
+  void CheckIfSuspending(bool* is_suspending, base::WaitableEvent* done) {
+    EXPECT_TRUE(
+        power_observer_helper_thread_.task_runner()->BelongsToCurrentThread());
+    *is_suspending = power_observer_helper_->IsSuspending();
+    done->Signal();
+  }
+
+  void ResetPowerObserverHelperOnPowerObserverHelperThread(
+      base::WaitableEvent* done) {
+    EXPECT_TRUE(
+        power_observer_helper_thread_.task_runner()->BelongsToCurrentThread());
+    power_observer_helper_.reset();
+    done->Signal();
+  }
+
+  // The test task environment.
+  base::test::TaskEnvironment task_environment_;
+
+  // The thread the helper is run on.
+  base::Thread power_observer_helper_thread_;
+
+  // PowerObserverHelper under test.
+  std::unique_ptr<PowerObserverHelper> power_observer_helper_;
+
+  // Events to signal a notifications.
+  base::WaitableEvent suspend_event_;
+  base::WaitableEvent resume_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(PowerObserverHelperTest);
+};
+
+// Suspend and resume notifications.
+TEST_F(PowerObserverHelperTest, SuspendAndResumeNotificationsTwice) {
+  CreatePowerObserverHelper();
+  EXPECT_FALSE(IsSuspending());
+
+  power_observer_helper()->OnSuspend();
+  WaitUntilSuspendNotification();
+  EXPECT_TRUE(IsSuspending());
+
+  power_observer_helper()->OnResume();
+  WaitUntilResumeNotification();
+  EXPECT_FALSE(IsSuspending());
+
+  power_observer_helper()->OnSuspend();
+  WaitUntilSuspendNotification();
+  EXPECT_TRUE(IsSuspending());
+
+  power_observer_helper()->OnResume();
+  WaitUntilResumeNotification();
+  EXPECT_FALSE(IsSuspending());
+}
+
+// Two suspend and two resume notifications.
+TEST_F(PowerObserverHelperTest, TwoSuspendAndTwoResumeNotifications) {
+  CreatePowerObserverHelper();
+  EXPECT_FALSE(IsSuspending());
+
+  power_observer_helper()->OnSuspend();
+  WaitUntilSuspendNotification();
+  EXPECT_TRUE(IsSuspending());
+
+  power_observer_helper()->OnSuspend();
+  WaitUntilSuspendNotification();
+  EXPECT_TRUE(IsSuspending());
+
+  power_observer_helper()->OnResume();
+  WaitUntilResumeNotification();
+  EXPECT_FALSE(IsSuspending());
+
+  power_observer_helper()->OnResume();
+  WaitUntilResumeNotification();
+  EXPECT_FALSE(IsSuspending());
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/pulse/audio_manager_pulse.cc b/third_party/chromium/media/audio/pulse/audio_manager_pulse.cc
new file mode 100644
index 0000000..147246b
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/audio_manager_pulse.cc
@@ -0,0 +1,349 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/pulse/audio_manager_pulse.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/nix/xdg_util.h"
+#include "build/chromeos_buildflags.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/pulse/pulse_input.h"
+#include "media/audio/pulse/pulse_output.h"
+#include "media/audio/pulse/pulse_util.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/channel_layout.h"
+
+namespace media {
+
+using pulse::AutoPulseLock;
+using pulse::WaitForOperationCompletion;
+
+// Maximum number of output streams that can be open simultaneously.
+constexpr int kMaxOutputStreams = 50;
+
+constexpr int kMinimumOutputBufferSize = 512;
+constexpr int kMaximumOutputBufferSize = 8192;
+constexpr int kDefaultInputBufferSize = 1024;
+constexpr int kDefaultSampleRate = 48000;
+constexpr int kDefaultChannelCount = 2;
+
+AudioManagerPulse::AudioManagerPulse(std::unique_ptr<AudioThread> audio_thread,
+                                     AudioLogFactory* audio_log_factory,
+                                     pa_threaded_mainloop* pa_mainloop,
+                                     pa_context* pa_context)
+    : AudioManagerBase(std::move(audio_thread), audio_log_factory),
+      input_mainloop_(pa_mainloop),
+      input_context_(pa_context),
+      devices_(nullptr),
+      native_input_sample_rate_(kDefaultSampleRate),
+      native_channel_count_(kDefaultChannelCount),
+      default_source_is_monitor_(false) {
+  DCHECK(input_mainloop_);
+  DCHECK(input_context_);
+  SetMaxOutputStreamsAllowed(kMaxOutputStreams);
+}
+
+AudioManagerPulse::~AudioManagerPulse() = default;
+
+void AudioManagerPulse::ShutdownOnAudioThread() {
+  AudioManagerBase::ShutdownOnAudioThread();
+  // The Pulse objects are the last things to be destroyed since
+  // AudioManagerBase::ShutdownOnAudioThread() needs them.
+  pulse::DestroyPulse(input_mainloop_, input_context_);
+}
+
+bool AudioManagerPulse::HasAudioOutputDevices() {
+  AudioDeviceNames devices;
+  GetAudioOutputDeviceNames(&devices);
+  return !devices.empty();
+}
+
+bool AudioManagerPulse::HasAudioInputDevices() {
+  AudioDeviceNames devices;
+  GetAudioInputDeviceNames(&devices);
+  return !devices.empty();
+}
+
+void AudioManagerPulse::GetAudioDeviceNames(
+    bool input, media::AudioDeviceNames* device_names) {
+  DCHECK(device_names->empty());
+  DCHECK(input_mainloop_);
+  DCHECK(input_context_);
+  AutoPulseLock auto_lock(input_mainloop_);
+  devices_ = device_names;
+  pa_operation* operation = NULL;
+  if (input) {
+    operation = pa_context_get_source_info_list(
+      input_context_, InputDevicesInfoCallback, this);
+  } else {
+    operation = pa_context_get_sink_info_list(
+        input_context_, OutputDevicesInfoCallback, this);
+  }
+  WaitForOperationCompletion(input_mainloop_, operation, input_context_);
+
+  // Prepend the default device if the list is not empty.
+  if (!device_names->empty())
+    device_names->push_front(AudioDeviceName::CreateDefault());
+}
+
+void AudioManagerPulse::GetAudioInputDeviceNames(
+    AudioDeviceNames* device_names) {
+  GetAudioDeviceNames(true, device_names);
+}
+
+void AudioManagerPulse::GetAudioOutputDeviceNames(
+    AudioDeviceNames* device_names) {
+  GetAudioDeviceNames(false, device_names);
+}
+
+AudioParameters AudioManagerPulse::GetInputStreamParameters(
+    const std::string& device_id) {
+  UpdateNativeAudioHardwareInfo();
+
+  {
+    AutoPulseLock auto_lock(input_mainloop_);
+    auto* operation = pa_context_get_source_info_by_name(
+        input_context_, default_source_name_.c_str(), DefaultSourceInfoCallback,
+        this);
+    WaitForOperationCompletion(input_mainloop_, operation, input_context_);
+  }
+
+  // We don't want to accidentally open a monitor device, so return invalid
+  // parameters for those. Note: The value of |default_source_is_monitor_|
+  // depends on the the call to pa_context_get_source_info_by_name() above.
+  if (device_id == AudioDeviceDescription::kDefaultDeviceId &&
+      default_source_is_monitor_) {
+    return AudioParameters();
+  }
+
+  const int user_buffer_size = GetUserBufferSize();
+  const int buffer_size =
+      user_buffer_size ? user_buffer_size : kDefaultInputBufferSize;
+  return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                         CHANNEL_LAYOUT_STEREO,
+                         native_input_sample_rate_ ? native_input_sample_rate_
+                                                   : kDefaultSampleRate,
+                         buffer_size);
+}
+
+const char* AudioManagerPulse::GetName() {
+  return "PulseAudio";
+}
+
+AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
+    const AudioParameters& params,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+  return MakeOutputStream(params, AudioDeviceDescription::kDefaultDeviceId,
+                          log_callback);
+}
+
+AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+  return MakeOutputStream(
+      params,
+      device_id.empty() ? AudioDeviceDescription::kDefaultDeviceId : device_id,
+      log_callback);
+}
+
+AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+  return MakeInputStream(params, device_id, log_callback);
+}
+
+AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    const LogCallback& log_callback) {
+  DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+  return MakeInputStream(params, device_id, log_callback);
+}
+
+std::string AudioManagerPulse::GetDefaultInputDeviceID() {
+  // Do not use the real default input device since it is a fallback
+  // device rather than a default device. Using the default input device
+  // reported by Pulse Audio prevents, for example, input redirection
+  // using the PULSE_SOURCE environment variable.
+  return AudioManagerBase::GetDefaultInputDeviceID();
+}
+
+std::string AudioManagerPulse::GetDefaultOutputDeviceID() {
+  // Do not use the real default output device since it is a fallback
+  // device rather than a default device. Using the default output device
+  // reported by Pulse Audio prevents, for example, output redirection
+  // using the PULSE_SINK environment variable.
+  return AudioManagerBase::GetDefaultOutputDeviceID();
+}
+
+std::string AudioManagerPulse::GetAssociatedOutputDeviceID(
+    const std::string& input_device_id) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  return AudioManagerBase::GetAssociatedOutputDeviceID(input_device_id);
+#else
+  DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
+  DCHECK(input_mainloop_);
+  DCHECK(input_context_);
+
+  if (input_device_id == AudioDeviceDescription::kDefaultDeviceId)
+    return std::string();
+
+  std::string input_bus =
+      pulse::GetBusOfInput(input_mainloop_, input_context_, input_device_id);
+  return input_bus.empty() ? std::string()
+                           : pulse::GetOutputCorrespondingTo(
+                                 input_mainloop_, input_context_, input_bus);
+#endif
+}
+
+AudioParameters AudioManagerPulse::GetPreferredOutputStreamParameters(
+    const std::string& output_device_id,
+    const AudioParameters& input_params) {
+  // TODO(tommi): Support |output_device_id|.
+  VLOG_IF(0, !output_device_id.empty()) << "Not implemented!";
+
+  int buffer_size = kMinimumOutputBufferSize;
+
+  // Query native parameters where applicable; Pulse does not require these to
+  // be respected though, so prefer the input parameters for channel count.
+  UpdateNativeAudioHardwareInfo();
+  int sample_rate = native_input_sample_rate_ ? native_input_sample_rate_
+                                              : kDefaultSampleRate;
+  ChannelLayout channel_layout =
+      GuessChannelLayout(native_channel_count_ ? native_channel_count_ : 2);
+
+  if (input_params.IsValid()) {
+    // Use the system's output channel count for the DISCRETE layout. This is to
+    // avoid a crash due to the lack of support on the multi-channel beyond 8 in
+    // the PulseAudio layer.
+    if (input_params.channel_layout() != CHANNEL_LAYOUT_DISCRETE)
+      channel_layout = input_params.channel_layout();
+
+    buffer_size =
+        std::min(kMaximumOutputBufferSize,
+                 std::max(buffer_size, input_params.frames_per_buffer()));
+  }
+
+  int user_buffer_size = GetUserBufferSize();
+  if (user_buffer_size)
+    buffer_size = user_buffer_size;
+
+  return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
+                         sample_rate, buffer_size);
+}
+
+AudioOutputStream* AudioManagerPulse::MakeOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    LogCallback log_callback) {
+  DCHECK(!device_id.empty());
+  return new PulseAudioOutputStream(params, device_id, this,
+                                    std::move(log_callback));
+}
+
+AudioInputStream* AudioManagerPulse::MakeInputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    LogCallback log_callback) {
+  return new PulseAudioInputStream(this, device_id, params, input_mainloop_,
+                                   input_context_, std::move(log_callback));
+}
+
+void AudioManagerPulse::UpdateNativeAudioHardwareInfo() {
+  DCHECK(input_mainloop_);
+  DCHECK(input_context_);
+  AutoPulseLock auto_lock(input_mainloop_);
+  pa_operation* operation = pa_context_get_server_info(
+      input_context_, AudioHardwareInfoCallback, this);
+  WaitForOperationCompletion(input_mainloop_, operation, input_context_);
+
+  // Be careful about adding OS calls to this method.
+  // GetPreferredOutputStreamParameters() calls this method on a critical path.
+  // If the OS calls hang they will hang all device authorizations.
+}
+
+void AudioManagerPulse::InputDevicesInfoCallback(pa_context* context,
+                                                 const pa_source_info* info,
+                                                 int eol,
+                                                 void* user_data) {
+  AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
+
+  if (eol) {
+    // Signal the pulse object that it is done.
+    pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
+    return;
+  }
+
+  // Exclude output monitor (i.e. loopback) devices.
+  if (info->monitor_of_sink != PA_INVALID_INDEX)
+    return;
+
+  // If the device has ports, but none of them are available, skip it.
+  if (info->n_ports > 0) {
+    uint32_t port = 0;
+    for (; port != info->n_ports; ++port) {
+      if (info->ports[port]->available != PA_PORT_AVAILABLE_NO)
+        break;
+    }
+    if (port == info->n_ports)
+      return;
+  }
+
+  manager->devices_->push_back(AudioDeviceName(info->description, info->name));
+}
+
+void AudioManagerPulse::OutputDevicesInfoCallback(pa_context* context,
+                                                  const pa_sink_info* info,
+                                                  int eol,
+                                                  void* user_data) {
+  AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
+
+  if (eol) {
+    // Signal the pulse object that it is done.
+    pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
+    return;
+  }
+
+  manager->devices_->push_back(AudioDeviceName(info->description, info->name));
+}
+
+void AudioManagerPulse::AudioHardwareInfoCallback(pa_context* context,
+                                                  const pa_server_info* info,
+                                                  void* user_data) {
+  AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
+
+  manager->native_input_sample_rate_ = info->sample_spec.rate;
+  manager->native_channel_count_ = info->sample_spec.channels;
+  if (info->default_source_name)
+    manager->default_source_name_ = info->default_source_name;
+  pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
+}
+
+void AudioManagerPulse::DefaultSourceInfoCallback(pa_context* context,
+                                                  const pa_source_info* info,
+                                                  int eol,
+                                                  void* user_data) {
+  AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
+  if (eol) {
+    // Signal the pulse object that it is done.
+    pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
+    return;
+  }
+
+  DCHECK(info);
+  manager->default_source_is_monitor_ =
+      info->monitor_of_sink != PA_INVALID_INDEX;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/pulse/audio_manager_pulse.h b/third_party/chromium/media/audio/pulse/audio_manager_pulse.h
new file mode 100644
index 0000000..856f2ea
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/audio_manager_pulse.h
@@ -0,0 +1,118 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_PULSE_AUDIO_MANAGER_PULSE_H_
+#define MEDIA_AUDIO_PULSE_AUDIO_MANAGER_PULSE_H_
+
+#include <pulse/pulseaudio.h>
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/audio/audio_manager_base.h"
+
+namespace media {
+
+class MEDIA_EXPORT AudioManagerPulse : public AudioManagerBase {
+ public:
+  AudioManagerPulse(std::unique_ptr<AudioThread> audio_thread,
+                    AudioLogFactory* audio_log_factory,
+                    pa_threaded_mainloop* pa_mainloop,
+                    pa_context* pa_context);
+
+  AudioManagerPulse(const AudioManagerPulse&) = delete;
+  AudioManagerPulse& operator=(const AudioManagerPulse&) = delete;
+
+  ~AudioManagerPulse() override;
+
+  // Implementation of AudioManager.
+  bool HasAudioOutputDevices() override;
+  bool HasAudioInputDevices() override;
+  void GetAudioInputDeviceNames(AudioDeviceNames* device_names) override;
+  void GetAudioOutputDeviceNames(AudioDeviceNames* device_names) override;
+  AudioParameters GetInputStreamParameters(
+      const std::string& device_id) override;
+  const char* GetName() override;
+
+  // Implementation of AudioManagerBase.
+  AudioOutputStream* MakeLinearOutputStream(
+      const AudioParameters& params,
+      const LogCallback& log_callback) override;
+  AudioOutputStream* MakeLowLatencyOutputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLinearInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  AudioInputStream* MakeLowLatencyInputStream(
+      const AudioParameters& params,
+      const std::string& device_id,
+      const LogCallback& log_callback) override;
+  std::string GetDefaultInputDeviceID() override;
+  std::string GetDefaultOutputDeviceID() override;
+  std::string GetAssociatedOutputDeviceID(
+      const std::string& input_device_id) override;
+
+  bool DefaultSourceIsMonitor() const { return default_source_is_monitor_; }
+
+ protected:
+  void ShutdownOnAudioThread() override;
+  AudioParameters GetPreferredOutputStreamParameters(
+      const std::string& output_device_id,
+      const AudioParameters& input_params) override;
+
+ private:
+  void GetAudioDeviceNames(bool input, media::AudioDeviceNames* device_names);
+
+  // Callback to get the devices' info like names, used by GetInputDevices().
+  static void InputDevicesInfoCallback(pa_context* context,
+                                       const pa_source_info* info,
+                                       int eol,
+                                       void* user_data);
+  static void OutputDevicesInfoCallback(pa_context* context,
+                                        const pa_sink_info* info,
+                                        int eol,
+                                        void* user_data);
+
+  // Callback to get the native sample rate of PulseAudio, used by
+  // UpdateNativeAudioHardwareInfo().
+  static void AudioHardwareInfoCallback(pa_context* context,
+                                        const pa_server_info* info,
+                                        void* user_data);
+
+  static void DefaultSourceInfoCallback(pa_context* context,
+                                        const pa_source_info* info,
+                                        int eol,
+                                        void* user_data);
+
+  // Called by MakeLinearOutputStream and MakeLowLatencyOutputStream.
+  AudioOutputStream* MakeOutputStream(const AudioParameters& params,
+                                      const std::string& device_id,
+                                      LogCallback log_callback);
+
+  // Called by MakeLinearInputStream and MakeLowLatencyInputStream.
+  AudioInputStream* MakeInputStream(const AudioParameters& params,
+                                    const std::string& device_id,
+                                    LogCallback log_callback);
+
+  // Updates |native_input_sample_rate_| and |native_channel_count_|.
+  void UpdateNativeAudioHardwareInfo();
+
+  pa_threaded_mainloop* input_mainloop_;
+  pa_context* input_context_;
+  AudioDeviceNames* devices_;
+  int native_input_sample_rate_;
+  int native_channel_count_;
+  std::string default_source_name_;
+  bool default_source_is_monitor_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_PULSE_AUDIO_MANAGER_PULSE_H_
diff --git a/third_party/chromium/media/audio/pulse/pulse.sigs b/third_party/chromium/media/audio/pulse/pulse.sigs
new file mode 100644
index 0000000..85ff08a
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/pulse.sigs
@@ -0,0 +1,61 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//------------------------------------------------
+// Functions from pulse used in media code.
+//------------------------------------------------
+pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop* m);
+void pa_threaded_mainloop_free(pa_threaded_mainloop* m);
+pa_threaded_mainloop* pa_threaded_mainloop_new();
+void pa_threaded_mainloop_lock(pa_threaded_mainloop* m);
+int pa_threaded_mainloop_in_thread(pa_threaded_mainloop* m);
+void pa_threaded_mainloop_signal(pa_threaded_mainloop* m, int wait_for_accept);
+int pa_threaded_mainloop_start(pa_threaded_mainloop* m);
+void pa_threaded_mainloop_stop(pa_threaded_mainloop* m);
+void pa_threaded_mainloop_unlock(pa_threaded_mainloop* m);
+void pa_threaded_mainloop_wait(pa_threaded_mainloop* m);
+pa_channel_map* pa_channel_map_init(pa_channel_map* m);
+pa_channel_map* pa_channel_map_init_mono(pa_channel_map* m);
+int pa_context_connect(pa_context* c, const char* server, pa_context_flags_t flags, const pa_spawn_api* api);
+void pa_context_disconnect(pa_context* c);
+pa_operation* pa_context_get_server_info(pa_context* c, pa_server_info_cb_t cb, void* userdata);
+pa_operation* pa_context_get_source_info_by_index(pa_context* c, uint32_t idx, pa_source_info_cb_t cb, void* userdata);
+pa_operation* pa_context_get_source_info_by_name(pa_context* c, const char* name, pa_source_info_cb_t cb, void *userdata);
+pa_operation* pa_context_get_source_info_list(pa_context* c, pa_source_info_cb_t cb, void* userdata);
+pa_operation* pa_context_get_sink_info_list(pa_context* c, pa_sink_info_cb_t cb, void* userdata);
+pa_context_state_t pa_context_get_state(const_pa_context_ptr c);
+pa_context* pa_context_new(pa_mainloop_api* mainloop, const char* name);
+pa_operation* pa_context_set_source_volume_by_index(pa_context* c, uint32_t idx, const pa_cvolume* volume, pa_context_success_cb_t cb, void* userdata);
+void pa_context_set_state_callback(pa_context* c, pa_context_notify_cb_t cb, void* userdata);
+pa_operation_state_t pa_operation_get_state(const_pa_operation_ptr o);
+void pa_context_unref(pa_context* c);
+void pa_operation_cancel(pa_operation* o)
+void pa_operation_unref(pa_operation* o);
+int pa_stream_begin_write(pa_stream* p, void** data, size_t* nbytes);
+int pa_stream_connect_playback(pa_stream* s, const char* dev, const pa_buffer_attr* attr, pa_stream_flags_t flags, const pa_cvolume* volume,pa_stream* sync_stream);
+int pa_stream_connect_record(pa_stream* s, const char* dev, const pa_buffer_attr* attr, pa_stream_flags_t flags);
+pa_operation* pa_stream_cork(pa_stream* s, int b, pa_stream_success_cb_t cb, void* userdata);
+int pa_stream_disconnect(pa_stream* s);
+int pa_stream_drop(pa_stream *p);
+pa_operation* pa_stream_flush(pa_stream* s, pa_stream_success_cb_t cb, void* userdata);
+uint32_t pa_stream_get_device_index(const_pa_stream_ptr s);
+int pa_stream_get_latency(pa_stream* s, pa_usec_t* r_usec, int* negative);
+pa_stream_state_t pa_stream_get_state(const_pa_stream_ptr p);
+pa_stream* pa_stream_new(pa_context*  c, const char*  name, const pa_sample_spec*  ss, const pa_channel_map *  map);
+pa_stream* pa_stream_new_with_proplist(pa_context* c, const char* name, const pa_sample_spec* ss, const pa_channel_map* map, pa_proplist* p);
+pa_proplist* pa_proplist_new(void);
+int pa_proplist_contains(const_pa_proplist_ptr p, const char* key);
+void pa_proplist_free(pa_proplist* p);
+const char* pa_proplist_gets(const_pa_proplist_ptr p, const char* key);
+int pa_proplist_sets(pa_proplist* p, const char* key, const char* value);
+size_t pa_stream_readable_size(const_pa_stream_ptr p);
+int pa_stream_peek(pa_stream* p, const void** data, size_t* nbytes);
+void pa_stream_set_read_callback(pa_stream* p, pa_stream_request_cb_t cb, void* userdata);
+void pa_stream_set_state_callback(pa_stream* s, pa_stream_notify_cb_t cb, void* userdata);
+int pa_stream_write(pa_stream* p, const void* data, size_t nbytes, pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek);
+void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
+void pa_stream_unref(pa_stream* s);
+int pa_context_errno(const_pa_context_ptr c);
+const char* pa_strerror(int error);
+pa_cvolume* pa_cvolume_set(pa_cvolume* a, unsigned  channels, pa_volume_t v);
diff --git a/third_party/chromium/media/audio/pulse/pulse_input.cc b/third_party/chromium/media/audio/pulse/pulse_input.cc
new file mode 100644
index 0000000..1f7cca7
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/pulse_input.cc
@@ -0,0 +1,392 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/pulse/pulse_input.h"
+
+#include <stdint.h>
+
+#include "base/check.h"
+#include "base/strings/stringprintf.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/pulse/audio_manager_pulse.h"
+#include "media/audio/pulse/pulse_util.h"
+#include "media/base/audio_timestamp_helper.h"
+
+namespace media {
+
+using pulse::AutoPulseLock;
+using pulse::WaitForOperationCompletion;
+
+// Number of blocks of buffers used in the |fifo_|.
+const int kNumberOfBlocksBufferInFifo = 2;
+
+PulseAudioInputStream::PulseAudioInputStream(
+    AudioManagerPulse* audio_manager,
+    const std::string& device_name,
+    const AudioParameters& params,
+    pa_threaded_mainloop* mainloop,
+    pa_context* context,
+    AudioManager::LogCallback log_callback)
+    : audio_manager_(audio_manager),
+      callback_(nullptr),
+      device_name_(device_name),
+      params_(params),
+      channels_(0),
+      volume_(0.0),
+      stream_started_(false),
+      muted_(false),
+      fifo_(params.channels(),
+            params.frames_per_buffer(),
+            kNumberOfBlocksBufferInFifo),
+      pa_mainloop_(mainloop),
+      pa_context_(context),
+      log_callback_(std::move(log_callback)),
+      handle_(nullptr) {
+  DCHECK(mainloop);
+  DCHECK(context);
+  CHECK(params_.IsValid());
+  SendLogMessage("%s({device_id=%s}, {params=[%s]})", __func__,
+                 device_name.c_str(), params.AsHumanReadableString().c_str());
+}
+
+PulseAudioInputStream::~PulseAudioInputStream() {
+  // All internal structures should already have been freed in Close(),
+  // which calls AudioManagerPulse::Release which deletes this object.
+  DCHECK(!handle_);
+}
+
+AudioInputStream::OpenOutcome PulseAudioInputStream::Open() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  SendLogMessage("%s()", __func__);
+  if (device_name_ == AudioDeviceDescription::kDefaultDeviceId &&
+      audio_manager_->DefaultSourceIsMonitor()) {
+    SendLogMessage("%s => (ERROR: can't open monitor device)", __func__);
+    return OpenOutcome::kFailed;
+  }
+
+  AutoPulseLock auto_lock(pa_mainloop_);
+  if (!pulse::CreateInputStream(pa_mainloop_, pa_context_, &handle_, params_,
+                                device_name_, &StreamNotifyCallback, this)) {
+    SendLogMessage("%s => (ERROR: failed to open PA stream)", __func__);
+    return OpenOutcome::kFailed;
+  }
+
+  DCHECK(handle_);
+
+  return OpenOutcome::kSuccess;
+}
+
+void PulseAudioInputStream::Start(AudioInputCallback* callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(callback);
+  DCHECK(handle_);
+  SendLogMessage("%s()", __func__);
+
+  // AGC needs to be started out of the lock.
+  StartAgc();
+
+  AutoPulseLock auto_lock(pa_mainloop_);
+
+  if (stream_started_)
+    return;
+
+  // Start the streaming.
+  callback_ = callback;
+  pa_stream_set_read_callback(handle_, &ReadCallback, this);
+  pa_stream_readable_size(handle_);
+  stream_started_ = true;
+
+  pa_operation* operation =
+      pa_stream_cork(handle_, 0, &pulse::StreamSuccessCallback, pa_mainloop_);
+
+  if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
+                                  handle_)) {
+    callback_->OnError();
+  }
+}
+
+void PulseAudioInputStream::Stop() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  SendLogMessage("%s()", __func__);
+  AutoPulseLock auto_lock(pa_mainloop_);
+  if (!stream_started_)
+    return;
+
+  StopAgc();
+
+  // Set the flag to false to stop filling new data to soundcard.
+  stream_started_ = false;
+
+  // Clean up the old buffer.
+  pa_stream_drop(handle_);
+  fifo_.Clear();
+
+  pa_operation* operation =
+      pa_stream_flush(handle_, &pulse::StreamSuccessCallback, pa_mainloop_);
+  if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
+                                  handle_)) {
+    callback_->OnError();
+  }
+
+  // Stop the stream.
+  pa_stream_set_read_callback(handle_, nullptr, nullptr);
+  operation =
+      pa_stream_cork(handle_, 1, &pulse::StreamSuccessCallback, pa_mainloop_);
+  if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
+                                  handle_)) {
+    callback_->OnError();
+  }
+  callback_ = nullptr;
+}
+
+void PulseAudioInputStream::Close() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  SendLogMessage("%s()", __func__);
+  {
+    AutoPulseLock auto_lock(pa_mainloop_);
+    if (handle_) {
+      // Disable all the callbacks before disconnecting.
+      pa_stream_set_state_callback(handle_, nullptr, nullptr);
+      pa_operation* operation =
+          pa_stream_flush(handle_, &pulse::StreamSuccessCallback, pa_mainloop_);
+      WaitForOperationCompletion(pa_mainloop_, operation, pa_context_, handle_);
+
+      if (pa_stream_get_state(handle_) != PA_STREAM_UNCONNECTED)
+        pa_stream_disconnect(handle_);
+
+      // Release PulseAudio structures.
+      pa_stream_unref(handle_);
+      handle_ = nullptr;
+    }
+  }
+
+  // Signal to the manager that we're closed and can be removed.
+  // This should be the last call in the function as it deletes "this".
+  audio_manager_->ReleaseInputStream(this);
+}
+
+double PulseAudioInputStream::GetMaxVolume() {
+  return static_cast<double>(PA_VOLUME_NORM);
+}
+
+void PulseAudioInputStream::SetVolume(double volume) {
+  AutoPulseLock auto_lock(pa_mainloop_);
+  if (!handle_)
+    return;
+  SendLogMessage("%s({volume=%.2f})", __func__, volume);
+
+  size_t index = pa_stream_get_device_index(handle_);
+  pa_operation* operation = nullptr;
+  if (!channels_) {
+    // Get the number of channels for the source only when the |channels_| is 0.
+    // We are assuming the stream source is not changed on the fly here.
+    operation = pa_context_get_source_info_by_index(pa_context_, index,
+                                                    &VolumeCallback, this);
+    if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
+                                    handle_) ||
+        !channels_) {
+      SendLogMessage("%s => (WARNING: failed to read number of channels)",
+                     __func__);
+      return;
+    }
+  }
+
+  pa_cvolume pa_volume;
+  pa_cvolume_set(&pa_volume, channels_, volume);
+  operation = pa_context_set_source_volume_by_index(
+      pa_context_, index, &pa_volume, nullptr, nullptr);
+
+  // Don't need to wait for this task to complete.
+  pa_operation_unref(operation);
+}
+
+double PulseAudioInputStream::GetVolume() {
+  if (pa_threaded_mainloop_in_thread(pa_mainloop_)) {
+    // When being called by the pulse thread, GetVolume() is asynchronous and
+    // called under AutoPulseLock.
+    if (!handle_)
+      return 0.0;
+
+    size_t index = pa_stream_get_device_index(handle_);
+    pa_operation* operation = pa_context_get_source_info_by_index(
+        pa_context_, index, &VolumeCallback, this);
+    // Do not wait for the operation since we can't block the pulse thread.
+    pa_operation_unref(operation);
+
+    // Return zero and the callback will asynchronously update the |volume_|.
+    return 0.0;
+  } else {
+    GetSourceInformation(&VolumeCallback);
+    return volume_;
+  }
+}
+
+bool PulseAudioInputStream::IsMuted() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  GetSourceInformation(&MuteCallback);
+  return muted_;
+}
+
+void PulseAudioInputStream::SetOutputDeviceForAec(
+    const std::string& output_device_id) {
+  // Not supported. Do nothing.
+}
+
+void PulseAudioInputStream::SendLogMessage(const char* format, ...) {
+  if (log_callback_.is_null())
+    return;
+  va_list args;
+  va_start(args, format);
+  log_callback_.Run("PAIS::" + base::StringPrintV(format, args));
+  va_end(args);
+}
+
+// static, used by pa_stream_set_read_callback.
+void PulseAudioInputStream::ReadCallback(pa_stream* handle,
+                                         size_t length,
+                                         void* user_data) {
+  PulseAudioInputStream* stream =
+      reinterpret_cast<PulseAudioInputStream*>(user_data);
+
+  stream->ReadData();
+}
+
+// static, used by pa_context_get_source_info_by_index.
+void PulseAudioInputStream::VolumeCallback(pa_context* context,
+                                           const pa_source_info* info,
+                                           int error, void* user_data) {
+  PulseAudioInputStream* stream =
+      reinterpret_cast<PulseAudioInputStream*>(user_data);
+
+  if (error) {
+    pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
+    return;
+  }
+
+  if (stream->channels_ != info->channel_map.channels)
+    stream->channels_ = info->channel_map.channels;
+
+  pa_volume_t volume = PA_VOLUME_MUTED;  // Minimum possible value.
+  // Use the max volume of any channel as the volume.
+  for (int i = 0; i < stream->channels_; ++i) {
+    if (volume < info->volume.values[i])
+      volume = info->volume.values[i];
+  }
+
+  // It is safe to access |volume_| here since VolumeCallback() is running
+  // under PulseLock.
+  stream->volume_ = static_cast<double>(volume);
+}
+
+// static, used by pa_context_get_source_info_by_index.
+void PulseAudioInputStream::MuteCallback(pa_context* context,
+                                         const pa_source_info* info,
+                                         int error,
+                                         void* user_data) {
+  // Runs on PulseAudio callback thread. It might be possible to make this
+  // method more thread safe by passing a struct (or pair) of a local copy of
+  // |pa_mainloop_| and |muted_| instead.
+  PulseAudioInputStream* stream =
+      reinterpret_cast<PulseAudioInputStream*>(user_data);
+
+  // Avoid infinite wait loop in case of error.
+  if (error) {
+    pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
+    return;
+  }
+
+  stream->muted_ = info->mute != 0;
+}
+
+// static, used by pa_stream_set_state_callback.
+void PulseAudioInputStream::StreamNotifyCallback(pa_stream* s,
+                                                 void* user_data) {
+  PulseAudioInputStream* stream =
+      reinterpret_cast<PulseAudioInputStream*>(user_data);
+
+  if (s && stream->callback_ &&
+      pa_stream_get_state(s) == PA_STREAM_FAILED) {
+    stream->callback_->OnError();
+  }
+
+  pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
+}
+
+void PulseAudioInputStream::ReadData() {
+  // Update the AGC volume level once every second. Note that,
+  // |volume| is also updated each time SetVolume() is called
+  // through IPC by the render-side AGC.
+  // We disregard the |normalized_volume| from GetAgcVolume()
+  // and use the value calculated by |volume_|.
+  double normalized_volume = 0.0;
+  GetAgcVolume(&normalized_volume);
+  normalized_volume = volume_ / GetMaxVolume();
+
+  // Compensate the audio delay caused by the FIFO.
+  // TODO(dalecurtis): This should probably use pa_stream_get_time() so we can
+  // get the capture time directly.
+  base::TimeTicks capture_time =
+      base::TimeTicks::Now() -
+      (pulse::GetHardwareLatency(handle_) +
+       AudioTimestampHelper::FramesToTime(fifo_.GetAvailableFrames(),
+                                          params_.sample_rate()));
+  do {
+    size_t length = 0;
+    const void* data = nullptr;
+    pa_stream_peek(handle_, &data, &length);
+    if (!data || length == 0)
+      break;
+
+    const int number_of_frames =
+        length / params_.GetBytesPerFrame(pulse::kInputSampleFormat);
+    if (number_of_frames > fifo_.GetUnfilledFrames()) {
+      // Dynamically increase capacity to the FIFO to handle larger buffer got
+      // from Pulse.
+      const int increase_blocks_of_buffer =
+          static_cast<int>((number_of_frames - fifo_.GetUnfilledFrames()) /
+                           params_.frames_per_buffer()) +
+          1;
+      fifo_.IncreaseCapacity(increase_blocks_of_buffer);
+    }
+
+    fifo_.Push(data, number_of_frames,
+               SampleFormatToBytesPerChannel(pulse::kInputSampleFormat));
+
+    // Checks if we still have data.
+    pa_stream_drop(handle_);
+  } while (pa_stream_readable_size(handle_) > 0);
+
+  while (fifo_.available_blocks()) {
+    const AudioBus* audio_bus = fifo_.Consume();
+
+    callback_->OnData(audio_bus, capture_time, normalized_volume);
+
+    // Move the capture time forward for each vended block.
+    capture_time += AudioTimestampHelper::FramesToTime(audio_bus->frames(),
+                                                       params_.sample_rate());
+
+    // Sleep 5ms to wait until render consumes the data in order to avoid
+    // back to back OnData() method.
+    // TODO(dalecurtis): Delete all this. It shouldn't be necessary now that we
+    // have a ring buffer and FIFO on the actual shared memory.,
+    if (fifo_.available_blocks())
+      base::PlatformThread::Sleep(base::Milliseconds(5));
+  }
+
+  pa_threaded_mainloop_signal(pa_mainloop_, 0);
+}
+
+bool PulseAudioInputStream::GetSourceInformation(pa_source_info_cb_t callback) {
+  AutoPulseLock auto_lock(pa_mainloop_);
+  if (!handle_)
+    return false;
+
+  size_t index = pa_stream_get_device_index(handle_);
+  pa_operation* operation =
+      pa_context_get_source_info_by_index(pa_context_, index, callback, this);
+  return WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
+                                    handle_);
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/pulse/pulse_input.h b/third_party/chromium/media/audio/pulse/pulse_input.h
new file mode 100644
index 0000000..df3f28b
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/pulse_input.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_PULSE_PULSE_INPUT_H_
+#define MEDIA_AUDIO_PULSE_PULSE_INPUT_H_
+
+#include <pulse/pulseaudio.h>
+#include <stddef.h>
+#include <string>
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/agc_audio_stream.h"
+#include "media/audio/audio_device_name.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
+#include "media/base/audio_block_fifo.h"
+#include "media/base/audio_parameters.h"
+
+namespace media {
+
+class AudioManagerPulse;
+
+class PulseAudioInputStream : public AgcAudioStream<AudioInputStream> {
+ public:
+  PulseAudioInputStream(AudioManagerPulse* audio_manager,
+                        const std::string& device_name,
+                        const AudioParameters& params,
+                        pa_threaded_mainloop* mainloop,
+                        pa_context* context,
+                        AudioManager::LogCallback log_callback);
+
+  PulseAudioInputStream(const PulseAudioInputStream&) = delete;
+  PulseAudioInputStream& operator=(const PulseAudioInputStream&) = delete;
+
+  ~PulseAudioInputStream() override;
+
+  // Implementation of AudioInputStream.
+  AudioInputStream::OpenOutcome Open() override;
+  void Start(AudioInputCallback* callback) override;
+  void Stop() override;
+  void Close() override;
+  double GetMaxVolume() override;
+  void SetVolume(double volume) override;
+  double GetVolume() override;
+  bool IsMuted() override;
+  void SetOutputDeviceForAec(const std::string& output_device_id) override;
+
+ private:
+  // Helper method used for sending native logs to the registered client.
+  void SendLogMessage(const char* format, ...) PRINTF_FORMAT(2, 3);
+
+  // PulseAudio Callbacks.
+  static void ReadCallback(pa_stream* handle, size_t length, void* user_data);
+  static void StreamNotifyCallback(pa_stream* stream, void* user_data);
+  static void VolumeCallback(pa_context* context, const pa_source_info* info,
+                             int error, void* user_data);
+  static void MuteCallback(pa_context* context,
+                           const pa_source_info* info,
+                           int error,
+                           void* user_data);
+
+  // Helper for the ReadCallback.
+  void ReadData();
+
+  // Utility method used by GetVolume() and IsMuted().
+  bool GetSourceInformation(pa_source_info_cb_t callback);
+
+  AudioManagerPulse* audio_manager_;
+  AudioInputCallback* callback_;
+  std::string device_name_;
+  AudioParameters params_;
+  int channels_;
+  double volume_;
+  bool stream_started_;
+
+  // Set to true in IsMuted() if user has muted the selected microphone in the
+  // sound settings UI.
+  bool muted_;
+
+  // Holds the data from the OS.
+  AudioBlockFifo fifo_;
+
+  // PulseAudio API structs.
+  pa_threaded_mainloop* pa_mainloop_; // Weak.
+
+  pa_context* pa_context_;  // Weak.
+
+  // Callback to send log messages to registered clients.
+  AudioManager::LogCallback log_callback_;
+
+  pa_stream* handle_;
+
+  base::ThreadChecker thread_checker_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_PULSE_PULSE_INPUT_H_
diff --git a/third_party/chromium/media/audio/pulse/pulse_output.cc b/third_party/chromium/media/audio/pulse/pulse_output.cc
new file mode 100644
index 0000000..2b773a3
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/pulse_output.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/pulse/pulse_output.h"
+
+#include <pulse/pulseaudio.h>
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "media/audio/audio_device_description.h"
+#include "media/audio/audio_manager_base.h"
+#include "media/audio/pulse/pulse_util.h"
+#include "media/base/audio_sample_types.h"
+
+namespace media {
+
+using pulse::AutoPulseLock;
+using pulse::WaitForOperationCompletion;
+
+// static, pa_stream_notify_cb
+void PulseAudioOutputStream::StreamNotifyCallback(pa_stream* s, void* p_this) {
+  PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);
+
+  // Forward unexpected failures to the AudioSourceCallback if available.  All
+  // these variables are only modified under pa_threaded_mainloop_lock() so this
+  // should be thread safe.
+  if (s && stream->source_callback_ &&
+      pa_stream_get_state(s) == PA_STREAM_FAILED) {
+    stream->source_callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+  }
+
+  pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
+}
+
+// static, pa_stream_request_cb_t
+void PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len,
+                                                   void* p_this) {
+  // Fulfill write request; must always result in a pa_stream_write() call.
+  static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len);
+}
+
+PulseAudioOutputStream::PulseAudioOutputStream(
+    const AudioParameters& params,
+    const std::string& device_id,
+    AudioManagerBase* manager,
+    AudioManager::LogCallback log_callback)
+    : params_(AudioParameters(params.format(),
+                              params.channel_layout(),
+                              params.sample_rate(),
+                              params.frames_per_buffer())),
+      device_id_(device_id),
+      manager_(manager),
+      log_callback_(std::move(log_callback)),
+      pa_context_(nullptr),
+      pa_mainloop_(nullptr),
+      pa_stream_(nullptr),
+      volume_(1.0f),
+      source_callback_(nullptr),
+      buffer_size_(params_.GetBytesPerBuffer(kSampleFormatF32)) {
+  CHECK(params_.IsValid());
+  SendLogMessage("%s({device_id=%s}, {params=[%s]})", __func__,
+                 device_id.c_str(), params.AsHumanReadableString().c_str());
+  audio_bus_ = AudioBus::Create(params_);
+}
+
+PulseAudioOutputStream::~PulseAudioOutputStream() {
+  // All internal structures should already have been freed in Close(), which
+  // calls AudioManagerBase::ReleaseOutputStream() which deletes this object.
+  DCHECK(!pa_stream_);
+  DCHECK(!pa_context_);
+  DCHECK(!pa_mainloop_);
+}
+
+bool PulseAudioOutputStream::Open() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  SendLogMessage("%s()", __func__);
+  bool result = pulse::CreateOutputStream(
+      &pa_mainloop_, &pa_context_, &pa_stream_, params_, device_id_,
+      AudioManager::GetGlobalAppName(), &StreamNotifyCallback,
+      &StreamRequestCallback, this);
+  if (!result) {
+    SendLogMessage("%s => (ERROR: failed to open PA stream)", __func__);
+  }
+  return result;
+}
+
+void PulseAudioOutputStream::Reset() {
+  if (!pa_mainloop_) {
+    DCHECK(!pa_stream_);
+    DCHECK(!pa_context_);
+    return;
+  }
+
+  {
+    AutoPulseLock auto_lock(pa_mainloop_);
+
+    // Close the stream.
+    if (pa_stream_) {
+      // Ensure all samples are played out before shutdown.
+      pa_operation* operation = pa_stream_flush(
+          pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
+      WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
+                                 pa_stream_);
+
+      // Release PulseAudio structures.
+      pa_stream_disconnect(pa_stream_);
+      pa_stream_set_write_callback(pa_stream_, nullptr, nullptr);
+      pa_stream_set_state_callback(pa_stream_, nullptr, nullptr);
+      pa_stream_unref(pa_stream_);
+      pa_stream_ = nullptr;
+    }
+
+    if (pa_context_) {
+      pa_context_disconnect(pa_context_);
+      pa_context_set_state_callback(pa_context_, nullptr, nullptr);
+      pa_context_unref(pa_context_);
+      pa_context_ = nullptr;
+    }
+  }
+
+  pa_threaded_mainloop_stop(pa_mainloop_);
+  pa_threaded_mainloop_free(pa_mainloop_);
+  pa_mainloop_ = nullptr;
+}
+
+void PulseAudioOutputStream::Close() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  SendLogMessage("%s()", __func__);
+
+  Reset();
+
+  // Signal to the manager that we're closed and can be removed.
+  // This should be the last call in the function as it deletes "this".
+  manager_->ReleaseOutputStream(this);
+}
+
+// This stream is always used with sub second buffer sizes, where it's
+// sufficient to simply always flush upon Start().
+void PulseAudioOutputStream::Flush() {}
+
+void PulseAudioOutputStream::SendLogMessage(const char* format, ...) {
+  if (log_callback_.is_null())
+    return;
+  va_list args;
+  va_start(args, format);
+  log_callback_.Run("PAOS::" + base::StringPrintV(format, args) +
+                    base::StringPrintf(" [this=%p]", this));
+  va_end(args);
+}
+
+void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
+  int bytes_remaining = requested_bytes;
+  while (bytes_remaining > 0) {
+    void* pa_buffer = nullptr;
+    size_t pa_buffer_size = buffer_size_;
+    CHECK_GE(pa_stream_begin_write(pa_stream_, &pa_buffer, &pa_buffer_size), 0);
+
+    if (!source_callback_) {
+      memset(pa_buffer, 0, pa_buffer_size);
+      pa_stream_write(pa_stream_, pa_buffer, pa_buffer_size, nullptr, 0LL,
+                      PA_SEEK_RELATIVE);
+      bytes_remaining -= pa_buffer_size;
+      continue;
+    }
+
+    size_t unwritten_frames_in_bus = audio_bus_->frames();
+    size_t frames_filled = source_callback_->OnMoreData(
+        pulse::GetHardwareLatency(pa_stream_), base::TimeTicks::Now(), 0,
+        audio_bus_.get());
+
+    // Zero any unfilled data so it plays back as silence.
+    if (frames_filled < unwritten_frames_in_bus) {
+      audio_bus_->ZeroFramesPartial(frames_filled,
+                                    unwritten_frames_in_bus - frames_filled);
+    }
+
+    audio_bus_->Scale(volume_);
+
+    size_t frame_size = buffer_size_ / unwritten_frames_in_bus;
+    size_t frames_to_copy = pa_buffer_size / frame_size;
+    size_t frame_offset_in_bus = 0;
+    do {
+      // Grab frames and get the count.
+      frames_to_copy =
+          std::min(audio_bus_->frames() - frame_offset_in_bus, frames_to_copy);
+
+      // We skip clipping since that occurs at the shared memory boundary.
+      audio_bus_->ToInterleavedPartial<Float32SampleTypeTraitsNoClip>(
+          frame_offset_in_bus, frames_to_copy,
+          reinterpret_cast<float*>(pa_buffer));
+      frame_offset_in_bus += frames_to_copy;
+      unwritten_frames_in_bus -= frames_to_copy;
+
+      if (pa_stream_write(pa_stream_, pa_buffer, pa_buffer_size, nullptr, 0LL,
+                          PA_SEEK_RELATIVE) < 0) {
+        source_callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
+        return;
+      }
+      bytes_remaining -= pa_buffer_size;
+      if (unwritten_frames_in_bus) {
+        // Reset the buffer and the size:
+        //   - If pa_buffer isn't nulled out, then it will get re-used, and
+        //     there will be a race between PA reading and us writing.
+        //   - If we don't shrink the pa_buffer_size to a small value, we get
+        //     stuttering as the memory allocation can take far too long. This
+        //     also means that we will never get more than we want, and we
+        //     dont need to memset.
+        pa_buffer = nullptr;
+        pa_buffer_size = unwritten_frames_in_bus * frame_size;
+        CHECK_GE(pa_stream_begin_write(pa_stream_, &pa_buffer, &pa_buffer_size),
+                 0);
+        frames_to_copy = pa_buffer_size / frame_size;
+      }
+    } while (unwritten_frames_in_bus);
+  }
+}
+
+void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  CHECK(callback);
+  CHECK(pa_stream_);
+  SendLogMessage("%s()", __func__);
+
+  AutoPulseLock auto_lock(pa_mainloop_);
+
+  // Ensure the context and stream are ready.
+  if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY &&
+      pa_stream_get_state(pa_stream_) != PA_STREAM_READY) {
+    callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+    return;
+  }
+
+  source_callback_ = callback;
+
+  // Uncork (resume) the stream.
+  pa_operation* operation = pa_stream_cork(
+      pa_stream_, 0, &pulse::StreamSuccessCallback, pa_mainloop_);
+  if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
+                                  pa_stream_)) {
+    callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+  }
+}
+
+void PulseAudioOutputStream::Stop() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  SendLogMessage("%s()", __func__);
+
+  // Cork (pause) the stream.  Waiting for the main loop lock will ensure
+  // outstanding callbacks have completed.
+  AutoPulseLock auto_lock(pa_mainloop_);
+
+  if (!source_callback_)
+    return;
+
+  // Set |source_callback_| to nullptr so all FulfillWriteRequest() calls which
+  // may occur while waiting on the flush and cork exit immediately.
+  auto* callback = source_callback_;
+  source_callback_ = nullptr;
+
+  // Flush the stream prior to cork, doing so after will cause hangs.  Write
+  // callbacks are suspended while inside pa_threaded_mainloop_lock() so this
+  // is all thread safe.
+  pa_operation* operation =
+      pa_stream_flush(pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
+  if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
+                                  pa_stream_)) {
+    callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+  }
+
+  operation = pa_stream_cork(pa_stream_, 1, &pulse::StreamSuccessCallback,
+                             pa_mainloop_);
+  if (!WaitForOperationCompletion(pa_mainloop_, operation, pa_context_,
+                                  pa_stream_)) {
+    callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
+  }
+}
+
+void PulseAudioOutputStream::SetVolume(double volume) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Waiting for the main loop lock will ensure outstanding callbacks have
+  // completed and |volume_| is not accessed from them.
+  AutoPulseLock auto_lock(pa_mainloop_);
+  volume_ = static_cast<float>(volume);
+}
+
+void PulseAudioOutputStream::GetVolume(double* volume) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  *volume = volume_;
+}
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/pulse/pulse_output.h b/third_party/chromium/media/audio/pulse/pulse_output.h
new file mode 100644
index 0000000..4dae18e
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/pulse_output.h
@@ -0,0 +1,115 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Creates an audio output stream based on the PulseAudio asynchronous API;
+// specifically using the pa_threaded_mainloop model.
+//
+// If the stream is successfully opened, Close() must be called before the
+// stream is deleted as Close() is responsible for ensuring resource cleanup
+// occurs.
+//
+// This object is designed so that all AudioOutputStream methods will be called
+// on the same thread that created the object.
+//
+// WARNING: This object blocks on internal PulseAudio calls in Open() while
+// waiting for PulseAudio's context structure to be ready.  It also blocks in
+// inside PulseAudio in Start() and repeated during playback, waiting for
+// PulseAudio write callbacks to occur.
+
+#ifndef MEDIA_AUDIO_PULSE_PULSE_OUTPUT_H_
+#define MEDIA_AUDIO_PULSE_PULSE_OUTPUT_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_manager.h"
+#include "media/base/audio_parameters.h"
+
+struct pa_context;
+struct pa_stream;
+struct pa_threaded_mainloop;
+
+namespace media {
+class AudioManagerBase;
+
+class PulseAudioOutputStream : public AudioOutputStream {
+ public:
+  PulseAudioOutputStream(const AudioParameters& params,
+                         const std::string& device_id,
+                         AudioManagerBase* manager,
+                         AudioManager::LogCallback log_callback);
+
+  PulseAudioOutputStream(const PulseAudioOutputStream&) = delete;
+  PulseAudioOutputStream& operator=(const PulseAudioOutputStream&) = delete;
+
+  ~PulseAudioOutputStream() override;
+
+  // Implementation of AudioOutputStream.
+  bool Open() override;
+  void Close() override;
+  void Flush() override;
+  void Start(AudioSourceCallback* callback) override;
+  void Stop() override;
+  void SetVolume(double volume) override;
+  void GetVolume(double* volume) override;
+
+ private:
+  // Helper method used for sending native logs to the registered client.
+  void SendLogMessage(const char* format, ...) PRINTF_FORMAT(2, 3);
+
+  // Called by PulseAudio when |pa_stream_| change state.  If an unexpected
+  // failure state change happens and |source_callback_| is set
+  // this method will forward the error via OnError().
+  static void StreamNotifyCallback(pa_stream* s, void* p_this);
+
+  // Called by PulseAudio when it needs more audio data.
+  static void StreamRequestCallback(pa_stream* s, size_t len, void* p_this);
+
+  // Fulfill a write request from the write request callback.  Outputs silence
+  // if the request could not be fulfilled.
+  void FulfillWriteRequest(size_t requested_bytes);
+
+  // Close() helper function to free internal structs.
+  void Reset();
+
+  // AudioParameters from the constructor.
+  const AudioParameters params_;
+
+  // The device ID for the device to open.
+  const std::string device_id_;
+
+  // Audio manager that created us.  Used to report that we've closed.
+  AudioManagerBase* manager_;
+
+  // Callback to send log messages to registered clients.
+  AudioManager::LogCallback log_callback_;
+
+  // PulseAudio API structs.
+  pa_context* pa_context_;
+  pa_threaded_mainloop* pa_mainloop_;
+  pa_stream* pa_stream_;
+
+  // Float representation of volume from 0.0 to 1.0.
+  float volume_;
+
+  // Callback to audio data source.  Must only be modified while holding a lock
+  // on |pa_mainloop_| via pa_threaded_mainloop_lock().
+  AudioSourceCallback* source_callback_;
+
+  // Container for retrieving data from AudioSourceCallback::OnMoreData().
+  std::unique_ptr<AudioBus> audio_bus_;
+
+  const size_t buffer_size_;
+
+  base::ThreadChecker thread_checker_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_PULSE_PULSE_OUTPUT_H_
diff --git a/third_party/chromium/media/audio/pulse/pulse_stub_header.fragment b/third_party/chromium/media/audio/pulse/pulse_stub_header.fragment
new file mode 100644
index 0000000..cdaa841
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/pulse_stub_header.fragment
@@ -0,0 +1,19 @@
+// The extra include header needed in the generated stub file for defining
+// various Pulse types.
+
+extern "C" {
+
+#include <pulse/pulseaudio.h>
+
+#if PA_MAJOR > 12
+typedef const pa_context* const_pa_context_ptr;
+typedef const pa_operation* const_pa_operation_ptr;
+typedef const pa_proplist* const_pa_proplist_ptr;
+typedef const pa_stream* const_pa_stream_ptr;
+#else
+typedef pa_context* const_pa_context_ptr;
+typedef pa_operation* const_pa_operation_ptr;
+typedef pa_proplist* const_pa_proplist_ptr;
+typedef pa_stream* const_pa_stream_ptr;
+#endif
+}
diff --git a/third_party/chromium/media/audio/pulse/pulse_util.cc b/third_party/chromium/media/audio/pulse/pulse_util.cc
new file mode 100644
index 0000000..9d8eb23
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/pulse_util.cc
@@ -0,0 +1,645 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/pulse/pulse_util.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/notreached.h"
+#include "base/synchronization/waitable_event.h"
+#include "build/branding_buildflags.h"
+#include "media/audio/audio_device_description.h"
+#include "media/base/audio_timestamp_helper.h"
+
+#if defined(DLOPEN_PULSEAUDIO)
+#include "media/audio/pulse/pulse_stubs.h"
+
+using media_audio_pulse::kModulePulse;
+using media_audio_pulse::InitializeStubs;
+using media_audio_pulse::StubPathMap;
+#endif  // defined(DLOPEN_PULSEAUDIO)
+
+namespace media {
+
+namespace pulse {
+
+namespace {
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+constexpr char kBrowserDisplayName[] = "google-chrome";
+#define PRODUCT_STRING "Google Chrome"
+#else
+constexpr char kBrowserDisplayName[] = "chromium-browser";
+#define PRODUCT_STRING "Chromium"
+#endif
+
+#if defined(DLOPEN_PULSEAUDIO)
+static const base::FilePath::CharType kPulseLib[] =
+    FILE_PATH_LITERAL("libpulse.so.0");
+#endif
+
+void DestroyMainloop(pa_threaded_mainloop* mainloop) {
+  pa_threaded_mainloop_stop(mainloop);
+  pa_threaded_mainloop_free(mainloop);
+}
+
+void DestroyContext(pa_context* context) {
+  pa_context_set_state_callback(context, nullptr, nullptr);
+  pa_context_disconnect(context);
+  pa_context_unref(context);
+}
+
+pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
+  switch (channel) {
+    // PulseAudio does not differentiate between left/right and
+    // stereo-left/stereo-right, both translate to front-left/front-right.
+    case LEFT:
+      return PA_CHANNEL_POSITION_FRONT_LEFT;
+    case RIGHT:
+      return PA_CHANNEL_POSITION_FRONT_RIGHT;
+    case CENTER:
+      return PA_CHANNEL_POSITION_FRONT_CENTER;
+    case LFE:
+      return PA_CHANNEL_POSITION_LFE;
+    case BACK_LEFT:
+      return PA_CHANNEL_POSITION_REAR_LEFT;
+    case BACK_RIGHT:
+      return PA_CHANNEL_POSITION_REAR_RIGHT;
+    case LEFT_OF_CENTER:
+      return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+    case RIGHT_OF_CENTER:
+      return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+    case BACK_CENTER:
+      return PA_CHANNEL_POSITION_REAR_CENTER;
+    case SIDE_LEFT:
+      return PA_CHANNEL_POSITION_SIDE_LEFT;
+    case SIDE_RIGHT:
+      return PA_CHANNEL_POSITION_SIDE_RIGHT;
+    default:
+      NOTREACHED() << "Invalid channel: " << channel;
+      return PA_CHANNEL_POSITION_INVALID;
+  }
+}
+
+class ScopedPropertyList {
+ public:
+  ScopedPropertyList() : property_list_(pa_proplist_new()) {}
+
+  ScopedPropertyList(const ScopedPropertyList&) = delete;
+  ScopedPropertyList& operator=(const ScopedPropertyList&) = delete;
+
+  ~ScopedPropertyList() { pa_proplist_free(property_list_); }
+
+  pa_proplist* get() const { return property_list_; }
+
+ private:
+  pa_proplist* property_list_;
+};
+
+struct InputBusData {
+  InputBusData(pa_threaded_mainloop* loop, const std::string& name)
+      : loop_(loop), name_(name), bus_() {}
+
+  pa_threaded_mainloop* const loop_;
+  const std::string& name_;
+  std::string bus_;
+};
+
+struct OutputBusData {
+  OutputBusData(pa_threaded_mainloop* loop, const std::string& bus)
+      : loop_(loop), name_(), bus_(bus) {}
+
+  pa_threaded_mainloop* const loop_;
+  std::string name_;
+  const std::string& bus_;
+};
+
+void InputBusCallback(pa_context* context,
+                      const pa_source_info* info,
+                      int error,
+                      void* user_data) {
+  InputBusData* data = static_cast<InputBusData*>(user_data);
+
+  if (error) {
+    // We have checked all the devices now.
+    pa_threaded_mainloop_signal(data->loop_, 0);
+    return;
+  }
+
+  if (strcmp(info->name, data->name_.c_str()) == 0 &&
+      pa_proplist_contains(info->proplist, PA_PROP_DEVICE_BUS)) {
+    data->bus_ = pa_proplist_gets(info->proplist, PA_PROP_DEVICE_BUS);
+  }
+}
+
+void OutputBusCallback(pa_context* context,
+                       const pa_sink_info* info,
+                       int error,
+                       void* user_data) {
+  OutputBusData* data = static_cast<OutputBusData*>(user_data);
+
+  if (error) {
+    // We have checked all the devices now.
+    pa_threaded_mainloop_signal(data->loop_, 0);
+    return;
+  }
+
+  if (pa_proplist_contains(info->proplist, PA_PROP_DEVICE_BUS) &&
+      strcmp(pa_proplist_gets(info->proplist, PA_PROP_DEVICE_BUS),
+             data->bus_.c_str()) == 0) {
+    data->name_ = info->name;
+  }
+}
+
+struct DefaultDevicesData {
+  explicit DefaultDevicesData(pa_threaded_mainloop* loop) : loop_(loop) {}
+  std::string input_;
+  std::string output_;
+  pa_threaded_mainloop* const loop_;
+};
+
+void GetDefaultDeviceIdCallback(pa_context* c,
+                                const pa_server_info* info,
+                                void* userdata) {
+  DefaultDevicesData* data = static_cast<DefaultDevicesData*>(userdata);
+  if (info->default_source_name)
+    data->input_ = info->default_source_name;
+  if (info->default_sink_name)
+    data->output_ = info->default_sink_name;
+  pa_threaded_mainloop_signal(data->loop_, 0);
+}
+
+struct ContextStartupData {
+  base::WaitableEvent* context_wait;
+  pa_threaded_mainloop* pa_mainloop;
+};
+
+void SignalReadyOrErrorStateCallback(pa_context* context, void* context_data) {
+  auto context_state = pa_context_get_state(context);
+  auto* data = static_cast<ContextStartupData*>(context_data);
+  if (!PA_CONTEXT_IS_GOOD(context_state) || context_state == PA_CONTEXT_READY)
+    data->context_wait->Signal();
+  pa_threaded_mainloop_signal(data->pa_mainloop, 0);
+}
+
+}  // namespace
+
+bool InitPulse(pa_threaded_mainloop** mainloop, pa_context** context) {
+#if defined(DLOPEN_PULSEAUDIO)
+  StubPathMap paths;
+
+  // Check if the pulse library is available.
+  paths[kModulePulse].push_back(kPulseLib);
+  if (!InitializeStubs(paths)) {
+    VLOG(1) << "Failed on loading the Pulse library and symbols";
+    return false;
+  }
+#endif  // defined(DLOPEN_PULSEAUDIO)
+
+  // The setup order below follows the pattern used by pa_simple_new():
+  // https://github.com/pulseaudio/pulseaudio/blob/master/src/pulse/simple.c
+
+  // Create a mainloop API and connect to the default server.
+  // The mainloop is the internal asynchronous API event loop.
+  pa_threaded_mainloop* pa_mainloop = pa_threaded_mainloop_new();
+  if (!pa_mainloop)
+    return false;
+
+  pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop);
+  pa_context* pa_context =
+      pa_context_new(pa_mainloop_api, PRODUCT_STRING " input");
+  if (!pa_context) {
+    pa_threaded_mainloop_free(pa_mainloop);
+    return false;
+  }
+
+  // We can't rely on pa_threaded_mainloop_wait() for PulseAudio startup since
+  // it can hang indefinitely. Instead we use a WaitableEvent to time out the
+  // startup process if it takes too long.
+  base::WaitableEvent context_wait;
+  ContextStartupData data = {&context_wait, pa_mainloop};
+
+  pa_context_set_state_callback(pa_context, &SignalReadyOrErrorStateCallback,
+                                &data);
+
+  if (pa_context_connect(pa_context, nullptr, PA_CONTEXT_NOAUTOSPAWN,
+                         nullptr)) {
+    VLOG(1) << "Failed to connect to the context.  Error: "
+            << pa_strerror(pa_context_errno(pa_context));
+    DestroyContext(pa_context);
+    pa_threaded_mainloop_free(pa_mainloop);
+    return false;
+  }
+
+  // Lock the event loop object, effectively blocking the event loop thread
+  // from processing events. This is necessary.
+  auto mainloop_lock = std::make_unique<AutoPulseLock>(pa_mainloop);
+
+  // Start the threaded mainloop after everything has been configured.
+  if (pa_threaded_mainloop_start(pa_mainloop)) {
+    DestroyContext(pa_context);
+    mainloop_lock.reset();
+    DestroyMainloop(pa_mainloop);
+    return false;
+  }
+
+  // Don't hold the mainloop lock while waiting for the context to become ready,
+  // or we'll never complete since PulseAudio can't continue working.
+  mainloop_lock.reset();
+
+  // Wait for up to 5 seconds for pa_context to become ready. We'll be signaled
+  // by the SignalReadyOrErrorStateCallback that we setup above.
+  //
+  // We've chosen a timeout value of 5 seconds because this can be executed at
+  // browser startup (other times it's during audio process startup). In the
+  // normal case, this should only take ~50ms, but we've seen some test bots
+  // hang indefinitely when the pulse daemon can't be started.
+  constexpr base::TimeDelta kStartupTimeout = base::Seconds(5);
+  const bool was_signaled = context_wait.TimedWait(kStartupTimeout);
+
+  // Require the mainloop lock before checking the context state.
+  mainloop_lock = std::make_unique<AutoPulseLock>(pa_mainloop);
+
+  auto context_state = pa_context_get_state(pa_context);
+  if (context_state != PA_CONTEXT_READY) {
+    if (!was_signaled)
+      VLOG(1) << "Timed out trying to connect to PulseAudio.";
+    else
+      VLOG(1) << "Failed to connect to PulseAudio: " << context_state;
+    DestroyContext(pa_context);
+    mainloop_lock.reset();
+    DestroyMainloop(pa_mainloop);
+    return false;
+  }
+
+  // Replace our function local state callback with a global appropriate one.
+  pa_context_set_state_callback(pa_context, &pulse::ContextStateCallback,
+                                pa_mainloop);
+
+  *mainloop = pa_mainloop;
+  *context = pa_context;
+  return true;
+}
+
+void DestroyPulse(pa_threaded_mainloop* mainloop, pa_context* context) {
+  DCHECK(mainloop);
+  DCHECK(context);
+
+  {
+    AutoPulseLock auto_lock(mainloop);
+    DestroyContext(context);
+  }
+
+  DestroyMainloop(mainloop);
+}
+
+// static, pa_stream_success_cb_t
+void StreamSuccessCallback(pa_stream* s, int error, void* mainloop) {
+  pa_threaded_mainloop* pa_mainloop =
+      static_cast<pa_threaded_mainloop*>(mainloop);
+  pa_threaded_mainloop_signal(pa_mainloop, 0);
+}
+
+// |pa_context| and |pa_stream| state changed cb.
+void ContextStateCallback(pa_context* context, void* mainloop) {
+  pa_threaded_mainloop* pa_mainloop =
+      static_cast<pa_threaded_mainloop*>(mainloop);
+  pa_threaded_mainloop_signal(pa_mainloop, 0);
+}
+
+pa_channel_map ChannelLayoutToPAChannelMap(ChannelLayout channel_layout) {
+  pa_channel_map channel_map;
+  if (channel_layout == CHANNEL_LAYOUT_MONO) {
+    // CHANNEL_LAYOUT_MONO only specifies audio on the C channel, but we
+    // want PulseAudio to play single-channel audio on more than just that.
+    pa_channel_map_init_mono(&channel_map);
+  } else {
+    pa_channel_map_init(&channel_map);
+
+    channel_map.channels = ChannelLayoutToChannelCount(channel_layout);
+    for (Channels ch = LEFT; ch <= CHANNELS_MAX;
+         ch = static_cast<Channels>(ch + 1)) {
+      int channel_index = ChannelOrder(channel_layout, ch);
+      if (channel_index < 0)
+        continue;
+
+      channel_map.map[channel_index] = ChromiumToPAChannelPosition(ch);
+    }
+  }
+
+  return channel_map;
+}
+
+bool WaitForOperationCompletion(pa_threaded_mainloop* mainloop,
+                                pa_operation* operation,
+                                pa_context* optional_context,
+                                pa_stream* optional_stream) {
+  if (!operation) {
+    LOG(ERROR) << "pa_operation is nullptr.";
+    return false;
+  }
+
+  while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) {
+    if (optional_context) {
+      pa_context_state_t context_state = pa_context_get_state(optional_context);
+      if (!PA_CONTEXT_IS_GOOD(context_state)) {
+        LOG(ERROR) << "pa_context went bad while waiting: state="
+                   << context_state << ", error="
+                   << pa_strerror(pa_context_errno(optional_context));
+        pa_operation_cancel(operation);
+        pa_operation_unref(operation);
+        return false;
+      }
+    }
+
+    if (optional_stream) {
+      pa_stream_state_t stream_state = pa_stream_get_state(optional_stream);
+      if (!PA_STREAM_IS_GOOD(stream_state)) {
+        LOG(ERROR) << "pa_stream went bad while waiting: " << stream_state;
+        pa_operation_cancel(operation);
+        pa_operation_unref(operation);
+        return false;
+      }
+    }
+
+    pa_threaded_mainloop_wait(mainloop);
+  }
+
+  pa_operation_unref(operation);
+  return true;
+}
+
+base::TimeDelta GetHardwareLatency(pa_stream* stream) {
+  DCHECK(stream);
+  int negative = 0;
+  pa_usec_t latency_micros = 0;
+  if (pa_stream_get_latency(stream, &latency_micros, &negative) != 0)
+    return base::TimeDelta();
+
+  if (negative)
+    return base::TimeDelta();
+
+  return base::Microseconds(latency_micros);
+}
+
+// Helper macro for CreateInput/OutputStream() to avoid code spam and
+// string bloat.
+#define RETURN_ON_FAILURE(expression, message) do { \
+  if (!(expression)) { \
+    DLOG(ERROR) << message; \
+    return false; \
+  } \
+} while (0)
+
+bool CreateInputStream(pa_threaded_mainloop* mainloop,
+                       pa_context* context,
+                       pa_stream** stream,
+                       const AudioParameters& params,
+                       const std::string& device_id,
+                       pa_stream_notify_cb_t stream_callback,
+                       void* user_data) {
+  DCHECK(mainloop);
+  DCHECK(context);
+
+  // Set sample specifications.
+  pa_sample_spec sample_specifications;
+
+  // FIXME: This should be PA_SAMPLE_FLOAT32, but there is more work needed in
+  // PulseAudioInputStream to support this.
+  static_assert(kInputSampleFormat == kSampleFormatS16,
+                "Only 16-bit input supported.");
+  sample_specifications.format = PA_SAMPLE_S16LE;
+  sample_specifications.rate = params.sample_rate();
+  sample_specifications.channels = params.channels();
+
+  // Get channel mapping and open recording stream.
+  pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
+      params.channel_layout());
+  pa_channel_map* map =
+      (source_channel_map.channels != 0) ? &source_channel_map : nullptr;
+
+  // Create a new recording stream and
+  // tells PulseAudio what the stream icon should be.
+  ScopedPropertyList property_list;
+  pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME,
+                   kBrowserDisplayName);
+  *stream = pa_stream_new_with_proplist(context, "RecordStream",
+                                        &sample_specifications, map,
+                                        property_list.get());
+  RETURN_ON_FAILURE(*stream, "failed to create PA recording stream");
+
+  pa_stream_set_state_callback(*stream, stream_callback, user_data);
+
+  // Set server-side capture buffer metrics. Detailed documentation on what
+  // values should be chosen can be found at
+  // freedesktop.org/software/pulseaudio/doxygen/structpa__buffer__attr.html.
+  pa_buffer_attr buffer_attributes;
+  const unsigned int buffer_size = params.GetBytesPerBuffer(kInputSampleFormat);
+  buffer_attributes.maxlength = static_cast<uint32_t>(-1);
+  buffer_attributes.tlength = buffer_size;
+  buffer_attributes.minreq = buffer_size;
+  buffer_attributes.prebuf = static_cast<uint32_t>(-1);
+  buffer_attributes.fragsize = buffer_size;
+  int flags = PA_STREAM_AUTO_TIMING_UPDATE |
+              PA_STREAM_INTERPOLATE_TIMING |
+              PA_STREAM_ADJUST_LATENCY |
+              PA_STREAM_START_CORKED;
+  RETURN_ON_FAILURE(
+      pa_stream_connect_record(
+          *stream,
+          device_id == AudioDeviceDescription::kDefaultDeviceId
+              ? nullptr
+              : device_id.c_str(),
+          &buffer_attributes, static_cast<pa_stream_flags_t>(flags)) == 0,
+      "pa_stream_connect_record FAILED ");
+
+  // Wait for the stream to be ready.
+  while (true) {
+    pa_stream_state_t stream_state = pa_stream_get_state(*stream);
+    RETURN_ON_FAILURE(
+        PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state");
+    if (stream_state == PA_STREAM_READY)
+        break;
+    pa_threaded_mainloop_wait(mainloop);
+  }
+
+  return true;
+}
+
+bool CreateOutputStream(pa_threaded_mainloop** mainloop,
+                        pa_context** context,
+                        pa_stream** stream,
+                        const AudioParameters& params,
+                        const std::string& device_id,
+                        const std::string& app_name,
+                        pa_stream_notify_cb_t stream_callback,
+                        pa_stream_request_cb_t write_callback,
+                        void* user_data) {
+  DCHECK(!*mainloop);
+  DCHECK(!*context);
+
+  *mainloop = pa_threaded_mainloop_new();
+  RETURN_ON_FAILURE(*mainloop, "Failed to create PulseAudio main loop.");
+
+  pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(*mainloop);
+  *context = pa_context_new(
+      pa_mainloop_api, app_name.empty() ? PRODUCT_STRING : app_name.c_str());
+  RETURN_ON_FAILURE(*context, "Failed to create PulseAudio context.");
+
+  // A state callback must be set before calling pa_threaded_mainloop_lock() or
+  // pa_threaded_mainloop_wait() calls may lead to dead lock.
+  pa_context_set_state_callback(*context, &ContextStateCallback, *mainloop);
+
+  // Lock the main loop while setting up the context.  Failure to do so may lead
+  // to crashes as the PulseAudio thread tries to run before things are ready.
+  AutoPulseLock auto_lock(*mainloop);
+
+  RETURN_ON_FAILURE(pa_threaded_mainloop_start(*mainloop) == 0,
+                    "Failed to start PulseAudio main loop.");
+  RETURN_ON_FAILURE(pa_context_connect(*context, nullptr,
+                                       PA_CONTEXT_NOAUTOSPAWN, nullptr) == 0,
+                    "Failed to connect PulseAudio context.");
+
+  // Wait until |pa_context_| is ready.  pa_threaded_mainloop_wait() must be
+  // called after pa_context_get_state() in case the context is already ready,
+  // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
+  while (true) {
+    pa_context_state_t context_state = pa_context_get_state(*context);
+    RETURN_ON_FAILURE(PA_CONTEXT_IS_GOOD(context_state),
+                      "Invalid PulseAudio context state.");
+    if (context_state == PA_CONTEXT_READY)
+      break;
+    pa_threaded_mainloop_wait(*mainloop);
+  }
+
+  // Set sample specifications.
+  pa_sample_spec sample_specifications;
+  sample_specifications.format = PA_SAMPLE_FLOAT32;
+  sample_specifications.rate = params.sample_rate();
+  sample_specifications.channels = params.channels();
+
+  // Get channel mapping.
+  pa_channel_map* map = nullptr;
+  pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
+      params.channel_layout());
+  if (source_channel_map.channels != 0) {
+    // The source data uses a supported channel map so we will use it rather
+    // than the default channel map (nullptr).
+    map = &source_channel_map;
+  }
+
+  // Open playback stream and
+  // tell PulseAudio what the stream icon should be.
+  ScopedPropertyList property_list;
+  pa_proplist_sets(property_list.get(), PA_PROP_APPLICATION_ICON_NAME,
+                   kBrowserDisplayName);
+  *stream = pa_stream_new_with_proplist(
+      *context, "Playback", &sample_specifications, map, property_list.get());
+  RETURN_ON_FAILURE(*stream, "failed to create PA playback stream");
+
+  pa_stream_set_state_callback(*stream, stream_callback, user_data);
+
+  // Even though we start the stream corked above, PulseAudio will issue one
+  // stream request after setup.  write_callback() must fulfill the write.
+  pa_stream_set_write_callback(*stream, write_callback, user_data);
+
+  // Pulse is very finicky with the small buffer sizes used by Chrome.  The
+  // settings below are mostly found through trial and error.  Essentially we
+  // want Pulse to auto size its internal buffers, but call us back nearly every
+  // |minreq| bytes.  |tlength| should be a multiple of |minreq|; too low and
+  // Pulse will issue callbacks way too fast, too high and we don't get
+  // callbacks frequently enough.
+  //
+  // Setting |minreq| to the exact buffer size leads to more callbacks than
+  // necessary, so we've clipped it to half the buffer size.  Regardless of the
+  // requested amount, we'll always fill |params.GetBytesPerBuffer()| though.
+  size_t buffer_size = params.GetBytesPerBuffer(kSampleFormatF32);
+  pa_buffer_attr pa_buffer_attributes;
+  pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
+  pa_buffer_attributes.minreq = buffer_size / 2;
+  pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
+  pa_buffer_attributes.tlength = buffer_size * 3;
+  pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);
+
+  // Connect playback stream.  Like pa_buffer_attr, the pa_stream_flags have a
+  // huge impact on the performance of the stream and were chosen through trial
+  // and error.
+  RETURN_ON_FAILURE(
+      pa_stream_connect_playback(
+          *stream,
+          device_id == AudioDeviceDescription::kDefaultDeviceId
+              ? nullptr
+              : device_id.c_str(),
+          &pa_buffer_attributes,
+          static_cast<pa_stream_flags_t>(
+              PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY |
+              PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONIC |
+              PA_STREAM_START_CORKED),
+          nullptr, nullptr) == 0,
+      "pa_stream_connect_playback FAILED ");
+
+  // Wait for the stream to be ready.
+  while (true) {
+    pa_stream_state_t stream_state = pa_stream_get_state(*stream);
+    RETURN_ON_FAILURE(
+        PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state");
+    if (stream_state == PA_STREAM_READY)
+      break;
+    pa_threaded_mainloop_wait(*mainloop);
+  }
+
+  return true;
+}
+
+std::string GetBusOfInput(pa_threaded_mainloop* mainloop,
+                          pa_context* context,
+                          const std::string& name) {
+  DCHECK(mainloop);
+  DCHECK(context);
+  AutoPulseLock auto_lock(mainloop);
+  InputBusData data(mainloop, name);
+  pa_operation* operation =
+      pa_context_get_source_info_list(context, InputBusCallback, &data);
+  WaitForOperationCompletion(mainloop, operation, context);
+  return data.bus_;
+}
+
+std::string GetOutputCorrespondingTo(pa_threaded_mainloop* mainloop,
+                                     pa_context* context,
+                                     const std::string& bus) {
+  DCHECK(mainloop);
+  DCHECK(context);
+  AutoPulseLock auto_lock(mainloop);
+  OutputBusData data(mainloop, bus);
+  pa_operation* operation =
+      pa_context_get_sink_info_list(context, OutputBusCallback, &data);
+  WaitForOperationCompletion(mainloop, operation, context);
+  return data.name_;
+}
+
+std::string GetRealDefaultDeviceId(pa_threaded_mainloop* mainloop,
+                                   pa_context* context,
+                                   RequestType type) {
+  DCHECK(mainloop);
+  DCHECK(context);
+  AutoPulseLock auto_lock(mainloop);
+  DefaultDevicesData data(mainloop);
+  pa_operation* operation =
+      pa_context_get_server_info(context, &GetDefaultDeviceIdCallback, &data);
+  WaitForOperationCompletion(mainloop, operation, context);
+  return (type == RequestType::INPUT) ? data.input_ : data.output_;
+}
+
+#undef RETURN_ON_FAILURE
+
+}  // namespace pulse
+
+}  // namespace media
diff --git a/third_party/chromium/media/audio/pulse/pulse_util.h b/third_party/chromium/media/audio/pulse/pulse_util.h
new file mode 100644
index 0000000..0e80f81
--- /dev/null
+++ b/third_party/chromium/media/audio/pulse/pulse_util.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_PULSE_PULSE_UTIL_H_
+#define MEDIA_AUDIO_PULSE_PULSE_UTIL_H_
+
+#include <pulse/pulseaudio.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "media/audio/audio_device_name.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/channel_layout.h"
+
+namespace media {
+
+class AudioParameters;
+
+namespace pulse {
+
+enum class RequestType : int8_t { INPUT, OUTPUT };
+
+// A helper class that acquires pa_threaded_mainloop_lock() while in scope.
+class AutoPulseLock {
+ public:
+  explicit AutoPulseLock(pa_threaded_mainloop* pa_mainloop)
+      : pa_mainloop_(pa_mainloop) {
+    pa_threaded_mainloop_lock(pa_mainloop_);
+  }
+
+  AutoPulseLock(const AutoPulseLock&) = delete;
+  AutoPulseLock& operator=(const AutoPulseLock&) = delete;
+
+  ~AutoPulseLock() {
+    pa_threaded_mainloop_unlock(pa_mainloop_);
+  }
+
+ private:
+  pa_threaded_mainloop* pa_mainloop_;
+};
+
+bool MEDIA_EXPORT InitPulse(pa_threaded_mainloop** mainloop,
+                            pa_context** context);
+void DestroyPulse(pa_threaded_mainloop* mainloop, pa_context* context);
+
+// Triggers pa_threaded_mainloop_signal() to avoid deadlocks.
+void StreamSuccessCallback(pa_stream* s, int error, void* mainloop);
+void ContextStateCallback(pa_context* context, void* mainloop);
+
+pa_channel_map ChannelLayoutToPAChannelMap(ChannelLayout channel_layout);
+
+// Blocks until pa_operation completes. If |optional_context| and/or
+// |optional_stream| are provided, the method will cancel |operation| and return
+// false if either the context or stream enter a bad state while waiting.
+bool WaitForOperationCompletion(pa_threaded_mainloop* mainloop,
+                                pa_operation* operation,
+                                pa_context* optional_context = nullptr,
+                                pa_stream* optional_stream = nullptr);
+
+base::TimeDelta GetHardwareLatency(pa_stream* stream);
+
+constexpr SampleFormat kInputSampleFormat = kSampleFormatS16;
+
+// Create a recording stream for the threaded mainloop, return true if success,
+// otherwise false. |mainloop| and |context| have to be from a valid Pulse
+// threaded mainloop and the handle of the created stream will be returned by
+// |stream|.
+bool CreateInputStream(pa_threaded_mainloop* mainloop,
+                       pa_context* context,
+                       pa_stream** stream,
+                       const AudioParameters& params,
+                       const std::string& device_id,
+                       pa_stream_notify_cb_t stream_callback,
+                       void* user_data);
+
+// Create a playback stream for the threaded mainloop, return true if success,
+// otherwise false. This function will create a new Pulse threaded mainloop,
+// and the handles of the mainloop, context and stream will be returned by
+// |mainloop|, |context| and |stream|.
+bool CreateOutputStream(pa_threaded_mainloop** mainloop,
+                        pa_context** context,
+                        pa_stream** stream,
+                        const AudioParameters& params,
+                        const std::string& device_id,
+                        const std::string& app_name,
+                        pa_stream_notify_cb_t stream_callback,
+                        pa_stream_request_cb_t write_callback,
+                        void* user_data);
+
+// Utility functions to match up outputs and inputs.
+std::string GetBusOfInput(pa_threaded_mainloop* mainloop,
+                          pa_context* context,
+                          const std::string& name);
+std::string GetOutputCorrespondingTo(pa_threaded_mainloop* mainloop,
+                                     pa_context* context,
+                                     const std::string& bus);
+std::string GetRealDefaultDeviceId(pa_threaded_mainloop* mainloop,
+                                   pa_context* context,
+                                   RequestType type);
+}  // namespace pulse
+
+}  // namespace media
+
+#endif  // MEDIA_AUDIO_PULSE_PULSE_UTIL_H_
diff --git a/third_party/chromium/media/audio/scoped_task_runner_observer.cc b/third_party/chromium/media/audio/scoped_task_runner_observer.cc
new file mode 100644
index 0000000..b4b7f5b
--- /dev/null
+++ b/third_party/chromium/media/audio/scoped_task_runner_observer.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/scoped_task_runner_observer.h"
+
+#include "base/bind.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace media {
+
+ScopedTaskRunnerObserver::ScopedTaskRunnerObserver(
+    const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
+    : task_runner_(task_runner) {
+  ObserveLoopDestruction(true, NULL);
+}
+
+ScopedTaskRunnerObserver::~ScopedTaskRunnerObserver() {
+  ObserveLoopDestruction(false, NULL);
+}
+
+void ScopedTaskRunnerObserver::ObserveLoopDestruction(
+    bool enable,
+    base::WaitableEvent* done) {
+  // Note: |done| may be NULL.
+  if (task_runner_->BelongsToCurrentThread()) {
+    base::CurrentThread loop = base::CurrentThread::Get();
+    if (enable) {
+      loop->AddDestructionObserver(this);
+    } else {
+      loop->RemoveDestructionObserver(this);
+    }
+  } else {
+    base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                              base::WaitableEvent::InitialState::NOT_SIGNALED);
+    if (task_runner_->PostTask(
+            FROM_HERE,
+            base::BindOnce(&ScopedTaskRunnerObserver::ObserveLoopDestruction,
+                           base::Unretained(this), enable, &event))) {
+      event.Wait();
+    } else {
+      // The message loop's thread has already terminated, so no need to wait.
+    }
+  }
+
+  if (done)
+    done->Signal();
+}
+
+}  // namespace media.
diff --git a/third_party/chromium/media/audio/scoped_task_runner_observer.h b/third_party/chromium/media/audio/scoped_task_runner_observer.h
new file mode 100644
index 0000000..7e551dc
--- /dev/null
+++ b/third_party/chromium/media/audio/scoped_task_runner_observer.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_AUDIO_SCOPED_TASK_RUNNER_OBSERVER_H_
+#define MEDIA_AUDIO_SCOPED_TASK_RUNNER_OBSERVER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/task/current_thread.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class WaitableEvent;
+}
+
+namespace media {
+
+// A common base class for AudioOutputDevice and AudioInputDevice that manages
+// a task runner and monitors it for destruction. If the object goes out of
+// scope before the task runner, the object will automatically remove itself
+// from the task runner's list of destruction observers.
+// NOTE: The class that inherits from this class must implement the
+// WillDestroyCurrentMessageLoop virtual method from DestructionObserver.
+class ScopedTaskRunnerObserver
+    : public base::CurrentThread::DestructionObserver {
+ public:
+  explicit ScopedTaskRunnerObserver(
+      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
+
+ protected:
+  ~ScopedTaskRunnerObserver() override;
+
+  // Accessor to the loop that's used by the derived class.
+  const scoped_refptr<base::SingleThreadTaskRunner>& task_runner() {
+    return task_runner_;
+  }
+
+ private:
+  // Call to add or remove ourselves from the list of destruction observers for
+  // the message loop.
+  void ObserveLoopDestruction(bool enable, base::WaitableEvent* done);
+
+  // A pointer to the task runner. In case it gets destroyed before this object
+  // goes out of scope, PostTask() etc will fail but not crash.
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedTaskRunnerObserver);
+};
+
+}  // namespace media.
+
+#endif  // MEDIA_AUDIO_SCOPED_TASK_RUNNER_OBSERVER_H_
diff --git a/third_party/chromium/media/audio/simple_sources.cc b/third_party/chromium/media/audio/simple_sources.cc
new file mode 100644
index 0000000..004df99
--- /dev/null
+++ b/third_party/chromium/media/audio/simple_sources.cc
@@ -0,0 +1,322 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/simple_sources.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "base/files/file.h"
+#include "base/logging.h"
+#include "base/numerics/math_constants.h"
+#include "base/thread_annotations.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "media/audio/wav_audio_handler.h"
+#include "media/base/audio_bus.h"
+
+namespace media {
+namespace {
+// Opens |wav_filename|, reads it and loads it as a wav file. This function will
+// return a null pointer if we can't read the file or if it's malformed. The
+// caller takes ownership of the returned data. The size of the data is stored
+// in |read_length|.
+std::unique_ptr<char[]> ReadWavFile(const base::FilePath& wav_filename,
+                                    size_t* read_length) {
+  base::File wav_file(
+      wav_filename, base::File::FLAG_OPEN | base::File::FLAG_READ);
+  if (!wav_file.IsValid()) {
+    LOG(ERROR) << "Failed to read " << wav_filename.value()
+               << " as input to the fake device."
+                  " Try disabling the sandbox with --no-sandbox.";
+    return nullptr;
+  }
+
+  int64_t wav_file_length = wav_file.GetLength();
+  if (wav_file_length < 0) {
+    LOG(ERROR) << "Failed to get size of " << wav_filename.value();
+    return nullptr;
+  }
+  if (wav_file_length == 0) {
+    LOG(ERROR) << "Input file to fake device is empty: "
+               << wav_filename.value();
+    return nullptr;
+  }
+
+  std::unique_ptr<char[]> data(new char[wav_file_length]);
+  int read_bytes = wav_file.Read(0, data.get(), wav_file_length);
+  if (read_bytes != wav_file_length) {
+    LOG(ERROR) << "Failed to read all bytes of " << wav_filename.value();
+    return nullptr;
+  }
+  *read_length = wav_file_length;
+  return data;
+}
+
+// These values are based on experiments for local-to-local
+// PeerConnection to demonstrate audio/video synchronization.
+static const int kBeepDurationMilliseconds = 20;
+static const int kBeepFrequency = 400;
+
+// Intervals between two automatic beeps.
+static const int kAutomaticBeepIntervalInMs = 500;
+
+// Automatic beep will be triggered every |kAutomaticBeepIntervalInMs| unless
+// users explicitly call BeepOnce(), which will disable the automatic beep.
+class BeepContext {
+ public:
+  BeepContext() : beep_once_(false), automatic_beep_(true) {}
+
+  void SetBeepOnce(bool enable) {
+    base::AutoLock auto_lock(lock_);
+    beep_once_ = enable;
+
+    // Disable the automatic beep if users explicit set |beep_once_| to true.
+    if (enable)
+      automatic_beep_ = false;
+  }
+
+  bool beep_once() const {
+    base::AutoLock auto_lock(lock_);
+    return beep_once_;
+  }
+
+  bool automatic_beep() const {
+    base::AutoLock auto_lock(lock_);
+    return automatic_beep_;
+  }
+
+ private:
+  mutable base::Lock lock_;
+  bool beep_once_ GUARDED_BY(lock_);
+  bool automatic_beep_ GUARDED_BY(lock_);
+};
+
+BeepContext* GetBeepContext() {
+  static BeepContext* context = new BeepContext();
+  return context;
+}
+
+}  // namespace
+
+//////////////////////////////////////////////////////////////////////////////
+// SineWaveAudioSource implementation.
+
+SineWaveAudioSource::SineWaveAudioSource(int channels,
+