Import Cobalt 21.lts.1.283720
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index 04eb0a6..f2872b4 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -102,6 +102,23 @@
    (https://lottiefiles.com/web-player). In order to support Lottie, Cobalt
    updated its Skia port from m61 to m79.
 
+ - **Added support for MediaKeySystemMediaCapability.encryptionScheme.**
+
+   Cobalt now supports `MediaKeySystemMediaCapability.encryptionScheme` for
+   `Navigator.requestMediaKeySystemAccess()`. `encryptionScheme` can be 'cenc',
+   'cbcs', or 'cbcs-1-9'.
+   The default implementation assumes that:
+   1. When the Widevine DRM system is used, all the above encryption schemes
+      should be supported across all containers and codecs supported by the
+      platform.
+   2. When the PlayReady DRM system is used, only 'cenc' is supported across all
+      containers and codecs supported by the platform.
+
+   It is possible to customize this behavior via an extension to
+  `SbMediaCanPlayMimeAndKeySystem()`.  Please see the Starboard change log and
+   the comment of `SbMediaCanPlayMimeAndKeySystem()` in `media.h` for more
+   details.
+
 ## Version 20
 
  - **Support for QUIC and SPDY is now enabled.**
diff --git a/src/cobalt/audio/async_audio_decoder.cc b/src/cobalt/audio/async_audio_decoder.cc
index 174cc57..c7885a1 100644
--- a/src/cobalt/audio/async_audio_decoder.cc
+++ b/src/cobalt/audio/async_audio_decoder.cc
@@ -38,7 +38,7 @@
     decode_finish_callback.Run(reader->sample_rate(),
                                reader->ResetAndReturnAudioBus());
   } else {
-    decode_finish_callback.Run(0.f, std::unique_ptr<ShellAudioBus>());
+    decode_finish_callback.Run(0.f, std::unique_ptr<AudioBus>());
   }
 }
 
diff --git a/src/cobalt/audio/async_audio_decoder.h b/src/cobalt/audio/async_audio_decoder.h
index ced6fed..db8a15a 100644
--- a/src/cobalt/audio/async_audio_decoder.h
+++ b/src/cobalt/audio/async_audio_decoder.h
@@ -28,7 +28,7 @@
 class AsyncAudioDecoder {
  public:
   typedef base::Callback<void(float sample_rate,
-                              std::unique_ptr<ShellAudioBus> audio_bus)>
+                              std::unique_ptr<AudioBus> audio_bus)>
       DecodeFinishCallback;
 
   AsyncAudioDecoder();
diff --git a/src/cobalt/audio/audio_buffer.cc b/src/cobalt/audio/audio_buffer.cc
index 1b6ff38..0a4c203 100644
--- a/src/cobalt/audio/audio_buffer.cc
+++ b/src/cobalt/audio/audio_buffer.cc
@@ -25,8 +25,7 @@
 namespace cobalt {
 namespace audio {
 
-AudioBuffer::AudioBuffer(float sample_rate,
-                         std::unique_ptr<ShellAudioBus> audio_bus)
+AudioBuffer::AudioBuffer(float sample_rate, std::unique_ptr<AudioBus> audio_bus)
     : sample_rate_(sample_rate), audio_bus_(std::move(audio_bus)) {
   DCHECK_GT(sample_rate_, 0);
   DCHECK_GT(length(), 0);
diff --git a/src/cobalt/audio/audio_buffer.h b/src/cobalt/audio/audio_buffer.h
index 9028a2b..d20beed 100644
--- a/src/cobalt/audio/audio_buffer.h
+++ b/src/cobalt/audio/audio_buffer.h
@@ -39,7 +39,7 @@
 //   https://www.w3.org/TR/webaudio/#AudioBuffer
 class AudioBuffer : public script::Wrappable {
  public:
-  AudioBuffer(float sample_rate, std::unique_ptr<ShellAudioBus> audio_bus);
+  AudioBuffer(float sample_rate, std::unique_ptr<AudioBus> audio_bus);
 
   // Web API: AudioBuffer
   //
@@ -60,14 +60,14 @@
 
   // Custom, not in any spec
   //
-  ShellAudioBus* audio_bus() { return audio_bus_.get(); }
+  AudioBus* audio_bus() { return audio_bus_.get(); }
 
   DEFINE_WRAPPABLE_TYPE(AudioBuffer);
 
  private:
   const float sample_rate_;
 
-  std::unique_ptr<ShellAudioBus> audio_bus_;
+  std::unique_ptr<AudioBus> audio_bus_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioBuffer);
 };
diff --git a/src/cobalt/audio/audio_buffer_source_node.cc b/src/cobalt/audio/audio_buffer_source_node.cc
index 654e6a8..df8ca6b 100644
--- a/src/cobalt/audio/audio_buffer_source_node.cc
+++ b/src/cobalt/audio/audio_buffer_source_node.cc
@@ -27,7 +27,7 @@
 namespace audio {
 
 typedef media::InterleavedSincResampler InterleavedSincResampler;
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
 
 // numberOfInputs  : 0
 // numberOfOutputs : 1
@@ -115,7 +115,7 @@
   state_ = kStopped;
 }
 
-std::unique_ptr<ShellAudioBus> AudioBufferSourceNode::PassAudioBusFromSource(
+std::unique_ptr<AudioBus> AudioBufferSourceNode::PassAudioBusFromSource(
     int32 number_of_frames, SampleType sample_type, bool* finished) {
   DCHECK_GT(number_of_frames, 0);
   DCHECK(finished);
@@ -126,7 +126,7 @@
   *finished = false;
 
   if (state_ == kNone || !buffer_) {
-    return std::unique_ptr<ShellAudioBus>();
+    return std::unique_ptr<AudioBus>();
   }
 
   if (state_ == kStopped ||
@@ -140,7 +140,7 @@
                                 base::Unretained(this)));
       buffer_source_added_ = false;
     }
-    return std::unique_ptr<ShellAudioBus>();
+    return std::unique_ptr<AudioBus>();
   }
 
   DCHECK_EQ(state_, kStarted);
@@ -151,22 +151,22 @@
   int32 frames_to_end = buffer_->length() - read_index_;
   int32 channel_count = static_cast<int32>(audio_bus->channels());
 
-  std::unique_ptr<ShellAudioBus> result;
+  std::unique_ptr<AudioBus> result;
 
   if (!interleaved_resampler_) {
     int32 audio_bus_frames = std::min(number_of_frames, frames_to_end);
     if (sample_type == kSampleTypeInt16) {
-      result.reset(new ShellAudioBus(
-          channel_count, audio_bus_frames,
-          reinterpret_cast<int16*>(audio_bus->interleaved_data()) +
-              read_index_ * channel_count));
+      result.reset(
+          new AudioBus(channel_count, audio_bus_frames,
+                       reinterpret_cast<int16*>(audio_bus->interleaved_data()) +
+                           read_index_ * channel_count));
     } else {
       DCHECK_EQ(sample_type, kSampleTypeFloat32);
 
-      result.reset(new ShellAudioBus(
-          channel_count, audio_bus_frames,
-          reinterpret_cast<float*>(audio_bus->interleaved_data()) +
-              read_index_ * channel_count));
+      result.reset(
+          new AudioBus(channel_count, audio_bus_frames,
+                       reinterpret_cast<float*>(audio_bus->interleaved_data()) +
+                           read_index_ * channel_count));
     }
     read_index_ += audio_bus_frames;
     return result;
@@ -219,8 +219,8 @@
     interleaved_resampler_->Resample(interleaved_output.get(),
                                      number_of_frames);
 
-    result.reset(new ShellAudioBus(channel_count, number_of_frames,
-                                   kSampleTypeInt16, kStorageTypeInterleaved));
+    result.reset(new AudioBus(channel_count, number_of_frames, kSampleTypeInt16,
+                              kStorageTypeInterleaved));
     for (int32 i = 0; i < channel_count * number_of_frames; ++i) {
       uint8* dest_ptr = result->interleaved_data() + sizeof(int16) * i;
       *reinterpret_cast<int16*>(dest_ptr) =
@@ -229,9 +229,8 @@
   } else {
     DCHECK_EQ(sample_type, kSampleTypeFloat32);
 
-    result.reset(new ShellAudioBus(channel_count, number_of_frames,
-                                   kSampleTypeFloat32,
-                                   kStorageTypeInterleaved));
+    result.reset(new AudioBus(channel_count, number_of_frames,
+                              kSampleTypeFloat32, kStorageTypeInterleaved));
     interleaved_resampler_->Resample(
         reinterpret_cast<float*>(result->interleaved_data()), number_of_frames);
   }
diff --git a/src/cobalt/audio/audio_buffer_source_node.h b/src/cobalt/audio/audio_buffer_source_node.h
index 1731aef..b01f54c 100644
--- a/src/cobalt/audio/audio_buffer_source_node.h
+++ b/src/cobalt/audio/audio_buffer_source_node.h
@@ -22,8 +22,8 @@
 #include "cobalt/audio/audio_buffer.h"
 #include "cobalt/audio/audio_node.h"
 #include "cobalt/base/tokens.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/media/base/interleaved_sinc_resampler.h"
-#include "cobalt/media/base/shell_audio_bus.h"
 #include "cobalt/script/environment_settings.h"
 
 namespace cobalt {
@@ -35,7 +35,7 @@
 //   https://www.w3.org/TR/webaudio/#AudioBufferSourceNode
 class AudioBufferSourceNode : public AudioNode {
   typedef media::InterleavedSincResampler InterleavedSincResampler;
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
  public:
   AudioBufferSourceNode(script::EnvironmentSettings* settings,
@@ -75,8 +75,9 @@
     SetAttributeEventListener(base::Tokens::ended(), event_listener);
   }
 
-  std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(
-      int32 number_of_frames, SampleType sample_type, bool* finished) override;
+  std::unique_ptr<AudioBus> PassAudioBusFromSource(int32 number_of_frames,
+                                                   SampleType sample_type,
+                                                   bool* finished) override;
 
   DEFINE_WRAPPABLE_TYPE(AudioBufferSourceNode);
   void TraceMembers(script::Tracer* tracer) override;
diff --git a/src/cobalt/audio/audio_context.cc b/src/cobalt/audio/audio_context.cc
index e909b72..6c4808e 100644
--- a/src/cobalt/audio/audio_context.cc
+++ b/src/cobalt/audio/audio_context.cc
@@ -64,7 +64,7 @@
   DCHECK(main_message_loop_->BelongsToCurrentThread());
 
   return scoped_refptr<AudioBuffer>(new AudioBuffer(
-      sample_rate, std::unique_ptr<ShellAudioBus>(new ShellAudioBus(
+      sample_rate, std::unique_ptr<AudioBus>(new AudioBus(
                        num_of_channels, length, GetPreferredOutputSampleType(),
                        kStorageTypeInterleaved))));
 }
@@ -151,7 +151,7 @@
 // Success callback and error callback should be scheduled to run on the main
 // thread's event loop.
 void AudioContext::DecodeFinish(int callback_id, float sample_rate,
-                                std::unique_ptr<ShellAudioBus> audio_bus) {
+                                std::unique_ptr<AudioBus> audio_bus) {
   if (!main_message_loop_->BelongsToCurrentThread()) {
     main_message_loop_->PostTask(
         FROM_HERE,
diff --git a/src/cobalt/audio/audio_context.h b/src/cobalt/audio/audio_context.h
index a4667bf..536b2bd 100644
--- a/src/cobalt/audio/audio_context.h
+++ b/src/cobalt/audio/audio_context.h
@@ -190,7 +190,7 @@
 
   void DecodeAudioDataInternal(std::unique_ptr<DecodeCallbackInfo> info);
   void DecodeFinish(int callback_id, float sample_rate,
-                    std::unique_ptr<ShellAudioBus> audio_bus);
+                    std::unique_ptr<AudioBus> audio_bus);
 
   script::GlobalEnvironment* global_environment_;
 
diff --git a/src/cobalt/audio/audio_destination_node.cc b/src/cobalt/audio/audio_destination_node.cc
index 4107070..e35f1f9 100644
--- a/src/cobalt/audio/audio_destination_node.cc
+++ b/src/cobalt/audio/audio_destination_node.cc
@@ -64,8 +64,7 @@
   audio_device_to_delete_ = NULL;
 }
 
-void AudioDestinationNode::FillAudioBus(bool all_consumed,
-                                        ShellAudioBus* audio_bus,
+void AudioDestinationNode::FillAudioBus(bool all_consumed, AudioBus* audio_bus,
                                         bool* silence) {
   // This is called on Audio thread.
   AudioLock::AutoLock lock(audio_lock());
diff --git a/src/cobalt/audio/audio_destination_node.h b/src/cobalt/audio/audio_destination_node.h
index 8744034..b758d92 100644
--- a/src/cobalt/audio/audio_destination_node.h
+++ b/src/cobalt/audio/audio_destination_node.h
@@ -22,7 +22,7 @@
 #include "cobalt/audio/audio_device.h"
 #include "cobalt/audio/audio_helpers.h"
 #include "cobalt/audio/audio_node.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/script/environment_settings.h"
 
 namespace cobalt {
@@ -37,7 +37,7 @@
 //   https://www.w3.org/TR/webaudio/#AudioDestinationNode
 class AudioDestinationNode : public AudioNode,
                              public AudioDevice::RenderCallback {
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
  public:
   AudioDestinationNode(script::EnvironmentSettings* settings,
@@ -51,14 +51,15 @@
 
   // From AudioNode.
   void OnInputNodeConnected() override;
-  std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(
-      int32 number_of_frames, SampleType sample_type, bool* finished) override {
+  std::unique_ptr<AudioBus> PassAudioBusFromSource(int32 number_of_frames,
+                                                   SampleType sample_type,
+                                                   bool* finished) override {
     NOTREACHED();
-    return std::unique_ptr<ShellAudioBus>();
+    return std::unique_ptr<AudioBus>();
   }
 
   // From AudioDevice::RenderCallback.
-  void FillAudioBus(bool all_consumed, ShellAudioBus* audio_bus,
+  void FillAudioBus(bool all_consumed, AudioBus* audio_bus,
                     bool* silence) override;
 
   DEFINE_WRAPPABLE_TYPE(AudioDestinationNode);
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index d253f51..b177d07 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -27,7 +27,7 @@
 namespace cobalt {
 namespace audio {
 
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
 
 namespace {
 const int kRenderBufferSizeFrames = 1024;
@@ -69,7 +69,7 @@
   // The |render_callback_| returns audio data in planar form.  So we read it
   // into |input_audio_bus_| and convert it into interleaved form and store in
   // |output_frame_buffer_|.
-  ShellAudioBus input_audio_bus_;
+  AudioBus input_audio_bus_;
 
   std::unique_ptr<uint8[]> output_frame_buffer_;
 
@@ -102,7 +102,7 @@
 #endif  // SB_API_VERSION >= 11
       input_audio_bus_(static_cast<size_t>(number_of_channels),
                        static_cast<size_t>(kRenderBufferSizeFrames),
-                       GetPreferredOutputSampleType(), ShellAudioBus::kPlanar),
+                       GetPreferredOutputSampleType(), AudioBus::kPlanar),
       output_frame_buffer_(
           new uint8[frames_per_channel_ * number_of_channels_ *
                     GetStarboardSampleTypeSize(output_sample_type_)]) {
@@ -226,8 +226,8 @@
     for (size_t channel = 0; channel < input_audio_bus_.channels(); ++channel) {
       *output_buffer = ConvertSample<InputType, OutputType>(
           input_audio_bus_
-              .GetSampleForType<InputType, media::ShellAudioBus::kPlanar>(
-                  channel, frame));
+              .GetSampleForType<InputType, media::AudioBus::kPlanar>(channel,
+                                                                     frame));
       ++output_buffer;
     }
   }
@@ -237,7 +237,7 @@
   TRACE_EVENT0("cobalt::audio", "AudioDevice::Impl::FillOutputAudioBus()");
 
   const bool is_input_int16 =
-      input_audio_bus_.sample_type() == media::ShellAudioBus::kInt16;
+      input_audio_bus_.sample_type() == media::AudioBus::kInt16;
   const bool is_output_int16 =
       output_sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated;
 
diff --git a/src/cobalt/audio/audio_device.h b/src/cobalt/audio/audio_device.h
index b7974a7..1ccc7f9 100644
--- a/src/cobalt/audio/audio_device.h
+++ b/src/cobalt/audio/audio_device.h
@@ -19,7 +19,7 @@
 #include <vector>
 
 #include "base/basictypes.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 
 namespace cobalt {
 namespace audio {
@@ -28,7 +28,7 @@
  public:
   class RenderCallback {
    public:
-    typedef media::ShellAudioBus ShellAudioBus;
+    typedef media::AudioBus AudioBus;
 
     // |all_consumed| will be set to true if all audio frames has been consumed.
     // This gives the AudioDestinationNode a chance to decide if the AudioDevice
@@ -38,7 +38,7 @@
     // |silence| will be set to true before calling if |audio_buffer| contains
     // only silence samples, it will be set to |false| otherwise.  It will be
     // set to false on return if |audio_buffer| has been modified.
-    virtual void FillAudioBus(bool all_consumed, ShellAudioBus* audio_buffer,
+    virtual void FillAudioBus(bool all_consumed, AudioBus* audio_buffer,
                               bool* silence) = 0;
 
    protected:
diff --git a/src/cobalt/audio/audio_file_reader.h b/src/cobalt/audio/audio_file_reader.h
index f53eae4..6a20209 100644
--- a/src/cobalt/audio/audio_file_reader.h
+++ b/src/cobalt/audio/audio_file_reader.h
@@ -35,7 +35,7 @@
   virtual int32 number_of_channels() const = 0;
   virtual SampleType sample_type() const = 0;
 
-  virtual std::unique_ptr<ShellAudioBus> ResetAndReturnAudioBus() = 0;
+  virtual std::unique_ptr<AudioBus> ResetAndReturnAudioBus() = 0;
 };
 
 }  // namespace audio
diff --git a/src/cobalt/audio/audio_file_reader_wav.cc b/src/cobalt/audio/audio_file_reader_wav.cc
index 94c0bf9..f92b8bd 100644
--- a/src/cobalt/audio/audio_file_reader_wav.cc
+++ b/src/cobalt/audio/audio_file_reader_wav.cc
@@ -192,9 +192,8 @@
       static_cast<int32>(size / (bytes_per_src_sample * number_of_channels_));
 
   // We store audio samples in the current platform's preferred format.
-  audio_bus_.reset(new ShellAudioBus(number_of_channels_, number_of_frames_,
-                                     sample_type_,
-                                     ShellAudioBus::kInterleaved));
+  audio_bus_.reset(new AudioBus(number_of_channels_, number_of_frames_,
+                                sample_type_, AudioBus::kInterleaved));
 
 // Both the source data and the destination data are stored in interleaved.
 #if SB_IS(LITTLE_ENDIAN)
diff --git a/src/cobalt/audio/audio_file_reader_wav.h b/src/cobalt/audio/audio_file_reader_wav.h
index 460f268..bf3b95a 100644
--- a/src/cobalt/audio/audio_file_reader_wav.h
+++ b/src/cobalt/audio/audio_file_reader_wav.h
@@ -36,7 +36,7 @@
   int32 number_of_channels() const override { return number_of_channels_; }
   SampleType sample_type() const override { return sample_type_; }
 
-  std::unique_ptr<ShellAudioBus> ResetAndReturnAudioBus() override {
+  std::unique_ptr<AudioBus> ResetAndReturnAudioBus() override {
     return std::move(audio_bus_);
   }
 
@@ -52,7 +52,7 @@
 
   bool is_valid() { return audio_bus_ != NULL; }
 
-  std::unique_ptr<ShellAudioBus> audio_bus_;
+  std::unique_ptr<AudioBus> audio_bus_;
   float sample_rate_;
   int32 number_of_frames_;
   int32 number_of_channels_;
diff --git a/src/cobalt/audio/audio_helpers.h b/src/cobalt/audio/audio_helpers.h
index b4dd6f9..dc98086 100644
--- a/src/cobalt/audio/audio_helpers.h
+++ b/src/cobalt/audio/audio_helpers.h
@@ -15,7 +15,7 @@
 #ifndef COBALT_AUDIO_AUDIO_HELPERS_H_
 #define COBALT_AUDIO_AUDIO_HELPERS_H_
 
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 
 #if defined(OS_STARBOARD)
 #include "starboard/audio_sink.h"
@@ -25,13 +25,13 @@
 namespace cobalt {
 namespace audio {
 
-typedef media::ShellAudioBus ShellAudioBus;
-typedef media::ShellAudioBus::SampleType SampleType;
-typedef media::ShellAudioBus::StorageType StorageType;
-const SampleType kSampleTypeInt16 = media::ShellAudioBus::kInt16;
-const SampleType kSampleTypeFloat32 = media::ShellAudioBus::kFloat32;
-const StorageType kStorageTypeInterleaved = media::ShellAudioBus::kInterleaved;
-const StorageType kStorageTypePlanar = media::ShellAudioBus::kPlanar;
+typedef media::AudioBus AudioBus;
+typedef media::AudioBus::SampleType SampleType;
+typedef media::AudioBus::StorageType StorageType;
+const SampleType kSampleTypeInt16 = media::AudioBus::kInt16;
+const SampleType kSampleTypeFloat32 = media::AudioBus::kFloat32;
+const StorageType kStorageTypeInterleaved = media::AudioBus::kInterleaved;
+const StorageType kStorageTypePlanar = media::AudioBus::kPlanar;
 
 const float kMaxInt16AsFloat32 = 32767.0f;
 
@@ -52,7 +52,7 @@
 #endif
 
 // Get the size in bytes of an internal sample type, which is an alias for
-// media::ShellAudioBus::SampleType.
+// media::AudioBus::SampleType.
 inline size_t GetSampleTypeSize(SampleType sample_type) {
   switch (sample_type) {
     case kSampleTypeInt16:
diff --git a/src/cobalt/audio/audio_node.h b/src/cobalt/audio/audio_node.h
index 1e9c1dd..925356c 100644
--- a/src/cobalt/audio/audio_node.h
+++ b/src/cobalt/audio/audio_node.h
@@ -27,7 +27,7 @@
 #include "cobalt/audio/audio_node_output.h"
 #include "cobalt/dom/dom_exception.h"
 #include "cobalt/dom/event_target.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/script/environment_settings.h"
 
 namespace cobalt {
@@ -49,7 +49,7 @@
 // (if it has any).
 //   https://www.w3.org/TR/webaudio/#AudioNode-section
 class AudioNode : public dom::EventTarget {
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
  public:
   AudioNode(script::EnvironmentSettings* settings, AudioContext* context);
@@ -108,7 +108,7 @@
   // Called when a new input node has been connected.
   virtual void OnInputNodeConnected() {}
 
-  virtual std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(
+  virtual std::unique_ptr<AudioBus> PassAudioBusFromSource(
       int32 number_of_frames, SampleType sample_type, bool* finished) = 0;
 
   AudioLock* audio_lock() const { return audio_lock_.get(); }
diff --git a/src/cobalt/audio/audio_node_input.cc b/src/cobalt/audio/audio_node_input.cc
index f8f5503..977d67e 100644
--- a/src/cobalt/audio/audio_node_input.cc
+++ b/src/cobalt/audio/audio_node_input.cc
@@ -26,12 +26,12 @@
 
 namespace {
 
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
 
 void MixAudioBufferBasedOnInterpretation(
     const float* speaker, const float* discrete,
-    const AudioNodeChannelInterpretation& interpretation, ShellAudioBus* source,
-    ShellAudioBus* output_audio_data) {
+    const AudioNodeChannelInterpretation& interpretation, AudioBus* source,
+    AudioBus* output_audio_data) {
   const float* kMatrix =
       interpretation == kAudioNodeChannelInterpretationSpeakers ? speaker
                                                                 : discrete;
@@ -49,7 +49,7 @@
 // Up down mix equations for mono, stereo, quad, 5.1:
 //   https://www.w3.org/TR/webaudio/#ChannelLayouts
 void MixAudioBuffer(const AudioNodeChannelInterpretation& interpretation,
-                    ShellAudioBus* source, ShellAudioBus* output_audio_data) {
+                    AudioBus* source, AudioBus* output_audio_data) {
   DCHECK_GT(source->channels(), 0u);
   DCHECK_GT(output_audio_data->channels(), 0u);
   DCHECK(interpretation == kAudioNodeChannelInterpretationSpeakers ||
@@ -224,8 +224,8 @@
   }
 }
 
-void AudioNodeInput::FillAudioBus(ShellAudioBus* output_audio_bus,
-                                  bool* silence, bool* all_finished) {
+void AudioNodeInput::FillAudioBus(AudioBus* output_audio_bus, bool* silence,
+                                  bool* all_finished) {
   DCHECK(silence);
   DCHECK(all_finished);
 
@@ -247,7 +247,7 @@
   for (std::set<AudioNodeOutput*>::iterator iter = outputs_.begin();
        iter != outputs_.end(); ++iter) {
     bool finished = false;
-    std::unique_ptr<ShellAudioBus> audio_bus = (*iter)->PassAudioBusFromSource(
+    std::unique_ptr<AudioBus> audio_bus = (*iter)->PassAudioBusFromSource(
         static_cast<int32>(output_audio_bus->frames()),
         output_audio_bus->sample_type(), &finished);
     *all_finished &= finished;
diff --git a/src/cobalt/audio/audio_node_input.h b/src/cobalt/audio/audio_node_input.h
index 74aca8b..1c5bee1 100644
--- a/src/cobalt/audio/audio_node_input.h
+++ b/src/cobalt/audio/audio_node_input.h
@@ -20,7 +20,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "cobalt/audio/audio_buffer.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 
 namespace cobalt {
 namespace audio {
@@ -36,7 +36,7 @@
 // number can change depending on the connection(s) made to the input. If the
 // input has no connections, then it has one channel which is silent.
 class AudioNodeInput : public base::RefCountedThreadSafe<AudioNodeInput> {
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
  public:
   explicit AudioNodeInput(AudioNode* owner_node) : owner_node_(owner_node) {}
@@ -50,8 +50,7 @@
   // For each input, an AudioNode performs a mixing of all connections to that
   // input. FillAudioBus() performs that action. In the case of multiple
   // connections, it sums the result into |audio_bus|.
-  void FillAudioBus(ShellAudioBus* audio_bus, bool* silence,
-                    bool* all_finished);
+  void FillAudioBus(AudioBus* audio_bus, bool* silence, bool* all_finished);
 
  private:
   AudioNode* const owner_node_;
diff --git a/src/cobalt/audio/audio_node_input_output_test.cc b/src/cobalt/audio/audio_node_input_output_test.cc
index 970254a..41a2ca2 100644
--- a/src/cobalt/audio/audio_node_input_output_test.cc
+++ b/src/cobalt/audio/audio_node_input_output_test.cc
@@ -27,18 +27,18 @@
 #include "cobalt/script/typed_arrays.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-// TODO: Consolidate ShellAudioBus creation code
+// TODO: Consolidate AudioBus creation code
 
 namespace cobalt {
 namespace audio {
 
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
 
 constexpr int kRenderBufferSizeFrames = 32;
 
 class AudioDestinationNodeMock : public AudioNode,
                                  public AudioDevice::RenderCallback {
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
  public:
   AudioDestinationNodeMock(script::EnvironmentSettings* settings,
@@ -50,14 +50,15 @@
   }
 
   // From AudioNode.
-  std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(
-      int32 number_of_frames, SampleType sample_type, bool* finished) override {
+  std::unique_ptr<AudioBus> PassAudioBusFromSource(int32 number_of_frames,
+                                                   SampleType sample_type,
+                                                   bool* finished) override {
     NOTREACHED();
-    return std::unique_ptr<ShellAudioBus>();
+    return std::unique_ptr<AudioBus>();
   }
 
   // From AudioDevice::RenderCallback.
-  void FillAudioBus(bool all_consumed, ShellAudioBus* audio_bus,
+  void FillAudioBus(bool all_consumed, AudioBus* audio_bus,
                     bool* silence) override {
     AudioLock::AutoLock lock(audio_lock());
 
@@ -68,9 +69,9 @@
 };
 
 void FillAudioBusFromOneSource(
-    std::unique_ptr<ShellAudioBus> src_data,
-    const AudioNodeChannelInterpretation& interpretation,
-    ShellAudioBus* audio_bus, bool* silence) {
+    std::unique_ptr<AudioBus> src_data,
+    const AudioNodeChannelInterpretation& interpretation, AudioBus* audio_bus,
+    bool* silence) {
   dom::testing::StubEnvironmentSettings environment_settings;
 
   scoped_refptr<AudioContext> audio_context(
@@ -139,11 +140,11 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -180,12 +181,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -219,12 +220,12 @@
     src_data_in_float[i] = 50.0f;
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -254,12 +255,12 @@
     src_data_in_float[i] = 50.0f;
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -292,12 +293,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -334,12 +335,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -376,12 +377,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -418,12 +419,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -460,12 +461,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -498,12 +499,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -536,12 +537,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -574,12 +575,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -612,12 +613,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -650,12 +651,12 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data(
-      new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+  std::unique_ptr<AudioBus> src_data(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -691,8 +692,8 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data_1(new ShellAudioBus(
-      kNumOfSrcChannels, kNumOfFrames_1, src_data_in_float_1));
+  std::unique_ptr<AudioBus> src_data_1(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames_1, src_data_in_float_1));
   scoped_refptr<AudioBufferSourceNode> source_1(
       audio_context->CreateBufferSource(environment_settings()));
   scoped_refptr<AudioBuffer> buffer_1(
@@ -709,8 +710,8 @@
     }
   }
 
-  std::unique_ptr<ShellAudioBus> src_data_2(new ShellAudioBus(
-      kNumOfSrcChannels, kNumOfFrames_2, src_data_in_float_2));
+  std::unique_ptr<AudioBus> src_data_2(
+      new AudioBus(kNumOfSrcChannels, kNumOfFrames_2, src_data_in_float_2));
   scoped_refptr<AudioBufferSourceNode> source_2(
       audio_context->CreateBufferSource(environment_settings()));
   scoped_refptr<AudioBuffer> buffer_2(
@@ -726,9 +727,9 @@
   source_1->Start(0, 0, NULL);
   source_2->Start(0, 0, NULL);
 
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+                   AudioBus::kFloat32, AudioBus::kPlanar));
   audio_bus->ZeroAllFrames();
   bool silence = true;
   destination->FillAudioBus(true, audio_bus.get(), &silence);
@@ -788,9 +789,9 @@
 
   scoped_refptr<AudioContext> audio_context(
       new AudioContext(environment_settings()));
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfChannels, kRenderBufferSizeFrames, AudioBus::kFloat32,
+                   AudioBus::kPlanar));
   audio_bus->ZeroAllFrames();
   scoped_refptr<AudioBuffer> buffer(
       new AudioBuffer(audio_context->sample_rate(), std::move(audio_bus)));
@@ -828,9 +829,9 @@
 
   scoped_refptr<AudioContext> audio_context(
       new AudioContext(environment_settings()));
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfChannels, kRenderBufferSizeFrames, AudioBus::kFloat32,
+                   AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
   scoped_refptr<AudioBuffer> buffer(
       new AudioBuffer(audio_context->sample_rate(), std::move(audio_bus)));
@@ -868,9 +869,9 @@
 
   scoped_refptr<AudioContext> audio_context(
       new AudioContext(environment_settings()));
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kInt16, ShellAudioBus::kPlanar));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfChannels, kRenderBufferSizeFrames, AudioBus::kInt16,
+                   AudioBus::kPlanar));
   audio_bus->ZeroAllFrames();
 
   scoped_refptr<AudioBuffer> buffer(
@@ -911,9 +912,9 @@
 
   scoped_refptr<AudioContext> audio_context(
       new AudioContext(environment_settings()));
-  std::unique_ptr<ShellAudioBus> audio_bus(
-      new ShellAudioBus(kNumOfChannels, kRenderBufferSizeFrames,
-                        ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> audio_bus(
+      new AudioBus(kNumOfChannels, kRenderBufferSizeFrames, AudioBus::kInt16,
+                   AudioBus::kInterleaved));
   audio_bus->ZeroAllFrames();
 
   scoped_refptr<AudioBuffer> buffer(
@@ -966,9 +967,9 @@
 
   for (size_t buffer_sample_rate : kBufferSampleRateArr) {
     for (SampleType sample_type : kSampleTypeArr) {
-      std::unique_ptr<ShellAudioBus> src_data(
-          new ShellAudioBus(kNumOfSrcChannels, kNumOfSrcFrames, sample_type,
-                            ShellAudioBus::kInterleaved));
+      std::unique_ptr<AudioBus> src_data(
+          new AudioBus(kNumOfSrcChannels, kNumOfSrcFrames, sample_type,
+                       AudioBus::kInterleaved));
       src_data->ZeroAllFrames();
       scoped_refptr<AudioBuffer> buffer(
           new AudioBuffer(buffer_sample_rate, std::move(src_data)));
@@ -988,9 +989,9 @@
       source->Connect(destination, 0, 0, NULL);
       source->Start(0, 0, NULL);
 
-      std::unique_ptr<ShellAudioBus> audio_bus(
-          new ShellAudioBus(kNumOfDestChannels, kNumOfDestFrames, sample_type,
-                            ShellAudioBus::kInterleaved));
+      std::unique_ptr<AudioBus> audio_bus(
+          new AudioBus(kNumOfDestChannels, kNumOfDestFrames, sample_type,
+                       AudioBus::kInterleaved));
       audio_bus->ZeroAllFrames();
       bool silence = true;
       destination->FillAudioBus(true, audio_bus.get(), &silence);
diff --git a/src/cobalt/audio/audio_node_output.cc b/src/cobalt/audio/audio_node_output.cc
index 8ab0f43..ed6c507 100644
--- a/src/cobalt/audio/audio_node_output.cc
+++ b/src/cobalt/audio/audio_node_output.cc
@@ -24,7 +24,7 @@
 namespace cobalt {
 namespace audio {
 
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
 
 AudioNodeOutput::~AudioNodeOutput() {
   owner_node_->audio_lock()->AssertLocked();
@@ -57,7 +57,7 @@
   }
 }
 
-std::unique_ptr<ShellAudioBus> AudioNodeOutput::PassAudioBusFromSource(
+std::unique_ptr<AudioBus> AudioNodeOutput::PassAudioBusFromSource(
     int32 number_of_frames, SampleType sample_type, bool* finished) {
   // This is called by Audio thread.
   owner_node_->audio_lock()->AssertLocked();
diff --git a/src/cobalt/audio/audio_node_output.h b/src/cobalt/audio/audio_node_output.h
index 4122390..1774ce4 100644
--- a/src/cobalt/audio/audio_node_output.h
+++ b/src/cobalt/audio/audio_node_output.h
@@ -22,7 +22,7 @@
 #include "base/memory/ref_counted.h"
 #include "cobalt/audio/audio_buffer.h"
 #include "cobalt/audio/audio_helpers.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 
 namespace cobalt {
 namespace audio {
@@ -33,7 +33,7 @@
 // This represents the output coming out of the AudioNode.
 // It may be connected to one or more AudioNodeInputs.
 class AudioNodeOutput : public base::RefCountedThreadSafe<AudioNodeOutput> {
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
  public:
   explicit AudioNodeOutput(AudioNode* owner_node) : owner_node_(owner_node) {}
@@ -44,9 +44,9 @@
 
   void DisconnectAll();
 
-  std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(int32 number_of_frames,
-                                                        SampleType sample_type,
-                                                        bool* finished);
+  std::unique_ptr<AudioBus> PassAudioBusFromSource(int32 number_of_frames,
+                                                   SampleType sample_type,
+                                                   bool* finished);
 
  private:
   AudioNode* const owner_node_;
diff --git a/src/cobalt/base/path_provider.cc b/src/cobalt/base/path_provider.cc
index ae07949..6dfbbe7 100644
--- a/src/cobalt/base/path_provider.cc
+++ b/src/cobalt/base/path_provider.cc
@@ -31,6 +31,9 @@
     base::FilePath directory(path.get());
     if (base::PathExists(directory) || base::CreateDirectory(directory)) {
       return directory;
+    } else {
+      DLOG(ERROR) << "Attempt to open or create this path failed: " +
+                         directory.value();
     }
   }
   return base::FilePath();
diff --git a/src/cobalt/black_box_tests/testdata/pointer_test.html b/src/cobalt/black_box_tests/testdata/pointer_test.html
index 1f1b275..340e5da 100644
--- a/src/cobalt/black_box_tests/testdata/pointer_test.html
+++ b/src/cobalt/black_box_tests/testdata/pointer_test.html
@@ -98,14 +98,10 @@
         ['pointerover', 'top', 'bubbling'],
         ['pointerover', 'outer', 'bubbling'],
         ['pointerenter', 'top_two', 'at target'],
-        ['pointerenter', 'top', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'top_two', 'at target'],
         ['mouseover', 'top', 'bubbling'],
         ['mouseover', 'outer', 'bubbling'],
         ['mouseenter', 'top_two', 'at target'],
-        ['mouseenter', 'top', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         // actions.move_to_element_with_offset(top_two, 10, 10).pause(_SLEEP_AFTER_MOVE_TIME)
         ['pointermove', 'top_two', 'at target'],
         ['pointermove', 'top', 'bubbling'],
@@ -139,14 +135,10 @@
         ['pointerover', 'top', 'bubbling'],
         ['pointerover', 'outer', 'bubbling'],
         ['pointerenter', 'top_one', 'at target'],
-        ['pointerenter', 'top', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'top_one', 'at target'],
         ['mouseover', 'top', 'bubbling'],
         ['mouseover', 'outer', 'bubbling'],
         ['mouseenter', 'top_one', 'at target'],
-        ['mouseenter', 'top', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         // actions.click(top_three)
         ['pointerout', 'top_one', 'at target'],
         ['pointerout', 'top', 'bubbling'],
@@ -166,14 +158,10 @@
         ['pointerover', 'top', 'bubbling'],
         ['pointerover', 'outer', 'bubbling'],
         ['pointerenter', 'top_three', 'at target'],
-        ['pointerenter', 'top', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'top_three', 'at target'],
         ['mouseover', 'top', 'bubbling'],
         ['mouseover', 'outer', 'bubbling'],
         ['mouseenter', 'top_three', 'at target'],
-        ['mouseenter', 'top', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         ['pointerdown', 'top_three', 'at target'],
         ['pointerdown', 'top', 'bubbling'],
         ['pointerdown', 'outer', 'bubbling'],
@@ -208,14 +196,10 @@
         ['pointerover', 'top', 'bubbling'],
         ['pointerover', 'outer', 'bubbling'],
         ['pointerenter', 'top_four', 'at target'],
-        ['pointerenter', 'top', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'top_four', 'at target'],
         ['mouseover', 'top', 'bubbling'],
         ['mouseover', 'outer', 'bubbling'],
         ['mouseenter', 'top_four', 'at target'],
-        ['mouseenter', 'top', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         ['pointerdown', 'top_four', 'at target'],
         ['pointerdown', 'top', 'bubbling'],
         ['pointerdown', 'outer', 'bubbling'],
@@ -257,14 +241,10 @@
         ['pointerover', 'top', 'bubbling'],
         ['pointerover', 'outer', 'bubbling'],
         ['pointerenter', 'top_six', 'at target'],
-        ['pointerenter', 'top', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'top_six', 'at target'],
         ['mouseover', 'top', 'bubbling'],
         ['mouseover', 'outer', 'bubbling'],
         ['mouseenter', 'top_six', 'at target'],
-        ['mouseenter', 'top', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         // actions.move_to_element(bottom_six).pause(_SLEEP_AFTER_MOVE_TIME)
         ['pointerout', 'top_six', 'at target'],
         ['pointerout', 'top', 'bubbling'],
@@ -287,13 +267,11 @@
         ['pointerover', 'outer', 'bubbling'],
         ['pointerenter', 'bottom_six', 'at target'],
         ['pointerenter', 'bottom', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'bottom_six', 'at target'],
         ['mouseover', 'bottom', 'bubbling'],
         ['mouseover', 'outer', 'bubbling'],
         ['mouseenter', 'bottom_six', 'at target'],
         ['mouseenter', 'bottom', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         // actions.click(bottom_five)
         ['pointerout', 'bottom_six', 'at target'],
         ['pointerout', 'bottom', 'bubbling'],
@@ -313,14 +291,10 @@
         ['pointerover', 'bottom', 'bubbling'],
         ['pointerover', 'outer', 'bubbling'],
         ['pointerenter', 'bottom_five', 'at target'],
-        ['pointerenter', 'bottom', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'bottom_five', 'at target'],
         ['mouseover', 'bottom', 'bubbling'],
         ['mouseover', 'outer', 'bubbling'],
         ['mouseenter', 'bottom_five', 'at target'],
-        ['mouseenter', 'bottom', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         ['pointerdown', 'bottom_five', 'at target'],
         ['pointerdown', 'bottom', 'bubbling'],
         ['pointerdown', 'outer', 'bubbling'],
@@ -349,14 +323,10 @@
         ['pointerover', 'bottom', 'bubbling'],
         ['pointerover', 'outer', 'bubbling'],
         ['pointerenter', 'bottom_four', 'at target'],
-        ['pointerenter', 'bottom', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'bottom_four', 'at target'],
         ['mouseover', 'bottom', 'bubbling'],
         ['mouseover', 'outer', 'bubbling'],
         ['mouseenter', 'bottom_four', 'at target'],
-        ['mouseenter', 'bottom', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         ['pointerdown', 'bottom_four', 'at target'],
         ['pointerdown', 'bottom', 'bubbling'],
         ['pointerdown', 'outer', 'bubbling'],
@@ -383,12 +353,8 @@
         ['mousemove', 'bottom_two', 'at target'],
         ['pointerover', 'bottom_two', 'at target'],
         ['pointerenter', 'bottom_two', 'at target'],
-        ['pointerenter', 'bottom', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'bottom_two', 'at target'],
         ['mouseenter', 'bottom_two', 'at target'],
-        ['mouseenter', 'bottom', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         // actions.move_to_element(bottom_one).pause(_SLEEP_AFTER_MOVE_TIME)
         ['pointerout', 'bottom_two', 'at target'],
         ['pointerleave', 'bottom_two', 'at target'],
@@ -398,12 +364,8 @@
         ['mousemove', 'bottom_one', 'at target'],
         ['pointerover', 'bottom_one', 'at target'],
         ['pointerenter', 'bottom_one', 'at target'],
-        ['pointerenter', 'bottom', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'bottom_one', 'at target'],
         ['mouseenter', 'bottom_one', 'at target'],
-        ['mouseenter', 'bottom', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         // find_element_by_id(runner, 'end').click()
         ['pointerout', 'bottom_one', 'at target'],
         ['pointerleave', 'bottom_one', 'at target'],
@@ -418,11 +380,9 @@
         ['pointerover', 'end', 'at target'],
         ['pointerover', 'outer', 'bubbling'],
         ['pointerenter', 'end', 'at target'],
-        ['pointerenter', 'outer', 'at target'],
         ['mouseover', 'end', 'at target'],
         ['mouseover', 'outer', 'bubbling'],
         ['mouseenter', 'end', 'at target'],
-        ['mouseenter', 'outer', 'at target'],
         ['pointerdown', 'end', 'at target'],
         ['pointerdown', 'outer', 'bubbling'],
         ['mousedown', 'end', 'at target'],
@@ -489,33 +449,72 @@
         e.target.setPointerCapture(e.pointerId);
       }
 
-      function SetHandlers(event, classname, callback) {
-        var elements = document.getElementsByClassName(classname);
+      // If the event type has value 'type', then report an error if the
+      // 'name' property on the event target already has 'value'. Otherwise,
+      // set it to the 'value'. This is used to detect erroneous boundary
+      // events (enter/leave, over/out), and up/down event sequences on an
+      // element.
+      function TrackAndVerifyTargetState(event, type, name, value) {
+        if (event.type == type) {
+          if (event.target[name] == value) {
+            console.log('ERROR: ' + type + 'event received while ' +
+                        name + ' == ' + event.target[name]);
+            assertTrue(event.target[name] != value);
+          }
+          event.target[name] = value;
+        }
+      }
+
+      function CheckState(e) {
+        // Check the target element state when the event is 'at target'.
+        if (e.eventPhase == 2)  {
+          // Verify that there is not a duplicated or missing event for enter,
+          // leave, over, out, up, or down.
+          TrackAndVerifyTargetState(e, 'mouseenter', 'mouseenter', true);
+          TrackAndVerifyTargetState(e, 'mouseleave', 'mouseenter', false);
+          TrackAndVerifyTargetState(e, 'mouseover', 'mouseover', true);
+          TrackAndVerifyTargetState(e, 'mouseout', 'mouseover', false);
+          TrackAndVerifyTargetState(e, 'mousedown', 'mousedown', true);
+          TrackAndVerifyTargetState(e, 'mouseup', 'mousedown', false);
+
+          TrackAndVerifyTargetState(e, 'pointerenter', 'pointerenter', true);
+          TrackAndVerifyTargetState(e, 'pointerleave', 'pointerenter', false);
+          TrackAndVerifyTargetState(e, 'pointerover', 'pointerover', true);
+          TrackAndVerifyTargetState(e, 'pointerout', 'pointerover', false);
+          TrackAndVerifyTargetState(e, 'pointerdown', 'pointerdown', true);
+          TrackAndVerifyTargetState(e, 'pointerup', 'pointerdown', false);
+        }
+      }
+
+      function SetHandlers(event, selector, callback) {
+        var elements = document.querySelectorAll(selector);
         for (var i = 0; i < elements.length; ++i) {
           elements[i].addEventListener(event, callback);
         }
       }
 
-      function SetAllHandlers(prefix, classname, callback) {
-        SetHandlers(prefix + 'enter', classname, callback);
-        SetHandlers(prefix + 'leave', classname, callback);
-        SetHandlers(prefix + 'over', classname, callback);
-        SetHandlers(prefix + 'out', classname, callback);
-        SetHandlers(prefix + 'down', classname, callback);
-        SetHandlers(prefix + 'up', classname, callback);
-        SetHandlers(prefix + 'move', classname, callback);
+      function SetAllHandlers(prefix, selector, callback) {
+        SetHandlers(prefix + 'enter', selector, callback);
+        SetHandlers(prefix + 'leave', selector, callback);
+        SetHandlers(prefix + 'over', selector, callback);
+        SetHandlers(prefix + 'out', selector, callback);
+        SetHandlers(prefix + 'down', selector, callback);
+        SetHandlers(prefix + 'up', selector, callback);
+        SetHandlers(prefix + 'move', selector, callback);
       }
 
       window.onload = function() {
-        SetAllHandlers('mouse', 'track', LogEvent);
-        SetAllHandlers('pointer', 'track', LogEvent);
-        SetHandlers('click', 'track', LogEvent);
-        SetAllHandlers('mouse', 'cancel', Cancel);
-        SetAllHandlers('pointer', 'cancel', Cancel);
-        SetAllHandlers('mouse', 'stop', Stop);
-        SetAllHandlers('pointer', 'stop', Stop);
-        SetHandlers('pointerdown', 'capture', Capture);
-        SetHandlers('click', 'end', EndTest);
+        SetAllHandlers('mouse', '.track', LogEvent);
+        SetAllHandlers('pointer', '.track', LogEvent);
+        SetHandlers('click', '.track', LogEvent);
+        SetAllHandlers('mouse', '.cancel', Cancel);
+        SetAllHandlers('pointer', '.cancel', Cancel);
+        SetAllHandlers('mouse', '.stop', Stop);
+        SetAllHandlers('pointer', '.stop', Stop);
+        SetHandlers('pointerdown', '.capture', Capture);
+        SetHandlers('click', '.end', EndTest);
+        SetAllHandlers('mouse', '*', CheckState);
+        SetAllHandlers('pointer', '*', CheckState);
         console.log("Setup finished");
         setupFinished();
       }
diff --git a/src/cobalt/black_box_tests/tests/pointer_test.py b/src/cobalt/black_box_tests/tests/pointer_test.py
index 706bbc5..02e9a00 100644
--- a/src/cobalt/black_box_tests/tests/pointer_test.py
+++ b/src/cobalt/black_box_tests/tests/pointer_test.py
@@ -44,8 +44,7 @@
 class PointerTest(black_box_tests.BlackBoxTestCase):
   """Tests pointer and mouse event."""
 
-  def test_simple(self):
-
+  def test_pointer_events(self):
     try:
       with ThreadedWebServer(
           binding_address=self.GetBindingAddress()) as server:
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index d5a6545..f5459ed 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -1327,3 +1327,9 @@
 
 }  // namespace browser
 }  // namespace cobalt
+
+const char* GetCobaltUserAgentString() {
+  static std::string ua = cobalt::browser::CreateUserAgentString(
+      cobalt::browser::GetUserAgentPlatformInfoFromSystem());
+  return ua.c_str();
+}
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index 2f8ab19..994e8df 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -239,4 +239,9 @@
 }  // namespace browser
 }  // namespace cobalt
 
+
+extern "C" {
+SB_IMPORT const char* GetCobaltUserAgentString();
+}
+
 #endif  // COBALT_BROWSER_APPLICATION_H_
diff --git a/src/cobalt/browser/user_agent_string.cc b/src/cobalt/browser/user_agent_string.cc
index 2e7f57d..8e973b5 100644
--- a/src/cobalt/browser/user_agent_string.cc
+++ b/src/cobalt/browser/user_agent_string.cc
@@ -224,10 +224,15 @@
   std::string os_name_and_version = platform_info.os_name_and_version;
 
 #if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kUserAgentOsNameVersion)) {
-    os_name_and_version =
-        command_line->GetSwitchValueASCII(switches::kUserAgentOsNameVersion);
+  // Because we add Cobalt's user agent string to Crashpad before we actually
+  // start Cobalt, the command line won't be initialized when we first try to
+  // get the user agent string.
+  if (base::CommandLine::InitializedForCurrentProcess()) {
+    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+    if (command_line->HasSwitch(switches::kUserAgentOsNameVersion)) {
+      os_name_and_version =
+          command_line->GetSwitchValueASCII(switches::kUserAgentOsNameVersion);
+    }
   }
 #endif  // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
 
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index d887bd0..f24700a 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-278101
\ No newline at end of file
+283720
\ No newline at end of file
diff --git a/src/cobalt/configuration/configuration.cc b/src/cobalt/configuration/configuration.cc
index 1502037..9c5087c 100644
--- a/src/cobalt/configuration/configuration.cc
+++ b/src/cobalt/configuration/configuration.cc
@@ -25,7 +25,8 @@
 Configuration* Configuration::configuration_ = nullptr;
 
 Configuration* Configuration::GetInstance() {
-  return base::Singleton<Configuration>::get();
+  return base::Singleton<Configuration,
+                         base::LeakySingletonTraits<Configuration>>::get();
 }
 
 Configuration::Configuration() {
diff --git a/src/cobalt/content/fonts/font_files/NotoColorEmoji.woff2 b/src/cobalt/content/fonts/font_files/NotoColorEmoji.woff2
index 9cac5c0..72a6830 100644
--- a/src/cobalt/content/fonts/font_files/NotoColorEmoji.woff2
+++ b/src/cobalt/content/fonts/font_files/NotoColorEmoji.woff2
Binary files differ
diff --git a/src/cobalt/demos/content/media-element-demo/key-systems.html b/src/cobalt/demos/content/media-element-demo/key-systems.html
new file mode 100644
index 0000000..7358ca5
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/key-systems.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Media Element Demo</title>
+  <style>
+    .title {
+      background-color: #FFF;
+      color: #0047ab;
+      font-size: 20px;
+    }
+    .query {
+    }
+    .result-success {
+      background-color: rgb(0, 128, 0);
+    }
+    .result-failure {
+      background-color: rgb(128, 0, 0);
+    }
+    body {
+      background-color: #FFF;
+      font-size: 20px;
+    }
+  </style>
+</head>
+<body>
+  <span class="title">Queries on Key Systems</span>
+  <script type="text/javascript" src="key-systems.js"></script>
+</body>
+</html>
diff --git a/src/cobalt/demos/content/media-element-demo/key-systems.js b/src/cobalt/demos/content/media-element-demo/key-systems.js
new file mode 100644
index 0000000..ec8f149
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/key-systems.js
@@ -0,0 +1,111 @@
+// Copyright 2020 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.
+
+function getRepresentation(keySystem, audioMime, videoMime, encryptionScheme) {
+  var representation = keySystem;
+  if (typeof audioMime !== 'undefined') {
+    representation += ', ' + audioMime;
+  }
+  if (typeof videoMime !== 'undefined') {
+    representation += ', ' + videoMime;
+  }
+  if (typeof encryptionScheme !== 'undefined') {
+    representation += ', encryptionscheme="' + encryptionScheme + '"';
+  }
+  return representation;
+}
+
+function checkForSupport(keySystem, audioMime, videoMime, encryptionScheme,
+                         expectedResult) {
+  var configs = [{
+    initDataTypes: ['cenc', 'sinf', 'webm'],
+    audioCapabilities: [],
+    videoCapabilities: [],
+  }];
+  if (typeof audioMime !== 'undefined') {
+    configs[0].audioCapabilities.push(
+        {contentType: audioMime, encryptionScheme: encryptionScheme});
+  }
+  if (typeof videoMime !== 'undefined') {
+    configs[0].videoCapabilities.push(
+        {contentType: videoMime, encryptionScheme: encryptionScheme});
+  }
+  var representation = getRepresentation(keySystem, audioMime, videoMime,
+                                         encryptionScheme);
+  navigator.requestMediaKeySystemAccess(keySystem, configs)
+      .then(onKeySystemAccess.bind(this, representation, expectedResult),
+            onFailure.bind(this, representation, expectedResult));
+}
+
+function addQueryResult(query, result, expectedResult) {
+  var row = document.createElement('div');
+
+  var cell = document.createElement('span');
+  cell.className = 'query';
+  cell.textContent = query + ' => ';
+  row.appendChild(cell);
+
+  cell = document.createElement('span');
+  cell.className = result == expectedResult ? 'result-success' :
+                                              'result-failure';
+  cell.textContent = result;
+  row.appendChild(cell);
+
+  document.body.appendChild(row);
+}
+
+function onKeySystemAccess(representation, expectedResult, keySystemAccess) {
+  addQueryResult(representation, 'supported', expectedResult);
+}
+
+function onFailure(representation, expectedResult) {
+  addQueryResult(representation, 'not supported', expectedResult);
+}
+
+checkForSupport('com.widevine.alpha.invalid', 'audio/mp4; codecs="mp4a.40.2"',
+                undefined, undefined, 'not supported');
+checkForSupport('com.widevine.alpha.invalid', undefined,
+                'video/webm; codecs="vp9"', undefined, 'not supported');
+
+// 'invalid-scheme' is not a valid scheme.
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+                undefined, 'invalid-scheme', 'not supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+                'invalid-scheme', 'not supported');
+
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+                undefined, undefined, 'supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+                undefined, 'supported');
+
+// Empty string is not a valid scheme.
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+                undefined, '', 'not supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+                '', 'not supported');
+
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+                undefined, 'cenc', 'supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+                'cenc', 'supported');
+
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+                undefined, 'cbcs', 'supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+                'cbcs', 'supported');
+
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+                 undefined, 'cbcs-1-9', 'supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+                'cbcs-1-9', 'supported');
diff --git a/src/cobalt/doc/resources/devtools-overlay-console-modes.png b/src/cobalt/doc/resources/devtools-overlay-console-modes.png
new file mode 100644
index 0000000..5103358
--- /dev/null
+++ b/src/cobalt/doc/resources/devtools-overlay-console-modes.png
Binary files differ
diff --git a/src/cobalt/doc/web_debugging.md b/src/cobalt/doc/web_debugging.md
index c4711db..5090e4f 100644
--- a/src/cobalt/doc/web_debugging.md
+++ b/src/cobalt/doc/web_debugging.md
@@ -102,28 +102,146 @@
 
 ### Console
 
-Cobalt has two consoles:
-* Overlay console in Cobalt itself (shown with ctrl-O or F1).
-* Remote console shown in a connected DevTools session.
+Cobalt has two types of consoles:
+
+*   Overlay Console: shown at runtime of Cobalt. It has multiple mode that it
+    can cycle between as well:
+    *   HUD
+    *   HUD & Debug Console
+    *   Media Console
+*   Remote Console: shown in a connected devtools session.
 
 Both console UIs show messages logged from JavaScript (with `console.log()`,
 etc.), and have a command line to evaluate arbitrary JavaScript in the context
 of the page being debugged.
 
+#### Overlay Console
+
 The overlay console also shows non-JavaScript logging from Cobalt itself, which
 is mostly interesting to Cobalt developers rather than web app developers.
 
+The various modes of the overlay console are accessed by repeatedly pressing
+"`F1`" or "`Ctrl+O`". They cycle in order between: none, HUD, HUD & Debug, and
+Media. Alternatively, initial console state can be set with the
+`--debug_console=off|hud|debug|media` command-line switch (`--debug_console=on`
+is accepted as a legacy option and maps to "debug" setting).
+
+![Overlay Console mode switching](resources/devtools-overlay-console-flow.png)
+
+##### HUD overlay
+
+This brings up an overlay panel which does not block sending input to the
+underlying Cobalt app. It serves to display real-time statistics (e.g. memory
+usage) and configuration values (e.g. disabled codecs) of the Cobalt app in a
+compact string.
+
+##### Debug Console overlay
+
+This overlay is interactive and it shows messages from Cobalt, along with logs
+from Javacript `console.log()`. While it is active, you cannot interact directly
+with the underlying page.
+
+Additionally, it can act as a JS interpreter that will evaluate arbitrary
+expressions on the page being debugged. The output from these JS commands will
+also be printed to the Debug console.
+
+Finally, it has some special debug commands which can be listed by calling
+`d.help()`. They are provided by a debug helper object and the list of functions
+are invoked by prepending either "`debug`" or "`d`". For example, you can
+disable the vp9 codec manually for all future played videos in this session of
+Cobalt by sending `debug.disable_media_codecs("vp9")` to the console.
+
+Note: you can clear the disabled media codecs by sending
+`debug.disable_media_codecs("")`. The command takes a semicolon separated list
+of codecs as the input list of codecs to disable.
+
+##### Media Console overlay
+
+The media console is a specialized console of the debug overlay system, for
+playback and media related tasks. The current list of implemented features are:
+
+*   Reading the play/pause state of the primary video
+*   Reading the current time and duration of the primary video
+*   Reading the playback rate of the primary video
+*   Reading the currently disabled codecs for the player
+*   Toggling between playing and pausing the primary video
+*   Setting the current playback rate between various presets for the primary
+    video
+*   Toggling the enabled/disabled state of the available codecs
+
+While the media console is shown, it is not possible to interact with the page
+below it directly.
+
+Additionally, the console does not show any meaningful information or
+interactions when no video is currently playing (all the readouts are blank or
+undefined). A status message of “No primary video.” indicates there is no valid
+player element on the current page.
+
+In the case of multiple videos playing (such as picture in picture), only the
+primary (fullscreen) video’s information is shown and the controls are only
+enabled for the primary video.
+
+The list of hotkeys and commands are dynamically generated as they are found to
+be available on app startup.
+
+Basic always-enabled commands are (case-sensitive):
+
+*   "`p`" Toggle the play/pause state
+*   "`]`" Increase the playback rate
+*   "`[`" Decrease the playback rate
+
+The above commands will take effect instantly for the currently playing video.
+They have no effect if there is no video playing.
+
+The following commands are dynamically loaded based on the capability of the
+system:
+
+*   "`CTRL+NUM`" Enable/disable specific video codec
+*   "`ALT+NUM`" Enable/disable specific audio codec
+
+**Important:** Media Console cannot be used to directly select a specific codec for
+playback. See the section below for rough outline of steps to work around this.
+
+The list of available codecs for any video is chosen based on the decoders on
+the platform, and what formats YouTube itself serves. As a result, the only way
+to get a particular codec to play is to disable all the options until the
+desired codec is the one that is picked. Simply do the following procedure:
+
+*   Pick the video you want to play.
+*   Enable “stats for nerds” (See [help page for
+    instructions](https://support.google.com/youtube/answer/7519898)).
+*   Write down the codecs that are chosen when playing the video, without any
+    codecs disabled (one for video, and one for audio).
+*   Disable the default codecs.
+*   Replay the same video from the browse screen.
+*   Repeat until you identify all codecs that are available for the video, until
+    the video is unable to be played.
+*   Use the above knowledge to disable the codecs to force the player into
+    choosing a particular codec, by process of elimination.
+
+**Important:** Disabled codecs only take effect when a video starts playing.
+When you play a video, the current list of disabled codecs is used to select an
+arbitrary enabled format. When you seek in the video, the disabled codecs list
+does not take effect. Only when you exit the player and re-enter by playing a
+video will any toggled codecs be affected.
+
+**Important:** Disabled codecs list is persistent for the app-run. If you
+disable “av01”, then until you re-enable it, “av01” formats will never be
+chosen.
+
+**Important:** If you disable all the available codecs, no video codec can be
+selected and an error dialog will be shown. This means that YouTube does not
+have the video in any other formats, outside of the codecs that are disabled.
+The player reports that it cannot play the video in any of the available formats
+so playback will fail here, which is intended.
+
+#### Remote Console
+
 The console in DevTools is a richer UI that can show evaluated objects with an
 expander so you can dig in to their properties. Logging from JavaScript with
 `console.log()` can show objects and exceptions as well, in contrast to the
 text-only messages shown in the console overlay.
 
-> There may be some things (e.g.  timers) that still need to be hooked up to the
-> V8 backend, so please file a bug if something isn't working as expected.
-
-> When built with MozJs instead of V8, the functionality of the console is
-> limited to showing only text log messages.
-
 Chrome docs:
 
 *   https://developers.google.com/web/tools/chrome-devtools/console/
diff --git a/src/cobalt/dom/eme/media_key_system_media_capability.idl b/src/cobalt/dom/eme/media_key_system_media_capability.idl
index 56e86d5..7f1da60 100644
--- a/src/cobalt/dom/eme/media_key_system_media_capability.idl
+++ b/src/cobalt/dom/eme/media_key_system_media_capability.idl
@@ -16,7 +16,11 @@
 
 dictionary MediaKeySystemMediaCapability {
   DOMString contentType = "";
+
   // TODO: Implement robustness as per
   //       https://www.w3.org/TR/encrypted-media/#dom-mediakeysystemmediacapability-robustness.
   // DOMString robustness = "";
+
+  // https://wicg.github.io/encrypted-media-encryption-scheme/
+  DOMString? encryptionScheme = null;
 };
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index ae93458..7a76ea5 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -1023,10 +1023,7 @@
   // themselves still need to have their computed style updated, in case the
   // value of display is changed.
   if (computed_style()->display() == cssom::KeywordValue::GetNone()) {
-    if (ui_nav_item_) {
-      ui_nav_item_->SetEnabled(false);
-      ui_nav_item_ = nullptr;
-    }
+    ReleaseUiNavigationItem();
     return;
   }
 
@@ -1207,12 +1204,7 @@
 }
 
 HTMLElement::~HTMLElement() {
-  // Disable any associated navigation item to prevent callbacks during
-  // destruction.
-  if (ui_nav_item_) {
-    ui_nav_item_->SetEnabled(false);
-    ui_nav_item_ = nullptr;
-  }
+  ReleaseUiNavigationItem();
 
   if (IsInDocument()) {
     dom_stat_tracker_->OnHtmlElementRemovedFromDocument();
@@ -1251,11 +1243,7 @@
   // Node::OnRemovedFromDocument().
   ClearRuleMatchingStateInternal(false /*invalidate_descendants*/);
 
-  // Release the associated navigation item as the object is no longer visible.
-  if (ui_nav_item_) {
-    ui_nav_item_->SetEnabled(false);
-    ui_nav_item_ = nullptr;
-  }
+  ReleaseUiNavigationItem();
 }
 
 void HTMLElement::OnMutation() { InvalidateMatchingRulesRecursively(); }
@@ -2007,6 +1995,11 @@
     }
   }
 
+  // Update the UI navigation item and invalidate layout boxes if needed.
+  if (!UpdateUiNavigationAndReturnIfLayoutBoxesAreValid()) {
+    invalidation_flags.invalidate_layout_boxes = true;
+  }
+
   if (invalidation_flags.mark_descendants_as_display_none) {
     MarkNotDisplayedOnDescendants();
   }
@@ -2029,9 +2022,6 @@
     }
   }
 
-  // Update the UI navigation item.
-  UpdateUiNavigationType();
-
   computed_style_valid_ = true;
   pseudo_elements_computed_styles_valid_ = true;
 }
@@ -2069,7 +2059,7 @@
          computed_style()->visibility() == cssom::KeywordValue::GetVisible();
 }
 
-void HTMLElement::UpdateUiNavigationType() {
+bool HTMLElement::UpdateUiNavigationAndReturnIfLayoutBoxesAreValid() {
   base::Optional<ui_navigation::NativeItemType> ui_nav_item_type;
   if (computed_style()->overflow() == cssom::KeywordValue::GetAuto() ||
       computed_style()->overflow() == cssom::KeywordValue::GetScroll()) {
@@ -2088,7 +2078,7 @@
       if (ui_nav_item_->GetType() == *ui_nav_item_type) {
         // Keep using the existing navigation item.
         ui_nav_item_->SetDir(ui_nav_item_dir);
-        return;
+        return true;
       }
       // The current navigation item isn't of the correct type. Disable it so
       // that callbacks won't be invoked for it. The object will be destroyed
@@ -2096,6 +2086,7 @@
       ui_nav_item_->SetEnabled(false);
       ui_nav_item_ = nullptr;
     }
+
     ui_nav_item_ = new ui_navigation::NavItem(
         *ui_nav_item_type,
         base::Bind(
@@ -2114,10 +2105,30 @@
             FROM_HERE,
             base::Bind(&HTMLElement::OnUiNavScroll, base::AsWeakPtr(this))));
     ui_nav_item_->SetDir(ui_nav_item_dir);
+    return false;
   } else if (ui_nav_item_) {
     // This navigation item is no longer relevant.
     ui_nav_item_->SetEnabled(false);
     ui_nav_item_ = nullptr;
+    return false;
+  }
+
+  return true;
+}
+
+void HTMLElement::ReleaseUiNavigationItem() {
+  if (ui_nav_item_) {
+    // Make sure layout updates this element.
+    InvalidateLayoutBoxesOfNodeAndAncestors();
+    if (ui_nav_item_->IsContainer()) {
+      // Make sure layout updates any focus items that may be in this container.
+      InvalidateLayoutBoxesOfDescendants();
+    }
+
+    // Disable the UI navigation item so it won't receive anymore callbacks
+    // while being released.
+    ui_nav_item_->SetEnabled(false);
+    ui_nav_item_ = nullptr;
   }
 }
 
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index fae0c2b..b29c27c 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -412,7 +412,8 @@
   void ClearRuleMatchingStateInternal(bool invalidate_descendants);
 
   // Update the UI navigation item type for this element.
-  void UpdateUiNavigationType();
+  bool UpdateUiNavigationAndReturnIfLayoutBoxesAreValid();
+  void ReleaseUiNavigationItem();
 
   // Clear the list of active background images, and notify the animated image
   // tracker to stop the animations.
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index aa21c6f..aff4334 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -250,10 +250,9 @@
   DLOG_IF(ERROR, !key_system.empty())
       << "CanPlayType() only accepts one parameter but (" << key_system
       << ") is passed as a second parameter.";
-  const bool kIsProgressive = true;
   auto support_type =
-      html_element_context()->can_play_type_handler()->CanPlayType(
-          mime_type, key_system, kIsProgressive);
+      html_element_context()->can_play_type_handler()->CanPlayProgressive(
+          mime_type);
   std::string result = "";
   switch (support_type) {
     case kSbMediaSupportTypeNotSupported:
diff --git a/src/cobalt/dom/media_source.cc b/src/cobalt/dom/media_source.cc
index e94f4c4..57817f4 100644
--- a/src/cobalt/dom/media_source.cc
+++ b/src/cobalt/dom/media_source.cc
@@ -269,10 +269,8 @@
   DOMSettings* dom_settings =
       base::polymorphic_downcast<DOMSettings*>(settings);
   DCHECK(dom_settings->can_play_type_handler());
-  const bool kIsProgressive = false;
   SbMediaSupportType support_type =
-      dom_settings->can_play_type_handler()->CanPlayType(type.c_str(), "",
-                                                         kIsProgressive);
+      dom_settings->can_play_type_handler()->CanPlayAdaptive(type.c_str(), "");
   if (support_type == kSbMediaSupportTypeNotSupported) {
     LOG(INFO) << "MediaSource::IsTypeSupported(" << type
               << ") -> not supported/false";
diff --git a/src/cobalt/dom/navigator.cc b/src/cobalt/dom/navigator.cc
index c57b886..a6d3ce3 100644
--- a/src/cobalt/dom/navigator.cc
+++ b/src/cobalt/dom/navigator.cc
@@ -31,12 +31,117 @@
 
 using cobalt::media_session::MediaSession;
 
-namespace {
-const char kLicensesRelativePath[] = "/licenses/licenses_cobalt.txt";
-}  // namespace
-
 namespace cobalt {
 namespace dom {
+namespace {
+
+const char kLicensesRelativePath[] = "/licenses/licenses_cobalt.txt";
+
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+
+std::string ToString(const std::string& str, int indent_level);
+std::string ToString(const eme::MediaKeySystemMediaCapability& capability,
+                     int indent_level);
+std::string ToString(const eme::MediaKeySystemConfiguration& configuration,
+                     int indent_level);
+
+std::string GetIndent(int indent_level) {
+  std::string indent;
+  while (indent_level > 0) {
+    indent += "  ";
+    --indent_level;
+  }
+  return indent;
+}
+
+template <typename T>
+std::string ToString(const script::Sequence<T>& sequence, int indent_level) {
+  std::stringstream ss;
+
+  ss << "{\n";
+
+  for (auto iter = sequence.begin(); iter != sequence.end(); ++iter) {
+    ss << ToString(*iter, indent_level + 1) << ",\n";
+  }
+
+  ss << GetIndent(indent_level) << "}";
+
+  return ss.str();
+}
+
+std::string ToString(const std::string& str, int indent_level) {
+  return GetIndent(indent_level) + str;
+}
+
+std::string ToString(const eme::MediaKeySystemMediaCapability& capability,
+                     int indent_level) {
+  return GetIndent(indent_level) + '\'' + capability.content_type() + "'/'" +
+         capability.encryption_scheme().value_or("(null)") + '\'';
+}
+
+std::string ToString(const eme::MediaKeySystemConfiguration& configuration,
+                     int indent_level) {
+  DCHECK(configuration.has_label());
+
+  std::stringstream ss;
+
+  ss << GetIndent(indent_level) << "label:'" << configuration.label()
+     << "': {\n";
+  if (configuration.has_init_data_types()) {
+    ss << GetIndent(indent_level + 1) << "init_data_types: "
+       << ToString(configuration.init_data_types(), indent_level + 1) << ",\n";
+  }
+  if (configuration.has_audio_capabilities()) {
+    ss << GetIndent(indent_level + 1) << "audio_capabilities: "
+       << ToString(configuration.audio_capabilities(), indent_level + 1)
+       << ",\n";
+  }
+  if (configuration.has_video_capabilities()) {
+    ss << GetIndent(indent_level + 1) << "video_capabilities: "
+       << ToString(configuration.video_capabilities(), indent_level + 1)
+       << ",\n";
+  }
+  ss << GetIndent(indent_level) << "}";
+
+  return ss.str();
+}
+
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
+
+// This function is used when the underlying SbMediaCanPlayMimeAndKeySystem()
+// implementation doesn't support extra attributes on |key_system|, it makes
+// decision based on the key system itself.
+bool IsEncryptionSchemeSupportedByDefault(
+    const std::string& key_system, const std::string& encryption_scheme) {
+  // 1. Playready only supports "cenc".
+  if (key_system.find("playready") != key_system.npos) {
+    return encryption_scheme == "cenc";
+  }
+  // 2. Fairplay only supports "cbcs" and "cbcs-1-9".
+  if (key_system.find("fairplay") != key_system.npos) {
+    return encryption_scheme == "cbcs" || encryption_scheme == "cbcs-1-9";
+  }
+  // 3. Widevine only supports "cenc", "cbcs" and "cbcs-1-9".
+  if (key_system.find("widevine") != key_system.npos) {
+    return encryption_scheme == "cenc" || encryption_scheme == "cbcs" ||
+           encryption_scheme == "cbcs-1-9";
+  }
+
+  // The key system is unknown, assume only "cenc" is supported.
+  return encryption_scheme == "cenc";
+}
+
+bool CanPlay(const media::CanPlayTypeHandler& can_play_type_handler,
+             const std::string& content_type, const std::string& key_system) {
+  auto can_play_result = can_play_type_handler.CanPlayAdaptive(
+      content_type.c_str(), key_system.c_str());
+  LOG_IF(INFO, can_play_result == kSbMediaSupportTypeMaybe)
+      << "CanPlayAdaptive() returns \"maybe\".";
+  return can_play_result == kSbMediaSupportTypeProbably ||
+         can_play_result == kSbMediaSupportTypeMaybe;
+}
+
+}  // namespace
 
 Navigator::Navigator(
     script::EnvironmentSettings* settings, const std::string& user_agent,
@@ -123,17 +228,16 @@
   return media_session_;
 }
 
-namespace {
-
+// TODO: Move the following two functions to the bottom of the file, in sync
+//       with the order of declaration.
 // See
 // https://www.w3.org/TR/encrypted-media/#get-supported-capabilities-for-audio-video-type.
 base::Optional<script::Sequence<MediaKeySystemMediaCapability>>
-TryGetSupportedCapabilities(
+Navigator::TryGetSupportedCapabilities(
+    const media::CanPlayTypeHandler& can_play_type_handler,
     const std::string& key_system,
     const script::Sequence<MediaKeySystemMediaCapability>&
-        requested_media_capabilities,
-    const media::CanPlayTypeHandler* can_play_type_handler) {
-  DCHECK(can_play_type_handler);
+        requested_media_capabilities) {
   // 2. Let supported media capabilities be an empty sequence of
   //    MediaKeySystemMediaCapability dictionaries.
   script::Sequence<MediaKeySystemMediaCapability> supported_media_capabilities;
@@ -152,17 +256,10 @@
     // 3.13. If the user agent and [CDM] implementation definitely support
     //       playback of encrypted media data for the combination of container,
     //       media types [...]:
-    const bool kIsProgressive = false;
-    if (can_play_type_handler->CanPlayType(
-            content_type.c_str(), key_system.c_str(), kIsProgressive) ==
-        kSbMediaSupportTypeProbably) {
-      LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
-                << ", " << key_system << ") -> supported";
+    if (CanPlayWithCapability(can_play_type_handler, key_system,
+                              requested_media_capability)) {
       // 3.13.1. Add requested media capability to supported media capabilities.
       supported_media_capabilities.push_back(requested_media_capability);
-    } else {
-      LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
-                << ", " << key_system << ") -> not supported";
     }
   }
   // 4. If supported media capabilities is empty, return null.
@@ -179,10 +276,11 @@
 // is always given and go straight to "3.1.1.2 Get Supported Configuration and
 // Consent". See
 // https://www.w3.org/TR/encrypted-media/#get-supported-configuration-and-consent.
-base::Optional<eme::MediaKeySystemConfiguration> TryGetSupportedConfiguration(
+base::Optional<eme::MediaKeySystemConfiguration>
+Navigator::TryGetSupportedConfiguration(
+    const media::CanPlayTypeHandler& can_play_type_handler,
     const std::string& key_system,
-    const eme::MediaKeySystemConfiguration& candidate_configuration,
-    const media::CanPlayTypeHandler* can_play_type_handler) {
+    const eme::MediaKeySystemConfiguration& candidate_configuration) {
   // 1. Let accumulated configuration be a new MediaKeySystemConfiguration
   //    dictionary.
   eme::MediaKeySystemConfiguration accumulated_configuration;
@@ -225,8 +323,8 @@
     //       Supported Capabilities for Audio/Video Type" algorithm.
     base::Optional<script::Sequence<MediaKeySystemMediaCapability>>
         maybe_video_capabilities = TryGetSupportedCapabilities(
-            key_system, candidate_configuration.video_capabilities(),
-            can_play_type_handler);
+            can_play_type_handler, key_system,
+            candidate_configuration.video_capabilities());
     // 16.2. If video capabilities is null, return NotSupported.
     if (!maybe_video_capabilities) {
       return base::nullopt;
@@ -249,8 +347,8 @@
     //       Supported Capabilities for Audio/Video Type" algorithm.
     base::Optional<script::Sequence<MediaKeySystemMediaCapability>>
         maybe_audio_capabilities = TryGetSupportedCapabilities(
-            key_system, candidate_configuration.audio_capabilities(),
-            can_play_type_handler);
+            can_play_type_handler, key_system,
+            candidate_configuration.audio_capabilities());
     // 17.2. If audio capabilities is null, return NotSupported.
     if (!maybe_audio_capabilities) {
       return base::nullopt;
@@ -269,8 +367,6 @@
   return accumulated_configuration;
 }
 
-}  // namespace
-
 // See
 // https://www.w3.org/TR/encrypted-media/#dom-navigator-requestmediakeysystemaccess.
 script::Handle<Navigator::InterfacePromise>
@@ -286,6 +382,12 @@
       script_value_factory_
           ->CreateInterfacePromise<scoped_refptr<eme::MediaKeySystemAccess>>();
 
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+  LOG(INFO) << "Navigator.RequestMediaKeySystemAccess() called with '"
+            << key_system << "', and\n"
+            << ToString(supported_configurations, 0);
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
+
   // 1. If |keySystem| is the empty string, return a promise rejected
   //    with a newly created TypeError.
   // 2. If |supportedConfigurations| is empty, return a promise rejected
@@ -302,14 +404,19 @@
     // 6.3.3. If supported configuration is not NotSupported:
     base::Optional<eme::MediaKeySystemConfiguration>
         maybe_supported_configuration = TryGetSupportedConfiguration(
-            key_system, supported_configurations.at(configuration_index),
-            dom_settings->can_play_type_handler());
+            *dom_settings->can_play_type_handler(), key_system,
+            supported_configurations.at(configuration_index));
     if (maybe_supported_configuration) {
       // 6.3.3.1. Let access be a new MediaKeySystemAccess object.
       scoped_refptr<eme::MediaKeySystemAccess> media_key_system_access(
           new eme::MediaKeySystemAccess(key_system,
                                         *maybe_supported_configuration,
                                         script_value_factory_));
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+      LOG(INFO) << "Navigator.RequestMediaKeySystemAccess() resolved with '"
+                << media_key_system_access->key_system() << "', and\n"
+                << ToString(media_key_system_access->GetConfiguration(), 0);
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
       // 6.3.3.2. Resolve promise.
       promise->Resolve(media_key_system_access);
       return promise;
@@ -334,5 +441,115 @@
   tracer->Trace(system_caption_settings_);
 }
 
+bool Navigator::CanPlayWithCapability(
+    const media::CanPlayTypeHandler& can_play_type_handler,
+    const std::string& key_system,
+    const MediaKeySystemMediaCapability& media_capability) {
+  const std::string& content_type = media_capability.content_type();
+
+  // There is no encryption scheme specified, check directly.
+  if (!media_capability.encryption_scheme().has_value()) {
+    if (CanPlay(can_play_type_handler, content_type, key_system)) {
+      LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+                << ", " << key_system << ") -> supported";
+      return true;
+    }
+    LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+              << ", " << key_system << ") -> not supported";
+    return false;
+  }
+
+  if (!key_system_with_attributes_supported_.has_value()) {
+    if (!CanPlay(can_play_type_handler, content_type, key_system)) {
+      // If the check on the basic key system fails, we don't even bother to
+      // check if it supports key system with attributes.
+      LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+                << ", " << key_system << ") -> not supported";
+      return false;
+    }
+    auto key_system_with_invalid_attribute =
+        std::string(key_system) + "; invalid_attributes=\"value\"";
+    // If an implementation supports attributes, it should ignore unknown
+    // attributes and return true, as the key system has been verified to be
+    // supported above.
+    key_system_with_attributes_supported_ = CanPlay(
+        can_play_type_handler, content_type, key_system_with_invalid_attribute);
+    if (key_system_with_attributes_supported_.value()) {
+      LOG(INFO) << "Navigator::RequestMediaKeySystemAccess() will use key"
+                << " system with attributes.";
+    } else {
+      LOG(INFO) << "Navigator::RequestMediaKeySystemAccess() won't use key"
+                << " system with attributes.";
+    }
+  }
+
+  DCHECK(key_system_with_attributes_supported_.has_value());
+
+  // As a key system with attributes support is optional, and the logic to
+  // determine whether the encryptionScheme is supported can be quite different
+  // depending on whether this is supported, we encapsulate the logic into two
+  // different functions.
+  if (key_system_with_attributes_supported_.value()) {
+    return CanPlayWithAttributes(can_play_type_handler, content_type,
+                                 key_system,
+                                 media_capability.encryption_scheme().value());
+  }
+
+  return CanPlayWithoutAttributes(can_play_type_handler, content_type,
+                                  key_system,
+                                  media_capability.encryption_scheme().value());
+}
+
+bool Navigator::CanPlayWithoutAttributes(
+    const media::CanPlayTypeHandler& can_play_type_handler,
+    const std::string& content_type, const std::string& key_system,
+    const std::string& encryption_scheme) {
+  DCHECK(key_system_with_attributes_supported_.has_value());
+  DCHECK(!key_system_with_attributes_supported_.value());
+
+  if (!IsEncryptionSchemeSupportedByDefault(key_system, encryption_scheme)) {
+    LOG(INFO) << "Navigator::RequestMediaKeySystemAccess() rejects "
+              << key_system << " because encryptionScheme \""
+              << encryption_scheme << "\" is not supported.";
+    return false;
+  }
+
+  if (CanPlay(can_play_type_handler, content_type, key_system)) {
+    LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+              << ", " << key_system << ") with encryptionScheme \""
+              << encryption_scheme << "\" -> supported";
+    return true;
+  }
+
+  LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type << ", "
+            << key_system << ") with encryptionScheme \"" << encryption_scheme
+            << "\" -> not supported";
+  return false;
+}
+
+bool Navigator::CanPlayWithAttributes(
+    const media::CanPlayTypeHandler& can_play_type_handler,
+    const std::string& content_type, const std::string& key_system,
+    const std::string& encryption_scheme) {
+  DCHECK(key_system_with_attributes_supported_.has_value());
+  DCHECK(key_system_with_attributes_supported_.value());
+
+  auto key_system_with_attributes =
+      key_system + "; encryptionscheme=\"" + encryption_scheme + '"';
+
+  if (CanPlay(can_play_type_handler, content_type,
+              key_system_with_attributes)) {
+    LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+              << ", " << key_system << ") with encryptionScheme \""
+              << encryption_scheme << "\" -> supported";
+    return true;
+  }
+
+  LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type << ", "
+            << key_system << ") with encryptionScheme \"" << encryption_scheme
+            << "\" -> not supported";
+  return false;
+}
+
 }  // namespace dom
 }  // namespace cobalt
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h
index 10a417d..1489f78 100644
--- a/src/cobalt/dom/navigator.h
+++ b/src/cobalt/dom/navigator.h
@@ -18,6 +18,7 @@
 #include <string>
 
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
 #include "cobalt/dom/captions/system_caption_settings.h"
 #include "cobalt/dom/eme/media_key_system_configuration.h"
 #include "cobalt/dom/mime_type_array.h"
@@ -26,6 +27,7 @@
 #include "cobalt/media_session/media_session.h"
 #include "cobalt/script/promise.h"
 #include "cobalt/script/script_value_factory.h"
+#include "cobalt/script/sequence.h"
 #include "cobalt/script/wrappable.h"
 
 namespace cobalt {
@@ -84,6 +86,33 @@
  private:
   ~Navigator() override {}
 
+  base::Optional<script::Sequence<MediaKeySystemMediaCapability>>
+  TryGetSupportedCapabilities(
+      const media::CanPlayTypeHandler& can_play_type_handler,
+      const std::string& key_system,
+      const script::Sequence<MediaKeySystemMediaCapability>&
+          requested_media_capabilities);
+
+  base::Optional<eme::MediaKeySystemConfiguration> TryGetSupportedConfiguration(
+      const media::CanPlayTypeHandler& can_play_type_handler,
+      const std::string& key_system,
+      const eme::MediaKeySystemConfiguration& candidate_configuration);
+
+  bool CanPlayWithCapability(
+      const media::CanPlayTypeHandler& can_play_type_handler,
+      const std::string& key_system,
+      const MediaKeySystemMediaCapability& media_capability);
+
+  bool CanPlayWithoutAttributes(
+      const media::CanPlayTypeHandler& can_play_type_handler,
+      const std::string& content_type, const std::string& key_system,
+      const std::string& encryption_scheme);
+
+  bool CanPlayWithAttributes(
+      const media::CanPlayTypeHandler& can_play_type_handler,
+      const std::string& content_type, const std::string& key_system,
+      const std::string& encryption_scheme);
+
   std::string user_agent_;
   std::string language_;
   scoped_refptr<MimeTypeArray> mime_types_;
@@ -93,6 +122,7 @@
   scoped_refptr<cobalt::dom::captions::SystemCaptionSettings>
       system_caption_settings_;
   script::ScriptValueFactory* script_value_factory_;
+  base::Optional<bool> key_system_with_attributes_supported_;
 
   DISALLOW_COPY_AND_ASSIGN(Navigator);
 };
diff --git a/src/cobalt/fetch/embedded_scripts/fetch.js b/src/cobalt/fetch/embedded_scripts/fetch.js
index ac5a7a3..ec89191 100644
--- a/src/cobalt/fetch/embedded_scripts/fetch.js
+++ b/src/cobalt/fetch/embedded_scripts/fetch.js
@@ -1,22 +1,22 @@
-'use strict';(function(c){function E(a){"string"!==typeof a&&(a=String(a));if(/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(a))throw new g("Invalid character in header field name");return a.toLowerCase()}function Q(a){"string"!==typeof a&&(a=String(a));var b;var d=0;for(b=a.length;d<b;d++){var e=a.charCodeAt(d);if(9!==e&&10!==e&&13!==e&&32!==e)break}for(b=a.length-1;b>d&&(e=a.charCodeAt(b),9===e||10===e||13===e||32===e);b--);a=a.substring(d,b+1);d=0;for(b=a.length;d<b;d++)if(e=a.charCodeAt(d),256<=e||0===e||
-10===e||13===e)throw new g("Invalid character in header field value");return a}function W(a,b){throw new g("Immutable header cannot be modified");}function X(a,b){return!1}function Y(a,b){a=a.toLowerCase();return-1<Z.indexOf(a)||a.startsWith("proxy-")||a.startsWith("sec-")?!0:!1}function aa(a,b){a=a.toLowerCase();return-1<ba.indexOf(a)||"content-type"===a&&(b=b.split(";")[0].toLowerCase(),-1<ca.indexOf(b))?!1:!0}function L(a,b){return-1<da.indexOf(a.toLowerCase())?!0:!1}function h(a){this[l]=new I;
-void 0===this[v]&&(this[v]=X);if(void 0!==a){if(null===a||"object"!==typeof a)throw new g("Constructing Headers with invalid parameters");a instanceof h?a.forEach(function(a,d){this.append(d,a)},this):F.isArray(a)?a.forEach(function(a){if(2!==a.length)throw new g("Constructing Headers with invalid parameters");this.append(a[0],a[1])},this):Object.getOwnPropertyNames(a).forEach(function(b){this.append(b,a[b])},this)}}function G(a,b){var d=ea(h.prototype);d[v]=b;h.call(d,a);return d}function M(a){if(a.bodyUsed)return t.reject(new g("Body was already read"));
-if(null===a.body)return t.resolve(new u(0));if(fa(a.body))return t.reject(new g("ReadableStream was already locked"));var b=a.body.getReader(),d=[],e=0;return b.read().then(function ha(a){if(a.done){if(0===d.length)a=new u(0);else if(1===d.length)a=new u(d[0].buffer);else{a=new u(e);for(var k=0,c=d.length,f=0;k<c;k++)a.set(d[k],f),f+=d[k].length}return a}return a.value instanceof u?(e+=a.value.length,d.push(a.value),b.read().then(ha)):t.reject(new g("Invalid stream data type"))})}function R(){this._initBody=
-function(a){this[N]=!1;this[m]=null===a||void 0===a?null:a instanceof O?a:new O({start:function(b){if(a)if("string"===typeof a)b.enqueue(FetchInternal.encodeToUTF8(a));else if(S.prototype.isPrototypeOf(a))b.enqueue(new u(a.slice(0)));else if(ia(a))b.enqueue(new u(a.buffer.slice(0)));else if(a instanceof Blob)b.enqueue(new u(FetchInternal.blobToArrayBuffer(a)));else throw new g("Unsupported BodyInit type");b.close()}});this[n].get("content-type")||("string"===typeof a?this[n].set("content-type","text/plain;charset=UTF-8"):
-a instanceof Blob&&""!==a.type&&this[n].set("content-type",a.type))};P(this,{body:{get:function(){return this[m]}},bodyUsed:{get:function(){return this[N]?!0:this[m]?!!ja(this[m]):!1}}});this.arrayBuffer=function(){return this[z]?t.reject(new DOMException("Aborted","AbortError")):M(this).then(function(a){return a.buffer})};this.text=function(){return this[z]?t.reject(new DOMException("Aborted","AbortError")):M(this).then(function(a){return FetchInternal.decodeFromUTF8(a)})};this.json=function(){return this[z]?
-t.reject(new DOMException("Aborted","AbortError")):this.text().then(JSON.parse)};return this}function w(a,b){var d=void 0!==b&&null!==b&&void 0===b.cloneBody;b=b||{};var e=b.body||b.cloneBody,c=b.headers,f=new AbortController;this[A]=f.signal;f=null;if(a instanceof w)this[x]=a.url,this[B]=a.cache,this[C]=a.credentials,void 0===c&&(c=a.headers),this[D]=a.integrity,this[y]=a.method,this[p]=a.mode,d&&"navigate"===this[p]&&(this[p]="same-origin"),this[H]=a.redirect,e||null===a.body||(e=a.body,a[N]=!0),
-f=a[A];else{this[x]=String(a);if(!FetchInternal.isUrlValid(this[x],!1))throw new g("Invalid request URL");this[p]="cors";this[C]="same-origin"}if(void 0!==b.window&&null!==b.window)throw new g("Invalid request window");this[B]=b.cache||this[B]||"default";if(-1===ka.indexOf(this[B]))throw new g("Invalid request cache mode");this[C]=b.credentials||this[C]||"same-origin";if(-1===la.indexOf(this[C]))throw new g("Invalid request credentials");void 0!==b.integrity?this[D]=b.integrity:void 0===this[D]&&
-(this[D]="");a=(b.method||this[y]||"GET").toUpperCase();if(-1===ma.indexOf(a))throw new g("Invalid request method");this[y]=a;if(b.mode&&-1===na.indexOf(b.mode))throw new g("Invalid request mode");this[p]=b.mode||this[p]||"no-cors";if("no-cors"===this[p]){if(-1===oa.indexOf(this[y]))throw new g("Invalid request method for no-cors");if(""!==this[D])throw new g("Request integrity data is not allowed with no-cors");}if("same-origin"!==this[p]&&"only-if-cached"===this[B])throw new g("Request mode must be same-origin for only-if-cached");
-this[H]=b.redirect||this[H]||"follow";if(-1===pa.indexOf(this[H]))throw new g("Invalid request redirect mode");this[n]="no-cors"===this[p]?G(c,aa):G(c,Y);if(("GET"===this[y]||"HEAD"===this[y])&&e)throw new g("Request body is not allowed for GET or HEAD");"signal"in b&&(f=b.signal);f&&this[A].follow(f);this._initBody(e)}function qa(a,b){var d=G(void 0,b);a.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach(function(a){var b=a.split(":");if(a=b.shift().trim())b=b.join(":").trim(),d.append(a,b)});return d}
-function r(a,b){b||(b={});this[J]="default";this[q]="status"in b?b.status:200;if(200>this[q]||599<this[q])throw new T("Invalid response status");this[U]=200<=this[q]&&300>this[q];if("statusText"in b){var d=b.statusText;for(var e=0,f=d.length,c;e<f;e++)if(c=d.charCodeAt(e),9!==c&&(32>c||255<c||127===c))throw g("Invalid response status text");}else d="OK";this[K]=d;this[n]=G(b.headers,L);this[x]=b.url||"";if(a&&-1<ra.indexOf(this[q]))throw new g("Response body is not allowed with a null body status");
-this[z]=b.is_aborted||!1;this._initBody(a)}if(!c.fetch){var F=c.Array,S=c.ArrayBuffer,ea=c.Object.create,P=c.Object.defineProperties,f=c.Symbol,sa=f.iterator,I=c.Map,T=c.RangeError,g=c.TypeError,u=c.Uint8Array,t=c.Promise,O=c.ReadableStream,V=c.ReadableStreamTee,ja=c.IsReadableStreamDisturbed,fa=c.IsReadableStreamLocked,m=f("body"),N=f("bodyUsed"),B=f("cache"),C=f("credentials"),v=f("guardCallback"),n=f("headers"),D=f("integrity"),l=f("map"),y=f("method"),p=f("mode"),U=f("ok"),H=f("redirect"),q=f("status"),
-K=f("statusText"),J=f("type"),x=f("url"),z=f("is_aborted"),A=f("signal"),Z="accept-charset accept-encoding access-control-request-headers access-control-request-method connection content-length cookie cookie2 date dnt expect host keep-alive origin referer te trailer transfer-encoding upgrade via".split(" "),da=["set-cookie","set-cookie2"],ba=["accept","accept-language","content-language"],ca=["application/x-www-form-urlencoded","multipart/form-data","text/plain"],ka="default no-store reload no-cache force-cache only-if-cached".split(" "),
-la=["omit","same-origin","include"],ma="DELETE GET HEAD OPTIONS POST PUT".split(" "),oa=["GET","HEAD","POST"],na=["same-origin","no-cors","cors"],pa=["follow","error","manual"],ra=[101,204,205,304],ta=[301,302,303,307,308],ua="[object Int8Array];[object Uint8Array];[object Uint8ClampedArray];[object Int16Array];[object Uint16Array];[object Int32Array];[object Uint32Array];[object Float32Array];[object Float64Array]".split(";"),ia=S.isView||function(a){return a&&-1<ua.indexOf(Object.prototype.toString.call(a))};
-h.prototype.append=function(a,b){if(2!==arguments.length)throw g("Invalid parameters to append");a=E(a);b=Q(b);this[v](a,b)||(this[l].has(a)?this[l].set(a,this[l].get(a)+", "+b):this[l].set(a,b))};h.prototype["delete"]=function(a){if(1!==arguments.length)throw g("Invalid parameters to delete");this[v](a,"invalid")||this[l].delete(E(a))};h.prototype.get=function(a){if(1!==arguments.length)throw g("Invalid parameters to get");a=E(a);var b=this[l].get(a);return void 0!==b?b:null};h.prototype.has=function(a){if(1!==
-arguments.length)throw g("Invalid parameters to has");return this[l].has(E(a))};h.prototype.set=function(a,b){if(2!==arguments.length)throw g("Invalid parameters to set");a=E(a);b=Q(b);this[v](a,b)||this[l].set(a,b)};h.prototype.forEach=function(a,b){var d=this;F.from(this[l].entries()).sort().forEach(function(e){a.call(b,e[1],e[0],d)})};h.prototype.keys=function(){return(new I(F.from(this[l].entries()).sort())).keys()};h.prototype.values=function(){return(new I(F.from(this[l].entries()).sort())).values()};
-h.prototype.entries=function(){return(new I(F.from(this[l].entries()).sort())).entries()};h.prototype[sa]=h.prototype.entries;w.prototype.clone=function(){var a=null;null!==this[m]&&(a=V(this[m],!0),this[m]=a[0],a=a[1]);return new w(this,{cloneBody:a,signal:this[A]})};P(w.prototype,{cache:{get:function(){return this[B]}},credentials:{get:function(){return this[C]}},headers:{get:function(){return this[n]}},integrity:{get:function(){return this[D]}},method:{get:function(){return this[y]}},mode:{get:function(){return this[p]}},
-redirect:{get:function(){return this[H]}},url:{get:function(){return this[x]}},signal:{get:function(){return this[A]}}});R.call(w.prototype);R.call(r.prototype);r.prototype.clone=function(){var a=null;null!==this[m]&&(a=V(this[m],!0),this[m]=a[0],a=a[1]);return new r(a,{status:this[q],statusText:this[K],headers:G(this[n],L),url:this[x],is_aborted:this[z]})};P(r.prototype,{headers:{get:function(){return this[n]}},ok:{get:function(){return this[U]}},status:{get:function(){return this[q]}},statusText:{get:function(){return this[K]}},
-type:{get:function(){return this[J]}},url:{get:function(){return this[x]}}});r.error=function(){var a=new r(null);a[n][v]=W;a[J]="error";a[q]=0;a[K]="";return a};r.redirect=function(a,b){if(!FetchInternal.isUrlValid(a,!0))throw new g("Invalid URL for response redirect");void 0===b&&(b=302);if(-1===ta.indexOf(b))throw new T("Invalid status code for response redirect");return new r(null,{status:b,headers:{location:a}})};c.Headers=h;c.Request=w;c.Response=r;c.fetch=function(a,b){return new t(function(d,
-e){var c=!1,f=!1,h=new w(a,b),k=new XMLHttpRequest,l=null;if(h.signal.aborted)return e(new DOMException("Aborted","AbortError"));var m=new O({start:function(a){l=a},cancel:function(a){c=!0;k.abort()}}),p=function(){if(!c){c=!0;m.cancel();if(l)try{ReadableStreamDefaultControllerError(l,new DOMException("Aborted","AbortError"))}catch(va){}setTimeout(function(){try{k.abort()}catch(va){}},0)}};k.onload=function(){l.close()};k.onreadystatechange=function(){if(k.readyState===k.HEADERS_RECEIVED){var a={status:k.status,
-statusText:k.statusText,headers:qa(k.getAllResponseHeaders()||"",L)};a.url="responseURL"in k?k.responseURL:a.headers.get("X-Request-URL");try{var b=new r(m,a);h[A].addEventListener("abort",function(){b[z]=!0;p();e(new DOMException("Aborted","AbortError"))});b[J]=f?"cors":"basic";d(b)}catch(wa){e(wa)}}};k.onerror=function(){l.error(new g("Network request failed"));e(new g("Network request failed"))};k.ontimeout=function(){l.error(new g("Network request failed"));e(new g("Network request failed"))};
-k.open(h.method,h.url,!0);"include"===h.credentials&&(k.withCredentials=!0);h.headers.forEach(function(a,b){k.setRequestHeader(b,a)});var n=function(a){c||l.enqueue(a)},q=function(a){f=a};null===h.body?k.fetch(n,q,null):M(h).then(function(a){k.fetch(n,q,a)})})};c.fetch.polyfill=!0}})(this);
\ No newline at end of file
+'use strict';(function(h){function J(a){"string"!==typeof a&&(a=String(a));if(/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(a))throw new e("Invalid character in header field name");return a.toLowerCase()}function X(a){"string"!==typeof a&&(a=String(a));var b;var c=0;for(b=a.length;c<b;c++){var d=a.charCodeAt(c);if(9!==d&&10!==d&&13!==d&&32!==d)break}for(b=a.length-1;b>c&&(d=a.charCodeAt(b),9===d||10===d||13===d||32===d);b--);a=a.substring(c,b+1);c=0;for(b=a.length;c<b;c++)if(d=a.charCodeAt(c),256<=d||0===d||
+10===d||13===d)throw new e("Invalid character in header field value");return a}function fa(a,b){throw new e("Immutable header cannot be modified");}function ha(a,b){return!1}function ia(a,b){a=a.toLowerCase();return-1<ja.indexOf(a)||a.startsWith("proxy-")||a.startsWith("sec-")?!0:!1}function ka(a,b){a=a.toLowerCase();return-1<la.indexOf(a)||"content-type"===a&&(b=b.split(";")[0].toLowerCase(),-1<ma.indexOf(b))?!1:!0}function S(a,b){return-1<na.indexOf(a.toLowerCase())?!0:!1}function n(a){this[p]=
+new P;void 0===this[A]&&(this[A]=ha);if(void 0!==a){if(null===a||"object"!==typeof a)throw new e("Constructing Headers with invalid parameters");a instanceof n?a.forEach(function(b,c){this.append(c,b)},this):K.isArray(a)?a.forEach(function(b){if(2!==b.length)throw new e("Constructing Headers with invalid parameters");this.append(b[0],b[1])},this):Object.getOwnPropertyNames(a).forEach(function(b){this.append(b,a[b])},this)}}function L(a,b){var c=oa(n.prototype);c[A]=b;n.call(c,a);return c}function T(a){if(a.bodyUsed)return z.reject(new e("Body was already read"));
+if(null===a.body)return z.resolve(new v(0));if(pa(a.body))return z.reject(new e("ReadableStream was already locked"));var b=a.body.getReader(),c=[],d=0;return b.read().then(function q(f){if(f.done){if(0===c.length)f=new v(0);else if(1===c.length)f=new v(c[0].buffer);else{f=new v(d);for(var g=0,w=c.length,M=0;g<w;g++)f.set(c[g],M),M+=c[g].length}return f}return f.value instanceof v?(d+=f.value.length,c.push(f.value),b.read().then(q)):z.reject(new e("Invalid stream data type"))})}function Y(){this._initBody=
+function(a){this[U]=!1;this[r]=null===a||void 0===a?null:a instanceof V?a:new V({start:function(b){if(a)if("string"===typeof a)b.enqueue(FetchInternal.encodeToUTF8(a));else if(Z.prototype.isPrototypeOf(a))b.enqueue(new v(a.slice(0)));else if(qa(a)){var c=new v(a.buffer);c=v.from(c.slice(a.byteOffset,a.byteLength+1));b.enqueue(c)}else if(a instanceof Blob)b.enqueue(new v(FetchInternal.blobToArrayBuffer(a)));else throw new e("Unsupported BodyInit type");b.close()}});this[x].get("content-type")||("string"===
+typeof a?this[x].set("content-type","text/plain;charset=UTF-8"):a instanceof Blob&&""!==a.type&&this[x].set("content-type",a.type))};W(this,{body:{get:function(){return this[r]}},bodyUsed:{get:function(){return this[U]?!0:this[r]?!!ra(this[r]):!1}}});this.arrayBuffer=function(){return this[E]?z.reject(new DOMException("Aborted","AbortError")):T(this).then(function(a){return a.buffer})};this.text=function(){return this[E]?z.reject(new DOMException("Aborted","AbortError")):T(this).then(function(a){return FetchInternal.decodeFromUTF8(a)})};
+this.json=function(){return this[E]?z.reject(new DOMException("Aborted","AbortError")):this.text().then(JSON.parse)};return this}function B(a,b){var c=void 0!==b&&null!==b&&void 0===b.cloneBody;b=b||{};var d=b.body||b.cloneBody;""===b.body&&(d="");var l=b.headers,f=new AbortController;this[F]=f.signal;f=null;if(a instanceof B)this[C]=a.url,this[G]=a.cache,this[H]=a.credentials,void 0===l&&(l=a.headers),this[I]=a.integrity,this[D]=a.method,this[t]=a.mode,c&&"navigate"===this[t]&&(this[t]="same-origin"),
+this[N]=a.redirect,d||null===a.body||(d=a.body,a[U]=!0),f=a[F];else{this[C]=String(a);if(!FetchInternal.isUrlValid(this[C],!1))throw new e("Invalid request URL");this[t]="cors";this[H]="same-origin"}if(void 0!==b.window&&null!==b.window)throw new e("Invalid request window");this[G]=b.cache||this[G]||"default";if(-1===sa.indexOf(this[G]))throw new e("Invalid request cache mode");this[H]=b.credentials||this[H]||"same-origin";if(-1===ta.indexOf(this[H]))throw new e("Invalid request credentials");void 0!==
+b.integrity?this[I]=b.integrity:void 0===this[I]&&(this[I]="");a=(b.method||this[D]||"GET").toUpperCase();if(-1===ua.indexOf(a))throw new e("Invalid request method");this[D]=a;if(b.mode&&-1===va.indexOf(b.mode))throw new e("Invalid request mode");this[t]=b.mode||this[t]||"no-cors";if("no-cors"===this[t]){if(-1===wa.indexOf(this[D]))throw new e("Invalid request method for no-cors");if(""!==this[I])throw new e("Request integrity data is not allowed with no-cors");}if("same-origin"!==this[t]&&"only-if-cached"===
+this[G])throw new e("Request mode must be same-origin for only-if-cached");this[N]=b.redirect||this[N]||"follow";if(-1===xa.indexOf(this[N]))throw new e("Invalid request redirect mode");this[x]="no-cors"===this[t]?L(l,ka):L(l,ia);if(("GET"===this[D]||"HEAD"===this[D])&&d)throw new e("Request body is not allowed for GET or HEAD");"signal"in b&&(f=b.signal);f&&this[F].follow(f);this._initBody(d)}function ya(a,b){var c=L(void 0,b);a.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach(function(d){var l=
+d.split(":");if(d=l.shift().trim())l=l.join(":").trim(),c.append(d,l)});return c}function u(a,b){b||(b={});this[Q]="default";this[y]="status"in b?b.status:200;if(200>this[y]||599<this[y])throw new aa("Invalid response status");this[ba]=200<=this[y]&&300>this[y];if("statusText"in b){var c=b.statusText;for(var d=0,l=c.length,f;d<l;d++)if(f=c.charCodeAt(d),9!==f&&(32>f||255<f||127===f))throw e("Invalid response status text");}else c="OK";this[R]=c;this[x]=L(b.headers,S);this[C]=b.url||"";if(a&&-1<za.indexOf(this[y]))throw new e("Response body is not allowed with a null body status");
+this[E]=b.is_aborted||!1;this._initBody(a)}if(!h.fetch){var K=h.Array,Z=h.ArrayBuffer,oa=h.Object.create,W=h.Object.defineProperties,k=h.Symbol,Aa=k.iterator,P=h.Map,aa=h.RangeError,e=h.TypeError,v=h.Uint8Array,z=h.Promise,V=h.ReadableStream,ca=h.ReadableStreamTee,ra=h.IsReadableStreamDisturbed,pa=h.IsReadableStreamLocked,r=k("body"),U=k("bodyUsed"),G=k("cache"),H=k("credentials"),A=k("guardCallback"),x=k("headers"),I=k("integrity"),p=k("map"),D=k("method"),t=k("mode"),ba=k("ok"),N=k("redirect"),
+y=k("status"),R=k("statusText"),Q=k("type"),C=k("url"),E=k("is_aborted"),F=k("signal"),ja="accept-charset accept-encoding access-control-request-headers access-control-request-method connection content-length cookie cookie2 date dnt expect host keep-alive origin referer te trailer transfer-encoding upgrade via".split(" "),na=["set-cookie","set-cookie2"],la=["accept","accept-language","content-language"],ma=["application/x-www-form-urlencoded","multipart/form-data","text/plain"],sa="default no-store reload no-cache force-cache only-if-cached".split(" "),
+ta=["omit","same-origin","include"],ua="DELETE GET HEAD OPTIONS POST PUT".split(" "),wa=["GET","HEAD","POST"],va=["same-origin","no-cors","cors"],xa=["follow","error","manual"],za=[101,204,205,304],Ba=[301,302,303,307,308],Ca="[object Int8Array];[object Uint8Array];[object Uint8ClampedArray];[object Int16Array];[object Uint16Array];[object Int32Array];[object Uint32Array];[object Float32Array];[object Float64Array]".split(";"),qa=Z.isView||function(a){return a&&-1<Ca.indexOf(Object.prototype.toString.call(a))};
+n.prototype.append=function(a,b){if(2!==arguments.length)throw e("Invalid parameters to append");a=J(a);b=X(b);this[A](a,b)||(this[p].has(a)?this[p].set(a,this[p].get(a)+", "+b):this[p].set(a,b))};n.prototype["delete"]=function(a){if(1!==arguments.length)throw e("Invalid parameters to delete");this[A](a,"invalid")||this[p].delete(J(a))};n.prototype.get=function(a){if(1!==arguments.length)throw e("Invalid parameters to get");a=J(a);var b=this[p].get(a);return void 0!==b?b:null};n.prototype.has=function(a){if(1!==
+arguments.length)throw e("Invalid parameters to has");return this[p].has(J(a))};n.prototype.set=function(a,b){if(2!==arguments.length)throw e("Invalid parameters to set");a=J(a);b=X(b);this[A](a,b)||this[p].set(a,b)};n.prototype.forEach=function(a,b){var c=this;K.from(this[p].entries()).sort().forEach(function(d){a.call(b,d[1],d[0],c)})};n.prototype.keys=function(){return(new P(K.from(this[p].entries()).sort())).keys()};n.prototype.values=function(){return(new P(K.from(this[p].entries()).sort())).values()};
+n.prototype.entries=function(){return(new P(K.from(this[p].entries()).sort())).entries()};n.prototype[Aa]=n.prototype.entries;B.prototype.clone=function(){var a=null;null!==this[r]&&(a=ca(this[r],!0),this[r]=a[0],a=a[1]);return new B(this,{cloneBody:a,signal:this[F]})};W(B.prototype,{cache:{get:function(){return this[G]}},credentials:{get:function(){return this[H]}},headers:{get:function(){return this[x]}},integrity:{get:function(){return this[I]}},method:{get:function(){return this[D]}},mode:{get:function(){return this[t]}},
+redirect:{get:function(){return this[N]}},url:{get:function(){return this[C]}},signal:{get:function(){return this[F]}}});Y.call(B.prototype);Y.call(u.prototype);u.prototype.clone=function(){var a=null;null!==this[r]&&(a=ca(this[r],!0),this[r]=a[0],a=a[1]);return new u(a,{status:this[y],statusText:this[R],headers:L(this[x],S),url:this[C],is_aborted:this[E]})};W(u.prototype,{headers:{get:function(){return this[x]}},ok:{get:function(){return this[ba]}},status:{get:function(){return this[y]}},statusText:{get:function(){return this[R]}},
+type:{get:function(){return this[Q]}},url:{get:function(){return this[C]}}});u.error=function(){var a=new u(null);a[x][A]=fa;a[Q]="error";a[y]=0;a[R]="";return a};u.redirect=function(a,b){if(!FetchInternal.isUrlValid(a,!0))throw new e("Invalid URL for response redirect");void 0===b&&(b=302);if(-1===Ba.indexOf(b))throw new aa("Invalid status code for response redirect");return new u(null,{status:b,headers:{location:a}})};h.Headers=n;h.Request=B;h.Response=u;h.fetch=function(a,b){return new z(function(c,
+d){var l=!1,f=!1,q=new B(a,b),g=new XMLHttpRequest,w=null;if(q.signal.aborted)return d(new DOMException("Aborted","AbortError"));var M=new V({start:function(m){w=m},cancel:function(m){l=!0;g.abort()}}),Da=function(){if(!l){l=!0;M.cancel();if(w)try{ReadableStreamDefaultControllerError(w,new DOMException("Aborted","AbortError"))}catch(m){}setTimeout(function(){try{g.abort()}catch(m){}},0)}};g.onload=function(){w.close()};g.onreadystatechange=function(){if(g.readyState===g.HEADERS_RECEIVED){var m={status:g.status,
+statusText:g.statusText,headers:ya(g.getAllResponseHeaders()||"",S)};m.url="responseURL"in g?g.responseURL:m.headers.get("X-Request-URL");try{var O=new u(M,m);q[F].addEventListener("abort",function(){O[E]=!0;Da();d(new DOMException("Aborted","AbortError"))});O[Q]=f?"cors":"basic";c(O)}catch(Ea){d(Ea)}}};g.onerror=function(){w.error(new e("Network request failed"));d(new e("Network request failed"))};g.ontimeout=function(){w.error(new e("Network request failed"));d(new e("Network request failed"))};
+g.open(q.method,q.url,!0);"include"===q.credentials&&(g.withCredentials=!0);q.headers.forEach(function(m,O){g.setRequestHeader(O,m)});var da=function(m){l||w.enqueue(m)},ea=function(m){f=m};null===q.body?g.fetch(da,ea,null):T(q).then(function(m){g.fetch(da,ea,m)})})};h.fetch.polyfill=!0}})(this);
\ No newline at end of file
diff --git a/src/cobalt/fetch/fetch.js b/src/cobalt/fetch/fetch.js
index b5de929..8c8162b 100644
--- a/src/cobalt/fetch/fetch.js
+++ b/src/cobalt/fetch/fetch.js
@@ -434,7 +434,11 @@
     } else if (ArrayBuffer.prototype.isPrototypeOf(data)) {
       controller.enqueue(new Uint8Array(data.slice(0)))
     } else if (isArrayBufferView(data)) {
-      controller.enqueue(new Uint8Array(data.buffer.slice(0)))
+      // View as bytes
+      const asBytes = new Uint8Array(data.buffer);
+      // slice and copy
+      var byteSlice = Uint8Array.from(asBytes.slice(data.byteOffset, data.byteLength + 1));
+      controller.enqueue(byteSlice);
     } else if (data instanceof Blob) {
       controller.enqueue(new Uint8Array(FetchInternal.blobToArrayBuffer(data)))
     } else {
@@ -529,6 +533,7 @@
                   init.cloneBody === undefined
     init = init || {}
     var body = init.body || init.cloneBody
+    if(init.body === '') body = '';
     var headersInit = init.headers
 
     // AbortSignal cannot be constructed directly, so create a temporary
diff --git a/src/cobalt/h5vcc/h5vcc_updater.cc b/src/cobalt/h5vcc/h5vcc_updater.cc
index 4b7a171..bebd3b0 100644
--- a/src/cobalt/h5vcc/h5vcc_updater.cc
+++ b/src/cobalt/h5vcc/h5vcc_updater.cc
@@ -33,7 +33,7 @@
   if (updater_module_->GetUpdaterChannel().compare(channel) != 0 &&
       updater_module_->IsChannelValid(channel)) {
     updater_module_->SetUpdaterChannel(channel);
-    updater_module_->MarkChannelChanged();
+    updater_module_->CompareAndSwapChannelChanged(0, 1);
     updater_module_->RunUpdateCheck();
   }
 }
diff --git a/src/cobalt/layout/paragraph.cc b/src/cobalt/layout/paragraph.cc
index 55ccd1e..a18aecb 100644
--- a/src/cobalt/layout/paragraph.cc
+++ b/src/cobalt/layout/paragraph.cc
@@ -125,17 +125,22 @@
   return start_position;
 }
 
-bool Paragraph::FindBreakPosition(const scoped_refptr<dom::FontList>& used_font,
-                                  int32 start_position, int32 end_position,
-                                  LayoutUnit available_width,
-                                  bool should_collapse_trailing_white_space,
-                                  bool allow_overflow,
-                                  Paragraph::BreakPolicy break_policy,
-                                  int32* break_position,
-                                  LayoutUnit* break_width) {
+bool Paragraph::FindBreakPosition(
+    BaseDirection direction, bool should_attempt_to_wrap,
+    const scoped_refptr<dom::FontList>& used_font, int32 start_position,
+    int32 end_position, LayoutUnit available_width,
+    bool should_collapse_trailing_white_space, bool allow_overflow,
+    Paragraph::BreakPolicy break_policy, int32* break_position,
+    LayoutUnit* break_width) {
   DCHECK(is_closed_);
 
-  *break_position = start_position;
+  DCHECK(direction == base_direction_);
+  if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+      should_attempt_to_wrap) {
+    *break_position = start_position;
+  } else {
+    *break_position = end_position;
+  }
   *break_width = LayoutUnit();
 
   // If overflow isn't allowed and there is no available width, then there is
@@ -165,9 +170,10 @@
     // |break_width| will be updated with the position of the last available
     // break position.
     FindIteratorBreakPosition(
-        used_font, line_break_iterator_, start_position, end_position,
-        available_width, should_collapse_trailing_white_space,
-        allow_normal_overflow, break_position, break_width);
+        direction, should_attempt_to_wrap, used_font, line_break_iterator_,
+        start_position, end_position, available_width,
+        should_collapse_trailing_white_space, allow_normal_overflow,
+        break_position, break_width);
   }
 
   // If break word is the break policy, attempt to break unbreakable "words" at
@@ -177,20 +183,39 @@
   if (break_policy == kBreakPolicyBreakWord) {
     // Only continue allowing overflow if the break position has not moved from
     // start, meaning that no normal break positions were found.
-    allow_overflow = allow_overflow && (*break_position == start_position);
+    if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+        should_attempt_to_wrap) {
+      allow_overflow = allow_overflow && (*break_position == start_position);
+    } else {
+      allow_overflow = allow_overflow && (*break_position == end_position);
+    }
 
     // Find the last available break-word break position. |break_position| and
     // |break_width| will be updated with the position of the last available
     // break position. The search begins at the location of the last normal
     // break position that fit within the available width.
-    FindIteratorBreakPosition(
-        used_font, character_break_iterator_, *break_position, end_position,
-        available_width, false, allow_overflow, break_position, break_width);
+    if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+        should_attempt_to_wrap) {
+      FindIteratorBreakPosition(direction, should_attempt_to_wrap, used_font,
+                                character_break_iterator_, *break_position,
+                                end_position, available_width, false,
+                                allow_overflow, break_position, break_width);
+    } else {
+      FindIteratorBreakPosition(direction, should_attempt_to_wrap, used_font,
+                                character_break_iterator_, start_position,
+                                *break_position, available_width, false,
+                                allow_overflow, break_position, break_width);
+    }
   }
 
   // No usable break position was found if the break position has not moved
   // from the start position.
-  return *break_position > start_position;
+  if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+      should_attempt_to_wrap) {
+    return *break_position > start_position;
+  } else {
+    return *break_position < end_position;
+  }
 }
 
 int32 Paragraph::GetNextBreakPosition(int32 position,
@@ -268,6 +293,12 @@
   return (GetBidiLevel(position) % 2) == 1;
 }
 
+bool Paragraph::AreInlineAndScriptDirectionsTheSame(BaseDirection direction,
+                                                    int32 position) const {
+  return ((direction == kLeftToRightBaseDirection && !IsRTL(position)) ||
+          (direction == kRightToLeftBaseDirection && IsRTL(position)));
+}
+
 bool Paragraph::IsCollapsibleWhiteSpace(int32 position) const {
   // Only check for the space character. Other collapsible white space
   // characters will have already been converted into the space characters and
@@ -338,6 +369,7 @@
 }
 
 void Paragraph::FindIteratorBreakPosition(
+    BaseDirection direction, bool should_attempt_to_wrap,
     const scoped_refptr<dom::FontList>& used_font,
     icu::BreakIterator* const break_iterator, int32 start_position,
     int32 end_position, LayoutUnit available_width,
@@ -347,19 +379,37 @@
   // position. Continue until TryIncludeSegmentWithinAvailableWidth() returns
   // false, indicating that no more segments can be included.
   break_iterator->setText(unicode_text_);
-  for (int32 segment_end = break_iterator->following(start_position);
-       segment_end != icu::BreakIterator::DONE && segment_end < end_position;
-       segment_end = break_iterator->next()) {
-    if (!TryIncludeSegmentWithinAvailableWidth(
-            used_font, *break_position, segment_end, available_width,
-            should_collapse_trailing_white_space, &allow_overflow,
-            break_position, break_width)) {
-      break;
+  if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+      should_attempt_to_wrap) {
+    for (int32 segment_end = break_iterator->following(start_position);
+         segment_end != icu::BreakIterator::DONE && segment_end < end_position;
+         segment_end = break_iterator->next()) {
+      if (!TryIncludeSegmentWithinAvailableWidth(
+              direction, should_attempt_to_wrap, used_font, *break_position,
+              segment_end, available_width,
+              should_collapse_trailing_white_space, &allow_overflow,
+              break_position, break_width)) {
+        break;
+      }
+    }
+  } else {
+    for (int32 segment_begin = break_iterator->preceding(end_position);
+         segment_begin != icu::BreakIterator::DONE &&
+         segment_begin > start_position;
+         segment_begin = break_iterator->previous()) {
+      if (!TryIncludeSegmentWithinAvailableWidth(
+              direction, should_attempt_to_wrap, used_font, segment_begin,
+              *break_position, available_width,
+              should_collapse_trailing_white_space, &allow_overflow,
+              break_position, break_width)) {
+        break;
+      }
     }
   }
 }
 
 bool Paragraph::TryIncludeSegmentWithinAvailableWidth(
+    BaseDirection direction, bool should_attempt_to_wrap,
     const scoped_refptr<dom::FontList>& used_font, int32 segment_start,
     int32 segment_end, LayoutUnit available_width,
     bool should_collapse_trailing_white_space, bool* allow_overflow,
@@ -388,7 +438,12 @@
     return false;
   }
 
-  *break_position = segment_end;
+  if (AreInlineAndScriptDirectionsTheSame(direction, segment_start) ||
+      should_attempt_to_wrap) {
+    *break_position = segment_end;
+  } else {
+    *break_position = segment_start;
+  }
   *break_width += segment_width;
 
   if (*allow_overflow) {
diff --git a/src/cobalt/layout/paragraph.h b/src/cobalt/layout/paragraph.h
index 3feb5d8..d64233c 100644
--- a/src/cobalt/layout/paragraph.h
+++ b/src/cobalt/layout/paragraph.h
@@ -111,7 +111,8 @@
   // substring coming before |break_position|.
   //
   // Returns false if no usable break position was found.
-  bool FindBreakPosition(const scoped_refptr<dom::FontList>& used_font,
+  bool FindBreakPosition(BaseDirection direction, bool should_attempt_to_wrap,
+                         const scoped_refptr<dom::FontList>& used_font,
                          int32 start_position, int32 end_position,
                          LayoutUnit available_width,
                          bool should_collapse_trailing_white_space,
@@ -136,6 +137,8 @@
 
   int GetBidiLevel(int32 position) const;
   bool IsRTL(int32 position) const;
+  bool AreInlineAndScriptDirectionsTheSame(BaseDirection direction,
+                                           int32 position) const;
   bool IsCollapsibleWhiteSpace(int32 position) const;
   bool GetNextRunPosition(int32 position, int32* next_run_position) const;
   int32 GetTextEndPosition() const;
@@ -174,7 +177,9 @@
   // that first overflowing segment will be included. The parameter
   // |break_width| indicates the width of the portion of the substring coming
   // before |break_position|.
-  void FindIteratorBreakPosition(const scoped_refptr<dom::FontList>& used_font,
+  void FindIteratorBreakPosition(BaseDirection direction,
+                                 bool should_attempt_to_wrap,
+                                 const scoped_refptr<dom::FontList>& used_font,
                                  icu::BreakIterator* const break_iterator,
                                  int32 start_position, int32 end_position,
                                  LayoutUnit available_width,
@@ -197,6 +202,7 @@
   // of false does not guarantee that the segment was not included, but simply
   // that no additional segments can be included.
   bool TryIncludeSegmentWithinAvailableWidth(
+      BaseDirection direction, bool should_attempt_to_wrap,
       const scoped_refptr<dom::FontList>& used_font, int32 start_position,
       int32 end_position, LayoutUnit available_width,
       bool should_collapse_trailing_white_space, bool* allow_overflow,
diff --git a/src/cobalt/layout/text_box.cc b/src/cobalt/layout/text_box.cc
index 4507080b..f1bd926 100644
--- a/src/cobalt/layout/text_box.cc
+++ b/src/cobalt/layout/text_box.cc
@@ -42,7 +42,9 @@
       paragraph_(paragraph),
       text_start_position_(text_start_position),
       text_end_position_(text_end_position),
+      truncated_text_start_position_(text_start_position),
       truncated_text_end_position_(text_end_position),
+      previous_truncated_text_start_position_(text_start_position),
       previous_truncated_text_end_position_(text_end_position),
       truncated_text_offset_from_left_(0),
       used_font_(used_style_provider->GetUsedFontList(
@@ -237,12 +239,16 @@
 }
 
 void TextBox::DoPreEllipsisPlacementProcessing() {
+  previous_truncated_text_start_position_ = truncated_text_start_position_;
   previous_truncated_text_end_position_ = truncated_text_end_position_;
+  truncated_text_start_position_ = text_start_position_;
   truncated_text_end_position_ = text_end_position_;
 }
 
 void TextBox::DoPostEllipsisPlacementProcessing() {
-  if (previous_truncated_text_end_position_ != truncated_text_end_position_) {
+  if (previous_truncated_text_start_position_ !=
+          truncated_text_start_position_ ||
+      previous_truncated_text_end_position_ != truncated_text_end_position_) {
     InvalidateRenderTreeNodesOfBoxAndAncestors();
   }
 }
@@ -397,7 +403,7 @@
     // color is animated, in which case it could become non-transparent.
     if (used_color.a() > 0.0f || is_color_animated ||
         text_shadow != cssom::KeywordValue::GetNone()) {
-      int32 text_start_position = GetNonCollapsedTextStartPosition();
+      int32 text_start_position = GetVisibleTextStartPosition();
       int32 text_length = GetVisibleTextLength();
 
       scoped_refptr<render_tree::GlyphBuffer> glyph_buffer =
@@ -481,7 +487,12 @@
   // If the ellipsis has already been placed, then the text is fully truncated
   // by the ellipsis.
   if (*is_placed) {
-    truncated_text_end_position_ = text_start_position_;
+    if (paragraph_->AreInlineAndScriptDirectionsTheSame(base_direction,
+                                                        text_start_position_)) {
+      truncated_text_end_position_ = text_start_position_;
+    } else {
+      truncated_text_start_position_ = text_end_position_;
+    }
     return;
   }
 
@@ -510,8 +521,8 @@
   // text box. Otherwise, it can only appear after the first character
   // (https://www.w3.org/TR/css3-ui/#propdef-text-overflow).
   if (paragraph_->FindBreakPosition(
-          used_font_, start_position, end_position, desired_content_offset,
-          false, !(*is_placement_requirement_met),
+          base_direction, false, used_font_, start_position, end_position,
+          desired_content_offset, false, !(*is_placement_requirement_met),
           Paragraph::kBreakPolicyBreakWord, &found_position, &found_offset)) {
     // A usable break position was found. Calculate the placed offset using the
     // the break position's distance from the content box's start edge. In the
@@ -525,7 +536,12 @@
     } else {
       *placed_offset = content_box_start_offset + found_offset;
     }
-    truncated_text_end_position_ = found_position;
+    if (paragraph_->AreInlineAndScriptDirectionsTheSame(base_direction,
+                                                        start_position)) {
+      truncated_text_end_position_ = found_position;
+    } else {
+      truncated_text_start_position_ = found_position;
+    }
     // An acceptable break position was not found. If the placement requirement
     // was already met prior to this box, then the ellipsis doesn't require a
     // character from this box to appear prior to its position, so simply place
@@ -533,7 +549,12 @@
   } else if (is_placement_requirement_met) {
     *placed_offset =
         GetMarginBoxStartEdgeOffsetFromContainingBlock(base_direction);
-    truncated_text_end_position_ = text_start_position_;
+    if (paragraph_->AreInlineAndScriptDirectionsTheSame(base_direction,
+                                                        start_position)) {
+      truncated_text_end_position_ = text_start_position_;
+    } else {
+      truncated_text_start_position_ = text_end_position_;
+    }
     // The placement requirement has not already been met. Given that an
     // acceptable break position was not found within the text, the ellipsis can
     // only be placed at the end edge of the box.
@@ -603,7 +624,8 @@
       // fits within the available width. Overflow is never allowed.
       LayoutUnit wrap_width;
       if (!paragraph_->FindBreakPosition(
-              used_font_, start_position, text_end_position_, available_width,
+              paragraph_->base_direction(), true, used_font_, start_position,
+              text_end_position_, available_width,
               should_collapse_trailing_white_space, false, break_policy,
               &wrap_position, &wrap_width)) {
         // If no break position is found, but the line existence is already
@@ -720,19 +742,24 @@
                                            Paragraph::kVisualTextOrder);
 }
 
+int32 TextBox::GetVisibleTextStartPosition() const {
+  return std::max(GetNonCollapsedTextStartPosition(),
+                  truncated_text_start_position_);
+}
+
 int32 TextBox::GetVisibleTextEndPosition() const {
   return std::min(GetNonCollapsedTextEndPosition(),
                   truncated_text_end_position_);
 }
 
 int32 TextBox::GetVisibleTextLength() const {
-  return GetVisibleTextEndPosition() - GetNonCollapsedTextStartPosition();
+  return GetVisibleTextEndPosition() - GetVisibleTextStartPosition();
 }
 
 bool TextBox::HasVisibleText() const { return GetVisibleTextLength() > 0; }
 
 std::string TextBox::GetVisibleText() const {
-  return paragraph_->RetrieveUtf8SubString(GetNonCollapsedTextStartPosition(),
+  return paragraph_->RetrieveUtf8SubString(GetVisibleTextStartPosition(),
                                            GetVisibleTextEndPosition(),
                                            Paragraph::kVisualTextOrder);
 }
diff --git a/src/cobalt/layout/text_box.h b/src/cobalt/layout/text_box.h
index c2edcc6..855a0a2 100644
--- a/src/cobalt/layout/text_box.h
+++ b/src/cobalt/layout/text_box.h
@@ -133,6 +133,7 @@
   int32 GetNonCollapsibleTextLength() const;
   std::string GetNonCollapsibleText() const;
 
+  int32 GetVisibleTextStartPosition() const;
   int32 GetVisibleTextEndPosition() const;
   int32 GetVisibleTextLength() const;
   bool HasVisibleText() const;
@@ -145,7 +146,7 @@
   const scoped_refptr<Paragraph> paragraph_;
   // The position within the paragraph where the text contained in this box
   // begins.
-  const int32 text_start_position_;
+  int32 text_start_position_;
   // The position within the paragraph where the text contained in this box
   // ends.
   int32 text_end_position_;
@@ -154,11 +155,13 @@
   // "Implementations must hide characters and atomic inline-level elements at
   // the applicable edge(s) of the line as necessary to fit the ellipsis."
   //   https://www.w3.org/TR/css3-ui/#propdef-text-overflow
+  int32 truncated_text_start_position_;
   int32 truncated_text_end_position_;
-  // Tracking of the previous value of |truncated_text_end_position_|, which
+  // Tracking of the previous value of the truncated text position, which
   // allows for determination of whether or not the value changed during
   // ellipsis placement. When this occurs, the cached render tree nodes of this
   // box and its ancestors are invalidated.
+  int32 previous_truncated_text_start_position_;
   int32 previous_truncated_text_end_position_;
   // The horizontal offset to apply to rendered text as a result of an ellipsis
   // truncating the text. This value can be non-zero when the text box is in a
diff --git a/src/cobalt/layout/topmost_event_target.cc b/src/cobalt/layout/topmost_event_target.cc
index 6316a6b..df2cd6a 100644
--- a/src/cobalt/layout/topmost_event_target.cc
+++ b/src/cobalt/layout/topmost_event_target.cc
@@ -130,18 +130,45 @@
 }
 
 namespace {
+// Return the nearest common ancestor of previous_element and target_element
+scoped_refptr<dom::Element> GetNearestCommonAncestor(
+    scoped_refptr<dom::HTMLElement> previous_element,
+    scoped_refptr<dom::HTMLElement> target_element) {
+  scoped_refptr<dom::Element> nearest_common_ancestor;
+  if (previous_element == target_element) {
+    nearest_common_ancestor = target_element;
+  } else {
+    if (previous_element && target_element) {
+      // Find the nearest common ancestor, if there is any.
+      dom::Document* previous_document = previous_element->node_document();
+      // The elements only have a common ancestor if they are both in the same
+      // document.
+      if (previous_document &&
+          previous_document == target_element->node_document()) {
+        // The nearest ancestor of the target element that is already
+        // designated is the nearest common ancestor of it and the previous
+        // element.
+        nearest_common_ancestor = target_element;
+        while (nearest_common_ancestor &&
+               nearest_common_ancestor->AsHTMLElement() &&
+               !nearest_common_ancestor->AsHTMLElement()->IsDesignated()) {
+          nearest_common_ancestor = nearest_common_ancestor->parent_element();
+        }
+      }
+    }
+  }
+  return nearest_common_ancestor;
+}
+
 void SendStateChangeLeaveEvents(
     bool is_pointer_event, scoped_refptr<dom::HTMLElement> previous_element,
     scoped_refptr<dom::HTMLElement> target_element,
+    scoped_refptr<dom::Element> nearest_common_ancestor,
     dom::PointerEventInit* event_init) {
   // Send enter/leave/over/out (status change) events when needed.
   if (previous_element != target_element) {
     const scoped_refptr<dom::Window>& view = event_init->view();
 
-    // The enter/leave status change events apply to all ancestors up to the
-    // nearest common ancestor between the previous and current element.
-    scoped_refptr<dom::Element> nearest_common_ancestor;
-
     // Send out and leave events.
     if (previous_element) {
       // LottiePlayer elements may change playback state.
@@ -149,24 +176,9 @@
         previous_element->AsLottiePlayer()->OnUnHover();
       }
 
-      event_init->set_related_target(target_element);
-      // Find the nearest common ancestor, if there is any.
       dom::Document* previous_document = previous_element->node_document();
-      if (previous_document) {
-        if (target_element &&
-            previous_document == target_element->node_document()) {
-          // The nearest ancestor of the current element that is already
-          // designated is the nearest common ancestor of it and the previous
-          // element.
-          nearest_common_ancestor = target_element;
-          while (nearest_common_ancestor &&
-                 nearest_common_ancestor->AsHTMLElement() &&
-                 !nearest_common_ancestor->AsHTMLElement()->IsDesignated()) {
-            nearest_common_ancestor = nearest_common_ancestor->parent_element();
-          }
-        }
-      }
 
+      event_init->set_related_target(target_element);
       if (is_pointer_event) {
         previous_element->DispatchEvent(new dom::PointerEvent(
             base::Tokens::pointerout(), view, *event_init));
@@ -209,15 +221,12 @@
 void SendStateChangeEnterEvents(
     bool is_pointer_event, scoped_refptr<dom::HTMLElement> previous_element,
     scoped_refptr<dom::HTMLElement> target_element,
+    scoped_refptr<dom::Element> nearest_common_ancestor,
     dom::PointerEventInit* event_init) {
   // Send enter/leave/over/out (status change) events when needed.
   if (previous_element != target_element) {
     const scoped_refptr<dom::Window>& view = event_init->view();
 
-    // The enter/leave status change events apply to all ancestors up to the
-    // nearest common ancestor between the previous and current element.
-    scoped_refptr<dom::Element> nearest_common_ancestor;
-
     // Send over and enter events.
     if (target_element) {
       // LottiePlayer elements may change playback state.
@@ -399,8 +408,14 @@
   scoped_refptr<dom::HTMLElement> previous_html_element(
       previous_html_element_weak_);
 
+  // The enter/leave status change events apply to all ancestors up to the
+  // nearest common ancestor between the previous and current element.
+  scoped_refptr<dom::Element> nearest_common_ancestor(
+      GetNearestCommonAncestor(previous_html_element, target_element));
+
   SendStateChangeLeaveEvents(pointer_event, previous_html_element,
-                             target_element, &event_init);
+                             target_element, nearest_common_ancestor,
+                             &event_init);
 
   if (target_element) {
     target_element->DispatchEvent(event);
@@ -424,26 +439,41 @@
     }
   }
 
-  if (target_element && !is_touchpad_event) {
-    // Send the click event if needed, which is not prevented by canceling the
-    // pointerdown event.
-    //   https://www.w3.org/TR/uievents/#event-type-click
-    //   https://www.w3.org/TR/pointerevents/#compatibility-mapping-with-mouse-events
-    if (event_init.button() == 0 &&
-        ((mouse_event->type() == base::Tokens::pointerup()) ||
-         (mouse_event->type() == base::Tokens::mouseup()))) {
+  if (event_init.button() == 0 &&
+      ((mouse_event->type() == base::Tokens::pointerup()) ||
+       (mouse_event->type() == base::Tokens::mouseup()))) {
+    // This is an 'up' event for the last pressed button indicating that no
+    // more buttons are pressed.
+    if (target_element && !is_touchpad_event) {
+      // Send the click event if needed, which is not prevented by canceling
+      // the pointerdown event.
+      //   https://www.w3.org/TR/uievents/#event-type-click
+      //   https://www.w3.org/TR/pointerevents/#compatibility-mapping-with-mouse-events
       target_element->DispatchEvent(
           new dom::MouseEvent(base::Tokens::click(), view, event_init));
     }
+    if (target_element && (pointer_event->pointer_type() != "mouse")) {
+      // If it's not a mouse event, then releasing the last button means
+      // that there is no longer an indicated element.
+      dom::Document* document = target_element->node_document();
+      if (document) {
+        document->SetIndicatedElement(NULL);
+        target_element = NULL;
+      }
+    }
   }
 
   SendStateChangeEnterEvents(pointer_event, previous_html_element,
-                             target_element, &event_init);
+                             target_element, nearest_common_ancestor,
+                             &event_init);
 
   if (target_element) {
-    dom::Document* document = target_element->node_document();
-    if (document) {
-      document->SetIndicatedElement(target_element);
+    // Touchpad input never indicates document elements.
+    if (!is_touchpad_event) {
+      dom::Document* document = target_element->node_document();
+      if (document) {
+        document->SetIndicatedElement(target_element);
+      }
     }
     previous_html_element_weak_ = base::AsWeakPtr(target_element.get());
   } else {
diff --git a/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines-expected.png b/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines-expected.png
index d0f9074..7e86a49 100644
--- a/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines-expected.png
+++ b/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines.html b/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines.html
index 71a614f..463fedd 100644
--- a/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines.html
+++ b/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines.html
@@ -30,8 +30,12 @@
           لتد2ل3تت4دل5ت6د ط1لتدلنش 123456789 745 ط1لت
           987 لتد 654</span>
   </div>
+  <div class="containing-block" dir="rtl">
+    <span>12لتدل3ت dدلت4دلتد5ل6تد يabطا32ل Single Word
+          1234 ط1لتدلنش ab12دلتد34cd ط1aلتد2ل 3تت489
+          12 ل5ت6745 ط1لتدلنش ط1لaتدلتدتد ط11246 ط1abcd
+          لتد2ل3تت4دل5ت6د ط1لتدلنش 123456789 745 ط1لت
+          987 لتد 654</span>
+  </div>
 </body>
 </html>
-
-</body>
-</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found-expected.png
index 5f2548e..bf339a6 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly-expected.png
index 30e5ac4..e509aaa 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly.html b/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly.html
index 22e2cab..e1e8f5b 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly.html
@@ -29,7 +29,7 @@
     <span>&#x1f648;&#x1f649;&#x1f64a;&#x1f468;&#x1f469;&#x1f3c3;&#x1f40c;</span>
     <span>&#x1f41c;&#x1f490;&#x1f332;&#x1f347;&#x1f95d;&#x1f344;&#x1f354;</span>
     <span>&#x1f35f;&#x1f35e;&#x1f95e;&#x1f32e;&#x1f363;&#x1f361;&#x1f368;</span>
-    <span class="bold-span">&#x1f30e;&#x1f31a;</span>
+    <span class="bold-span">&#x1f30e;&#x1f31a;&#x1f970;</span>
   </div>
 </body>
 </html>
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-clip-when-it-overflows-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-clip-when-it-overflows-expected.png
index 27b72da..0bbe59c 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-clip-when-it-overflows-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-clip-when-it-overflows-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows-expected.png
index 164d112..2e89634 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word-expected.png
index 59aa7b9..4db6e7b 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix-expected.png
index 7c2215c..21d31da 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation-expected.png
index 4cfa868..85a1acb 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line-expected.png
index 27b72da..0bbe59c 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins-expected.png
index a69a33a..f71519f 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span-expected.png
index 1c4e931..ac84ae0 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-use-style-of-containing-block-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-use-style-of-containing-block-expected.png
index 5d56a8b..e059ac1 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-use-style-of-containing-block-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-use-style-of-containing-block-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis-expected.png
index 7b2d801..8b20b2a 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden-expected.png
index bf2da54..7fecf85 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited-expected.png
index bb3987f..a264063 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt
index 5c11ed1..72ab7d8 100644
--- a/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt
@@ -97,12 +97,8 @@
 api/request/request-cache-only-if-cached.html,DISABLE
 api/request/request-cache-reload.html,DISABLE
 api/request/request-clone.sub.html,PASS
-# Fails because Body only supports text, json, and arrayBuffer. See the
-# corresponding tests for Response which have been customized to test only
-# those types.
-api/request/request-consume.html,DISABLE
-# Fails because blob and formData are not supported.
-api/request/request-consume-empty.html,DISABLE
+api/request/request-consume.html,PASS
+api/request/request-consume-empty.html,PASS
 # Disabled due to failing test fixture
 api/request/request-disturbed.html,DISABLE
 api/request/request-error.html,PASS
diff --git a/src/cobalt/media/base/shell_audio_bus.cc b/src/cobalt/media/base/audio_bus.cc
similarity index 87%
rename from src/cobalt/media/base/shell_audio_bus.cc
rename to src/cobalt/media/base/audio_bus.cc
index 5140d01..ea63e7a 100644
--- a/src/cobalt/media/base/shell_audio_bus.cc
+++ b/src/cobalt/media/base/audio_bus.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 
 #include <algorithm>
 #include <limits>
@@ -24,20 +24,17 @@
 
 namespace {
 
-typedef ShellAudioBus::StorageType StorageType;
-typedef ShellAudioBus::SampleType SampleType;
+typedef AudioBus::StorageType StorageType;
+typedef AudioBus::SampleType SampleType;
 
 const float kFloat32ToInt16Factor = 32768.f;
 
-inline void ConvertSample(ShellAudioBus::SampleType src_type,
-                          const uint8* src_ptr,
-                          ShellAudioBus::SampleType dest_type,
-                          uint8* dest_ptr) {
+inline void ConvertSample(AudioBus::SampleType src_type, const uint8* src_ptr,
+                          AudioBus::SampleType dest_type, uint8* dest_ptr) {
   if (src_type == dest_type) {
-    SbMemoryCopy(
-        dest_ptr, src_ptr,
-        src_type == ShellAudioBus::kInt16 ? sizeof(int16) : sizeof(float));
-  } else if (src_type == ShellAudioBus::kFloat32) {
+    SbMemoryCopy(dest_ptr, src_ptr,
+                 src_type == AudioBus::kInt16 ? sizeof(int16) : sizeof(float));
+  } else if (src_type == AudioBus::kFloat32) {
     float sample_in_float = *reinterpret_cast<const float*>(src_ptr);
     int32 sample_in_int32 =
         static_cast<int32>(sample_in_float * kFloat32ToInt16Factor);
@@ -78,8 +75,8 @@
 
 }  // namespace
 
-ShellAudioBus::ShellAudioBus(size_t channels, size_t frames,
-                             SampleType sample_type, StorageType storage_type)
+AudioBus::AudioBus(size_t channels, size_t frames, SampleType sample_type,
+                   StorageType storage_type)
     : channels_(channels),
       frames_(frames),
       sample_type_(sample_type),
@@ -106,7 +103,7 @@
   }
 }
 
-ShellAudioBus::ShellAudioBus(size_t frames, const std::vector<float*>& samples)
+AudioBus::AudioBus(size_t frames, const std::vector<float*>& samples)
     : channels_(samples.size()),
       frames_(frames),
       sample_type_(kFloat32),
@@ -119,7 +116,7 @@
   }
 }
 
-ShellAudioBus::ShellAudioBus(size_t channels, size_t frames, float* samples)
+AudioBus::AudioBus(size_t channels, size_t frames, float* samples)
     : channels_(channels),
       frames_(frames),
       sample_type_(kFloat32),
@@ -129,7 +126,7 @@
   channel_data_.push_back(reinterpret_cast<uint8*>(samples));
 }
 
-ShellAudioBus::ShellAudioBus(size_t frames, const std::vector<int16*>& samples)
+AudioBus::AudioBus(size_t frames, const std::vector<int16*>& samples)
     : channels_(samples.size()),
       frames_(frames),
       sample_type_(kInt16),
@@ -142,7 +139,7 @@
   }
 }
 
-ShellAudioBus::ShellAudioBus(size_t channels, size_t frames, int16* samples)
+AudioBus::AudioBus(size_t channels, size_t frames, int16* samples)
     : channels_(channels),
       frames_(frames),
       sample_type_(kInt16),
@@ -152,7 +149,7 @@
   channel_data_.push_back(reinterpret_cast<uint8*>(samples));
 }
 
-size_t ShellAudioBus::GetSampleSizeInBytes() const {
+size_t AudioBus::GetSampleSizeInBytes() const {
   if (sample_type_ == kInt16) {
     return sizeof(int16);
   }
@@ -160,29 +157,29 @@
   return sizeof(float);
 }
 
-const uint8* ShellAudioBus::interleaved_data() const {
+const uint8* AudioBus::interleaved_data() const {
   DCHECK_EQ(storage_type_, kInterleaved);
   return channel_data_[0];
 }
 
-const uint8* ShellAudioBus::planar_data(size_t channel) const {
+const uint8* AudioBus::planar_data(size_t channel) const {
   DCHECK_LT(channel, channels_);
   DCHECK_EQ(storage_type_, kPlanar);
   return channel_data_[channel];
 }
 
-uint8* ShellAudioBus::interleaved_data() {
+uint8* AudioBus::interleaved_data() {
   DCHECK_EQ(storage_type_, kInterleaved);
   return channel_data_[0];
 }
 
-uint8* ShellAudioBus::planar_data(size_t channel) {
+uint8* AudioBus::planar_data(size_t channel) {
   DCHECK_LT(channel, channels_);
   DCHECK_EQ(storage_type_, kPlanar);
   return channel_data_[channel];
 }
 
-void ShellAudioBus::ZeroFrames(size_t start_frame, size_t end_frame) {
+void AudioBus::ZeroFrames(size_t start_frame, size_t end_frame) {
   DCHECK_LE(start_frame, end_frame);
   DCHECK_LE(end_frame, frames_);
   end_frame = std::min(end_frame, frames_);
@@ -201,7 +198,7 @@
   }
 }
 
-void ShellAudioBus::Assign(const ShellAudioBus& source) {
+void AudioBus::Assign(const AudioBus& source) {
   DCHECK_EQ(channels_, source.channels_);
   if (channels_ != source.channels_) {
     ZeroAllFrames();
@@ -232,8 +229,8 @@
   }
 }
 
-void ShellAudioBus::Assign(const ShellAudioBus& source,
-                           const std::vector<float>& matrix) {
+void AudioBus::Assign(const AudioBus& source,
+                      const std::vector<float>& matrix) {
   DCHECK_EQ(channels() * source.channels(), matrix.size());
   DCHECK_EQ(sample_type_, kFloat32);
   DCHECK_EQ(source.sample_type_, kFloat32);
@@ -258,7 +255,7 @@
 }
 
 template <StorageType SourceStorageType, StorageType DestStorageType>
-void ShellAudioBus::MixFloatSamples(const ShellAudioBus& source) {
+void AudioBus::MixFloatSamples(const AudioBus& source) {
   const size_t frames = std::min(frames_, source.frames_);
 
   if (SourceStorageType == DestStorageType) {
@@ -284,7 +281,7 @@
 }
 
 template <StorageType SourceStorageType, StorageType DestStorageType>
-void ShellAudioBus::MixInt16Samples(const ShellAudioBus& source) {
+void AudioBus::MixInt16Samples(const AudioBus& source) {
   const size_t frames = std::min(frames_, source.frames_);
 
   if (SourceStorageType == DestStorageType) {
@@ -318,7 +315,7 @@
   }
 }
 
-void ShellAudioBus::Mix(const ShellAudioBus& source) {
+void AudioBus::Mix(const AudioBus& source) {
   DCHECK_EQ(channels_, source.channels_);
   DCHECK_EQ(sample_type_, source.sample_type_);
 
@@ -359,8 +356,7 @@
   }
 }
 
-void ShellAudioBus::Mix(const ShellAudioBus& source,
-                        const std::vector<float>& matrix) {
+void AudioBus::Mix(const AudioBus& source, const std::vector<float>& matrix) {
   DCHECK_EQ(channels() * source.channels(), matrix.size());
   DCHECK_EQ(sample_type_, source.sample_type_);
 
@@ -411,7 +407,7 @@
   }
 }
 
-uint8* ShellAudioBus::GetSamplePtr(size_t channel, size_t frame) {
+uint8* AudioBus::GetSamplePtr(size_t channel, size_t frame) {
   DCHECK_LT(channel, channels_);
   DCHECK_LT(frame, frames_);
 
@@ -423,7 +419,7 @@
   }
 }
 
-const uint8* ShellAudioBus::GetSamplePtr(size_t channel, size_t frame) const {
+const uint8* AudioBus::GetSamplePtr(size_t channel, size_t frame) const {
   DCHECK_LT(channel, channels_);
   DCHECK_LT(frame, frames_);
 
diff --git a/src/cobalt/media/base/shell_audio_bus.h b/src/cobalt/media/base/audio_bus.h
similarity index 85%
rename from src/cobalt/media/base/shell_audio_bus.h
rename to src/cobalt/media/base/audio_bus.h
index cdd4668..2b4c369 100644
--- a/src/cobalt/media/base/shell_audio_bus.h
+++ b/src/cobalt/media/base/audio_bus.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_BASE_SHELL_AUDIO_BUS_H_
-#define COBALT_MEDIA_BASE_SHELL_AUDIO_BUS_H_
+#ifndef COBALT_MEDIA_BASE_AUDIO_BUS_H_
+#define COBALT_MEDIA_BASE_AUDIO_BUS_H_
 
 #include <memory>
 #include <vector>
@@ -37,11 +37,11 @@
 // second of such audio contains 48000 frames (96000 samples).
 // Note: This class doesn't do endianness conversions.  It assumes that all data
 // is in the correct endianness.
-class COBALT_EXPORT ShellAudioBus {
+class COBALT_EXPORT AudioBus {
  public:
   // Guaranteed alignment of each channel's data; use 64-byte alignment so it
   // satisfies all our current platforms.  Note that this is only used for
-  // buffers that are allocated and owned by the ShellAudioBus.  We don't
+  // buffers that are allocated and owned by the AudioBus.  We don't
   // enforce alignment for the buffers passed in and extra caution should be
   // taken if they are used as hardware buffer.
   static const size_t kChannelAlignmentInBytes = 64;
@@ -50,12 +50,12 @@
 
   enum StorageType { kInterleaved, kPlanar };
 
-  ShellAudioBus(size_t channels, size_t frames, SampleType sample_type,
-                StorageType storage_type);
-  ShellAudioBus(size_t frames, const std::vector<float*>& samples);
-  ShellAudioBus(size_t channels, size_t frames, float* samples);
-  ShellAudioBus(size_t frames, const std::vector<int16*>& samples);
-  ShellAudioBus(size_t channels, size_t frames, int16* samples);
+  AudioBus(size_t channels, size_t frames, SampleType sample_type,
+           StorageType storage_type);
+  AudioBus(size_t frames, const std::vector<float*>& samples);
+  AudioBus(size_t channels, size_t frames, float* samples);
+  AudioBus(size_t frames, const std::vector<int16*>& samples);
+  AudioBus(size_t channels, size_t frames, int16* samples);
 
   size_t channels() const { return channels_; }
   size_t frames() const { return frames_; }
@@ -84,7 +84,7 @@
   // conversion between different sample types and storage types.  When source
   // has less frames than the destination object, it will only copy these frames
   // and will not fill the rest frames in our buffer with 0.
-  void Assign(const ShellAudioBus& source);
+  void Assign(const AudioBus& source);
 
   // The same as the above function except that this function also does mixing.
   // |matrix| is a |dest.channels()| row * |source.channels()| column matrix in
@@ -96,14 +96,14 @@
   //   + source.sample[source.channels() - 1][frame] *
   //         matrix[channels() * source.channels() + source.channels() - 1];
   // Note: Both objects must have storage type of kFloat32.
-  void Assign(const ShellAudioBus& source, const std::vector<float>& matrix);
+  void Assign(const AudioBus& source, const std::vector<float>& matrix);
 
   // The following functions are the same as the Assign() functions except that
   // they add the calculated samples to the target samples instead of replacing
   // the target samples with the calculated samples.
   // Note: Both objects must have storage type of kFloat32.
-  void Mix(const ShellAudioBus& source);
-  void Mix(const ShellAudioBus& source, const std::vector<float>& matrix);
+  void Mix(const AudioBus& source);
+  void Mix(const AudioBus& source, const std::vector<float>& matrix);
 
  public:
   // The .*ForTypes? functions below are optimized versions that assume what
@@ -134,10 +134,10 @@
   }
 
   template <StorageType SourceStorageType, StorageType DestStorageType>
-  void MixFloatSamples(const ShellAudioBus& source);
+  void MixFloatSamples(const AudioBus& source);
 
   template <StorageType SourceStorageType, StorageType DestStorageType>
-  void MixInt16Samples(const ShellAudioBus& source);
+  void MixInt16Samples(const AudioBus& source);
 
  private:
   void SetFloat32Sample(size_t channel, size_t frame, float sample) {
@@ -160,10 +160,10 @@
   SampleType sample_type_;
   StorageType storage_type_;
 
-  DISALLOW_COPY_AND_ASSIGN(ShellAudioBus);
+  DISALLOW_COPY_AND_ASSIGN(AudioBus);
 };
 
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_BASE_SHELL_AUDIO_BUS_H_
+#endif  // COBALT_MEDIA_BASE_AUDIO_BUS_H_
diff --git a/src/cobalt/media/can_play_type_handler.h b/src/cobalt/media/can_play_type_handler.h
index 40d1ea6..00b94f1 100644
--- a/src/cobalt/media/can_play_type_handler.h
+++ b/src/cobalt/media/can_play_type_handler.h
@@ -25,9 +25,10 @@
 class CanPlayTypeHandler {
  public:
   virtual ~CanPlayTypeHandler() {}
-  virtual SbMediaSupportType CanPlayType(const std::string& mime_type,
-                                         const std::string& key_system,
-                                         bool is_progressive) const = 0;
+  virtual SbMediaSupportType CanPlayProgressive(
+      const std::string& mime_type) const = 0;
+  virtual SbMediaSupportType CanPlayAdaptive(
+      const std::string& mime_type, const std::string& key_system) const = 0;
   virtual void SetDisabledMediaCodecs(const std::string& codecs) = 0;
 
  protected:
diff --git a/src/cobalt/media/fetcher_buffered_data_source.cc b/src/cobalt/media/fetcher_buffered_data_source.cc
index 2409eb8..9eb25be 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.cc
+++ b/src/cobalt/media/fetcher_buffered_data_source.cc
@@ -407,7 +407,7 @@
     read_cb.Run(static_cast<int>(bytes_peeked));
     // If we have a large buffer size, it could be ideal if we can keep sending
     // small requests when the read offset is far from the beginning of the
-    // buffer.  However as the ShellDemuxer will cache many frames and the
+    // buffer.  However as the ProgressiveDemuxer will cache many frames and the
     // buffer we are using is usually small, we will just avoid sending requests
     // here to make code simple.
     return;
diff --git a/src/cobalt/media/filters/source_buffer_stream.cc b/src/cobalt/media/filters/source_buffer_stream.cc
index c4208ba..0d30352 100644
--- a/src/cobalt/media/filters/source_buffer_stream.cc
+++ b/src/cobalt/media/filters/source_buffer_stream.cc
@@ -736,7 +736,7 @@
 
   size_t bytes_to_free = 0;
 
-  int garbage_collection_duration_threshold_in_seconds =
+  int64_t garbage_collection_duration_threshold_in_seconds =
       SbMediaGetBufferGarbageCollectionDurationThreshold() / kSbTimeSecond;
   // Check if we're under or at the memory/duration limit.
   const auto kGcDurationThresholdInMilliseconds =
diff --git a/src/cobalt/media/media.gyp b/src/cobalt/media/media.gyp
index bc1e422..4b31fc0 100644
--- a/src/cobalt/media/media.gyp
+++ b/src/cobalt/media/media.gyp
@@ -29,6 +29,8 @@
         'media_module.cc',
         'media_module.h',
 
+        'base/audio_bus.cc',
+        'base/audio_bus.h',
         'base/audio_codecs.cc',
         'base/audio_codecs.h',
         'base/audio_decoder_config.cc',
@@ -90,10 +92,6 @@
         'base/sbplayer_pipeline.cc',
         'base/sbplayer_set_bounds_helper.cc',
         'base/sbplayer_set_bounds_helper.h',
-        'base/shell_audio_bus.cc',
-        'base/shell_audio_bus.h',
-        'base/shell_data_source_reader.cc',
-        'base/shell_data_source_reader.h',
         'base/starboard_player.cc',
         'base/starboard_player.h',
         'base/starboard_utils.cc',
@@ -125,20 +123,6 @@
         'filters/h264_to_annex_b_bitstream_converter.h',
         'filters/h265_parser.cc',
         'filters/h265_parser.h',
-        'filters/shell_au.cc',
-        'filters/shell_au.h',
-        'filters/shell_avc_parser.cc',
-        'filters/shell_avc_parser.h',
-        'filters/shell_demuxer.cc',
-        'filters/shell_demuxer.h',
-        'filters/shell_mp4_map.cc',
-        'filters/shell_mp4_map.h',
-        'filters/shell_mp4_parser.cc',
-        'filters/shell_mp4_parser.h',
-        'filters/shell_parser.cc',
-        'filters/shell_parser.h',
-        'filters/shell_rbsp_stream.cc',
-        'filters/shell_rbsp_stream.h',
         'filters/source_buffer_range.cc',
         'filters/source_buffer_range.h',
         'filters/source_buffer_state.cc',
@@ -216,6 +200,22 @@
         'player/web_media_player_impl.h',
         'player/web_media_player_proxy.cc',
         'player/web_media_player_proxy.h',
+        'progressive/avc_access_unit.cc',
+        'progressive/avc_access_unit.h',
+        'progressive/avc_parser.cc',
+        'progressive/avc_parser.h',
+        'progressive/data_source_reader.cc',
+        'progressive/data_source_reader.h',
+        'progressive/mp4_map.cc',
+        'progressive/mp4_map.h',
+        'progressive/mp4_parser.cc',
+        'progressive/mp4_parser.h',
+        'progressive/progressive_demuxer.cc',
+        'progressive/progressive_demuxer.h',
+        'progressive/progressive_parser.cc',
+        'progressive/progressive_parser.h',
+        'progressive/rbsp_stream.cc',
+        'progressive/rbsp_stream.h',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
@@ -240,9 +240,9 @@
         '<(DEPTH)/testing/gtest.gyp:gtest',
       ],
       'sources': [
-        'base/mock_shell_data_source_reader.h',
-        'filters/shell_mp4_map_unittest.cc',
-        'filters/shell_rbsp_stream_unittest.cc',
+        'progressive/mock_data_source_reader.h',
+        'progressive/mp4_map_unittest.cc',
+        'progressive/rbsp_stream_unittest.cc',
       ],
       'includes': [ '<(DEPTH)/cobalt/test/test.gypi' ],
     },
diff --git a/src/cobalt/media/media_module.cc b/src/cobalt/media/media_module.cc
index e476a6f..82ba5c2 100644
--- a/src/cobalt/media/media_module.cc
+++ b/src/cobalt/media/media_module.cc
@@ -48,37 +48,27 @@
               << "\" from console/command line.";
   }
 
-  SbMediaSupportType CanPlayType(const std::string& mime_type,
-                                 const std::string& key_system,
-                                 bool is_progressive) const override {
-    if (is_progressive) {
-      // |mime_type| is something like:
-      //   video/mp4
-      //   video/webm
-      //   video/mp4; codecs="avc1.4d401e"
-      //   video/webm; codecs="vp9"
-      // We do a rough pre-filter to ensure that only video/mp4 is supported as
-      // progressive.
-      if (SbStringFindString(mime_type.c_str(), "video/mp4") == 0 &&
-          SbStringFindString(mime_type.c_str(), "application/x-mpegURL") == 0) {
-        return kSbMediaSupportTypeNotSupported;
-      }
+  SbMediaSupportType CanPlayProgressive(
+      const std::string& mime_type) const override {
+    // |mime_type| is something like:
+    //   video/mp4
+    //   video/webm
+    //   video/mp4; codecs="avc1.4d401e"
+    //   video/webm; codecs="vp9"
+    // We do a rough pre-filter to ensure that only video/mp4 is supported as
+    // progressive.
+    if (SbStringFindString(mime_type.c_str(), "video/mp4") == 0 &&
+        SbStringFindString(mime_type.c_str(), "application/x-mpegURL") == 0) {
+      return kSbMediaSupportTypeNotSupported;
     }
-    if (!disabled_media_codecs_.empty()) {
-      auto mime_codecs = ExtractCodecs(mime_type);
-      for (auto& disabled_codec : disabled_media_codecs_) {
-        for (auto& mime_codec : mime_codecs) {
-          if (mime_codec.find(disabled_codec) != std::string::npos) {
-            LOG(INFO) << "Codec (" << mime_codec
-                      << ") is disabled via console/command line.";
-            return kSbMediaSupportTypeNotSupported;
-          }
-        }
-      }
-    }
-    SbMediaSupportType type =
-        SbMediaCanPlayMimeAndKeySystem(mime_type.c_str(), key_system.c_str());
-    return type;
+
+    return CanPlayType(mime_type, "");
+  }
+
+  SbMediaSupportType CanPlayAdaptive(
+      const std::string& mime_type,
+      const std::string& key_system) const override {
+    return CanPlayType(mime_type, key_system);
   }
 
  private:
@@ -107,6 +97,25 @@
     return codecs;
   }
 
+  SbMediaSupportType CanPlayType(const std::string& mime_type,
+                                 const std::string& key_system) const {
+    if (!disabled_media_codecs_.empty()) {
+      auto mime_codecs = ExtractCodecs(mime_type);
+      for (auto& disabled_codec : disabled_media_codecs_) {
+        for (auto& mime_codec : mime_codecs) {
+          if (mime_codec.find(disabled_codec) != std::string::npos) {
+            LOG(INFO) << "Codec (" << mime_codec
+                      << ") is disabled via console/command line.";
+            return kSbMediaSupportTypeNotSupported;
+          }
+        }
+      }
+    }
+    SbMediaSupportType type =
+        SbMediaCanPlayMimeAndKeySystem(mime_type.c_str(), key_system.c_str());
+    return type;
+  }
+
   // List of disabled media codecs that will be treated as unsupported.
   std::vector<std::string> disabled_media_codecs_;
 };
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index 6f18dc7..d5cfa6f 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -24,8 +24,8 @@
 #include "cobalt/media/base/limits.h"
 #include "cobalt/media/base/media_log.h"
 #include "cobalt/media/filters/chunk_demuxer.h"
-#include "cobalt/media/filters/shell_demuxer.h"
 #include "cobalt/media/player/web_media_player_proxy.h"
+#include "cobalt/media/progressive/progressive_demuxer.h"
 #include "starboard/double.h"
 #include "starboard/types.h"
 
@@ -292,8 +292,8 @@
   is_local_source_ = !url.SchemeIs("http") && !url.SchemeIs("https");
 
   progressive_demuxer_.reset(
-      new ShellDemuxer(pipeline_thread_.task_runner(), buffer_allocator_,
-                       proxy_->data_source(), media_log_));
+      new ProgressiveDemuxer(pipeline_thread_.task_runner(), buffer_allocator_,
+                             proxy_->data_source(), media_log_));
 
   state_.is_progressive = true;
   StartPipeline(progressive_demuxer_.get());
diff --git a/src/cobalt/media/filters/shell_au.cc b/src/cobalt/media/progressive/avc_access_unit.cc
similarity index 74%
rename from src/cobalt/media/filters/shell_au.cc
rename to src/cobalt/media/progressive/avc_access_unit.cc
index fadfadb..25ad24e 100644
--- a/src/cobalt/media/filters/shell_au.cc
+++ b/src/cobalt/media/progressive/avc_access_unit.cc
@@ -12,14 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/filters/shell_au.h"
+#include "cobalt/media/progressive/avc_access_unit.h"
 
 #include <algorithm>
 
 #include "cobalt/media/base/decoder_buffer.h"
 #include "cobalt/media/base/endian_util.h"
 #include "cobalt/media/base/timestamp_constants.h"
-#include "cobalt/media/filters/shell_parser.h"
+#include "cobalt/media/progressive/progressive_parser.h"
 
 namespace cobalt {
 namespace media {
@@ -27,7 +27,7 @@
 namespace {
 
 bool ReadBytes(uint64 offset, size_t size, uint8* buffer,
-               ShellDataSourceReader* reader) {
+               DataSourceReader* reader) {
   if (reader->BlockingRead(offset, size, buffer) != size) {
     DLOG(ERROR) << "unable to download AU";
     return false;
@@ -36,7 +36,7 @@
 }
 
 bool ReadBytes(uint64 offset, size_t size, DecoderBuffer* decoder_buffer,
-               uint64 decoder_buffer_offset, ShellDataSourceReader* reader) {
+               uint64 decoder_buffer_offset, DataSourceReader* reader) {
   size_t buffer_index = 0;
   auto& allocations = decoder_buffer->allocations();
   while (size > 0) {
@@ -67,17 +67,17 @@
   return true;
 }
 
-// ==== ShellEndOfStreamAU ==================================================
+// ==== EndOfStreamAU ==================================================
 
-class ShellEndOfStreamAU : public ShellAU {
+class EndOfStreamAU : public AvcAccessUnit {
  public:
-  ShellEndOfStreamAU(Type type, TimeDelta timestamp)
+  EndOfStreamAU(Type type, TimeDelta timestamp)
       : type_(type), timestamp_(timestamp), duration_(kInfiniteDuration) {}
 
  private:
   bool IsEndOfStream() const override { return true; }
   bool IsValid() const override { return true; }
-  bool Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) override {
+  bool Read(DataSourceReader* reader, DecoderBuffer* buffer) override {
     NOTREACHED();
     return false;
   }
@@ -102,20 +102,19 @@
   TimeDelta duration_;
 };
 
-// ==== ShellAudioAU =======================================================
+// ==== AudioAU =======================================================
 
-class ShellAudioAU : public ShellAU {
+class AudioAU : public AvcAccessUnit {
  public:
-  ShellAudioAU(uint64 offset, size_t size, size_t prepend_size,
-               bool is_keyframe, TimeDelta timestamp, TimeDelta duration,
-               ShellParser* parser);
+  AudioAU(uint64 offset, size_t size, size_t prepend_size, bool is_keyframe,
+          TimeDelta timestamp, TimeDelta duration, ProgressiveParser* parser);
 
  private:
   bool IsEndOfStream() const override { return false; }
   bool IsValid() const override {
     return offset_ != 0 && size_ != 0 && timestamp_ != kNoTimestamp;
   }
-  bool Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) override;
+  bool Read(DataSourceReader* reader, DecoderBuffer* buffer) override;
   Type GetType() const override { return DemuxerStream::AUDIO; }
   bool IsKeyframe() const override { return is_keyframe_; }
   bool AddPrepend() const override { return true; }
@@ -132,12 +131,12 @@
   bool is_keyframe_;
   TimeDelta timestamp_;
   TimeDelta duration_;
-  ShellParser* parser_;
+  ProgressiveParser* parser_;
 };
 
-ShellAudioAU::ShellAudioAU(uint64 offset, size_t size, size_t prepend_size,
-                           bool is_keyframe, TimeDelta timestamp,
-                           TimeDelta duration, ShellParser* parser)
+AudioAU::AudioAU(uint64 offset, size_t size, size_t prepend_size,
+                 bool is_keyframe, TimeDelta timestamp, TimeDelta duration,
+                 ProgressiveParser* parser)
     : offset_(offset),
       size_(size),
       prepend_size_(prepend_size),
@@ -146,7 +145,7 @@
       duration_(duration),
       parser_(parser) {}
 
-bool ShellAudioAU::Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) {
+bool AudioAU::Read(DataSourceReader* reader, DecoderBuffer* buffer) {
   DCHECK_LE(size_ + prepend_size_, buffer->data_size());
   if (!ReadBytes(offset_, size_, buffer, prepend_size_, reader)) return false;
 
@@ -158,20 +157,20 @@
   return true;
 }
 
-// ==== ShellVideoAU =======================================================
+// ==== VideoAU =======================================================
 
-class ShellVideoAU : public ShellAU {
+class VideoAU : public AvcAccessUnit {
  public:
-  ShellVideoAU(uint64 offset, size_t size, size_t prepend_size,
-               uint8 length_of_nalu_size, bool is_keyframe, TimeDelta timestamp,
-               TimeDelta duration, ShellParser* parser);
+  VideoAU(uint64 offset, size_t size, size_t prepend_size,
+          uint8 length_of_nalu_size, bool is_keyframe, TimeDelta timestamp,
+          TimeDelta duration, ProgressiveParser* parser);
 
  private:
   bool IsEndOfStream() const override { return false; }
   bool IsValid() const override {
     return offset_ != 0 && size_ != 0 && timestamp_ != kNoTimestamp;
   }
-  bool Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) override;
+  bool Read(DataSourceReader* reader, DecoderBuffer* buffer) override;
   Type GetType() const override { return DemuxerStream::VIDEO; }
   bool IsKeyframe() const override { return is_keyframe_; }
   bool AddPrepend() const override { return is_keyframe_; }
@@ -193,13 +192,13 @@
   bool is_keyframe_;
   TimeDelta timestamp_;
   TimeDelta duration_;
-  ShellParser* parser_;
+  ProgressiveParser* parser_;
 };
 
-ShellVideoAU::ShellVideoAU(uint64 offset, size_t size, size_t prepend_size,
-                           uint8 length_of_nalu_size, bool is_keyframe,
-                           TimeDelta timestamp, TimeDelta duration,
-                           ShellParser* parser)
+VideoAU::VideoAU(uint64 offset, size_t size, size_t prepend_size,
+                 uint8 length_of_nalu_size, bool is_keyframe,
+                 TimeDelta timestamp, TimeDelta duration,
+                 ProgressiveParser* parser)
     : offset_(offset),
       size_(size),
       prepend_size_(prepend_size),
@@ -212,7 +211,7 @@
   CHECK_NE(length_of_nalu_size_, 3);
 }
 
-bool ShellVideoAU::Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) {
+bool VideoAU::Read(DataSourceReader* reader, DecoderBuffer* buffer) {
   size_t au_left = size_;                 // bytes left in the AU
   uint64 au_offset = offset_;             // offset to read in the reader
   size_t buf_left = buffer->data_size();  // bytes left in the buffer
@@ -280,33 +279,34 @@
 
 }  // namespace
 
-// ==== ShellAU ================================================================
+// ==== AvcAccessUnit
+// ================================================================
 
-ShellAU::ShellAU() {}
+AvcAccessUnit::AvcAccessUnit() {}
 
-ShellAU::~ShellAU() {}
+AvcAccessUnit::~AvcAccessUnit() {}
 
 // static
-scoped_refptr<ShellAU> ShellAU::CreateEndOfStreamAU(DemuxerStream::Type type,
-                                                    TimeDelta timestamp) {
-  return new ShellEndOfStreamAU(type, timestamp);
+scoped_refptr<AvcAccessUnit> AvcAccessUnit::CreateEndOfStreamAU(
+    DemuxerStream::Type type, TimeDelta timestamp) {
+  return new EndOfStreamAU(type, timestamp);
 }
 
 // static
-scoped_refptr<ShellAU> ShellAU::CreateAudioAU(
+scoped_refptr<AvcAccessUnit> AvcAccessUnit::CreateAudioAU(
     uint64 offset, size_t size, size_t prepend_size, bool is_keyframe,
-    TimeDelta timestamp, TimeDelta duration, ShellParser* parser) {
-  return new ShellAudioAU(offset, size, prepend_size, is_keyframe, timestamp,
-                          duration, parser);
+    TimeDelta timestamp, TimeDelta duration, ProgressiveParser* parser) {
+  return new AudioAU(offset, size, prepend_size, is_keyframe, timestamp,
+                     duration, parser);
 }
 
 // static
-scoped_refptr<ShellAU> ShellAU::CreateVideoAU(
+scoped_refptr<AvcAccessUnit> AvcAccessUnit::CreateVideoAU(
     uint64 offset, size_t size, size_t prepend_size, uint8 length_of_nalu_size,
     bool is_keyframe, TimeDelta timestamp, TimeDelta duration,
-    ShellParser* parser) {
-  return new ShellVideoAU(offset, size, prepend_size, length_of_nalu_size,
-                          is_keyframe, timestamp, duration, parser);
+    ProgressiveParser* parser) {
+  return new VideoAU(offset, size, prepend_size, length_of_nalu_size,
+                     is_keyframe, timestamp, duration, parser);
 }
 
 }  // namespace media
diff --git a/src/cobalt/media/filters/shell_au.h b/src/cobalt/media/progressive/avc_access_unit.h
similarity index 63%
rename from src/cobalt/media/filters/shell_au.h
rename to src/cobalt/media/progressive/avc_access_unit.h
index d6254e4..7c283e9 100644
--- a/src/cobalt/media/filters/shell_au.h
+++ b/src/cobalt/media/progressive/avc_access_unit.h
@@ -12,44 +12,44 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_FILTERS_SHELL_AU_H_
-#define COBALT_MEDIA_FILTERS_SHELL_AU_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_ACCESS_UNIT_H_
+#define COBALT_MEDIA_PROGRESSIVE_ACCESS_UNIT_H_
 
 #include "base/memory/ref_counted.h"
 #include "cobalt/media/base/demuxer_stream.h"
-#include "cobalt/media/base/shell_data_source_reader.h"
+#include "cobalt/media/progressive/data_source_reader.h"
 
 namespace cobalt {
 namespace media {
 
-class ShellParser;
+class ProgressiveParser;
 
 static const int kAnnexBStartCodeSize = 4;
 static const uint8_t kAnnexBStartCode[] = {0, 0, 0, 1};
 
-// The basic unit of currency between ShellDemuxer and ShellParser, the ShellAU
-// defines all needed information for a given AccessUnit (Frame) of encoded
-// media data.
-class ShellAU : public base::RefCountedThreadSafe<ShellAU> {
+// The basic unit of currency between ProgressiveDemuxer and ProgressiveParser,
+// the AvcAccessUnit defines all needed information for a given AvcAccessUnit
+// (Frame) of encoded media data.
+class AvcAccessUnit : public base::RefCountedThreadSafe<AvcAccessUnit> {
  public:
   typedef base::TimeDelta TimeDelta;
   typedef DemuxerStream::Type Type;
 
-  static scoped_refptr<ShellAU> CreateEndOfStreamAU(Type type,
-                                                    TimeDelta timestamp);
-  static scoped_refptr<ShellAU> CreateAudioAU(
+  static scoped_refptr<AvcAccessUnit> CreateEndOfStreamAU(Type type,
+                                                          TimeDelta timestamp);
+  static scoped_refptr<AvcAccessUnit> CreateAudioAU(
       uint64 offset, size_t size, size_t prepend_size, bool is_keyframe,
-      TimeDelta timestamp, TimeDelta duration, ShellParser* parser);
-  static scoped_refptr<ShellAU> CreateVideoAU(
+      TimeDelta timestamp, TimeDelta duration, ProgressiveParser* parser);
+  static scoped_refptr<AvcAccessUnit> CreateVideoAU(
       uint64 offset, size_t size, size_t prepend_size,
       uint8 length_of_nalu_size, bool is_keyframe, TimeDelta timestamp,
-      TimeDelta duration, ShellParser* parser);
+      TimeDelta duration, ProgressiveParser* parser);
 
   virtual bool IsEndOfStream() const = 0;
   virtual bool IsValid() const = 0;
   // Read an AU from reader to buffer and also do all the necessary operations
   // like prepending head to make it ready to decode.
-  virtual bool Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) = 0;
+  virtual bool Read(DataSourceReader* reader, DecoderBuffer* buffer) = 0;
   virtual Type GetType() const = 0;
   virtual bool IsKeyframe() const = 0;
   virtual bool AddPrepend() const = 0;
@@ -63,13 +63,13 @@
   virtual void SetTimestamp(TimeDelta timestamp) = 0;
 
  protected:
-  friend class base::RefCountedThreadSafe<ShellAU>;
+  friend class base::RefCountedThreadSafe<AvcAccessUnit>;
 
-  ShellAU();
-  virtual ~ShellAU();
+  AvcAccessUnit();
+  virtual ~AvcAccessUnit();
 };
 
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_FILTERS_SHELL_AU_H_
+#endif  // COBALT_MEDIA_PROGRESSIVE_ACCESS_UNIT_H_
diff --git a/src/cobalt/media/filters/shell_avc_parser.cc b/src/cobalt/media/progressive/avc_parser.cc
similarity index 92%
rename from src/cobalt/media/filters/shell_avc_parser.cc
rename to src/cobalt/media/progressive/avc_parser.cc
index baca931..e08141a 100644
--- a/src/cobalt/media/filters/shell_avc_parser.cc
+++ b/src/cobalt/media/progressive/avc_parser.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/filters/shell_avc_parser.h"
+#include "cobalt/media/progressive/avc_parser.h"
 
 #include <limits>
 #include <vector>
@@ -23,9 +23,9 @@
 #include "cobalt/media/base/endian_util.h"
 #include "cobalt/media/base/media_util.h"
 #include "cobalt/media/base/video_types.h"
-#include "cobalt/media/filters/shell_au.h"
-#include "cobalt/media/filters/shell_rbsp_stream.h"
 #include "cobalt/media/formats/mp4/aac.h"
+#include "cobalt/media/progressive/avc_access_unit.h"
+#include "cobalt/media/progressive/rbsp_stream.h"
 #include "starboard/memory.h"
 
 namespace cobalt {
@@ -36,19 +36,19 @@
 // lower five bits of first byte in SPS should be 7
 static const uint8 kSPSNALType = 7;
 
-ShellAVCParser::ShellAVCParser(scoped_refptr<ShellDataSourceReader> reader,
-                               const scoped_refptr<MediaLog>& media_log)
-    : ShellParser(reader),
+AVCParser::AVCParser(scoped_refptr<DataSourceReader> reader,
+                     const scoped_refptr<MediaLog>& media_log)
+    : ProgressiveParser(reader),
       media_log_(media_log),
       nal_header_size_(0),
       video_prepend_size_(0) {
   DCHECK(media_log);
 }
 
-ShellAVCParser::~ShellAVCParser() {}
+AVCParser::~AVCParser() {}
 
-bool ShellAVCParser::Prepend(scoped_refptr<ShellAU> au,
-                             scoped_refptr<DecoderBuffer> buffer) {
+bool AVCParser::Prepend(scoped_refptr<AvcAccessUnit> au,
+                        scoped_refptr<DecoderBuffer> buffer) {
   // sanity-check inputs
   if (!au || !buffer) {
     NOTREACHED() << "bad input to Prepend()";
@@ -93,8 +93,7 @@
   return true;
 }
 
-bool ShellAVCParser::DownloadAndParseAVCConfigRecord(uint64 offset,
-                                                     uint32 size) {
+bool AVCParser::DownloadAndParseAVCConfigRecord(uint64 offset, uint32 size) {
   if (size == 0) {
     return false;
   }
@@ -109,8 +108,8 @@
 }
 
 // static
-bool ShellAVCParser::ParseSPS(const uint8* sps, size_t sps_size,
-                              ShellSPSRecord* record_out) {
+bool AVCParser::ParseSPS(const uint8* sps, size_t sps_size,
+                         SPSRecord* record_out) {
   DCHECK(sps) << "no sps provided";
   DCHECK(record_out) << "no output structure provided";
   // first byte is NAL type id, check that it is SPS
@@ -119,7 +118,7 @@
     return false;
   }
   // convert SPS NALU to RBSP stream
-  ShellRBSPStream sps_rbsp(sps + 1, sps_size - 1);
+  RBSPStream sps_rbsp(sps + 1, sps_size - 1);
   uint8 profile_idc = 0;
   if (!sps_rbsp.ReadByte(&profile_idc)) {
     DLOG(ERROR) << "failure reading profile_idc from sps RBSP";
@@ -294,7 +293,7 @@
   return true;
 }
 
-bool ShellAVCParser::ParseAVCConfigRecord(uint8* buffer, uint32 size) {
+bool AVCParser::ParseAVCConfigRecord(uint8* buffer, uint32 size) {
   if (size < kAVCConfigMinSize) {
     DLOG(ERROR) << base::StringPrintf("AVC config record bad size: %d", size);
     return false;
@@ -392,7 +391,7 @@
     }
   }
   // now we parse the valid SPS we extracted from byte stream earlier.
-  ShellSPSRecord sps_record;
+  SPSRecord sps_record;
   if (!ParseSPS(buffer + usable_sps_offset, usable_sps_size, &sps_record)) {
     DLOG(WARNING) << "error parsing SPS";
     return false;
@@ -409,8 +408,8 @@
                             buffer + usable_pps_offset, usable_pps_size);
 }
 
-bool ShellAVCParser::BuildAnnexBPrepend(uint8* sps, uint32 sps_size, uint8* pps,
-                                        uint32 pps_size) {
+bool AVCParser::BuildAnnexBPrepend(uint8* sps, uint32 sps_size, uint8* pps,
+                                   uint32 pps_size) {
   // We will need to attach the sps and pps (if provided) to each keyframe
   // video packet, with the AnnexB start code in front of each. Start with
   // sps size and start code
@@ -445,7 +444,7 @@
   return true;
 }
 
-void ShellAVCParser::ParseAudioSpecificConfig(uint8 b0, uint8 b1) {
+void AVCParser::ParseAudioSpecificConfig(uint8 b0, uint8 b1) {
   media::mp4::AAC aac;
   std::vector<uint8> aac_config(2);
 
@@ -472,8 +471,8 @@
       Unencrypted(), base::TimeDelta(), 0);
 }
 
-size_t ShellAVCParser::CalculatePrependSize(DemuxerStream::Type type,
-                                            bool is_keyframe) {
+size_t AVCParser::CalculatePrependSize(DemuxerStream::Type type,
+                                       bool is_keyframe) {
   size_t prepend_size = 0;
   if (type == DemuxerStream::VIDEO) {
     bool needs_prepend = is_keyframe;
diff --git a/src/cobalt/media/filters/shell_avc_parser.h b/src/cobalt/media/progressive/avc_parser.h
similarity index 77%
rename from src/cobalt/media/filters/shell_avc_parser.h
rename to src/cobalt/media/progressive/avc_parser.h
index 758a7a2..e383bdf 100644
--- a/src/cobalt/media/filters/shell_avc_parser.h
+++ b/src/cobalt/media/progressive/avc_parser.h
@@ -12,13 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_FILTERS_SHELL_AVC_PARSER_H_
-#define COBALT_MEDIA_FILTERS_SHELL_AVC_PARSER_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_AVC_PARSER_H_
+#define COBALT_MEDIA_PROGRESSIVE_AVC_PARSER_H_
 
 #include <vector>
 
 #include "cobalt/media/base/media_log.h"
-#include "cobalt/media/filters/shell_parser.h"
+#include "cobalt/media/progressive/progressive_parser.h"
 
 namespace cobalt {
 namespace media {
@@ -29,27 +29,27 @@
 static const int kAnnexBPrependMaxSize = 1024;
 
 // while not an actual parser, provides shared functionality to both the
-// mp4 and flv parsers which derive from it. Implements part of ShellParser
-// while leaving the rest for its children.
-class ShellAVCParser : public ShellParser {
+// mp4 and flv parsers which derive from it. Implements part of
+// ProgressiveParser while leaving the rest for its children.
+class AVCParser : public ProgressiveParser {
  public:
-  explicit ShellAVCParser(scoped_refptr<ShellDataSourceReader> reader,
-                          const scoped_refptr<MediaLog>& media_log);
-  virtual ~ShellAVCParser();
+  explicit AVCParser(scoped_refptr<DataSourceReader> reader,
+                     const scoped_refptr<MediaLog>& media_log);
+  virtual ~AVCParser();
 
-  struct ShellSPSRecord {
+  struct SPSRecord {
     math::Size coded_size;
     math::Rect visible_rect;
     math::Size natural_size;
     uint32 num_ref_frames;
   };
   static bool ParseSPS(const uint8* sps, size_t sps_size,
-                       ShellSPSRecord* record_out);
+                       SPSRecord* record_out);
 
   // GetNextAU we must pass on to FLV or MP4 children.
-  virtual scoped_refptr<ShellAU> GetNextAU(DemuxerStream::Type type) = 0;
+  virtual scoped_refptr<AvcAccessUnit> GetNextAU(DemuxerStream::Type type) = 0;
   // Prepends are common to all AVC/AAC containers so we can do this one here.
-  bool Prepend(scoped_refptr<ShellAU> au,
+  bool Prepend(scoped_refptr<AvcAccessUnit> au,
                scoped_refptr<DecoderBuffer> buffer) override;
 
  protected:
@@ -77,4 +77,4 @@
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_FILTERS_SHELL_AVC_PARSER_H_
+#endif  // COBALT_MEDIA_PROGRESSIVE_AVC_PARSER_H_
diff --git a/src/cobalt/media/base/shell_data_source_reader.cc b/src/cobalt/media/progressive/data_source_reader.cc
similarity index 81%
rename from src/cobalt/media/base/shell_data_source_reader.cc
rename to src/cobalt/media/progressive/data_source_reader.cc
index 2c7c38f..fd4ff71 100644
--- a/src/cobalt/media/base/shell_data_source_reader.cc
+++ b/src/cobalt/media/progressive/data_source_reader.cc
@@ -12,16 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/base/shell_data_source_reader.h"
+#include "cobalt/media/progressive/data_source_reader.h"
 
 #include "starboard/types.h"
 
 namespace cobalt {
 namespace media {
 
-const int ShellDataSourceReader::kReadError = DataSource::kReadError;
+const int DataSourceReader::kReadError = DataSource::kReadError;
 
-ShellDataSourceReader::ShellDataSourceReader()
+DataSourceReader::DataSourceReader()
     : data_source_(NULL),
       blocking_read_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
                            base::WaitableEvent::InitialState::NOT_SIGNALED),
@@ -29,15 +29,15 @@
       read_has_failed_(false),
       last_bytes_read_(0) {}
 
-ShellDataSourceReader::~ShellDataSourceReader() {}
+DataSourceReader::~DataSourceReader() {}
 
-void ShellDataSourceReader::SetDataSource(DataSource* data_source) {
+void DataSourceReader::SetDataSource(DataSource* data_source) {
   DCHECK(data_source);
   data_source_ = data_source;
 }
 
 // currently only single-threaded reads supported
-int ShellDataSourceReader::BlockingRead(int64 position, int size, uint8* data) {
+int DataSourceReader::BlockingRead(int64 position, int size, uint8* data) {
   // read failures are unrecoverable, all subsequent reads will also fail
   if (read_has_failed_) {
     return kReadError;
@@ -57,7 +57,7 @@
       }
       data_source_->Read(
           position, size, data,
-          base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+          base::Bind(&DataSourceReader::BlockingReadCompleted, this));
     }
 
     // wait for callback on read completion
@@ -91,7 +91,7 @@
   return total_bytes_read;
 }
 
-void ShellDataSourceReader::Stop() {
+void DataSourceReader::Stop() {
   if (data_source_) {
     data_source_->Stop();
 
@@ -100,13 +100,13 @@
   }
 }
 
-void ShellDataSourceReader::BlockingReadCompleted(int bytes_read) {
+void DataSourceReader::BlockingReadCompleted(int bytes_read) {
   last_bytes_read_ = bytes_read;
   // wake up blocked thread
   blocking_read_event_.Signal();
 }
 
-int64 ShellDataSourceReader::FileSize() {
+int64 DataSourceReader::FileSize() {
   if (file_size_ == -1) {
     base::AutoLock auto_lock(lock_);
     if (data_source_ && !data_source_->GetSize(&file_size_)) {
diff --git a/src/cobalt/media/base/shell_data_source_reader.h b/src/cobalt/media/progressive/data_source_reader.h
similarity index 85%
rename from src/cobalt/media/base/shell_data_source_reader.h
rename to src/cobalt/media/progressive/data_source_reader.h
index 2049a9d..c773083 100644
--- a/src/cobalt/media/base/shell_data_source_reader.h
+++ b/src/cobalt/media/progressive/data_source_reader.h
@@ -12,9 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_BASE_SHELL_DATA_SOURCE_READER_H_
-#define COBALT_MEDIA_BASE_SHELL_DATA_SOURCE_READER_H_
-
+#ifndef COBALT_MEDIA_PROGRESSIVE_DATA_SOURCE_READER_H_
+#define COBALT_MEDIA_PROGRESSIVE_DATA_SOURCE_READER_H_
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
@@ -32,12 +31,11 @@
 // object is also the sole owner of a pointer to DataSource.  If we want to add
 // asynchronous reading to this object it will need its own thread and a
 // callback queue.
-class ShellDataSourceReader
-    : public base::RefCountedThreadSafe<ShellDataSourceReader> {
+class DataSourceReader : public base::RefCountedThreadSafe<DataSourceReader> {
  public:
   static const int kReadError;
 
-  ShellDataSourceReader();
+  DataSourceReader();
   virtual void SetDataSource(DataSource* data_source);
 
   // Block the calling thread's message loop until read is complete.
@@ -53,8 +51,8 @@
   virtual void Stop();
 
  protected:
-  friend class base::RefCountedThreadSafe<ShellDataSourceReader>;
-  virtual ~ShellDataSourceReader();
+  friend class base::RefCountedThreadSafe<DataSourceReader>;
+  virtual ~DataSourceReader();
   // blocking read callback
   virtual void BlockingReadCompleted(int bytes_read);
 
@@ -69,4 +67,4 @@
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_BASE_SHELL_DATA_SOURCE_READER_H_
+#endif  // COBALT_MEDIA_PROGRESSIVE_DATA_SOURCE_READER_H_
diff --git a/src/cobalt/media/sandbox/shell_demuxer_fuzzer.cc b/src/cobalt/media/progressive/demuxer_fuzzer.cc
similarity index 78%
rename from src/cobalt/media/sandbox/shell_demuxer_fuzzer.cc
rename to src/cobalt/media/progressive/demuxer_fuzzer.cc
index 74d1e7e..ebd2766 100644
--- a/src/cobalt/media/sandbox/shell_demuxer_fuzzer.cc
+++ b/src/cobalt/media/progressive/demuxer_fuzzer.cc
@@ -25,7 +25,7 @@
 #include "cobalt/media/sandbox/media_sandbox.h"
 #include "media/base/bind_to_loop.h"
 #include "media/base/pipeline_status.h"
-#include "media/filters/shell_demuxer.h"
+#include "media/progressive/progressive_demuxer.h"
 
 namespace cobalt {
 namespace media {
@@ -39,19 +39,20 @@
 using ::media::DemuxerHost;
 using ::media::DemuxerStream;
 using ::media::PipelineStatus;
-using ::media::ShellDemuxer;
+using ::media::ProgressiveDemuxer;
 
-class ShellDemuxerFuzzer : DemuxerHost {
+class DemuxerFuzzer : DemuxerHost {
  public:
-  explicit ShellDemuxerFuzzer(const std::vector<uint8>& content)
+  explicit DemuxerFuzzer(const std::vector<uint8>& content)
       : error_occurred_(false), eos_count_(0), stopped_(false) {
-    demuxer_ = new ShellDemuxer(base::MessageLoop::current()->task_runner(),
-                                new InMemoryDataSource(content));
+    demuxer_ =
+        new ProgressiveDemuxer(base::MessageLoop::current()->task_runner(),
+                               new InMemoryDataSource(content));
   }
 
   void Fuzz() {
     demuxer_->Initialize(
-        this, Bind(&ShellDemuxerFuzzer::InitializeCB, base::Unretained(this)));
+        this, Bind(&DemuxerFuzzer::InitializeCB, base::Unretained(this)));
 
     // Check if there is any error or if both of the audio and video streams
     // have reached eos.
@@ -59,7 +60,7 @@
       base::RunLoop().RunUntilIdle();
     }
 
-    demuxer_->Stop(Bind(&ShellDemuxerFuzzer::StopCB, base::Unretained(this)));
+    demuxer_->Stop(Bind(&DemuxerFuzzer::StopCB, base::Unretained(this)));
 
     while (!stopped_) {
       base::RunLoop().RunUntilIdle();
@@ -101,10 +102,10 @@
       error_occurred_ = true;
       return;
     }
-    audio_stream->Read(BindToCurrentLoop(Bind(
-        &ShellDemuxerFuzzer::ReadCB, base::Unretained(this), audio_stream)));
-    video_stream->Read(BindToCurrentLoop(Bind(
-        &ShellDemuxerFuzzer::ReadCB, base::Unretained(this), video_stream)));
+    audio_stream->Read(BindToCurrentLoop(
+        Bind(&DemuxerFuzzer::ReadCB, base::Unretained(this), audio_stream)));
+    video_stream->Read(BindToCurrentLoop(
+        Bind(&DemuxerFuzzer::ReadCB, base::Unretained(this), video_stream)));
   }
 
   void StopCB() { stopped_ = true; }
@@ -122,18 +123,18 @@
     }
     DCHECK(!error_occurred_);
     stream->Read(BindToCurrentLoop(
-        Bind(&ShellDemuxerFuzzer::ReadCB, base::Unretained(this), stream)));
+        Bind(&DemuxerFuzzer::ReadCB, base::Unretained(this), stream)));
   }
 
   bool error_occurred_;
   int eos_count_;
   bool stopped_;
-  scoped_refptr<ShellDemuxer> demuxer_;
+  scoped_refptr<ProgressiveDemuxer> demuxer_;
 };
 
-class ShellDemuxerFuzzerApp : public FuzzerApp {
+class DemuxerFuzzerApp : public FuzzerApp {
  public:
-  explicit ShellDemuxerFuzzerApp(MediaSandbox* media_sandbox)
+  explicit DemuxerFuzzerApp(MediaSandbox* media_sandbox)
       : media_sandbox_(media_sandbox) {}
 
   std::vector<uint8> ParseFileContent(
@@ -149,7 +150,7 @@
 
   void Fuzz(const std::string& file_name,
             const std::vector<uint8>& fuzzing_content) override {
-    ShellDemuxerFuzzer demuxer_fuzzer(fuzzing_content);
+    DemuxerFuzzer demuxer_fuzzer(fuzzing_content);
     demuxer_fuzzer.Fuzz();
   }
 
@@ -159,9 +160,8 @@
 
 int SandboxMain(int argc, char** argv) {
   MediaSandbox media_sandbox(
-      argc, argv,
-      base::FilePath(FILE_PATH_LITERAL("shell_demuxer_fuzzer.json")));
-  ShellDemuxerFuzzerApp fuzzer_app(&media_sandbox);
+      argc, argv, base::FilePath(FILE_PATH_LITERAL("demuxer_fuzzer.json")));
+  DemuxerFuzzerApp fuzzer_app(&media_sandbox);
 
   if (fuzzer_app.Init(argc, argv)) {
     fuzzer_app.RunFuzzingLoop();
diff --git a/src/cobalt/media/base/mock_shell_data_source_reader.h b/src/cobalt/media/progressive/mock_data_source_reader.h
similarity index 71%
rename from src/cobalt/media/base/mock_shell_data_source_reader.h
rename to src/cobalt/media/progressive/mock_data_source_reader.h
index 08935b2..cf3ddab 100644
--- a/src/cobalt/media/base/mock_shell_data_source_reader.h
+++ b/src/cobalt/media/progressive/mock_data_source_reader.h
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
-#ifndef COBALT_MEDIA_BASE_MOCK_SHELL_DATA_SOURCE_READER_H_
-#define COBALT_MEDIA_BASE_MOCK_SHELL_DATA_SOURCE_READER_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_MOCK_DATA_SOURCE_READER_H_
+#define COBALT_MEDIA_PROGRESSIVE_MOCK_DATA_SOURCE_READER_H_
 
-#include "media/base/shell_data_source_reader.h"
+#include "media/progressive/data_source_reader.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace cobalt {
 namespace media {
 
-class MockShellDataSourceReader : public ShellDataSourceReader {
+class MockDataSourceReader : public DataSourceReader {
  public:
-  MockShellDataSourceReader() {}
+  MockDataSourceReader() {}
 
-  // ShellDataSourceReader implementation
+  // DataSourceReader implementation
   MOCK_METHOD1(SetDataSource, void(DataSource*));
   MOCK_METHOD3(BlockingRead, int(int64, int, uint8*));
   MOCK_METHOD0(FileSize, int64());
@@ -37,4 +37,4 @@
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_BASE_MOCK_SHELL_DATA_SOURCE_READER_H_
+#endif  // COBALT_MEDIA_PROGRESSIVE_MOCK_DATA_SOURCE_READER_H_
diff --git a/src/cobalt/media/filters/shell_mp4_map.cc b/src/cobalt/media/progressive/mp4_map.cc
similarity index 92%
rename from src/cobalt/media/filters/shell_mp4_map.cc
rename to src/cobalt/media/progressive/mp4_map.cc
index 3a5189e..3a65467 100644
--- a/src/cobalt/media/filters/shell_mp4_map.cc
+++ b/src/cobalt/media/progressive/mp4_map.cc
@@ -12,23 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/filters/shell_mp4_map.h"
+#include "cobalt/media/progressive/mp4_map.h"
 
 #include <algorithm>
 
 #include "base/strings/stringprintf.h"
 #include "cobalt/media/base/endian_util.h"
-#include "cobalt/media/filters/shell_mp4_parser.h"
+#include "cobalt/media/progressive/mp4_parser.h"
 
 namespace cobalt {
 namespace media {
 
 // ==== TableCache =============================================================
 
-ShellMP4Map::TableCache::TableCache(uint64 table_offset, uint32 entry_count,
-                                    uint32 entry_size,
-                                    uint32 cache_size_entries,
-                                    scoped_refptr<ShellDataSourceReader> reader)
+MP4Map::TableCache::TableCache(uint64 table_offset, uint32 entry_count,
+                               uint32 entry_size, uint32 cache_size_entries,
+                               scoped_refptr<DataSourceReader> reader)
     : entry_size_(entry_size),
       entry_count_(entry_count),
       cache_size_entries_(cache_size_entries),
@@ -37,7 +36,7 @@
       cache_first_entry_number_(-1),
       cache_entry_count_(0) {}
 
-uint8* ShellMP4Map::TableCache::GetBytesAtEntry(uint32 entry_number) {
+uint8* MP4Map::TableCache::GetBytesAtEntry(uint32 entry_number) {
   // don't fetch the unfetchable
   if (entry_number >= entry_count_) {
     return NULL;
@@ -77,7 +76,7 @@
   return &cache_[0] + (cache_offset * entry_size_);
 }
 
-bool ShellMP4Map::TableCache::ReadU32Entry(uint32 entry_number, uint32* entry) {
+bool MP4Map::TableCache::ReadU32Entry(uint32 entry_number, uint32* entry) {
   if (uint8* data = GetBytesAtEntry(entry_number)) {
     *entry = endian_util::load_uint32_big_endian(data);
     return true;
@@ -86,8 +85,8 @@
   return false;
 }
 
-bool ShellMP4Map::TableCache::ReadU32PairEntry(uint32 entry_number,
-                                               uint32* first, uint32* second) {
+bool MP4Map::TableCache::ReadU32PairEntry(uint32 entry_number, uint32* first,
+                                          uint32* second) {
   if (uint8* data = GetBytesAtEntry(entry_number)) {
     if (first) *first = endian_util::load_uint32_big_endian(data);
     if (second) *second = endian_util::load_uint32_big_endian(data + 4);
@@ -97,8 +96,8 @@
   return false;
 }
 
-bool ShellMP4Map::TableCache::ReadU32EntryIntoU64(uint32 entry_number,
-                                                  uint64* entry) {
+bool MP4Map::TableCache::ReadU32EntryIntoU64(uint32 entry_number,
+                                             uint64* entry) {
   if (uint8* data = GetBytesAtEntry(entry_number)) {
     *entry = endian_util::load_uint32_big_endian(data);
     return true;
@@ -107,7 +106,7 @@
   return false;
 }
 
-bool ShellMP4Map::TableCache::ReadU64Entry(uint32 entry_number, uint64* entry) {
+bool MP4Map::TableCache::ReadU64Entry(uint32 entry_number, uint64* entry) {
   if (uint8* data = GetBytesAtEntry(entry_number)) {
     *entry = endian_util::load_uint64_big_endian(data);
     return true;
@@ -116,7 +115,7 @@
   return false;
 }
 
-// ==== ShellMP4Map ============================================================
+// ==== MP4Map ============================================================
 
 // atom | name                  | size | description, (*) means optional table
 // -----+-----------------------+------+----------------------------------------
@@ -128,7 +127,7 @@
 // stts | time-to-sample        | 8    | run-length sample number to duration
 // stsz | sample size           | 4    | per-sample list of sample sizes
 
-ShellMP4Map::ShellMP4Map(scoped_refptr<ShellDataSourceReader> reader)
+MP4Map::MP4Map(scoped_refptr<DataSourceReader> reader)
     : reader_(reader),
       current_chunk_sample_(0),
       next_chunk_sample_(0),
@@ -155,14 +154,14 @@
       stts_table_index_(0),
       stsz_default_size_(0) {}
 
-bool ShellMP4Map::IsComplete() {
+bool MP4Map::IsComplete() {
   // all required table pointers must be valid for map to function
   return (co64_ || stco_) && stsc_ && stts_ && (stsz_ || stsz_default_size_);
 }
 
 // The sample size is a lookup in the stsz table, which is indexed per sample
 // number.
-bool ShellMP4Map::GetSize(uint32 sample_number, uint32* size_out) {
+bool MP4Map::GetSize(uint32 sample_number, uint32* size_out) {
   DCHECK(size_out);
   DCHECK(stsz_ || stsz_default_size_);
 
@@ -184,7 +183,7 @@
 // then use the stsz to sum samples to the byte offset with that chunk. The sum
 // of the chunk offset and the byte offset within the chunk is the offset of
 // the sample.
-bool ShellMP4Map::GetOffset(uint32 sample_number, uint64* offset_out) {
+bool MP4Map::GetOffset(uint32 sample_number, uint64* offset_out) {
   DCHECK(offset_out);
   DCHECK(stsc_);
   DCHECK(stco_ || co64_);
@@ -255,7 +254,7 @@
 // durations to find the dts of that sample number. We then integrate sample
 // numbers through the ctts to find the composition time offset, which we add to
 // the dts to return the pts.
-bool ShellMP4Map::GetTimestamp(uint32 sample_number, uint64* timestamp_out) {
+bool MP4Map::GetTimestamp(uint32 sample_number, uint64* timestamp_out) {
   DCHECK(timestamp_out);
   if (sample_number > highest_valid_sample_number_) {
     return false;
@@ -280,7 +279,7 @@
 }
 
 // Sum through the stts to find the duration of the given sample_number.
-bool ShellMP4Map::GetDuration(uint32 sample_number, uint32* duration_out) {
+bool MP4Map::GetDuration(uint32 sample_number, uint32* duration_out) {
   DCHECK(duration_out);
   if (sample_number > highest_valid_sample_number_) {
     return false;
@@ -295,7 +294,7 @@
   return true;
 }
 
-bool ShellMP4Map::GetIsKeyframe(uint32 sample_number, bool* is_keyframe_out) {
+bool MP4Map::GetIsKeyframe(uint32 sample_number, bool* is_keyframe_out) {
   DCHECK(is_keyframe_out);
   if (sample_number > highest_valid_sample_number_) {
     return false;
@@ -336,14 +335,14 @@
   return true;
 }
 
-bool ShellMP4Map::IsEOS(uint32 sample_number) {
+bool MP4Map::IsEOS(uint32 sample_number) {
   return (sample_number > highest_valid_sample_number_);
 }
 
 // First look up the sample number for the provided timestamp by integrating
 // timestamps through the stts. Then do a binary search on the stss to find the
 // keyframe nearest that sample number.
-bool ShellMP4Map::GetKeyframe(uint64 timestamp, uint32* sample_out) {
+bool MP4Map::GetKeyframe(uint64 timestamp, uint32* sample_out) {
   DCHECK(sample_out);
   // Advance stts to the provided timestamp range
   if (!stts_AdvanceToTime(timestamp)) {
@@ -375,8 +374,8 @@
 
 // Set up map state and load first part of table, or entire table if it is small
 // enough, for each of the supporated atoms.
-bool ShellMP4Map::SetAtom(uint32 four_cc, uint64 offset, uint64 size,
-                          uint32 cache_size_entries, const uint8* atom) {
+bool MP4Map::SetAtom(uint32 four_cc, uint64 offset, uint64 size,
+                     uint32 cache_size_entries, const uint8* atom) {
   // All map atoms are variable-length tables starting with 4 bytes of
   // version/flag info followed by a uint32 indicating the number of items in
   // table. The stsz atom bucks tradition by putting an optional default value
@@ -463,7 +462,7 @@
   return atom_init;
 }
 
-bool ShellMP4Map::co64_Init() {
+bool MP4Map::co64_Init() {
   DCHECK(co64_);
   // load offset of first chunk into current_chunk_offset_
   if (co64_->GetEntryCount() > 0) {
@@ -482,7 +481,7 @@
 // uint32 sample count
 // uint32 composition offset in ticks
 //
-bool ShellMP4Map::ctts_Init() {
+bool MP4Map::ctts_Init() {
   DCHECK(ctts_);
   // get cache segment vector to reserve table entries in advance
   int cache_segments =
@@ -506,7 +505,7 @@
 // To find the composition offset of a given sample number we must integrate
 // through the ctts to find the range of samples containing sample_number. Note
 // that the ctts is an optional table.
-bool ShellMP4Map::ctts_AdvanceToSample(uint32 sample_number) {
+bool MP4Map::ctts_AdvanceToSample(uint32 sample_number) {
   // ctts table is optional, so treat not having one as non-fatal
   if (!ctts_) {
     return true;
@@ -573,8 +572,8 @@
   return true;
 }
 
-bool ShellMP4Map::ctts_SlipCacheToSample(uint32 sample_number,
-                                         int starting_cache_index) {
+bool MP4Map::ctts_SlipCacheToSample(uint32 sample_number,
+                                    int starting_cache_index) {
   DCHECK_LT(starting_cache_index, ctts_samples_.size());
   int cache_index = starting_cache_index;
   for (; cache_index + 1 < ctts_samples_.size(); cache_index++) {
@@ -593,7 +592,7 @@
   return true;
 }
 
-bool ShellMP4Map::stco_Init() {
+bool MP4Map::stco_Init() {
   DCHECK(stco_);
   // load offset of first chunk into current_chunk_offset_
   if (stco_->GetEntryCount() > 0) {
@@ -610,7 +609,7 @@
 // uint32 first chunk number with this sample count
 // uint32 samples-per-chunk
 // uint32 sample description id (unused)
-bool ShellMP4Map::stsc_Init() {
+bool MP4Map::stsc_Init() {
   DCHECK(stsc_);
   // set up vector to correct final size
   int cache_segments =
@@ -660,7 +659,7 @@
 // to be consumed incrementally and with minimal memory consumption we calculate
 // this integration step only when needed, and save results for each cached
 // piece of the table, to avoid having to recalculate needed data.
-bool ShellMP4Map::stsc_AdvanceToSample(uint32 sample_number) {
+bool MP4Map::stsc_AdvanceToSample(uint32 sample_number) {
   DCHECK(stsc_);
   // sample_number could be before first chunk, meaning that we are seeking
   // backwards and have left the current chunk. Find the closest part of the
@@ -736,8 +735,8 @@
   return true;
 }
 
-bool ShellMP4Map::stsc_SlipCacheToSample(uint32 sample_number,
-                                         int starting_cache_index) {
+bool MP4Map::stsc_SlipCacheToSample(uint32 sample_number,
+                                    int starting_cache_index) {
   DCHECK_LT(starting_cache_index, stsc_sample_sums_.size());
   // look through old sample sums for the first entry that exceeds sample
   // sample_number, we want the entry right before that
@@ -776,7 +775,7 @@
 }
 
 // stss is a list of sample numbers that are keyframes.
-bool ShellMP4Map::stss_Init() {
+bool MP4Map::stss_Init() {
   int cache_segments =
       (stss_->GetEntryCount() / stss_->GetCacheSizeEntries()) + 1;
   stss_keyframes_.reserve(cache_segments);
@@ -800,7 +799,7 @@
 }
 
 // advance by one table entry through stss, updating cache if necessary
-bool ShellMP4Map::stss_AdvanceStep() {
+bool MP4Map::stss_AdvanceStep() {
   DCHECK(stss_);
   stss_last_keyframe_ = stss_next_keyframe_;
   stss_table_index_++;
@@ -823,7 +822,7 @@
   return true;
 }
 
-bool ShellMP4Map::stss_FindNearestKeyframe(uint32 sample_number) {
+bool MP4Map::stss_FindNearestKeyframe(uint32 sample_number) {
   DCHECK(stss_);
   // it is assumed that there's at least one cache entry created by
   // stss_Init();
@@ -955,7 +954,7 @@
 // The stts table has the following per-entry layout:
 // uint32 sample count - number of sequential samples with this duration
 // uint32 sample duration - duration in ticks of this sample range
-bool ShellMP4Map::stts_Init() {
+bool MP4Map::stts_Init() {
   int cache_segments =
       (stts_->GetEntryCount() / stts_->GetCacheSizeEntries()) + 1;
   stts_samples_.reserve(cache_segments);
@@ -982,7 +981,7 @@
   return true;
 }
 
-bool ShellMP4Map::stts_AdvanceToSample(uint32 sample_number) {
+bool MP4Map::stts_AdvanceToSample(uint32 sample_number) {
   DCHECK(stts_);
   // sample_number could be before our current sample range, in which case
   // we skip to the nearest table entry before sample_number and integrate
@@ -1015,8 +1014,8 @@
 // Move our integration steps to a previously saved entry in the cache tables.
 // Searches linearly through the vector of old cached values, so can accept a
 // starting index to do the search from.
-bool ShellMP4Map::stts_SlipCacheToSample(uint32 sample_number,
-                                         int starting_cache_index) {
+bool MP4Map::stts_SlipCacheToSample(uint32 sample_number,
+                                    int starting_cache_index) {
   DCHECK_LT(starting_cache_index, stts_samples_.size());
   int cache_index = starting_cache_index;
   for (; cache_index + 1 < stts_samples_.size(); cache_index++) {
@@ -1039,7 +1038,7 @@
   return true;
 }
 
-bool ShellMP4Map::stts_AdvanceToTime(uint64 timestamp) {
+bool MP4Map::stts_AdvanceToTime(uint64 timestamp) {
   DCHECK(stts_);
 
   if (timestamp < stts_first_sample_time_) {
@@ -1067,7 +1066,7 @@
   return true;
 }
 
-bool ShellMP4Map::stts_IntegrateStep() {
+bool MP4Map::stts_IntegrateStep() {
   // advance time to next sample range
   uint32 range_size = stts_next_first_sample_ - stts_first_sample_;
   stts_first_sample_time_ += (range_size * stts_sample_duration_);
@@ -1111,8 +1110,7 @@
   return true;
 }
 
-bool ShellMP4Map::stts_SlipCacheToTime(uint64 timestamp,
-                                       int starting_cache_index) {
+bool MP4Map::stts_SlipCacheToTime(uint64 timestamp, int starting_cache_index) {
   DCHECK_LT(starting_cache_index, stts_timestamps_.size());
   int cache_index = starting_cache_index;
   for (; cache_index + 1 < stts_timestamps_.size(); cache_index++) {
@@ -1135,7 +1133,7 @@
   return true;
 }
 
-bool ShellMP4Map::stsz_Init() { return stsz_->GetBytesAtEntry(0) != NULL; }
+bool MP4Map::stsz_Init() { return stsz_->GetBytesAtEntry(0) != NULL; }
 
 }  // namespace media
 }  // namespace cobalt
diff --git a/src/cobalt/media/filters/shell_mp4_map.h b/src/cobalt/media/progressive/mp4_map.h
similarity index 93%
rename from src/cobalt/media/filters/shell_mp4_map.h
rename to src/cobalt/media/progressive/mp4_map.h
index 88e1319..93bc08f 100644
--- a/src/cobalt/media/filters/shell_mp4_map.h
+++ b/src/cobalt/media/progressive/mp4_map.h
@@ -12,14 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_FILTERS_SHELL_MP4_MAP_H_
-#define COBALT_MEDIA_FILTERS_SHELL_MP4_MAP_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_MP4_MAP_H_
+#define COBALT_MEDIA_PROGRESSIVE_MP4_MAP_H_
 
 #include <vector>
 
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
-#include "cobalt/media/base/shell_data_source_reader.h"
+#include "cobalt/media/progressive/data_source_reader.h"
 
 namespace cobalt {
 namespace media {
@@ -37,9 +37,9 @@
 // them to provide byte offsets, sizes, and timestamps of a mp4 atom. The
 // caching design benefits from, but does not require, sequential access
 // in sample numbers.
-class ShellMP4Map : public base::RefCountedThreadSafe<ShellMP4Map> {
+class MP4Map : public base::RefCountedThreadSafe<MP4Map> {
  public:
-  explicit ShellMP4Map(scoped_refptr<ShellDataSourceReader> reader);
+  explicit MP4Map(scoped_refptr<DataSourceReader> reader);
 
   bool IsComplete();
 
@@ -117,7 +117,7 @@
                uint32 entry_count,   // number of entries in table
                uint32 entry_size,    // size in bytes of each entry in table
                uint32 cache_size_entries,  // number of entries to cache in mem
-               scoped_refptr<ShellDataSourceReader> reader);  // reader to use
+               scoped_refptr<DataSourceReader> reader);  // reader to use
 
     // The following Read* functions all read values in big endian.
     bool ReadU32Entry(uint32 entry_number, uint32* entry);
@@ -138,7 +138,7 @@
     uint32 entry_count_;         // size of table in entries
     uint32 cache_size_entries_;  // max number of entries to fit in memory
     uint64 table_offset_;        // offset of table in stream
-    scoped_refptr<ShellDataSourceReader> reader_;  // means to read more table
+    scoped_refptr<DataSourceReader> reader_;  // means to read more table
 
     // current cache state
     std::vector<uint8> cache_;         // the cached part of the table
@@ -146,7 +146,7 @@
     uint32 cache_entry_count_;         // number of valid entries in cache
   };
 
-  scoped_refptr<ShellDataSourceReader> reader_;
+  scoped_refptr<DataSourceReader> reader_;
 
   // current integration state for GetOffset(), we save the sum of sample sizes
   // within the current chunk.
@@ -211,4 +211,4 @@
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_FILTERS_SHELL_MP4_MAP_H_
+#endif  // COBALT_MEDIA_PROGRESSIVE_MP4_MAP_H_
diff --git a/src/cobalt/media/filters/shell_mp4_map_unittest.cc b/src/cobalt/media/progressive/mp4_map_unittest.cc
similarity index 95%
rename from src/cobalt/media/filters/shell_mp4_map_unittest.cc
rename to src/cobalt/media/progressive/mp4_map_unittest.cc
index 1fa0a73..347b7bb 100644
--- a/src/cobalt/media/filters/shell_mp4_map_unittest.cc
+++ b/src/cobalt/media/progressive/mp4_map_unittest.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/filters/shell_mp4_map.h"
+#include "cobalt/media/progressive/mp4_map.h"
 
 #include <stdlib.h>  // for rand and srand
 
@@ -23,8 +23,8 @@
 #include <vector>
 
 #include "cobalt/media/base/endian_util.h"
-#include "cobalt/media/base/mock_shell_data_source_reader.h"
-#include "cobalt/media/filters/shell_mp4_parser.h"
+#include "cobalt/media/progressive/mock_data_source_reader.h"
+#include "cobalt/media/progressive/mp4_parser.h"
 #include "starboard/memory.h"
 #include "starboard/types.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -48,8 +48,8 @@
 using cobalt::media::kEntrySize_stss;
 using cobalt::media::kEntrySize_stsz;
 using cobalt::media::kEntrySize_stts;
-using cobalt::media::MockShellDataSourceReader;
-using cobalt::media::ShellMP4Map;
+using cobalt::media::MockDataSourceReader;
+using cobalt::media::MP4Map;
 
 using ::testing::_;
 using ::testing::AllOf;
@@ -403,16 +403,16 @@
   }
 };
 
-class ShellMP4MapTest : public testing::Test {
+class MP4MapTest : public testing::Test {
  protected:
-  ShellMP4MapTest() {
+  MP4MapTest() {
     // make a new mock reader
-    reader_ = new ::testing::NiceMock<MockShellDataSourceReader>();
+    reader_ = new ::testing::NiceMock<MockDataSourceReader>();
     // make a new map with a mock reader.
-    map_ = new ShellMP4Map(reader_);
+    map_ = new MP4Map(reader_);
   }
 
-  virtual ~ShellMP4MapTest() {
+  virtual ~MP4MapTest() {
     DCHECK(map_->HasOneRef());
     map_ = NULL;
 
@@ -421,7 +421,7 @@
     reader_ = NULL;
   }
 
-  void ResetMap() { map_ = new ShellMP4Map(reader_); }
+  void ResetMap() { map_ = new MP4Map(reader_); }
 
   void CreateTestSampleTable(unsigned int seed, int num_of_samples,
                              int min_sample_size, int max_sample_size,
@@ -454,21 +454,21 @@
   }
 
   // ==== Test Fixture Members
-  scoped_refptr<ShellMP4Map> map_;
-  scoped_refptr<MockShellDataSourceReader> reader_;
+  scoped_refptr<MP4Map> map_;
+  scoped_refptr<MockDataSourceReader> reader_;
   std::unique_ptr<SampleTable> sample_table_;
 };
 
 // ==== SetAtom() Tests ========================================================
 /*
-TEST_F(ShellMP4MapTest, SetAtomWithZeroDefaultSize) {
+TEST_F(MP4MapTest, SetAtomWithZeroDefaultSize) {
   // SetAtom() should fail with a zero default size on an stsc.
   NOTIMPLEMENTED();
 }
 */
 // ==== GetSize() Tests ========================================================
 
-TEST_F(ShellMP4MapTest, GetSizeWithDefaultSize) {
+TEST_F(MP4MapTest, GetSizeWithDefaultSize) {
   CreateTestSampleTable(100, 1000, 0xb0df00d, 0xb0df00d, 5, 10, 5, 10, 10, 20,
                         10, 20);
   sample_table_->ClearReadStatistics();
@@ -490,7 +490,7 @@
   ASSERT_EQ(sample_table_->read_count(), 0);
 }
 
-TEST_F(ShellMP4MapTest, GetSizeIterationWithHugeCache) {
+TEST_F(MP4MapTest, GetSizeIterationWithHugeCache) {
   for (int max_sample_size = 10; max_sample_size < 20; ++max_sample_size) {
     CreateTestSampleTable(200 + max_sample_size, 1000, 10, max_sample_size, 5,
                           10, 5, 10, 10, 20, 10, 20);
@@ -517,7 +517,7 @@
   }
 }
 
-TEST_F(ShellMP4MapTest, GetSizeIterationTinyCache) {
+TEST_F(MP4MapTest, GetSizeIterationTinyCache) {
   for (int max_sample_size = 10; max_sample_size < 20; ++max_sample_size) {
     CreateTestSampleTable(300 + max_sample_size, 1000, 10, max_sample_size, 5,
                           10, 5, 10, 10, 20, 10, 20);
@@ -543,7 +543,7 @@
   }
 }
 
-TEST_F(ShellMP4MapTest, GetSizeRandomAccess) {
+TEST_F(MP4MapTest, GetSizeRandomAccess) {
   CreateTestSampleTable(101, 2000, 20, 24, 5, 10, 5, 10, 10, 20, 10, 20);
   for (int i = 24; i < 27; ++i) {
     ResetMap();
@@ -593,7 +593,7 @@
 
 // ==== GetOffset() Tests ======================================================
 
-TEST_F(ShellMP4MapTest, GetOffsetIterationHugeCache) {
+TEST_F(MP4MapTest, GetOffsetIterationHugeCache) {
   for (int coindex = 0; coindex < 2; ++coindex) {
     CreateTestSampleTable(102 + coindex, 1000, 20, 25, 5, 10, 5, 10, 10, 20, 10,
                           20);
@@ -617,7 +617,7 @@
   }
 }
 
-TEST_F(ShellMP4MapTest, GetOffsetIterationTinyCache) {
+TEST_F(MP4MapTest, GetOffsetIterationTinyCache) {
   for (int coindex = 0; coindex < 2; ++coindex) {
     CreateTestSampleTable(103, 30, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
     for (int i = 1; i < 12; ++i) {
@@ -644,7 +644,7 @@
 
 // Random access within cache should just result in correct re-integration
 // through the stsc.
-TEST_F(ShellMP4MapTest, GetOffsetRandomAccessHugeCache) {
+TEST_F(MP4MapTest, GetOffsetRandomAccessHugeCache) {
   for (int coindex = 0; coindex < 2; ++coindex) {
     CreateTestSampleTable(104, 300, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
     ResetMap();
@@ -664,7 +664,7 @@
 
 // Random access across cache boundaries should not break computation of
 // offsets.
-TEST_F(ShellMP4MapTest, GetOffsetRandomAccessTinyCache) {
+TEST_F(MP4MapTest, GetOffsetRandomAccessTinyCache) {
   for (int coindex = 0; coindex < 2; ++coindex) {
     CreateTestSampleTable(105, 300, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
     ResetMap();
@@ -718,7 +718,7 @@
   }
 }
 
-TEST_F(ShellMP4MapTest, GetOffsetRandomAccessWithDefaultSize) {
+TEST_F(MP4MapTest, GetOffsetRandomAccessWithDefaultSize) {
   for (int coindex = 0; coindex < 2; ++coindex) {
     CreateTestSampleTable(106, 300, 20, 20, 5, 10, 5, 10, 10, 20, 10, 20);
     ResetMap();
@@ -755,7 +755,7 @@
 
 // ==== GetDuration() Tests ====================================================
 
-TEST_F(ShellMP4MapTest, GetDurationIteration) {
+TEST_F(MP4MapTest, GetDurationIteration) {
   CreateTestSampleTable(107, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
   ResetMap();
   SetTestTable(kAtomType_stts, 2);
@@ -773,7 +773,7 @@
       map_->GetDuration(sample_table_->sample_count(), &failed_duration));
 }
 
-TEST_F(ShellMP4MapTest, GetDurationRandomAccess) {
+TEST_F(MP4MapTest, GetDurationRandomAccess) {
   CreateTestSampleTable(108, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
   ResetMap();
   SetTestTable(kAtomType_stts, 3);
@@ -810,7 +810,7 @@
 
 // ==== GetTimestamp() Tests ===================================================
 
-TEST_F(ShellMP4MapTest, GetTimestampIterationNoCompositionTime) {
+TEST_F(MP4MapTest, GetTimestampIterationNoCompositionTime) {
   CreateTestSampleTable(109, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
   ResetMap();
   SetTestTable(kAtomType_stts, 7);
@@ -828,7 +828,7 @@
       map_->GetTimestamp(sample_table_->sample_count(), &failed_timestamp));
 }
 
-TEST_F(ShellMP4MapTest, GetTimestampRandomAccessNoCompositionTime) {
+TEST_F(MP4MapTest, GetTimestampRandomAccessNoCompositionTime) {
   CreateTestSampleTable(110, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
   ResetMap();
   SetTestTable(kAtomType_stts, 10);
@@ -858,7 +858,7 @@
   }
 }
 
-TEST_F(ShellMP4MapTest, GetTimestampIteration) {
+TEST_F(MP4MapTest, GetTimestampIteration) {
   CreateTestSampleTable(111, 300, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
   for (int i = 1; i < 20; ++i) {
     ResetMap();
@@ -879,7 +879,7 @@
   }
 }
 
-TEST_F(ShellMP4MapTest, GetTimestampRandomAccess) {
+TEST_F(MP4MapTest, GetTimestampRandomAccess) {
   CreateTestSampleTable(112, 300, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
   for (int i = 1; i < 20; ++i) {
     ResetMap();
@@ -904,7 +904,7 @@
 // ==== GetIsKeyframe() Tests ==================================================
 
 // the map should consider every valid sample number a keyframe without an stss
-TEST_F(ShellMP4MapTest, GetIsKeyframeNoKeyframeTable) {
+TEST_F(MP4MapTest, GetIsKeyframeNoKeyframeTable) {
   ResetMap();
   bool is_keyframe_out = false;
   ASSERT_TRUE(map_->GetIsKeyframe(100, &is_keyframe_out));
@@ -921,7 +921,7 @@
   }
 }
 
-TEST_F(ShellMP4MapTest, GetIsKeyframeIteration) {
+TEST_F(MP4MapTest, GetIsKeyframeIteration) {
   CreateTestSampleTable(113, 1000, 0xb0df00d, 0xb0df00d, 5, 10, 5, 10, 10, 20,
                         10, 20);
   ResetMap();
@@ -936,7 +936,7 @@
   }
 }
 
-TEST_F(ShellMP4MapTest, GetIsKeyframeRandomAccess) {
+TEST_F(MP4MapTest, GetIsKeyframeRandomAccess) {
   CreateTestSampleTable(114, 1000, 0xb0df00d, 0xb0df00d, 5, 10, 5, 10, 10, 20,
                         10, 20);
   ResetMap();
@@ -1005,7 +1005,7 @@
 
 // every frame should be returned as a keyframe. This tests if our computation
 // of timestamps => sample numbers is equivalent to sample numbers => timestamps
-TEST_F(ShellMP4MapTest, GetKeyframeNoKeyframeTableIteration) {
+TEST_F(MP4MapTest, GetKeyframeNoKeyframeTableIteration) {
   CreateTestSampleTable(115, 30, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
   ResetMap();
   SetTestTable(kAtomType_stts, 7);
@@ -1022,7 +1022,7 @@
   }
 }
 
-TEST_F(ShellMP4MapTest, GetKeyframeNoKeyframeTableRandomAccess) {
+TEST_F(MP4MapTest, GetKeyframeNoKeyframeTableRandomAccess) {
   CreateTestSampleTable(116, 30, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
   ResetMap();
   SetTestTable(kAtomType_stts, 5);
@@ -1056,7 +1056,7 @@
 }
 
 // GetKeyframe is not normally called iteratively, so we test random access
-TEST_F(ShellMP4MapTest, GetKeyframe) {
+TEST_F(MP4MapTest, GetKeyframe) {
   CreateTestSampleTable(117, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
   ResetMap();
   SetTestTable(kAtomType_stss, 3);
diff --git a/src/cobalt/media/filters/shell_mp4_parser.cc b/src/cobalt/media/progressive/mp4_parser.cc
similarity index 91%
rename from src/cobalt/media/filters/shell_mp4_parser.cc
rename to src/cobalt/media/progressive/mp4_parser.cc
index 7d9efb1..957395d 100644
--- a/src/cobalt/media/filters/shell_mp4_parser.cc
+++ b/src/cobalt/media/progressive/mp4_parser.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/filters/shell_mp4_parser.h"
+#include "cobalt/media/progressive/mp4_parser.h"
 
 #include <inttypes.h>
 #include <limits>
@@ -67,10 +67,10 @@
 static const int kMapTableAtomCacheEntries_ctts = 51543 / kEntrySize_ctts;
 
 // static
-PipelineStatus ShellMP4Parser::Construct(
-    scoped_refptr<ShellDataSourceReader> reader,
-    const uint8* construction_header, scoped_refptr<ShellParser>* parser,
-    const scoped_refptr<MediaLog>& media_log) {
+PipelineStatus MP4Parser::Construct(scoped_refptr<DataSourceReader> reader,
+                                    const uint8* construction_header,
+                                    scoped_refptr<ProgressiveParser>* parser,
+                                    const scoped_refptr<MediaLog>& media_log) {
   DCHECK(parser);
   DCHECK(media_log);
   *parser = NULL;
@@ -90,28 +90,28 @@
   }
 
   // construct new mp4 parser
-  *parser = new ShellMP4Parser(reader, ftyp_atom_size, media_log);
+  *parser = new MP4Parser(reader, ftyp_atom_size, media_log);
   return PIPELINE_OK;
 }
 
-ShellMP4Parser::ShellMP4Parser(scoped_refptr<ShellDataSourceReader> reader,
-                               uint32 ftyp_atom_size,
-                               const scoped_refptr<MediaLog>& media_log)
-    : ShellAVCParser(reader, media_log),
+MP4Parser::MP4Parser(scoped_refptr<DataSourceReader> reader,
+                     uint32 ftyp_atom_size,
+                     const scoped_refptr<MediaLog>& media_log)
+    : AVCParser(reader, media_log),
       atom_offset_(ftyp_atom_size),  // start at next atom, skipping over ftyp
       current_trak_is_video_(false),
       current_trak_is_audio_(false),
       current_trak_time_scale_(0),
       video_time_scale_hz_(0),
       audio_time_scale_hz_(0),
-      audio_map_(new ShellMP4Map(reader)),
-      video_map_(new ShellMP4Map(reader)),
+      audio_map_(new MP4Map(reader)),
+      video_map_(new MP4Map(reader)),
       audio_sample_(0),
       video_sample_(0),
       first_audio_hole_ticks_(0),
       first_audio_hole_(base::TimeDelta::FromSeconds(0)) {}
 
-ShellMP4Parser::~ShellMP4Parser() {}
+MP4Parser::~MP4Parser() {}
 
 // For MP4 we traverse the file's atom structure attempting to find the audio
 // and video configuration information and the locations in the file of the
@@ -119,7 +119,7 @@
 // NALUs in the file. As some of the stbl subatoms can be quite large we cache
 // a fixed maximum quantity of each stbl subatom and update the cache only on
 // miss.
-bool ShellMP4Parser::ParseConfig() {
+bool MP4Parser::ParseConfig() {
   while (!IsConfigComplete() || !audio_map_->IsComplete() ||
          !video_map_->IsComplete()) {
     if (!ParseNextAtom()) {
@@ -129,7 +129,7 @@
   return true;
 }
 
-scoped_refptr<ShellAU> ShellMP4Parser::GetNextAU(DemuxerStream::Type type) {
+scoped_refptr<AvcAccessUnit> MP4Parser::GetNextAU(DemuxerStream::Type type) {
   uint32 size = 0;
   uint32 duration_ticks = 0;
   uint64 timestamp_ticks = 0;
@@ -148,8 +148,8 @@
         !audio_map_->GetTimestamp(audio_sample_, &timestamp_ticks)) {
       // determine if EOS or error
       if (audio_map_->IsEOS(audio_sample_)) {
-        return ShellAU::CreateEndOfStreamAU(DemuxerStream::AUDIO,
-                                            audio_track_duration_);
+        return AvcAccessUnit::CreateEndOfStreamAU(DemuxerStream::AUDIO,
+                                                  audio_track_duration_);
       } else {
         DLOG(ERROR) << "parsed bad audio AU";
         return NULL;
@@ -195,8 +195,8 @@
         !video_map_->GetTimestamp(video_sample_, &timestamp_ticks) ||
         !video_map_->GetIsKeyframe(video_sample_, &is_keyframe)) {
       if (video_map_->IsEOS(video_sample_)) {
-        return ShellAU::CreateEndOfStreamAU(DemuxerStream::VIDEO,
-                                            video_track_duration_);
+        return AvcAccessUnit::CreateEndOfStreamAU(DemuxerStream::VIDEO,
+                                                  video_track_duration_);
       } else {
         DLOG(ERROR) << "parsed bad video AU";
         return NULL;
@@ -219,13 +219,14 @@
   size_t prepend_size = CalculatePrependSize(type, is_keyframe);
 
   if (type == DemuxerStream::AUDIO)
-    return ShellAU::CreateAudioAU(offset, size, prepend_size, is_keyframe,
-                                  timestamp, duration, this);
-  return ShellAU::CreateVideoAU(offset, size, prepend_size, nal_header_size_,
-                                is_keyframe, timestamp, duration, this);
+    return AvcAccessUnit::CreateAudioAU(offset, size, prepend_size, is_keyframe,
+                                        timestamp, duration, this);
+  return AvcAccessUnit::CreateVideoAU(offset, size, prepend_size,
+                                      nal_header_size_, is_keyframe, timestamp,
+                                      duration, this);
 }
 
-bool ShellMP4Parser::SeekTo(base::TimeDelta timestamp) {
+bool MP4Parser::SeekTo(base::TimeDelta timestamp) {
   if (audio_time_scale_hz_ == 0 || video_time_scale_hz_ == 0) {
     DLOG_IF(ERROR, audio_time_scale_hz_ == 0)
         << "|audio_time_scale_hz_| cannot be 0.";
@@ -272,7 +273,7 @@
 // fourCC code       | ASCII  | four-byte ASCII code we treat as uint32
 // extended size     | uint64 | optional size field, only here if atom size is 1
 // <--- rest of atom body starts here
-bool ShellMP4Parser::ParseNextAtom() {
+bool MP4Parser::ParseNextAtom() {
   uint8 atom[kAtomDownload];
   int bytes_read = reader_->BlockingRead(atom_offset_, kAtomDownload, atom);
   if (bytes_read < kAtomDownload) {
@@ -461,7 +462,7 @@
   return atom_parse_success;
 }
 
-bool ShellMP4Parser::ParseMP4_esds(uint64 atom_data_size) {
+bool MP4Parser::ParseMP4_esds(uint64 atom_data_size) {
   if (atom_data_size < kFullBoxHeaderAndFlagSize) {
     DLOG(WARNING) << base::StringPrintf(
         "esds box should at least be %d bytes but now it is %" PRId64 " bytes",
@@ -501,7 +502,7 @@
   return false;
 }
 
-bool ShellMP4Parser::ParseMP4_hdlr(uint64 atom_data_size, uint8* hdlr) {
+bool MP4Parser::ParseMP4_hdlr(uint64 atom_data_size, uint8* hdlr) {
   // ensure we're downloading enough of the hdlr to parse
   DCHECK_LE(kDesiredBytes_hdlr + 16, kAtomDownload);
   // sanity-check for minimum size
@@ -534,7 +535,7 @@
   return true;
 }
 
-bool ShellMP4Parser::ParseMP4_mdhd(uint64 atom_data_size, uint8* mdhd) {
+bool MP4Parser::ParseMP4_mdhd(uint64 atom_data_size, uint8* mdhd) {
   DCHECK_LE(kDesiredBytes_mdhd + 16, kAtomDownload);
   if (atom_data_size < kDesiredBytes_mdhd) {
     DLOG(WARNING) << base::StringPrintf("bad size %" PRId64 " on mdhd",
@@ -577,7 +578,7 @@
   return true;
 }
 
-bool ShellMP4Parser::ParseMP4_mp4a(uint64 atom_data_size, uint8* mp4a) {
+bool MP4Parser::ParseMP4_mp4a(uint64 atom_data_size, uint8* mp4a) {
   DCHECK_LE(kDesiredBytes_mp4a + 16, kAtomDownload);
   // we only need the first two bytes of the header, which details the version
   // number of this atom, which tells us the size of the rest of the header,
@@ -619,7 +620,7 @@
 // 12     | time scale        | 4
 // 16     | duration:         | 4
 //
-bool ShellMP4Parser::ParseMP4_mvhd(uint64 atom_data_size, uint8* mvhd) {
+bool MP4Parser::ParseMP4_mvhd(uint64 atom_data_size, uint8* mvhd) {
   DCHECK_LE(kDesiredBytes_mvhd + 16, kAtomDownload);
   // it should be at least long enough for us to extract the parts we want
   if (atom_data_size < kDesiredBytes_mvhd) {
@@ -641,8 +642,7 @@
   return true;
 }
 
-base::TimeDelta ShellMP4Parser::TicksToTime(uint64 ticks,
-                                            uint32 time_scale_hz) {
+base::TimeDelta MP4Parser::TicksToTime(uint64 ticks, uint32 time_scale_hz) {
   DCHECK_NE(time_scale_hz, 0);
 
   if (time_scale_hz == 0) {
@@ -652,7 +652,7 @@
                                            time_scale_hz);
 }
 
-uint64 ShellMP4Parser::TimeToTicks(base::TimeDelta time, uint32 time_scale_hz) {
+uint64 MP4Parser::TimeToTicks(base::TimeDelta time, uint32 time_scale_hz) {
   DCHECK_NE(time_scale_hz, 0);
 
   if (time_scale_hz == 0) {
diff --git a/src/cobalt/media/filters/shell_mp4_parser.h b/src/cobalt/media/progressive/mp4_parser.h
similarity index 82%
rename from src/cobalt/media/filters/shell_mp4_parser.h
rename to src/cobalt/media/progressive/mp4_parser.h
index 6a16b9f..6d0d617 100644
--- a/src/cobalt/media/filters/shell_mp4_parser.h
+++ b/src/cobalt/media/progressive/mp4_parser.h
@@ -12,12 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_FILTERS_SHELL_MP4_PARSER_H_
-#define COBALT_MEDIA_FILTERS_SHELL_MP4_PARSER_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_MP4_PARSER_H_
+#define COBALT_MEDIA_PROGRESSIVE_MP4_PARSER_H_
 
 #include "cobalt/media/base/media_log.h"
-#include "cobalt/media/filters/shell_avc_parser.h"
-#include "cobalt/media/filters/shell_mp4_map.h"
+#include "cobalt/media/progressive/avc_parser.h"
+#include "cobalt/media/progressive/mp4_map.h"
 
 namespace cobalt {
 namespace media {
@@ -27,7 +27,7 @@
 // second download (typically), but no larger. This is currently set at 16
 // bytes for the 8 byte header + optional 8 byte size extension plus 20 bytes
 // for the needed values within an mvhd header. We leave this is the header so
-// that ShellMP4Map can re-use,
+// that MP4Map can re-use,
 static const int kAtomDownload = 36;
 
 // mp4 atom fourCC codes as big-endian unsigned ints
@@ -59,24 +59,23 @@
 static const uint32 kAtomType_vmhd = 0x766d6864;  // skip whole atom
 // TODO: mp4v!!
 
-class ShellMP4Parser : public ShellAVCParser {
+class MP4Parser : public AVCParser {
  public:
   // Attempts to make sense of the provided bytes of the top of a file as an
   // flv, and if it does make sense returns PIPELINE_OK and |*parser| contains a
-  // ShellMP4Parser initialized with some basic state.  If it doesn't make sense
+  // MP4Parser initialized with some basic state.  If it doesn't make sense
   // this returns an error status and |*parser| contains NULL.
-  static PipelineStatus Construct(scoped_refptr<ShellDataSourceReader> reader,
+  static PipelineStatus Construct(scoped_refptr<DataSourceReader> reader,
                                   const uint8* construction_header,
-                                  scoped_refptr<ShellParser>* parser,
+                                  scoped_refptr<ProgressiveParser>* parser,
                                   const scoped_refptr<MediaLog>& media_log);
-  ShellMP4Parser(scoped_refptr<ShellDataSourceReader> reader,
-                 uint32 ftyp_atom_size,
-                 const scoped_refptr<MediaLog>& media_log);
-  ~ShellMP4Parser() override;
+  MP4Parser(scoped_refptr<DataSourceReader> reader, uint32 ftyp_atom_size,
+            const scoped_refptr<MediaLog>& media_log);
+  ~MP4Parser() override;
 
-  // === ShellParser implementation
+  // === ProgressiveParser implementation
   bool ParseConfig() override;
-  scoped_refptr<ShellAU> GetNextAU(DemuxerStream::Type type) override;
+  scoped_refptr<AvcAccessUnit> GetNextAU(DemuxerStream::Type type) override;
   bool SeekTo(base::TimeDelta timestamp) override;
 
  private:
@@ -99,8 +98,8 @@
   uint32 audio_time_scale_hz_;
   base::TimeDelta audio_track_duration_;
   base::TimeDelta video_track_duration_;
-  scoped_refptr<ShellMP4Map> audio_map_;
-  scoped_refptr<ShellMP4Map> video_map_;
+  scoped_refptr<MP4Map> audio_map_;
+  scoped_refptr<MP4Map> video_map_;
   uint32 audio_sample_;
   uint32 video_sample_;
   // for keeping buffers continuous across time scales
@@ -111,4 +110,4 @@
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_FILTERS_SHELL_MP4_PARSER_H_
+#endif  // COBALT_MEDIA_PROGRESSIVE_MP4_PARSER_H_
diff --git a/src/cobalt/media/filters/shell_demuxer.cc b/src/cobalt/media/progressive/progressive_demuxer.cc
similarity index 77%
rename from src/cobalt/media/filters/shell_demuxer.cc
rename to src/cobalt/media/progressive/progressive_demuxer.cc
index 3d0a8cf..a396825 100644
--- a/src/cobalt/media/filters/shell_demuxer.cc
+++ b/src/cobalt/media/progressive/progressive_demuxer.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/filters/shell_demuxer.h"
+#include "cobalt/media/progressive/progressive_demuxer.h"
 
 #include <inttypes.h>
 
@@ -33,14 +33,16 @@
 namespace cobalt {
 namespace media {
 
-ShellDemuxerStream::ShellDemuxerStream(ShellDemuxer* demuxer, Type type)
+ProgressiveDemuxerStream::ProgressiveDemuxerStream(ProgressiveDemuxer* demuxer,
+                                                   Type type)
     : demuxer_(demuxer), type_(type) {
-  TRACE_EVENT0("media_stack", "ShellDemuxerStream::ShellDemuxerStream()");
+  TRACE_EVENT0("media_stack",
+               "ProgressiveDemuxerStream::ProgressiveDemuxerStream()");
   DCHECK(demuxer_);
 }
 
-void ShellDemuxerStream::Read(const ReadCB& read_cb) {
-  TRACE_EVENT0("media_stack", "ShellDemuxerStream::Read()");
+void ProgressiveDemuxerStream::Read(const ReadCB& read_cb) {
+  TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Read()");
   DCHECK(!read_cb.is_null());
 
   base::AutoLock auto_lock(lock_);
@@ -48,7 +50,7 @@
   // Don't accept any additional reads if we've been told to stop.
   // The demuxer_ may have been destroyed in the pipleine thread.
   if (stopped_) {
-    TRACE_EVENT0("media_stack", "ShellDemuxerStream::Read() EOS sent.");
+    TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Read() EOS sent.");
     read_cb.Run(DemuxerStream::kOk,
                 scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
     return;
@@ -61,7 +63,7 @@
     // Send the oldest buffer back.
     scoped_refptr<DecoderBuffer> buffer = buffer_queue_.front();
     if (buffer->end_of_stream()) {
-      TRACE_EVENT0("media_stack", "ShellDemuxerStream::Read() EOS sent.");
+      TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Read() EOS sent.");
     } else {
       // Do not pop EOS buffers, so that subsequent read requests also get EOS
       total_buffer_size_ -= buffer->data_size();
@@ -70,31 +72,33 @@
     }
     read_cb.Run(DemuxerStream::kOk, buffer);
   } else {
-    TRACE_EVENT0("media_stack", "ShellDemuxerStream::Read() request queued.");
+    TRACE_EVENT0("media_stack",
+                 "ProgressiveDemuxerStream::Read() request queued.");
     read_queue_.push_back(read_cb);
   }
 }
 
-AudioDecoderConfig ShellDemuxerStream::audio_decoder_config() {
+AudioDecoderConfig ProgressiveDemuxerStream::audio_decoder_config() {
   return demuxer_->AudioConfig();
 }
 
-VideoDecoderConfig ShellDemuxerStream::video_decoder_config() {
+VideoDecoderConfig ProgressiveDemuxerStream::video_decoder_config() {
   return demuxer_->VideoConfig();
 }
 
-Ranges<base::TimeDelta> ShellDemuxerStream::GetBufferedRanges() {
+Ranges<base::TimeDelta> ProgressiveDemuxerStream::GetBufferedRanges() {
   base::AutoLock auto_lock(lock_);
   return buffered_ranges_;
 }
 
-DemuxerStream::Type ShellDemuxerStream::type() const { return type_; }
+DemuxerStream::Type ProgressiveDemuxerStream::type() const { return type_; }
 
-void ShellDemuxerStream::EnableBitstreamConverter() { NOTIMPLEMENTED(); }
+void ProgressiveDemuxerStream::EnableBitstreamConverter() { NOTIMPLEMENTED(); }
 
-void ShellDemuxerStream::EnqueueBuffer(scoped_refptr<DecoderBuffer> buffer) {
+void ProgressiveDemuxerStream::EnqueueBuffer(
+    scoped_refptr<DecoderBuffer> buffer) {
   TRACE_EVENT1(
-      "media_stack", "ShellDemuxerStream::EnqueueBuffer()", "timestamp",
+      "media_stack", "ProgressiveDemuxerStream::EnqueueBuffer()", "timestamp",
       buffer->end_of_stream() ? -1 : buffer->timestamp().InMicroseconds());
   base::AutoLock auto_lock(lock_);
   if (stopped_) {
@@ -107,7 +111,7 @@
 
   if (buffer->end_of_stream()) {
     TRACE_EVENT0("media_stack",
-                 "ShellDemuxerStream::EnqueueBuffer() EOS received.");
+                 "ProgressiveDemuxerStream::EnqueueBuffer() EOS received.");
   } else if (buffer->timestamp() != kNoTimestamp) {
     if (last_buffer_timestamp_ != kNoTimestamp &&
         last_buffer_timestamp_ < buffer->timestamp()) {
@@ -135,23 +139,23 @@
   }
 }
 
-base::TimeDelta ShellDemuxerStream::GetLastBufferTimestamp() const {
+base::TimeDelta ProgressiveDemuxerStream::GetLastBufferTimestamp() const {
   base::AutoLock auto_lock(lock_);
   return last_buffer_timestamp_;
 }
 
-size_t ShellDemuxerStream::GetTotalBufferSize() const {
+size_t ProgressiveDemuxerStream::GetTotalBufferSize() const {
   base::AutoLock auto_lock(lock_);
   return total_buffer_size_;
 }
 
-size_t ShellDemuxerStream::GetTotalBufferCount() const {
+size_t ProgressiveDemuxerStream::GetTotalBufferCount() const {
   base::AutoLock auto_lock(lock_);
   return total_buffer_count_;
 }
 
-void ShellDemuxerStream::FlushBuffers() {
-  TRACE_EVENT0("media_stack", "ShellDemuxerStream::FlushBuffers()");
+void ProgressiveDemuxerStream::FlushBuffers() {
+  TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::FlushBuffers()");
   base::AutoLock auto_lock(lock_);
   // TODO: Investigate if the following warning is valid.
   DLOG_IF(WARNING, !read_queue_.empty()) << "Read requests should be empty";
@@ -161,8 +165,8 @@
   last_buffer_timestamp_ = kNoTimestamp;
 }
 
-void ShellDemuxerStream::Stop() {
-  TRACE_EVENT0("media_stack", "ShellDemuxerStream::Stop()");
+void ProgressiveDemuxerStream::Stop() {
+  TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Stop()");
   DCHECK(demuxer_->MessageLoopBelongsToCurrentThread());
   base::AutoLock auto_lock(lock_);
   buffer_queue_.clear();
@@ -172,7 +176,7 @@
   // fulfill any pending callbacks with EOS buffers set to end timestamp
   for (ReadQueue::iterator it = read_queue_.begin(); it != read_queue_.end();
        ++it) {
-    TRACE_EVENT0("media_stack", "ShellDemuxerStream::Stop() EOS sent.");
+    TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Stop() EOS sent.");
     it->Run(DemuxerStream::kOk,
             scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
   }
@@ -181,16 +185,16 @@
 }
 
 //
-// ShellDemuxer
+// ProgressiveDemuxer
 //
-ShellDemuxer::ShellDemuxer(
+ProgressiveDemuxer::ProgressiveDemuxer(
     const scoped_refptr<base::SingleThreadTaskRunner>& message_loop,
     DecoderBuffer::Allocator* buffer_allocator, DataSource* data_source,
     const scoped_refptr<MediaLog>& media_log)
     : message_loop_(message_loop),
       buffer_allocator_(buffer_allocator),
       host_(NULL),
-      blocking_thread_("ShellDemuxerBlk"),
+      blocking_thread_("ProgDemuxerBlk"),
       data_source_(data_source),
       media_log_(media_log),
       stopped_(false),
@@ -201,20 +205,20 @@
   DCHECK(buffer_allocator_);
   DCHECK(data_source_);
   DCHECK(media_log_);
-  reader_ = new ShellDataSourceReader();
+  reader_ = new DataSourceReader();
   reader_->SetDataSource(data_source_);
 }
 
-ShellDemuxer::~ShellDemuxer() {
+ProgressiveDemuxer::~ProgressiveDemuxer() {
   // Explicitly stop |blocking_thread_| to ensure that it stops before the
   // destructiing of any other members.
   blocking_thread_.Stop();
 }
 
-void ShellDemuxer::Initialize(DemuxerHost* host,
-                              const PipelineStatusCB& status_cb,
-                              bool enable_text_tracks) {
-  TRACE_EVENT0("media_stack", "ShellDemuxer::Initialize()");
+void ProgressiveDemuxer::Initialize(DemuxerHost* host,
+                                    const PipelineStatusCB& status_cb,
+                                    bool enable_text_tracks) {
+  TRACE_EVENT0("media_stack", "ProgressiveDemuxer::Initialize()");
   DCHECK(!enable_text_tracks);
   DCHECK(MessageLoopBelongsToCurrentThread());
   DCHECK(reader_);
@@ -226,9 +230,9 @@
 
   // create audio and video demuxer stream objects
   audio_demuxer_stream_.reset(
-      new ShellDemuxerStream(this, DemuxerStream::AUDIO));
+      new ProgressiveDemuxerStream(this, DemuxerStream::AUDIO));
   video_demuxer_stream_.reset(
-      new ShellDemuxerStream(this, DemuxerStream::VIDEO));
+      new ProgressiveDemuxerStream(this, DemuxerStream::VIDEO));
 
   // start the blocking thread and have it download and parse the media config
   if (!blocking_thread_.Start()) {
@@ -237,16 +241,18 @@
   }
 
   blocking_thread_.task_runner()->PostTask(
-      FROM_HERE, base::Bind(&ShellDemuxer::ParseConfigBlocking,
+      FROM_HERE, base::Bind(&ProgressiveDemuxer::ParseConfigBlocking,
                             base::Unretained(this), status_cb));
 }
 
-void ShellDemuxer::ParseConfigBlocking(const PipelineStatusCB& status_cb) {
+void ProgressiveDemuxer::ParseConfigBlocking(
+    const PipelineStatusCB& status_cb) {
   DCHECK(blocking_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!parser_);
 
   // construct stream parser with error callback
-  PipelineStatus status = ShellParser::Construct(reader_, &parser_, media_log_);
+  PipelineStatus status =
+      ProgressiveParser::Construct(reader_, &parser_, media_log_);
   // if we can't construct a parser for this stream it's a fatal error, return
   // false so ParseConfigDone will notify the caller to Initialize() via
   // status_cb.
@@ -286,8 +292,8 @@
   ParseConfigDone(status_cb, PIPELINE_OK);
 }
 
-void ShellDemuxer::ParseConfigDone(const PipelineStatusCB& status_cb,
-                                   PipelineStatus status) {
+void ProgressiveDemuxer::ParseConfigDone(const PipelineStatusCB& status_cb,
+                                         PipelineStatus status) {
   DCHECK(blocking_thread_.task_runner()->BelongsToCurrentThread());
 
   if (HasStopCalled()) {
@@ -306,18 +312,18 @@
   status_cb.Run(PIPELINE_OK);
 }
 
-void ShellDemuxer::Request(DemuxerStream::Type type) {
+void ProgressiveDemuxer::Request(DemuxerStream::Type type) {
   if (!blocking_thread_.task_runner()->BelongsToCurrentThread()) {
     blocking_thread_.task_runner()->PostTask(
         FROM_HERE,
-        base::Bind(&ShellDemuxer::Request, base::Unretained(this), type));
+        base::Bind(&ProgressiveDemuxer::Request, base::Unretained(this), type));
     return;
   }
 
   DCHECK(!requested_au_) << "overlapping requests not supported!";
   flushing_ = false;
   // Ask parser for next AU
-  scoped_refptr<ShellAU> au = parser_->GetNextAU(type);
+  scoped_refptr<AvcAccessUnit> au = parser_->GetNextAU(type);
   // fatal parsing error returns NULL or malformed AU
   if (!au || !au->IsValid()) {
     if (!HasStopCalled()) {
@@ -332,12 +338,12 @@
 
   const char* ALLOW_UNUSED_TYPE event_type =
       type == DemuxerStream::AUDIO ? "audio" : "video";
-  TRACE_EVENT2("media_stack", "ShellDemuxer::RequestTask()", "type", event_type,
-               "timestamp", au->GetTimestamp().InMicroseconds());
+  TRACE_EVENT2("media_stack", "ProgressiveDemuxer::RequestTask()", "type",
+               event_type, "timestamp", au->GetTimestamp().InMicroseconds());
 
   // don't issue allocation requests for EOS AUs
   if (au->IsEndOfStream()) {
-    TRACE_EVENT0("media_stack", "ShellDemuxer::RequestTask() EOS sent");
+    TRACE_EVENT0("media_stack", "ProgressiveDemuxer::RequestTask() EOS sent");
     // enqueue EOS buffer with correct stream
     scoped_refptr<DecoderBuffer> eos_buffer = DecoderBuffer::CreateEOSBuffer();
     if (type == DemuxerStream::AUDIO) {
@@ -357,7 +363,7 @@
   AllocateBuffer();
 }
 
-void ShellDemuxer::AllocateBuffer() {
+void ProgressiveDemuxer::AllocateBuffer() {
   DCHECK(requested_au_);
 
   if (HasStopCalled()) {
@@ -385,7 +391,8 @@
       const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(100);
       blocking_thread_.message_loop()->task_runner()->PostDelayedTask(
           FROM_HERE,
-          base::Bind(&ShellDemuxer::AllocateBuffer, base::Unretained(this)),
+          base::Bind(&ProgressiveDemuxer::AllocateBuffer,
+                     base::Unretained(this)),
           kDelay);
       return;
     }
@@ -404,13 +411,14 @@
       const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(100);
       blocking_thread_.message_loop()->task_runner()->PostDelayedTask(
           FROM_HERE,
-          base::Bind(&ShellDemuxer::AllocateBuffer, base::Unretained(this)),
+          base::Bind(&ProgressiveDemuxer::AllocateBuffer,
+                     base::Unretained(this)),
           kDelay);
     }
   }
 }
 
-void ShellDemuxer::Download(scoped_refptr<DecoderBuffer> buffer) {
+void ProgressiveDemuxer::Download(scoped_refptr<DecoderBuffer> buffer) {
   DCHECK(blocking_thread_.task_runner()->BelongsToCurrentThread());
   // We need a requested_au_ or to have canceled this request and
   // are buffering to a new location for this to make sense
@@ -418,8 +426,9 @@
 
   const char* ALLOW_UNUSED_TYPE event_type =
       requested_au_->GetType() == DemuxerStream::AUDIO ? "audio" : "video";
-  TRACE_EVENT2("media_stack", "ShellDemuxer::Download()", "type", event_type,
-               "timestamp", requested_au_->GetTimestamp().InMicroseconds());
+  TRACE_EVENT2("media_stack", "ProgressiveDemuxer::Download()", "type",
+               event_type, "timestamp",
+               requested_au_->GetTimestamp().InMicroseconds());
   // do nothing if stopped
   if (HasStopCalled()) {
     DLOG(INFO) << "aborting download task, stopped";
@@ -466,11 +475,11 @@
   host_->OnBufferedTimeRangesChanged(buffered);
 
   blocking_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&ShellDemuxer::IssueNextRequest, base::Unretained(this)));
+      FROM_HERE, base::Bind(&ProgressiveDemuxer::IssueNextRequest,
+                            base::Unretained(this)));
 }
 
-void ShellDemuxer::IssueNextRequest() {
+void ProgressiveDemuxer::IssueNextRequest() {
   DCHECK(!requested_au_);
   // if we're stopped don't download anymore
   if (HasStopCalled()) {
@@ -519,10 +528,10 @@
   // running in a tight loop and seek or stop request has no chance to kick in.
   blocking_thread_.task_runner()->PostTask(
       FROM_HERE,
-      base::Bind(&ShellDemuxer::Request, base::Unretained(this), type));
+      base::Bind(&ProgressiveDemuxer::Request, base::Unretained(this), type));
 }
 
-void ShellDemuxer::Stop() {
+void ProgressiveDemuxer::Stop() {
   DCHECK(MessageLoopBelongsToCurrentThread());
   // set our internal stop flag, to not treat read failures as
   // errors anymore but as a natural part of stopping
@@ -535,8 +544,8 @@
   reader_->Stop();
 }
 
-void ShellDemuxer::DataSourceStopped(const base::Closure& callback) {
-  TRACE_EVENT0("media_stack", "ShellDemuxer::DataSourceStopped()");
+void ProgressiveDemuxer::DataSourceStopped(const base::Closure& callback) {
+  TRACE_EVENT0("media_stack", "ProgressiveDemuxer::DataSourceStopped()");
   DCHECK(MessageLoopBelongsToCurrentThread());
   // stop the download thread
   blocking_thread_.Stop();
@@ -548,20 +557,23 @@
   callback.Run();
 }
 
-bool ShellDemuxer::HasStopCalled() {
+bool ProgressiveDemuxer::HasStopCalled() {
   base::AutoLock auto_lock(lock_for_stopped_);
   return stopped_;
 }
 
-void ShellDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
+void ProgressiveDemuxer::Seek(base::TimeDelta time,
+                              const PipelineStatusCB& cb) {
   blocking_thread_.message_loop()->task_runner()->PostTask(
-      FROM_HERE, base::Bind(&ShellDemuxer::SeekTask, base::Unretained(this),
-                            time, BindToCurrentLoop(cb)));
+      FROM_HERE,
+      base::Bind(&ProgressiveDemuxer::SeekTask, base::Unretained(this), time,
+                 BindToCurrentLoop(cb)));
 }
 
 // runs on blocking thread
-void ShellDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) {
-  TRACE_EVENT1("media_stack", "ShellDemuxer::SeekTask()", "timestamp",
+void ProgressiveDemuxer::SeekTask(base::TimeDelta time,
+                                  const PipelineStatusCB& cb) {
+  TRACE_EVENT1("media_stack", "ProgressiveDemuxer::SeekTask()", "timestamp",
                time.InMicroseconds());
   DLOG(INFO) << base::StringPrintf("seek to: %" PRId64 " ms",
                                    time.InMilliseconds());
@@ -586,7 +598,7 @@
   }
 }
 
-DemuxerStream* ShellDemuxer::GetStream(media::DemuxerStream::Type type) {
+DemuxerStream* ProgressiveDemuxer::GetStream(media::DemuxerStream::Type type) {
   if (type == DemuxerStream::AUDIO) {
     return audio_demuxer_stream_.get();
   } else if (type == DemuxerStream::VIDEO) {
@@ -597,20 +609,20 @@
   return NULL;
 }
 
-base::TimeDelta ShellDemuxer::GetStartTime() const {
+base::TimeDelta ProgressiveDemuxer::GetStartTime() const {
   // we always assume a start time of 0
   return base::TimeDelta();
 }
 
-const AudioDecoderConfig& ShellDemuxer::AudioConfig() {
+const AudioDecoderConfig& ProgressiveDemuxer::AudioConfig() {
   return parser_->AudioConfig();
 }
 
-const VideoDecoderConfig& ShellDemuxer::VideoConfig() {
+const VideoDecoderConfig& ProgressiveDemuxer::VideoConfig() {
   return parser_->VideoConfig();
 }
 
-bool ShellDemuxer::MessageLoopBelongsToCurrentThread() const {
+bool ProgressiveDemuxer::MessageLoopBelongsToCurrentThread() const {
   return message_loop_->BelongsToCurrentThread();
 }
 
diff --git a/src/cobalt/media/filters/shell_demuxer.h b/src/cobalt/media/progressive/progressive_demuxer.h
similarity index 81%
rename from src/cobalt/media/filters/shell_demuxer.h
rename to src/cobalt/media/progressive/progressive_demuxer.h
index 38f3dde..6ecb323 100644
--- a/src/cobalt/media/filters/shell_demuxer.h
+++ b/src/cobalt/media/progressive/progressive_demuxer.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_FILTERS_SHELL_DEMUXER_H_
-#define COBALT_MEDIA_FILTERS_SHELL_DEMUXER_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_PROGRESSIVE_DEMUXER_H_
+#define COBALT_MEDIA_PROGRESSIVE_PROGRESSIVE_DEMUXER_H_
 
 #include <deque>
 #include <memory>
@@ -28,17 +28,17 @@
 #include "cobalt/media/base/demuxer_stream.h"
 #include "cobalt/media/base/media_log.h"
 #include "cobalt/media/base/ranges.h"
-#include "cobalt/media/filters/shell_parser.h"
+#include "cobalt/media/progressive/progressive_parser.h"
 
 namespace cobalt {
 namespace media {
 
 class DecoderBuffer;
-class ShellDemuxer;
+class ProgressiveDemuxer;
 
-class ShellDemuxerStream : public DemuxerStream {
+class ProgressiveDemuxerStream : public DemuxerStream {
  public:
-  ShellDemuxerStream(ShellDemuxer* demuxer, Type type);
+  ProgressiveDemuxerStream(ProgressiveDemuxer* demuxer, Type type);
 
   // DemuxerStream implementation
   void Read(const ReadCB& read_cb) override;
@@ -56,7 +56,7 @@
     NOTREACHED();
   }
 
-  // Functions used by ShellDemuxer
+  // Functions used by ProgressiveDemuxer
   Ranges<base::TimeDelta> GetBufferedRanges();
   void EnqueueBuffer(scoped_refptr<DecoderBuffer> buffer);
   void FlushBuffers();
@@ -72,7 +72,7 @@
   void RebuildEnqueuedRanges_Locked();
 
   // non-owning pointer to avoid circular reference
-  ShellDemuxer* demuxer_;
+  ProgressiveDemuxer* demuxer_;
   Type type_;
 
   // Used to protect everything below.
@@ -97,19 +97,19 @@
   size_t total_buffer_size_ = 0;
   size_t total_buffer_count_ = 0;
 
-  DISALLOW_COPY_AND_ASSIGN(ShellDemuxerStream);
+  DISALLOW_COPY_AND_ASSIGN(ProgressiveDemuxerStream);
 };
 
-class MEDIA_EXPORT ShellDemuxer : public Demuxer {
+class MEDIA_EXPORT ProgressiveDemuxer : public Demuxer {
  public:
-  ShellDemuxer(const scoped_refptr<base::SingleThreadTaskRunner>& message_loop,
-               DecoderBuffer::Allocator* buffer_allocator,
-               DataSource* data_source,
-               const scoped_refptr<MediaLog>& media_log);
-  ~ShellDemuxer() override;
+  ProgressiveDemuxer(
+      const scoped_refptr<base::SingleThreadTaskRunner>& message_loop,
+      DecoderBuffer::Allocator* buffer_allocator, DataSource* data_source,
+      const scoped_refptr<MediaLog>& media_log);
+  ~ProgressiveDemuxer() override;
 
   // Demuxer implementation.
-  std::string GetDisplayName() const override { return "ShellDemuxer"; }
+  std::string GetDisplayName() const override { return "ProgressiveDemuxer"; }
   void Initialize(DemuxerHost* host, const PipelineStatusCB& status_cb,
                   bool enable_text_tracks) override;
   void AbortPendingReads() override {}
@@ -140,12 +140,12 @@
   // in to it, and enqueue the data in the appropriate demuxer stream.
   void Request(DemuxerStream::Type type);
 
-  // The DemuxerStream objects ask their parent ShellDemuxer stream class
+  // The DemuxerStream objects ask their parent ProgressiveDemuxer stream class
   // for these configuration data rather than duplicating in the child classes
   const AudioDecoderConfig& AudioConfig();
   const VideoDecoderConfig& VideoConfig();
 
-  // Provide access to ShellDemuxerStream.
+  // Provide access to ProgressiveDemuxerStream.
   bool MessageLoopBelongsToCurrentThread() const;
 
  private:
@@ -170,17 +170,17 @@
   base::Thread blocking_thread_;
   DataSource* data_source_;
   scoped_refptr<MediaLog> media_log_;
-  scoped_refptr<ShellDataSourceReader> reader_;
+  scoped_refptr<DataSourceReader> reader_;
 
   base::Lock lock_for_stopped_;
   bool stopped_;
   bool flushing_;
 
-  std::unique_ptr<ShellDemuxerStream> audio_demuxer_stream_;
-  std::unique_ptr<ShellDemuxerStream> video_demuxer_stream_;
-  scoped_refptr<ShellParser> parser_;
+  std::unique_ptr<ProgressiveDemuxerStream> audio_demuxer_stream_;
+  std::unique_ptr<ProgressiveDemuxerStream> video_demuxer_stream_;
+  scoped_refptr<ProgressiveParser> parser_;
 
-  scoped_refptr<ShellAU> requested_au_;
+  scoped_refptr<AvcAccessUnit> requested_au_;
   bool audio_reached_eos_;
   bool video_reached_eos_;
 };
@@ -188,4 +188,4 @@
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_FILTERS_SHELL_DEMUXER_H_
+#endif  // COBALT_MEDIA_PROGRESSIVE_PROGRESSIVE_DEMUXER_H_
diff --git a/src/cobalt/media/filters/shell_parser.cc b/src/cobalt/media/progressive/progressive_parser.cc
similarity index 71%
rename from src/cobalt/media/filters/shell_parser.cc
rename to src/cobalt/media/progressive/progressive_parser.cc
index 3ff978e..f9ffb84 100644
--- a/src/cobalt/media/filters/shell_parser.cc
+++ b/src/cobalt/media/progressive/progressive_parser.cc
@@ -12,24 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/filters/shell_parser.h"
+#include "cobalt/media/progressive/progressive_parser.h"
 
 #include "base/logging.h"
 #include "cobalt/media/base/timestamp_constants.h"
-#include "cobalt/media/filters/shell_mp4_parser.h"
+#include "cobalt/media/progressive/mp4_parser.h"
 
 namespace cobalt {
 namespace media {
 
-// ==== ShellParser ============================================================
+// ==== ProgressiveParser
+// ============================================================
 
 // how many bytes to download of the file to determine type?
-const int ShellParser::kInitialHeaderSize = 9;
+const int ProgressiveParser::kInitialHeaderSize = 9;
 
 // static
-PipelineStatus ShellParser::Construct(
-    scoped_refptr<ShellDataSourceReader> reader,
-    scoped_refptr<ShellParser>* parser,
+PipelineStatus ProgressiveParser::Construct(
+    scoped_refptr<DataSourceReader> reader,
+    scoped_refptr<ProgressiveParser>* parser,
     const scoped_refptr<MediaLog>& media_log) {
   DCHECK(parser);
   DCHECK(media_log);
@@ -44,15 +45,15 @@
   }
 
   // attempt to construct mp4 parser from this header
-  return ShellMP4Parser::Construct(reader, header, parser, media_log);
+  return MP4Parser::Construct(reader, header, parser, media_log);
 }
 
-ShellParser::ShellParser(scoped_refptr<ShellDataSourceReader> reader)
+ProgressiveParser::ProgressiveParser(scoped_refptr<DataSourceReader> reader)
     : reader_(reader), duration_(kInfiniteDuration), bits_per_second_(0) {}
 
-ShellParser::~ShellParser() {}
+ProgressiveParser::~ProgressiveParser() {}
 
-bool ShellParser::IsConfigComplete() {
+bool ProgressiveParser::IsConfigComplete() {
   return video_config_.IsValidConfig() && audio_config_.IsValidConfig() &&
          duration_ != kInfiniteDuration;
 }
diff --git a/src/cobalt/media/filters/shell_parser.h b/src/cobalt/media/progressive/progressive_parser.h
similarity index 77%
rename from src/cobalt/media/filters/shell_parser.h
rename to src/cobalt/media/progressive/progressive_parser.h
index 119a20c..a0e6f41 100644
--- a/src/cobalt/media/filters/shell_parser.h
+++ b/src/cobalt/media/progressive/progressive_parser.h
@@ -12,31 +12,31 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_FILTERS_SHELL_PARSER_H_
-#define COBALT_MEDIA_FILTERS_SHELL_PARSER_H_
-
+#ifndef COBALT_MEDIA_PROGRESSIVE_PARSER_H_
+#define COBALT_MEDIA_PROGRESSIVE_PARSER_H_
 #include "base/memory/ref_counted.h"
 #include "cobalt/media/base/audio_decoder_config.h"
 #include "cobalt/media/base/demuxer_stream.h"
 #include "cobalt/media/base/media_log.h"
 #include "cobalt/media/base/pipeline.h"
-#include "cobalt/media/base/shell_data_source_reader.h"
 #include "cobalt/media/base/video_decoder_config.h"
-#include "cobalt/media/filters/shell_au.h"
+#include "cobalt/media/progressive/avc_access_unit.h"
+#include "cobalt/media/progressive/data_source_reader.h"
 
 namespace cobalt {
 namespace media {
 
-// abstract base class to define a stream parser interface used by ShellDemuxer.
-class ShellParser : public base::RefCountedThreadSafe<ShellParser> {
+// abstract base class to define a stream parser interface used by
+// ProgressiveDemuxer.
+class ProgressiveParser : public base::RefCountedThreadSafe<ProgressiveParser> {
  public:
   static const int kInitialHeaderSize;
   // Determine stream type, construct appropriate parser object, and returns
   // PIPELINE_OK on success or error code.
-  static PipelineStatus Construct(scoped_refptr<ShellDataSourceReader> reader,
-                                  scoped_refptr<ShellParser>* parser,
+  static PipelineStatus Construct(scoped_refptr<DataSourceReader> reader,
+                                  scoped_refptr<ProgressiveParser>* parser,
                                   const scoped_refptr<MediaLog>& media_log);
-  explicit ShellParser(scoped_refptr<ShellDataSourceReader> reader);
+  explicit ProgressiveParser(scoped_refptr<DataSourceReader> reader);
 
   // Seek through the file looking for audio and video configuration info,
   // saving as much config state as is possible. Should try to be fast but this
@@ -47,10 +47,10 @@
   // downloding and decoding the next access unit in the stream, or NULL on
   // fatal error. On success this advances the respective audio or video cursor
   // to the next AU.
-  virtual scoped_refptr<ShellAU> GetNextAU(DemuxerStream::Type type) = 0;
+  virtual scoped_refptr<AvcAccessUnit> GetNextAU(DemuxerStream::Type type) = 0;
   // Write the appropriate prepend header for the supplied au into the supplied
   // buffer. Return false on error.
-  virtual bool Prepend(scoped_refptr<ShellAU> au,
+  virtual bool Prepend(scoped_refptr<AvcAccessUnit> au,
                        scoped_refptr<DecoderBuffer> buffer) = 0;
   // Advance internal state to provided timestamp. Return false on error.
   virtual bool SeekTo(base::TimeDelta timestamp) = 0;
@@ -68,9 +68,9 @@
 
  protected:
   // only allow RefCountedThreadSafe to delete us
-  friend class base::RefCountedThreadSafe<ShellParser>;
-  virtual ~ShellParser();
-  scoped_refptr<ShellDataSourceReader> reader_;
+  friend class base::RefCountedThreadSafe<ProgressiveParser>;
+  virtual ~ProgressiveParser();
+  scoped_refptr<DataSourceReader> reader_;
   AudioDecoderConfig audio_config_;
   VideoDecoderConfig video_config_;
   base::TimeDelta duration_;
@@ -80,4 +80,4 @@
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_FILTERS_SHELL_PARSER_H_
+#endif  // COBALT_MEDIA_PROGRESSIVE_PARSER_H_
diff --git a/src/cobalt/media/filters/shell_rbsp_stream.cc b/src/cobalt/media/progressive/rbsp_stream.cc
similarity index 90%
rename from src/cobalt/media/filters/shell_rbsp_stream.cc
rename to src/cobalt/media/progressive/rbsp_stream.cc
index 6a3fb50..ea90f7a 100644
--- a/src/cobalt/media/filters/shell_rbsp_stream.cc
+++ b/src/cobalt/media/progressive/rbsp_stream.cc
@@ -12,15 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/filters/shell_rbsp_stream.h"
+#include "cobalt/media/progressive/rbsp_stream.h"
 
 #include "base/logging.h"
 
 namespace cobalt {
 namespace media {
 
-ShellRBSPStream::ShellRBSPStream(const uint8* nalu_buffer,
-                                 size_t nalu_buffer_size)
+RBSPStream::RBSPStream(const uint8* nalu_buffer, size_t nalu_buffer_size)
     : nalu_buffer_(nalu_buffer),
       nalu_buffer_size_(nalu_buffer_size),
       nalu_buffer_byte_offset_(0),
@@ -29,7 +28,7 @@
       rbsp_bit_offset_(0) {}
 
 // read unsigned Exp-Golomb coded integer, ISO 14496-10 Section 9.1
-bool ShellRBSPStream::ReadUEV(uint32* uev_out) {
+bool RBSPStream::ReadUEV(uint32* uev_out) {
   DCHECK(uev_out);
   int leading_zero_bits = -1;
   for (uint8 b = 0; b == 0; leading_zero_bits++) {
@@ -52,7 +51,7 @@
 }
 
 // read signed Exp-Golomb coded integer, ISO 14496-10 Section 9.1
-bool ShellRBSPStream::ReadSEV(int32* sev_out) {
+bool RBSPStream::ReadSEV(int32* sev_out) {
   DCHECK(sev_out);
   // we start off by reading an unsigned Exp-Golomb coded number
   uint32 uev = 0;
@@ -71,7 +70,7 @@
 
 // read and return up to 32 bits, filling from the right, meaning that
 // ReadBits(17) on a stream of all 1s would return 0x01ffff
-bool ShellRBSPStream::ReadBits(size_t bits, uint32* bits_out) {
+bool RBSPStream::ReadBits(size_t bits, uint32* bits_out) {
   DCHECK(bits_out);
   if (bits > 32) {
     return false;
@@ -105,7 +104,7 @@
 }
 
 // jump over bytes in the RBSP stream
-bool ShellRBSPStream::SkipBytes(size_t bytes) {
+bool RBSPStream::SkipBytes(size_t bytes) {
   for (int i = 0; i < bytes; ++i) {
     if (!ConsumeNALUByte()) {
       return false;
@@ -115,7 +114,7 @@
 }
 
 // jump over bits in the RBSP stream
-bool ShellRBSPStream::SkipBits(size_t bits) {
+bool RBSPStream::SkipBits(size_t bits) {
   // skip bytes first
   size_t bytes = bits >> 3;
   if (bytes > 0) {
@@ -152,7 +151,7 @@
 
 // advance by one byte through the NALU buffer, respecting the encoding of
 // 00 00 03 => 00 00. Updates the state of current_nalu_byte_ to the new value.
-bool ShellRBSPStream::ConsumeNALUByte() {
+bool RBSPStream::ConsumeNALUByte() {
   if (nalu_buffer_byte_offset_ >= nalu_buffer_size_) {
     return false;
   }
@@ -174,7 +173,7 @@
 
 // return single bit in the LSb from the RBSP stream. Bits are read from MSb
 // to LSb in the stream.
-bool ShellRBSPStream::ReadRBSPBit(uint8* bit_out) {
+bool RBSPStream::ReadRBSPBit(uint8* bit_out) {
   DCHECK(bit_out);
   // check to see if we need to consume a fresh byte
   if (rbsp_bit_offset_ == 0) {
@@ -190,7 +189,7 @@
   return true;
 }
 
-bool ShellRBSPStream::ReadRBSPByte(uint8* byte_out) {
+bool RBSPStream::ReadRBSPByte(uint8* byte_out) {
   DCHECK(byte_out);
   // fast path for byte-aligned access
   if (rbsp_bit_offset_ == 0) {
diff --git a/src/cobalt/media/filters/shell_rbsp_stream.h b/src/cobalt/media/progressive/rbsp_stream.h
similarity index 91%
rename from src/cobalt/media/filters/shell_rbsp_stream.h
rename to src/cobalt/media/progressive/rbsp_stream.h
index 5a46499..0c8d8bc 100644
--- a/src/cobalt/media/filters/shell_rbsp_stream.h
+++ b/src/cobalt/media/progressive/rbsp_stream.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef COBALT_MEDIA_FILTERS_SHELL_RBSP_STREAM_H_
-#define COBALT_MEDIA_FILTERS_SHELL_RBSP_STREAM_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_RBSP_STREAM_H_
+#define COBALT_MEDIA_PROGRESSIVE_RBSP_STREAM_H_
 
 #include "base/basictypes.h"
 
@@ -25,11 +25,11 @@
 // that some other atoms are defined. This class takes a non-owning reference
 // to a buffer and extract various types from the stream while silently
 // consuming the extra encoding bytes and advancing a bit stream pointer.
-class ShellRBSPStream {
+class RBSPStream {
  public:
   // NON-OWNING pointer to buffer. It is assumed the client will dispose of
   // this buffer.
-  ShellRBSPStream(const uint8* nalu_buffer, size_t nalu_buffer_size);
+  RBSPStream(const uint8* nalu_buffer, size_t nalu_buffer_size);
   // all Read/Skip methods return the value by reference and return true
   // on success, false on read error/EOB. Once the object has returned
   // false the consistency of the data is not guaranteed.
@@ -70,4 +70,4 @@
 }  // namespace media
 }  // namespace cobalt
 
-#endif  // COBALT_MEDIA_FILTERS_SHELL_RBSP_STREAM_H_
+#endif  // COBALT_MEDIA_PROGRESSIVE_RBSP_STREAM_H_
diff --git a/src/cobalt/media/filters/shell_rbsp_stream_unittest.cc b/src/cobalt/media/progressive/rbsp_stream_unittest.cc
similarity index 89%
rename from src/cobalt/media/filters/shell_rbsp_stream_unittest.cc
rename to src/cobalt/media/progressive/rbsp_stream_unittest.cc
index 472639b..33669ed 100644
--- a/src/cobalt/media/filters/shell_rbsp_stream_unittest.cc
+++ b/src/cobalt/media/progressive/rbsp_stream_unittest.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "cobalt/media/filters/shell_rbsp_stream.h"
+#include "cobalt/media/progressive/rbsp_stream.h"
 
 #include <list>
 #include <memory>
@@ -24,11 +24,11 @@
 namespace cobalt {
 namespace media {
 
-class ShellRBSPStreamTest : public testing::Test {
+class RBSPStreamTest : public testing::Test {
  protected:
-  ShellRBSPStreamTest() {}
+  RBSPStreamTest() {}
 
-  virtual ~ShellRBSPStreamTest() {}
+  virtual ~RBSPStreamTest() {}
 
   // Given num encode the value in signed exp-golomb syntax and push
   // the value on the provided bitlist
@@ -76,7 +76,7 @@
   }
 
   // after building a bitlist in various fun ways call this method to
-  // create a buffer on the heap that can be passed to ShellRBSPStream
+  // create a buffer on the heap that can be passed to RBSPStream
   // for deserialization.
   std::unique_ptr<uint8[]> SerializeToBuffer(const std::list<bool>& bitlist,
                                              bool add_sequence_bytes,
@@ -150,7 +150,7 @@
   }
 };
 
-TEST_F(ShellRBSPStreamTest, ReadUEV) {
+TEST_F(RBSPStreamTest, ReadUEV) {
   std::list<bool> fibbits;
   // encode first 47 Fibonacci numbers
   uint32 f_n_minus_2 = 0;
@@ -171,9 +171,9 @@
   size_t fib_buffer_no_sequence_size;
   std::unique_ptr<uint8[]> fib_buffer_no_sequence =
       SerializeToBuffer(fibbits, false, fib_buffer_no_sequence_size);
-  ShellRBSPStream fib_stream(fib_buffer.get(), fib_buffer_size);
-  ShellRBSPStream fib_stream_no_sequence(fib_buffer_no_sequence.get(),
-                                         fib_buffer_no_sequence_size);
+  RBSPStream fib_stream(fib_buffer.get(), fib_buffer_size);
+  RBSPStream fib_stream_no_sequence(fib_buffer_no_sequence.get(),
+                                    fib_buffer_no_sequence_size);
   // deserialize the same sequence from both buffers
   uint32 uev = 0;
   uint32 uev_n = 0;
@@ -203,7 +203,7 @@
   ASSERT_FALSE(fib_stream_no_sequence.ReadUEV(&uev_n));
 }
 
-TEST_F(ShellRBSPStreamTest, ReadSEV) {
+TEST_F(RBSPStreamTest, ReadSEV) {
   std::list<bool> lucasbits;
   // encode first 44 Lucas numbers with alternating sign
   int32 l_n_minus_2 = 1;
@@ -227,10 +227,9 @@
   size_t lucas_deseq_buffer_size = 0;
   std::unique_ptr<uint8[]> lucas_deseq_buffer =
       SerializeToBuffer(lucasbits, false, lucas_deseq_buffer_size);
-  ShellRBSPStream lucas_seq_stream(lucas_seq_buffer.get(),
-                                   lucas_seq_buffer_size);
-  ShellRBSPStream lucas_deseq_stream(lucas_deseq_buffer.get(),
-                                     lucas_deseq_buffer_size);
+  RBSPStream lucas_seq_stream(lucas_seq_buffer.get(), lucas_seq_buffer_size);
+  RBSPStream lucas_deseq_stream(lucas_deseq_buffer.get(),
+                                lucas_deseq_buffer_size);
   l_n_minus_2 = 1;
   l_n_minus_1 = 2;
   int32 sev = 0;
@@ -286,10 +285,10 @@
     // 1111 111+0 (to complete the byte)
     0xfe};
 
-TEST_F(ShellRBSPStreamTest, ReadUEVTooLarge) {
+TEST_F(RBSPStreamTest, ReadUEVTooLarge) {
   // construct a stream from the supplied test data
-  ShellRBSPStream uev_too_big(kTestRBSPExpGolombTooBig,
-                              sizeof(kTestRBSPExpGolombTooBig));
+  RBSPStream uev_too_big(kTestRBSPExpGolombTooBig,
+                         sizeof(kTestRBSPExpGolombTooBig));
   // first call should succeed
   uint32 uev = 0;
   ASSERT_TRUE(uev_too_big.ReadUEV(&uev));
@@ -301,10 +300,10 @@
   ASSERT_FALSE(uev_too_big.ReadUEV(&uev));
 }
 
-TEST_F(ShellRBSPStreamTest, ReadSEVTooLarge) {
+TEST_F(RBSPStreamTest, ReadSEVTooLarge) {
   // construct a stream from the supplied test data
-  ShellRBSPStream sev_too_big(kTestRBSPExpGolombTooBig,
-                              sizeof(kTestRBSPExpGolombTooBig));
+  RBSPStream sev_too_big(kTestRBSPExpGolombTooBig,
+                         sizeof(kTestRBSPExpGolombTooBig));
   // first call should succeed
   int32 sev = 0;
   ASSERT_TRUE(sev_too_big.ReadSEV(&sev));
@@ -316,7 +315,7 @@
   ASSERT_FALSE(sev_too_big.ReadSEV(&sev));
 }
 
-TEST_F(ShellRBSPStreamTest, ReadBit) {
+TEST_F(RBSPStreamTest, ReadBit) {
   std::list<bool> padded_ones;
   // build a bitfield of 1 padded by n zeros, for n in range[0, 1024]
   for (int i = 0; i < 1024; i++) {
@@ -329,12 +328,12 @@
   size_t sequence_buff_size = 0;
   std::unique_ptr<uint8[]> sequence_buff =
       SerializeToBuffer(padded_ones, true, sequence_buff_size);
-  ShellRBSPStream seq_stream(sequence_buff.get(), sequence_buff_size);
+  RBSPStream seq_stream(sequence_buff.get(), sequence_buff_size);
 
   size_t desequence_buff_size = 0;
   std::unique_ptr<uint8[]> desequence_buff =
       SerializeToBuffer(padded_ones, false, desequence_buff_size);
-  ShellRBSPStream deseq_stream(desequence_buff.get(), desequence_buff_size);
+  RBSPStream deseq_stream(desequence_buff.get(), desequence_buff_size);
   for (std::list<bool>::iterator it = padded_ones.begin();
        it != padded_ones.end(); ++it) {
     uint8 bit = 0;
@@ -351,7 +350,7 @@
   ASSERT_FALSE(deseq_stream.ReadByte(&fail_byte));
 }
 
-TEST_F(ShellRBSPStreamTest, ReadByte) {
+TEST_F(RBSPStreamTest, ReadByte) {
   // build a field of 16 x (0xaa byte followed by 0 bit)
   std::list<bool> aa_field;
   for (int i = 0; i < 16; ++i) {
@@ -364,7 +363,7 @@
   size_t aabuff_size = 0;
   std::unique_ptr<uint8[]> aabuff =
       SerializeToBuffer(aa_field, true, aabuff_size);
-  ShellRBSPStream aa_stream(aabuff.get(), aabuff_size);
+  RBSPStream aa_stream(aabuff.get(), aabuff_size);
   for (int i = 0; i < 16; ++i) {
     uint8 aa = 0;
     ASSERT_TRUE(aa_stream.ReadByte(&aa));
@@ -397,11 +396,11 @@
   size_t zseqbuff_size = 0;
   std::unique_ptr<uint8[]> zseqbuff =
       SerializeToBuffer(zero_field, true, zseqbuff_size);
-  ShellRBSPStream zseq_stream(zseqbuff.get(), zseqbuff_size);
+  RBSPStream zseq_stream(zseqbuff.get(), zseqbuff_size);
   size_t zdseqbuff_size = 0;
   std::unique_ptr<uint8[]> zdseqbuff =
       SerializeToBuffer(zero_field, false, zdseqbuff_size);
-  ShellRBSPStream zdseq_stream(zdseqbuff.get(), zdseqbuff_size);
+  RBSPStream zdseq_stream(zdseqbuff.get(), zdseqbuff_size);
   for (int i = 0; i < 24; ++i) {
     // read the leading 1 bit
     uint8 seq_bit = 0;
@@ -454,7 +453,7 @@
   }
 }
 
-TEST_F(ShellRBSPStreamTest, ReadBits) {
+TEST_F(RBSPStreamTest, ReadBits) {
   // test the assertion in the ReadBits comment, as it had a bug :)
   std::list<bool> seventeen_ones;
   for (int i = 0; i < 17; ++i) {
@@ -463,8 +462,8 @@
   size_t seventeen_ones_size = 0;
   std::unique_ptr<uint8[]> seventeen_ones_buff =
       SerializeToBuffer(seventeen_ones, false, seventeen_ones_size);
-  ShellRBSPStream seventeen_ones_stream(seventeen_ones_buff.get(),
-                                        seventeen_ones_size);
+  RBSPStream seventeen_ones_stream(seventeen_ones_buff.get(),
+                                   seventeen_ones_size);
   uint32 seventeen_ones_word = 0;
   ASSERT_TRUE(seventeen_ones_stream.ReadBits(17, &seventeen_ones_word));
   ASSERT_EQ(seventeen_ones_word, 0x0001ffff);
@@ -479,7 +478,7 @@
   }
   size_t pows_size = 0;
   std::unique_ptr<uint8[]> pows_buff = SerializeToBuffer(pows, true, pows_size);
-  ShellRBSPStream pows_stream(pows_buff.get(), pows_size);
+  RBSPStream pows_stream(pows_buff.get(), pows_size);
   // ReadBits(0) should succeed and not modify the value of the ref output or
   // internal bit iterator
   uint32 dont_touch = 0xfeedfeed;
@@ -493,7 +492,7 @@
   }
 }
 
-TEST_F(ShellRBSPStreamTest, SkipBytes) {
+TEST_F(RBSPStreamTest, SkipBytes) {
   // serialize all nine-bit values from zero to 512
   std::list<bool> nines;
   for (int i = 0; i < 512; ++i) {
@@ -507,8 +506,8 @@
   size_t nines_deseq_size = 0;
   std::unique_ptr<uint8[]> nines_deseq_buff =
       SerializeToBuffer(nines, false, nines_deseq_size);
-  ShellRBSPStream nines_stream(nines_buff.get(), nines_size);
-  ShellRBSPStream nines_deseq_stream(nines_deseq_buff.get(), nines_deseq_size);
+  RBSPStream nines_stream(nines_buff.get(), nines_size);
+  RBSPStream nines_deseq_stream(nines_deseq_buff.get(), nines_deseq_size);
   // iterate through streams, skipping in one and reading in the other, always
   // comparing values.
   for (int i = 0; i < 512; ++i) {
@@ -547,9 +546,9 @@
   size_t run_length_deseq_size = 0;
   std::unique_ptr<uint8[]> run_length_deseq_buff =
       SerializeToBuffer(run_length, false, run_length_deseq_size);
-  ShellRBSPStream run_length_stream(run_length_buff.get(), run_length_size);
-  ShellRBSPStream run_length_deseq_stream(run_length_deseq_buff.get(),
-                                          run_length_deseq_size);
+  RBSPStream run_length_stream(run_length_buff.get(), run_length_size);
+  RBSPStream run_length_deseq_stream(run_length_deseq_buff.get(),
+                                     run_length_deseq_size);
   // read first bit, skip first byte from each stream, read next bit
   uint8 bit = 0;
   ASSERT_TRUE(run_length_stream.ReadBit(&bit));
@@ -593,7 +592,7 @@
   ASSERT_FALSE(run_length_deseq_stream.SkipBytes(1));
 }
 
-TEST_F(ShellRBSPStreamTest, SkipBits) {
+TEST_F(RBSPStreamTest, SkipBits) {
   std::list<bool> one_ohs;
   // encode one 1, followed by one zero, followed by 2 1s, followed by 2 zeros,
   // etc
@@ -611,8 +610,8 @@
   size_t skip_ohs_size = 0;
   std::unique_ptr<uint8[]> skip_ohs_buff =
       SerializeToBuffer(one_ohs, false, skip_ohs_size);
-  ShellRBSPStream skip_ones(skip_ones_buff.get(), skip_ones_size);
-  ShellRBSPStream skip_ohs(skip_ohs_buff.get(), skip_ohs_size);
+  RBSPStream skip_ones(skip_ones_buff.get(), skip_ones_size);
+  RBSPStream skip_ohs(skip_ohs_buff.get(), skip_ohs_size);
   for (int i = 1; i < 64; ++i) {
     // skip the ones
     ASSERT_TRUE(skip_ones.SkipBits(i));
diff --git a/src/cobalt/media/sandbox/demuxer_helper.cc b/src/cobalt/media/sandbox/demuxer_helper.cc
index fa343fa..c5b2991 100644
--- a/src/cobalt/media/sandbox/demuxer_helper.cc
+++ b/src/cobalt/media/sandbox/demuxer_helper.cc
@@ -24,7 +24,7 @@
 #include "media/base/bind_to_loop.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/video_decoder_config.h"
-#include "media/filters/shell_demuxer.h"
+#include "media/progressive/progressive_demuxer.h"
 
 namespace cobalt {
 namespace media {
@@ -263,7 +263,7 @@
                                     fetcher_factory->network_module(),
                                     loader::kNoCORSMode, loader::Origin()));
   scoped_refptr<Demuxer> demuxer =
-      new ::media::ShellDemuxer(media_message_loop, data_source);
+      new ::media::ProgressiveDemuxer(media_message_loop, data_source);
   demuxer->Initialize(
       host_, base::Bind(&DemuxerHelper::OnDemuxerReady, base::Unretained(this),
                         demuxer, demuxer_ready_cb, bytes_to_cache));
diff --git a/src/cobalt/media_capture/encoders/audio_encoder.h b/src/cobalt/media_capture/encoders/audio_encoder.h
index 63134e0..0efe09b 100644
--- a/src/cobalt/media_capture/encoders/audio_encoder.h
+++ b/src/cobalt/media_capture/encoders/audio_encoder.h
@@ -20,10 +20,10 @@
 #include "base/basictypes.h"
 #include "base/strings/string_piece.h"
 #include "base/time/time.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/media_stream/audio_parameters.h"
 #include "starboard/common/mutex.h"
 
-#include "cobalt/media/base/shell_audio_bus.h"
 
 namespace cobalt {
 namespace media_capture {
@@ -31,7 +31,7 @@
 
 class AudioEncoder {
  public:
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
   class Listener {
    public:
@@ -44,7 +44,7 @@
   virtual ~AudioEncoder() = default;
 
   // Encode raw audio data.
-  virtual void Encode(const ShellAudioBus& audio_bus,
+  virtual void Encode(const AudioBus& audio_bus,
                       base::TimeTicks reference_time) = 0;
   // Finish encoding.
   virtual void Finish(base::TimeTicks timecode) = 0;
diff --git a/src/cobalt/media_capture/encoders/flac_audio_encoder.cc b/src/cobalt/media_capture/encoders/flac_audio_encoder.cc
index 0e45933..7c57c69 100644
--- a/src/cobalt/media_capture/encoders/flac_audio_encoder.cc
+++ b/src/cobalt/media_capture/encoders/flac_audio_encoder.cc
@@ -59,11 +59,11 @@
       base::Bind(&FlacAudioEncoder::DestroyEncoder, base::Unretained(this)));
 }
 
-void FlacAudioEncoder::Encode(const ShellAudioBus& audio_bus,
+void FlacAudioEncoder::Encode(const AudioBus& audio_bus,
                               base::TimeTicks reference_time) {
-  std::unique_ptr<ShellAudioBus> audio_bus_copy(
-      new ShellAudioBus(audio_bus.channels(), audio_bus.frames(),
-                        audio_bus.sample_type(), audio_bus.storage_type()));
+  std::unique_ptr<AudioBus> audio_bus_copy(
+      new AudioBus(audio_bus.channels(), audio_bus.frames(),
+                   audio_bus.sample_type(), audio_bus.storage_type()));
   audio_bus_copy->Assign(audio_bus);
 
   // base::Unretained usage is safe here, since we're posting to a thread that
@@ -107,7 +107,7 @@
 
 void FlacAudioEncoder::DestroyEncoder() { flac_encoder_.reset(); }
 
-void FlacAudioEncoder::DoEncode(std::unique_ptr<ShellAudioBus> audio_bus,
+void FlacAudioEncoder::DoEncode(std::unique_ptr<AudioBus> audio_bus,
                                 base::TimeTicks reference_time) {
   DCHECK(flac_encoder_);
   flac_encoder_->Encode(audio_bus.get());
diff --git a/src/cobalt/media_capture/encoders/flac_audio_encoder.h b/src/cobalt/media_capture/encoders/flac_audio_encoder.h
index 90d4bd4..a982eaf 100644
--- a/src/cobalt/media_capture/encoders/flac_audio_encoder.h
+++ b/src/cobalt/media_capture/encoders/flac_audio_encoder.h
@@ -36,7 +36,7 @@
 
   // Called from the thread the object is constructed on (usually
   // a dedicated audio thread).
-  void Encode(const ShellAudioBus& audio_bus,
+  void Encode(const AudioBus& audio_bus,
               base::TimeTicks reference_time) override;
 
   // This can be called from any thread
@@ -54,7 +54,7 @@
   // These functions are called on the encoder thread.
   void CreateEncoder(const media_stream::AudioParameters& params);
   void DestroyEncoder();
-  void DoEncode(std::unique_ptr<ShellAudioBus> audio_bus,
+  void DoEncode(std::unique_ptr<AudioBus> audio_bus,
                 base::TimeTicks reference_time);
   void DoFinish(base::TimeTicks reference_time);
 
diff --git a/src/cobalt/media_capture/encoders/linear16_audio_encoder.cc b/src/cobalt/media_capture/encoders/linear16_audio_encoder.cc
index f18f035..c5148ac 100644
--- a/src/cobalt/media_capture/encoders/linear16_audio_encoder.cc
+++ b/src/cobalt/media_capture/encoders/linear16_audio_encoder.cc
@@ -43,9 +43,9 @@
   return match_iterator == mime_type_container.begin();
 }
 
-void Linear16AudioEncoder::Encode(const ShellAudioBus& audio_bus,
+void Linear16AudioEncoder::Encode(const AudioBus& audio_bus,
                                   base::TimeTicks reference_time) {
-  DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
+  DCHECK_EQ(audio_bus.sample_type(), AudioBus::kInt16);
   DCHECK_EQ(audio_bus.channels(), size_t(1));
   auto data = audio_bus.interleaved_data();
   size_t data_size = audio_bus.GetSampleSizeInBytes() * audio_bus.frames();
diff --git a/src/cobalt/media_capture/encoders/linear16_audio_encoder.h b/src/cobalt/media_capture/encoders/linear16_audio_encoder.h
index bb5f5d7..75040f4 100644
--- a/src/cobalt/media_capture/encoders/linear16_audio_encoder.h
+++ b/src/cobalt/media_capture/encoders/linear16_audio_encoder.h
@@ -29,7 +29,7 @@
   static bool IsLinear16MIMEType(const base::StringPiece& mime_type);
 
   Linear16AudioEncoder() = default;
-  void Encode(const ShellAudioBus& audio_bus,
+  void Encode(const AudioBus& audio_bus,
               base::TimeTicks reference_time) override;
   void Finish(base::TimeTicks reference_time) override;
   std::string GetMimeType() const override;
diff --git a/src/cobalt/media_capture/media_recorder.cc b/src/cobalt/media_capture/media_recorder.cc
index 3a015f0..6642bfc 100644
--- a/src/cobalt/media_capture/media_recorder.cc
+++ b/src/cobalt/media_capture/media_recorder.cc
@@ -161,10 +161,10 @@
   StopRecording();
 }
 
-void MediaRecorder::OnData(const ShellAudioBus& audio_bus,
+void MediaRecorder::OnData(const AudioBus& audio_bus,
                            base::TimeTicks reference_time) {
   // The source is always int16 data from the microphone.
-  DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
+  DCHECK_EQ(audio_bus.sample_type(), AudioBus::kInt16);
   DCHECK_EQ(audio_bus.channels(), size_t(1));
   DCHECK(audio_encoder_);
   audio_encoder_->Encode(audio_bus, reference_time);
diff --git a/src/cobalt/media_capture/media_recorder.h b/src/cobalt/media_capture/media_recorder.h
index a7b1c9d..5883af8 100644
--- a/src/cobalt/media_capture/media_recorder.h
+++ b/src/cobalt/media_capture/media_recorder.h
@@ -118,7 +118,7 @@
   RecordingState state() { return recording_state_; }
 
   // MediaStreamAudioSink overrides.
-  void OnData(const ShellAudioBus& audio_bus,
+  void OnData(const AudioBus& audio_bus,
               base::TimeTicks reference_time) override;
   void OnSetFormat(const media_stream::AudioParameters& params) override;
   void OnReadyStateChanged(
diff --git a/src/cobalt/media_capture/media_recorder_test.cc b/src/cobalt/media_capture/media_recorder_test.cc
index cfc5e3c..46fab4a 100644
--- a/src/cobalt/media_capture/media_recorder_test.cc
+++ b/src/cobalt/media_capture/media_recorder_test.cc
@@ -52,7 +52,7 @@
   frames.push_back(32767);
   frames.push_back(1000);
   frames.push_back(0);
-  cobalt::media_stream::MediaStreamAudioTrack::ShellAudioBus audio_bus(
+  cobalt::media_stream::MediaStreamAudioTrack::AudioBus audio_bus(
       1, frames.size(), frames.data());
   base::TimeTicks current_time = base::TimeTicks::Now();
   media_recorder->OnData(audio_bus, current_time);
@@ -68,9 +68,8 @@
   MOCK_METHOD0(EnsureSourceIsStarted, bool());
   MOCK_METHOD0(EnsureSourceIsStopped, void());
 
-  void DeliverDataToTracks(
-      const MediaStreamAudioTrack::ShellAudioBus& audio_bus,
-      base::TimeTicks reference_time) {
+  void DeliverDataToTracks(const MediaStreamAudioTrack::AudioBus& audio_bus,
+                           base::TimeTicks reference_time) {
     MediaStreamAudioSource::DeliverDataToTracks(audio_bus, reference_time);
   }
 
@@ -191,8 +190,8 @@
   frames.push_back(32767);
   frames.push_back(1000);
   frames.push_back(0);
-  media_stream::MediaStreamAudioTrack::ShellAudioBus audio_bus(1, frames.size(),
-                                                               frames.data());
+  media_stream::MediaStreamAudioTrack::AudioBus audio_bus(1, frames.size(),
+                                                          frames.data());
   base::TimeTicks current_time = base::TimeTicks::Now();
   media_recorder_->OnData(audio_bus, current_time);
   current_time += base::TimeDelta::FromSecondsD(frames.size() / kSampleRate);
diff --git a/src/cobalt/media_stream/media_stream_audio_deliverer.h b/src/cobalt/media_stream/media_stream_audio_deliverer.h
index 40628d4..b942bdd 100644
--- a/src/cobalt/media_stream/media_stream_audio_deliverer.h
+++ b/src/cobalt/media_stream/media_stream_audio_deliverer.h
@@ -26,9 +26,10 @@
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
+
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/media_stream/audio_parameters.h"
 
-#include "cobalt/media/base/shell_audio_bus.h"
 
 namespace cobalt {
 namespace media_stream {
@@ -119,7 +120,7 @@
 
 // Deliver data to all consumers. This method may be called on any thread.
 
-  void OnData(const media::ShellAudioBus& audio_bus,
+  void OnData(const media::AudioBus& audio_bus,
               base::TimeTicks reference_time) {
     TRACE_EVENT1("media_stream", "MediaStreamAudioDeliverer::OnData",
                  "reference time (ms)",
diff --git a/src/cobalt/media_stream/media_stream_audio_sink.h b/src/cobalt/media_stream/media_stream_audio_sink.h
index 680bbd3..034dbec 100644
--- a/src/cobalt/media_stream/media_stream_audio_sink.h
+++ b/src/cobalt/media_stream/media_stream_audio_sink.h
@@ -15,12 +15,13 @@
 #ifndef COBALT_MEDIA_STREAM_MEDIA_STREAM_AUDIO_SINK_H_
 #define COBALT_MEDIA_STREAM_MEDIA_STREAM_AUDIO_SINK_H_
 
+#include "cobalt/media/base/audio_bus.h"
+
 #include "cobalt/media_stream/audio_parameters.h"
 
 #include "cobalt/media_stream/media_stream_source.h"
 #include "cobalt/media_stream/media_stream_track.h"
 
-#include "cobalt/media/base/shell_audio_bus.h"
 
 namespace cobalt {
 namespace media_stream {
@@ -29,11 +30,11 @@
 // Note: users of this class will call OnSetFormat is before OnData.
 class MediaStreamAudioSink {
  public:
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
   MediaStreamAudioSink() = default;
 
   // These are called on the same thread.
-  virtual void OnData(const ShellAudioBus& audio_bus,
+  virtual void OnData(const AudioBus& audio_bus,
                       base::TimeTicks reference_time) = 0;
   virtual void OnSetFormat(const media_stream::AudioParameters& params) = 0;
   virtual void OnReadyStateChanged(
diff --git a/src/cobalt/media_stream/media_stream_audio_source.cc b/src/cobalt/media_stream/media_stream_audio_source.cc
index 6c20d49..2d15393 100644
--- a/src/cobalt/media_stream/media_stream_audio_source.cc
+++ b/src/cobalt/media_stream/media_stream_audio_source.cc
@@ -60,7 +60,7 @@
 }
 
 void MediaStreamAudioSource::DeliverDataToTracks(
-    const MediaStreamAudioTrack::ShellAudioBus& audio_bus,
+    const MediaStreamAudioTrack::AudioBus& audio_bus,
     base::TimeTicks reference_time) {
   deliverer_.OnData(audio_bus, reference_time);
 }
diff --git a/src/cobalt/media_stream/media_stream_audio_source.h b/src/cobalt/media_stream/media_stream_audio_source.h
index 77c00d7..69d789c 100644
--- a/src/cobalt/media_stream/media_stream_audio_source.h
+++ b/src/cobalt/media_stream/media_stream_audio_source.h
@@ -58,9 +58,8 @@
   // Subclasses should call these methods. |DeliverDataToTracks| can be
   // called from a different thread than where MediaStreamAudioSource
   // was created.
-  void DeliverDataToTracks(
-      const MediaStreamAudioTrack::ShellAudioBus& audio_bus,
-      base::TimeTicks reference_time);
+  void DeliverDataToTracks(const MediaStreamAudioTrack::AudioBus& audio_bus,
+                           base::TimeTicks reference_time);
 
   void SetFormat(const media_stream::AudioParameters& params) {
     DLOG(INFO) << "MediaStreamAudioSource@" << this << "::SetFormat("
diff --git a/src/cobalt/media_stream/media_stream_audio_source_test.cc b/src/cobalt/media_stream/media_stream_audio_source_test.cc
index f526bba..4157d3d 100644
--- a/src/cobalt/media_stream/media_stream_audio_source_test.cc
+++ b/src/cobalt/media_stream/media_stream_audio_source_test.cc
@@ -17,7 +17,7 @@
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/media_stream/media_stream_audio_track.h"
 #include "cobalt/media_stream/testing/mock_media_stream_audio_source.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -35,7 +35,7 @@
 namespace cobalt {
 namespace media_stream {
 
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
 
 class MediaStreamAudioSourceTest : public testing::Test {
  public:
diff --git a/src/cobalt/media_stream/media_stream_audio_track.cc b/src/cobalt/media_stream/media_stream_audio_track.cc
index c6deb82..0823274 100644
--- a/src/cobalt/media_stream/media_stream_audio_track.cc
+++ b/src/cobalt/media_stream/media_stream_audio_track.cc
@@ -69,7 +69,7 @@
   stop_callback_ = stop_callback;
 }
 
-void MediaStreamAudioTrack::OnData(const ShellAudioBus& audio_bus,
+void MediaStreamAudioTrack::OnData(const AudioBus& audio_bus,
                                    base::TimeTicks reference_time) {
   deliverer_.OnData(audio_bus, reference_time);
 }
diff --git a/src/cobalt/media_stream/media_stream_audio_track.h b/src/cobalt/media_stream/media_stream_audio_track.h
index b7d7708..f1b9b7d 100644
--- a/src/cobalt/media_stream/media_stream_audio_track.h
+++ b/src/cobalt/media_stream/media_stream_audio_track.h
@@ -19,7 +19,7 @@
 #include "base/strings/string_piece.h"
 #include "base/threading/thread_checker.h"
 #include "cobalt/dom/event_target.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/media_stream/audio_parameters.h"
 #include "cobalt/media_stream/media_stream_audio_deliverer.h"
 #include "cobalt/media_stream/media_stream_audio_sink.h"
@@ -50,7 +50,7 @@
     settings.set_sample_size(parameters.bits_per_sample());
     return settings;
   }
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
   explicit MediaStreamAudioTrack(script::EnvironmentSettings* settings)
       : MediaStreamTrack(settings) {}
 
@@ -81,7 +81,7 @@
   void Start(const base::Closure& stop_callback);
 
   // Called by MediaStreamAudioDeliverer.
-  void OnData(const ShellAudioBus& audio_bus, base::TimeTicks reference_time);
+  void OnData(const AudioBus& audio_bus, base::TimeTicks reference_time);
   void OnSetFormat(const media_stream::AudioParameters& params);
 
   THREAD_CHECKER(thread_checker_);
diff --git a/src/cobalt/media_stream/media_stream_audio_track_test.cc b/src/cobalt/media_stream/media_stream_audio_track_test.cc
index 8fc542a..5512ae2 100644
--- a/src/cobalt/media_stream/media_stream_audio_track_test.cc
+++ b/src/cobalt/media_stream/media_stream_audio_track_test.cc
@@ -16,7 +16,7 @@
 
 #include "base/bind_helpers.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/media_stream/media_stream_audio_deliverer.h"
 #include "cobalt/media_stream/testing/mock_media_stream_audio_sink.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -36,7 +36,7 @@
 namespace cobalt {
 namespace media_stream {
 
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
 
 // This fixture is created, so it can be added as a friend to
 // |MediaStreamAudioTrack|.  This enables calling a private method
@@ -71,8 +71,8 @@
 
   deliverer.OnSetFormat(expected_params);
 
-  ShellAudioBus audio_bus(kChannelCount, kFrameCount, ShellAudioBus::kInt16,
-                          ShellAudioBus::kInterleaved);
+  AudioBus audio_bus(kChannelCount, kFrameCount, AudioBus::kInt16,
+                     AudioBus::kInterleaved);
 
   deliverer.OnData(audio_bus, expected_time);
 }
@@ -104,8 +104,8 @@
 
   deliverer.OnSetFormat(expected_params);
 
-  ShellAudioBus audio_bus(kChannelCount, kFrameCount, ShellAudioBus::kInt16,
-                          ShellAudioBus::kInterleaved);
+  AudioBus audio_bus(kChannelCount, kFrameCount, AudioBus::kInt16,
+                     AudioBus::kInterleaved);
 
   deliverer.OnData(audio_bus, expected_time);
 }
@@ -141,8 +141,8 @@
 
   deliverer.OnSetFormat(expected_params);
 
-  ShellAudioBus audio_bus(kChannelCount, kFrameCount, ShellAudioBus::kInt16,
-                          ShellAudioBus::kInterleaved);
+  AudioBus audio_bus(kChannelCount, kFrameCount, AudioBus::kInt16,
+                     AudioBus::kInterleaved);
 
   deliverer.OnData(audio_bus, expected_time);
 }
@@ -166,8 +166,8 @@
 
   deliverer.OnSetFormat(expected_params);
 
-  ShellAudioBus audio_bus(kChannelCount, kFrameCount, ShellAudioBus::kInt16,
-                          ShellAudioBus::kInterleaved);
+  AudioBus audio_bus(kChannelCount, kFrameCount, AudioBus::kInt16,
+                     AudioBus::kInterleaved);
 
   track->RemoveSink(&mock_sink);
   deliverer.OnData(audio_bus, expected_time);
diff --git a/src/cobalt/media_stream/microphone_audio_source.cc b/src/cobalt/media_stream/microphone_audio_source.cc
index 778223e..74eff37 100644
--- a/src/cobalt/media_stream/microphone_audio_source.cc
+++ b/src/cobalt/media_stream/microphone_audio_source.cc
@@ -100,7 +100,7 @@
                      base::Unretained(this), options))) {}
 
 void MicrophoneAudioSource::OnDataReceived(
-    std::unique_ptr<MediaStreamAudioTrack::ShellAudioBus> audio_bus) {
+    std::unique_ptr<MediaStreamAudioTrack::AudioBus> audio_bus) {
   base::TimeTicks now = base::TimeTicks::Now();
   DeliverDataToTracks(*audio_bus, now);
 }
diff --git a/src/cobalt/media_stream/microphone_audio_source.h b/src/cobalt/media_stream/microphone_audio_source.h
index d3049d7..c930298 100644
--- a/src/cobalt/media_stream/microphone_audio_source.h
+++ b/src/cobalt/media_stream/microphone_audio_source.h
@@ -70,7 +70,7 @@
       int buffer_size_bytes);
 
   void OnDataReceived(
-      std::unique_ptr<MediaStreamAudioTrack::ShellAudioBus> audio_bus);
+      std::unique_ptr<MediaStreamAudioTrack::AudioBus> audio_bus);
 
   void OnDataCompletion();
   void OnMicrophoneOpen();
diff --git a/src/cobalt/media_stream/testing/mock_media_stream_audio_sink.h b/src/cobalt/media_stream/testing/mock_media_stream_audio_sink.h
index bd11714..7fa06b6 100644
--- a/src/cobalt/media_stream/testing/mock_media_stream_audio_sink.h
+++ b/src/cobalt/media_stream/testing/mock_media_stream_audio_sink.h
@@ -24,8 +24,8 @@
 
 class MockMediaStreamAudioSink : public MediaStreamAudioSink {
  public:
-  MOCK_METHOD2(OnData, void(const ShellAudioBus& audio_bus,
-                            base::TimeTicks reference_time));
+  MOCK_METHOD2(OnData,
+               void(const AudioBus& audio_bus, base::TimeTicks reference_time));
   MOCK_METHOD1(OnSetFormat, void(const media_stream::AudioParameters& params));
   MOCK_METHOD1(OnReadyStateChanged,
                void(media_stream::MediaStreamTrack::ReadyState new_state));
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
index 09ce038..9ba721f 100644
--- a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
@@ -96,6 +96,16 @@
   SelectAtlasCache(&offscreen_atlases_, &offscreen_cache_);
   SelectAtlasCache(&offscreen_atlases_1d_, &offscreen_cache_1d_);
 
+  // Delete skottie targets that were not used in the previous render frame.
+  for (size_t index = 0; index < skottie_targets_.size();) {
+    if (skottie_targets_[index]->allocations_used == 0) {
+      skottie_targets_.erase(skottie_targets_.begin() + index);
+    } else {
+      skottie_targets_[index]->allocations_used = 0;
+      ++index;
+    }
+  }
+
   // Delete uncached targets that were not used in the previous render frame.
   for (size_t index = 0; index < uncached_targets_.size();) {
     if (uncached_targets_[index]->allocations_used == 0) {
@@ -215,6 +225,33 @@
   return false;
 }
 
+bool OffscreenTargetManager::GetCachedTarget(
+    const skia::SkottieAnimation* skottie_animation, const math::SizeF& size,
+    TargetInfo* out_target_info) {
+  // Each SkottieAnimation uses its own render target cache. The cache is keyed
+  // off the SkottieAnimation's size and pointer value. Using the pointer can
+  // be risky as an object may be destroyed and a new one created with the same
+  // pointer value. However, since each SkottieAnimation is created assuming
+  // the render cache is dirty, it is safe to accidentally use the render cache
+  // of an old SkottieAnimation with a new one.
+  int64_t id = reinterpret_cast<int64_t>(skottie_animation);
+  math::RectF target_size(std::ceil(size.width()), std::ceil(size.height()));
+  for (size_t i = 0; i < skottie_targets_.size(); ++i) {
+    OffscreenAtlas* target = skottie_targets_[i].get();
+    auto iter = target->allocation_map.find(id);
+    if (iter != target->allocation_map.end() &&
+        iter->second.error_data == target_size) {
+      target->allocations_used += 1;
+      out_target_info->framebuffer = target->framebuffer.get();
+      out_target_info->skia_canvas = target->skia_surface->getCanvas();
+      out_target_info->region = iter->second.target_region;
+      return true;
+    }
+  }
+
+  return false;
+}
+
 void OffscreenTargetManager::AllocateCachedTarget(const render_tree::Node* node,
                                                   const math::SizeF& size,
                                                   const ErrorData& error_data,
@@ -325,6 +362,34 @@
   }
 }
 
+void OffscreenTargetManager::AllocateCachedTarget(
+    const skia::SkottieAnimation* skottie_animation, const math::SizeF& size,
+    TargetInfo* out_target_info) {
+  // Each SkottieAnimation has its own offscreen atlas.
+  math::Size target_size(static_cast<int>(std::ceil(size.width())),
+                         static_cast<int>(std::ceil(size.height())));
+  OffscreenAtlas* atlas = CreateOffscreenAtlas(target_size, true).release();
+  if (!atlas) {
+    // If there was an error allocating the offscreen atlas, indicate by
+    // marking framebuffer and skia canvas as null and returning early.
+    out_target_info->framebuffer = nullptr;
+    out_target_info->skia_canvas = nullptr;
+    return;
+  }
+
+  int64_t id = reinterpret_cast<int64_t>(skottie_animation);
+  // |target_rect| must be calculated the same way as in GetCachedTarget().
+  math::RectF target_rect(std::ceil(size.width()), std::ceil(size.height()));
+  atlas->allocation_map.insert(AllocationMap::value_type(
+      id, AllocationMapValue(target_rect, target_rect)));
+  skottie_targets_.emplace_back(atlas);
+
+  atlas->allocations_used = 1;
+  out_target_info->framebuffer = atlas->framebuffer.get();
+  out_target_info->skia_canvas = atlas->skia_surface->getCanvas();
+  out_target_info->region = target_rect;
+}
+
 void OffscreenTargetManager::AllocateUncachedTarget(
     const math::SizeF& size, TargetInfo* out_target_info) {
   // Align up the requested target size to increase the chances that it can
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
index ec64a19..69e4bde 100644
--- a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
@@ -23,6 +23,7 @@
 #include "cobalt/render_tree/node.h"
 #include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
 #include "cobalt/renderer/backend/egl/graphics_context.h"
+#include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
@@ -85,6 +86,9 @@
   bool GetCachedTarget(const render_tree::Node* node,
                        const CacheErrorFunction1D& error_function,
                        TargetInfo* out_target_info);
+  bool GetCachedTarget(const skia::SkottieAnimation* skottie_animation,
+                       const math::SizeF& size,
+                       TargetInfo* out_target_info);
 
   // Allocate a cached offscreen target of the specified size.
   // The returned values are only valid until the next call to Update().
@@ -95,6 +99,9 @@
   void AllocateCachedTarget(const render_tree::Node* node, float size,
                             const ErrorData1D& error_data,
                             TargetInfo* out_target_info);
+  void AllocateCachedTarget(const skia::SkottieAnimation* skottie_animation,
+                            const math::SizeF& size,
+                            TargetInfo* out_target_info);
 
   // Allocate an uncached render target. The contents of the target cannot be
   // reused in subsequent frames.  If there was an error allocating the
@@ -122,6 +129,7 @@
   std::unique_ptr<OffscreenAtlas> offscreen_cache_1d_;
 
   std::vector<std::unique_ptr<OffscreenAtlas>> uncached_targets_;
+  std::vector<std::unique_ptr<OffscreenAtlas>> skottie_targets_;
 
   // Align offscreen targets to a particular size to more efficiently use the
   // offscreen target atlas. Use a power of 2 for the alignment so that a bit
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index 9ed9a55..7d73e88 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -44,6 +44,7 @@
 #include "cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h"
 #include "cobalt/renderer/rasterizer/skia/hardware_image.h"
 #include "cobalt/renderer/rasterizer/skia/image.h"
+#include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
 
 namespace cobalt {
 namespace renderer {
@@ -111,6 +112,17 @@
       0, 1);
 }
 
+math::SizeF GetTransformScale(const math::Matrix3F& transform) {
+  math::PointF mapped_origin = transform * math::PointF(0, 0);
+  math::PointF mapped_x = transform * math::PointF(1, 0);
+  math::PointF mapped_y = transform * math::PointF(0, 1);
+  math::Vector2dF mapped_vecx(mapped_x.x() - mapped_origin.x(),
+                              mapped_x.y() - mapped_origin.y());
+  math::Vector2dF mapped_vecy(mapped_y.x() - mapped_origin.x(),
+                              mapped_y.y() - mapped_origin.y());
+  return math::SizeF(mapped_vecx.Length(), mapped_vecy.Length());
+}
+
 bool ImageNodeSupportedNatively(render_tree::ImageNode* image_node) {
   // The image node may contain nothing. For example, when it represents a video
   // element before any frame is decoded.
@@ -407,17 +419,9 @@
         }
         draw_state_.opacity = 1.0f;
 
-        math::PointF mapped_origin = draw_state_.transform * math::PointF(0, 0);
-        math::PointF mapped_x = draw_state_.transform * math::PointF(1, 0);
-        math::PointF mapped_y = draw_state_.transform * math::PointF(0, 1);
-        math::Vector2dF mapped_vecx(mapped_x.x() - mapped_origin.x(),
-                                    mapped_x.y() - mapped_origin.y());
-        math::Vector2dF mapped_vecy(mapped_y.x() - mapped_origin.x(),
-                                    mapped_y.y() - mapped_origin.y());
-        float scale_x = mapped_vecx.Length();
-        float scale_y = mapped_vecy.Length();
-        draw_state_.transform = math::ScaleMatrix(std::max(1.0f, scale_x),
-                                                  std::max(1.0f, scale_y));
+        math::SizeF scale = GetTransformScale(draw_state_.transform);
+        draw_state_.transform = math::ScaleMatrix(
+            std::max(1.0f, scale.width()), std::max(1.0f, scale.height()));
 
         // Don't clip the source since it is in its own local space.
         bool limit_to_screen_size = false;
@@ -672,12 +676,63 @@
 }
 
 void RenderTreeNodeVisitor::Visit(render_tree::LottieNode* lottie_node) {
-  if (!IsVisible(lottie_node->GetBounds())) {
+  const render_tree::LottieNode::Builder& data = lottie_node->data();
+  math::RectF content_rect = data.destination_rect;
+  if (!IsVisible(content_rect)) {
     return;
   }
 
-  // Use Skottie to render Lottie animations.
-  FallbackRasterize(lottie_node);
+  skia::SkottieAnimation* animation =
+      base::polymorphic_downcast<skia::SkottieAnimation*>(data.animation.get());
+  if (animation->GetSize().GetArea() == 0) {
+    return;
+  }
+  animation->SetAnimationTime(data.animation_time);
+
+  // Get an offscreen target to cache the animation. Make the target big enough
+  // to avoid scaling artifacts.
+  math::SizeF scale = GetTransformScale(draw_state_.transform);
+  math::SizeF mapped_size = content_rect.size();
+  // Use a uniform scale to avoid impacting aspect ratio calculations.
+  mapped_size.Scale(std::max(scale.width(), scale.height()));
+
+  OffscreenTargetManager::TargetInfo target_info;
+  if (!offscreen_target_manager_->GetCachedTarget(
+      animation, mapped_size, &target_info)) {
+    // No pre-existing target was found. Allocate a new target.
+    animation->ResetRenderCache();
+    offscreen_target_manager_->AllocateCachedTarget(
+        animation, mapped_size, &target_info);
+  }
+  if (target_info.framebuffer == nullptr) {
+    // Unable to allocate the render target for the animation cache.
+    return;
+  }
+
+  // Add a draw call to update the cache.
+  std::unique_ptr<DrawObject> update_cache(new DrawCallback(
+      base::Bind(&skia::SkottieAnimation::UpdateRenderCache,
+                 base::Unretained(animation),
+                 base::Unretained(target_info.skia_canvas),
+                 target_info.region.size())));
+  draw_object_manager_->AddBatchedExternalDraw(
+      std::move(update_cache), lottie_node->GetTypeId(),
+      target_info.framebuffer, target_info.region);
+
+  // Add a draw call to render the cached animation to the current target.
+  backend::TextureEGL* texture = target_info.framebuffer->GetColorTexture();
+  math::Matrix3F texcoord_transform = GetTexcoordTransform(target_info);
+  if (IsOpaque(draw_state_.opacity)) {
+    std::unique_ptr<DrawObject> draw(
+        new DrawRectTexture(graphics_state_, draw_state_, content_rect, texture,
+                            texcoord_transform));
+    AddDraw(std::move(draw), content_rect, DrawObjectManager::kBlendSrcAlpha);
+  } else {
+    std::unique_ptr<DrawObject> draw(new DrawRectColorTexture(
+        graphics_state_, draw_state_, content_rect, kOpaqueWhite, texture,
+        texcoord_transform, false /* clamp_texcoords */));
+    AddDraw(std::move(draw), content_rect, DrawObjectManager::kBlendSrcAlpha);
+  }
 }
 
 void RenderTreeNodeVisitor::Visit(
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index 464ef3d..a751832 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -4538,6 +4538,61 @@
   TestTree(lottie_node);
 }
 
+TEST_F(PixelTest, LottiePreserveAspectRatioTooShortAnimationTest) {
+  std::vector<uint8> animation_data =
+      GetFileData(GetTestFilePath("white_material_wave_loading.json"));
+  scoped_refptr<LottieAnimation> animation =
+      GetResourceProvider()->CreateLottieAnimation(
+          reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
+  LottieAnimation::LottieProperties lottie_properties;
+  lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
+  animation->BeginRenderFrame(lottie_properties);
+
+  LottieNode::Builder node_builder =
+      LottieNode::Builder(animation, RectF(output_surface_size().width(),
+                                           100.0f));
+  node_builder.animation_time = base::TimeDelta::FromSecondsD(0);
+  scoped_refptr<LottieNode> lottie_node = new LottieNode(node_builder);
+  TestTree(lottie_node);
+}
+
+TEST_F(PixelTest, LottiePreserveAspectRatioTooNarrowAnimationTest) {
+  std::vector<uint8> animation_data =
+      GetFileData(GetTestFilePath("white_material_wave_loading.json"));
+  scoped_refptr<LottieAnimation> animation =
+      GetResourceProvider()->CreateLottieAnimation(
+          reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
+  LottieAnimation::LottieProperties lottie_properties;
+  lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
+  animation->BeginRenderFrame(lottie_properties);
+
+  LottieNode::Builder node_builder =
+      LottieNode::Builder(animation, RectF(100.0f,
+                                           output_surface_size().height()));
+  node_builder.animation_time = base::TimeDelta::FromSecondsD(0);
+  scoped_refptr<LottieNode> lottie_node = new LottieNode(node_builder);
+  TestTree(lottie_node);
+}
+
+TEST_F(PixelTest, LottieScaledWideAnimationTest) {
+  std::vector<uint8> animation_data =
+      GetFileData(GetTestFilePath("white_material_wave_loading.json"));
+  scoped_refptr<LottieAnimation> animation =
+      GetResourceProvider()->CreateLottieAnimation(
+          reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
+  LottieAnimation::LottieProperties lottie_properties;
+  lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
+  animation->BeginRenderFrame(lottie_properties);
+
+  LottieNode::Builder node_builder =
+      LottieNode::Builder(animation, RectF(output_surface_size()));
+  node_builder.animation_time = base::TimeDelta::FromSecondsD(0);
+  scoped_refptr<LottieNode> lottie_node = new LottieNode(node_builder);
+  TestTree(new MatrixTransformNode(lottie_node,
+      ScaleMatrix(4.0f, 1.0f) *
+      TranslateMatrix(output_surface_size().width() * -0.5f, 0.0f)));
+}
+
 #endif  // !SB_HAS(BLITTER)
 
 }  // namespace rasterizer
diff --git a/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc b/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
index 4fae87f..224b4a9 100644
--- a/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
@@ -15,6 +15,7 @@
 #include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
 
 #include "base/bind.h"
+#include "third_party/skia/include/core/SkRect.h"
 
 namespace cobalt {
 namespace renderer {
@@ -23,6 +24,7 @@
 
 SkottieAnimation::SkottieAnimation(const char* data, size_t length)
     : last_updated_animation_time_(base::TimeDelta()) {
+  ResetRenderCache();
   skottie::Animation::Builder builder;
   skottie_animation_ = builder.make(data, length);
   animation_size_ = math::Size(skottie_animation_->size().width(),
@@ -72,6 +74,11 @@
     return;
   }
 
+  // Do not update the animation time if it has already reached the last frame.
+  if (is_complete_) {
+    return;
+  }
+
   base::TimeDelta current_animation_time = last_updated_animation_time_;
   base::TimeDelta time_elapsed =
       animate_function_time - last_updated_animate_function_time_;
@@ -124,6 +131,21 @@
                                               animate_function_time);
 }
 
+void SkottieAnimation::UpdateRenderCache(SkCanvas* render_target,
+    const math::SizeF& size) {
+  DCHECK(render_target);
+  if (cached_animation_time_ == last_updated_animation_time_) {
+    // The render cache is already up-to-date.
+    return;
+  }
+
+  cached_animation_time_ = last_updated_animation_time_;
+  SkRect bounding_rect = SkRect::MakeWH(size.width(), size.height());
+  render_target->clear(SK_ColorTRANSPARENT);
+  skottie_animation_->render(render_target, &bounding_rect);
+  render_target->flush();
+}
+
 void SkottieAnimation::UpdateAnimationFrameAndAnimateFunctionTimes(
     base::TimeDelta current_animation_time,
     base::TimeDelta current_animate_function_time) {
diff --git a/src/cobalt/renderer/rasterizer/skia/skottie_animation.h b/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
index 3d5531c..0acb854 100644
--- a/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
+++ b/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
@@ -15,7 +15,9 @@
 #ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKOTTIE_ANIMATION_H_
 #define COBALT_RENDERER_RASTERIZER_SKIA_SKOTTIE_ANIMATION_H_
 
+#include "cobalt/math/size_f.h"
 #include "cobalt/render_tree/lottie_animation.h"
+#include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/modules/skottie/include/Skottie.h"
 
 namespace cobalt {
@@ -41,6 +43,12 @@
 
   sk_sp<skottie::Animation> GetSkottieAnimation() { return skottie_animation_; }
 
+  // Rendering the lottie animation can be CPU-intensive. To minimize the cost,
+  // the animation can be updated in an offscreen render target only as needed,
+  // then the offscreen target rendered to the screen.
+  void ResetRenderCache() { cached_animation_time_ = base::TimeDelta::Min(); }
+  void UpdateRenderCache(SkCanvas* render_target, const math::SizeF& size);
+
  private:
   void UpdateAnimationFrameAndAnimateFunctionTimes(
       base::TimeDelta current_animation_time,
@@ -79,6 +87,9 @@
 
   // The most recently updated frame time for |skottie_animation_|.
   base::TimeDelta last_updated_animation_time_;
+
+  // This is the animation time used for the last cache update.
+  base::TimeDelta cached_animation_time_;
 };
 
 }  // namespace skia
diff --git a/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooNarrowAnimationTest-expected.png b/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooNarrowAnimationTest-expected.png
new file mode 100644
index 0000000..803f356
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooNarrowAnimationTest-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooShortAnimationTest-expected.png b/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooShortAnimationTest-expected.png
new file mode 100644
index 0000000..804797d
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooShortAnimationTest-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LottieScaledWideAnimationTest-expected.png b/src/cobalt/renderer/rasterizer/testdata/LottieScaledWideAnimationTest-expected.png
new file mode 100644
index 0000000..86784fd
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LottieScaledWideAnimationTest-expected.png
Binary files differ
diff --git a/src/cobalt/site/docs/development/setup-android.md b/src/cobalt/site/docs/development/setup-android.md
index 1e570e4..4a6f2b0 100644
--- a/src/cobalt/site/docs/development/setup-android.md
+++ b/src/cobalt/site/docs/development/setup-android.md
@@ -24,6 +24,32 @@
     PATH=${PATH}:/path/to/depot_tools
     ```
 
+1.  Additional build dependencies may need to be installed:
+    ```
+    sudo apt-get install python python-pip
+    ```
+
+    If `python-pip` is not available via your package mangaer, you can install `pip` following [recommended instructions](https://pip.pypa.io/en/stable/installing/) from the official Python guide.
+
+    There are also some Python module requirements:
+
+    ```
+    python -m pip install requests
+    ```
+
+1.  Install ccache to support build acceleration. ccache is automatically used
+    when available, otherwise defaults to unaccelerated building:
+
+    ```
+    $ sudo apt-get install ccache
+    ```
+
+    We recommend adjusting the cache size as needed to increase cache hits:
+
+    ```
+    $ ccache --max-size=20G
+    ```
+
 1.  Download and install [Android Studio](https://developer.android.com/studio/).
 1.  Run `cobalt/build/gyp_cobalt android-x86` to configure the Cobalt build,
     which also installs the SDK and NDK. (This step will have to be repeated
diff --git a/src/cobalt/site/docs/development/setup-linux.md b/src/cobalt/site/docs/development/setup-linux.md
index e67b94c..3c4d49f 100644
--- a/src/cobalt/site/docs/development/setup-linux.md
+++ b/src/cobalt/site/docs/development/setup-linux.md
@@ -9,6 +9,8 @@
 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.
 
+## Set up your workstation
+
 1.  Choose where you want to put the `depot_tools` directory, which is used
     by the Cobalt code. An easy option is to put them in `~/depot_tools`.
     Clone the tools by running the following command:
@@ -30,19 +32,35 @@
     Cobalt on Linux:
 
     ```
-    $ sudo apt-get install bison build-essential coreutils git gperf \
-           libaom-dev libasound2-dev libavformat-dev libavresample-dev \
-           libdirectfb-dev libdirectfb-extra libpulse-dev \
-           libgl1-mesa-dev libgles2-mesa-dev libvpx-dev libx11-dev \
-           libxcomposite-dev libxcomposite1 libxrender-dev libxrender1 \
-           libxpm-dev m4 python ruby tar xserver-xephyr xz-utils yasm
+    $ 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
     ```
 
-1.  Install the latest version of the standard C++ header files (`libstdc++`).
-    For example:
+1.  Install Node.js via `nvm`:
+    ```
+    $ export NVM_DIR=~/.nvm
+    $ export NODE_VERSION=12.17.0
+
+    $ curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash
+
+    $ . $NVM_DIR/nvm.sh \
+        && nvm install --lts \
+        && nvm alias default lts/* \
+        && nvm use default
+    ```
+
+1.  Install ccache to support build acceleration. ccache is automatically used
+    when available, otherwise defaults to unaccelerated building:
 
     ```
-    sudo apt-get install libstdc++-4.8-dev
+    $ sudo apt install -qqy --no-install-recommends ccache
+    ```
+
+    We recommend adjusting the cache size as needed to increase cache hits:
+
+    ```
+    $ ccache --max-size=20G
     ```
 
 1.  Clone the Cobalt code repository. The following `git` command creates a
@@ -52,13 +70,7 @@
     $ git clone https://cobalt.googlesource.com/cobalt
     ```
 
-1.  Modify your path to include the version of Clang that is downloaded
-    in the next step of the instructions. The next step will return an
-    error if this version of Clang is not in your path before it runs.
-
-    ```
-    $PATH="/path/to/cobalt/src/third_party/llvm-build/Release+Asserts/bin:${PATH}"
-    ```
+## Build and Run Cobalt
 
 1.  Build the code by navigating to the `src` directory in your new
     `cobalt` directory and running the following command. You must
diff --git a/src/cobalt/site/docs/development/setup-raspi.md b/src/cobalt/site/docs/development/setup-raspi.md
index 13936bb..3e88d8c 100644
--- a/src/cobalt/site/docs/development/setup-raspi.md
+++ b/src/cobalt/site/docs/development/setup-raspi.md
@@ -48,15 +48,27 @@
 $ 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
+          libavresample-dev rsync
 ```
 
 ## Set up your workstation
 
+<aside class="note">
+<b>Note:</b> Before proceeding further, refer to the documentation for ["Set up your environment - Linux"](setup-linux.md). Complete the section **Set up your workstation**, then return and complete the following steps.
+</aside>
+
 The following steps install the cross-compiling toolchain on your workstation.
 The toolchain runs on an x86 workstation and compiles binaries for your ARM
 Raspberry Pi.
 
+1.  Run the following command to install packages needed to build and run
+    Cobalt for Raspberry Pi:
+
+    ```
+    $ sudo apt install -qqy --no-install-recommends g++-multilib \
+          python-requests wget xz-utils
+    ```
+
 1.  Choose a location for the installed toolchain &ndash; e.g. `raspi-tools`
     &ndash; and set `$RASPI_HOME` to that location.
 
@@ -94,10 +106,16 @@
 
 ## Build, install, and run Cobalt for Raspberry Pi
 
-1.  Run the following commands to build Cobalt:
+1.  Build the code by navigating to the src directory in your cobalt directory and run the
+    following command :
 
     ```
-    $ gyp_cobalt raspi-2
+    $ cobalt/build/gyp_cobalt raspi-2
+    ```
+
+1.  Compile the code from the `src/` directory:
+
+    ```
     $ ninja -C out/raspi-2_debug cobalt
     ```
 
@@ -105,7 +123,7 @@
     on the device:
 
     ```
-    rsync -avzh --exclude="obj*" \
+    rsync -avzLPh --exclude="obj*" --exclude="gen/" \
           $COBALT_SRC/out/raspi-2_debug pi@$RASPI_ADDR:~/
     ```
 
@@ -129,3 +147,21 @@
     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/src/cobalt/site/docs/reference/starboard/modules/10/file.md b/src/cobalt/site/docs/reference/starboard/modules/10/file.md
index b115501..b9c0e36 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/10/file.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/10/file.md
@@ -169,7 +169,7 @@
 is used primarily to clean up after unit tests. On some platforms, this function
 fails if the file in question is being held open.
 
-`path`: The absolute path fo the file, symlink, or directory to be deleted.
+`path`: The absolute path of the file, symlink, or directory to be deleted.
 
 #### Declaration ####
 
diff --git a/src/cobalt/site/docs/reference/starboard/modules/10/media.md b/src/cobalt/site/docs/reference/starboard/modules/10/media.md
index 9b83d06..7271b29 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/10/media.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/10/media.md
@@ -665,6 +665,8 @@
 outputs. If `true`, then non-protection-capable outputs are expected to be
 blanked.
 
+presubmit: allow sb_export mismatch
+
 #### Declaration ####
 
 ```
@@ -700,6 +702,8 @@
 
 `enabled`: Indicates whether output protection is enabled (`true`) or disabled.
 
+presubmit: allow sb_export mismatch
+
 #### Declaration ####
 
 ```
diff --git a/src/cobalt/site/docs/reference/starboard/modules/11/file.md b/src/cobalt/site/docs/reference/starboard/modules/11/file.md
index ad890dc..f910cb0 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/11/file.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/11/file.md
@@ -168,7 +168,7 @@
 is used primarily to clean up after unit tests. On some platforms, this function
 fails if the file in question is being held open.
 
-`path`: The absolute path fo the file, symlink, or directory to be deleted.
+`path`: The absolute path of the file, symlink, or directory to be deleted.
 
 #### Declaration ####
 
diff --git a/src/cobalt/site/docs/reference/starboard/modules/11/media.md b/src/cobalt/site/docs/reference/starboard/modules/11/media.md
index 8d94612..377cb2b 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/11/media.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/11/media.md
@@ -659,6 +659,8 @@
 outputs. If `true`, then non-protection-capable outputs are expected to be
 blanked.
 
+presubmit: allow sb_export mismatch
+
 #### Declaration ####
 
 ```
@@ -712,6 +714,8 @@
 
 `enabled`: Indicates whether output protection is enabled (`true`) or disabled.
 
+presubmit: allow sb_export mismatch
+
 #### Declaration ####
 
 ```
diff --git a/src/cobalt/site/docs/reference/starboard/modules/12/condition_variable.md b/src/cobalt/site/docs/reference/starboard/modules/12/condition_variable.md
index 836c4b8..22c2b79 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/12/condition_variable.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/12/condition_variable.md
@@ -40,7 +40,7 @@
 #### Definition ####
 
 ```
-typedef union SbConditionVariable  SbConditionVariable
+typedef union SbConditionVariable SbConditionVariable
 ```
 
 ## Functions ##
diff --git a/src/cobalt/site/docs/reference/starboard/modules/12/file.md b/src/cobalt/site/docs/reference/starboard/modules/12/file.md
index c3ba551..e22edeb 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/12/file.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/12/file.md
@@ -184,7 +184,7 @@
 is used primarily to clean up after unit tests. On some platforms, this function
 fails if the file in question is being held open.
 
-`path`: The absolute path fo the file, symlink, or directory to be deleted.
+`path`: The absolute path of the file, symlink, or directory to be deleted.
 
 #### Declaration ####
 
diff --git a/src/cobalt/site/docs/reference/starboard/modules/12/mutex.md b/src/cobalt/site/docs/reference/starboard/modules/12/mutex.md
index 58f1907..a9d2a9a 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/12/mutex.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/12/mutex.md
@@ -40,7 +40,7 @@
 #### Definition ####
 
 ```
-typedef union SbMutex  SbMutex
+typedef union SbMutex SbMutex
 ```
 
 ## Functions ##
diff --git a/src/cobalt/site/docs/reference/starboard/modules/12/once.md b/src/cobalt/site/docs/reference/starboard/modules/12/once.md
index ce3c4d6..61aad5d 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/12/once.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/12/once.md
@@ -22,7 +22,7 @@
 #### Definition ####
 
 ```
-typedef union SbOnceControl  SbOnceControl
+typedef union SbOnceControl SbOnceControl
 ```
 
 ### SbOnceInitRoutine ###
diff --git a/src/cobalt/site/docs/reference/starboard/modules/condition_variable.md b/src/cobalt/site/docs/reference/starboard/modules/condition_variable.md
index 836c4b8..22c2b79 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/condition_variable.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/condition_variable.md
@@ -40,7 +40,7 @@
 #### Definition ####
 
 ```
-typedef union SbConditionVariable  SbConditionVariable
+typedef union SbConditionVariable SbConditionVariable
 ```
 
 ## Functions ##
diff --git a/src/cobalt/site/docs/reference/starboard/modules/file.md b/src/cobalt/site/docs/reference/starboard/modules/file.md
index c3ba551..e22edeb 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/file.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/file.md
@@ -184,7 +184,7 @@
 is used primarily to clean up after unit tests. On some platforms, this function
 fails if the file in question is being held open.
 
-`path`: The absolute path fo the file, symlink, or directory to be deleted.
+`path`: The absolute path of the file, symlink, or directory to be deleted.
 
 #### Declaration ####
 
diff --git a/src/cobalt/site/docs/reference/starboard/modules/mutex.md b/src/cobalt/site/docs/reference/starboard/modules/mutex.md
index 58f1907..a9d2a9a 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/mutex.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/mutex.md
@@ -40,7 +40,7 @@
 #### Definition ####
 
 ```
-typedef union SbMutex  SbMutex
+typedef union SbMutex SbMutex
 ```
 
 ## Functions ##
diff --git a/src/cobalt/site/docs/reference/starboard/modules/once.md b/src/cobalt/site/docs/reference/starboard/modules/once.md
index ce3c4d6..61aad5d 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/once.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/once.md
@@ -22,7 +22,7 @@
 #### Definition ####
 
 ```
-typedef union SbOnceControl  SbOnceControl
+typedef union SbOnceControl SbOnceControl
 ```
 
 ### SbOnceInitRoutine ###
diff --git a/src/cobalt/speech/audio_encoder_flac.cc b/src/cobalt/speech/audio_encoder_flac.cc
index c6899de..279b5fe 100644
--- a/src/cobalt/speech/audio_encoder_flac.cc
+++ b/src/cobalt/speech/audio_encoder_flac.cc
@@ -56,18 +56,18 @@
   FLAC__stream_encoder_delete(encoder_);
 }
 
-void AudioEncoderFlac::Encode(const ShellAudioBus* audio_bus) {
+void AudioEncoderFlac::Encode(const AudioBus* audio_bus) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   DCHECK_EQ(audio_bus->channels(), size_t(1));
   uint32 frames = static_cast<uint32>(audio_bus->frames());
   std::unique_ptr<FLAC__int32[]> flac_samples(new FLAC__int32[frames]);
   for (uint32 i = 0; i < frames; ++i) {
-    if (audio_bus->sample_type() == ShellAudioBus::kFloat32) {
+    if (audio_bus->sample_type() == AudioBus::kFloat32) {
       flac_samples[i] = static_cast<FLAC__int32>(
           audio_bus->GetFloat32Sample(0, i) * kMaxInt16AsFloat32);
     } else {
-      DCHECK_EQ(audio_bus->sample_type(), ShellAudioBus::kInt16);
+      DCHECK_EQ(audio_bus->sample_type(), AudioBus::kInt16);
       flac_samples[i] =
           static_cast<FLAC__int32>(audio_bus->GetInt16Sample(0, i));
     }
diff --git a/src/cobalt/speech/audio_encoder_flac.h b/src/cobalt/speech/audio_encoder_flac.h
index 71b1822..3317af9 100644
--- a/src/cobalt/speech/audio_encoder_flac.h
+++ b/src/cobalt/speech/audio_encoder_flac.h
@@ -20,7 +20,7 @@
 #include "base/basictypes.h"
 #include "base/callback.h"
 #include "base/threading/thread_checker.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "third_party/flac/include/FLAC/stream_encoder.h"
 
 namespace cobalt {
@@ -29,13 +29,13 @@
 // Encode raw audio to using FLAC codec.
 class AudioEncoderFlac {
  public:
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
   explicit AudioEncoderFlac(int sample_rate);
   ~AudioEncoderFlac();
 
   // Encode raw audio data.
-  void Encode(const ShellAudioBus* audio_bus);
+  void Encode(const AudioBus* audio_bus);
   // Finish encoding.
   void Finish();
 
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.cc b/src/cobalt/speech/cobalt_speech_recognizer.cc
index eb9fd8d..0d61e70 100644
--- a/src/cobalt/speech/cobalt_speech_recognizer.cc
+++ b/src/cobalt/speech/cobalt_speech_recognizer.cc
@@ -123,7 +123,7 @@
 }
 
 void CobaltSpeechRecognizer::OnDataReceived(
-    std::unique_ptr<ShellAudioBus> audio_bus) {
+    std::unique_ptr<AudioBus> audio_bus) {
   if (endpointer_delegate_.IsFirstTimeSoundStarted(*audio_bus)) {
     RunEventCallback(new dom::Event(base::Tokens::soundstart()));
   }
@@ -135,8 +135,8 @@
   // silence at the end in case encoder had no data already.
   size_t dummy_frames =
       static_cast<size_t>(kSampleRate * kAudioPacketDurationInSeconds);
-  std::unique_ptr<ShellAudioBus> dummy_audio_bus(new ShellAudioBus(
-      1, dummy_frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+  std::unique_ptr<AudioBus> dummy_audio_bus(
+      new AudioBus(1, dummy_frames, AudioBus::kInt16, AudioBus::kInterleaved));
   dummy_audio_bus->ZeroAllFrames();
   service_->RecognizeAudio(std::move(dummy_audio_bus), true);
 }
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.h b/src/cobalt/speech/cobalt_speech_recognizer.h
index f9ca9d4..d67ab5e 100644
--- a/src/cobalt/speech/cobalt_speech_recognizer.h
+++ b/src/cobalt/speech/cobalt_speech_recognizer.h
@@ -18,7 +18,7 @@
 #include <memory>
 #include <string>
 
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/network/network_module.h"
 #include "cobalt/speech/endpointer_delegate.h"
 #include "cobalt/speech/google_speech_service.h"
@@ -39,7 +39,7 @@
 // from there.
 class CobaltSpeechRecognizer : public SpeechRecognizer {
  public:
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
   CobaltSpeechRecognizer(network::NetworkModule* network_module,
                          const Microphone::Options& microphone_options,
@@ -51,7 +51,7 @@
 
  private:
   // Callbacks from mic.
-  void OnDataReceived(std::unique_ptr<ShellAudioBus> audio_bus);
+  void OnDataReceived(std::unique_ptr<AudioBus> audio_bus);
   void OnDataCompletion();
   void OnMicrophoneError(MicrophoneManager::MicrophoneError error,
                          std::string error_message);
diff --git a/src/cobalt/speech/endpointer_delegate.cc b/src/cobalt/speech/endpointer_delegate.cc
index 6340eea..1fc2658 100644
--- a/src/cobalt/speech/endpointer_delegate.cc
+++ b/src/cobalt/speech/endpointer_delegate.cc
@@ -45,8 +45,7 @@
 
 void EndPointerDelegate::Stop() { endpointer_.EndSession(); }
 
-bool EndPointerDelegate::IsFirstTimeSoundStarted(
-    const ShellAudioBus& audio_bus) {
+bool EndPointerDelegate::IsFirstTimeSoundStarted(const AudioBus& audio_bus) {
   if (is_first_time_sound_started_) {
     return false;
   }
diff --git a/src/cobalt/speech/endpointer_delegate.h b/src/cobalt/speech/endpointer_delegate.h
index fe2f18b..e222f02 100644
--- a/src/cobalt/speech/endpointer_delegate.h
+++ b/src/cobalt/speech/endpointer_delegate.h
@@ -15,8 +15,8 @@
 #ifndef COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
 #define COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
 
+#include "cobalt/media/base/audio_bus.h"
 #include "content/browser/speech/endpointer/endpointer.h"
-#include "cobalt/media/base/shell_audio_bus.h"
 
 namespace cobalt {
 namespace speech {
@@ -25,7 +25,7 @@
 // speech session (from start speaking to end of speaking).
 class EndPointerDelegate {
  public:
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
 
   explicit EndPointerDelegate(int sample_rate);
   ~EndPointerDelegate();
@@ -36,7 +36,7 @@
   void Stop();
 
   // Return true if it is the first time that the sound started.
-  bool IsFirstTimeSoundStarted(const ShellAudioBus& audio_bus);
+  bool IsFirstTimeSoundStarted(const AudioBus& audio_bus);
 
  private:
   // Used for detecting sound start event.
diff --git a/src/cobalt/speech/google_speech_service.cc b/src/cobalt/speech/google_speech_service.cc
index 83ef7ec..0bd11ac 100644
--- a/src/cobalt/speech/google_speech_service.cc
+++ b/src/cobalt/speech/google_speech_service.cc
@@ -220,8 +220,8 @@
       base::Bind(&GoogleSpeechService::StopInternal, base::Unretained(this)));
 }
 
-void GoogleSpeechService::RecognizeAudio(
-    std::unique_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) {
+void GoogleSpeechService::RecognizeAudio(std::unique_ptr<AudioBus> audio_bus,
+                                         bool is_last_chunk) {
   // Called by the speech recognition manager thread.
   thread_.message_loop()->task_runner()->PostTask(
       FROM_HERE, base::Bind(&GoogleSpeechService::UploadAudioDataInternal,
@@ -393,7 +393,7 @@
 }
 
 void GoogleSpeechService::UploadAudioDataInternal(
-    std::unique_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) {
+    std::unique_ptr<AudioBus> audio_bus, bool is_last_chunk) {
   DCHECK_EQ(thread_.message_loop(), base::MessageLoop::current());
   DCHECK(audio_bus);
 
diff --git a/src/cobalt/speech/google_speech_service.h b/src/cobalt/speech/google_speech_service.h
index b0002ae..a1ea1e5 100644
--- a/src/cobalt/speech/google_speech_service.h
+++ b/src/cobalt/speech/google_speech_service.h
@@ -21,7 +21,7 @@
 
 #include "base/threading/thread.h"
 #include "cobalt/loader/url_fetcher_string_writer.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/network/network_module.h"
 #include "cobalt/speech/audio_encoder_flac.h"
 #include "cobalt/speech/google_streaming_api.pb.h"
@@ -43,7 +43,7 @@
 // manager.
 class GoogleSpeechService : public net::URLFetcherDelegate {
  public:
-  typedef media::ShellAudioBus ShellAudioBus;
+  typedef media::AudioBus AudioBus;
   typedef base::Callback<void(const scoped_refptr<dom::Event>&)> EventCallback;
   typedef SpeechRecognitionResultList::SpeechRecognitionResults
       SpeechRecognitionResults;
@@ -63,8 +63,7 @@
   // Stop speech recognizer.
   void Stop();
   // An encoded audio data is available and ready to be recognized.
-  void RecognizeAudio(std::unique_ptr<ShellAudioBus> audio_bus,
-                      bool is_last_chunk);
+  void RecognizeAudio(std::unique_ptr<AudioBus> audio_bus, bool is_last_chunk);
 
   // net::URLFetcherDelegate interface
   void OnURLFetchDownloadProgress(const net::URLFetcher* source,
@@ -81,7 +80,7 @@
   void StopInternal();
   // This method handles wrappables and should run on the MainWebModule thread.
   void ClearFinalResults();
-  void UploadAudioDataInternal(std::unique_ptr<ShellAudioBus> audio_bus,
+  void UploadAudioDataInternal(std::unique_ptr<AudioBus> audio_bus,
                                bool is_last_chunk);
   // This method handles wrappables, and so it must run on the MainWebModule.
   void ProcessAndFireSuccessEvent(proto::SpeechRecognitionEvent event);
diff --git a/src/cobalt/speech/microphone_fake.cc b/src/cobalt/speech/microphone_fake.cc
index 15c955a..29a86a4 100644
--- a/src/cobalt/speech/microphone_fake.cc
+++ b/src/cobalt/speech/microphone_fake.cc
@@ -31,7 +31,7 @@
 namespace cobalt {
 namespace speech {
 
-typedef audio::ShellAudioBus ShellAudioBus;
+typedef audio::AudioBus AudioBus;
 
 namespace {
 
@@ -89,10 +89,10 @@
   } else {
     file_length_ = std::min(options.audio_data_size, kMaxBufferSize);
     DCHECK_GT(file_length_, 0);
-    audio_bus_.reset(new ShellAudioBus(
-        kSupportedMonoChannel,
-        file_length_ / audio::GetSampleTypeSize(ShellAudioBus::kInt16),
-        ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+    audio_bus_.reset(
+        new AudioBus(kSupportedMonoChannel,
+                     file_length_ / audio::GetSampleTypeSize(AudioBus::kInt16),
+                     AudioBus::kInt16, AudioBus::kInterleaved));
     SbMemoryCopy(audio_bus_->interleaved_data(), options.external_audio_data,
                  file_length_);
   }
@@ -131,14 +131,14 @@
     const float kSupportedSampleRate = 16000.0f;
     if (!reader) {
       // If it is not a WAV file, read audio data as raw audio.
-      audio_bus_.reset(new ShellAudioBus(
+      audio_bus_.reset(new AudioBus(
           kSupportedMonoChannel,
-          file_buffer_size / audio::GetSampleTypeSize(ShellAudioBus::kInt16),
-          ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+          file_buffer_size / audio::GetSampleTypeSize(AudioBus::kInt16),
+          AudioBus::kInt16, AudioBus::kInterleaved));
       SbMemoryCopy(audio_bus_->interleaved_data(), audio_input.get(),
                    file_buffer_size);
       file_length_ = file_buffer_size;
-    } else if (reader->sample_type() != ShellAudioBus::kInt16 ||
+    } else if (reader->sample_type() != AudioBus::kInt16 ||
                reader->sample_rate() != kSupportedSampleRate ||
                reader->number_of_channels() != kSupportedMonoChannel) {
       // If it is a WAV file but it doesn't meet the audio input criteria, treat
diff --git a/src/cobalt/speech/microphone_fake.h b/src/cobalt/speech/microphone_fake.h
index ac4cf8b..3f6cbbc 100644
--- a/src/cobalt/speech/microphone_fake.h
+++ b/src/cobalt/speech/microphone_fake.h
@@ -52,7 +52,7 @@
 
   bool read_data_from_file_;
   std::vector<base::FilePath> file_paths_;
-  std::unique_ptr<audio::ShellAudioBus> audio_bus_;
+  std::unique_ptr<audio::AudioBus> audio_bus_;
   int file_length_;
   int read_index_;
   bool is_valid_;
diff --git a/src/cobalt/speech/microphone_manager.cc b/src/cobalt/speech/microphone_manager.cc
index a957f2d..cddf31e 100644
--- a/src/cobalt/speech/microphone_manager.cc
+++ b/src/cobalt/speech/microphone_manager.cc
@@ -145,9 +145,9 @@
   // If |read_bytes| is zero, nothing should happen.
   if (read_bytes > 0 && read_bytes % sizeof(int16_t) == 0) {
     size_t frames = read_bytes / sizeof(int16_t);
-    std::unique_ptr<ShellAudioBus> output_audio_bus(new ShellAudioBus(
-        1, frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
-    ShellAudioBus source(1, frames, samples);
+    std::unique_ptr<AudioBus> output_audio_bus(
+        new AudioBus(1, frames, AudioBus::kInt16, AudioBus::kInterleaved));
+    AudioBus source(1, frames, samples);
     output_audio_bus->Assign(source);
     data_received_callback_.Run(std::move(output_audio_bus));
   } else if (read_bytes != 0) {
diff --git a/src/cobalt/speech/microphone_manager.h b/src/cobalt/speech/microphone_manager.h
index 765d2b6..602f6bf 100644
--- a/src/cobalt/speech/microphone_manager.h
+++ b/src/cobalt/speech/microphone_manager.h
@@ -25,7 +25,7 @@
 #include "base/threading/thread.h"
 #include "base/timer/timer.h"
 #include "cobalt/dom/event.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "cobalt/speech/microphone.h"
 
 namespace cobalt {
@@ -39,9 +39,8 @@
     kAudioCapture,
     kAborted,
   };
-  typedef media::ShellAudioBus ShellAudioBus;
-  typedef base::Callback<void(std::unique_ptr<ShellAudioBus>)>
-      DataReceivedCallback;
+  typedef media::AudioBus AudioBus;
+  typedef base::Callback<void(std::unique_ptr<AudioBus>)> DataReceivedCallback;
   typedef base::Closure CompletionCallback;
   typedef base::Closure SuccessfulOpenCallback;
   typedef base::Callback<void(MicrophoneError, std::string)> ErrorCallback;
diff --git a/src/cobalt/updater/configurator.cc b/src/cobalt/updater/configurator.cc
index d626083..210f088 100644
--- a/src/cobalt/updater/configurator.cc
+++ b/src/cobalt/updater/configurator.cc
@@ -31,6 +31,7 @@
 
 #if defined(COBALT_BUILD_TYPE_DEBUG) || defined(COBALT_BUILD_TYPE_DEVEL)
 const std::set<std::string> valid_channels = {"dev"};
+const std::string kDefaultUpdaterChannel = "dev";
 #elif defined(COBALT_BUILD_TYPE_QA)
 // Find more information about these test channels in the Evergreen test plan.
 const std::set<std::string> valid_channels = {
@@ -51,8 +52,10 @@
     // Test an update that's larger than the available storage on the device
     "tistore",
 };
+const std::string kDefaultUpdaterChannel = "qa";
 #elif defined(COBALT_BUILD_TYPE_GOLD)
 const std::set<std::string> valid_channels = {"prod", "dogfood"};
+const std::string kDefaultUpdaterChannel = "prod";
 #endif
 
 std::string GetDeviceProperty(SbSystemPropertyId id) {
@@ -74,10 +77,21 @@
 
 Configurator::Configurator(network::NetworkModule* network_module)
     : pref_service_(CreatePrefService()),
+      persisted_data_(std::make_unique<update_client::PersistedData>(
+          pref_service_.get(), nullptr)),
+      is_channel_changed_(0),
       unzip_factory_(base::MakeRefCounted<UnzipperFactory>()),
       network_fetcher_factory_(
           base::MakeRefCounted<NetworkFetcherFactoryCobalt>(network_module)),
-      patch_factory_(base::MakeRefCounted<PatcherFactory>()) {}
+      patch_factory_(base::MakeRefCounted<PatcherFactory>()) {
+  const std::string persisted_channel =
+      persisted_data_->GetUpdaterChannel(GetAppGuid());
+  if (persisted_channel.empty()) {
+    SetChannel(kDefaultUpdaterChannel);
+  } else {
+    SetChannel(persisted_channel);
+  }
+}
 Configurator::~Configurator() = default;
 
 int Configurator::InitialDelay() const { return 0; }
@@ -134,9 +148,9 @@
   params.insert(std::make_pair("sbversion", std::to_string(SB_API_VERSION)));
   params.insert(std::make_pair(
       "jsengine", script::GetJavaScriptEngineNameAndVersion()));
-  params.insert(std::make_pair("updaterchannelchanged",
-                               IsChannelChanged() ? "True" : "False"));
-
+  params.insert(std::make_pair(
+      "updaterchannelchanged",
+      SbAtomicNoBarrier_Load(&is_channel_changed_) == 1 ? "True" : "False"));
   // Brand name
   params.insert(
       std::make_pair("brand", GetDeviceProperty(kSbSystemPropertyBrandName)));
@@ -200,6 +214,10 @@
   return {};
 }
 
+void Configurator::CompareAndSwapChannelChanged(int old_value, int new_value) {
+  SbAtomicNoBarrier_CompareAndSwap(&is_channel_changed_, old_value, new_value);
+}
+
 // The updater channel is get and set by main web module thread and update
 // client thread. The getter and set use a lock to prevent synchronization
 // issue.
@@ -211,6 +229,7 @@
 void Configurator::SetChannel(const std::string& updater_channel) {
   base::AutoLock auto_lock(updater_channel_lock_);
   updater_channel_ = updater_channel;
+  persisted_data_->SetUpdaterChannel(GetAppGuid(), updater_channel);
 }
 
 bool Configurator::IsChannelValid(const std::string& channel) {
diff --git a/src/cobalt/updater/configurator.h b/src/cobalt/updater/configurator.h
index 8012b2f..e335b4f 100644
--- a/src/cobalt/updater/configurator.h
+++ b/src/cobalt/updater/configurator.h
@@ -17,6 +17,7 @@
 #include "base/synchronization/lock.h"
 #include "cobalt/network/network_module.h"
 #include "components/update_client/configurator.h"
+#include "components/update_client/persisted_data.h"
 
 class GURL;
 class PrefService;
@@ -76,8 +77,8 @@
 
   void SetChannel(const std::string& updater_channel) override;
 
-  void MarkChannelChanged() { is_channel_changed = true; }
-  bool IsChannelChanged() const override { return is_channel_changed; }
+  void CompareAndSwapChannelChanged(int old_value, int new_value) override;
+
   bool IsChannelValid(const std::string& channel);
 
   std::string GetUpdaterStatus() const;
@@ -88,12 +89,13 @@
   ~Configurator() override;
 
   std::unique_ptr<PrefService> pref_service_;
+  std::unique_ptr<update_client::PersistedData> persisted_data_;
   scoped_refptr<update_client::NetworkFetcherFactory> network_fetcher_factory_;
   scoped_refptr<update_client::UnzipperFactory> unzip_factory_;
   scoped_refptr<update_client::PatcherFactory> patch_factory_;
   std::string updater_channel_;
   base::Lock updater_channel_lock_;
-  bool is_channel_changed = false;
+  SbAtomic32 is_channel_changed_;
   std::string updater_status_;
   base::Lock updater_status_lock_;
 
diff --git a/src/cobalt/updater/crash_sandbox.cc b/src/cobalt/updater/crash_sandbox.cc
index 8f69c81..869c589 100644
--- a/src/cobalt/updater/crash_sandbox.cc
+++ b/src/cobalt/updater/crash_sandbox.cc
@@ -18,5 +18,6 @@
 #include "starboard/event.h"
 
 void SbEventHandle(const SbEvent* event) {
-  SB_CHECK(false);
+  volatile int* a = (int*)(NULL);
+  *a = 1;
 }
diff --git a/src/cobalt/updater/updater_module.cc b/src/cobalt/updater/updater_module.cc
index 0a7904e..2f0452b 100644
--- a/src/cobalt/updater/updater_module.cc
+++ b/src/cobalt/updater/updater_module.cc
@@ -246,6 +246,10 @@
       base::TimeDelta::FromHours(kNextUpdateCheckHours));
 }
 
+void UpdaterModule::CompareAndSwapChannelChanged(int old_value, int new_value) {
+  updater_configurator_->CompareAndSwapChannelChanged(old_value, new_value);
+}
+
 // The following three methods all called by the main web module thread.
 std::string UpdaterModule::GetUpdaterChannel() const {
   return updater_configurator_->GetChannel();
diff --git a/src/cobalt/updater/updater_module.h b/src/cobalt/updater/updater_module.h
index c441d1d..d275556 100644
--- a/src/cobalt/updater/updater_module.h
+++ b/src/cobalt/updater/updater_module.h
@@ -65,8 +65,7 @@
   std::string GetUpdaterChannel() const;
   void SetUpdaterChannel(const std::string& updater_channel);
 
-  void MarkChannelChanged() { updater_configurator_->MarkChannelChanged(); }
-  bool IsChannelChanged() { return updater_configurator_->IsChannelChanged(); }
+  void CompareAndSwapChannelChanged(int old_value, int new_value);
   bool IsChannelValid(const std::string& channel) {
     return updater_configurator_->IsChannelValid(channel);
   }
diff --git a/src/cobalt/updater/utils.cc b/src/cobalt/updater/utils.cc
index 17f0822..dab4b92 100644
--- a/src/cobalt/updater/utils.cc
+++ b/src/cobalt/updater/utils.cc
@@ -22,6 +22,12 @@
 
 namespace cobalt {
 namespace updater {
+namespace {
+// The default manifest version to assume when the actual manifest cannot be
+// parsed for any reason. This should not be used for installation manager
+// errors, or any other error unrelated to parsing the manifest.
+const std::string kDefaultManifestVersion = "1.0.0";
+}  // namespace
 
 bool CreateProductDirectory(base::FilePath* path) {
   if (!GetProductDirectoryPath(path)) {
@@ -112,6 +118,12 @@
       std::string(installation_path.begin(), installation_path.end())));
 
   if (!version.IsValid()) {
+    if (!index) {
+      SB_LOG(ERROR) << "Failed to get the Everegreen version. Defaulting to "
+                    << kDefaultManifestVersion << ".";
+      return kDefaultManifestVersion;
+    }
+
     SB_LOG(ERROR) << "Failed to get the Everegreen version.";
     return "";
   }
diff --git a/src/cobalt/xhr/url_fetcher_buffer_writer.cc b/src/cobalt/xhr/url_fetcher_buffer_writer.cc
index a01a9a8..3b43b8a 100644
--- a/src/cobalt/xhr/url_fetcher_buffer_writer.cc
+++ b/src/cobalt/xhr/url_fetcher_buffer_writer.cc
@@ -103,7 +103,8 @@
   return copy_of_data_as_string_;
 }
 
-void URLFetcherResponseWriter::Buffer::GetAndReset(std::string* str) {
+void URLFetcherResponseWriter::Buffer::GetAndResetDataAndDownloadProgress(
+    std::string* str) {
   DCHECK(str);
 
   ReleaseMemory(str);
@@ -119,9 +120,15 @@
   }
 
   data_as_string_.swap(*str);
+
+  // It is important to reset the |download_progress_| and return the data in
+  // one function to avoid potential race condition that may prevent the last
+  // bit of data of Fetcher from being downloaded, because the data download is
+  // guarded by HasProgressSinceLastGetAndReset().
+  download_progress_ = 0;
 }
 
-void URLFetcherResponseWriter::Buffer::GetAndReset(
+void URLFetcherResponseWriter::Buffer::GetAndResetData(
     PreallocatedArrayBufferData* data) {
   DCHECK(data);
 
diff --git a/src/cobalt/xhr/url_fetcher_buffer_writer.h b/src/cobalt/xhr/url_fetcher_buffer_writer.h
index c82ce32..8eb9736 100644
--- a/src/cobalt/xhr/url_fetcher_buffer_writer.h
+++ b/src/cobalt/xhr/url_fetcher_buffer_writer.h
@@ -57,8 +57,8 @@
     // public member function is called on this object.
     const std::string& GetTemporaryReferenceOfString();
 
-    void GetAndReset(std::string* str);
-    void GetAndReset(PreallocatedArrayBufferData* data);
+    void GetAndResetDataAndDownloadProgress(std::string* str);
+    void GetAndResetData(PreallocatedArrayBufferData* data);
 
     void MaybePreallocate(int64_t capacity);
     void Write(const void* buffer, int num_bytes);
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc
index 27408e2..0ea3345 100644
--- a/src/cobalt/xhr/xml_http_request.cc
+++ b/src/cobalt/xhr/xml_http_request.cc
@@ -717,7 +717,7 @@
 
   if (fetch_callback_) {
     std::string downloaded_data;
-    response_body_->GetAndReset(&downloaded_data);
+    response_body_->GetAndResetDataAndDownloadProgress(&downloaded_data);
     script::Handle<script::Uint8Array> data =
         script::Uint8Array::New(settings_->global_environment(),
                                 downloaded_data.data(), downloaded_data.size());
@@ -729,9 +729,13 @@
   const base::TimeDelta elapsed(now - last_progress_time_);
   if (elapsed > base::TimeDelta::FromMilliseconds(kProgressPeriodMs)) {
     last_progress_time_ = now;
-    // TODO: Investigate if we have to fire progress event with 0 loaded bytes
-    // when used as Fetch API.
-    UpdateProgress(response_body_->GetAndResetDownloadProgress());
+    if (fetch_callback_) {
+      // TODO: Investigate if we have to fire progress event with 0 loaded bytes
+      //       when used as Fetch API.
+      UpdateProgress(0);
+    } else {
+      UpdateProgress(response_body_->GetAndResetDownloadProgress());
+    }
   }
 }
 
@@ -1025,7 +1029,7 @@
     // request is re-opened.
     std::unique_ptr<script::PreallocatedArrayBufferData> downloaded_data(
         new script::PreallocatedArrayBufferData());
-    response_body_->GetAndReset(downloaded_data.get());
+    response_body_->GetAndResetData(downloaded_data.get());
     auto array_buffer = script::ArrayBuffer::New(
         settings_->global_environment(), std::move(downloaded_data));
     response_array_buffer_reference_.reset(
diff --git a/src/components/update_client/component.cc b/src/components/update_client/component.cc
index df190d0..50ec274 100644
--- a/src/components/update_client/component.cc
+++ b/src/components/update_client/component.cc
@@ -195,6 +195,14 @@
 #endif
 
   if (result.error != UnpackerError::kNone) {
+#if defined(OS_STARBOARD)
+    // 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());
+    }
+#endif
     main_task_runner->PostTask(
         FROM_HERE,
         base::BindOnce(std::move(callback), ErrorCategory::kUnpack,
diff --git a/src/components/update_client/configurator.h b/src/components/update_client/configurator.h
index 5baa3cc..ab7e01e 100644
--- a/src/components/update_client/configurator.h
+++ b/src/components/update_client/configurator.h
@@ -171,7 +171,9 @@
   // parameters.
   virtual void SetChannel(const std::string& channel) = 0;
 
-  virtual bool IsChannelChanged() const = 0;
+  // Compare and swap the is_channel_changed flag.
+  virtual void CompareAndSwapChannelChanged(int old_value, int new_value) = 0;
+
 #endif
 
  protected:
diff --git a/src/components/update_client/test_configurator.h b/src/components/update_client/test_configurator.h
index f6ede80..c3fbda1 100644
--- a/src/components/update_client/test_configurator.h
+++ b/src/components/update_client/test_configurator.h
@@ -128,7 +128,7 @@
 
 #if defined(STARBOARD)
   void SetChannel(const std::string& channel) override {}
-  bool IsChannelChanged() const override { return false; }
+  void CompareAndSwapChannelChanged(int old_value, int new_value) override {}
 #else
   network::TestURLLoaderFactory* test_url_loader_factory() {
     return &test_url_loader_factory_;
diff --git a/src/components/update_client/update_checker.cc b/src/components/update_client/update_checker.cc
index c4045f0..3cfa2d5 100644
--- a/src/components/update_client/update_checker.cc
+++ b/src/components/update_client/update_checker.cc
@@ -44,14 +44,6 @@
 
 namespace {
 
-#if defined(COBALT_BUILD_TYPE_DEBUG) || defined(COBALT_BUILD_TYPE_DEVEL)
-const std::string kDefaultUpdaterChannel = "dev";
-#elif defined(COBALT_BUILD_TYPE_QA)
-const std::string kDefaultUpdaterChannel = "qa";
-#elif defined(COBALT_BUILD_TYPE_GOLD)
-const std::string kDefaultUpdaterChannel = "prod";
-#endif
-
 // Returns a sanitized version of the brand or an empty string otherwise.
 std::string SanitizeBrand(const std::string& brand) {
   return IsValidBrand(brand) ? brand : std::string("");
@@ -249,22 +241,6 @@
         MakeProtocolPing(app_id, metadata_)));
   }
   std::string updater_channel = config_->GetChannel();
-#if defined(OS_STARBOARD)
-  // If the updater channel is not set, read from pref store instead.
-  if (updater_channel.empty()) {
-    // All apps of the update use the same channel.
-    updater_channel = GetPersistedData()->GetUpdaterChannel(ids_checked_[0]);
-    if (updater_channel.empty()) {
-      updater_channel = kDefaultUpdaterChannel;
-    }
-    // Set the updater channel from the persistent store or to default channel,
-    // if it's not set already.
-    config_->SetChannel(updater_channel);
-  } else {
-    // Update the record of updater channel in pref store.
-    GetPersistedData()->SetUpdaterChannel(ids_checked_[0], updater_channel);
-  }
-#endif
 
   const auto request = MakeProtocolRequest(
       session_id, config_->GetProdId(),
@@ -284,6 +260,10 @@
       config_->EnabledCupSigning(),
       base::BindOnce(&UpdateCheckerImpl::OnRequestSenderComplete,
                      base::Unretained(this)));
+#if defined(OS_STARBOARD)
+  // Reset is_channel_changed flag to false if it is true
+  config_->CompareAndSwapChannelChanged(1, 0);
+#endif
 }
 
 void UpdateCheckerImpl::OnRequestSenderComplete(int error,
diff --git a/src/content/browser/speech/endpointer/endpointer.cc b/src/content/browser/speech/endpointer/endpointer.cc
index 5c19c4a..96d8e6c 100644
--- a/src/content/browser/speech/endpointer/endpointer.cc
+++ b/src/content/browser/speech/endpointer/endpointer.cc
@@ -89,7 +89,7 @@
 }
 
 #if defined(STARBOARD)
-EpStatus Endpointer::ProcessAudio(const ShellAudioBus& audio_bus, float* rms_out) {
+EpStatus Endpointer::ProcessAudio(const AudioBus& audio_bus, float* rms_out) {
   // TODO[Cobalt]: replace ShellAudioData with AudioChunk and deprecate
   // ShellAudioData.
   DCHECK_EQ(audio_bus.channels(), 1);
@@ -97,16 +97,16 @@
   const size_t num_samples = audio_bus.frames();
   const int16_t* audio_data = NULL;
 
-  ShellAudioBus int16_audio_bus(1, num_samples, ShellAudioBus::kInt16,
-                                ShellAudioBus::kInterleaved);
+  AudioBus int16_audio_bus(1, num_samples, AudioBus::kInt16,
+                           AudioBus::kInterleaved);
 
-  if (audio_bus.sample_type() == ShellAudioBus::kFloat32) {
+  if (audio_bus.sample_type() == AudioBus::kFloat32) {
     int16_audio_bus.Assign(audio_bus);
-    DCHECK_EQ(int16_audio_bus.sample_type(), ShellAudioBus::kInt16);
+    DCHECK_EQ(int16_audio_bus.sample_type(), AudioBus::kInt16);
     audio_data =
         reinterpret_cast<const int16_t*>(int16_audio_bus.interleaved_data());
   } else {
-    DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
+    DCHECK_EQ(audio_bus.sample_type(), AudioBus::kInt16);
     audio_data =
         reinterpret_cast<const int16_t*>(audio_bus.interleaved_data());
   }
diff --git a/src/content/browser/speech/endpointer/endpointer.h b/src/content/browser/speech/endpointer/endpointer.h
index 16bfed6..2264842 100644
--- a/src/content/browser/speech/endpointer/endpointer.h
+++ b/src/content/browser/speech/endpointer/endpointer.h
@@ -7,7 +7,7 @@
 
 #include <stdint.h>
 
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
 #include "content/browser/speech/endpointer/energy_endpointer.h"
 #include "content/common/content_export.h"
 
@@ -47,7 +47,7 @@
 // long_speech_input_complete_silence_length.
 class CONTENT_EXPORT Endpointer {
  public:
-  typedef cobalt::media::ShellAudioBus ShellAudioBus;
+  typedef cobalt::media::AudioBus AudioBus;
 
   explicit Endpointer(int sample_rate);
 
@@ -68,7 +68,7 @@
   // Process a segment of audio, which may be more than one frame.
   // The status of the last frame will be returned.
 #if defined(STARBOARD)
-  EpStatus ProcessAudio(const ShellAudioBus& audio_bus, float* rms_out);
+  EpStatus ProcessAudio(const AudioBus& audio_bus, float* rms_out);
 #else
   EpStatus ProcessAudio(const AudioChunk& raw_audio, float* rms_out);
 #endif
diff --git a/src/content/browser/speech/endpointer/endpointer_unittest.cc b/src/content/browser/speech/endpointer/endpointer_unittest.cc
index 632cc37..4b3cbb5 100644
--- a/src/content/browser/speech/endpointer/endpointer_unittest.cc
+++ b/src/content/browser/speech/endpointer/endpointer_unittest.cc
@@ -120,7 +120,7 @@
 class EndpointerFrameProcessor : public FrameProcessor {
  public:
 #if defined(STARBOARD)
-  typedef Endpointer::ShellAudioBus ShellAudioBus;
+  typedef Endpointer::AudioBus AudioBus;
 #endif
   explicit EndpointerFrameProcessor(Endpointer* endpointer)
       : endpointer_(endpointer) {}
@@ -129,7 +129,7 @@
                         int16_t* samples,
                         int frame_size) override {
 #if defined(STARBOARD)
-    auto frame = std::make_unique<ShellAudioBus>(1, kFrameSize, samples);
+    auto frame = std::make_unique<AudioBus>(1, kFrameSize, samples);
     endpointer_->ProcessAudio(*frame.get(), NULL);
 #else
     scoped_refptr<AudioChunk> frame(
diff --git a/src/docker-compose.yml b/src/docker-compose.yml
index 5d3f3a7..749a24a 100644
--- a/src/docker-compose.yml
+++ b/src/docker-compose.yml
@@ -1,27 +1,66 @@
 version: '2.4'
 
+volumes:
+  android-debug-keystore:
+  container-ccache:
+  container-starboard-toolchains:
+
 x-common-definitions: &common-definitions
   stdin_open: true
   tty: true
+
+x-build-volumes: &build-volumes
   volumes:
     - ${COBALT_SRC:-.}:/code/
-    - ./docker/linux/scripts:/scripts
-    - ${COBALT_SRC:-.}/out/ccache:/root/ccache
+    - android-debug-keystore:/root/.android/
+    - ${CCACHE_DIR:-container-ccache}:/root/ccache
+    - ${STARBOARD_TOOLCHAINS_DIR:-container-starboard-toolchains}:/root/starboard-toolchains
+
+x-build-common-definitions: &build-common-definitions
+  <<: *common-definitions
+  <<: *build-volumes
   depends_on:
-    - base
+    - build-base
 
 services:
-  # Define common build container for Linux
   base:
     build:
       args:
         - BASE_OS
       context: ./docker/linux
       dockerfile: base/Dockerfile
+    image: cobalt-base
+
+  base-xenial:
+    build:
+      args:
+        - BASE_OS=ubuntu
+        - BASE_OS_TAG=xenial
+      context: ./docker/linux
+      dockerfile: base/Dockerfile
+    image: base-xenial
+
+  # Define common build container for Linux
+  build-base:
+    build:
+      context: ./docker/linux
+      dockerfile: base/build/Dockerfile
     image: cobalt-build-base
+    depends_on:
+      - base
+
+  build-base-xenial:
+    build:
+      context: ./docker/linux
+      dockerfile: base/build/Dockerfile
+      args:
+        - FROM_IMAGE=base-xenial
+    image: build-base-xenial
+    depends_on:
+      - base-xenial
 
   stub:
-    <<: *common-definitions
+    <<: *build-common-definitions
     build:
       context: ./docker/linux
       dockerfile: stub/Dockerfile
@@ -31,18 +70,89 @@
       - CONFIG=${CONFIG:-debug}
 
   linux-x64x11:
-    <<: *common-definitions
+    <<: *build-common-definitions
     build:
       context: ./docker/linux
       dockerfile: linux-x64x11/Dockerfile
     image: cobalt-build-linux
+    depends_on: [ build-base ]
     environment:
       - PLATFORM=linux-x64x11
       - CONFIG=${CONFIG:-debug}
 
+  linux-x64x11-xenial:
+    <<: *common-definitions
+    <<: *build-volumes
+    build:
+      context: ./docker/linux
+      dockerfile: linux-x64x11/Dockerfile
+      args:
+        - FROM_IMAGE=build-base-xenial
+    image: linux-x64x11-xenial
+    depends_on:
+      - build-base-xenial
+
+  linux-x64x11-clang-3-6:
+    <<: *common-definitions
+    <<: *build-volumes
+    build:
+      context: ./docker/linux/
+      dockerfile: clang-3-6/Dockerfile
+    image: cobalt-build-linux-clang-3-6
+    environment:
+      - PLATFORM=linux-x64x11-clang-3-6
+      - CONFIG=${CONFIG:-debug}
+    depends_on:
+      - linux-x64x11-xenial
+
+  # Define common build container for Android
+  build-android:
+    <<: *build-common-definitions
+    build:
+      context: ./docker/linux/android
+      dockerfile: ./Dockerfile
+    image: cobalt-build-android
+
+  android-x86:
+    <<: *build-common-definitions
+    image: cobalt-build-android
+    depends_on: [ build-android ]
+    environment:
+      - IS_DOCKER=1
+      - PLATFORM=android-x86
+      - CONFIG=${CONFIG:-debug}
+
+  android-arm:
+    <<: *build-common-definitions
+    image: cobalt-build-android
+    depends_on: [ build-android ]
+    environment:
+      - IS_DOCKER=1
+      - PLATFORM=android-arm
+      - CONFIG=${CONFIG:-debug}
+
+  android-arm64:
+    <<: *build-common-definitions
+    image: cobalt-build-android
+    depends_on: [ build-android ]
+    environment:
+      - IS_DOCKER=1
+      - PLATFORM=android-arm64
+      - CONFIG=${CONFIG:-debug}
+
+  raspi:
+    <<: *build-common-definitions
+    build:
+      context: ./docker/linux/raspi
+      dockerfile: ./Dockerfile
+    image: cobalt-build-raspi
+    environment:
+      - PLATFORM
+      - CONFIG=${CONFIG:-debug}
+
   # Define common build container for Evergreen
   build-evergreen:
-    <<: *common-definitions
+    <<: *build-common-definitions
     build:
       context: ./docker/linux
       dockerfile: evergreen/Dockerfile
@@ -52,36 +162,51 @@
       - CONFIG
 
   evergreen-x64-sbversion-12:
-    <<: *common-definitions
+    <<: *build-common-definitions
     image: cobalt-build-evergreen
     depends_on: [ build-evergreen ]
     environment:
       - PLATFORM=evergreen-x64-sbversion-12
 
   evergreen-x86-sbversion-12:
-    <<: *common-definitions
+    <<: *build-common-definitions
     image: cobalt-build-evergreen
     depends_on: [ build-evergreen ]
     environment:
       - PLATFORM=evergreen-x86-sbversion-12
 
   evergreen-arm64-sbversion-12:
-    <<: *common-definitions
+    <<: *build-common-definitions
     image: cobalt-build-evergreen
     depends_on: [ build-evergreen ]
     environment:
       - PLATFORM=evergreen-arm64-sbversion-12
 
   evergreen-arm-hardfp-sbversion-12:
-    <<: *common-definitions
+    <<: *build-common-definitions
     image: cobalt-build-evergreen
     depends_on: [ build-evergreen ]
     environment:
       - PLATFORM=evergreen-arm-hardfp-sbversion-12
 
   evergreen-arm-softfp-sbversion-12:
-    <<: *common-definitions
+    <<: *build-common-definitions
     image: cobalt-build-evergreen
     depends_on: [ build-evergreen ]
     environment:
       - PLATFORM=evergreen-arm-softfp-sbversion-12
+
+  unittest:
+    <<: *common-definitions
+    build:
+      context: ./docker/linux
+      dockerfile: unittest/Dockerfile
+    image: cobalt-unittest
+    environment:
+      - PLATFORM=${PLATFORM:-linux-x64x11}
+      - CONFIG=${CONFIG:-debug}
+    volumes:
+      - ${COBALT_SRC:-.}/out/${PLATFORM:-linux-x64x11}_${CONFIG:-debug}:/out
+    # TODO: Get NPLB unittests to run with IPv6 without using the host network.
+    network_mode: "host"
+    depends_on: [ base ]
diff --git a/src/docker/linux/android/Dockerfile b/src/docker/linux/android/Dockerfile
new file mode 100644
index 0000000..ad34732
--- /dev/null
+++ b/src/docker/linux/android/Dockerfile
@@ -0,0 +1,34 @@
+FROM cobalt-build-base
+
+RUN apt update -qqy \
+    && apt install -qqy --no-install-recommends \
+        binutils \
+        bison \
+        default-jdk \
+        g++-multilib \
+        ninja-build \
+        pkgconf \
+        python-requests \
+        yasm \
+    && apt-get clean autoclean \
+    && apt-get autoremove -y --purge \
+    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
+    && rm -rf /var/lib/{apt,dpkg,cache,log} \
+    && echo "Done"
+
+RUN mkdir -p /root/.android
+
+CMD (test -f /root/.android/debug.keystore \
+    && echo "Android debug keystore exists." \
+    || (keytool -genkey -v \
+               -keystore /root/.android/debug.keystore \
+               -dname "cn=Android Docker, ou=YouTube, o=Google, c=US" \
+               -storepass android \
+               -alias androiddebugkey \
+               -keypass android \
+               -keyalg RSA \
+               -keysize 2048 \
+               -validity 10000 \
+    && echo "Generated Android Debug keystore.")) \
+    && /code/cobalt/build/gyp_cobalt -v -C ${CONFIG} ${PLATFORM} \
+    && ninja -C ${OUTDIR}/${PLATFORM}_${CONFIG} ${TARGET:-cobalt_deploy}
diff --git a/src/docker/linux/base/Dockerfile b/src/docker/linux/base/Dockerfile
index 59e3da8..2746def 100644
--- a/src/docker/linux/base/Dockerfile
+++ b/src/docker/linux/base/Dockerfile
@@ -1,5 +1,6 @@
 ARG BASE_OS
-FROM ${BASE_OS:-gcr.io/cloud-marketplace-containers/google/debian9}
+ARG BASE_OS_TAG
+FROM ${BASE_OS:-gcr.io/cloud-marketplace-containers/google/debian9}:${BASE_OS_TAG:-latest}
 
 ENV PYTHONUNBUFFERED 1
 
@@ -15,36 +16,4 @@
     && rm -rf /var/lib/{apt,dpkg,cache,log} \
     && echo "Done"
 
-# === Get Nodejs pinned LTS version via NVM
-ENV NVM_DIR /root/.nvm
-ENV NODE_VERSION 12.17.0
-
-RUN curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash
-
-RUN . $NVM_DIR/nvm.sh \
-   && nvm install --lts \
-   && nvm alias default lts/* \
-   && nvm use default
-
-ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
-ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
-
-# === Install depot_tools
-RUN git clone https://cobalt.googlesource.com/depot_tools /depot_tools
-
-# === Configure common env vars
-ENV PATH="${PATH}:/depot_tools:/root/fake_goma" \
-    OUTDIR=out \
-    DEPOT_TOOLS_UPDATE=0 \
-    NINJA_STATUS="[%f/%t %c/sec] " \
-    CCACHE_DIR=/root/ccache \
-    CCACHE_MAXSIZE=30G
-
-# == Set up gclient and fake Goma with ccache
-COPY ./files/fake_goma /root/fake_goma
-RUN cd /tmp && gclient verify || true \
-    && chmod +x /root/fake_goma/gomacc /root/fake_goma/goma_ctl.py \
-    && mkdir /root/ccache
-
-WORKDIR /code
 CMD ["/usr/bin/python","--version"]
diff --git a/src/docker/linux/base/build/Dockerfile b/src/docker/linux/base/build/Dockerfile
new file mode 100644
index 0000000..545908d
--- /dev/null
+++ b/src/docker/linux/base/build/Dockerfile
@@ -0,0 +1,34 @@
+ARG FROM_IMAGE
+FROM ${FROM_IMAGE:-cobalt-base}
+
+# === Get Nodejs pinned LTS version via NVM
+ENV NVM_DIR /root/.nvm
+ENV NODE_VERSION 12.17.0
+
+RUN curl --silent -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash
+
+RUN . $NVM_DIR/nvm.sh \
+   && nvm install --lts \
+   && nvm alias default lts/* \
+   && nvm use default
+
+ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
+ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH
+
+# === Install depot_tools
+RUN git clone https://cobalt.googlesource.com/depot_tools /depot_tools
+
+# === Configure common env vars
+ENV PATH="${PATH}:/depot_tools" \
+    OUTDIR=out \
+    DEPOT_TOOLS_UPDATE=0 \
+    NINJA_STATUS="[%f/%t %c/sec] " \
+    CCACHE_DIR=/root/ccache \
+    CCACHE_MAXSIZE=30G
+
+# == Set up gclient and ccache
+RUN cd /tmp && gclient verify || true \
+    && mkdir /root/ccache
+
+WORKDIR /code
+CMD ["/usr/bin/python","--version"]
diff --git a/src/docker/linux/clang-3-6/Dockerfile b/src/docker/linux/clang-3-6/Dockerfile
new file mode 100644
index 0000000..0bf3926
--- /dev/null
+++ b/src/docker/linux/clang-3-6/Dockerfile
@@ -0,0 +1,14 @@
+FROM linux-x64x11-xenial
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN apt update -qqy \
+    && apt install -qqy --no-install-recommends clang-3.6 \
+    && apt-get clean autoclean \
+    && apt-get autoremove -y --purge \
+    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
+    && rm -rf /var/lib/{apt,dpkg,cache,log} \
+    && echo "Done"
+
+CMD /code/cobalt/build/gyp_cobalt -v -C ${CONFIG} ${PLATFORM} && \
+    ninja -C ${OUTDIR}/${PLATFORM}_${CONFIG} ${TARGET:-cobalt_deploy}
diff --git a/src/docker/linux/files/fake_goma/goma_ctl.py b/src/docker/linux/files/fake_goma/goma_ctl.py
deleted file mode 100755
index 2f2ceb9..0000000
--- a/src/docker/linux/files/fake_goma/goma_ctl.py
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env python
-
-print("Faking Goma via ccache")
diff --git a/src/docker/linux/files/fake_goma/gomacc b/src/docker/linux/files/fake_goma/gomacc
deleted file mode 100755
index d64f812..0000000
--- a/src/docker/linux/files/fake_goma/gomacc
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-ccache "$@"
diff --git a/src/docker/linux/linux-x64x11/Dockerfile b/src/docker/linux/linux-x64x11/Dockerfile
index 524f320..29f7d8d 100644
--- a/src/docker/linux/linux-x64x11/Dockerfile
+++ b/src/docker/linux/linux-x64x11/Dockerfile
@@ -1,4 +1,5 @@
-FROM cobalt-build-base
+ARG FROM_IMAGE
+FROM ${FROM_IMAGE:-cobalt-build-base}
 
 RUN apt update -qqy \
     && apt install -qqy --no-install-recommends \
diff --git a/src/docker/linux/raspi/Dockerfile b/src/docker/linux/raspi/Dockerfile
new file mode 100644
index 0000000..7ed69d0
--- /dev/null
+++ b/src/docker/linux/raspi/Dockerfile
@@ -0,0 +1,36 @@
+FROM cobalt-build-base
+
+ARG raspi_home=/root/raspi-home
+ARG raspi_sysroot=raspbian_lite_2017-07-05_sysroot.tar.xz
+
+# Required by the gyp build system.
+ENV RASPI_HOME=${raspi_home}
+
+RUN apt update -qqy \
+    && apt install -qqy --no-install-recommends \
+        binutils \
+        bison \
+        g++-multilib \
+        ninja-build \
+        pkgconf \
+        python-requests \
+        wget \
+        xz-utils \
+        yasm \
+    && apt-get clean autoclean \
+    && apt-get autoremove -y --purge \
+    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
+    && rm -rf /var/lib/{apt,dpkg,cache,log} \
+    && echo "Done"
+
+RUN mkdir -p ${raspi_home}/tools \
+    && git clone https://github.com/raspberrypi/tools.git ${raspi_home}/tools
+
+RUN cd ${raspi_home} \
+    && wget -q \
+    "https://storage.googleapis.com/cobalt-static-storage/${raspi_sysroot}"
+
+RUN cd ${raspi_home} && tar Jxpvf ${raspi_sysroot}
+
+CMD /code/cobalt/build/gyp_cobalt -v -C ${CONFIG} ${PLATFORM} \
+    && ninja -C ${OUTDIR}/${PLATFORM}_${CONFIG} ${TARGET:-cobalt_deploy}
diff --git a/src/docker/linux/unittest/Dockerfile b/src/docker/linux/unittest/Dockerfile
new file mode 100644
index 0000000..e876c8d
--- /dev/null
+++ b/src/docker/linux/unittest/Dockerfile
@@ -0,0 +1,35 @@
+FROM cobalt-base
+
+RUN apt update -qqy \
+    && apt install -qqy --no-install-recommends \
+        libasound2 \
+        libavcodec57 \
+        libavformat57 \
+        libavresample3 \
+        libavutil55 \
+        libegl1-mesa \
+        libgl1-mesa-dri \
+        libgles2-mesa \
+        libx11-6 \
+        libxcomposite1 \
+        libxrender1 \
+        unzip \
+        xauth \
+        xvfb \
+    && apt-get clean autoclean \
+    && apt-get autoremove -y --purge \
+    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
+    && rm -rf /var/lib/{apt,dpkg,cache,log} \
+    && echo "Done"
+
+WORKDIR /out
+
+# Sets the locale in the environment. This is needed for NPLB unit tests.
+ENV LANG en_US.UTF-8
+
+RUN mkdir -p /app_launcher_out
+
+CMD unzip -q /out/app_launcher -d /app_launcher_out && \
+    xvfb-run --server-args="-screen 0 1920x1080x24 +render +extension GLX -noreset" \
+    python /app_launcher_out/starboard/tools/testing/test_runner.py --run \
+           -o /out --platform $PLATFORM --config $CONFIG
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index cdd706c..40aab86 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -261,6 +261,23 @@
 when those settings change. For older starboard versions, use
 kSbEventTypeAccessiblitySettingsChanged instead.
 
+### Add extension to SbMediaCanPlayMimeAndKeySystem() for encryptionScheme.
+
+Now the Starboard implementation may choose to support |key_system| with extra
+attributes, in order to selectively support encryption schemes on particular
+containers or codecs.
+The Starboard implementation needn't support |key_system| with extra attributes
+if it meets the requirements for the default implementation of
+`Navigator.requestMediaKeySystemAccess()`, which assumes that:
+1. When the Widevine DRM system is used, all the encryption schemes ('cenc',
+   'cbcs', 'cbcs-1-9') should be supported across all containers and codecs
+   supported by the platform.
+2. When the PlayReady DRM system is used, only 'cenc' is supported across all
+   containers and codecs supported by the platform.
+
+Please see the comment of `SbMediaCanPlayMimeAndKeySystem()` in `media.h` for
+more details.
+
 ## Version 11
 
 ### Add arguments to `SbMediaIsVideoSupported`.
diff --git a/src/starboard/android/apk/app/build.gradle b/src/starboard/android/apk/app/build.gradle
index fdf3b29..5ab0feb 100644
--- a/src/starboard/android/apk/app/build.gradle
+++ b/src/starboard/android/apk/app/build.gradle
@@ -66,7 +66,7 @@
     defaultConfig {
         applicationId "dev.cobalt.coat"
         minSdkVersion 21
-        targetSdkVersion 30
+        targetSdkVersion 29
         versionCode 1
         versionName "${buildId}"
         manifestPlaceholders = [applicationName: "CoAT: ${cobaltTarget}"]
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index fe988bb..9cee635 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -30,7 +30,7 @@
 import android.os.Build;
 import android.util.Size;
 import android.util.SizeF;
-import android.view.Display;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.CaptioningManager;
 import androidx.annotation.RequiresApi;
@@ -155,13 +155,17 @@
   @SuppressWarnings("unused")
   @UsedByNative
   protected void beforeSuspend() {
-    Log.i(TAG, "Prepare to suspend");
-    // We want the MediaSession to be deactivated immediately before suspending so that by the time
-    // the launcher is visible our "Now Playing" card is already gone. Then Cobalt and the web app
-    // can take their time suspending after that.
-    cobaltMediaSession.suspend();
-    for (CobaltService service : cobaltServices.values()) {
-      service.beforeSuspend();
+    try {
+      Log.i(TAG, "Prepare to suspend");
+      // We want the MediaSession to be deactivated immediately before suspending so that by the time
+      // the launcher is visible our "Now Playing" card is already gone. Then Cobalt and the web app
+      // can take their time suspending after that.
+      cobaltMediaSession.suspend();
+      for (CobaltService service : cobaltServices.values()) {
+        service.beforeSuspend();
+      }
+    } catch (Throwable e) {
+      Log.i(TAG, "Caught exception in beforeSuspend: " + e.getMessage());
     }
   }
 
@@ -521,12 +525,18 @@
       return false;
     }
 
-    Display defaultDisplay = DisplayUtil.getDefaultDisplay(activityHolder.get());
-    if (defaultDisplay == null) {
+    Activity activity = activityHolder.get();
+    if (activity == null) {
       return false;
     }
 
-    int[] supportedHdrTypes = defaultDisplay.getHdrCapabilities().getSupportedHdrTypes();
+    WindowManager windowManager = activity.getWindowManager();
+    if (windowManager == null) {
+      return false;
+    }
+
+    int[] supportedHdrTypes =
+        windowManager.getDefaultDisplay().getHdrCapabilities().getSupportedHdrTypes();
     for (int supportedType : supportedHdrTypes) {
       if (supportedType == hdrType) {
         return true;
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
index 42c4e98..faa8742 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -69,7 +69,6 @@
   private static final String KEY_CROP_TOP = "crop-top";
 
   private static final int BITRATE_ADJUSTMENT_FPS = 30;
-  private static final int MAXIMUM_INITIAL_FPS = 30;
 
   private long mNativeMediaCodecBridge;
   private MediaCodec mMediaCodec;
@@ -78,6 +77,8 @@
   private long mLastPresentationTimeUs;
   private final String mMime;
   private boolean mAdaptivePlaybackSupported;
+  private double mPlaybackRate = 1.0;
+  private int mFps = 30;
 
   // Functions that require this will be called frequently in a tight loop.
   // Only create one of these and reuse it to avoid excessive allocations,
@@ -104,6 +105,45 @@
     public static final String VIDEO_AV1 = "video/av01";
   }
 
+  private class FrameRateEstimator {
+    private static final int INVALID_FRAME_RATE = -1;
+    private static final long INVALID_FRAME_TIMESTAMP = -1;
+    private static final int MINIMUN_REQUIRED_FRAMES = 4;
+    private long mLastFrameTimestampUs = INVALID_FRAME_TIMESTAMP;
+    private long mNumberOfFrames = 0;
+    private long mTotalDurationUs = 0;
+
+    public int getEstimatedFrameRate() {
+      if (mTotalDurationUs <= 0 || mNumberOfFrames < MINIMUN_REQUIRED_FRAMES) {
+        return INVALID_FRAME_RATE;
+      }
+      return Math.round((mNumberOfFrames - 1) * 1000000.0f / mTotalDurationUs);
+    }
+
+    public void reset() {
+      mLastFrameTimestampUs = INVALID_FRAME_TIMESTAMP;
+      mNumberOfFrames = 0;
+      mTotalDurationUs = 0;
+    }
+
+    public void onNewFrame(long presentationTimeUs) {
+      mNumberOfFrames++;
+
+      if (mLastFrameTimestampUs == INVALID_FRAME_TIMESTAMP) {
+        mLastFrameTimestampUs = presentationTimeUs;
+        return;
+      }
+      if (presentationTimeUs <= mLastFrameTimestampUs) {
+        Log.v(TAG, String.format("Invalid output presentation timestamp."));
+        return;
+      }
+
+      mTotalDurationUs += presentationTimeUs - mLastFrameTimestampUs;
+      mLastFrameTimestampUs = presentationTimeUs;
+    }
+  }
+
+  private FrameRateEstimator mFrameRateEstimator = null;
   private BitrateAdjustmentTypes mBitrateAdjustmentType = BitrateAdjustmentTypes.NO_ADJUSTMENT;
 
   @SuppressWarnings("unused")
@@ -415,6 +455,14 @@
                   info.offset,
                   info.presentationTimeUs,
                   info.size);
+              if (mFrameRateEstimator != null) {
+                mFrameRateEstimator.onNewFrame(info.presentationTimeUs);
+                int fps = mFrameRateEstimator.getEstimatedFrameRate();
+                if (fps != FrameRateEstimator.INVALID_FRAME_RATE && mFps != fps) {
+                  mFps = fps;
+                  updateOperatingRate();
+                }
+              }
             }
           }
 
@@ -599,6 +647,40 @@
 
   @SuppressWarnings("unused")
   @UsedByNative
+  private void setPlaybackRate(double playbackRate) {
+    if (mPlaybackRate == playbackRate) {
+      return;
+    }
+    mPlaybackRate = playbackRate;
+    if (mFrameRateEstimator != null) {
+      updateOperatingRate();
+    }
+  }
+
+  private void updateOperatingRate() {
+    // We needn't set operation rate if playback rate is 0 or less.
+    if (Double.compare(mPlaybackRate, 0.0) <= 0) {
+      return;
+    }
+    if (mFps == FrameRateEstimator.INVALID_FRAME_RATE) {
+      return;
+    }
+    if (mFps <= 0) {
+      Log.e(TAG, "Failed to set operating rate with invalid fps " + mFps);
+      return;
+    }
+    double operatingRate = mPlaybackRate * mFps;
+    Bundle b = new Bundle();
+    b.putFloat(MediaFormat.KEY_OPERATING_RATE, (float) operatingRate);
+    try {
+      mMediaCodec.setParameters(b);
+    } catch (IllegalStateException e) {
+      Log.e(TAG, "Failed to set MediaCodec operating rate", e);
+    }
+  }
+
+  @SuppressWarnings("unused")
+  @UsedByNative
   private int flush() {
     try {
       mFlushed = true;
@@ -830,17 +912,23 @@
       if (mAdaptivePlaybackSupported) {
         // Since we haven't passed the properties of the stream we're playing
         // down to this level, from our perspective, we could potentially
-        // adapt up to 8k at any point.  We thus request 8k buffers up front,
+        // adapt up to 8k at any point. We thus request 8k buffers up front,
         // unless the decoder claims to not be able to do 8k, in which case
         // we're ok, since we would've rejected a 8k stream when canPlayType
         // was called, and then use those decoder values instead.
-        int maxWidth = Math.min(7680, maxSupportedWidth);
-        int maxHeight = Math.min(4320, maxSupportedHeight);
-        format.setInteger(MediaFormat.KEY_MAX_WIDTH, maxWidth);
-        format.setInteger(MediaFormat.KEY_MAX_HEIGHT, maxHeight);
+        if (Build.VERSION.SDK_INT > 22) {
+          format.setInteger(MediaFormat.KEY_MAX_WIDTH, Math.min(7680, maxSupportedWidth));
+          format.setInteger(MediaFormat.KEY_MAX_HEIGHT, Math.min(4320, maxSupportedHeight));
+        } else {
+          // Android 5.0/5.1 seems not support 8K. Fallback to 4K until we get a
+          // better way to get maximum supported resolution.
+          format.setInteger(MediaFormat.KEY_MAX_WIDTH, Math.min(3840, maxSupportedWidth));
+          format.setInteger(MediaFormat.KEY_MAX_HEIGHT, Math.min(2160, maxSupportedHeight));
+        }
       }
       maybeSetMaxInputSize(format);
       mMediaCodec.configure(format, surface, crypto, flags);
+      mFrameRateEstimator = new FrameRateEstimator();
       return true;
     } catch (IllegalArgumentException e) {
       Log.e(TAG, "Cannot configure the video codec with IllegalArgumentException: ", e);
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
index 1e39d63..0274811 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
@@ -35,9 +35,8 @@
 import android.os.Message;
 import android.view.Choreographer;
 import android.view.Choreographer.FrameCallback;
-import android.view.Display;
+import android.view.WindowManager;
 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. */
@@ -222,9 +221,9 @@
   }
 
   private static double getDefaultDisplayRefreshRate(Context context) {
-    Display defaultDisplay = DisplayUtil.getDefaultDisplay(context);
-    return defaultDisplay != null
-        ?  defaultDisplay.getRefreshRate()
+    WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+    return manager.getDefaultDisplay() != null
+        ? manager.getDefaultDisplay().getRefreshRate()
         : DISPLAY_REFRESH_RATE_UNKNOWN;
   }
 
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java
index c7c2208..4b23394 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java
@@ -15,14 +15,10 @@
 package dev.cobalt.util;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.util.DisplayMetrics;
 import android.util.Size;
 import android.util.SizeF;
-import android.view.Display;
 import android.view.WindowManager;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
 
 /** Utility functions for querying display attributes. */
 public class DisplayUtil {
@@ -30,34 +26,6 @@
   private DisplayUtil() {}
 
   /**
-   * Returns the default display associated with a context.
-   */
-  @Nullable
-  public static Display getDefaultDisplay(Context context) {
-    if (context == null) {
-      return null;
-    }
-    if (android.os.Build.VERSION.SDK_INT >= 30) {
-      return getDefaultDisplayV30(context);
-    } else {
-      return getDefaultDisplayDeprecated(context);
-    }
-  }
-
-  @Nullable
-  @SuppressWarnings("deprecation")
-  private static Display getDefaultDisplayDeprecated(Context context) {
-    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-    return (wm == null) ? null : wm.getDefaultDisplay();
-  }
-
-  @Nullable
-  @RequiresApi(30)
-  private static Display getDefaultDisplayV30(Context context) {
-    return context.getDisplay();
-  }
-
-  /**
    * Returns the physical pixels per inch of the screen in the X and Y
    * dimensions.
    */
@@ -114,10 +82,10 @@
   private static DisplayMetrics cachedDisplayMetrics = null;
 
   private static DisplayMetrics getDisplayMetrics(Context context) {
-    Resources.getSystem().getDisplayMetrics();
     if (cachedDisplayMetrics == null) {
       cachedDisplayMetrics = new DisplayMetrics();
-      getDefaultDisplay(context).getRealMetrics(cachedDisplayMetrics);
+      ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
+          .getDefaultDisplay().getRealMetrics(cachedDisplayMetrics);
     }
     return cachedDisplayMetrics;
   }
diff --git a/src/starboard/android/apk/build.id b/src/starboard/android/apk/build.id
deleted file mode 100644
index 20ae2c6..0000000
--- a/src/starboard/android/apk/build.id
+++ /dev/null
@@ -1 +0,0 @@
-277473
\ No newline at end of file
diff --git a/src/starboard/android/shared/android_main.cc b/src/starboard/android/shared/android_main.cc
index 1f6513d..a44497e 100644
--- a/src/starboard/android/shared/android_main.cc
+++ b/src/starboard/android/shared/android_main.cc
@@ -67,6 +67,7 @@
   if (j_url) {
     start_url = env->GetStringStandardUTFOrAbort(j_url.Get());
   }
+  SB_LOG(INFO) << "GetStartDeepLink: " << start_url;
   return start_url;
 }
 
diff --git a/src/starboard/android/shared/application_android.cc b/src/starboard/android/shared/application_android.cc
index e8fab42..1352a26 100644
--- a/src/starboard/android/shared/application_android.cc
+++ b/src/starboard/android/shared/application_android.cc
@@ -69,6 +69,8 @@
         return "WindowFocusGained";
       case ApplicationAndroid::AndroidCommand::kWindowFocusLost:
         return "WindowFocusLost";
+      case ApplicationAndroid::AndroidCommand::kDeepLink:
+        return "DeepLink";
       default:
         return "unknown";
     }
@@ -298,6 +300,21 @@
     case AndroidCommand::kStop:
       sync_state = activity_state_ = cmd.type;
       break;
+    case AndroidCommand::kDeepLink:
+      char* deep_link = static_cast<char*>(cmd.data);
+      SB_LOG(INFO) << "AndroidCommand::kDeepLink: deep_link=" << deep_link
+                   << " state=" << state();
+      if (deep_link != NULL) {
+        if (state() == kStateUnstarted) {
+          SetStartLink(deep_link);
+          SB_LOG(INFO) << "ApplicationAndroid SetStartLink";
+          SbMemoryDeallocate(static_cast<void*>(deep_link));
+        } else {
+          SB_LOG(INFO) << "ApplicationAndroid Inject: kSbEventTypeLink";
+          Inject(new Event(kSbEventTypeLink, deep_link, SbMemoryDeallocate));
+        }
+      }
+      break;
   }
 
   // If there's a window, sync the app state to the Activity lifecycle, letting
@@ -337,7 +354,7 @@
     // Android main thread. This lets the MediaSession get released now without
     // having to wait to bounce between threads.
     JniEnvExt* env = JniEnvExt::Get();
-    env->CallStarboardVoidMethodOrAbort("beforeSuspend", "()V");
+    env->CallStarboardVoidMethod("beforeSuspend", "()V");
   }
   AndroidCommand cmd {type, data};
   ScopedLock lock(android_command_mutex_);
@@ -538,12 +555,14 @@
 }
 
 void ApplicationAndroid::HandleDeepLink(const char* link_url) {
+  SB_LOG(INFO) << "ApplicationAndroid::HandleDeepLink link_url=" << link_url;
   if (link_url == NULL || link_url[0] == '\0') {
     return;
   }
   char* deep_link = SbStringDuplicate(link_url);
   SB_DCHECK(deep_link);
-  Inject(new Event(kSbEventTypeLink, deep_link, SbMemoryDeallocate));
+
+  SendAndroidCommand(AndroidCommand::kDeepLink, deep_link);
 }
 
 extern "C" SB_EXPORT_PLATFORM
diff --git a/src/starboard/android/shared/application_android.h b/src/starboard/android/shared/application_android.h
index 3ad4820..6dad099 100644
--- a/src/starboard/android/shared/application_android.h
+++ b/src/starboard/android/shared/application_android.h
@@ -51,6 +51,7 @@
       kNativeWindowDestroyed,
       kWindowFocusGained,
       kWindowFocusLost,
+      kDeepLink,
     } CommandType;
 
     CommandType type;
diff --git a/src/starboard/android/shared/audio_track_audio_sink_type.h b/src/starboard/android/shared/audio_track_audio_sink_type.h
index 1a8cb37..43b0c86 100644
--- a/src/starboard/android/shared/audio_track_audio_sink_type.h
+++ b/src/starboard/android/shared/audio_track_audio_sink_type.h
@@ -74,10 +74,10 @@
                                        SbMediaAudioSampleType sample_type,
                                        int sampling_frequency_hz);
 
-  MinRequiredFramesTester min_required_frames_tester_;
   Mutex min_required_frames_map_mutex_;
   // The minimum frames required to avoid underruns of different frequencies.
   std::map<int, int> min_required_frames_map_;
+  MinRequiredFramesTester min_required_frames_tester_;
 };
 
 class AudioTrackAudioSink : public SbAudioSinkPrivate {
diff --git a/src/starboard/android/shared/cobalt/configuration.py b/src/starboard/android/shared/cobalt/configuration.py
index 96c11fe..f9cf7d7 100644
--- a/src/starboard/android/shared/cobalt/configuration.py
+++ b/src/starboard/android/shared/cobalt/configuration.py
@@ -66,6 +66,11 @@
 
   # A map of failing or crashing tests per target.
   __FILTERED_TESTS = {
+      'layout_tests': [
+          # Android relies of system fonts and some older Android builds do not
+          # have the update (Emoji 11.0) NotoColorEmoji.ttf installed.
+          'CSS3FontsLayoutTests/Layout.Test/color_emojis_should_render_properly'
+      ],
       'renderer_test': [
           # Instead of returning an error when allocating too much texture
           # memory, Android instead just terminates the process.  Since this
diff --git a/src/starboard/android/shared/gyp_configuration.py b/src/starboard/android/shared/gyp_configuration.py
index ce072b3..04d5372 100644
--- a/src/starboard/android/shared/gyp_configuration.py
+++ b/src/starboard/android/shared/gyp_configuration.py
@@ -146,7 +146,7 @@
     if not self._target_toolchain:
       tool_prefix = os.path.join(sdk_utils.GetNdkPath(), 'toolchains', 'llvm',
                                  'prebuilt', 'linux-x86_64', 'bin', '')
-      cc_path = tool_prefix + _ABI_TOOL_NAMES[self.android_abi][0]
+      cc_path = self.build_accelerator + ' ' + tool_prefix + _ABI_TOOL_NAMES[self.android_abi][0]
       cxx_path = cc_path + '++'
       ar_path = tool_prefix + _ABI_TOOL_NAMES[self.android_abi][1]
       clang_flags = [
@@ -243,7 +243,7 @@
     if not self._host_toolchain:
       if not hasattr(self, 'host_compiler_environment'):
         self.host_compiler_environment = build.GetHostCompilerEnvironment(
-            clang_build.GetClangSpecification(), False)
+            clang_build.GetClangSpecification(), self.build_accelerator)
       cc_path = self.host_compiler_environment['CC_host'],
       cxx_path = self.host_compiler_environment['CXX_host']
       self._host_toolchain = [
@@ -327,6 +327,18 @@
           'SbDirectoryGetNextTest.SunnyDayStaticContent',
           'SbDirectoryOpenTest.SunnyDayStaticContent',
           'SbFileGetPathInfoTest.WorksOnStaticContentDirectories',
+          # Android doesn't currently support specifying a bitrate under 8000
+          'SbMediaCanPlayMimeAndKeySystem.MinimumSupport',
+          # There are issues with playback of heeac format files where the input
+          # |frames_per_channel| is 6912, instead of 12544 (as with other
+          # formats).
+          'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/6',
+          # These tests are disabled due to not receiving the kEndOfStream
+          # player state update within the specified timeout.
+          'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/7',
+          'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/8',
+          'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/9',
+          'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/10',
       ],
   }
 
diff --git a/src/starboard/android/shared/media_codec_bridge.cc b/src/starboard/android/shared/media_codec_bridge.cc
index b39a61d..f78c347 100644
--- a/src/starboard/android/shared/media_codec_bridge.cc
+++ b/src/starboard/android/shared/media_codec_bridge.cc
@@ -344,6 +344,11 @@
                                           render_timestamp_ns);
 }
 
+void MediaCodecBridge::SetPlaybackRate(double playback_rate) {
+  JniEnvExt::Get()->CallVoidMethodOrAbort(
+      j_media_codec_bridge_, "setPlaybackRate", "(D)V", playback_rate);
+}
+
 jint MediaCodecBridge::Flush() {
   return JniEnvExt::Get()->CallIntMethodOrAbort(j_media_codec_bridge_, "flush",
                                                 "()I");
diff --git a/src/starboard/android/shared/media_codec_bridge.h b/src/starboard/android/shared/media_codec_bridge.h
index 1fc6618..77452c1 100644
--- a/src/starboard/android/shared/media_codec_bridge.h
+++ b/src/starboard/android/shared/media_codec_bridge.h
@@ -131,6 +131,7 @@
   void ReleaseOutputBuffer(jint index, jboolean render);
   void ReleaseOutputBufferAtTimestamp(jint index, jlong render_timestamp_ns);
 
+  void SetPlaybackRate(double playback_rate);
   jint Flush();
   SurfaceDimensions GetOutputDimensions();
   AudioOutputFormatResult GetAudioOutputFormat();
diff --git a/src/starboard/android/shared/media_decoder.cc b/src/starboard/android/shared/media_decoder.cc
index d5668d0..fc5fa0d 100644
--- a/src/starboard/android/shared/media_decoder.cc
+++ b/src/starboard/android/shared/media_decoder.cc
@@ -177,6 +177,12 @@
   }
 }
 
+void MediaDecoder::SetPlaybackRate(double playback_rate) {
+  SB_DCHECK(media_type_ == kSbMediaTypeVideo);
+  SB_DCHECK(media_codec_bridge_);
+  media_codec_bridge_->SetPlaybackRate(playback_rate);
+}
+
 // static
 void* MediaDecoder::DecoderThreadEntryPoint(void* context) {
   SB_DCHECK(context);
diff --git a/src/starboard/android/shared/media_decoder.h b/src/starboard/android/shared/media_decoder.h
index 41a7a11..80d0b7e 100644
--- a/src/starboard/android/shared/media_decoder.h
+++ b/src/starboard/android/shared/media_decoder.h
@@ -86,6 +86,8 @@
   void WriteInputBuffer(const scoped_refptr<InputBuffer>& input_buffer);
   void WriteEndOfStream();
 
+  void SetPlaybackRate(double playback_rate);
+
   size_t GetNumberOfPendingTasks() const {
     return number_of_pending_tasks_.load();
   }
diff --git a/src/starboard/android/shared/media_is_supported.cc b/src/starboard/android/shared/media_is_supported.cc
index 26d1e19..42aa227 100644
--- a/src/starboard/android/shared/media_is_supported.cc
+++ b/src/starboard/android/shared/media_is_supported.cc
@@ -16,12 +16,19 @@
 
 #include "starboard/android/shared/jni_env_ext.h"
 #include "starboard/android/shared/media_common.h"
+#include "starboard/string.h"
 
 bool SbMediaIsSupported(SbMediaVideoCodec video_codec,
                         SbMediaAudioCodec audio_codec,
                         const char* key_system) {
   using starboard::android::shared::IsWidevineL1;
   using starboard::android::shared::JniEnvExt;
+
+  if (SbStringFindCharacter(key_system, ';')) {
+    // TODO: Remove this check and enable key system with attributes support.
+    return false;
+  }
+
   // Filter anything other then aac as we only support paid content on aac.
   // TODO: Add support of Opus if we are going to support software based drm
   // systems.
diff --git a/src/starboard/android/shared/player_components_factory.cc b/src/starboard/android/shared/player_components_factory.cc
index 948b85a..6ac984a 100644
--- a/src/starboard/android/shared/player_components_factory.cc
+++ b/src/starboard/android/shared/player_components_factory.cc
@@ -38,6 +38,15 @@
 
 namespace {
 
+const int kAudioSinkFramesAlignment = 256;
+const int kDefaultAudioSinkMinFramesPerAppend = 1024;
+const int kDefaultAudioSinkMaxCachedFrames =
+    8 * kDefaultAudioSinkMinFramesPerAppend;
+
+int AlignUp(int value, int alignment) {
+  return (value + alignment - 1) / alignment * alignment;
+}
+
 class PlayerComponentsFactory : public PlayerComponents::Factory {
   bool CreateSubComponents(
       const CreationParameters& creation_parameters,
@@ -94,6 +103,8 @@
               creation_parameters.decode_target_graphics_context_provider(),
               creation_parameters.max_video_capabilities(), error_message));
       if (video_decoder_impl->is_valid()) {
+        video_render_algorithm->reset(new android::shared::VideoRenderAlgorithm(
+            video_decoder_impl.get()));
         *video_renderer_sink = video_decoder_impl->GetSink();
         video_decoder->reset(video_decoder_impl.release());
       } else {
@@ -103,12 +114,35 @@
             "Failed to create video decoder with error: " + *error_message;
         return false;
       }
-
-      video_render_algorithm->reset(new android::shared::VideoRenderAlgorithm);
     }
 
     return true;
   }
+
+  void GetAudioRendererParams(const CreationParameters& creation_parameters,
+                              int* max_cached_frames,
+                              int* min_frames_per_append) const override {
+    SB_DCHECK(max_cached_frames);
+    SB_DCHECK(min_frames_per_append);
+    SB_DCHECK(kDefaultAudioSinkMinFramesPerAppend % kAudioSinkFramesAlignment ==
+              0);
+    *min_frames_per_append = kDefaultAudioSinkMinFramesPerAppend;
+
+    // AudioRenderer prefers to use kSbMediaAudioSampleTypeFloat32 and only uses
+    // kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported.
+    int min_frames_required = SbAudioSinkGetMinBufferSizeInFrames(
+        creation_parameters.audio_sample_info().number_of_channels,
+        SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)
+            ? kSbMediaAudioSampleTypeFloat32
+            : kSbMediaAudioSampleTypeInt16Deprecated,
+        creation_parameters.audio_sample_info().samples_per_second);
+    // On Android 5.0, the size of audio renderer sink buffer need to be two
+    // times larger than AudioTrack minBufferSize. Otherwise, AudioTrack may
+    // stop working after pause.
+    *max_cached_frames =
+        min_frames_required * 2 + kDefaultAudioSinkMinFramesPerAppend;
+    *max_cached_frames = AlignUp(*max_cached_frames, kAudioSinkFramesAlignment);
+  }
 };
 
 }  // namespace
diff --git a/src/starboard/android/shared/video_decoder.cc b/src/starboard/android/shared/video_decoder.cc
index 5a5030e..96a490a 100644
--- a/src/starboard/android/shared/video_decoder.cc
+++ b/src/starboard/android/shared/video_decoder.cc
@@ -377,6 +377,7 @@
       media_decoder_->Initialize(
           std::bind(&VideoDecoder::ReportError, this, _1, _2));
     }
+    media_decoder_->SetPlaybackRate(playback_rate_);
     return true;
   }
   media_decoder_.reset();
@@ -577,6 +578,13 @@
   return kSbDecodeTargetInvalid;
 }
 
+void VideoDecoder::SetPlaybackRate(double playback_rate) {
+  playback_rate_ = playback_rate;
+  if (media_decoder_) {
+    media_decoder_->SetPlaybackRate(playback_rate);
+  }
+}
+
 void VideoDecoder::OnNewTextureAvailable() {
   has_new_texture_available_.store(true);
 }
diff --git a/src/starboard/android/shared/video_decoder.h b/src/starboard/android/shared/video_decoder.h
index 8202f57..508817f 100644
--- a/src/starboard/android/shared/video_decoder.h
+++ b/src/starboard/android/shared/video_decoder.h
@@ -83,6 +83,8 @@
   void Reset() override;
   SbDecodeTarget GetCurrentDecodeTarget() override;
 
+  void SetPlaybackRate(double playback_rate);
+
   bool is_valid() const { return media_decoder_ != NULL; }
 
   void OnNewTextureAvailable();
@@ -133,6 +135,8 @@
   int32_t frame_width_ = 0;
   int32_t frame_height_ = 0;
 
+  double playback_rate_ = 1.0;
+
   // The last enqueued |SbMediaColorMetadata|.
   optional<SbMediaColorMetadata> color_metadata_;
 
diff --git a/src/starboard/android/shared/video_render_algorithm.cc b/src/starboard/android/shared/video_render_algorithm.cc
index 1b7e9aa..b234937 100644
--- a/src/starboard/android/shared/video_render_algorithm.cc
+++ b/src/starboard/android/shared/video_render_algorithm.cc
@@ -18,6 +18,7 @@
 
 #include "starboard/android/shared/jni_utils.h"
 #include "starboard/android/shared/media_common.h"
+#include "starboard/common/log.h"
 
 namespace starboard {
 namespace android {
@@ -36,6 +37,12 @@
 
 }  // namespace
 
+VideoRenderAlgorithm::VideoRenderAlgorithm(VideoDecoder* video_decoder)
+    : video_decoder_(video_decoder) {
+  SB_DCHECK(video_decoder_);
+  video_decoder_->SetPlaybackRate(playback_rate_);
+}
+
 void VideoRenderAlgorithm::Render(
     MediaTimeProvider* media_time_provider,
     std::list<scoped_refptr<VideoFrame>>* frames,
@@ -61,6 +68,10 @@
     if (!is_audio_playing) {
       break;
     }
+    if (playback_rate != playback_rate_) {
+      playback_rate_ = playback_rate;
+      video_decoder_->SetPlaybackRate(playback_rate);
+    }
 
     jlong early_us = frames->front()->timestamp() - playback_time;
 
diff --git a/src/starboard/android/shared/video_render_algorithm.h b/src/starboard/android/shared/video_render_algorithm.h
index 187a1ca..132e871 100644
--- a/src/starboard/android/shared/video_render_algorithm.h
+++ b/src/starboard/android/shared/video_render_algorithm.h
@@ -18,6 +18,7 @@
 #include <list>
 
 #include "starboard/android/shared/jni_env_ext.h"
+#include "starboard/android/shared/video_decoder.h"
 #include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
 
 namespace starboard {
@@ -27,6 +28,8 @@
 class VideoRenderAlgorithm : public ::starboard::shared::starboard::player::
                                  filter::VideoRenderAlgorithm {
  public:
+  explicit VideoRenderAlgorithm(VideoDecoder* video_decoder);
+
   void Render(MediaTimeProvider* media_time_provider,
               std::list<scoped_refptr<VideoFrame>>* frames,
               VideoRendererSink::DrawFrameCB draw_frame_cb) override;
@@ -45,6 +48,8 @@
     jobject j_video_frame_release_time_helper_ = nullptr;
   };
 
+  VideoDecoder* video_decoder_ = nullptr;
+  double playback_rate_ = 1.0;
   VideoFrameReleaseTimeHelper video_frame_release_time_helper_;
   int dropped_frames_ = 0;
 };
diff --git a/src/starboard/android/shared/video_window.cc b/src/starboard/android/shared/video_window.cc
index 8329a85..88b7037 100644
--- a/src/starboard/android/shared/video_window.cc
+++ b/src/starboard/android/shared/video_window.cc
@@ -123,6 +123,11 @@
   // during painting.
   ScopedLock lock(*GetViewSurfaceMutex());
 
+  if (!g_native_video_window) {
+    SB_LOG(INFO) << "Tried to clear video window when it was null.";
+    return;
+  }
+
   if (g_reset_surface_on_clear_window) {
     int width = ANativeWindow_getWidth(g_native_video_window);
     int height = ANativeWindow_getHeight(g_native_video_window);
@@ -133,11 +138,6 @@
     }
   }
 
-  if (!g_native_video_window) {
-    SB_LOG(INFO) << "Tried to clear video window when it was null.";
-    return;
-  }
-
   EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
   eglInitialize(display, NULL, NULL);
   if (display == EGL_NO_DISPLAY) {
diff --git a/src/starboard/build/platform_configuration.py b/src/starboard/build/platform_configuration.py
index 44f7155..ebb054e 100644
--- a/src/starboard/build/platform_configuration.py
+++ b/src/starboard/build/platform_configuration.py
@@ -22,6 +22,7 @@
 from starboard.build.application_configuration import ApplicationConfiguration
 from starboard.optional import get_optional_tests
 from starboard.sabi import sabi
+from starboard.tools import ccache
 from starboard.tools import environment
 from starboard.tools import paths
 from starboard.tools import platform
@@ -62,14 +63,23 @@
                asan_enabled_by_default=False,
                directory=None):
     self._platform_name = platform_name
+    self._asan_default = 1 if asan_enabled_by_default else 0
     if directory:
       self._directory = directory
     else:
       self._directory = os.path.realpath(os.path.dirname(__file__))
-    self._asan_default = 1 if asan_enabled_by_default else 0
     self._application_configuration = None
     self._application_configuration_search_path = [self._directory]
 
+    # Specifies the build accelerator to be used. Default is ccache.
+    build_accelerator = ccache.Ccache()
+    if build_accelerator.Use():
+      self.build_accelerator = build_accelerator.GetName()
+      logging.info('Using %sbuild accelerator.', self.build_accelerator)
+    else:
+      self.build_accelerator = ''
+      logging.info('Not using a build accelerator.')
+
   def GetBuildFormat(self):
     """Returns the desired build format."""
     return 'ninja'
diff --git a/src/starboard/build/toolchain/gcc_toolchain.gni b/src/starboard/build/toolchain/gcc_toolchain.gni
index ef3d025..6fab1ae 100644
--- a/src/starboard/build/toolchain/gcc_toolchain.gni
+++ b/src/starboard/build/toolchain/gcc_toolchain.gni
@@ -17,7 +17,6 @@
 # limitations under the License.
 
 import("//starboard/build/toolchain/clang.gni")
-import("//starboard/build/toolchain/goma.gni")
 
 # This template defines a toolchain for something that works like gcc
 # (including clang).
@@ -111,29 +110,8 @@
       forward_variables_from(invoker_toolchain_args, "*")
     }
 
-    # When the invoker has explicitly overridden use_goma or cc_wrapper in the
-    # toolchain args, use those values, otherwise default to the global one.
-    # This works because the only reasonable override that toolchains might
-    # supply for these values are to force-disable them.
-    if (defined(toolchain_args.use_goma)) {
-      toolchain_uses_goma = toolchain_args.use_goma
-    } else {
-      toolchain_uses_goma = use_goma
-    }
-
-    # When the invoker has explicitly overridden use_goma in the
-    # toolchain args, use those values, otherwise default to the global one.
-    # This works because the only reasonable override that toolchains might
-    # supply for these values are to force-disable them.
-    if (toolchain_uses_goma) {
-      goma_path = "$goma_dir/gomacc"
-      compiler_prefix = "${goma_path} "
-    } else {
-      compiler_prefix = ""
-    }
-
-    cc = compiler_prefix + invoker.cc
-    cxx = compiler_prefix + invoker.cxx
+    cc = invoker.cc
+    cxx = invoker.cxx
     ar = invoker.ar
     ld = invoker.ld
     if (!defined(asm)) {
diff --git a/src/starboard/build/toolchain/goma.gni b/src/starboard/build/toolchain/goma.gni
deleted file mode 100644
index 84914b2..0000000
--- a/src/starboard/build/toolchain/goma.gni
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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.
-
-# Modifications Copyright 2017 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Defines the configuration of Goma.
-
-# Allow ports to set an alternate default for use_goma
-import("//$starboard_path/configuration.gni")
-
-declare_args() {
-  if (!defined(use_goma)) {
-    # Set to true to enable distributed compilation using Goma. By default we
-    # use Goma for stub and linux.
-    use_goma = false
-  }
-
-  # Set the default value based on the platform.
-  if (host_os == "win" || host_os == "winrt_81" ||
-      host_os == "winrt_81_phone" || host_os == "winrt_10") {
-    # Absolute directory containing the gomacc.exe binary.
-    goma_dir = "C:\goma\goma-win64"
-  } else {
-    # Absolute directory containing the gomacc binary.
-    goma_dir = getenv("HOME") + "/goma"
-  }
-}
diff --git a/src/starboard/client_porting/eztime/test_constants.h b/src/starboard/client_porting/eztime/test_constants.h
index 92e0e59..0561ea1 100644
--- a/src/starboard/client_porting/eztime/test_constants.h
+++ b/src/starboard/client_porting/eztime/test_constants.h
@@ -48,16 +48,16 @@
 // agree.
 static const EzTimeT kTestTimeWindowsNegative = SB_INT64_C(-12212553600);
 
-// 1443473373 in POSIX time is
-// Monday, 9/28/2015 20:49:33 UTC
-// NOTE: Update this value once every 5 or so years.
-static const EzTimeT kTestTimeWritten = SB_INT64_C(1443473373);
+// 1600970155 in POSIX time is
+// Thursday, 9/24/2020 17:55:55 UTC
+// NOTE: Update this value once every 25 or so years.
+static const EzTimeT kTestTimeWritten = SB_INT64_C(1600970155);
 
-// 5 years after the time this test was written.
+// 25 years after the time this test was written.
 static const EzTimeT kTestTimePastWritten =
-    (kTestTimeWritten + (5 * kTestEzTimeTYear));
+    (kTestTimeWritten + (25 * kTestEzTimeTYear));
 
-// 1443473373 in POSIX time is
+// 4133980800 in POSIX time is
 // Saturday, 01 Jan 2101 00:00:00 UTC
 // NOTE: Update this value once every 100 or so years.
 static const EzTimeT kTestTimeNextCentury = SB_INT64_C(4133980800);
diff --git a/src/starboard/contrib/linux/x64wl/gyp_configuration.py b/src/starboard/contrib/linux/x64wl/gyp_configuration.py
index 3d6687a..64a2c4b 100644
--- a/src/starboard/contrib/linux/x64wl/gyp_configuration.py
+++ b/src/starboard/contrib/linux/x64wl/gyp_configuration.py
@@ -26,11 +26,13 @@
   """Starboard Linux X64 Wayland platform configuration."""
 
   def __init__(self,
-               platform_name='linux-x64wl',
+               platform='linux-x64wl',
                asan_enabled_by_default=False,
-               goma_supports_compiler=True):
-    super(LinuxX64WaylandConfiguration, self).__init__(
-        platform_name, asan_enabled_by_default, goma_supports_compiler)
+               sabi_json_path='starboard/sabi/default/sabi.json'):
+    # pylint: disable=useless-super-delegation
+    super(LinuxX64WaylandConfiguration, self).__init__(platform,
+                                                       asan_enabled_by_default,
+                                                       sabi_json_path)
 
   def GetTargetToolchain(self, **kwargs):
     return self.GetHostToolchain(**kwargs)
diff --git a/src/starboard/doc/evergreen/symbolizing_minidumps.md b/src/starboard/doc/evergreen/symbolizing_minidumps.md
new file mode 100644
index 0000000..4931300
--- /dev/null
+++ b/src/starboard/doc/evergreen/symbolizing_minidumps.md
@@ -0,0 +1,129 @@
+# How to Symbolize Dumps
+
+Evergreen will store the minidumps (`.dmp` files) from the 2 most recent
+crashes on the disk. They are stored under `kSbSystemPathCacheDirectory` in the
+subdirectory `crashpad_database/`. These files can be used along with
+Breakpad's tools to get a full stacktrace of the past crashes. This can help in
+debugging, as these minidumps have the information for the dynamic
+`libcobalt.so` module correctly mapped, which a out-of-the-box dumper could not
+manage.
+
+## Obtaining the Tools to Symbolize Minidumps
+
+Tools for symbolizing these dumps are available through
+[Breakpad](https://chromium.googlesource.com/breakpad/breakpad/). Breakpad is
+an open source crash reporting library that we use to obtain symbol files
+(`.sym`) from unstripped binaries, and to process the symbol files with the
+minidumps to produce human-readable stacktraces.
+
+
+### Building Breakpad
+
+[Breakpad](https://chromium.googlesource.com/breakpad/breakpad/) provides
+instructions for building these tools yourself. The
+[Getting Started with Breakpad](https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/getting_started_with_breakpad.md)
+guide is a good place to start if you want to go through the docs yourself, but
+below is a brief overview of how to get and build the tools.
+
+Download depot_tools:
+```
+$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
+$ export PATH=/path/to/depot_tools:$PATH
+```
+
+Get breakpad:
+```
+$ mkdir breakpad && cd breakpad
+$ fetch breakpad
+$ cd src
+```
+
+Build breakpad:
+```
+$ ./configure && make
+```
+
+This will build the processor (`src/processor/minidump_stackwalk`), and when
+building on Linux it will also build the `dump_syms` tool
+(`src/tools/linux/dump_syms/dump_syms`).
+
+**IMPORTANT:** Once you have fetched Breakpad, you should remove the path to
+depot_tools from your `$PATH` environment variable, as it can conflict with
+Cobalt's depot_tools.
+
+## Symbolizing Minidumps
+
+Now that you have all the tools you need, we can symbolize the dumps. To be
+able to symbolize Cobalt using Evergreen, you need to be get the unstripped
+`libcobalt.so` binary. These will be available as assets in GitHub releases
+[on Cobalt's public GitHub repo](https://github.com/youtube/cobalt/releases).
+
+libcobalt releases will be labeled by the Evergreen version, the architecture,
+the config, and the ELF build id, for example
+"libcobalt_1.0.10_unstripped_armeabi_softfp_qa_ac3132014007df0e.tgz". Here, we
+have:
+* Evergreen Version: 1.0.10
+* Architecture: armeabi_softfp
+* Config: qa
+* ELF Build Id: ac3132014007df0e
+
+Knowing the architecture and config you want, you'll just have to know which
+version of Evergreen you're on or obtain the build id of the library. If you
+need to obtain the ELF build id, you can do so easily by running
+`readelf -n /path/to/libcobalt.so` and look at the hash displayed after "Build
+ID:".
+
+Now you can get the debug symbols from the library using the tools we
+downloaded previously. Unpack libcobalt and dump its symbols into a file:
+
+```
+$ tar xzf /path/to/libcobalt.tgz
+$ /path/to/dump_syms /path/to/unzipped/libcobalt > libcobalt.so.sym
+$ head -n1 libcobalt.so.sym
+MODULE Linux x86_64 6462A5D44C0843D100000000000000000 libcobalt.so
+```
+
+We run `head` on the symbol file to get the debug identifier, the hash
+displayed above (in this case, it's `6462A5D44C0843D100000000000000000`). Now
+we can create the file structure that `minidump_stackwalker` expects and run
+the stackwalker against the minidump:
+
+```
+$ mkdir -p symbols/libcobalt.so/<debug identifier>/
+$ mv libcobalt.so.sym symbols/libcobalt.so/<debug identifier>/
+$ /path/to/minidump_stackwalk /path/to/your/minidump.dmp symbols/
+```
+
+`minidump_stackwalk` produces verbose output on stderr, and the stacktrace on
+stdout, so you may want to redirect stderr.
+
+### Addendum: Adding Other Symbols
+
+We can use the process above to add symbols for any library or executable you
+use, not just `libcobalt.so`. To do this, all you have to do is run the
+`dump_syms` tools on the binary you want symbolized and put that in the
+"symbols/" folder.
+
+```
+$ /path/to/dump_syms /path/to/<your-binary> > <your-binary>.sym
+$ head -n1 <your-binary.sym>
+MODULE Linux x86_64 <debug-identifier> <your-binary>
+$ mkdir -p symbols/<your-binary>/<debug-identifier>
+$ mv <your-binary>.sym symbols/<your-binary>/<debug-identifier>/
+```
+
+Now, `minidump_stackwalk` should symbolize sections within `<your-binary>`. For
+example, if you decided to symbolize the `loader_app`, it would transform the
+stacktrace output from `minidump_stackwalk` from:
+
+```
+9  loader_app + 0x3a31130
+```
+
+to:
+
+```
+9  loader_app!SbEventHandle [sandbox.cc : 44 + 0x8]
+```
+
+Note that the addresses will vary.
diff --git a/src/starboard/elf_loader/evergreen_info.h b/src/starboard/elf_loader/evergreen_info.h
index fecd079..2adfe29 100644
--- a/src/starboard/elf_loader/evergreen_info.h
+++ b/src/starboard/elf_loader/evergreen_info.h
@@ -26,6 +26,7 @@
 // the starboard implementation.
 #define EVERGREEN_FILE_PATH_MAX_SIZE 4096
 #define EVERGREEN_BUILD_ID_MAX_SIZE 128
+#define EVERGREEN_USER_AGENT_MAX_SIZE 2048
 
 #define IS_EVERGREEN_ADDRESS(address, evergreen_info)                    \
   (evergreen_info.base_address != 0 &&                                   \
@@ -62,6 +63,12 @@
   size_t build_id_length;
 } EvergreenInfo;
 
+// Annotations that Evergreen will add to Crashpad for more detailed crash
+// reports.
+typedef struct EvergreenAnnotations {
+  char user_agent_string[EVERGREEN_USER_AGENT_MAX_SIZE];
+} EvergreenAnnotations;
+
 // Set the Evergreen information. Should be called only from the
 // elf_loader module. Passing NULL clears the currently stored
 // information.
diff --git a/src/starboard/elf_loader/sandbox.cc b/src/starboard/elf_loader/sandbox.cc
index 857ab89..9dd2f73 100644
--- a/src/starboard/elf_loader/sandbox.cc
+++ b/src/starboard/elf_loader/sandbox.cc
@@ -21,6 +21,7 @@
 #include "starboard/event.h"
 #include "starboard/mutex.h"
 #include "starboard/shared/starboard/command_line.h"
+#include "starboard/string.h"
 #include "starboard/thread_types.h"
 #include "third_party/crashpad/wrapper/wrapper.h"
 
@@ -63,6 +64,20 @@
   g_sb_event_func = reinterpret_cast<void (*)(const SbEvent*)>(
       g_elf_loader.LookupSymbol("SbEventHandle"));
 
+  auto get_user_agent_func = reinterpret_cast<const char* (*)()>(
+      g_elf_loader.LookupSymbol("GetCobaltUserAgentString"));
+  if (!get_user_agent_func) {
+    SB_LOG(ERROR) << "Failed to get user agent string";
+  } else {
+    EvergreenAnnotations cobalt_version_info;
+    SbMemorySet(&cobalt_version_info, sizeof(EvergreenAnnotations), 0);
+    SbStringCopy(cobalt_version_info.user_agent_string, get_user_agent_func(),
+                 EVERGREEN_USER_AGENT_MAX_SIZE);
+    third_party::crashpad::wrapper::AddAnnotationsToCrashpad(
+        cobalt_version_info);
+    SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+  }
+
   if (!g_sb_event_func) {
     SB_LOG(ERROR) << "Failed to find SbEventHandle.";
     return;
diff --git a/src/starboard/evergreen/arm/shared/gyp_configuration.gypi b/src/starboard/evergreen/arm/shared/gyp_configuration.gypi
index 404d3a3..4192b6c 100644
--- a/src/starboard/evergreen/arm/shared/gyp_configuration.gypi
+++ b/src/starboard/evergreen/arm/shared/gyp_configuration.gypi
@@ -24,6 +24,8 @@
 
       # Force char to be signed.
       '-fsigned-char',
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
 
     'linker_flags': [
diff --git a/src/starboard/evergreen/arm/shared/gyp_configuration.py b/src/starboard/evergreen/arm/shared/gyp_configuration.py
index c6c67ed..33a2284 100644
--- a/src/starboard/evergreen/arm/shared/gyp_configuration.py
+++ b/src/starboard/evergreen/arm/shared/gyp_configuration.py
@@ -17,7 +17,6 @@
 
 from starboard.build import clang as clang_build
 from starboard.evergreen.shared import gyp_configuration as shared_configuration
-from starboard.tools import build
 from starboard.tools.testing import test_filter
 from starboard.tools.toolchain import ar
 from starboard.tools.toolchain import bash
@@ -32,34 +31,29 @@
   """Starboard Evergreen ARM platform configuration."""
 
   def __init__(self,
-               platform_name='evergreen-arm',
+               platform='evergreen-arm',
                asan_enabled_by_default=False,
-               goma_supports_compiler=True,
                sabi_json_path='starboard/sabi/default/sabi.json'):
-    # pylint: disable=useless-super-delegation
-    super(EvergreenArmConfiguration,
-          self).__init__(platform_name, asan_enabled_by_default,
-                         goma_supports_compiler, sabi_json_path)
+    super(EvergreenArmConfiguration, self).__init__(platform,
+                                                    asan_enabled_by_default,
+                                                    sabi_json_path)
+
     self.AppendApplicationConfigurationPath(os.path.dirname(__file__))
-    self._host_toolchain = None
 
   def GetTargetToolchain(self, **kwargs):
     return self.GetHostToolchain(**kwargs)
 
   def GetHostToolchain(self, **kwargs):
-    if not self._host_toolchain:
-      if not hasattr(self, 'host_compiler_environment'):
-        self.host_compiler_environment = build.GetHostCompilerEnvironment(
-            clang_build.GetClangSpecification(), False)
-      cc_path = self.host_compiler_environment['CC_host']
-      cxx_path = self.host_compiler_environment['CXX_host']
+    if not hasattr(self, '_host_toolchain'):
+      env_variables = self.GetEnvironmentVariables()
+      cc_path = env_variables['CC_host']
+      cxx_path = env_variables['CXX_host']
 
-      # Takes the provided value of CXX_HOST with a prepended 'gomacc' and an
+      # Takes the provided value of CXX_HOST with a prepended 'ccache' and an
       # appended 'bin/clang++' and strips them off, leaving us with an absolute
       # path to the root directory of our toolchain.
       begin_path_index = cxx_path.find('/')
       end_path_index = cxx_path.rfind('/', 0, cxx_path.rfind('/')) + 1
-
       cxx_path_root = cxx_path[begin_path_index:end_path_index]
 
       self._host_toolchain = [
@@ -93,7 +87,8 @@
     return variables
 
   __FILTERED_TESTS = {  # pylint: disable=invalid-name
-      'nplb': ['SbSystemGetStackTest.SunnyDayStackDirection',
+      'nplb': ['SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/4',
+               'SbSystemGetStackTest.SunnyDayStackDirection',
                'SbSystemGetStackTest.SunnyDay',
                'SbSystemGetStackTest.SunnyDayShortStack',
                'SbSystemSymbolizeTest.SunnyDay'],
diff --git a/src/starboard/evergreen/arm64/gyp_configuration.gypi b/src/starboard/evergreen/arm64/gyp_configuration.gypi
index b2a0525..f8843b2 100644
--- a/src/starboard/evergreen/arm64/gyp_configuration.gypi
+++ b/src/starboard/evergreen/arm64/gyp_configuration.gypi
@@ -22,6 +22,8 @@
 
     'compiler_flags': [
       '-isystem<(cobalt_repo_root)/third_party/musl/arch/aarch64',
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
   },
 
diff --git a/src/starboard/evergreen/arm64/gyp_configuration.py b/src/starboard/evergreen/arm64/gyp_configuration.py
index fc09a0d..e8b282a 100644
--- a/src/starboard/evergreen/arm64/gyp_configuration.py
+++ b/src/starboard/evergreen/arm64/gyp_configuration.py
@@ -15,7 +15,6 @@
 
 from starboard.build import clang as clang_build
 from starboard.evergreen.shared import gyp_configuration as shared_configuration
-from starboard.tools import build
 from starboard.tools.toolchain import ar
 from starboard.tools.toolchain import bash
 from starboard.tools.toolchain import clang
@@ -29,33 +28,28 @@
   """Starboard Evergreen 64-bit ARM platform configuration."""
 
   def __init__(self,
-               platform_name='evergreen-arm64',
+               platform='evergreen-arm64',
                asan_enabled_by_default=False,
-               goma_supports_compiler=True,
                sabi_json_path='starboard/sabi/default/sabi.json'):
     # pylint: disable=useless-super-delegation
-    super(EvergreenArm64Configuration,
-          self).__init__(platform_name, asan_enabled_by_default,
-                         goma_supports_compiler, sabi_json_path)
-    self._host_toolchain = None
+    super(EvergreenArm64Configuration, self).__init__(platform,
+                                                      asan_enabled_by_default,
+                                                      sabi_json_path)
 
   def GetTargetToolchain(self, **kwargs):
     return self.GetHostToolchain(**kwargs)
 
   def GetHostToolchain(self, **kwargs):
-    if not self._host_toolchain:
-      if not hasattr(self, 'host_compiler_environment'):
-        self.host_compiler_environment = build.GetHostCompilerEnvironment(
-            clang_build.GetClangSpecification(), False)
-      cc_path = self.host_compiler_environment['CC_host']
-      cxx_path = self.host_compiler_environment['CXX_host']
+    if not hasattr(self, '_host_toolchain'):
+      env_variables = self.GetEnvironmentVariables()
+      cc_path = env_variables['CC_host']
+      cxx_path = env_variables['CXX_host']
 
-      # Takes the provided value of CXX_HOST with a prepended 'gomacc' and an
+      # Takes the provided value of CXX_HOST with a prepended 'ccache' and an
       # appended 'bin/clang++' and strips them off, leaving us with an absolute
       # path to the root directory of our toolchain.
       begin_path_index = cxx_path.find('/')
       end_path_index = cxx_path.rfind('/', 0, cxx_path.rfind('/')) + 1
-
       cxx_path_root = cxx_path[begin_path_index:end_path_index]
 
       self._host_toolchain = [
diff --git a/src/starboard/evergreen/arm64/sbversion/12/gyp_configuration.py b/src/starboard/evergreen/arm64/sbversion/12/gyp_configuration.py
index f0b21ba..d2a4708 100644
--- a/src/starboard/evergreen/arm64/sbversion/12/gyp_configuration.py
+++ b/src/starboard/evergreen/arm64/sbversion/12/gyp_configuration.py
@@ -18,5 +18,5 @@
 
 def CreatePlatformConfig():
   return parent_configuration.EvergreenArm64Configuration(
-      platform_name='evergreen-arm64-sbversion-12',
+      'evergreen-arm64-sbversion-12',
       sabi_json_path='starboard/sabi/arm64/sabi-v12.json')
diff --git a/src/starboard/evergreen/shared/gyp_configuration.py b/src/starboard/evergreen/shared/gyp_configuration.py
index 71c24c4..526b209 100644
--- a/src/starboard/evergreen/shared/gyp_configuration.py
+++ b/src/starboard/evergreen/shared/gyp_configuration.py
@@ -28,13 +28,12 @@
   def __init__(self,
                platform,
                asan_enabled_by_default=True,
-               goma_supports_compiler=True,
                sabi_json_path='starboard/sabi/default/sabi.json'):
-    self.goma_supports_compiler = goma_supports_compiler
-    self.sabi_json_path = sabi_json_path
     super(EvergreenConfiguration, self).__init__(platform,
                                                  asan_enabled_by_default)
 
+    self.sabi_json_path = sabi_json_path
+
   def GetBuildFormat(self):
     """Returns the desired build format."""
     # The comma means that ninja and qtcreator_ninja will be chained and use the
@@ -67,14 +66,14 @@
     return generator_variables
 
   def GetEnvironmentVariables(self):
-    if not hasattr(self, 'host_compiler_environment'):
-      self.host_compiler_environment = build.GetHostCompilerEnvironment(
-          clang.GetClangSpecification(), self.goma_supports_compiler)
+    if not hasattr(self, '_host_compiler_environment'):
+      self._host_compiler_environment = build.GetHostCompilerEnvironment(
+          clang.GetClangSpecification(), self.build_accelerator)
 
-    env_variables = self.host_compiler_environment
+    env_variables = self._host_compiler_environment
     env_variables.update({
-        'CC': self.host_compiler_environment['CC_host'],
-        'CXX': self.host_compiler_environment['CXX_host'],
+        'CC': self._host_compiler_environment['CC_host'],
+        'CXX': self._host_compiler_environment['CXX_host'],
     })
     return env_variables
 
diff --git a/src/starboard/evergreen/x64/gyp_configuration.gypi b/src/starboard/evergreen/x64/gyp_configuration.gypi
index dcb689e..a9f3eaa 100644
--- a/src/starboard/evergreen/x64/gyp_configuration.gypi
+++ b/src/starboard/evergreen/x64/gyp_configuration.gypi
@@ -22,6 +22,8 @@
 
     'compiler_flags': [
       '-isystem<(cobalt_repo_root)/third_party/musl/arch/x86_64',
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
   },
 
diff --git a/src/starboard/evergreen/x64/gyp_configuration.py b/src/starboard/evergreen/x64/gyp_configuration.py
index 86174cd..dbb32d6 100644
--- a/src/starboard/evergreen/x64/gyp_configuration.py
+++ b/src/starboard/evergreen/x64/gyp_configuration.py
@@ -17,7 +17,6 @@
 
 from starboard.build import clang as clang_build
 from starboard.evergreen.shared import gyp_configuration as shared_configuration
-from starboard.tools import build
 from starboard.tools import paths
 from starboard.tools.toolchain import ar
 from starboard.tools.toolchain import bash
@@ -32,34 +31,29 @@
   """Starboard Evergreen x64 platform configuration."""
 
   def __init__(self,
-               platform_name='evergreen-x64',
+               platform='evergreen-x64',
                asan_enabled_by_default=False,
-               goma_supports_compiler=True,
                sabi_json_path='starboard/sabi/default/sabi.json'):
-    # pylint: disable=useless-super-delegation
-    super(EvergreenX64Configuration,
-          self).__init__(platform_name, asan_enabled_by_default,
-                         goma_supports_compiler, sabi_json_path)
+    super(EvergreenX64Configuration, self).__init__(platform,
+                                                    asan_enabled_by_default,
+                                                    sabi_json_path)
+
     self.AppendApplicationConfigurationPath(os.path.dirname(__file__))
-    self._host_toolchain = None
 
   def GetTargetToolchain(self, **kwargs):
     return self.GetHostToolchain(**kwargs)
 
   def GetHostToolchain(self, **kwargs):
-    if not self._host_toolchain:
-      if not hasattr(self, 'host_compiler_environment'):
-        self.host_compiler_environment = build.GetHostCompilerEnvironment(
-            clang_build.GetClangSpecification(), False)
-      cc_path = self.host_compiler_environment['CC_host']
-      cxx_path = self.host_compiler_environment['CXX_host']
+    if not hasattr(self, '_host_toolchain'):
+      env_variables = self.GetEnvironmentVariables()
+      cc_path = env_variables['CC_host']
+      cxx_path = env_variables['CXX_host']
 
-      # Takes the provided value of CXX_HOST with a prepended 'gomacc' and an
+      # Takes the provided value of CXX_HOST with a prepended 'ccache' and an
       # appended 'bin/clang++' and strips them off, leaving us with an absolute
       # path to the root directory of our toolchain.
       begin_path_index = cxx_path.find('/')
       end_path_index = cxx_path.rfind('/', 0, cxx_path.rfind('/')) + 1
-
       cxx_path_root = cxx_path[begin_path_index:end_path_index]
 
       self._host_toolchain = [
diff --git a/src/starboard/evergreen/x64/sbversion/12/gyp_configuration.py b/src/starboard/evergreen/x64/sbversion/12/gyp_configuration.py
index a5ce04a..f03e574 100644
--- a/src/starboard/evergreen/x64/sbversion/12/gyp_configuration.py
+++ b/src/starboard/evergreen/x64/sbversion/12/gyp_configuration.py
@@ -18,5 +18,5 @@
 
 def CreatePlatformConfig():
   return parent_configuration.EvergreenX64Configuration(
-      platform_name='evergreen-x64-sbversion-12',
+      'evergreen-x64-sbversion-12',
       sabi_json_path='starboard/sabi/x64/sysv/sabi-v12.json')
diff --git a/src/starboard/evergreen/x86/gyp_configuration.gypi b/src/starboard/evergreen/x86/gyp_configuration.gypi
index f8a868f..58ebaca 100644
--- a/src/starboard/evergreen/x86/gyp_configuration.gypi
+++ b/src/starboard/evergreen/x86/gyp_configuration.gypi
@@ -22,6 +22,8 @@
 
     'compiler_flags': [
       '-isystem<(cobalt_repo_root)/third_party/musl/arch/i386',
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
   },
 
diff --git a/src/starboard/evergreen/x86/gyp_configuration.py b/src/starboard/evergreen/x86/gyp_configuration.py
index 5ac4c8e..441c4e3 100644
--- a/src/starboard/evergreen/x86/gyp_configuration.py
+++ b/src/starboard/evergreen/x86/gyp_configuration.py
@@ -17,7 +17,6 @@
 
 from starboard.build import clang as clang_build
 from starboard.evergreen.shared import gyp_configuration as shared_configuration
-from starboard.tools import build
 from starboard.tools import paths
 from starboard.tools.toolchain import ar
 from starboard.tools.toolchain import bash
@@ -32,33 +31,28 @@
   """Starboard Evergreen x86 platform configuration."""
 
   def __init__(self,
-               platform_name='evergreen-x86',
+               platform='evergreen-x86',
                asan_enabled_by_default=False,
-               goma_supports_compiler=True,
                sabi_json_path='starboard/sabi/default/sabi.json'):
     # pylint: disable=useless-super-delegation
-    super(EvergreenX86Configuration,
-          self).__init__(platform_name, asan_enabled_by_default,
-                         goma_supports_compiler, sabi_json_path)
-    self._host_toolchain = None
+    super(EvergreenX86Configuration, self).__init__(platform,
+                                                    asan_enabled_by_default,
+                                                    sabi_json_path)
 
   def GetTargetToolchain(self, **kwargs):
     return self.GetHostToolchain(**kwargs)
 
   def GetHostToolchain(self, **kwargs):
-    if not self._host_toolchain:
-      if not hasattr(self, 'host_compiler_environment'):
-        self.host_compiler_environment = build.GetHostCompilerEnvironment(
-            clang_build.GetClangSpecification(), False)
-      cc_path = self.host_compiler_environment['CC_host']
-      cxx_path = self.host_compiler_environment['CXX_host']
+    if not hasattr(self, '_host_toolchain'):
+      env_variables = self.GetEnvironmentVariables()
+      cc_path = env_variables['CC_host']
+      cxx_path = env_variables['CXX_host']
 
-      # Takes the provided value of CXX_HOST with a prepended 'gomacc' and an
+      # Takes the provided value of CXX_HOST with a prepended 'ccache' and an
       # appended 'bin/clang++' and strips them off, leaving us with an absolute
       # path to the root directory of our toolchain.
       begin_path_index = cxx_path.find('/')
       end_path_index = cxx_path.rfind('/', 0, cxx_path.rfind('/')) + 1
-
       cxx_path_root = cxx_path[begin_path_index:end_path_index]
 
       self._host_toolchain = [
diff --git a/src/starboard/evergreen/x86/sbversion/12/gyp_configuration.py b/src/starboard/evergreen/x86/sbversion/12/gyp_configuration.py
index 0b040a4..4de44be 100644
--- a/src/starboard/evergreen/x86/sbversion/12/gyp_configuration.py
+++ b/src/starboard/evergreen/x86/sbversion/12/gyp_configuration.py
@@ -18,5 +18,5 @@
 
 def CreatePlatformConfig():
   return parent_configuration.EvergreenX86Configuration(
-      platform_name='evergreen-x86-sbversion-12',
+      'evergreen-x86-sbversion-12',
       sabi_json_path='starboard/sabi/x86/sabi-v12.json')
diff --git a/src/starboard/linux/shared/compiler_flags.gypi b/src/starboard/linux/shared/compiler_flags.gypi
index 53b3229..af40c10 100644
--- a/src/starboard/linux/shared/compiler_flags.gypi
+++ b/src/starboard/linux/shared/compiler_flags.gypi
@@ -36,9 +36,15 @@
     ],
     'compiler_flags_qa_size': [
       '-Os',
+      # Compile symbols in separate sections
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
     'compiler_flags_qa_speed': [
       '-O2',
+      # Compile symbols in separate sections
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
     'compiler_flags_gold': [
       '-fno-rtti',
@@ -46,9 +52,15 @@
     ],
     'compiler_flags_gold_size': [
       '-Os',
+      # Compile symbols in separate sections
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
     'compiler_flags_gold_speed': [
       '-O2',
+      # Compile symbols in separate sections
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
     'conditions': [
       ['clang==1', {
@@ -135,6 +147,8 @@
     ],
     'ldflags': [
       '-Wl,-rpath=$ORIGIN/lib',
+      # Cleanup unused sections
+      '-Wl,-gc-sections',
     ],
     'target_conditions': [
       ['sb_pedantic_warnings==1', {
diff --git a/src/starboard/linux/shared/configuration.gni b/src/starboard/linux/shared/configuration.gni
index c4562c3..49d1b21 100644
--- a/src/starboard/linux/shared/configuration.gni
+++ b/src/starboard/linux/shared/configuration.gni
@@ -39,9 +39,3 @@
 
 # Use ASAN by default when building with Clang.
 use_asan_by_default = true
-
-declare_args() {
-  # Set to true to enable distributed compilation using Goma. By default we
-  # use Goma for stub and linux.
-  use_goma = true
-}
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index a635fda..0cb799e 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -18,6 +18,7 @@
 from starboard.build import clang
 from starboard.build import platform_configuration
 from starboard.tools import build
+from starboard.tools import paths
 from starboard.tools.testing import test_filter
 
 
@@ -27,11 +28,10 @@
   def __init__(self,
                platform,
                asan_enabled_by_default=True,
-               goma_supports_compiler=True,
                sabi_json_path='starboard/sabi/default/sabi.json'):
-    self.goma_supports_compiler = goma_supports_compiler
-    self.sabi_json_path = sabi_json_path
     super(LinuxConfiguration, self).__init__(platform, asan_enabled_by_default)
+
+    self.sabi_json_path = sabi_json_path
     self.AppendApplicationConfigurationPath(os.path.dirname(__file__))
 
   def GetBuildFormat(self):
@@ -64,14 +64,14 @@
     return generator_variables
 
   def GetEnvironmentVariables(self):
-    if not hasattr(self, 'host_compiler_environment'):
-      self.host_compiler_environment = build.GetHostCompilerEnvironment(
-          clang.GetClangSpecification(), self.goma_supports_compiler)
+    if not hasattr(self, '_host_compiler_environment'):
+      self._host_compiler_environment = build.GetHostCompilerEnvironment(
+          clang.GetClangSpecification(), self.build_accelerator)
 
-    env_variables = self.host_compiler_environment
+    env_variables = self._host_compiler_environment
     env_variables.update({
-        'CC': self.host_compiler_environment['CC_host'],
-        'CXX': self.host_compiler_environment['CXX_host'],
+        'CC': self._host_compiler_environment['CC_host'],
+        'CXX': self._host_compiler_environment['CXX_host'],
     })
     return env_variables
 
@@ -89,13 +89,25 @@
 
   def GetTestFilters(self):
     filters = super(LinuxConfiguration, self).GetTestFilters()
-    for target, tests in self.__FILTERED_TESTS.iteritems():
+
+    has_cdm = os.path.isfile(
+        os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'ce_cdm', 'cdm',
+                     'include', 'cdm.h'))
+
+    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
 
   def GetPathToSabiJsonFile(self):
     return self.sabi_json_path
 
-  __FILTERED_TESTS = {  # pylint: disable=invalid-name
-      'nplb': ['SbDrmTest.AnySupportedKeySystems',],
+  __DRM_RELATED_TESTS = {  # pylint: disable=invalid-name
+      'nplb': [
+          'SbDrmTest.AnySupportedKeySystems',
+          'SbMediaCanPlayMimeAndKeySystem.AnySupportedKeySystems',
+      ],
   }
diff --git a/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi b/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi
index b5368e1..7ff50f5 100644
--- a/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi
+++ b/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi
@@ -50,8 +50,6 @@
         'common_clang_flags': [
           '-Werror',
           '-fcolor-diagnostics',
-          # Point to a gcc toolchain that works with this compiler.
-          '--gcc-toolchain=<(GCC_TOOLCHAIN_FOLDER)',
           # Default visibility to hidden, to enable dead stripping.
           '-fvisibility=hidden',
           # Warn for implicit type conversions that may change a value.
@@ -107,6 +105,16 @@
           '-Wno-unused-parameter',
           # Suppress warnings about unknown pragmas.
           '-Wno-unknown-pragmas',
+          # Suppress warnings about equality checks within double parentheses.
+          '-Wno-parentheses-equality',
+          # Suppress warnings about unreachable code due to constexpr conditions
+          '-Wno-unreachable-code',
+          # Suppress warnings about values being written but not read before the next write.
+          '-Wno-unused-value',
+          # Suppress warnings related to unused compilation flags in clang.
+          '-Wno-unused-command-line-argument',
+          # Suppress warnings related to tautological comparisons.
+          '-Wno-tautological-compare',
         ],
       }],
       ['cobalt_fastbuild==0', {
diff --git a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
index 0266fbc..8bb0020 100644
--- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
@@ -18,7 +18,6 @@
 import subprocess
 
 from starboard.linux.shared import gyp_configuration as shared_configuration
-from starboard.tools import build
 from starboard.tools.toolchain import ar
 from starboard.tools.toolchain import bash
 from starboard.tools.toolchain import clang
@@ -31,45 +30,37 @@
   """Starboard Linux X64 X11 Clang 3.6 platform configuration."""
 
   def __init__(self,
-               platform,
+               platform='linux-x64x11-clang-3-6',
                asan_enabled_by_default=False,
                sabi_json_path='starboard/sabi/default/sabi.json'):
-    super(LinuxX64X11Clang36Configuration, self).__init__(
-        platform,
-        asan_enabled_by_default,
-        goma_supports_compiler=False,
-        sabi_json_path=sabi_json_path)
+    super(LinuxX64X11Clang36Configuration,
+          self).__init__(platform, asan_enabled_by_default, sabi_json_path)
 
-    self.toolchain_top_dir = os.path.join(build.GetToolchainsDir(),
-                                          'x86_64-linux-gnu-clang-3.6')
-    self.toolchain_dir = os.path.join(self.toolchain_top_dir, 'llvm',
-                                      'Release+Asserts')
+    self.toolchain_dir = '/usr/lib/llvm-3.6'
 
   def SetupPlatformTools(self, build_number):
-    script_path = os.path.dirname(os.path.realpath(__file__))
-    # Run the script that ensures clang 3.6 is installed.
-    subprocess.call(
-        os.path.join(script_path, 'download_clang.sh'), cwd=script_path)
+    ret = subprocess.call('/usr/bin/clang-3.6 --version', shell=True)
+    if ret != 0:
+      raise Exception('clang-3.6 is not installed.')
 
   def GetEnvironmentVariables(self):
+    toolchain_bin_dir = os.path.join(self.toolchain_dir, 'bin')
+
     env_variables = super(LinuxX64X11Clang36Configuration,
                           self).GetEnvironmentVariables()
-    toolchain_bin_dir = os.path.join(self.toolchain_dir, 'bin')
     env_variables.update({
-        'CC': os.path.join(toolchain_bin_dir, 'clang'),
-        'CXX': os.path.join(toolchain_bin_dir, 'clang++'),
+        'CC':
+            self.build_accelerator + ' ' +
+            os.path.join(toolchain_bin_dir, 'clang'),
+        'CXX':
+            self.build_accelerator + ' ' +
+            os.path.join(toolchain_bin_dir, 'clang++'),
     })
     return env_variables
 
   def GetVariables(self, config_name):
-    # A significant amount of code in V8 fails to compile on clang 3.6 using
-    # the debug config, due to an internal error in clang.
     variables = super(LinuxX64X11Clang36Configuration,
                       self).GetVariables(config_name)
-    variables.update({
-        'GCC_TOOLCHAIN_FOLDER':
-            '\"%s\"' % os.path.join(self.toolchain_top_dir, 'libstdc++-7'),
-    })
     return variables
 
   def GetTargetToolchain(self, **kwargs):
@@ -112,7 +103,6 @@
 def CreatePlatformConfig():
   try:
     return LinuxX64X11Clang36Configuration(
-        'linux-x64x11-clang-3-6',
         sabi_json_path='starboard/sabi/x64/sysv/sabi-v{sb_api_version}.json')
   except RuntimeError as e:
     logging.critical(e)
diff --git a/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py b/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
index 2fbd354..df1a163 100644
--- a/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/gcc/6.3/gyp_configuration.py
@@ -30,14 +30,13 @@
   """Starboard Linux platform configuration."""
 
   def __init__(self,
-               platform,
+               platform='linux-x64x11-gcc-6-3',
                asan_enabled_by_default=False,
                sabi_json_path='starboard/sabi/default/sabi.json'):
     super(LinuxX64X11Gcc63Configuration, self).__init__(
         platform,
         asan_enabled_by_default,
-        goma_supports_compiler=False,
-        sabi_json_path=sabi_json_path)
+        sabi_json_path)
 
     self.toolchain_dir = os.path.join(build.GetToolchainsDir(),
                                       'x86_64-linux-gnu-gcc-6.3.0', 'gcc')
@@ -62,11 +61,16 @@
 
   def GetEnvironmentVariables(self):
     toolchain_bin_dir = os.path.join(self.toolchain_dir, 'bin')
+
     env_variables = {
-        'CC': os.path.join(toolchain_bin_dir, 'gcc'),
-        'CXX': os.path.join(toolchain_bin_dir, 'g++'),
-        'CC_HOST': os.path.join(toolchain_bin_dir, 'gcc'),
-        'CXX_HOST': os.path.join(toolchain_bin_dir, 'g++'),
+        'CC': self.build_accelerator + ' ' + os.path.join(toolchain_bin_dir,
+                                                          'gcc'),
+        'CXX': self.build_accelerator + ' ' + os.path.join(toolchain_bin_dir,
+                                                           'g++'),
+        'CC_HOST': self.build_accelerator + ' ' + os.path.join(
+            toolchain_bin_dir, 'gcc'),
+        'CXX_HOST': self.build_accelerator + ' ' + os.path.join(
+            toolchain_bin_dir, 'g++'),
     }
     return env_variables
 
@@ -109,5 +113,4 @@
 
 def CreatePlatformConfig():
   return LinuxX64X11Gcc63Configuration(
-      'linux-x64x11-gcc-6-3',
       sabi_json_path='starboard/sabi/x64/sysv/sabi-v{sb_api_version}.json')
diff --git a/src/starboard/linux/x64x11/gyp_configuration.py b/src/starboard/linux/x64x11/gyp_configuration.py
index f62c9ea..5f8069a 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/gyp_configuration.py
@@ -13,8 +13,6 @@
 # limitations under the License.
 """Starboard Linux X64 X11 platform configuration."""
 
-import os.path
-
 from starboard.linux.shared import gyp_configuration as shared_configuration
 from starboard.tools.toolchain import ar
 from starboard.tools.toolchain import bash
@@ -22,22 +20,17 @@
 from starboard.tools.toolchain import clangxx
 from starboard.tools.toolchain import cp
 from starboard.tools.toolchain import touch
-from starboard.tools import paths
 
 
 class LinuxX64X11Configuration(shared_configuration.LinuxConfiguration):
   """Starboard Linux X64 X11 platform configuration."""
 
   def __init__(self,
-               platform_name='linux-x64x11',
+               platform='linux-x64x11',
                asan_enabled_by_default=True,
-               goma_supports_compiler=True,
                sabi_json_path='starboard/sabi/default/sabi.json'):
-    super(LinuxX64X11Configuration, self).__init__(
-        platform_name,
-        asan_enabled_by_default,
-        goma_supports_compiler,
-        sabi_json_path=sabi_json_path)
+    super(LinuxX64X11Configuration,
+          self).__init__(platform, asan_enabled_by_default, sabi_json_path)
 
   def GetTargetToolchain(self, **kwargs):
     return self.GetHostToolchain(**kwargs)
@@ -60,25 +53,6 @@
         bash.Shell(),
     ]
 
-  def GetTestFilters(self):
-    filters = super(LinuxX64X11Configuration, 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
-
 
 def CreatePlatformConfig():
   return LinuxX64X11Configuration(
diff --git a/src/starboard/linux/x64x11/sbversion/12/gyp_configuration.py b/src/starboard/linux/x64x11/sbversion/12/gyp_configuration.py
index 6dd53b0..a85e677 100644
--- a/src/starboard/linux/x64x11/sbversion/12/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/sbversion/12/gyp_configuration.py
@@ -18,5 +18,5 @@
 
 def CreatePlatformConfig():
   return parent_configuration.LinuxX64X11Configuration(
-      platform_name='linux-x64x11-sbversion-12',
+      'linux-x64x11-sbversion-12',
       sabi_json_path='starboard/sabi/x64/sysv/sabi-v12.json')
diff --git a/src/starboard/loader_app/loader_app.cc b/src/starboard/loader_app/loader_app.cc
index c74437f..1c661af 100644
--- a/src/starboard/loader_app/loader_app.cc
+++ b/src/starboard/loader_app/loader_app.cc
@@ -25,6 +25,7 @@
 #include "starboard/loader_app/loader_app_switches.h"
 #include "starboard/loader_app/slot_management.h"
 #include "starboard/loader_app/system_get_extension_shim.h"
+#include "starboard/memory.h"
 #include "starboard/mutex.h"
 #include "starboard/shared/starboard/command_line.h"
 #include "starboard/string.h"
@@ -109,6 +110,20 @@
     SB_LOG(INFO) << "Loaded Cobalt library information into Crashpad.";
   }
 
+  auto get_user_agent_func = reinterpret_cast<const char* (*)()>(
+      g_elf_loader.LookupSymbol("GetCobaltUserAgentString"));
+  if (!get_user_agent_func) {
+    SB_LOG(ERROR) << "Failed to get user agent string";
+  } else {
+    EvergreenAnnotations cobalt_version_info;
+    SbMemorySet(&cobalt_version_info, sizeof(EvergreenAnnotations), 0);
+    SbStringCopy(cobalt_version_info.user_agent_string, get_user_agent_func(),
+                 EVERGREEN_USER_AGENT_MAX_SIZE);
+    third_party::crashpad::wrapper::AddAnnotationsToCrashpad(
+        cobalt_version_info);
+    SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+  }
+
   g_sb_event_func = reinterpret_cast<void (*)(const SbEvent*)>(
       g_elf_loader.LookupSymbol("SbEventHandle"));
 
diff --git a/src/starboard/loader_app/slot_management.cc b/src/starboard/loader_app/slot_management.cc
index 9e1eadd..5dc1212 100644
--- a/src/starboard/loader_app/slot_management.cc
+++ b/src/starboard/loader_app/slot_management.cc
@@ -23,6 +23,7 @@
 #include "starboard/loader_app/app_key_files.h"
 #include "starboard/loader_app/drain_file.h"
 #include "starboard/loader_app/installation_manager.h"
+#include "starboard/memory.h"
 #include "starboard/string.h"
 #include "third_party/crashpad/wrapper/wrapper.h"
 
@@ -243,6 +244,20 @@
       SB_LOG(INFO) << "Loaded Cobalt library information into Crashpad.";
     }
 
+    auto get_user_agent_func = reinterpret_cast<const char* (*)()>(
+        library_loader->Resolve("GetCobaltUserAgentString"));
+    if (!get_user_agent_func) {
+      SB_LOG(ERROR) << "Failed to get user agent string";
+    } else {
+      EvergreenAnnotations cobalt_version_info;
+      SbMemorySet(&cobalt_version_info, sizeof(EvergreenAnnotations), 0);
+      SbStringCopy(cobalt_version_info.user_agent_string, get_user_agent_func(),
+                   EVERGREEN_USER_AGENT_MAX_SIZE);
+      third_party::crashpad::wrapper::AddAnnotationsToCrashpad(
+          cobalt_version_info);
+      SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+    }
+
     SB_DLOG(INFO) << "Successfully loaded Cobalt!\n";
     void* p = library_loader->Resolve("SbEventHandle");
     if (p != NULL) {
diff --git a/src/starboard/loader_app/slot_management_test.cc b/src/starboard/loader_app/slot_management_test.cc
index 0eeb83c..5ff9e20 100644
--- a/src/starboard/loader_app/slot_management_test.cc
+++ b/src/starboard/loader_app/slot_management_test.cc
@@ -37,6 +37,10 @@
 
 void SbEventFake(const SbEvent*) {}
 
+const char* GetCobaltUserAgentStringFake() {
+  return "";
+}
+
 class MockLibraryLoader : public LibraryLoader {
  public:
   MOCK_METHOD2(Load,
@@ -132,6 +136,10 @@
                 Load(testing::EndsWith(lib), testing::EndsWith(content)))
         .Times(1)
         .WillOnce(testing::Return(true));
+    EXPECT_CALL(library_loader, Resolve("GetCobaltUserAgentString"))
+        .Times(1)
+        .WillOnce(testing::Return(
+            reinterpret_cast<void*>(&GetCobaltUserAgentStringFake)));
     EXPECT_CALL(library_loader, Resolve("SbEventHandle"))
         .Times(1)
         .WillOnce(testing::Return(reinterpret_cast<void*>(&SbEventFake)));
@@ -261,6 +269,10 @@
                                    testing::EndsWith("/foo")))
       .Times(1)
       .WillOnce(testing::Return(true));
+  EXPECT_CALL(library_loader, Resolve("GetCobaltUserAgentString"))
+      .Times(1)
+      .WillOnce(testing::Return(
+          reinterpret_cast<void*>(&GetCobaltUserAgentStringFake)));
   EXPECT_CALL(library_loader, Resolve("SbEventHandle"))
       .Times(1)
       .WillOnce(testing::Return(reinterpret_cast<void*>(&SbEventFake)));
diff --git a/src/starboard/media.h b/src/starboard/media.h
index bdafbe4..05aaf0d 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -544,11 +544,44 @@
 //   or |video/mp4; codecs="avc1.42001E"|. It may include arbitrary parameters
 //   like "codecs", "channels", etc.  Note that the "codecs" parameter may
 //   contain more than one codec, delimited by comma.
-// |key_system|: A lowercase value in fhe form of "com.example.somesystem"
+// |key_system|: A lowercase value in the form of "com.example.somesystem"
 //   as suggested by https://w3c.github.io/encrypted-media/#key-system
 //   that can be matched exactly with known DRM key systems of the platform.
 //   When |key_system| is an empty string, the return value is an indication for
 //   non-encrypted media.
+//
+//   An implementation may choose to support |key_system| with extra attributes,
+//   separated by ';', like
+//   |com.example.somesystem; attribute_name1="value1"; attribute_name2=value1|.
+//   If |key_system| with attributes is not supported by an implementation, it
+//   should treat |key_system| as if it contains only the key system, and reject
+//   any input containing extra attributes, i.e. it can keep using its existing
+//   implementation.
+//   When an implementation supports |key_system| with attributes, it has to
+//   support all attributes defined by the Starboard version the implementation
+//   uses.
+//   An implementation should ignore any unknown attributes, and make a decision
+//   solely based on the key system and the known attributes.  For example, if
+//   an implementation supports "com.widevine.alpha", it should also return
+//   `kSbMediaSupportTypeProbably` when |key_system| is
+//   |com.widevine.alpha; invalid_attribute="invalid_value"|.
+//   Currently the only attribute has to be supported is |encryptionscheme|.  It
+//   reflects the value passed to `encryptionScheme` of
+//   MediaKeySystemMediaCapability, as defined in
+//   https://wicg.github.io/encrypted-media-encryption-scheme/, which can take
+//   value "cenc", "cbcs", or "cbcs-1-9".
+//   Empty string is not a valid value for |encryptionscheme| and the
+//   implementation should return `kSbMediaSupportTypeNotSupported` when
+//   |encryptionscheme| is set to "".
+//   The implementation should return `kSbMediaSupportTypeNotSupported` for
+//   unknown values of known attributes.  For example, if an implementation
+//   supports "encryptionscheme" with value "cenc", "cbcs", or "cbcs-1-9", then
+//   it should return `kSbMediaSupportTypeProbably` when |key_system| is
+//   |com.widevine.alpha; encryptionscheme="cenc"|, and return
+//   `kSbMediaSupportTypeNotSupported` when |key_system| is
+//   |com.widevine.alpha; encryptionscheme="invalid"|.
+//   If an implementation supports key system with attributes on one key system,
+//   it has to support key system with attributes on all key systems supported.
 SB_EXPORT SbMediaSupportType
 SbMediaCanPlayMimeAndKeySystem(const char* mime, const char* key_system);
 
diff --git a/src/starboard/nplb/drm_helpers.h b/src/starboard/nplb/drm_helpers.h
index c24b696..e6b0972 100644
--- a/src/starboard/nplb/drm_helpers.h
+++ b/src/starboard/nplb/drm_helpers.h
@@ -68,6 +68,10 @@
     "com.youtube.fairplay",
 };
 
+static const char* kEncryptionSchemes[] = {
+    "cenc", "cbcs", "cbcs-1-9",
+};
+
 }  // namespace nplb
 }  // namespace starboard
 
diff --git a/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc b/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc
index 2e38c86..3003a56 100644
--- a/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc
+++ b/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc
@@ -14,14 +14,38 @@
 
 #include "starboard/media.h"
 
+#include "starboard/common/string.h"
+#include "starboard/nplb/drm_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace starboard {
-namespace shared {
-namespace starboard {
-namespace media {
+namespace nplb {
 namespace {
 
+bool IsKeySystemWithAttributesSupported() {
+  for (auto key_system : kKeySystems) {
+    if (!SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                        key_system)) {
+      continue;
+    }
+
+    // The key system is supported, let's check if the implementation supports
+    // attributes.  By definition, when an implementation supports key system
+    // with attributes, it should make the decision without any unknown
+    // attributes.  So the following |key_system_with_invalid_attribute| should
+    // be supported on such implementation.
+    std::string key_system_with_invalid_attribute = key_system;
+    key_system_with_invalid_attribute += "; invalid_attribute=\"some_value\"";
+    if (SbMediaCanPlayMimeAndKeySystem(
+            "video/mp4; codecs=\"avc1.4d4015\"",
+            key_system_with_invalid_attribute.c_str())) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 TEST(SbMediaCanPlayMimeAndKeySystem, SunnyDay) {
   // Vp9
   SbMediaCanPlayMimeAndKeySystem(
@@ -89,14 +113,121 @@
       "audio/mp4; codecs=\"mp4a.40.2\"; channels=99", "");
   ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
 
-  // Invalid keysystem
-  result = SbMediaCanPlayMimeAndKeySystem(
-      "video/mp4; codecs=\"avc1.4d4015\"; width=1920; height=1080;", "abc");
+  // Invalid key system
+  result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                          "abc");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+  result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                          "widevine");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+  result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                          "com.widevine.alpha.invalid");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+  result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                          "playready");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+  result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                          "com.youtube.playready.invalid");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+  result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                          "fairplay");
+  ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+  result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                          "com.youtube.fairplay.invalid");
   ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
 }
 
+TEST(SbMediaCanPlayMimeAndKeySystem, MinimumSupport) {
+  // H.264 Main Profile Level 4.2
+  SbMediaSupportType result = SbMediaCanPlayMimeAndKeySystem(
+      "video/mp4; codecs=\"avc1.4d402a\"; width=1920; height=1080; "
+      "framerate=30;",
+      "");
+  ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "video/mp4; codecs=\"avc1.4d402a\"; width=1920; height=1080; "
+      "framerate=60;",
+      "");
+  ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+
+  // H.264 Main Profile Level 2.1
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "video/mp4; codecs=\"avc1.4d4015\"; width=432; height=240; "
+      "framerate=15;",
+      "");
+  ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+
+  // AAC-LC
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "audio/mp4; codecs=\"mp4a.40.2\"; channels=2; bitrate=256;", "");
+  ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+
+  // HE-AAC
+  result = SbMediaCanPlayMimeAndKeySystem(
+      "audio/mp4; codecs=\"mp4a.40.5\"; channels=2; bitrate=48;", "");
+  ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+}
+
+TEST(SbMediaCanPlayMimeAndKeySystem, AnySupportedKeySystems) {
+  bool any_supported_key_systems = false;
+  for (auto key_system : kKeySystems) {
+    if (SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                       key_system)) {
+      any_supported_key_systems = true;
+      break;
+    }
+  }
+  ASSERT_TRUE(any_supported_key_systems);
+}
+
+TEST(SbMediaCanPlayMimeAndKeySystem, KeySystemWithAttributes) {
+  if (!IsKeySystemWithAttributesSupported()) {
+    SB_LOG(INFO) << "KeySystemWithAttributes test skipped because key system"
+                 << " with attribute is not supported.";
+    return;
+  }
+
+  for (auto key_system : kKeySystems) {
+    if (!SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+                                        key_system)) {
+      continue;
+    }
+
+    EXPECT_TRUE(SbMediaCanPlayMimeAndKeySystem(
+        "video/mp4; codecs=\"avc1.4d4015\"",
+        FormatString("%s; %s=\"%s\"", key_system, "invalid_attribute",
+                     "some_value")
+            .c_str()));
+
+    // "" is not a valid value for "encryptionscheme".
+    EXPECT_FALSE(SbMediaCanPlayMimeAndKeySystem(
+        "video/mp4; codecs=\"avc1.4d4015\"",
+        FormatString("%s; %s=\"%s\"", key_system, "encryptionscheme", "")
+            .c_str()));
+
+    bool has_supported_encryption_scheme = false;
+    for (auto encryption_scheme : kEncryptionSchemes) {
+      if (!SbMediaCanPlayMimeAndKeySystem(
+              "video/mp4; codecs=\"avc1.4d4015\"",
+              FormatString("%s; %s=\"%s\"", key_system, "encryptionscheme",
+                           encryption_scheme)
+                  .c_str())) {
+        continue;
+      }
+      has_supported_encryption_scheme = true;
+      EXPECT_TRUE(SbMediaCanPlayMimeAndKeySystem(
+          "video/mp4; codecs=\"avc1.4d4015\"",
+          FormatString("%s; %s=\"%s\"; %s=\"%s\"", key_system,
+                       "encryptionscheme", encryption_scheme,
+                       "invalid_attribute", "some_value")
+              .c_str()));
+    }
+
+    ASSERT_TRUE(has_supported_encryption_scheme);
+  }
+}
+
 }  // namespace
-}  // namespace media
-}  // namespace starboard
-}  // namespace shared
+}  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 8407b1de..24563cf 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -191,6 +191,7 @@
         'player_output_mode_supported_test.cc',
         'player_test_util.cc',
         'player_test_util.h',
+        'player_write_sample_test.cc',
         'random_helpers.cc',
         'recursive_mutex_test.cc',
         'rwlock_test.cc',
diff --git a/src/starboard/nplb/player_test_util.cc b/src/starboard/nplb/player_test_util.cc
index 88f8233..151cbdf 100644
--- a/src/starboard/nplb/player_test_util.cc
+++ b/src/starboard/nplb/player_test_util.cc
@@ -14,12 +14,92 @@
 
 #include "starboard/nplb/player_test_util.h"
 
+#include <functional>
+
+#include "starboard/audio_sink.h"
+#include "starboard/common/string.h"
 #include "starboard/directory.h"
 #include "starboard/nplb/player_creation_param_helpers.h"
+#include "starboard/shared/starboard/player/video_dmp_reader.h"
+#include "starboard/testing/fake_graphics_context_provider.h"
 
 namespace starboard {
 namespace nplb {
 
+namespace {
+
+using shared::starboard::player::video_dmp::VideoDmpReader;
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::placeholders::_3;
+using std::placeholders::_4;
+using testing::FakeGraphicsContextProvider;
+
+void ErrorFunc(SbPlayer player,
+               void* context,
+               SbPlayerError error,
+               const char* message) {
+  atomic_bool* error_occurred = static_cast<atomic_bool*>(context);
+  error_occurred->exchange(true);
+}
+
+}  // namespace
+
+std::vector<SbPlayerTestConfig> GetSupportedSbPlayerTestConfigs() {
+  const char* kAudioTestFiles[] = {"beneath_the_canopy_aac_stereo.dmp",
+                                   "beneath_the_canopy_aac_5_1.dmp",
+                                   "beneath_the_canopy_aac_mono.dmp",
+                                   "beneath_the_canopy_opus_5_1.dmp",
+                                   "beneath_the_canopy_opus_stereo.dmp",
+                                   "beneath_the_canopy_opus_mono.dmp",
+                                   "sintel_329_ec3.dmp",
+                                   "sintel_381_ac3.dmp",
+                                   "heaac.dmp"};
+
+  const char* kVideoTestFiles[] = {"beneath_the_canopy_137_avc.dmp",
+                                   "beneath_the_canopy_248_vp9.dmp",
+                                   "sintel_399_av1.dmp"};
+
+  const SbPlayerOutputMode kOutputModes[] = {kSbPlayerOutputModeDecodeToTexture,
+                                             kSbPlayerOutputModePunchOut};
+
+  std::vector<SbPlayerTestConfig> test_configs;
+
+  const char* kEmptyName = NULL;
+
+  for (auto audio_filename : kAudioTestFiles) {
+    VideoDmpReader dmp_reader(ResolveTestFileName(audio_filename).c_str());
+    SB_DCHECK(dmp_reader.number_of_audio_buffers() > 0);
+
+    const auto* audio_sample_info = &dmp_reader.audio_sample_info();
+    if (IsMediaConfigSupported(kSbMediaVideoCodecNone, dmp_reader.audio_codec(),
+                               kSbDrmSystemInvalid, audio_sample_info,
+                               "", /* max_video_capabilities */
+                               kSbPlayerOutputModePunchOut)) {
+      test_configs.push_back(std::make_tuple(audio_filename, kEmptyName,
+                                             kSbPlayerOutputModePunchOut));
+    }
+  }
+
+  for (auto video_filename : kVideoTestFiles) {
+    VideoDmpReader dmp_reader(ResolveTestFileName(video_filename).c_str());
+    SB_DCHECK(dmp_reader.number_of_video_buffers() > 0);
+
+    for (auto output_mode : kOutputModes) {
+      if (IsMediaConfigSupported(dmp_reader.video_codec(),
+                                 kSbMediaAudioCodecNone, kSbDrmSystemInvalid,
+                                 NULL, /* audio_sample_info */
+                                 "",   /* max_video_capabilities */
+                                 output_mode)) {
+        test_configs.push_back(
+            std::make_tuple(kEmptyName, video_filename, output_mode));
+      }
+    }
+  }
+
+  return test_configs;
+}
+
 std::string ResolveTestFileName(const char* filename) {
   std::vector<char> content_path(kSbFileMaxPath);
   SB_CHECK(SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(),
@@ -45,10 +125,10 @@
                             SbPlayerDecoderState state,
                             int ticket) {}
 
-void DummyStatusFunc(SbPlayer player,
-                     void* context,
-                     SbPlayerState state,
-                     int ticket) {}
+void DummyPlayerStatusFunc(SbPlayer player,
+                           void* context,
+                           SbPlayerState state,
+                           int ticket) {}
 
 void DummyErrorFunc(SbPlayer player,
                     void* context,
@@ -116,5 +196,30 @@
 #endif  // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
 }
 
+bool IsMediaConfigSupported(SbMediaVideoCodec video_codec,
+                            SbMediaAudioCodec audio_codec,
+                            SbDrmSystem drm_system,
+                            const SbMediaAudioSampleInfo* audio_sample_info,
+                            const char* max_video_capabilities,
+                            SbPlayerOutputMode output_mode) {
+  if (audio_codec != kSbMediaAudioCodecNone &&
+      audio_sample_info->number_of_channels > SbAudioSinkGetMaxChannels()) {
+    return false;
+  }
+
+  atomic_bool error_occurred;
+  FakeGraphicsContextProvider fake_graphics_context_provider;
+  SbPlayer player = CallSbPlayerCreate(
+      fake_graphics_context_provider.window(), video_codec, audio_codec,
+      drm_system, audio_sample_info, max_video_capabilities,
+      DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyPlayerStatusFunc,
+      ErrorFunc, &error_occurred, output_mode,
+      fake_graphics_context_provider.decoder_target_provider());
+  bool is_valid_player = SbPlayerIsValid(player);
+  SbPlayerDestroy(player);
+
+  return is_valid_player && !error_occurred.load();
+}
+
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/nplb/player_test_util.h b/src/starboard/nplb/player_test_util.h
index 335e414..ece5faf 100644
--- a/src/starboard/nplb/player_test_util.h
+++ b/src/starboard/nplb/player_test_util.h
@@ -16,6 +16,7 @@
 #define STARBOARD_NPLB_PLAYER_TEST_UTIL_H_
 
 #include <string>
+#include <tuple>
 #include <vector>
 
 #include "starboard/configuration_constants.h"
@@ -24,6 +25,13 @@
 namespace starboard {
 namespace nplb {
 
+typedef std::tuple<const char* /* audio_filename */,
+                   const char* /* video_filename */,
+                   SbPlayerOutputMode /* output_mode */>
+    SbPlayerTestConfig;
+
+std::vector<SbPlayerTestConfig> GetSupportedSbPlayerTestConfigs();
+
 std::string ResolveTestFileName(const char* filename);
 
 void DummyDeallocateSampleFunc(SbPlayer player,
@@ -64,6 +72,13 @@
 bool IsOutputModeSupported(SbPlayerOutputMode output_mode,
                            SbMediaVideoCodec codec);
 
+bool IsMediaConfigSupported(SbMediaVideoCodec video_codec,
+                            SbMediaAudioCodec audio_codec,
+                            SbDrmSystem drm_system,
+                            const SbMediaAudioSampleInfo* audio_sample_info,
+                            const char* max_video_capabilities,
+                            SbPlayerOutputMode output_mode);
+
 }  // namespace nplb
 }  // namespace starboard
 
diff --git a/src/starboard/nplb/player_write_sample_test.cc b/src/starboard/nplb/player_write_sample_test.cc
new file mode 100644
index 0000000..13977ee
--- /dev/null
+++ b/src/starboard/nplb/player_write_sample_test.cc
@@ -0,0 +1,520 @@
+// Copyright 2020 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 <algorithm>
+#include <deque>
+#include <functional>
+
+#include "starboard/atomic.h"
+#include "starboard/common/optional.h"
+#include "starboard/common/queue.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/common/string.h"
+#include "starboard/nplb/player_test_util.h"
+#include "starboard/shared/starboard/player/video_dmp_reader.h"
+#include "starboard/string.h"
+#include "starboard/testing/fake_graphics_context_provider.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+using shared::starboard::player::video_dmp::VideoDmpReader;
+using testing::FakeGraphicsContextProvider;
+using ::testing::ValuesIn;
+
+const SbTimeMonotonic kDefaultWaitForDecoderStateNeedsDataTimeout =
+    5 * kSbTimeSecond;
+const SbTimeMonotonic kDefaultWaitForPlayerStateTimeout = 5 * kSbTimeSecond;
+const SbTimeMonotonic kDefaultWaitForCallbackEventTimeout =
+    15 * kSbTimeMillisecond;
+
+class SbPlayerWriteSampleTest
+    : public ::testing::TestWithParam<SbPlayerTestConfig> {
+ public:
+  SbPlayerWriteSampleTest();
+
+  void SetUp() override;
+  void TearDown() override;
+
+ protected:
+  struct CallbackEvent {
+    CallbackEvent() {}
+
+    CallbackEvent(SbPlayer player, SbPlayerState state, int ticket)
+        : player(player), player_state(state), ticket(ticket) {}
+
+    CallbackEvent(SbPlayer player, SbPlayerDecoderState state, int ticket)
+        : player(player), decoder_state(state), ticket(ticket) {}
+
+    bool HasStateUpdate() const {
+      return player_state.has_engaged() || decoder_state.has_engaged();
+    }
+
+    SbPlayer player = kSbPlayerInvalid;
+    optional<SbPlayerState> player_state;
+    optional<SbPlayerDecoderState> decoder_state;
+    int ticket = SB_PLAYER_INITIAL_TICKET;
+  };
+
+  static void DecoderStatusCallback(SbPlayer player,
+                                    void* context,
+                                    SbMediaType type,
+                                    SbPlayerDecoderState state,
+                                    int ticket);
+
+  static void PlayerStatusCallback(SbPlayer player,
+                                   void* context,
+                                   SbPlayerState state,
+                                   int ticket);
+
+  static void ErrorCallback(SbPlayer player,
+                            void* context,
+                            SbPlayerError error,
+                            const char* message);
+
+  void InitializePlayer();
+
+  void PrepareForSeek();
+
+  void Seek(const SbTime time);
+
+  // When the |output_mode| is decoding to texture, then this method is used to
+  // advance the decoded frames.
+  void GetDecodeTargetWhenSupported();
+
+  // Callback methods from the underlying player.
+  void OnDecoderState(SbPlayer player,
+                      SbMediaType media_type,
+                      SbPlayerDecoderState state,
+                      int ticket);
+  void OnPlayerState(SbPlayer player, SbPlayerState state, int ticket);
+  void OnError(SbPlayer player, SbPlayerError error, const char* message);
+
+  // Waits for |kSbPlayerDecoderStateNeedsData| to be sent.
+  void WaitForDecoderStateNeedsData(
+      const SbTime timeout = kDefaultWaitForDecoderStateNeedsDataTimeout);
+
+  // Waits for desired player state update to be sent.
+  void WaitForPlayerState(
+      const SbPlayerState desired_state,
+      const SbTime timeout = kDefaultWaitForPlayerStateTimeout);
+
+  // Player and Decoder methods for driving input and output.
+  void WriteSingleInput(size_t index);
+  void WriteEndOfStream();
+  void WriteMultipleInputs(size_t start_index, size_t num_inputs_to_write);
+  void DrainOutputs();
+
+  int GetNumBuffers() const;
+
+  SbMediaType GetTestMediaType() const { return test_media_type_; }
+
+  // Determine if the the current event is valid based on previously received
+  // player state updates, or other inputs to the player.
+  void AssertPlayerStateIsValid(SbPlayerState state) const;
+
+  bool HasReceivedPlayerState(SbPlayerState state) const {
+    return player_state_set_.find(state) != player_state_set_.end();
+  }
+
+  // Checks if there are pending callback events and, if so, logs the received
+  // state update in the appropriate tracking container:
+  // * |decoder_state_queue_| for SbPlayerDecoderState updates.
+  // * |player_state_set_| for SbPlayerState updates.
+  // Executes a blocking wait for any new CallbackEvents to be enqueued.
+  void TryProcessCallbackEvents(SbTime timeout);
+
+  // Queue of events from the underlying player.
+  Queue<CallbackEvent> callback_event_queue_;
+
+  // Ordered queue of pending decoder state updates.
+  std::deque<SbPlayerDecoderState> decoder_state_queue_;
+
+  // Set of received player state updates from the underlying player. This is
+  // used to check that the state updates occur in a valid order during normal
+  // playback.
+  std::set<SbPlayerState> player_state_set_;
+
+  // Test instance specific configuration.
+  std::string dmp_filename_;
+  SbMediaType test_media_type_;
+  SbPlayerOutputMode output_mode_;
+  scoped_ptr<VideoDmpReader> dmp_reader_;
+
+  FakeGraphicsContextProvider fake_graphics_context_provider_;
+  SbPlayer player_ = kSbPlayerInvalid;
+
+  bool destroy_player_called_ = false;
+  bool end_of_stream_written_ = false;
+  atomic_bool error_occurred_;
+  int ticket_ = SB_PLAYER_INITIAL_TICKET;
+};
+
+SbPlayerWriteSampleTest::SbPlayerWriteSampleTest() {
+  const char* audio_filename = std::get<0>(GetParam());
+  const char* video_filename = std::get<1>(GetParam());
+  output_mode_ = std::get<2>(GetParam());
+
+  SB_DCHECK(output_mode_ != kSbPlayerOutputModeInvalid);
+
+  if (audio_filename != NULL) {
+    SB_DCHECK(video_filename == NULL);
+
+    dmp_filename_ = audio_filename;
+    test_media_type_ = kSbMediaTypeAudio;
+  } else {
+    SB_DCHECK(video_filename != NULL);
+
+    dmp_filename_ = video_filename;
+    test_media_type_ = kSbMediaTypeVideo;
+  }
+  dmp_reader_.reset(
+      new VideoDmpReader(ResolveTestFileName(dmp_filename_.c_str()).c_str()));
+
+  SB_LOG(INFO) << FormatString(
+      "Initialize SbPlayerWriteSampleTest with dmp file '%s' and with output "
+      "mode '%s'.",
+      dmp_filename_.c_str(),
+      output_mode_ == kSbPlayerOutputModeDecodeToTexture ? "Decode To Texture"
+                                                         : "Punchout");
+}
+
+void SbPlayerWriteSampleTest::SetUp() {
+  SbMediaVideoCodec video_codec = dmp_reader_->video_codec();
+  SbMediaAudioCodec audio_codec = dmp_reader_->audio_codec();
+  const SbMediaAudioSampleInfo* audio_sample_info = NULL;
+
+  if (test_media_type_ == kSbMediaTypeAudio) {
+    SB_DCHECK(audio_codec != kSbMediaAudioCodecNone);
+
+    audio_sample_info = &dmp_reader_->audio_sample_info();
+    video_codec = kSbMediaVideoCodecNone;
+  } else {
+    SB_DCHECK(video_codec != kSbMediaVideoCodecNone);
+
+    audio_codec = kSbMediaAudioCodecNone;
+  }
+
+  player_ = CallSbPlayerCreate(
+      fake_graphics_context_provider_.window(), video_codec, audio_codec,
+      kSbDrmSystemInvalid, audio_sample_info, "", DummyDeallocateSampleFunc,
+      DecoderStatusCallback, PlayerStatusCallback, ErrorCallback, this,
+      output_mode_, fake_graphics_context_provider_.decoder_target_provider());
+
+  ASSERT_TRUE(SbPlayerIsValid(player_));
+
+  InitializePlayer();
+}
+
+void SbPlayerWriteSampleTest::TearDown() {
+  SB_DCHECK(SbPlayerIsValid(player_));
+
+  ASSERT_FALSE(destroy_player_called_);
+  destroy_player_called_ = true;
+  SbPlayerDestroy(player_);
+
+  // We expect the event to be sent already.
+  ASSERT_NO_FATAL_FAILURE(WaitForPlayerState(kSbPlayerStateDestroyed, 0));
+  ASSERT_FALSE(error_occurred_.load());
+}
+
+// static
+void SbPlayerWriteSampleTest::DecoderStatusCallback(SbPlayer player,
+                                                    void* context,
+                                                    SbMediaType type,
+                                                    SbPlayerDecoderState state,
+                                                    int ticket) {
+  SbPlayerWriteSampleTest* sb_player_write_sample_test =
+      static_cast<SbPlayerWriteSampleTest*>(context);
+  sb_player_write_sample_test->OnDecoderState(player, type, state, ticket);
+}
+
+// static
+void SbPlayerWriteSampleTest::PlayerStatusCallback(SbPlayer player,
+                                                   void* context,
+                                                   SbPlayerState state,
+                                                   int ticket) {
+  SbPlayerWriteSampleTest* sb_player_write_sample_test =
+      static_cast<SbPlayerWriteSampleTest*>(context);
+  sb_player_write_sample_test->OnPlayerState(player, state, ticket);
+}
+
+// static
+void SbPlayerWriteSampleTest::ErrorCallback(SbPlayer player,
+                                            void* context,
+                                            SbPlayerError error,
+                                            const char* message) {
+  SbPlayerWriteSampleTest* sb_player_write_sample_test =
+      static_cast<SbPlayerWriteSampleTest*>(context);
+  sb_player_write_sample_test->OnError(player, error, message);
+}
+
+void SbPlayerWriteSampleTest::InitializePlayer() {
+  ASSERT_FALSE(destroy_player_called_);
+  ASSERT_NO_FATAL_FAILURE(WaitForPlayerState(kSbPlayerStateInitialized));
+  Seek(0);
+  SbPlayerSetPlaybackRate(player_, 1.0);
+  SbPlayerSetVolume(player_, 1.0);
+}
+
+void SbPlayerWriteSampleTest::PrepareForSeek() {
+  ASSERT_FALSE(destroy_player_called_);
+  ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+  ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStateInitialized));
+  player_state_set_.clear();
+  player_state_set_.insert(kSbPlayerStateInitialized);
+  ticket_++;
+}
+
+void SbPlayerWriteSampleTest::Seek(const SbTime time) {
+  PrepareForSeek();
+  SbPlayerSeek2(player_, time, ticket_);
+  ASSERT_NO_FATAL_FAILURE(WaitForDecoderStateNeedsData());
+  ASSERT_TRUE(decoder_state_queue_.empty());
+}
+
+void SbPlayerWriteSampleTest::GetDecodeTargetWhenSupported() {
+  if (destroy_player_called_) {
+    return;
+  }
+#if SB_HAS(GLES2)
+  fake_graphics_context_provider_.RunOnGlesContextThread([&]() {
+    ASSERT_TRUE(SbPlayerIsValid(player_));
+    if (output_mode_ != kSbPlayerOutputModeDecodeToTexture) {
+#if SB_API_VERSION >= 12
+      ASSERT_EQ(SbPlayerGetCurrentFrame(player_), kSbDecodeTargetInvalid);
+#endif  // SB_API_VERSION >= 12
+      return;
+    }
+    ASSERT_EQ(output_mode_, kSbPlayerOutputModeDecodeToTexture);
+    SbDecodeTarget frame = SbPlayerGetCurrentFrame(player_);
+    if (SbDecodeTargetIsValid(frame)) {
+      SbDecodeTargetRelease(frame);
+    }
+  });
+#endif  // SB_HAS(GLES2)
+}
+
+void SbPlayerWriteSampleTest::OnDecoderState(SbPlayer player,
+                                             SbMediaType type,
+                                             SbPlayerDecoderState state,
+                                             int ticket) {
+  switch (state) {
+    case kSbPlayerDecoderStateNeedsData:
+      callback_event_queue_.Put(CallbackEvent(player, state, ticket));
+      break;
+#if SB_API_VERSION < 12
+    // Note: we do not add these events to the queue since these states are not
+    // used in Cobalt and are being deprecated.
+    case kSbPlayerDecoderStateBufferFull:
+    case kSbPlayerDecoderStateDestroyed:
+      break;
+#endif  // SB_API_VERSION < 12
+  }
+}
+
+void SbPlayerWriteSampleTest::OnPlayerState(SbPlayer player,
+                                            SbPlayerState state,
+                                            int ticket) {
+  callback_event_queue_.Put(CallbackEvent(player, state, ticket));
+}
+
+void SbPlayerWriteSampleTest::OnError(SbPlayer player,
+                                      SbPlayerError error,
+                                      const char* message) {
+  SB_LOG(ERROR) << FormatString("Got SbPlayerError %d with message '%s'", error,
+                                message != NULL ? message : "");
+  error_occurred_.exchange(true);
+}
+
+void SbPlayerWriteSampleTest::WaitForDecoderStateNeedsData(
+    const SbTime timeout) {
+  optional<SbPlayerDecoderState> received_state;
+  SbTimeMonotonic start = SbTimeGetMonotonicNow();
+  do {
+    ASSERT_FALSE(error_occurred_.load());
+    GetDecodeTargetWhenSupported();
+    ASSERT_NO_FATAL_FAILURE(TryProcessCallbackEvents(
+        std::min(timeout, kDefaultWaitForCallbackEventTimeout)));
+    if (decoder_state_queue_.empty()) {
+      continue;
+    }
+    received_state = decoder_state_queue_.front();
+    decoder_state_queue_.pop_front();
+    if (received_state.value() == kSbPlayerDecoderStateNeedsData) {
+      break;
+    }
+  } while (SbTimeGetMonotonicNow() - start < timeout);
+
+  ASSERT_TRUE(received_state.has_engaged()) << "Did not receive any states.";
+  ASSERT_EQ(kSbPlayerDecoderStateNeedsData, received_state.value())
+      << "Did not receive expected state.";
+}
+
+void SbPlayerWriteSampleTest::WaitForPlayerState(
+    const SbPlayerState desired_state,
+    const SbTime timeout) {
+  SbTimeMonotonic start = SbTimeGetMonotonicNow();
+  do {
+    ASSERT_FALSE(error_occurred_.load());
+    GetDecodeTargetWhenSupported();
+    ASSERT_NO_FATAL_FAILURE(TryProcessCallbackEvents(
+        std::min(timeout, kDefaultWaitForCallbackEventTimeout)));
+    if (HasReceivedPlayerState(desired_state)) {
+      break;
+    }
+  } while (SbTimeGetMonotonicNow() - start < timeout);
+  ASSERT_TRUE(HasReceivedPlayerState(desired_state))
+      << "Did not received expected state.";
+}
+
+void SbPlayerWriteSampleTest::WriteSingleInput(size_t index) {
+  ASSERT_FALSE(destroy_player_called_);
+  ASSERT_LT(index, GetNumBuffers());
+  SbPlayerSampleInfo sample_info =
+      dmp_reader_->GetPlayerSampleInfo(test_media_type_, index);
+  SbPlayerWriteSample2(player_, test_media_type_, &sample_info, 1);
+}
+
+void SbPlayerWriteSampleTest::WriteEndOfStream() {
+  ASSERT_FALSE(destroy_player_called_);
+  ASSERT_FALSE(end_of_stream_written_);
+  end_of_stream_written_ = true;
+  SbPlayerWriteEndOfStream(player_, test_media_type_);
+}
+
+void SbPlayerWriteSampleTest::WriteMultipleInputs(size_t start_index,
+                                                  size_t num_inputs_to_write) {
+  SB_DCHECK(num_inputs_to_write > 0);
+  SB_DCHECK(start_index < GetNumBuffers());
+
+  ASSERT_NO_FATAL_FAILURE(WriteSingleInput(start_index));
+  ++start_index;
+  --num_inputs_to_write;
+
+  while (num_inputs_to_write > 0 && start_index < GetNumBuffers()) {
+    ASSERT_NO_FATAL_FAILURE(WaitForDecoderStateNeedsData());
+    ASSERT_NO_FATAL_FAILURE(WriteSingleInput(start_index));
+    ++start_index;
+    --num_inputs_to_write;
+  }
+}
+
+void SbPlayerWriteSampleTest::DrainOutputs() {
+  ASSERT_TRUE(end_of_stream_written_);
+  ASSERT_NO_FATAL_FAILURE(WaitForPlayerState(kSbPlayerStateEndOfStream));
+  // We should not get any new decoder events after end of stream.
+  ASSERT_TRUE(decoder_state_queue_.empty());
+}
+
+int SbPlayerWriteSampleTest::GetNumBuffers() const {
+  return test_media_type_ == kSbMediaTypeAudio
+             ? dmp_reader_->number_of_audio_buffers()
+             : dmp_reader_->number_of_video_buffers();
+}
+
+void SbPlayerWriteSampleTest::AssertPlayerStateIsValid(
+    SbPlayerState state) const {
+  // Note: it is possible to receive the same state that has been previously
+  // received in the case of multiple Seek() calls. Prior to any Seek commands
+  // issued in this test, we should reset the |player_state_set_| member.
+  ASSERT_FALSE(HasReceivedPlayerState(state));
+
+  switch (state) {
+    case kSbPlayerStateInitialized:
+      // No other states have been received before getting Initialized.
+      ASSERT_TRUE(player_state_set_.empty());
+      break;
+    case kSbPlayerStatePrerolling:
+      ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStateInitialized));
+      ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+      break;
+    case kSbPlayerStatePresenting:
+      ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStatePrerolling));
+      ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+      break;
+    case kSbPlayerStateEndOfStream:
+      ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStateInitialized));
+      ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStatePrerolling));
+      ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+      break;
+    case kSbPlayerStateDestroyed:
+      // Nothing stops the user of the player from destroying the player during
+      // any of the previous states.
+      ASSERT_TRUE(destroy_player_called_);
+      break;
+  }
+}
+
+void SbPlayerWriteSampleTest::TryProcessCallbackEvents(SbTime timeout) {
+  for (;;) {
+    auto event = callback_event_queue_.GetTimed(timeout);
+    if (!event.HasStateUpdate()) {
+      break;
+    }
+    if (event.ticket != ticket_) {
+      continue;
+    }
+    ASSERT_EQ(event.player, player_);
+    SB_DCHECK(event.decoder_state.has_engaged() ^
+              event.player_state.has_engaged());
+    if (event.decoder_state.has_engaged()) {
+      // Callbacks may be in-flight at the time that the player is destroyed by
+      // a call to |SbPlayerDestroy|. In this case, the callbacks are ignored.
+      // However no new callbacks are expected after receiving the player status
+      // |kSbPlayerStateDestroyed|.
+      ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+      decoder_state_queue_.push_back(event.decoder_state.value());
+      continue;
+    }
+    ASSERT_NO_FATAL_FAILURE(
+        AssertPlayerStateIsValid(event.player_state.value()));
+    player_state_set_.insert(event.player_state.value());
+  }
+}
+
+TEST_P(SbPlayerWriteSampleTest, SeekAndDestroy) {
+  PrepareForSeek();
+  SbPlayerSeek2(player_, 1 * kSbTimeSecond, ticket_);
+}
+
+TEST_P(SbPlayerWriteSampleTest, NoInput) {
+  WriteEndOfStream();
+  DrainOutputs();
+}
+
+TEST_P(SbPlayerWriteSampleTest, SingleInput) {
+  ASSERT_NO_FATAL_FAILURE(WriteSingleInput(0));
+  ASSERT_NO_FATAL_FAILURE(WaitForDecoderStateNeedsData());
+  WriteEndOfStream();
+  DrainOutputs();
+}
+
+TEST_P(SbPlayerWriteSampleTest, MultipleInputs) {
+  ASSERT_NO_FATAL_FAILURE(
+      WriteMultipleInputs(0, std::min<size_t>(10, GetNumBuffers())));
+  ASSERT_NO_FATAL_FAILURE(WaitForDecoderStateNeedsData());
+  WriteEndOfStream();
+  DrainOutputs();
+}
+
+INSTANTIATE_TEST_CASE_P(SbPlayerWriteSampleTests,
+                        SbPlayerWriteSampleTest,
+                        ValuesIn(GetSupportedSbPlayerTestConfigs()));
+
+}  // namespace
+}  // namespace nplb
+}  // namespace starboard
diff --git a/src/starboard/nplb/time_constants.h b/src/starboard/nplb/time_constants.h
index 2b87459..e816429 100644
--- a/src/starboard/nplb/time_constants.h
+++ b/src/starboard/nplb/time_constants.h
@@ -47,15 +47,15 @@
 static const SbTime kTestTimeWindowsNegative =
     SbTimeFromPosix(SB_INT64_C(-15065654400) * kSbTimeSecond);
 
-// 1443473373 in POSIX time is
-// Monday, 9/28/2015 20:49:33 UTC
-// NOTE: Update this value once every 5 or so years.
+// 1600970155 in POSIX time is
+// Thursday, 9/24/2020 17:55:55 UTC
+// NOTE: Update this value once every 25 or so years.
 static const SbTime kTestTimeWritten =
-    SbTimeFromPosix(SB_INT64_C(1443473373) * kSbTimeSecond);
+    SbTimeFromPosix(SB_INT64_C(1600970155) * kSbTimeSecond);
 
-// 5 years after the time this test was written.
+// 25 years after the time this test was written.
 static const SbTime kTestTimePastWritten =
-    SbTimeFromPosix(kTestTimeWritten + (5 * kTestSbTimeYear));
+    SbTimeFromPosix(kTestTimeWritten + (25 * kTestSbTimeYear));
 
 }  // namespace nplb
 }  // namespace starboard
diff --git a/src/starboard/raspi/2/sbversion/12/gyp_configuration.py b/src/starboard/raspi/2/sbversion/12/gyp_configuration.py
index 622f13c..995e684 100644
--- a/src/starboard/raspi/2/sbversion/12/gyp_configuration.py
+++ b/src/starboard/raspi/2/sbversion/12/gyp_configuration.py
@@ -22,5 +22,5 @@
 
 def CreatePlatformConfig():
   return _PARENT_CONFIGURATION.Raspi2PlatformConfig(
-      platform='raspi-2-sbversion-12',
+      'raspi-2-sbversion-12',
       sabi_json_path='starboard/sabi/arm/hardfp/sabi-v12.json')
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi
index deb191c..d2dd46b 100644
--- a/src/starboard/raspi/shared/gyp_configuration.gypi
+++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -76,7 +76,8 @@
       # libraries.
       '-L<(sysroot)/opt/vc/lib',
       '-Wl,-rpath=<(sysroot)/opt/vc/lib',
-
+      # Cleanup unused sections
+      '-Wl,-gc-sections',
       # We don't wrap these symbols, but this ensures that they aren't
       # linked in.
       '-Wl,--wrap=malloc',
@@ -107,9 +108,15 @@
     ],
     'compiler_flags_qa_size': [
       '-Os',
+      # Compile symbols in separate sections
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
     'compiler_flags_qa_speed': [
       '-O2',
+      # Compile symbols in separate sections
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
     'compiler_flags_cc_qa': [
       '-fno-rtti',
@@ -119,9 +126,15 @@
     ],
     'compiler_flags_gold_size': [
       '-Os',
+      # Compile symbols in separate sections
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
     'compiler_flags_gold_speed': [
       '-O2',
+      # Compile symbols in separate sections
+      '-ffunction-sections',
+      '-fdata-sections',
     ],
     'compiler_flags_cc_gold': [
       '-fno-rtti',
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py
index 65e4796..faacb40 100644
--- a/src/starboard/raspi/shared/gyp_configuration.py
+++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -39,9 +39,10 @@
                platform,
                sabi_json_path='starboard/sabi/default/sabi.json'):
     super(RaspiPlatformConfig, self).__init__(platform)
+
+    self.sabi_json_path = sabi_json_path
     self.AppendApplicationConfigurationPath(os.path.dirname(__file__))
     self.raspi_home = os.environ.get('RASPI_HOME', _UNDEFINED_RASPI_HOME)
-    self.sabi_json_path = sabi_json_path
     self.sysroot = os.path.realpath(os.path.join(self.raspi_home, 'sysroot'))
 
   def GetBuildFormat(self):
@@ -69,18 +70,24 @@
   def GetEnvironmentVariables(self):
     if not hasattr(self, 'host_compiler_environment'):
       self.host_compiler_environment = build.GetHostCompilerEnvironment(
-          clang_specification.GetClangSpecification(), False)
+          clang_specification.GetClangSpecification(), self.build_accelerator)
 
-    env_variables = self.host_compiler_environment
     toolchain = os.path.realpath(
         os.path.join(
             self.raspi_home,
             'tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64'))
     toolchain_bin_dir = os.path.join(toolchain, 'bin')
+
+    env_variables = self.host_compiler_environment
     env_variables.update({
-        'CC': os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-gcc'),
-        'CXX': os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-g++'),
-        'STRIP': os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-strip'),
+        'CC':
+            self.build_accelerator + ' ' +
+            os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-gcc'),
+        'CXX':
+            self.build_accelerator + ' ' +
+            os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-g++'),
+        'STRIP':
+            os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-strip'),
     })
     return env_variables
 
@@ -149,6 +156,9 @@
   __FILTERED_TESTS = {  # pylint: disable=invalid-name
       'nplb': [
           'SbDrmTest.AnySupportedKeySystems',
+          'SbMediaCanPlayMimeAndKeySystem.AnySupportedKeySystems',
+          'SbMediaCanPlayMimeAndKeySystem.KeySystemWithAttributes',
+          'SbMediaCanPlayMimeAndKeySystem.MinimumSupport',
       ],
       'player_filter_tests': [
           # The implementations for the raspberry pi (0 and 2) are incomplete
diff --git a/src/starboard/shared/starboard/media/codec_util.cc b/src/starboard/shared/starboard/media/codec_util.cc
index 64a649b..7d192fd 100644
--- a/src/starboard/shared/starboard/media/codec_util.cc
+++ b/src/starboard/shared/starboard/media/codec_util.cc
@@ -499,10 +499,10 @@
     return true;
   }
 
-  // 6. Parse chroma subsampling, which we only support 01.
+  // 6. Parse chroma subsampling, which we only support 00 and 01.
   // Note that this value is not returned.
   int chroma;
-  if (!ReadTwoDigitDecimal(codec + 14, &chroma) || chroma != 1) {
+  if (!ReadTwoDigitDecimal(codec + 14, &chroma) || (chroma != 0 && chroma != 1)) {
     return false;
   }
 
diff --git a/src/starboard/shared/starboard/media/media_util.cc b/src/starboard/shared/starboard/media/media_util.cc
index 9b721cb..3269d04 100644
--- a/src/starboard/shared/starboard/media/media_util.cc
+++ b/src/starboard/shared/starboard/media/media_util.cc
@@ -373,8 +373,11 @@
   }
 
   if (codecs.size() == 0) {
-    // This is a progressive query.  We only support "video/mp4" in this case.
-    if (mime_type.type() == "video" && mime_type.subtype() == "mp4") {
+    // This happens when the H5 player is either querying for progressive
+    // playback support, or probing for generic mp4 support without specific
+    // codecs.  We only support "audio/mp4" and "video/mp4" for these cases.
+    if ((mime_type.type() == "audio" || mime_type.type() == "video") &&
+        mime_type.subtype() == "mp4") {
       return kSbMediaSupportTypeMaybe;
     }
     return kSbMediaSupportTypeNotSupported;
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h
index 567c4a1..e4c0eef 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.h
+++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -197,9 +197,10 @@
         scoped_refptr<VideoRendererSink>* video_renderer_sink);
 
     // Check AudioRenderer ctor for more details on the parameters.
-    void GetAudioRendererParams(const CreationParameters& creation_parameters,
-                                int* max_cached_frames,
-                                int* min_frames_per_append) const;
+    virtual void GetAudioRendererParams(
+        const CreationParameters& creation_parameters,
+        int* max_cached_frames,
+        int* min_frames_per_append) const;
 
    private:
     SB_DISALLOW_COPY_AND_ASSIGN(Factory);
diff --git a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.h b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.h
index 1450b58..49dbd09 100644
--- a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.h
+++ b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.h
@@ -86,7 +86,11 @@
                           SbPlayerOutputMode output_mode,
                           bool using_stub_decoder);
 
-  ~VideoDecoderTestFixture() { video_decoder_->Reset(); }
+  ~VideoDecoderTestFixture() {
+    if (video_decoder_) {
+      video_decoder_->Reset();
+    }
+  }
 
   void Initialize();
 
diff --git a/src/starboard/shared/widevine/drm_system_widevine.cc b/src/starboard/shared/widevine/drm_system_widevine.cc
index c3910d3..4a8eaf1 100644
--- a/src/starboard/shared/widevine/drm_system_widevine.cc
+++ b/src/starboard/shared/widevine/drm_system_widevine.cc
@@ -26,6 +26,7 @@
 #include "starboard/memory.h"
 #include "starboard/once.h"
 #include "starboard/shared/starboard/application.h"
+#include "starboard/shared/starboard/media/mime_type.h"
 #include "starboard/shared/widevine/widevine_storage.h"
 #include "starboard/shared/widevine/widevine_timer.h"
 #include "starboard/time.h"
@@ -40,7 +41,7 @@
 namespace {
 
 const int kInitializationVectorSize = 16;
-const char* kWidevineKeySystem[] = {"com.widevine", "com.widevine.alpha"};
+const char* kWidevineKeySystems[] = {"com.widevine", "com.widevine.alpha"};
 const char kWidevineStorageFileName[] = "wvcdm.dat";
 
 // Key usage may be blocked due to incomplete HDCP authentication which could
@@ -264,8 +265,28 @@
 
 // static
 bool DrmSystemWidevine::IsKeySystemSupported(const char* key_system) {
-  for (auto wv_key_system : kWidevineKeySystem) {
-    if (SbStringCompareAll(key_system, wv_key_system) == 0) {
+  SB_DCHECK(key_system);
+
+  // It is possible that the |key_system| comes with extra attributes, like
+  // `com.widevine.alpha; encryptionscheme="cenc"`.  We prepend "key_system/"
+  // to it, so it can be parsed by MimeType.
+  starboard::media::MimeType mime_type(std::string("key_system/") + key_system);
+
+  if (!mime_type.is_valid()) {
+    return false;
+  }
+  SB_DCHECK(mime_type.type() == "key_system");
+
+  for (auto wv_key_system : kWidevineKeySystems) {
+    if (mime_type.subtype() == wv_key_system) {
+      for (int i = 0; i < mime_type.GetParamCount(); ++i) {
+        if (mime_type.GetParamName(i) == "encryptionscheme") {
+          auto value = mime_type.GetParamStringValue(i);
+          if (value != "cenc" && value != "cbcs" && value != "cbcs-1-9") {
+            return false;
+          }
+        }
+      }
       return true;
     }
   }
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index 07cfc1f..b206794 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -41,6 +41,10 @@
 #include "starboard/shared/x11/window_internal.h"
 #include "starboard/time.h"
 
+namespace {
+const char kTouchscreenPointerSwitch[] = "touchscreen_pointer";
+}
+
 namespace starboard {
 namespace shared {
 namespace x11 {
@@ -715,6 +719,7 @@
     // evdev input will be sent to the first created window only.
     dev_input_.reset(DevInput::Create(window, ConnectionNumber(display_)));
   }
+  touchscreen_pointer_ = GetCommandLine()->HasSwitch(kTouchscreenPointerSwitch);
   return window;
 }
 
@@ -1242,7 +1247,8 @@
       data->key = XButtonEventToSbKey(x_button_event);
       data->type =
           is_press_event ? kSbInputEventTypePress : kSbInputEventTypeUnpress;
-      data->device_type = kSbInputDeviceTypeMouse;
+      data->device_type = touchscreen_pointer_ ? kSbInputDeviceTypeTouchScreen
+                                               : kSbInputDeviceTypeMouse;
       if (is_wheel_event) {
         data->pressure = NAN;
         data->size = {NAN, NAN};
@@ -1268,11 +1274,17 @@
       data->size = {NAN, NAN};
       data->tilt = {NAN, NAN};
       data->type = kSbInputEventTypeMove;
-      data->device_type = kSbInputDeviceTypeMouse;
+      data->device_type = touchscreen_pointer_ ? kSbInputDeviceTypeTouchScreen
+                                               : kSbInputDeviceTypeMouse;
       data->device_id = kMouseDeviceId;
       data->key_modifiers = XEventStateToSbKeyModifiers(x_motion_event->state);
       data->position.x = x_motion_event->x;
       data->position.y = x_motion_event->y;
+      if (touchscreen_pointer_ && !data->key_modifiers) {
+        // For touch screens, only report motion events when a button is
+        // pressed.
+        return NULL;
+      }
       return new Event(kSbEventTypeInput, data.release(),
                        &DeleteDestructor<SbInputData>);
     }
diff --git a/src/starboard/shared/x11/application_x11.h b/src/starboard/shared/x11/application_x11.h
index e63cd91..6b1ade7 100644
--- a/src/starboard/shared/x11/application_x11.h
+++ b/src/starboard/shared/x11/application_x11.h
@@ -150,6 +150,9 @@
 
   // The /dev/input input handler. Only set when there is an open window.
   scoped_ptr<::starboard::shared::dev_input::DevInput> dev_input_;
+
+  // Indicates whether pointer input is from a touchscreen.
+  bool touchscreen_pointer_;
 };
 
 }  // namespace x11
diff --git a/src/starboard/stub/configuration.gni b/src/starboard/stub/configuration.gni
index 9babbcb..774ab8a 100644
--- a/src/starboard/stub/configuration.gni
+++ b/src/starboard/stub/configuration.gni
@@ -18,9 +18,3 @@
 # Use media source extension implementation that is conformed to the
 # Candidate Recommendation of July 5th 2016.
 cobalt_use_media_source_2016 = true
-
-declare_args() {
-  # Set to true to enable distributed compilation using Goma. By default we
-  # use Goma for stub and linux.
-  use_goma = true
-}
diff --git a/src/starboard/stub/gyp_configuration.py b/src/starboard/stub/gyp_configuration.py
index 2d97119..4f0285f 100644
--- a/src/starboard/stub/gyp_configuration.py
+++ b/src/starboard/stub/gyp_configuration.py
@@ -52,9 +52,8 @@
 
   def GetEnvironmentVariables(self):
     if not hasattr(self, 'host_compiler_environment'):
-      goma_supports_compiler = True
       self.host_compiler_environment = build.GetHostCompilerEnvironment(
-          clang_specification.GetClangSpecification(), goma_supports_compiler)
+          clang_specification.GetClangSpecification(), 'ccache')
 
     env_variables = self.host_compiler_environment
     env_variables.update({
diff --git a/src/starboard/tools/build.py b/src/starboard/tools/build.py
index 7a071e3..7652a4c 100644
--- a/src/starboard/tools/build.py
+++ b/src/starboard/tools/build.py
@@ -26,7 +26,6 @@
 from starboard.tools import config
 from starboard.tools import paths
 from starboard.tools import platform
-import starboard.tools.goma
 
 _STARBOARD_TOOLCHAINS_DIR_KEY = 'STARBOARD_TOOLCHAINS_DIR'
 _STARBOARD_TOOLCHAINS_DIR_NAME = 'starboard-toolchains'
@@ -190,27 +189,20 @@
   return _GetClangBasePath(clang_spec)
 
 
-def GetHostCompilerEnvironment(clang_spec, goma_supports_compiler):
+def GetHostCompilerEnvironment(clang_spec, build_accelerator):
   """Return the host compiler toolchain environment."""
-
   toolchain_dir = EnsureClangAvailable(clang_spec)
   toolchain_bin_dir = os.path.join(toolchain_dir, 'bin')
 
   cc_clang = os.path.join(toolchain_bin_dir, 'clang')
   cxx_clang = os.path.join(toolchain_bin_dir, 'clang++')
   host_clang_environment = {
-      'CC_host': cc_clang,
-      'CXX_host': cxx_clang,
+      'CC_host': build_accelerator + ' ' + cc_clang,
+      'CXX_host': build_accelerator + ' ' + cxx_clang,
       'LD_host': cxx_clang,
       'ARFLAGS_host': 'rcs',
       'ARTHINFLAGS_host': 'rcsT',
   }
-  # Check if goma is installed. Initialize if needed and use if possible.
-  if goma_supports_compiler and starboard.tools.goma.FindAndStartGoma():
-    host_clang_environment.update({
-        'CC_host': 'gomacc ' + cc_clang,
-        'CXX_host': 'gomacc ' + cxx_clang,
-    })
   return host_clang_environment
 
 
diff --git a/src/starboard/tools/build_accelerator.py b/src/starboard/tools/build_accelerator.py
new file mode 100644
index 0000000..5c4a39f
--- /dev/null
+++ b/src/starboard/tools/build_accelerator.py
@@ -0,0 +1,34 @@
+#
+# Copyright 2020 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.
+#
+"""Defines abstract build accelerators."""
+
+import abc
+
+
+class BuildAccelerator(object):
+  """A base class for all build accelerators."""
+
+  __metaclass__ = abc.ABCMeta
+
+  @abc.abstractmethod
+  def GetName(self):
+    """Returns name of build accelerator to be called."""
+    pass
+
+  @abc.abstractmethod
+  def Use(self):
+    """Returns boolean of whether the build accelerator should be used."""
+    pass
diff --git a/src/starboard/tools/ccache.py b/src/starboard/tools/ccache.py
new file mode 100644
index 0000000..5f1928c
--- /dev/null
+++ b/src/starboard/tools/ccache.py
@@ -0,0 +1,57 @@
+#
+# Copyright 2020 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.
+#
+"""Ccache specific definitions and helper functions.
+
+Override settings using environment variables.
+USE_CCACHE  To enable/disable ccache. Default is None.
+"""
+
+import logging
+import os
+import util
+
+from starboard.tools import build_accelerator
+
+
+class Ccache(build_accelerator.BuildAccelerator):
+  """Ccache is a cache based build accelerator."""
+
+  def GetName(self):
+    return 'ccache'
+
+  def Use(self):
+    return CcacheEnvOverride() and CcacheInstalled()
+
+
+def CcacheEnvOverride():
+  """Checks USE_CCACHE to enable/disable ccache.
+
+  Returns:
+    USE_CCACHE boolean if exists, defaults to True.
+  """
+  if 'USE_CCACHE' in os.environ:
+    return os.environ['USE_CCACHE'] == '1'
+  return True
+
+
+def CcacheInstalled():
+  """Returns True if ccache is installed, otherwise is False."""
+  ccache_path = util.Which('ccache')
+  if ccache_path is not None:
+    logging.info('Using ccache installed at: %s', ccache_path)
+    return True
+  logging.error('Unable to find ccache.')
+  return False
diff --git a/src/starboard/tools/goma.py b/src/starboard/tools/goma.py
deleted file mode 100644
index deec990..0000000
--- a/src/starboard/tools/goma.py
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# 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.
-#
-"""Goma specific definitions and helper functions.
-
-Goma is used for faster builds.  Install goma and add directory to PATH.
-Provides common functions to setup goma across platform builds.
-It checks that goma is enabled and installed, and starts the
-goma proxy.
-
-Override settings using environment variables.
-USE_GOMA  To enable/disable goma. Default is None.
-"""
-
-import logging
-import os
-import subprocess
-import sys
-import util
-
-
-def _GomaEnabledFromEnv():
-  """Enable goma if USE_GOMA is defined.
-
-  Returns:
-    True to enable goma, defaults to False.
-  """
-  if 'USE_GOMA' in os.environ:
-    return os.environ['USE_GOMA'] == '1'
-  return False
-
-
-def _GetGomaFromPath():
-  """Returns goma directory from PATH, otherwise is None."""
-  gomacc_path = util.Which('gomacc')
-  if gomacc_path is not None:
-    return os.path.dirname(gomacc_path)
-  return None
-
-
-def _GomaInstalled(goma_dir):
-  """Returns True if goma is installed, otherwise is False."""
-  if goma_dir and os.path.isdir(goma_dir):
-    if os.path.isfile(GomaControlFile(goma_dir)):
-      logging.error('Using Goma installed at: %s', goma_dir)
-      return True
-  logging.error('Failed to find goma dir. Check PATH location: %s', goma_dir)
-  return False
-
-
-def GomaControlFile(goma_dir):
-  """Returns path to goma control script."""
-  return os.path.join(goma_dir, 'goma_ctl.py')
-
-
-def _GomaEnsureStart(goma_dir):
-  """Starts the goma proxy.
-
-  Checks the proxy status and tries to start if not running.
-
-  Args:
-    goma_dir: goma install directory
-
-  Returns:
-    True if goma started, otherwise False.
-  """
-  if not _GomaInstalled(goma_dir):
-    return False
-
-  goma_ctl = GomaControlFile(goma_dir)
-  command = [goma_ctl, 'ensure_start']
-  logging.error('starting goma proxy...')
-
-  try:
-    subprocess.check_call(command)
-    return True
-  except subprocess.CalledProcessError as e:
-    logging.error('Goma proxy failed to start.\nCommand: %s\n%s',
-                  ' '.join(e.cmd), e.output)
-    return False
-
-
-def FindAndStartGoma(enable_in_path=True, exit_on_failed_start=False):
-  """Uses goma if installed and proxy is running.
-
-  Args:
-    enable_in_path: If True, enable goma if found in PATH. Otherwise,
-                    it enables it when USE_GOMA=1 is set.
-    exit_on_failed_start: Boolean to exit if goma is enabled, but wasn't
-                            started.
-  Returns:
-    True if goma is enabled and running, otherwise False.
-  """
-  if enable_in_path or _GomaEnabledFromEnv():
-    goma_dir = _GetGomaFromPath()
-    if _GomaEnsureStart(goma_dir):
-      return True
-    else:
-      logging.critical('goma was enabled, but failed to start.')
-      if exit_on_failed_start:
-        sys.exit(1)
-      return False
-  else:
-    logging.info('Goma is disabled. To enable, check for gomacc in PATH '
-                 'and/or set USE_GOMA=1.')
-    return False
diff --git a/src/starboard/tools/toolchain/evergreen_linker.py b/src/starboard/tools/toolchain/evergreen_linker.py
index a7bf4d7..04fdc19 100644
--- a/src/starboard/tools/toolchain/evergreen_linker.py
+++ b/src/starboard/tools/toolchain/evergreen_linker.py
@@ -28,6 +28,7 @@
 
     return shell.And('{0} '
                      '--build-id '
+                     '-gc-sections '
                      '-X '
                      '-v '
                      '--eh-frame-hdr '
diff --git a/src/third_party/aom_includes/METATDATA b/src/third_party/aom_includes/METADATA
similarity index 100%
rename from src/third_party/aom_includes/METATDATA
rename to src/third_party/aom_includes/METADATA
diff --git a/src/third_party/crashpad/client/crash_report_database.h b/src/third_party/crashpad/client/crash_report_database.h
index cc4b8df..0739a9c 100644
--- a/src/third_party/crashpad/client/crash_report_database.h
+++ b/src/third_party/crashpad/client/crash_report_database.h
@@ -404,6 +404,15 @@
   //! \return The number of reports cleaned.
   virtual int CleanDatabase(time_t lockfile_ttl) { return 0; }
 
+  //! \brief Deletes the oldest crash reports and their associated metadata,
+  //!     leaving only num_reports_to_keep left in the database.
+  //!
+  //! \param[in] num_reports_to_keep To number of most recent reports to leave
+  //!     in the database.
+  //!
+  //! \return The operation status code.
+  virtual OperationStatus RemoveOldReports(int num_reports_to_keep) = 0;
+
  protected:
   CrashReportDatabase() {}
 
diff --git a/src/third_party/crashpad/client/crash_report_database_generic.cc b/src/third_party/crashpad/client/crash_report_database_generic.cc
index e90bfa6..e807f17 100644
--- a/src/third_party/crashpad/client/crash_report_database_generic.cc
+++ b/src/third_party/crashpad/client/crash_report_database_generic.cc
@@ -18,7 +18,9 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <algorithm>
 #include <utility>
+#include <vector>
 
 #include "base/logging.h"
 #include "build/build_config.h"
@@ -191,6 +193,11 @@
   }
 }
 
+bool WasCreatedSooner(CrashReportDatabase::Report a,
+                      CrashReportDatabase::Report b) {
+  return a.creation_time < b.creation_time;
+}
+
 }  // namespace
 
 class CrashReportDatabaseGeneric : public CrashReportDatabase {
@@ -218,6 +225,7 @@
   OperationStatus DeleteReport(const UUID& uuid) override;
   OperationStatus RequestUpload(const UUID& uuid) override;
   int CleanDatabase(time_t lockfile_ttl) override;
+  OperationStatus RemoveOldReports(int num_reports_to_keep) override;
 
   // Build a filepath for the directory for the report to hold attachments.
   base::FilePath AttachmentsPath(const UUID& uuid);
@@ -640,6 +648,32 @@
   return removed;
 }
 
+OperationStatus CrashReportDatabaseGeneric::RemoveOldReports(
+    int num_reports_to_keep) {
+  std::vector<CrashReportDatabase::Report> pending_reports;
+  std::vector<CrashReportDatabase::Report> completed_reports;
+  std::vector<CrashReportDatabase::Report> all_reports;
+
+  GetPendingReports(&pending_reports);
+  GetCompletedReports(&completed_reports);
+
+  all_reports.insert(
+      all_reports.end(), pending_reports.begin(), pending_reports.end());
+  all_reports.insert(
+      all_reports.end(), completed_reports.begin(), completed_reports.end());
+  std::sort(all_reports.begin(), all_reports.end(), WasCreatedSooner);
+
+  while (all_reports.size() > num_reports_to_keep) {
+    OperationStatus os = DeleteReport((*all_reports.begin()).uuid);
+    if (os != kNoError) {
+      return os;
+    }
+    all_reports.erase(all_reports.begin());
+  }
+
+  return kNoError;
+}
+
 OperationStatus CrashReportDatabaseGeneric::RecordUploadAttempt(
     UploadReport* report,
     bool successful,
diff --git a/src/third_party/crashpad/client/crashpad_client.h b/src/third_party/crashpad/client/crashpad_client.h
index e381b41..147b974 100644
--- a/src/third_party/crashpad/client/crashpad_client.h
+++ b/src/third_party/crashpad/client/crashpad_client.h
@@ -388,6 +388,15 @@
   //!
   //! \return `true` on success, `false` on failure with a message logged.
   static bool SendEvergreenInfoToHandler(EvergreenInfo evergreen_info);
+
+  //! \brief Sends mapping info to the handler
+  //!
+  //! A handler must have already been installed before calling this method.
+  //! \param[in] annotations A EvergreenAnnotations struct, whose information
+  //!     was created on Evergreen startup.
+  //!
+  //! \return `true` on success, `false` on failure with a message logged.
+  static bool SendAnnotationsToHandler(EvergreenAnnotations annotations);
 #endif
 
   //! \brief Requests that the handler capture a dump even though there hasn't
diff --git a/src/third_party/crashpad/client/crashpad_client_linux.cc b/src/third_party/crashpad/client/crashpad_client_linux.cc
index 02a4e57..2435153 100644
--- a/src/third_party/crashpad/client/crashpad_client_linux.cc
+++ b/src/third_party/crashpad/client/crashpad_client_linux.cc
@@ -141,6 +141,11 @@
     evergreen_info_ = evergreen_info;
     return SendEvergreenInfoImpl();
   }
+
+  bool SendAnnotations(EvergreenAnnotations annotations) {
+    annotations_ = annotations;
+    return SendAnnotationsImpl();
+  }
 #endif
 
   // The base implementation for all signal handlers, suitable for calling
@@ -181,6 +186,7 @@
 
 #if defined(STARBOARD)
   const EvergreenInfo& GetEvergreenInfo() { return evergreen_info_; }
+  const EvergreenAnnotations& GetAnnotations() { return annotations_; }
 #endif
 
   const ExceptionInformation& GetExceptionInfo() {
@@ -189,6 +195,7 @@
 
 #if defined(STARBOARD)
   virtual bool SendEvergreenInfoImpl() = 0;
+  virtual bool SendAnnotationsImpl() = 0;
 #endif
 
   virtual void HandleCrashImpl() = 0;
@@ -211,6 +218,7 @@
 
 #if defined(STARBOARD)
   EvergreenInfo evergreen_info_;
+  EvergreenAnnotations annotations_;
 #endif
 
   static SignalHandler* handler_;
@@ -250,6 +258,7 @@
 
 #if defined(STARBOARD)
   bool SendEvergreenInfoImpl() override { return false; }
+  bool SendAnnotationsImpl() override { return false; }
 #endif
 
   void HandleCrashImpl() override {
@@ -353,6 +362,14 @@
     client.SendEvergreenInfo(info);
     return true;
   }
+
+  bool SendAnnotationsImpl() override {
+    ExceptionHandlerClient client(sock_to_handler_.get(), true);
+    ExceptionHandlerProtocol::ClientInformation info = {};
+    info.annotations_address = FromPointerCast<VMAddress>(&GetAnnotations());
+    client.SendAnnotations(info);
+    return true;
+  }
 #endif
 
   void HandleCrashImpl() override {
@@ -576,6 +593,16 @@
 
   return SignalHandler::Get()->SendEvergreenInfo(evergreen_info);
 }
+
+bool CrashpadClient::SendAnnotationsToHandler(
+    EvergreenAnnotations annotations) {
+  if (!SignalHandler::Get()) {
+    DLOG(ERROR) << "Crashpad isn't enabled";
+    return false;
+  }
+
+  return SignalHandler::Get()->SendAnnotations(annotations);
+}
 #endif
 
 // static
diff --git a/src/third_party/crashpad/handler/crash_report_upload_thread.cc b/src/third_party/crashpad/handler/crash_report_upload_thread.cc
index ac0f7c1..db960b1 100644
--- a/src/third_party/crashpad/handler/crash_report_upload_thread.cc
+++ b/src/third_party/crashpad/handler/crash_report_upload_thread.cc
@@ -242,7 +242,7 @@
       break;
   }
 #if defined(STARBOARD)
-  database_->DeleteReport(report.uuid);
+  database_->RemoveOldReports(/*num_reports_to_save=*/2);
 #endif
 }
 
diff --git a/src/third_party/crashpad/handler/linux/capture_snapshot.cc b/src/third_party/crashpad/handler/linux/capture_snapshot.cc
index d792945..488bbba 100644
--- a/src/third_party/crashpad/handler/linux/capture_snapshot.cc
+++ b/src/third_party/crashpad/handler/linux/capture_snapshot.cc
@@ -34,14 +34,15 @@
     std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot
 #if defined(STARBOARD)
     ,
-    VMAddress evergreen_information_address
+    VMAddress evergreen_information_address,
+    VMAddress annotations_address
 #endif
     ) {
   std::unique_ptr<ProcessSnapshotLinux> process_snapshot(
       new ProcessSnapshotLinux());
 #if defined(STARBOARD)
-  if (!process_snapshot->Initialize(connection,
-                                    evergreen_information_address)) {
+  if (!process_snapshot->Initialize(
+          connection, evergreen_information_address, annotations_address)) {
 #else
   if (!process_snapshot->Initialize(connection)) {
 #endif
diff --git a/src/third_party/crashpad/handler/linux/capture_snapshot.h b/src/third_party/crashpad/handler/linux/capture_snapshot.h
index d191b8b..51eca35 100644
--- a/src/third_party/crashpad/handler/linux/capture_snapshot.h
+++ b/src/third_party/crashpad/handler/linux/capture_snapshot.h
@@ -67,7 +67,8 @@
     std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot
 #if defined(STARBOARD)
     ,
-    VMAddress evergreen_information_address
+    VMAddress evergreen_information_address,
+    VMAddress annotations_address
 #endif
     );
 
diff --git a/src/third_party/crashpad/handler/linux/crash_report_exception_handler.cc