merge mozilla-inbound to mozilla-central a=merge FIREFOX_AURORA_46_BASE
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 25 Jan 2016 11:50:09 +0100
changeset 281478 67c66c2878aed17ae3096d7db483ddbb2293c503
parent 281442 ba9f38e1af50dbca0a1f8df450cc355eab143c70 (current diff)
parent 281477 a0254596dc6693a6b7e931d14a53a53a2d306af1 (diff)
child 281479 a2a6201322ad1cd09202b53b9c2cbf40b26026ee
child 281483 5ae2ccf49f97e518715e8dfbbbab4dd9eda5348c
child 281497 c39242d264c8f3ccfe1039a5cce3ec519ef7b638
child 287457 671e1d2fe00379370df4f14f1fb7396f8eae2877
push idunknown
push userunknown
push dateunknown
reviewersmerge
milestone46.0a1
merge mozilla-inbound to mozilla-central a=merge
gfx/layers/apz/test/gtest/TestOverscrollHandoff.cpp
--- a/configure.in
+++ b/configure.in
@@ -1687,25 +1687,16 @@ dnl ====================================
 MOZ_ARG_ENABLE_BOOL(systrace,
 [  --enable-systrace       Set compile flags necessary for using sampling profilers (e.g. shark, perf)],
     MOZ_USE_SYSTRACE=1,
     MOZ_USE_SYSTRACE= )
 if test -n "$MOZ_USE_SYSTRACE"; then
     AC_DEFINE(MOZ_USE_SYSTRACE)
 fi
 
-# For profiling builds keep the symbol information
-if test "$MOZ_PROFILING" -a -z "$STRIP_FLAGS"; then
-    case "$OS_TARGET" in
-    Linux|DragonFly|FreeBSD|NetBSD|OpenBSD)
-        STRIP_FLAGS="--strip-debug"
-        ;;
-    esac
-fi
-
 dnl ========================================================
 dnl = Use Valgrind
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(valgrind,
 [  --enable-valgrind       Enable Valgrind integration hooks (default=no)],
     MOZ_VALGRIND=1,
     MOZ_VALGRIND= )
 if test -n "$MOZ_VALGRIND"; then
@@ -1794,16 +1785,48 @@ MOZ_ARG_ENABLE_BOOL(vtune,
 [  --enable-vtune          Enable vtune profiling. Implies --enable-profiling.],
     MOZ_VTUNE=1,
     MOZ_VTUNE= )
 if test -n "$MOZ_VTUNE"; then
     MOZ_PROFILING=1
     AC_DEFINE(MOZ_VTUNE)
 fi
 
+# For profiling builds keep the symbol information
+if test "$MOZ_PROFILING" -a -z "$STRIP_FLAGS"; then
+    case "$OS_TARGET" in
+    Linux|DragonFly|FreeBSD|NetBSD|OpenBSD)
+        STRIP_FLAGS="--strip-debug"
+        ;;
+    esac
+fi
+
+dnl ========================================================
+dnl = Enable DMD
+dnl ========================================================
+
+MOZ_ARG_ENABLE_BOOL(dmd,
+[  --enable-dmd            Enable DMD; also enables jemalloc, replace-malloc and profiling],
+    MOZ_DMD=1,
+    MOZ_DMD= )
+
+if test "$MOZ_DMD"; then
+    AC_DEFINE(MOZ_DMD)
+
+    if test "${CPU_ARCH}" = "arm"; then
+        CFLAGS="$CFLAGS -funwind-tables"
+        CXXFLAGS="$CXXFLAGS -funwind-tables"
+    fi
+
+    MOZ_MEMORY=1                        # DMD enables jemalloc
+    MOZ_REPLACE_MALLOC=1                # DMD enables replace-malloc
+    MOZ_PROFILING=1                     # DMD enables profiling
+fi
+AC_SUBST(MOZ_DMD)
+
 dnl ========================================================
 dnl Profiling
 dnl ========================================================
 if test -n "$MOZ_PROFILING"; then
     AC_DEFINE(MOZ_PROFILING)
 fi
 
 dnl ========================================================
@@ -7058,38 +7081,16 @@ if test -n "$MOZ_DUMP_PAINTING"; then
     AC_DEFINE(MOZ_DUMP_PAINTING)
     AC_DEFINE(MOZ_LAYERS_HAVE_LOG)
 fi
 if test -n "$MOZ_DEBUG"; then
     AC_DEFINE(MOZ_DUMP_PAINTING)
 fi
 
 dnl ========================================================
-dnl = Enable DMD
-dnl ========================================================
-
-MOZ_ARG_ENABLE_BOOL(dmd,
-[  --enable-dmd            Enable DMD; also enables jemalloc and replace-malloc],
-    MOZ_DMD=1,
-    MOZ_DMD= )
-
-if test "$MOZ_DMD"; then
-    AC_DEFINE(MOZ_DMD)
-
-    if test "${CPU_ARCH}" = "arm"; then
-        CFLAGS="$CFLAGS -funwind-tables"
-        CXXFLAGS="$CXXFLAGS -funwind-tables"
-    fi
-
-    MOZ_MEMORY=1                        # DMD enables jemalloc
-    MOZ_REPLACE_MALLOC=1                # DMD enables replace-malloc
-fi
-AC_SUBST(MOZ_DMD)
-
-dnl ========================================================
 dnl = Enable jemalloc
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(jemalloc,
 [  --enable-jemalloc       Replace memory allocator with jemalloc],
     MOZ_MEMORY=1,
     MOZ_MEMORY=)
 
 case "${OS_TARGET}" in
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -5875,17 +5875,16 @@ private:
       : mTask(aTask) { }
 
   private:
     ~Observer() {}
 
     RefPtr<FullscreenTransitionTask> mTask;
   };
 
-  static const uint32_t kNextPaintTimeout = 1000; // ms
   static const char* const kPaintedTopic;
 
   RefPtr<nsGlobalWindow> mWindow;
   nsCOMPtr<nsIWidget> mWidget;
   nsCOMPtr<nsIScreen> mScreen;
   nsCOMPtr<nsITimer> mTimer;
   nsCOMPtr<nsISupports> mTransitionData;
 
@@ -5935,18 +5934,24 @@ FullscreenTransitionTask::Run()
     obs->AddObserver(observer, kPaintedTopic, false);
     // There are several edge cases where we may never get the paint
     // notification, including:
     // 1. the window/tab is closed before the next paint;
     // 2. the user has switched to another tab before we get here.
     // Completely fixing those cases seems to be tricky, and since they
     // should rarely happen, it probably isn't worth to fix. Hence we
     // simply add a timeout here to ensure we never hang forever.
+    // In addition, if the page is complicated or the machine is less
+    // powerful, layout could take a long time, in which case, staying
+    // in black screen for that long could hurt user experience even
+    // more than exposing an intermediate state.
     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-    mTimer->Init(observer, kNextPaintTimeout, nsITimer::TYPE_ONE_SHOT);
+    uint32_t timeout =
+      Preferences::GetUint("full-screen-api.transition.timeout", 500);
+    mTimer->Init(observer, timeout, nsITimer::TYPE_ONE_SHOT);
   } else if (stage == eAfterToggle) {
     mWidget->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle,
                                          mDuration.mFadeOut, mTransitionData,
                                          this);
   }
   return NS_OK;
 }
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -537,17 +537,52 @@ WebGLContext::FramebufferTexture2D(GLenu
     if (!ValidateFramebufferTarget(target, "framebufferTexture2D"))
         return;
 
     if (level < 0) {
         ErrorInvalidValue("framebufferTexture2D: level must not be negative.");
         return;
     }
 
-    if (!IsWebGL2() && level != 0) {
+    if (textarget != LOCAL_GL_TEXTURE_2D &&
+        (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
+         textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
+    {
+        return ErrorInvalidEnumInfo("framebufferTexture2D: textarget:",
+                                    textarget);
+    }
+
+    if (IsWebGL2()) {
+        /* GLES 3.0.4 p208:
+         *   If textarget is one of TEXTURE_CUBE_MAP_POSITIVE_X,
+         *   TEXTURE_CUBE_MAP_POSITIVE_Y, TEXTURE_CUBE_MAP_POSITIVE_Z,
+         *   TEXTURE_CUBE_MAP_NEGATIVE_X, TEXTURE_CUBE_MAP_NEGATIVE_Y,
+         *   or TEXTURE_CUBE_MAP_NEGATIVE_Z, then level must be greater
+         *   than or equal to zero and less than or equal to log2 of the
+         *   value of MAX_CUBE_MAP_TEXTURE_SIZE. If textarget is TEXTURE_2D,
+         *   level must be greater than or equal to zero and no larger than
+         *   log2 of the value of MAX_TEXTURE_SIZE. Otherwise, an
+         *   INVALID_VALUE error is generated.
+         */
+
+        if (textarget == LOCAL_GL_TEXTURE_2D) {
+            if (uint32_t(level) > FloorLog2(mImplMaxTextureSize)) {
+                ErrorInvalidValue("framebufferTexture2D: level is too large.");
+                return;
+            }
+        } else {
+            MOZ_ASSERT(textarget >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
+                       textarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
+
+            if (uint32_t(level) > FloorLog2(mImplMaxCubeMapTextureSize)) {
+                ErrorInvalidValue("framebufferTexture2D: level is too large.");
+                return;
+            }
+        }
+    } else if (level != 0) {
         ErrorInvalidValue("framebufferTexture2D: level must be 0.");
         return;
     }
 
     WebGLFramebuffer* fb;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
@@ -562,24 +597,16 @@ WebGLContext::FramebufferTexture2D(GLenu
         MOZ_CRASH("Bad target.");
     }
 
     if (!fb) {
         return ErrorInvalidOperation("framebufferTexture2D: cannot modify"
                                      " framebuffer 0.");
     }
 
-    if (textarget != LOCAL_GL_TEXTURE_2D &&
-        (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
-         textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
-    {
-        return ErrorInvalidEnumInfo("framebufferTexture2D: textarget:",
-                                    textarget);
-    }
-
     if (!ValidateFramebufferAttachment(fb, attachment, "framebufferTexture2D"))
         return;
 
     fb->FramebufferTexture2D(attachment, textarget, tobj, level);
 }
 
 void
 WebGLContext::FrontFace(GLenum mode)
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1152,16 +1152,18 @@ WebGLFramebuffer::GetAttachmentParameter
                                             " have different objects bound.",
                                             funcName);
             return JS::NullValue();
         }
 
         attachPoint = GetAttachPoint(LOCAL_GL_DEPTH_ATTACHMENT);
     }
 
+    FinalizeAttachments();
+
     return attachPoint->GetParameter(funcName, mContext, cx, target, attachment, pname,
                                      out_error);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Goop.
 
 JSObject*
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -732,16 +732,17 @@ void HTMLMediaElement::AbortExistingLoad
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING ||
       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE)
   {
     DispatchAsyncEvent(NS_LITERAL_STRING("abort"));
   }
 
   mError = nullptr;
+  mCurrentPlayRangeStart = -1.0;
   mLoadedDataFired = false;
   mAutoplaying = true;
   mIsLoadingFromSourceChildren = false;
   mSuspendedAfterFirstFrame = false;
   mAllowSuspendAfterFirstFrame = true;
   mHaveQueuedSelectResource = false;
   mSuspendedForPreloadNone = false;
   mDownloadSuspendedByCache = false;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -91,18 +91,17 @@ MediaFormatReader::Shutdown()
   mSkipRequest.DisconnectIfExists();
 
   if (mAudio.mDecoder) {
     Flush(TrackInfo::kAudioTrack);
     if (mAudio.HasPromise()) {
       mAudio.RejectPromise(CANCELED, __func__);
     }
     mAudio.mInitPromise.DisconnectIfExists();
-    mAudio.mDecoder->Shutdown();
-    mAudio.mDecoder = nullptr;
+    mAudio.ShutdownDecoder();
   }
   if (mAudio.mTrackDemuxer) {
     mAudio.ResetDemuxer();
     mAudio.mTrackDemuxer->BreakCycles();
     mAudio.mTrackDemuxer = nullptr;
   }
   if (mAudio.mTaskQueue) {
     mAudio.mTaskQueue->BeginShutdown();
@@ -112,18 +111,17 @@ MediaFormatReader::Shutdown()
   MOZ_ASSERT(mAudio.mPromise.IsEmpty());
 
   if (mVideo.mDecoder) {
     Flush(TrackInfo::kVideoTrack);
     if (mVideo.HasPromise()) {
       mVideo.RejectPromise(CANCELED, __func__);
     }
     mVideo.mInitPromise.DisconnectIfExists();
-    mVideo.mDecoder->Shutdown();
-    mVideo.mDecoder = nullptr;
+    mVideo.ShutdownDecoder();
   }
   if (mVideo.mTrackDemuxer) {
     mVideo.ResetDemuxer();
     mVideo.mTrackDemuxer->BreakCycles();
     mVideo.mTrackDemuxer = nullptr;
   }
   if (mVideo.mTaskQueue) {
     mVideo.mTaskQueue->BeginShutdown();
@@ -376,16 +374,18 @@ MediaFormatReader::EnsureDecoderCreated(
       // EME not supported.
       return false;
 #endif
     }
   }
 
   decoder.mDecoderInitialized = false;
 
+  MonitorAutoLock mon(decoder.mMonitor);
+
   switch (aTrack) {
     case TrackType::kAudioTrack:
       decoder.mDecoder =
         mPlatform->CreateDecoder(decoder.mInfo ?
                                    *decoder.mInfo->GetAsAudioInfo() :
                                    mInfo.mAudio,
                                  decoder.mTaskQueue,
                                  decoder.mCallback);
@@ -401,16 +401,21 @@ MediaFormatReader::EnsureDecoderCreated(
                                  decoder.mCallback,
                                  mHardwareAccelerationDisabled ? LayersBackend::LAYERS_NONE :
                                  mLayersBackendType,
                                  GetImageContainer());
       break;
     default:
       break;
   }
+  if (decoder.mDecoder ) {
+    decoder.mDescription = decoder.mDecoder->GetDescriptionName();
+  } else {
+    decoder.mDescription = "error creating decoder";
+  }
   return decoder.mDecoder != nullptr;
 }
 
 bool
 MediaFormatReader::EnsureDecoderInitialized(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
@@ -424,23 +429,24 @@ MediaFormatReader::EnsureDecoderInitiali
   }
   RefPtr<MediaFormatReader> self = this;
   decoder.mInitPromise.Begin(decoder.mDecoder->Init()
        ->Then(OwnerThread(), __func__,
               [self] (TrackType aTrack) {
                 auto& decoder = self->GetDecoderData(aTrack);
                 decoder.mInitPromise.Complete();
                 decoder.mDecoderInitialized = true;
+                MonitorAutoLock mon(decoder.mMonitor);
+                decoder.mDescription = decoder.mDecoder->GetDescriptionName();
                 self->ScheduleUpdate(aTrack);
               },
               [self, aTrack] (MediaDataDecoder::DecoderFailureReason aResult) {
                 auto& decoder = self->GetDecoderData(aTrack);
                 decoder.mInitPromise.Complete();
-                decoder.mDecoder->Shutdown();
-                decoder.mDecoder = nullptr;
+                decoder.ShutdownDecoder();
                 self->NotifyError(aTrack);
               }));
   return false;
 }
 
 void
 MediaFormatReader::ReadUpdatedMetadata(MediaInfo* aInfo)
 {
@@ -460,18 +466,17 @@ MediaFormatReader::GetDecoderData(TrackT
 
 void
 MediaFormatReader::DisableHardwareAcceleration()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (HasVideo() && !mHardwareAccelerationDisabled) {
     mHardwareAccelerationDisabled = true;
     Flush(TrackInfo::kVideoTrack);
-    mVideo.mDecoder->Shutdown();
-    mVideo.mDecoder = nullptr;
+    mVideo.ShutdownDecoder();
     if (!EnsureDecoderCreated(TrackType::kVideoTrack)) {
       LOG("Unable to re-create decoder, aborting");
       NotifyError(TrackInfo::kVideoTrack);
       return;
     }
     ScheduleUpdate(TrackInfo::kVideoTrack);
   }
 }
@@ -914,18 +919,17 @@ MediaFormatReader::HandleDemuxedSamples(
           TrackTypeToStr(aTrack), decoder.mLastStreamSourceID,
           info->GetID());
       decoder.mInfo = info;
       decoder.mLastStreamSourceID = info->GetID();
       decoder.mNextStreamSourceID.reset();
       // Flush will clear our array of queued samples. So make a copy now.
       nsTArray<RefPtr<MediaRawData>> samples{decoder.mQueuedSamples};
       Flush(aTrack);
-      decoder.mDecoder->Shutdown();
-      decoder.mDecoder = nullptr;
+      decoder.ShutdownDecoder();
       if (sample->mKeyframe) {
         decoder.mQueuedSamples.AppendElements(Move(samples));
         NotifyDecodingRequested(aTrack);
       } else {
         SeekTarget seekTarget =
           decoder.mTimeThreshold.refOr(SeekTarget(TimeUnit::FromMicroseconds(sample->mTime), false));
         LOG("Stream change occurred on a non-keyframe. Seeking to:%lld",
             seekTarget.mTime.ToMicroseconds());
@@ -1599,21 +1603,18 @@ MediaFormatReader::GetBuffered()
 
 void MediaFormatReader::ReleaseMediaResources()
 {
   // Before freeing a video codec, all video buffers needed to be released
   // even from graphics pipeline.
   if (mVideoFrameContainer) {
     mVideoFrameContainer->ClearCurrentFrame();
   }
-  if (mVideo.mDecoder) {
-    mVideo.mInitPromise.DisconnectIfExists();
-    mVideo.mDecoder->Shutdown();
-    mVideo.mDecoder = nullptr;
-  }
+  mVideo.mInitPromise.DisconnectIfExists();
+  mVideo.ShutdownDecoder();
 }
 
 bool
 MediaFormatReader::VideoIsHardwareAccelerated() const
 {
   return mVideo.mIsHardwareAccelerated;
 }
 
@@ -1661,20 +1662,33 @@ MediaFormatReader::GetImageContainer()
   return mVideoFrameContainer
     ? mVideoFrameContainer->GetImageContainer() : nullptr;
 }
 
 void
 MediaFormatReader::GetMozDebugReaderData(nsAString& aString)
 {
   nsAutoCString result;
+  const char* audioName = "unavailable";
+  const char* videoName = audioName;
+
+  if (HasAudio()) {
+    MonitorAutoLock mon(mAudio.mMonitor);
+    audioName = mAudio.mDescription;
+  }
+  if (HasVideo()) {
+    MonitorAutoLock mon(mVideo.mMonitor);
+    videoName = mVideo.mDescription;
+  }
+
+  result += nsPrintfCString("audio decoder: %s\n", audioName);
+  result += nsPrintfCString("audio frames decoded: %lld\n",
+                            mAudio.mNumSamplesOutputTotal);
+  result += nsPrintfCString("video decoder: %s\n", videoName);
   result += nsPrintfCString("hardware video decoding: %s\n",
                             VideoIsHardwareAccelerated() ? "enabled" : "disabled");
-  result += nsPrintfCString("audio frames decoded: %lld (skipped:%lld)\n"
-                            "video frames decoded: %lld (skipped:%lld)\n",
-                            mAudio.mNumSamplesOutputTotal,
-                            mAudio.mNumSamplesSkippedTotal,
+  result += nsPrintfCString("video frames decoded: %lld (skipped:%lld)\n",
                             mVideo.mNumSamplesOutputTotal,
                             mVideo.mNumSamplesSkippedTotal);
   aString += NS_ConvertUTF8toUTF16(result);
 }
 
 } // namespace mozilla
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(MediaFormatReader_h_)
 #define MediaFormatReader_h_
 
 #include "mozilla/Atomics.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/TaskQueue.h"
+#include "mozilla/Monitor.h"
 
 #include "MediaDataDemuxer.h"
 #include "MediaDecoderReader.h"
 #include "PDMFactory.h"
 
 namespace mozilla {
 
 class CDMProxy;
@@ -208,16 +209,18 @@ private:
   };
 
   struct DecoderData {
     DecoderData(MediaFormatReader* aOwner,
                 MediaData::Type aType,
                 uint32_t aDecodeAhead)
       : mOwner(aOwner)
       , mType(aType)
+      , mMonitor("DecoderData")
+      , mDescription("shutdown")
       , mDecodeAhead(aDecodeAhead)
       , mUpdateScheduled(false)
       , mDemuxEOS(false)
       , mWaitingForData(false)
       , mReceivedNewData(false)
       , mDiscontinuity(true)
       , mDecoderInitialized(false)
       , mDecodingRequested(false)
@@ -235,24 +238,37 @@ private:
       , mIsHardwareAccelerated(false)
       , mLastStreamSourceID(UINT32_MAX)
     {}
 
     MediaFormatReader* mOwner;
     // Disambiguate Audio vs Video.
     MediaData::Type mType;
     RefPtr<MediaTrackDemuxer> mTrackDemuxer;
-    // The platform decoder.
-    RefPtr<MediaDataDecoder> mDecoder;
     // TaskQueue on which decoder can choose to decode.
     // Only non-null up until the decoder is created.
     RefPtr<FlushableTaskQueue> mTaskQueue;
     // Callback that receives output and error notifications from the decoder.
     nsAutoPtr<DecoderCallback> mCallback;
 
+    // Monitor protecting mDescription and mDecoder.
+    Monitor mMonitor;
+    // The platform decoder.
+    RefPtr<MediaDataDecoder> mDecoder;
+    const char* mDescription;
+    void ShutdownDecoder()
+    {
+      MonitorAutoLock mon(mMonitor);
+      if (mDecoder) {
+        mDecoder->Shutdown();
+      }
+      mDescription = "shutdown";
+      mDecoder = nullptr;
+    }
+
     // Only accessed from reader's task queue.
     uint32_t mDecodeAhead;
     bool mUpdateScheduled;
     bool mDemuxEOS;
     bool mWaitingForData;
     bool mReceivedNewData;
     bool mDiscontinuity;
 
--- a/dom/media/mediasink/DecodedAudioDataSink.cpp
+++ b/dom/media/mediasink/DecodedAudioDataSink.cpp
@@ -197,22 +197,28 @@ DecodedAudioDataSink::PopFrames(uint32_t
     AudioDataValue* GetWritable() const { return mData.get(); }
   private:
     const uint32_t mFrames;
     const uint32_t mChannels;
     const uint32_t mRate;
     UniquePtr<AudioDataValue[]> mData;
   };
 
-  if (!mCurrentData) {
+  while (!mCurrentData) {
     // No data in the queue. Return an empty chunk.
     if (AudioQueue().GetSize() == 0) {
       return MakeUnique<Chunk>();
     }
 
+    // Ignore the element with 0 frames and try next.
+    if (AudioQueue().PeekFront()->mFrames == 0) {
+      RefPtr<MediaData> releaseMe = AudioQueue().PopFront();
+      continue;
+    }
+
     // See if there's a gap in the audio. If there is, push silence into the
     // audio hardware, so we can play across the gap.
     // Calculate the timestamp of the next chunk of audio in numbers of
     // samples.
     CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate);
     // Calculate the number of frames that have been pushed onto the audio hardware.
     CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) +
                                 static_cast<int64_t>(mWritten);
@@ -234,16 +240,17 @@ DecodedAudioDataSink::PopFrames(uint32_t
       mWritten += framesToPop;
       return MakeUnique<SilentChunk>(framesToPop, mInfo.mChannels, mInfo.mRate);
     }
 
     mCurrentData = dont_AddRef(AudioQueue().PopFront().take()->As<AudioData>());
     mCursor = MakeUnique<AudioBufferCursor>(mCurrentData->mAudioData.get(),
                                             mCurrentData->mChannels,
                                             mCurrentData->mFrames);
+    MOZ_ASSERT(mCurrentData->mFrames > 0);
   }
 
   auto framesToPop = std::min(aFrames, mCursor->Available());
 
   SINK_LOG_V("playing audio at time=%lld offset=%u length=%u",
              mCurrentData->mTime, mCurrentData->mFrames - mCursor->Available(), framesToPop);
 
   UniquePtr<AudioStream::Chunk> chunk =
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -215,13 +215,18 @@ public:
   // If video decoder, aConfig will be a VideoInfo object.
   // If audio decoder, aConfig will be a AudioInfo object.
   // It is not safe to store a reference to this object and the decoder must
   // make a copy.
   virtual nsresult ConfigurationChanged(const TrackInfo& aConfig)
   {
     return NS_OK;
   }
+
+  // Return the name of the MediaDataDecoder, only used for decoding.
+  // Only return a static const string, as the information may be accessed
+  // in a non thread-safe fashion.
+  virtual const char* GetDescriptionName() const = 0;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp
@@ -88,16 +88,21 @@ public:
     return NS_OK;
   }
 
   nsresult Drain() override {
     mCallback->DrainComplete();
     return NS_OK;
   }
 
+  const char* GetDescriptionName() const override
+  {
+    return "blank media data decoder";
+  }
+
 private:
   nsAutoPtr<BlankMediaDataCreator> mCreator;
   RefPtr<FlushableTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
   TrackInfo::TrackType mType;
 };
 
 class BlankVideoDataCreator {
--- a/dom/media/platforms/agnostic/OpusDecoder.h
+++ b/dom/media/platforms/agnostic/OpusDecoder.h
@@ -22,16 +22,20 @@ public:
                   MediaDataDecoderCallback* aCallback);
   ~OpusDataDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
+  const char* GetDescriptionName() const override
+  {
+    return "opus audio decoder";
+  }
 
   // Return true if mimetype is Opus
   static bool IsOpus(const nsACString& aMimeType);
 
 private:
   nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
 
   void Decode (MediaRawData* aSample);
--- a/dom/media/platforms/agnostic/VPXDecoder.h
+++ b/dom/media/platforms/agnostic/VPXDecoder.h
@@ -28,16 +28,20 @@ public:
 
   ~VPXDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
+  const char* GetDescriptionName() const override
+  {
+    return "libvpx video decoder";
+  }
 
   // Return true if mimetype is a VPX codec
   static bool IsVPX(const nsACString& aMimeType);
 
   enum Codec {
     VP8,
     VP9
   };
--- a/dom/media/platforms/agnostic/VorbisDecoder.h
+++ b/dom/media/platforms/agnostic/VorbisDecoder.h
@@ -25,16 +25,20 @@ public:
                 MediaDataDecoderCallback* aCallback);
   ~VorbisDataDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
+  const char* GetDescriptionName() const override
+  {
+    return "vorbis audio decoder";
+  }
 
   // Return true if mimetype is Vorbis
   static bool IsVorbis(const nsACString& aMimeType);
 
 private:
   nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
 
   void Decode (MediaRawData* aSample);
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
@@ -132,16 +132,20 @@ public:
     mSamplesWaitingForKey->BreakCycles();
     mSamplesWaitingForKey = nullptr;
     mDecoder = nullptr;
     mProxy = nullptr;
     mCallback = nullptr;
     return rv;
   }
 
+  const char* GetDescriptionName() const override {
+    return mDecoder->GetDescriptionName();
+  }
+
 private:
 
   RefPtr<MediaDataDecoder> mDecoder;
   MediaDataDecoderCallback* mCallback;
   RefPtr<TaskQueue> mTaskQueue;
   RefPtr<CDMProxy> mProxy;
   nsClassHashtable<nsRefPtrHashKey<MediaRawData>, DecryptPromiseRequestHolder> mDecrypts;
   RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
--- a/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.h
@@ -69,16 +69,20 @@ public:
   {
   }
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
+  const char* GetDescriptionName() const override
+  {
+    return "GMP audio decoder";
+  }
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
 
 private:
 
   class GMPInitDoneCallback : public GetGMPAudioDecoderCallback
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
@@ -84,16 +84,20 @@ public:
   {
   }
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
+  const char* GetDescriptionName() const override
+  {
+    return "GMP video decoder";
+  }
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
   virtual GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(MediaRawData* aSample);
 
 private:
 
--- a/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.h
+++ b/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.h
@@ -133,16 +133,21 @@ public:
   // asynchronously and responded to via the MediaDataDecoderCallback.
   // Note: the nsresults returned by the proxied decoder are lost.
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
 
+  const char* GetDescriptionName() const override
+  {
+    return "GMP proxy data decoder";
+  }
+
   // Called by MediaDataDecoderCallbackProxy.
   void FlushComplete();
 
 private:
   RefPtr<InitPromise> InternalInit();
 
 #ifdef DEBUG
   bool IsOnProxyThread() {
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -80,16 +80,21 @@ public:
     : MediaCodecDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType,
                             aFormat, aCallback)
     , mImageContainer(aImageContainer)
     , mConfig(aConfig)
   {
 
   }
 
+  const char* GetDescriptionName() const override
+  {
+    return "android video decoder";
+  }
+
   RefPtr<InitPromise> Init() override
   {
     mSurfaceTexture = AndroidSurfaceTexture::Create();
     if (!mSurfaceTexture) {
       NS_WARNING("Failed to create SurfaceTexture for video decode\n");
       return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
     }
 
@@ -184,16 +189,21 @@ public:
       buffer = jni::Object::LocalRef::Adopt(
           env, env->NewDirectByteBuffer(aConfig.mCodecSpecificConfig->Elements(),
           aConfig.mCodecSpecificConfig->Length()));
       NS_ENSURE_SUCCESS_VOID(aFormat->SetByteBuffer(NS_LITERAL_STRING("csd-0"),
                                                     buffer));
     }
   }
 
+  const char* GetDescriptionName() const override
+  {
+    return "android audio decoder";
+  }
+
   nsresult Output(BufferInfo::Param aInfo, void* aBuffer,
                   MediaFormat::Param aFormat, const TimeUnit& aDuration)
   {
     // The output on Android is always 16-bit signed
     nsresult rv;
     int32_t numChannels;
     NS_ENSURE_SUCCESS(rv =
         aFormat->GetInteger(NS_LITERAL_STRING("channel-count"), &numChannels), rv);
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -52,16 +52,20 @@ public:
 
   virtual ~MediaCodecDataDecoder();
 
   RefPtr<MediaDataDecoder::InitPromise> Init() override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
   nsresult Input(MediaRawData* aSample) override;
+  const char* GetDescriptionName() const override
+  {
+    return "android decoder";
+  }
 
 protected:
   enum ModuleState {
     kDecoding = 0,
     kFlushing,
     kDrainQueue,
     kDrainDecoder,
     kDrainWaitEOS,
--- a/dom/media/platforms/apple/AppleATDecoder.h
+++ b/dom/media/platforms/apple/AppleATDecoder.h
@@ -26,16 +26,21 @@ public:
   virtual ~AppleATDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
 
+  const char* GetDescriptionName() const override
+  {
+    return "apple CoreMedia decoder";
+  }
+
   // Callbacks also need access to the config.
   const AudioInfo& mConfig;
 
   // Use to extract magic cookie for HE-AAC detection.
   nsTArray<uint8_t> mMagicCookie;
   // Will be set to true should an error occurred while attempting to retrieve
   // the magic cookie property.
   bool mFileStreamError;
--- a/dom/media/platforms/apple/AppleVDADecoder.h
+++ b/dom/media/platforms/apple/AppleVDADecoder.h
@@ -76,16 +76,21 @@ public:
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override
   {
     return true;
   }
 
+  const char* GetDescriptionName() const override
+  {
+    return "apple VDA decoder";
+  }
+
   // Access from the taskqueue and the decoder's thread.
   // OutputFrame is thread-safe.
   nsresult OutputFrame(CVPixelBufferRef aImage,
                        AppleFrameRef aFrameRef);
 
 protected:
   // Flush and Drain operation, always run
   virtual void ProcessFlush();
--- a/dom/media/platforms/apple/AppleVTDecoder.h
+++ b/dom/media/platforms/apple/AppleVTDecoder.h
@@ -22,30 +22,37 @@ public:
   virtual ~AppleVTDecoder();
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override
   {
     return mIsHardwareAccelerated;
   }
 
+  const char* GetDescriptionName() const override
+  {
+    return mIsHardwareAccelerated
+      ? "apple hardware VT decoder"
+      : "apple software VT decoder";
+  }
+
 protected:
   void ProcessFlush() override;
   void ProcessDrain() override;
   void ProcessShutdown() override;
 
 private:
   CMVideoFormatDescriptionRef mFormat;
   VTDecompressionSessionRef mSession;
 
   // Method to pass a frame to VideoToolbox for decoding.
   nsresult SubmitFrame(MediaRawData* aSample);
   // Method to set up the decompression session.
   nsresult InitializeSession();
   nsresult WaitForAsynchronousFrames();
   CFDictionaryRef CreateDecoderSpecification();
   CFDictionaryRef CreateDecoderExtensions();
-  bool mIsHardwareAccelerated;
+  Atomic<bool> mIsHardwareAccelerated;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_AppleVTDecoder_h
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
@@ -26,16 +26,20 @@ public:
                      const AudioInfo& aConfig);
   virtual ~FFmpegAudioDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   void ProcessDrain() override;
   void InitCodecContext() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
+  const char* GetDescriptionName() const override
+  {
+    return "ffmpeg audio decoder";
+  }
 
 private:
   void DecodePacket(MediaRawData* aSample);
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegAACDecoder_h__
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
@@ -39,16 +39,24 @@ public:
                      ImageContainer* aImageContainer);
   virtual ~FFmpegVideoDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   void ProcessDrain() override;
   void ProcessFlush() override;
   void InitCodecContext() override;
+  const char* GetDescriptionName() const override
+  {
+#ifdef USING_MOZFFVPX
+    return "ffvpx video decoder";
+#else
+    return "ffmpeg video decoder";
+#endif
+  }
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   void DecodeFrame(MediaRawData* aSample);
   DecodeResult DoDecodeFrame(MediaRawData* aSample);
   DecodeResult DoDecodeFrame(MediaRawData* aSample, uint8_t* aData, int aSize);
   void DoDrain();
   void OutputDelayedFrames();
--- a/dom/media/platforms/gonk/GonkAudioDecoderManager.h
+++ b/dom/media/platforms/gonk/GonkAudioDecoderManager.h
@@ -28,16 +28,21 @@ public:
 
   RefPtr<InitPromise> Init() override;
 
   nsresult Output(int64_t aStreamOffset,
                           RefPtr<MediaData>& aOutput) override;
 
   void ProcessFlush() override;
 
+  const char* GetDescriptionName() const override
+  {
+    return "gonk audio decoder";
+  }
+
 private:
   bool InitMediaCodecProxy();
 
   nsresult CreateAudioData(MediaBuffer* aBuffer, int64_t aStreamOffset);
 
   uint32_t mAudioChannels;
   uint32_t mAudioRate;
   const uint32_t mAudioProfile;
--- a/dom/media/platforms/gonk/GonkMediaDataDecoder.h
+++ b/dom/media/platforms/gonk/GonkMediaDataDecoder.h
@@ -23,16 +23,17 @@ class GonkDecoderManager : public androi
 public:
   typedef TrackInfo::TrackType TrackType;
   typedef MediaDataDecoder::InitPromise InitPromise;
   typedef MediaDataDecoder::DecoderFailureReason DecoderFailureReason;
 
   virtual ~GonkDecoderManager() {}
 
   virtual RefPtr<InitPromise> Init() = 0;
+  virtual const char* GetDescriptionName() const = 0;
 
   // Asynchronously send sample into mDecoder. If out of input buffer, aSample
   // will be queued for later re-send.
   nsresult Input(MediaRawData* aSample);
 
   // Flush the queued samples and signal decoder to throw all pending input/output away.
   nsresult Flush();
 
@@ -194,16 +195,21 @@ public:
   nsresult Input(MediaRawData* aSample) override;
 
   nsresult Flush() override;
 
   nsresult Drain() override;
 
   nsresult Shutdown() override;
 
+  const char* GetDescriptionName() const override
+  {
+    return "gonk decoder";
+  }
+
 private:
 
   android::sp<GonkDecoderManager> mManager;
 };
 
 } // namespace mozilla
 
 #endif // GonkMediaDataDecoder_h_
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.h
@@ -45,16 +45,21 @@ public:
 
   RefPtr<InitPromise> Init() override;
 
   nsresult Output(int64_t aStreamOffset,
                           RefPtr<MediaData>& aOutput) override;
 
   nsresult Shutdown() override;
 
+  const char* GetDescriptionName() const override
+  {
+    return "gonk video decoder";
+  }
+
   static void RecycleCallback(TextureClient* aClient, void* aClosure);
 
 protected:
   // Bug 1199809: workaround to avoid sending the graphic buffer by making a
   // copy of output buffer after calling flush(). Bug 1203859 was created to
   // reimplementing Gonk PDM on top of OpenMax IL directly. Its buffer
   // management will work better with Gecko and solve problems like this.
   void ProcessFlush() override
--- a/dom/media/platforms/omx/OmxDataDecoder.h
+++ b/dom/media/platforms/omx/OmxDataDecoder.h
@@ -66,16 +66,21 @@ public:
   nsresult Input(MediaRawData* aSample) override;
 
   nsresult Flush() override;
 
   nsresult Drain() override;
 
   nsresult Shutdown() override;
 
+  const char* GetDescriptionName() const override
+  {
+    return "omx decoder";
+  }
+
   // Return true if event is handled.
   bool Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2);
 
 protected:
   void InitializationTask();
 
   void ResolveInitPromise(const char* aMethodName);
 
--- a/dom/media/platforms/wmf/MFTDecoder.cpp
+++ b/dom/media/platforms/wmf/MFTDecoder.cpp
@@ -231,17 +231,19 @@ MFTDecoder::Output(RefPtr<IMFSample>* aO
   if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
     // Not enough input to produce output. This is an expected failure,
     // so don't warn on encountering it.
     return hr;
   }
   // Treat other errors as unexpected, and warn.
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-  MOZ_ASSERT(output.pSample);
+  if (!output.pSample) {
+    return S_OK;
+  }
 
   if (mDiscontinuity) {
     output.pSample->SetUINT32(MFSampleExtension_Discontinuity, TRUE);
     mDiscontinuity = false;
   }
 
   *aOutput = output.pSample; // AddRefs
   if (mMFTProvidesOutputSamples && !providedSample) {
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WMFAudioMFTManager.h"
 #include "MediaInfo.h"
 #include "VideoUtils.h"
 #include "WMFUtils.h"
 #include "nsTArray.h"
 #include "TimeUnits.h"
-
+#include "mozilla/Telemetry.h"
 #include "mozilla/Logging.h"
 
 extern mozilla::LogModule* GetPDMLog();
 #define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 static void
@@ -221,16 +221,26 @@ WMFAudioMFTManager::Output(int64_t aStre
       ++typeChangeCount;
       continue;
     }
     break;
   }
 
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
+  if (!sample) {
+    LOG("Audio MFTDecoder returned success but null output.");
+    nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([]() -> void {
+      LOG("Reporting telemetry AUDIO_MFT_OUTPUT_NULL_SAMPLES");
+      Telemetry::Accumulate(Telemetry::ID::AUDIO_MFT_OUTPUT_NULL_SAMPLES, 1);
+    });
+    AbstractThread::MainThread()->Dispatch(task.forget());
+    return E_FAIL;
+  }
+
   RefPtr<IMFMediaBuffer> buffer;
   hr = sample->ConvertToContiguousBuffer(getter_AddRefs(buffer));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
   DWORD maxLength = 0, currentLength = 0;
   hr = buffer->Lock(&data, &maxLength, &currentLength);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.h
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.h
@@ -33,16 +33,21 @@ public:
                          RefPtr<MediaData>& aOutput) override;
 
   void Shutdown() override;
 
   TrackInfo::TrackType GetType() override {
     return TrackInfo::kAudioTrack;
   }
 
+  const char* GetDescriptionName() const override
+  {
+    return "wmf audio decoder";
+  }
+
 private:
 
   HRESULT UpdateOutputType();
 
   uint32_t mAudioChannels;
   uint32_t mAudioRate;
   nsTArray<BYTE> mUserData;
 
--- a/dom/media/platforms/wmf/WMFMediaDataDecoder.h
+++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.h
@@ -49,16 +49,18 @@ public:
   virtual void Shutdown() = 0;
 
   virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const { return false; }
 
   virtual TrackInfo::TrackType GetType() = 0;
 
   virtual void ConfigurationChanged(const TrackInfo& aConfig) {}
 
+  virtual const char* GetDescriptionName() const = 0;
+
 protected:
   // IMFTransform wrapper that performs the decoding.
   RefPtr<MFTDecoder> mDecoder;
 };
 
 // Decodes audio and video using Windows Media Foundation. Samples are decoded
 // using the MFTDecoder created by the MFTManager. This class implements
 // the higher-level logic that drives mapping the MFT to the async
@@ -80,16 +82,21 @@ public:
   nsresult Drain() override;
 
   nsresult Shutdown() override;
 
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
 
   nsresult ConfigurationChanged(const TrackInfo& aConfig) override;
 
+  const char* GetDescriptionName() const override
+  {
+    return mMFTManager ? mMFTManager->GetDescriptionName() : "";
+  }
+
 private:
 
   // Called on the task queue. Inserts the sample into the decoder, and
   // extracts output if available.
   void ProcessDecode(MediaRawData* aSample);
 
   // Called on the task queue. Extracts output if available, and delivers
   // it to the reader. Called after ProcessDecode() and ProcessDrain().
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/layers/LayersTypes.h"
 #include "MediaInfo.h"
 #include "mozilla/Logging.h"
 #include "gfx2DGlue.h"
 #include "gfxWindowsPlatform.h"
 #include "IMFYCbCrImage.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
 #include "nsPrintfCString.h"
 
 extern mozilla::LogModule* GetPDMLog();
 #define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 using mozilla::layers::Image;
 using mozilla::layers::IMFYCbCrImage;
 using mozilla::layers::LayerManager;
@@ -73,16 +74,19 @@ WMFVideoMFTManager::WMFVideoMFTManager(
                             mozilla::layers::LayersBackend aLayersBackend,
                             mozilla::layers::ImageContainer* aImageContainer,
                             bool aDXVAEnabled)
   : mVideoInfo(aConfig)
   , mVideoStride(0)
   , mImageContainer(aImageContainer)
   , mDXVAEnabled(aDXVAEnabled)
   , mLayersBackend(aLayersBackend)
+  , mNullOutputCount(0)
+  , mGotValidOutputAfterNullOutput(false)
+  , mGotExcessiveNullOutput(false)
   // mVideoStride, mVideoWidth, mVideoHeight, mUseHwAccel are initialized in
   // Init().
 {
   MOZ_COUNT_CTOR(WMFVideoMFTManager);
 
   // Need additional checks/params to check vp8/vp9
   if (aConfig.mMimeType.EqualsLiteral("video/mp4") ||
       aConfig.mMimeType.EqualsLiteral("video/avc")) {
@@ -98,16 +102,30 @@ WMFVideoMFTManager::WMFVideoMFTManager(
 
 WMFVideoMFTManager::~WMFVideoMFTManager()
 {
   MOZ_COUNT_DTOR(WMFVideoMFTManager);
   // Ensure DXVA/D3D9 related objects are released on the main thread.
   if (mDXVA2Manager) {
     DeleteOnMainThread(mDXVA2Manager);
   }
+
+  // Record whether the video decoder successfully decoded, or output null
+  // samples but did/didn't recover.
+  uint32_t telemetry = (mNullOutputCount == 0) ? 0 :
+                       (mGotValidOutputAfterNullOutput && mGotExcessiveNullOutput) ? 1 :
+                       mGotExcessiveNullOutput ? 2 :
+                       mGotValidOutputAfterNullOutput ? 3 :
+                       4;
+
+  nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([=]() -> void {
+    LOG(nsPrintfCString("Reporting telemetry VIDEO_MFT_OUTPUT_NULL_SAMPLES=%d", telemetry).get());
+    Telemetry::Accumulate(Telemetry::ID::VIDEO_MFT_OUTPUT_NULL_SAMPLES, telemetry);
+  });
+  AbstractThread::MainThread()->Dispatch(task.forget());
 }
 
 const GUID&
 WMFVideoMFTManager::GetMFTGUID()
 {
   MOZ_ASSERT(mStreamType != Unknown);
   switch (mStreamType) {
     case H264: return CLSID_CMSH264DecoderMFT;
@@ -570,16 +588,33 @@ WMFVideoMFTManager::Output(int64_t aStre
       // changes on consecutive calls, so be permissive.
       // 100 is arbitrarily > 2.
       NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE);
       // Loop back and try decoding again...
       ++typeChangeCount;
       continue;
     }
     if (SUCCEEDED(hr)) {
+      if (!sample) {
+        LOG("Video MFTDecoder returned success but no output!");
+        // On some machines/input the MFT returns success but doesn't output
+        // a video frame. If we detect this, try again, but only up to a
+        // point; after 250 failures, give up. Note we count all failures
+        // over the life of the decoder, as we may end up exiting with a
+        // NEED_MORE_INPUT and coming back to hit the same error. So just
+        // counting with a local variable (like typeChangeCount does) may
+        // not work in this situation.
+        ++mNullOutputCount;
+        if (mNullOutputCount > 250) {
+          LOG("Excessive Video MFTDecoder returning success but no output; giving up");
+          mGotExcessiveNullOutput = true;
+          return E_FAIL;
+        }
+        continue;
+      }
       break;
     }
     // Else unexpected error, assert, and bail.
     NS_WARNING("WMFVideoMFTManager::Output() unexpected error");
     return hr;
   }
 
   RefPtr<VideoData> frame;
@@ -590,16 +625,20 @@ WMFVideoMFTManager::Output(int64_t aStre
   }
   // Frame should be non null only when we succeeded.
   MOZ_ASSERT((frame != nullptr) == SUCCEEDED(hr));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
   NS_ENSURE_TRUE(frame, E_FAIL);
 
   aOutData = frame;
 
+  if (mNullOutputCount) {
+    mGotValidOutputAfterNullOutput = true;
+  }
+
   return S_OK;
 }
 
 void
 WMFVideoMFTManager::Shutdown()
 {
   mDecoder = nullptr;
   DeleteOnMainThread(mDXVA2Manager);
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.h
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h
@@ -36,16 +36,23 @@ public:
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
 
   TrackInfo::TrackType GetType() override {
     return TrackInfo::kVideoTrack;
   }
 
   void ConfigurationChanged(const TrackInfo& aConfig) override;
 
+  const char* GetDescriptionName() const override
+  {
+    nsCString failureReason;
+    return IsHardwareAccelerated(failureReason)
+      ? "wmf hardware video decoder" : "wmf software video decoder";
+  }
+
 private:
 
   bool InitializeDXVA(bool aForceD3D9);
 
   bool InitInternal(bool aForceD3D9);
 
   HRESULT ConfigureVideoFrameGeometry();
 
@@ -83,13 +90,17 @@ private:
     VP8,
     VP9
   };
 
   StreamType mStreamType;
 
   const GUID& GetMFTGUID();
   const GUID& GetMediaSubtypeGUID();
+
+  uint32_t mNullOutputCount;
+  bool mGotValidOutputAfterNullOutput;
+  bool mGotExcessiveNullOutput;
 };
 
 } // namespace mozilla
 
 #endif // WMFVideoMFTManager_h_
--- a/dom/media/platforms/wrappers/FuzzingWrapper.h
+++ b/dom/media/platforms/wrappers/FuzzingWrapper.h
@@ -106,16 +106,20 @@ private:
   // MediaDataDecoder implementation.
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
   nsresult ConfigurationChanged(const TrackInfo& aConfig) override;
+  const char* GetDescriptionName() const override
+  {
+    return mDecoder->GetDescriptionName();
+  }
 
   RefPtr<MediaDataDecoder> mDecoder;
   RefPtr<DecoderCallbackFuzzingWrapper> mCallbackWrapper;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -30,16 +30,23 @@ public:
   virtual ~H264Converter();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
+  const char* GetDescriptionName() const override
+  {
+    if (mDecoder) {
+      return mDecoder->GetDescriptionName();
+    }
+    return "H264Converter decoder (pending)";
+  }
 
   // Return true if mimetype is H.264.
   static bool IsH264(const TrackInfo& aConfig);
   nsresult GetLastError() const { return mLastError; }
 
 private:
   // Will create the required MediaDataDecoder if need AVCC and we have a SPS NAL.
   // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
@@ -784,13 +784,19 @@ nsSynthVoiceRegistry::SpeakImpl(VoiceDat
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to get speech service type");
 
   if (serviceType == nsISpeechService::SERVICETYPE_INDIRECT_AUDIO) {
     aTask->InitIndirectAudio();
   } else {
     aTask->InitDirectAudio();
   }
 
-  aVoice->mService->Speak(aText, aVoice->mUri, aVolume, aRate, aPitch, aTask);
+  if (NS_FAILED(aVoice->mService->Speak(aText, aVoice->mUri, aVolume, aRate,
+                                        aPitch, aTask))) {
+    if (serviceType == nsISpeechService::SERVICETYPE_INDIRECT_AUDIO) {
+      aTask->DispatchError(0, 0);
+    }
+    // XXX When using direct audio, no way to dispatch error
+  }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
+++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
@@ -263,18 +263,17 @@ FakeIndirectAudioSynth::Speak(const nsAS
   uint32_t flags = 0;
   for (uint32_t i = 0; i < ArrayLength(sIndirectVoices); i++) {
     if (aUri.EqualsASCII(sIndirectVoices[i].uri)) {
       flags = sIndirectVoices[i].flags;
     }
   }
 
   if (flags & eFailAtStart) {
-    aTask->DispatchError(0, 0);
-    return NS_OK;
+    return NS_ERROR_FAILURE;
   }
 
   RefPtr<FakeSynthCallback> cb = new FakeSynthCallback(
     (flags & eSuppressEvents) ? nullptr : aTask);
 
   aTask->Setup(cb, 0, 0, 0);
 
   nsCOMPtr<nsIRunnable> runnable = new DispatchStart(aTask);
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -67,16 +67,17 @@ skip-if = e10s || buildapp == 'b2g' # b2
 [test_donottrack.html]
 skip-if = buildapp == 'mulet'
 [test_focus_legend_noparent.html]
 [test_focusrings.xul]
 skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_for_of.html]
 [test_frameElementWrapping.html]
 [test_pointerPreserves3D.html]
+[test_pointerPreserves3DClip.html]
 [test_framedhistoryframes.html]
 [test_idleapi_permissions.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_img_mutations.html]
 [test_interfaces.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 # [test_network_events.html]
 # Disable this test until bug 795711 is fixed.
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_pointerPreserves3DClip.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for pointer events with preserve-3d and clips</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <style type="text/css">
+      .outer {
+      transform-style: preserve-3d;
+      }
+      .container {
+      overflow-y: scroll;
+      overflow-x: hidden;
+      width: 200px;
+      height: 300px;
+      }
+      .content {
+      width: 200px;
+      height: 1000px;
+      transform-style: preserve-3d;
+      }
+      #container1 {
+      background-color: green;
+      transform: translateZ(2px);
+      }
+      #container2 {
+      height: 100px;
+      transform: translateY(-200px) translateZ(10px);
+      background-color: red;
+      }
+    </style>
+  </head>
+  <body onload="runTest();">
+    <div class="outer" id="outer">
+      <div class="container" id="container1">
+	<div class="content"></div>
+      </div>
+      <div class="container" id="container2">
+	<div class="content"></div>
+      </div>
+    </div>
+<script class="testbody" type="text/javascript">
+function runTest() {
+    var outer = document.getElementById("outer");
+    var x = outer.offsetLeft;
+    var y = outer.offsetTop;
+    var target = document.elementFromPoint(x + 100, y + 250);
+    ok(target.parentNode == document.getElementById("container1"), "Find the right target.");
+    SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+  </body>
+</html>
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1334,17 +1334,23 @@ nsEventStatus AsyncPanZoomController::On
         CurrentTouchBlock()->GetActiveTouchCount());
     // In cases where the user is panning, then taps the second finger without
     // entering a pinch, we will arrive here when the second finger is lifted.
     // However the first finger is still down so we want to remain in state
     // TOUCHING.
     if (CurrentTouchBlock()->GetActiveTouchCount() == 0) {
       // It's possible we may be overscrolled if the user tapped during a
       // previous overscroll pan. Make sure to snap back in this situation.
-      if (!SnapBackIfOverscrolled()) {
+      // An ancestor APZC could be overscrolled instead of this APZC, so
+      // walk the handoff chain as well.
+      CurrentTouchBlock()->GetOverscrollHandoffChain()->SnapBackOverscrolledApzc(this);
+      // SnapBackOverscrolledApzc() will put any APZC it causes to snap back
+      // into the OVERSCROLL_ANIMATION state. If that's not us, since we're
+      // done TOUCHING enter the NOTHING state.
+      if (mState != OVERSCROLL_ANIMATION) {
         SetState(NOTHING);
       }
     }
     return nsEventStatus_eIgnore;
 
   case PANNING:
   case PANNING_LOCKED_X:
   case PANNING_LOCKED_Y:
@@ -3553,16 +3559,21 @@ void
 AsyncPanZoomController::ResetTouchInputState()
 {
   MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
   RefPtr<GestureEventListener> listener = GetGestureEventListener();
   if (listener) {
     listener->HandleInputEvent(cancel);
   }
   CancelAnimationAndGestureState();
+  // Clear overscroll along the entire handoff chain, in case an APZC
+  // later in the chain is overscrolled.
+  if (TouchBlockState* block = CurrentTouchBlock()) {
+    block->GetOverscrollHandoffChain()->ClearOverscroll();
+  }
 }
 
 void
 AsyncPanZoomController::CancelAnimationAndGestureState()
 {
   mX.CancelGesture();
   mY.CancelGesture();
   CancelAnimation(CancelAnimationFlags::RequestSnap);
--- a/gfx/layers/apz/test/gtest/InputUtils.h
+++ b/gfx/layers/apz/test/gtest/InputUtils.h
@@ -27,31 +27,38 @@
  * code to dispatch input events.
  */
 
 // Some helper functions for constructing input event objects suitable to be
 // passed either to an APZC (which expects an transformed point), or to an APZTM
 // (which expects an untransformed point). We handle both cases by setting both
 // the transformed and untransformed fields to the same value.
 SingleTouchData
-CreateSingleTouchData(int32_t aIdentifier, int aX, int aY)
+CreateSingleTouchData(int32_t aIdentifier, const ScreenIntPoint& aPoint)
 {
-  SingleTouchData touch(aIdentifier, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0);
-  touch.mLocalScreenPoint = ParentLayerPoint(aX, aY);
+  SingleTouchData touch(aIdentifier, aPoint, ScreenSize(0, 0), 0, 0);
+  touch.mLocalScreenPoint = ParentLayerPoint(aPoint.x, aPoint.y);
   return touch;
 }
 
+// Convenience wrapper for CreateSingleTouchData() that takes loose coordinates.
+SingleTouchData
+CreateSingleTouchData(int32_t aIdentifier, ScreenIntCoord aX, ScreenIntCoord aY)
+{
+  return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY));
+}
+
 PinchGestureInput
 CreatePinchGestureInput(PinchGestureInput::PinchGestureType aType,
-                        int aFocusX, int aFocusY,
+                        const ScreenIntPoint& aFocus,
                         float aCurrentSpan, float aPreviousSpan)
 {
-  PinchGestureInput result(aType, 0, TimeStamp(), ScreenPoint(aFocusX, aFocusY),
+  PinchGestureInput result(aType, 0, TimeStamp(), aFocus,
                            aCurrentSpan, aPreviousSpan, 0);
-  result.mLocalFocusPoint = ParentLayerPoint(aFocusX, aFocusY);
+  result.mLocalFocusPoint = ParentLayerPoint(aFocus.x, aFocus.y);
   return result;
 }
 
 template<class InputReceiver>
 void
 SetDefaultAllowedTouchBehavior(const RefPtr<InputReceiver>& aTarget,
                                uint64_t aInputBlockId,
                                int touchPoints = 1)
@@ -71,90 +78,95 @@ SetDefaultAllowedTouchBehavior(const Ref
 MultiTouchInput
 CreateMultiTouchInput(MultiTouchInput::MultiTouchType aType, TimeStamp aTime)
 {
   return MultiTouchInput(aType, MillisecondsSinceStartup(aTime), aTime, 0);
 }
 
 template<class InputReceiver>
 nsEventStatus
-TouchDown(const RefPtr<InputReceiver>& aTarget, int aX, int aY, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
+TouchDown(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+          TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
 {
   MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime);
-  mti.mTouches.AppendElement(CreateSingleTouchData(0, aX, aY));
+  mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
   return aTarget->ReceiveInputEvent(mti, nullptr, aOutInputBlockId);
 }
 
 template<class InputReceiver>
 nsEventStatus
-TouchMove(const RefPtr<InputReceiver>& aTarget, int aX, int aY, TimeStamp aTime)
+TouchMove(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+          TimeStamp aTime)
 {
   MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime);
-  mti.mTouches.AppendElement(CreateSingleTouchData(0, aX, aY));
+  mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
   return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
 }
 
 template<class InputReceiver>
 nsEventStatus
-TouchUp(const RefPtr<InputReceiver>& aTarget, int aX, int aY, TimeStamp aTime)
+TouchUp(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+        TimeStamp aTime)
 {
   MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime);
-  mti.mTouches.AppendElement(CreateSingleTouchData(0, aX, aY));
+  mti.mTouches.AppendElement(CreateSingleTouchData(0, aPoint));
   return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
 }
 
 template<class InputReceiver>
 void
-Tap(const RefPtr<InputReceiver>& aTarget, int aX, int aY, MockContentControllerDelayed* aMcc,
+Tap(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+    MockContentControllerDelayed* aMcc,
     TimeDuration aTapLength,
     nsEventStatus (*aOutEventStatuses)[2] = nullptr,
     uint64_t* aOutInputBlockId = nullptr)
 {
   // Even if the caller doesn't care about the block id, we need it to set the
   // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
   uint64_t blockId;
   if (!aOutInputBlockId) {
     aOutInputBlockId = &blockId;
   }
 
-  nsEventStatus status = TouchDown(aTarget, aX, aY, aMcc->Time(), aOutInputBlockId);
+  nsEventStatus status = TouchDown(aTarget, aPoint, aMcc->Time(), aOutInputBlockId);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = status;
   }
   aMcc->AdvanceBy(aTapLength);
 
   // If touch-action is enabled then simulate the allowed touch behaviour
   // notification that the main thread is supposed to deliver.
   if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
     SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
   }
 
-  status = TouchUp(aTarget, aX, aY, aMcc->Time());
+  status = TouchUp(aTarget, aPoint, aMcc->Time());
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[1] = status;
   }
 }
 
 template<class InputReceiver>
 void
-TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget, int aX, int aY,
-    MockContentControllerDelayed* aMcc, TimeDuration aTapLength)
+TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+    const ScreenIntPoint& aPoint, MockContentControllerDelayed* aMcc,
+    TimeDuration aTapLength)
 {
   nsEventStatus statuses[2];
-  Tap(aTarget, aX, aY, aMcc, aTapLength, &statuses);
+  Tap(aTarget, aPoint, aMcc, aTapLength, &statuses);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
 }
 
 template<class InputReceiver>
 void
 Pan(const RefPtr<InputReceiver>& aTarget,
     MockContentControllerDelayed* aMcc,
-    const ScreenPoint& aTouchStart,
-    const ScreenPoint& aTouchEnd,
+    const ScreenIntPoint& aTouchStart,
+    const ScreenIntPoint& aTouchEnd,
     bool aKeepFingerDown = false,
     nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
     nsEventStatus (*aOutEventStatuses)[4] = nullptr,
     uint64_t* aOutInputBlockId = nullptr)
 {
   // Reduce the touch start and move tolerance to a tiny value.
   // We can't use a scoped pref because this value might be read at some later
   // time when the events are actually processed, rather than when we deliver
@@ -168,49 +180,51 @@ Pan(const RefPtr<InputReceiver>& aTarget
   // Even if the caller doesn't care about the block id, we need it to set the
   // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
   uint64_t blockId;
   if (!aOutInputBlockId) {
     aOutInputBlockId = &blockId;
   }
 
   // Make sure the move is large enough to not be handled as a tap
-  nsEventStatus status = TouchDown(aTarget, aTouchStart.x, aTouchStart.y + OVERCOME_TOUCH_TOLERANCE, aMcc->Time(), aOutInputBlockId);
+  nsEventStatus status = TouchDown(aTarget,
+      ScreenIntPoint(aTouchStart.x, aTouchStart.y + OVERCOME_TOUCH_TOLERANCE),
+      aMcc->Time(), aOutInputBlockId);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = status;
   }
 
   aMcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
 
   // Allowed touch behaviours must be set after sending touch-start.
   if (status != nsEventStatus_eConsumeNoDefault) {
     if (aAllowedTouchBehaviors) {
       EXPECT_EQ(1UL, aAllowedTouchBehaviors->Length());
       aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
     } else if (gfxPrefs::TouchActionEnabled()) {
       SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
     }
   }
 
-  status = TouchMove(aTarget, aTouchStart.x, aTouchStart.y, aMcc->Time());
+  status = TouchMove(aTarget, aTouchStart, aMcc->Time());
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[1] = status;
   }
 
   aMcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
 
-  status = TouchMove(aTarget, aTouchEnd.x, aTouchEnd.y, aMcc->Time());
+  status = TouchMove(aTarget, aTouchEnd, aMcc->Time());
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[2] = status;
   }
 
   aMcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
 
   if (!aKeepFingerDown) {
-    status = TouchUp(aTarget, aTouchEnd.x, aTouchEnd.y, aMcc->Time());
+    status = TouchUp(aTarget, aTouchEnd, aMcc->Time());
   } else {
     status = nsEventStatus_eIgnore;
   }
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[3] = status;
   }
 
   // Don't increment the time here. Animations started on touch-up, such as
@@ -227,17 +241,17 @@ Pan(const RefPtr<InputReceiver>& aTarget
     MockContentControllerDelayed* aMcc,
     int aTouchStartY,
     int aTouchEndY,
     bool aKeepFingerDown = false,
     nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
     nsEventStatus (*aOutEventStatuses)[4] = nullptr,
     uint64_t* aOutInputBlockId = nullptr)
 {
-  ::Pan(aTarget, aMcc, ScreenPoint(10, aTouchStartY), ScreenPoint(10, aTouchEndY),
+  ::Pan(aTarget, aMcc, ScreenIntPoint(10, aTouchStartY), ScreenIntPoint(10, aTouchEndY),
       aKeepFingerDown, aAllowedTouchBehaviors, aOutEventStatuses, aOutInputBlockId);
 }
 
 /*
  * Dispatches mock touch events to the apzc and checks whether apzc properly
  * consumed them and triggered scrolling behavior.
  */
 template<class InputReceiver>
@@ -274,64 +288,65 @@ ApzcPanNoFling(const RefPtr<TestAsyncPan
 {
   Pan(aApzc, aMcc, aTouchStartY, aTouchEndY, false, nullptr, nullptr, aOutInputBlockId);
   aApzc->CancelAnimation();
 }
 
 template<class InputReceiver>
 void
 PinchWithPinchInput(const RefPtr<InputReceiver>& aTarget,
-                    int aFocusX, int aFocusY, int aSecondFocusX, int aSecondFocusY, float aScale,
+                    const ScreenIntPoint& aFocus,
+                    const ScreenIntPoint& aSecondFocus, float aScale,
                     nsEventStatus (*aOutEventStatuses)[3] = nullptr)
 {
   nsEventStatus actualStatus = aTarget->ReceiveInputEvent(
       CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
-                              aFocusX, aFocusY, 10.0, 10.0),
+                              aFocus, 10.0, 10.0),
       nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = actualStatus;
   }
   actualStatus = aTarget->ReceiveInputEvent(
       CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
-                              aSecondFocusX, aSecondFocusY, 10.0 * aScale, 10.0),
+                              aSecondFocus, 10.0 * aScale, 10.0),
       nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[1] = actualStatus;
   }
   actualStatus = aTarget->ReceiveInputEvent(
       CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
                               // note: negative values here tell APZC
                               //       not to turn the pinch into a pan
-                              aFocusX, aFocusY, -1.0, -1.0),
+                              aFocus, -1.0, -1.0),
       nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[2] = actualStatus;
   }
 }
 
 template<class InputReceiver>
 void
 PinchWithPinchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
-                                  int aFocusX, int aFocusY, float aScale,
+                                  const ScreenIntPoint& aFocus, float aScale,
                                   bool aShouldTriggerPinch)
 {
   nsEventStatus statuses[3];  // scalebegin, scale, scaleend
-  PinchWithPinchInput(aTarget, aFocusX, aFocusY, aFocusX, aFocusY, aScale, &statuses);
+  PinchWithPinchInput(aTarget, aFocus, aFocus, aScale, &statuses);
 
   nsEventStatus expectedStatus = aShouldTriggerPinch
       ? nsEventStatus_eConsumeNoDefault
       : nsEventStatus_eIgnore;
   EXPECT_EQ(expectedStatus, statuses[0]);
   EXPECT_EQ(expectedStatus, statuses[1]);
 }
 
 template<class InputReceiver>
 void
 PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
-                    int aFocusX, int aFocusY, float aScale,
+                    const ScreenIntPoint& aFocus, float aScale,
                     int& inputId,
                     nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
                     nsEventStatus (*aOutEventStatuses)[4] = nullptr,
                     uint64_t* aOutInputBlockId = nullptr)
 {
   // Having pinch coordinates in float type may cause problems with high-precision scale values
   // since SingleTouchData accepts integer value. But for trivial tests it should be ok.
   float pinchLength = 100.0;
@@ -340,127 +355,129 @@ PinchWithTouchInput(const RefPtr<InputRe
   // Even if the caller doesn't care about the block id, we need it to set the
   // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
   uint64_t blockId;
   if (!aOutInputBlockId) {
     aOutInputBlockId = &blockId;
   }
 
   MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
-  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocusX, aFocusY));
-  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocusX, aFocusY));
+  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus));
+  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus));
   nsEventStatus status = aTarget->ReceiveInputEvent(mtiStart, aOutInputBlockId);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = status;
   }
 
   if (aAllowedTouchBehaviors) {
     EXPECT_EQ(2UL, aAllowedTouchBehaviors->Length());
     aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
   } else if (gfxPrefs::TouchActionEnabled()) {
     SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId, 2);
   }
 
   MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
-  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocusX - pinchLength, aFocusY));
-  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocusX + pinchLength, aFocusY));
+  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLength, aFocus.y));
+  mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLength, aFocus.y));
   status = aTarget->ReceiveInputEvent(mtiMove1, nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[1] = status;
   }
 
   MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
-  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocusX - pinchLengthScaled, aFocusY));
-  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocusX + pinchLengthScaled, aFocusY));
+  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
+  mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
   status = aTarget->ReceiveInputEvent(mtiMove2, nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[2] = status;
   }
 
   MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
-  mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocusX - pinchLengthScaled, aFocusY));
-  mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocusX + pinchLengthScaled, aFocusY));
+  mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus.x - pinchLengthScaled, aFocus.y));
+  mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus.x + pinchLengthScaled, aFocus.y));
   status = aTarget->ReceiveInputEvent(mtiEnd, nullptr);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[3] = status;
   }
 
   inputId += 2;
 }
 
 template<class InputReceiver>
 void
 PinchWithTouchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
-                                  int aFocusX, int aFocusY, float aScale,
+                                  const ScreenIntPoint& aFocus, float aScale,
                                   int& inputId, bool aShouldTriggerPinch,
                                   nsTArray<uint32_t>* aAllowedTouchBehaviors)
 {
   nsEventStatus statuses[4];  // down, move, move, up
-  PinchWithTouchInput(aTarget, aFocusX, aFocusY, aScale, inputId, aAllowedTouchBehaviors, &statuses);
+  PinchWithTouchInput(aTarget, aFocus, aScale, inputId, aAllowedTouchBehaviors, &statuses);
 
   nsEventStatus expectedMoveStatus = aShouldTriggerPinch
       ? nsEventStatus_eConsumeDoDefault
       : nsEventStatus_eIgnore;
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
   EXPECT_EQ(expectedMoveStatus, statuses[1]);
   EXPECT_EQ(expectedMoveStatus, statuses[2]);
 }
 
 template<class InputReceiver>
 void
-DoubleTap(const RefPtr<InputReceiver>& aTarget, int aX, int aY, MockContentControllerDelayed* aMcc,
+DoubleTap(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
+          MockContentControllerDelayed* aMcc,
           nsEventStatus (*aOutEventStatuses)[4] = nullptr,
           uint64_t (*aOutInputBlockIds)[2] = nullptr)
 {
   uint64_t blockId;
-  nsEventStatus status = TouchDown(aTarget, aX, aY, aMcc->Time(), &blockId);
+  nsEventStatus status = TouchDown(aTarget, aPoint, aMcc->Time(), &blockId);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[0] = status;
   }
   if (aOutInputBlockIds) {
     (*aOutInputBlockIds)[0] = blockId;
   }
   aMcc->AdvanceByMillis(10);
 
   // If touch-action is enabled then simulate the allowed touch behaviour
   // notification that the main thread is supposed to deliver.
   if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
     SetDefaultAllowedTouchBehavior(aTarget, blockId);
   }
 
-  status = TouchUp(aTarget, aX, aY, aMcc->Time());
+  status = TouchUp(aTarget, aPoint, aMcc->Time());
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[1] = status;
   }
   aMcc->AdvanceByMillis(10);
-  status = TouchDown(aTarget, aX, aY, aMcc->Time(), &blockId);
+  status = TouchDown(aTarget, aPoint, aMcc->Time(), &blockId);
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[2] = status;
   }
   if (aOutInputBlockIds) {
     (*aOutInputBlockIds)[1] = blockId;
   }
   aMcc->AdvanceByMillis(10);
 
   if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
     SetDefaultAllowedTouchBehavior(aTarget, blockId);
   }
 
-  status = TouchUp(aTarget, aX, aY, aMcc->Time());
+  status = TouchUp(aTarget, aPoint, aMcc->Time());
   if (aOutEventStatuses) {
     (*aOutEventStatuses)[3] = status;
   }
 }
 
 template<class InputReceiver>
 void
-DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget, int aX, int aY,
-    MockContentControllerDelayed* aMcc, uint64_t (*aOutInputBlockIds)[2] = nullptr)
+DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
+    const ScreenIntPoint& aPoint, MockContentControllerDelayed* aMcc,
+    uint64_t (*aOutInputBlockIds)[2] = nullptr)
 {
   nsEventStatus statuses[4];
-  DoubleTap(aTarget, aX, aY, aMcc, &statuses, aOutInputBlockIds);
+  DoubleTap(aTarget, aPoint, aMcc, &statuses, aOutInputBlockIds);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]);
 }
 
 #endif // mozilla_layers_InputUtils_h
--- a/gfx/layers/apz/test/gtest/TestBasic.cpp
+++ b/gfx/layers/apz/test/gtest/TestBasic.cpp
@@ -17,17 +17,17 @@ TEST_F(APZCBasicTester, Overzoom) {
   fm.SetZoom(CSSToParentLayerScale2D(1.0, 1.0));
   fm.SetIsRootContent(true);
   apzc->SetFrameMetrics(fm);
 
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
-  PinchWithPinchInputAndCheckStatus(apzc, 50, 50, 0.5, true);
+  PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(50, 50), 0.5, true);
 
   fm = apzc->GetFrameMetrics();
   EXPECT_EQ(0.8f, fm.GetZoom().ToScaleFactor().scale);
   // bug 936721 - PGO builds introduce rounding error so
   // use a fuzzy match instead
   EXPECT_LT(std::abs(fm.GetScrollOffset().x), 1e-5);
   EXPECT_LT(std::abs(fm.GetScrollOffset().y), 1e-5);
 }
@@ -290,18 +290,18 @@ TEST_F(APZCBasicTester, OverScroll_Bug11
   // the overscroll animation once, to get it to initialize the first overscroll
   // sample.
   SampleAnimationOnce();
 
   // Do a touch-down to cancel the overscroll animation, and then a touch-up
   // to schedule a new one since we're still overscrolled. We don't pan because
   // panning can trigger functions that clear the overscroll animation state
   // in other ways.
-  TouchDown(apzc, 10, 10, mcc->Time(), nullptr);
-  TouchUp(apzc, 10, 10, mcc->Time());
+  TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), nullptr);
+  TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
 
   // Sample the second overscroll animation to its end.
   // If the ending of the first overscroll animation fails to clear state
   // properly, this will assert.
   ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
   SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
 }
 
--- a/gfx/layers/apz/test/gtest/TestEventRegions.cpp
+++ b/gfx/layers/apz/test/gtest/TestEventRegions.cpp
@@ -181,52 +181,52 @@ TEST_F(APZEventRegionsTester, HitRegionI
     EXPECT_CALL(*mcc, HandleSingleTap(_, _, left->GetGuid())).Times(1);
     EXPECT_CALL(check, Call("Tapped on left this time"));
   }
 
   TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
 
   // Tap in the exposed hit regions of each of the layers once and ensure
   // the clicks are dispatched right away
-  Tap(manager, 10, 10, mcc, tapDuration);
+  Tap(manager, ScreenIntPoint(10, 10), mcc, tapDuration);
   mcc->RunThroughDelayedTasks();    // this runs the tap event
   check.Call("Tapped on left");
-  Tap(manager, 110, 110, mcc, tapDuration);
+  Tap(manager, ScreenIntPoint(110, 110), mcc, tapDuration);
   mcc->RunThroughDelayedTasks();    // this runs the tap event
   check.Call("Tapped on bottom");
-  Tap(manager, 110, 10, mcc, tapDuration);
+  Tap(manager, ScreenIntPoint(110, 10), mcc, tapDuration);
   mcc->RunThroughDelayedTasks();    // this runs the tap event
   check.Call("Tapped on root");
 
   // Now tap on the dispatch-to-content region where the layers overlap
-  Tap(manager, 10, 110, mcc, tapDuration);
+  Tap(manager, ScreenIntPoint(10, 110), mcc, tapDuration);
   mcc->RunThroughDelayedTasks();    // this runs the main-thread timeout
   check.Call("Tap pending on d-t-c region");
   mcc->RunThroughDelayedTasks();    // this runs the tap event
   check.Call("Tapped on bottom again");
 
   // Now let's do that again, but simulate a main-thread response
   uint64_t inputBlockId = 0;
-  Tap(manager, 10, 110, mcc, tapDuration, nullptr, &inputBlockId);
+  Tap(manager, ScreenIntPoint(10, 110), mcc, tapDuration, nullptr, &inputBlockId);
   nsTArray<ScrollableLayerGuid> targets;
   targets.AppendElement(left->GetGuid());
   manager->SetTargetAPZC(inputBlockId, targets);
   while (mcc->RunThroughDelayedTasks());    // this runs the tap event
   check.Call("Tapped on left this time");
 }
 
 TEST_F(APZEventRegionsTester, HitRegionAccumulatesChildren) {
   CreateEventRegionsLayerTree2();
 
   // Tap in the area of the child layer that's not directly included in the
   // parent layer's hit region. Verify that it comes out of the APZC's
   // content controller, which indicates the input events got routed correctly
   // to the APZC.
   EXPECT_CALL(*mcc, HandleSingleTap(_, _, rootApzc->GetGuid())).Times(1);
-  Tap(manager, 10, 160, mcc, TimeDuration::FromMilliseconds(100));
+  Tap(manager, ScreenIntPoint(10, 160), mcc, TimeDuration::FromMilliseconds(100));
 }
 
 TEST_F(APZEventRegionsTester, Obscuration) {
   CreateObscuringLayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
 
   manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
 
@@ -255,17 +255,17 @@ TEST_F(APZEventRegionsTester, Bug1119497
 TEST_F(APZEventRegionsTester, Bug1117712) {
   CreateBug1117712LayerTree();
 
   TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]);
 
   // These touch events should hit the dispatch-to-content region of layers[3]
   // and so get queued with that APZC as the tentative target.
   uint64_t inputBlockId = 0;
-  Tap(manager, 55, 5, mcc, TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId);
+  Tap(manager, ScreenIntPoint(55, 5), mcc, TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId);
   // But now we tell the APZ that really it hit layers[2], and expect the tap
   // to be delivered at the correct coordinates.
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(55, 5), 0, apzc2->GetGuid())).Times(1);
 
   nsTArray<ScrollableLayerGuid> targets;
   targets.AppendElement(apzc2->GetGuid());
   manager->SetTargetAPZC(inputBlockId, targets);
 }
--- a/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
+++ b/gfx/layers/apz/test/gtest/TestGestureDetector.cpp
@@ -205,22 +205,22 @@ protected:
     // Advance the fling animation by timeDelta milliseconds.
     ParentLayerPoint pointOut;
     AsyncTransform viewTransformOut;
     apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(timeDelta));
 
     // Deliver a tap to abort the fling. Ensure that we get a HandleSingleTap
     // call out of it if and only if the fling is slow.
     EXPECT_CALL(*mcc, HandleSingleTap(_, 0, apzc->GetGuid())).Times(tapCallsExpected);
-    Tap(apzc, 10, 10, mcc, 0);
+    Tap(apzc, ScreenIntPoint(10, 10), mcc, 0);
     while (mcc->RunThroughDelayedTasks());
 
     // Deliver another tap, to make sure that taps are flowing properly once
     // the fling is aborted.
-    Tap(apzc, 100, 100, mcc, 0);
+    Tap(apzc, ScreenIntPoint(100, 100), mcc, 0);
     while (mcc->RunThroughDelayedTasks());
 
     // Verify that we didn't advance any further after the fling was aborted, in either case.
     ParentLayerPoint finalPointOut;
     apzc->SampleContentTransformForFrame(&viewTransformOut, finalPointOut);
     EXPECT_EQ(pointOut.x, finalPointOut.x);
     EXPECT_EQ(pointOut.y, finalPointOut.y);
 
@@ -242,34 +242,34 @@ protected:
     // Sample the fling a couple of times to ensure it's going.
     ParentLayerPoint point, finalPoint;
     AsyncTransform viewTransform;
     apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
     apzc->SampleContentTransformForFrame(&viewTransform, finalPoint, TimeDuration::FromMilliseconds(10));
     EXPECT_GT(finalPoint.y, point.y);
 
     // Now we put our finger down to stop the fling
-    TouchDown(apzc, 10, 10, mcc->Time(), &blockId);
+    TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
 
     // Re-sample to make sure it hasn't moved
     apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
     EXPECT_EQ(finalPoint.x, point.x);
     EXPECT_EQ(finalPoint.y, point.y);
 
     // respond to the touchdown that stopped the fling.
     // even if we do a prevent-default on it, the animation should remain stopped.
     apzc->ContentReceivedInputBlock(blockId, aPreventDefault);
 
     // Verify the page hasn't moved
     apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(70));
     EXPECT_EQ(finalPoint.x, point.x);
     EXPECT_EQ(finalPoint.y, point.y);
 
     // clean up
-    TouchUp(apzc, 10, 10, mcc->Time());
+    TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
 
     apzc->AssertStateIsReset();
   }
 };
 
 TEST_F(APZCFlingStopTester, FlingStop) {
   DoFlingStopTest(false);
 }
@@ -295,17 +295,17 @@ TEST_F(APZCGestureDetectorTester, ShortP
     // This verifies that the single tap notification is sent after the
     // touchup is fully processed. The ordering here is important.
     EXPECT_CALL(check, Call("pre-tap"));
     EXPECT_CALL(check, Call("post-tap"));
     EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   }
 
   check.Call("pre-tap");
-  TapAndCheckStatus(apzc, 10, 10, mcc, TimeDuration::FromMilliseconds(100));
+  TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), mcc, TimeDuration::FromMilliseconds(100));
   check.Call("post-tap");
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, MediumPress) {
   MakeApzcUnzoomable();
 
@@ -315,30 +315,30 @@ TEST_F(APZCGestureDetectorTester, Medium
     // This verifies that the single tap notification is sent after the
     // touchup is fully processed. The ordering here is important.
     EXPECT_CALL(check, Call("pre-tap"));
     EXPECT_CALL(check, Call("post-tap"));
     EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   }
 
   check.Call("pre-tap");
-  TapAndCheckStatus(apzc, 10, 10, mcc, TimeDuration::FromMilliseconds(400));
+  TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), mcc, TimeDuration::FromMilliseconds(400));
   check.Call("post-tap");
 
   apzc->AssertStateIsReset();
 }
 
 class APZCLongPressTester : public APZCGestureDetectorTester {
 protected:
   void DoLongPressTest(uint32_t aBehavior) {
     MakeApzcUnzoomable();
 
     uint64_t blockId = 0;
 
-    nsEventStatus status = TouchDown(apzc, 10, 10, mcc->Time(), &blockId);
+    nsEventStatus status = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time(), &blockId);
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
       // SetAllowedTouchBehavior() must be called after sending touch-start.
       nsTArray<uint32_t> allowedTouchBehaviors;
       allowedTouchBehaviors.AppendElement(aBehavior);
       apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
     }
@@ -371,17 +371,17 @@ protected:
     // with preventDefault=false, and then we run the timeout task which
     // "loses the race" and does nothing.
     apzc->ContentReceivedInputBlock(blockId, false);
     mcc->AdvanceByMillis(1000);
 
     // Finally, simulate lifting the finger. Since the long-press wasn't
     // prevent-defaulted, we should get a long-tap-up event.
     check.Call("preHandleSingleTap");
-    status = TouchUp(apzc, 10, 10, mcc->Time());
+    status = TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());
     mcc->RunThroughDelayedTasks();
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
     check.Call("postHandleSingleTap");
 
     apzc->AssertStateIsReset();
   }
 
   void DoLongPressPreventDefaultTest(uint32_t aBehavior) {
@@ -389,17 +389,17 @@ protected:
 
     EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
 
     int touchX = 10,
         touchStartY = 10,
         touchEndY = 50;
 
     uint64_t blockId = 0;
-    nsEventStatus status = TouchDown(apzc, touchX, touchStartY, mcc->Time(), &blockId);
+    nsEventStatus status = TouchDown(apzc, ScreenIntPoint(touchX, touchStartY), mcc->Time(), &blockId);
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
       // SetAllowedTouchBehavior() must be called after sending touch-start.
       nsTArray<uint32_t> allowedTouchBehaviors;
       allowedTouchBehaviors.AppendElement(aBehavior);
       apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
     }
@@ -431,17 +431,17 @@ protected:
     mcc->AdvanceByMillis(1000);
 
     MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time());
     mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
     status = apzc->ReceiveInputEvent(mti, nullptr);
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0);
-    status = TouchUp(apzc, touchX, touchEndY, mcc->Time());
+    status = TouchUp(apzc, ScreenIntPoint(touchX, touchEndY), mcc->Time());
     EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
 
     ParentLayerPoint pointOut;
     AsyncTransform viewTransformOut;
     apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
 
     EXPECT_EQ(ParentLayerPoint(), pointOut);
     EXPECT_EQ(AsyncTransform(), viewTransformOut);
@@ -477,84 +477,84 @@ TEST_F(APZCLongPressTester, LongPressPre
 TEST_F(APZCGestureDetectorTester, DoubleTap) {
   MakeApzcWaitForMainThread();
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
   EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
 
   uint64_t blockIds[2];
-  DoubleTapAndCheckStatus(apzc, 10, 10, mcc, &blockIds);
+  DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), mcc, &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], false);
   apzc->ContentReceivedInputBlock(blockIds[1], false);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
   MakeApzcWaitForMainThread();
   MakeApzcUnzoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(2);
   EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
 
   uint64_t blockIds[2];
-  DoubleTapAndCheckStatus(apzc, 10, 10, mcc, &blockIds);
+  DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), mcc, &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], false);
   apzc->ContentReceivedInputBlock(blockIds[1], false);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
   MakeApzcWaitForMainThread();
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
   EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
 
   uint64_t blockIds[2];
-  DoubleTapAndCheckStatus(apzc, 10, 10, mcc, &blockIds);
+  DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), mcc, &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], true);
   apzc->ContentReceivedInputBlock(blockIds[1], false);
 
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
   MakeApzcWaitForMainThread();
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
   EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
 
   uint64_t blockIds[2];
-  DoubleTapAndCheckStatus(apzc, 10, 10, mcc, &blockIds);
+  DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), mcc, &blockIds);
 
   // responses to the two touchstarts
   apzc->ContentReceivedInputBlock(blockIds[0], true);
   apzc->ContentReceivedInputBlock(blockIds[1], true);
 
   apzc->AssertStateIsReset();
 }
 
 // Test for bug 947892
 // We test whether we dispatch tap event when the tap is followed by pinch.
 TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) {
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
 
-  Tap(apzc, 10, 10, mcc, TimeDuration::FromMilliseconds(100));
+  Tap(apzc, ScreenIntPoint(10, 10), mcc, TimeDuration::FromMilliseconds(100));
 
   int inputId = 0;
   MultiTouchInput mti;
   mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
   mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
   mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
   apzc->ReceiveInputEvent(mti, nullptr);
 
@@ -566,17 +566,17 @@ TEST_F(APZCGestureDetectorTester, TapFol
   apzc->AssertStateIsReset();
 }
 
 TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
   MakeApzcZoomable();
 
   EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
 
-  Tap(apzc, 10, 10, mcc, TimeDuration::FromMilliseconds(100));
+  Tap(apzc, ScreenIntPoint(10, 10), mcc, TimeDuration::FromMilliseconds(100));
 
   int inputId = 0;
   MultiTouchInput mti;
   mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
   mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
   apzc->ReceiveInputEvent(mti, nullptr);
 
   mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
--- a/gfx/layers/apz/test/gtest/TestHitTesting.cpp
+++ b/gfx/layers/apz/test/gtest/TestHitTesting.cpp
@@ -460,28 +460,28 @@ TEST_F(APZHitTestingTester, Bug1148350) 
   {
     InSequence s;
     EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid())).Times(1);
     EXPECT_CALL(check, Call("Tapped without transform"));
     EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid())).Times(1);
     EXPECT_CALL(check, Call("Tapped with interleaved transform"));
   }
 
-  Tap(manager, 100, 100, mcc, TimeDuration::FromMilliseconds(100));
+  Tap(manager, ScreenIntPoint(100, 100), mcc, TimeDuration::FromMilliseconds(100));
   mcc->RunThroughDelayedTasks();
   check.Call("Tapped without transform");
 
   uint64_t blockId;
-  TouchDown(manager, 100, 100, mcc->Time(), &blockId);
+  TouchDown(manager, ScreenIntPoint(100, 100), mcc->Time(), &blockId);
   if (gfxPrefs::TouchActionEnabled()) {
     SetDefaultAllowedTouchBehavior(manager, blockId);
   }
   mcc->AdvanceByMillis(100);
 
   layers[0]->SetVisibleRegion(LayerIntRegion(LayerIntRect(0,50,200,150)));
   layers[0]->SetBaseTransform(Matrix4x4::Translation(0, 50, 0));
   manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
 
-  TouchUp(manager, 100, 100, mcc->Time());
+  TouchUp(manager, ScreenIntPoint(100, 100), mcc->Time());
   mcc->RunThroughDelayedTasks();
   check.Call("Tapped with interleaved transform");
 }
 
--- a/gfx/layers/apz/test/gtest/TestPinching.cpp
+++ b/gfx/layers/apz/test/gtest/TestPinching.cpp
@@ -39,19 +39,21 @@ protected:
       // One repaint request for each gesture.
       EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
     } else {
       EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
     }
 
     int touchInputId = 0;
     if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
-      PinchWithTouchInputAndCheckStatus(apzc, 250, 300, 1.25, touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
+      PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
+          touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
     } else {
-      PinchWithPinchInputAndCheckStatus(apzc, 250, 300, 1.25, aShouldTriggerPinch);
+      PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 1.25,
+          aShouldTriggerPinch);
     }
 
     FrameMetrics fm = apzc->GetFrameMetrics();
 
     if (aShouldTriggerPinch) {
       // the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
       EXPECT_EQ(2.5f, fm.GetZoom().ToScaleFactor().scale);
       EXPECT_EQ(305, fm.GetScrollOffset().x);
@@ -67,19 +69,21 @@ protected:
     // part 2 of the test, move to the top-right corner of the page and pinch and
     // make sure we stay in the correct spot
     fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
     fm.SetScrollOffset(CSSPoint(930, 5));
     apzc->SetFrameMetrics(fm);
     // the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100
 
     if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
-      PinchWithTouchInputAndCheckStatus(apzc, 250, 300, 0.5, touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
+      PinchWithTouchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
+          touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
     } else {
-      PinchWithPinchInputAndCheckStatus(apzc, 250, 300, 0.5, aShouldTriggerPinch);
+      PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(250, 300), 0.5,
+          aShouldTriggerPinch);
     }
 
     fm = apzc->GetFrameMetrics();
 
     if (aShouldTriggerPinch) {
       // the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200
       EXPECT_EQ(1.0f, fm.GetZoom().ToScaleFactor().scale);
       EXPECT_EQ(880, fm.GetScrollOffset().x);
@@ -137,17 +141,18 @@ TEST_F(APZCPinchGestureDetectorTester, P
   FrameMetrics originalMetrics = GetPinchableFrameMetrics();
   apzc->SetFrameMetrics(originalMetrics);
 
   MakeApzcWaitForMainThread();
   MakeApzcZoomable();
 
   int touchInputId = 0;
   uint64_t blockId = 0;
-  PinchWithTouchInput(apzc, 250, 300, 1.25, touchInputId, nullptr, nullptr, &blockId);
+  PinchWithTouchInput(apzc, ScreenIntPoint(250, 300), 1.25, touchInputId,
+      nullptr, nullptr, &blockId);
 
   // Send the prevent-default notification for the touch block
   apzc->ContentReceivedInputBlock(blockId, true);
 
   // verify the metrics didn't change (i.e. the pinch was ignored)
   FrameMetrics fm = apzc->GetFrameMetrics();
   EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
   EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
@@ -157,17 +162,18 @@ TEST_F(APZCPinchGestureDetectorTester, P
 }
 
 TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
   // set up APZ
   apzc->SetFrameMetrics(GetPinchableFrameMetrics());
   MakeApzcUnzoomable();
 
   nsEventStatus statuses[3];  // scalebegin, scale, scaleend
-  PinchWithPinchInput(apzc, 250, 350, 200, 300, 10, &statuses);
+  PinchWithPinchInput(apzc, ScreenIntPoint(250, 350), ScreenIntPoint(200, 300),
+      10, &statuses);
 
   FrameMetrics fm = apzc->GetFrameMetrics();
 
   // It starts from (300, 300), then moves the focus point from (250, 350) to
   // (200, 300) pans by (50, 50) screen pixels, but there is a 2x zoom, which
   // causes the scroll offset to change by half of that (25, 25) pixels.
   EXPECT_EQ(325, fm.GetScrollOffset().x);
   EXPECT_EQ(325, fm.GetScrollOffset().y);
rename from gfx/layers/apz/test/gtest/TestOverscrollHandoff.cpp
rename to gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
--- a/gfx/layers/apz/test/gtest/TestOverscrollHandoff.cpp
+++ b/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
@@ -3,37 +3,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "APZCTreeManagerTester.h"
 #include "APZTestCommon.h"
 #include "InputUtils.h"
 
-class APZOverscrollHandoffTester : public APZCTreeManagerTester {
+class APZScrollHandoffTester : public APZCTreeManagerTester {
 protected:
   UniquePtr<ScopedLayerTreeRegistration> registration;
   TestAsyncPanZoomController* rootApzc;
 
-  void CreateOverscrollHandoffLayerTree1() {
+  void CreateScrollHandoffLayerTree1() {
     const char* layerTreeSyntax = "c(t)";
     nsIntRegion layerVisibleRegion[] = {
       nsIntRegion(IntRect(0, 0, 100, 100)),
       nsIntRegion(IntRect(0, 50, 100, 50))
     };
     root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
     SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
     SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
     SetScrollHandoff(layers[1], root);
     registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
     manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
     rootApzc = ApzcOf(root);
+    rootApzc->GetFrameMetrics().SetIsRootContent(true);  // make root APZC zoomable
   }
 
-  void CreateOverscrollHandoffLayerTree2() {
+  void CreateScrollHandoffLayerTree2() {
     const char* layerTreeSyntax = "c(c(t))";
     nsIntRegion layerVisibleRegion[] = {
       nsIntRegion(IntRect(0, 0, 100, 100)),
       nsIntRegion(IntRect(0, 0, 100, 100)),
       nsIntRegion(IntRect(0, 50, 100, 50))
     };
     root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
     SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
@@ -43,17 +44,17 @@ protected:
     SetScrollHandoff(layers[2], layers[1]);
     // No ScopedLayerTreeRegistration as that just needs to be done once per test
     // and this is the second layer tree for a particular test.
     MOZ_ASSERT(registration);
     manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
     rootApzc = ApzcOf(root);
   }
 
-  void CreateOverscrollHandoffLayerTree3() {
+  void CreateScrollHandoffLayerTree3() {
     const char* layerTreeSyntax = "c(c(t)c(t))";
     nsIntRegion layerVisibleRegion[] = {
       nsIntRegion(IntRect(0, 0, 100, 100)),  // root
       nsIntRegion(IntRect(0, 0, 100, 50)),   // scrolling parent 1
       nsIntRegion(IntRect(0, 0, 100, 50)),   // scrolling child 1
       nsIntRegion(IntRect(0, 50, 100, 50)),  // scrolling parent 2
       nsIntRegion(IntRect(0, 50, 100, 50))   // scrolling child 2
     };
@@ -121,19 +122,19 @@ protected:
     EXPECT_LE(childVelocityAfterFling2,
               childVelocityAfterFling1 * kAcceleration * kAcceleration / 4);
   }
 };
 
 // Here we test that if the processing of a touch block is deferred while we
 // wait for content to send a prevent-default message, overscroll is still
 // handed off correctly when the block is processed.
-TEST_F(APZOverscrollHandoffTester, DeferredInputEventProcessing) {
+TEST_F(APZScrollHandoffTester, DeferredInputEventProcessing) {
   // Set up the APZC tree.
-  CreateOverscrollHandoffLayerTree1();
+  CreateScrollHandoffLayerTree1();
 
   TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
 
   // Enable touch-listeners so that we can separate the queueing of input
   // events from them being processed.
   childApzc->SetWaitForMainThread();
 
   // Queue input events for a pan.
@@ -149,33 +150,33 @@ TEST_F(APZOverscrollHandoffTester, Defer
   EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
 }
 
 // Here we test that if the layer structure changes in between two input
 // blocks being queued, and the first block is only processed after the second
 // one has been queued, overscroll handoff for the first block follows
 // the original layer structure while overscroll handoff for the second block
 // follows the new layer structure.
-TEST_F(APZOverscrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
+TEST_F(APZScrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
   // Set up an initial APZC tree.
-  CreateOverscrollHandoffLayerTree1();
+  CreateScrollHandoffLayerTree1();
 
   TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
 
   // Enable touch-listeners so that we can separate the queueing of input
   // events from them being processed.
   childApzc->SetWaitForMainThread();
 
   // Queue input events for a pan.
   uint64_t blockId = 0;
   ApzcPanNoFling(childApzc, mcc, 90, 30, &blockId);
 
   // Modify the APZC tree to insert a new APZC 'middle' into the handoff chain
   // between the child and the root.
-  CreateOverscrollHandoffLayerTree2();
+  CreateScrollHandoffLayerTree2();
   RefPtr<Layer> middle = layers[1];
   childApzc->SetWaitForMainThread();
   TestAsyncPanZoomController* middleApzc = ApzcOf(middle);
 
   // Queue input events for another pan.
   uint64_t secondBlockId = 0;
   ApzcPanNoFling(childApzc, mcc, 30, 90, &secondBlockId);
 
@@ -197,21 +198,21 @@ TEST_F(APZOverscrollHandoffTester, Layer
   // place at the time the touch-start of the second pan was queued.
   EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y);
 }
 
 // Test that putting a second finger down on an APZC while a down-chain APZC
 // is overscrolled doesn't result in being stuck in overscroll.
-TEST_F(APZOverscrollHandoffTester, StuckInOverscroll_Bug1073250) {
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1073250) {
   // Enable overscrolling.
   SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
 
-  CreateOverscrollHandoffLayerTree1();
+  CreateScrollHandoffLayerTree1();
 
   TestAsyncPanZoomController* child = ApzcOf(layers[1]);
 
   // Pan, causing the parent APZC to overscroll.
   Pan(manager, mcc, 10, 40, true /* keep finger down */);
   EXPECT_FALSE(child->IsOverscrolled());
   EXPECT_TRUE(rootApzc->IsOverscrolled());
 
@@ -234,21 +235,21 @@ TEST_F(APZOverscrollHandoffTester, Stuck
   // Make sure nothing is overscrolled.
   EXPECT_FALSE(child->IsOverscrolled());
   EXPECT_FALSE(rootApzc->IsOverscrolled());
 }
 
 // This is almost exactly like StuckInOverscroll_Bug1073250, except the
 // APZC receiving the input events for the first touch block is the child
 // (and thus not the same APZC that overscrolls, which is the parent).
-TEST_F(APZOverscrollHandoffTester, StuckInOverscroll_Bug1231228) {
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1231228) {
   // Enable overscrolling.
   SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
 
-  CreateOverscrollHandoffLayerTree1();
+  CreateScrollHandoffLayerTree1();
 
   TestAsyncPanZoomController* child = ApzcOf(layers[1]);
 
   // Pan, causing the parent APZC to overscroll.
   Pan(manager, mcc, 60, 90, true /* keep finger down */);
   EXPECT_FALSE(child->IsOverscrolled());
   EXPECT_TRUE(rootApzc->IsOverscrolled());
 
@@ -268,46 +269,124 @@ TEST_F(APZOverscrollHandoffTester, Stuck
   child->AdvanceAnimationsUntilEnd();
   rootApzc->AdvanceAnimationsUntilEnd();
 
   // Make sure nothing is overscrolled.
   EXPECT_FALSE(child->IsOverscrolled());
   EXPECT_FALSE(rootApzc->IsOverscrolled());
 }
 
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202a) {
+  // Enable overscrolling.
+  SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+  CreateScrollHandoffLayerTree1();
+
+  TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+  // Pan, causing the parent APZC to overscroll.
+  Pan(manager, mcc, 60, 90, true /* keep finger down */);
+  EXPECT_FALSE(child->IsOverscrolled());
+  EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+  // Lift the finger, triggering an overscroll animation
+  // (but don't allow it to run).
+  TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+  // Put the finger down again, interrupting the animation
+  // and entering the TOUCHING state.
+  TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+  // Lift the finger once again.
+  TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+  // Allow any animations to run their course.
+  child->AdvanceAnimationsUntilEnd();
+  rootApzc->AdvanceAnimationsUntilEnd();
+
+  // Make sure nothing is overscrolled.
+  EXPECT_FALSE(child->IsOverscrolled());
+  EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
+TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202b) {
+  // Enable overscrolling.
+  SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+  CreateScrollHandoffLayerTree1();
+
+  TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+  // Pan, causing the parent APZC to overscroll.
+  Pan(manager, mcc, 60, 90, true /* keep finger down */);
+  EXPECT_FALSE(child->IsOverscrolled());
+  EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+  // Lift the finger, triggering an overscroll animation
+  // (but don't allow it to run).
+  TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+  // Put the finger down again, interrupting the animation
+  // and entering the TOUCHING state.
+  TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time());
+
+  // Put a second finger down. Since we're in the TOUCHING state,
+  // the "are we panned into overscroll" check will fail and we
+  // will not ignore the second finger, instead entering the
+  // PINCHING state.
+  MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+  // Use the same touch identifier for the first touch (0) as TouchDown(). (A bit hacky.)
+  secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 90), ScreenSize(0, 0), 0, 0));
+  secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(10, 80), ScreenSize(0, 0), 0, 0));
+  manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
+
+  // Release the fingers.
+  MultiTouchInput fingersUp = secondFingerDown;
+  fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+  manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
+
+  // Allow any animations to run their course.
+  child->AdvanceAnimationsUntilEnd();
+  rootApzc->AdvanceAnimationsUntilEnd();
+
+  // Make sure nothing is overscrolled.
+  EXPECT_FALSE(child->IsOverscrolled());
+  EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
 // Test that flinging in a direction where one component of the fling goes into
 // overscroll but the other doesn't, results in just the one component being
 // handed off to the parent, while the original APZC continues flinging in the
 // other direction.
-TEST_F(APZOverscrollHandoffTester, PartialFlingHandoff) {
-  CreateOverscrollHandoffLayerTree1();
+TEST_F(APZScrollHandoffTester, PartialFlingHandoff) {
+  CreateScrollHandoffLayerTree1();
 
   // Fling up and to the left. The child APZC has room to scroll up, but not
   // to the left, so the horizontal component of the fling should be handed
   // off to the parent APZC.
-  Pan(manager, mcc, ScreenPoint(90, 90), ScreenPoint(55, 55));
+  Pan(manager, mcc, ScreenIntPoint(90, 90), ScreenIntPoint(55, 55));
 
   RefPtr<TestAsyncPanZoomController> parent = ApzcOf(root);
   RefPtr<TestAsyncPanZoomController> child = ApzcOf(layers[1]);
 
   // Advance the child's fling animation once to give the partial handoff
   // a chance to occur.
   mcc->AdvanceByMillis(10);
   child->AdvanceAnimations(mcc->Time());
 
   // Assert that partial handoff has occurred.
   child->AssertStateIsFling();
   parent->AssertStateIsFling();
 }
 
 // Here we test that if two flings are happening simultaneously, overscroll
 // is handed off correctly for each.
-TEST_F(APZOverscrollHandoffTester, SimultaneousFlings) {
+TEST_F(APZScrollHandoffTester, SimultaneousFlings) {
   // Set up an initial APZC tree.
-  CreateOverscrollHandoffLayerTree3();
+  CreateScrollHandoffLayerTree3();
 
   RefPtr<TestAsyncPanZoomController> parent1 = ApzcOf(layers[1]);
   RefPtr<TestAsyncPanZoomController> child1 = ApzcOf(layers[2]);
   RefPtr<TestAsyncPanZoomController> parent2 = ApzcOf(layers[3]);
   RefPtr<TestAsyncPanZoomController> child2 = ApzcOf(layers[4]);
 
   // Pan on the lower child.
   Pan(child2, mcc, 45, 5);
@@ -325,59 +404,59 @@ TEST_F(APZOverscrollHandoffTester, Simul
 
   // Check that the flings have been handed off to the parents.
   child1->AssertStateIsReset();
   parent1->AssertStateIsFling();
   child2->AssertStateIsReset();
   parent2->AssertStateIsFling();
 }
 
-TEST_F(APZOverscrollHandoffTester, Scrollgrab) {
+TEST_F(APZScrollHandoffTester, Scrollgrab) {
   // Set up the layer tree
   CreateScrollgrabLayerTree();
 
   RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
 
   // Pan on the child, enough to fully scroll the scrollgrab parent (20 px)
   // and leave some more (another 15 px) for the child.
   Pan(childApzc, mcc, 80, 45);
 
   // Check that the parent and child have scrolled as much as we expect.
   EXPECT_EQ(20, rootApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(15, childApzc->GetFrameMetrics().GetScrollOffset().y);
 }
 
-TEST_F(APZOverscrollHandoffTester, ScrollgrabFling) {
+TEST_F(APZScrollHandoffTester, ScrollgrabFling) {
   // Set up the layer tree
   CreateScrollgrabLayerTree();
 
   RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
 
   // Pan on the child, not enough to fully scroll the scrollgrab parent.
   Pan(childApzc, mcc, 80, 70);
 
   // Check that it is the scrollgrab parent that's in a fling, not the child.
   rootApzc->AssertStateIsFling();
   childApzc->AssertStateIsReset();
 }
 
-TEST_F(APZOverscrollHandoffTester, ScrollgrabFlingAcceleration1) {
+TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration1) {
   CreateScrollgrabLayerTree(true /* make parent scrollable */);
   TestFlingAcceleration();
 }
 
-TEST_F(APZOverscrollHandoffTester, ScrollgrabFlingAcceleration2) {
+TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration2) {
   CreateScrollgrabLayerTree(false /* do not make parent scrollable */);
   TestFlingAcceleration();
 }
 
-TEST_F(APZOverscrollHandoffTester, ImmediateHandoffDisallowed_Pan) {
+TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Pan) {
   SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
 
-  CreateOverscrollHandoffLayerTree1();
+  CreateScrollHandoffLayerTree1();
 
   RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
   RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
 
   // Pan on the child, enough to scroll it to its end and have scroll
   // left to hand off. Since immediate handoff is disallowed, we expect
   // the leftover scroll not to be handed off.
   Pan(childApzc, mcc, 60, 5);
@@ -389,20 +468,20 @@ TEST_F(APZOverscrollHandoffTester, Immed
   // Pan again on the child. This time, since the child was scrolled to
   // its end when the gesture began, we expect the scroll to be handed off.
   Pan(childApzc, mcc, 60, 50);
 
   // Verify that the parent scrolled.
   EXPECT_EQ(10, parentApzc->GetFrameMetrics().GetScrollOffset().y);
 }
 
-TEST_F(APZOverscrollHandoffTester, ImmediateHandoffDisallowed_Fling) {
+TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Fling) {
   SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
 
-  CreateOverscrollHandoffLayerTree1();
+  CreateScrollHandoffLayerTree1();
 
   RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
   RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
 
   // Pan on the child, enough to get very close to the end, so that the
   // subsequent fling reaches the end and has leftover velocity to hand off.
   Pan(childApzc, mcc, 60, 12);
 
--- a/gfx/layers/apz/test/gtest/moz.build
+++ b/gfx/layers/apz/test/gtest/moz.build
@@ -4,19 +4,19 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'TestBasic.cpp',
     'TestEventRegions.cpp',
     'TestGestureDetector.cpp',
     'TestHitTesting.cpp',
-    'TestOverscrollHandoff.cpp',
     'TestPanning.cpp',
     'TestPinching.cpp',
+    'TestScrollHandoff.cpp',
     'TestTreeManager.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/gfx/2d',
     '/gfx/layers',
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -319,16 +319,17 @@ MessageChannel::MessageChannel(MessageLi
     mAwaitingSyncReply(false),
     mAwaitingSyncReplyPriority(0),
     mDispatchingSyncMessage(false),
     mDispatchingSyncMessagePriority(0),
     mDispatchingAsyncMessage(false),
     mDispatchingAsyncMessagePriority(0),
     mCurrentTransaction(0),
     mTimedOutMessageSeqno(0),
+    mTimedOutMessagePriority(0),
     mRecvdErrors(0),
     mRemoteStackDepthGuess(false),
     mSawInterruptOutMsg(false),
     mIsWaitingForIncoming(false),
     mAbortOnError(false),
     mBlockScripts(false),
     mFlags(REQUIRE_DEFAULT),
     mPeerPidSet(false),
@@ -1034,16 +1035,17 @@ MessageChannel::Send(Message* aMsg, Mess
                 mRecvdErrors--;
                 return false;
             }
             if (mRecvd) {
                 break;
             }
 
             mTimedOutMessageSeqno = seqno;
+            mTimedOutMessagePriority = prio;
             return false;
         }
     }
 
     MOZ_ASSERT(mRecvd);
     MOZ_ASSERT(mRecvd->is_reply(), "expected reply");
     MOZ_ASSERT(!mRecvd->is_reply_error());
     MOZ_ASSERT(mRecvd->type() == replyType, "wrong reply type");
@@ -1403,17 +1405,32 @@ MessageChannel::DispatchSyncMessage(cons
     // no longer blocked.
     MOZ_ASSERT_IF(prio > IPC::Message::PRIORITY_NORMAL, NS_IsMainThread());
     MaybeScriptBlocker scriptBlocker(this, prio > IPC::Message::PRIORITY_NORMAL);
 
     MessageChannel* dummy;
     MessageChannel*& blockingVar = ShouldBlockScripts() ? gParentProcessBlocker : dummy;
 
     Result rv;
-    {
+    if (mTimedOutMessageSeqno && mTimedOutMessagePriority >= prio) {
+        // If the other side sends a message in response to one of our messages
+        // that we've timed out, then we reply with an error.
+        //
+        // We do this because want to avoid a situation where we process an
+        // incoming message from the child here while it simultaneously starts
+        // processing our timed-out CPOW. It's very bad for both sides to
+        // be processing sync messages concurrently.
+        //
+        // The only exception is if the incoming message has urgent priority and
+        // our timed-out message had only high priority. In that case it's safe
+        // to process the incoming message because we know that the child won't
+        // process anything (the child will defer incoming messages when waiting
+        // for a response to its urgent message).
+        rv = MsgNotAllowed;
+    } else {
         AutoSetValue<MessageChannel*> blocked(blockingVar, this);
         AutoSetValue<bool> sync(mDispatchingSyncMessage, true);
         AutoSetValue<int> prioSet(mDispatchingSyncMessagePriority, prio);
         rv = mListener->OnMessageReceived(aMsg, aReply);
     }
 
     if (!MaybeHandleError(rv, aMsg, "DispatchSyncMessage")) {
         aReply = new Message();
@@ -2112,18 +2129,20 @@ MessageChannel::CancelCurrentTransaction
     // see if mCurrentTransaction is 0 before examining DispatchSyncMessage.
 }
 
 void
 MessageChannel::CancelCurrentTransaction()
 {
     MonitorAutoLock lock(*mMonitor);
     if (mCurrentTransaction) {
+        CancelMessage *cancel = new CancelMessage();
+        cancel->set_transaction_id(mCurrentTransaction);
+        mLink->SendMessage(cancel);
         CancelCurrentTransactionInternal();
-        mLink->SendMessage(new CancelMessage());
     }
 }
 
 void
 CancelCPOWs()
 {
     if (gParentProcessBlocker) {
         mozilla::Telemetry::Accumulate(mozilla::Telemetry::IPC_TRANSACTION_CANCEL, true);
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -625,16 +625,17 @@ class MessageChannel : HasResultCodes
     // side is responsible for replying to all sync messages sent by the other
     // side when it dispatches the timed out message. The response is always an
     // error.
     //
     // A message is only timed out if it initiated a transaction. This avoids
     // hitting a lot of corner cases with message nesting that we don't really
     // care about.
     int32_t mTimedOutMessageSeqno;
+    int mTimedOutMessagePriority;
 
     // If waiting for the reply to a sync out-message, it will be saved here
     // on the I/O thread and then read and cleared by the worker thread.
     nsAutoPtr<Message> mRecvd;
 
     // If a sync message reply that is an error arrives, we increment this
     // counter rather than storing it in mRecvd.
     size_t mRecvdErrors;
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -190,16 +190,46 @@ function ArrayStaticSome(list, callbackf
     if (arguments.length < 2)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.some');
     if (!IsCallable(callbackfn))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn));
     var T = arguments.length > 2 ? arguments[2] : void 0;
     return callFunction(ArraySome, list, callbackfn, T);
 }
 
+/* ES6 draft 2016-1-15 22.1.3.25 Array.prototype.sort (comparefn) */
+function ArraySort(comparefn) {
+    /* Step 1. */
+    var O = ToObject(this);
+
+    /* Step 2. */
+    var len = TO_UINT32(O.length);
+
+    /* 22.1.3.25.1 Runtime Semantics: SortCompare( x, y ) */
+    var wrappedCompareFn = comparefn;
+    comparefn = function(x, y) {
+        /* Steps 1-3. */
+        if (x === undefined) {
+            if (y === undefined)
+                return 0;
+           return 1;
+        }
+        if (y === undefined)
+            return -1;
+
+        /* Step 4.a. */
+        var v = ToNumber(wrappedCompareFn(x, y));
+
+        /* Step 4.b-c. */
+        return v !== v ? 0 : v;
+    }
+
+    return MergeSort(O, len, comparefn);
+}
+
 /* ES5 15.4.4.18. */
 function ArrayForEach(callbackfn/*, thisArg*/) {
     /* Step 1. */
     var O = ToObject(this);
 
     /* Steps 2-3. */
     var len = TO_UINT32(O.length);
 
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/Sorting.js
@@ -0,0 +1,185 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// We use varying sorts across the self-hosted codebase. All sorts are
+// consolidated here to avoid confusion and re-implementation of existing
+// algorithms.
+
+// For sorting small arrays.
+function InsertionSort(array, from, to, comparefn) {
+    var item, swap;
+    for (var i = from + 1; i <= to; i++) {
+        item = array[i];
+        for (var j = i - 1; j >= from; j--) {
+            swap = array[j];
+            if (comparefn(swap, item) <= 0)
+                break;
+            array[j + 1] = swap;
+        }
+        array[j + 1] = item;
+    }
+}
+
+function SwapArrayElements(array, i, j) {
+    var swap = array[i];
+    array[i] = array[j];
+    array[j] = swap;
+}
+
+// A helper function for MergeSort.
+function Merge(array, start, mid, end, lBuffer, rBuffer, comparefn) {
+    var i, j, k;
+
+    var sizeLeft = mid - start + 1;
+    var sizeRight =  end - mid;
+
+    // Copy our virtual arrays into separate buffers.
+    for (i = 0; i < sizeLeft; i++)
+        lBuffer[i] = array[start + i];
+
+    for (j = 0; j < sizeRight; j++)
+        rBuffer[j] = array[mid + 1 + j];
+
+    i = 0;
+    j = 0;
+    k = start;
+    while (i < sizeLeft && j < sizeRight) {
+        if (comparefn(lBuffer[i], rBuffer[j]) <= 0) {
+            array[k] = lBuffer[i];
+            i++;
+        } else {
+            array[k] = rBuffer[j];
+            j++;
+        }
+        k++;
+    }
+
+    // Empty out any remaining elements in the buffer.
+    while (i < sizeLeft) {
+        array[k] = lBuffer[i];
+        i++;
+        k++;
+    }
+
+    while (j < sizeRight) {
+        array[k] = rBuffer[j];
+        j++;
+        k++;
+    }
+}
+
+// Iterative, bottom up, mergesort.
+function MergeSort(array, len, comparefn) {
+    // Insertion sort for small arrays, where "small" is defined by performance
+    // testing.
+    if (len < 24) {
+       InsertionSort(array, 0, len - 1, comparefn);
+       return array;
+    }
+
+    // We do all of our allocating up front
+    var lBuffer = new List();
+    var rBuffer = new List();
+    var mid, end, endOne, endTwo;
+
+    for (var windowSize = 1; windowSize < len; windowSize = 2*windowSize) {
+        for (var start = 0; start < len - 1; start += 2*windowSize) {
+            assert(windowSize < len, "The window size is larger than the array length!");
+            // The midpoint between the two subarrays.
+            mid = start + windowSize - 1;
+            // To keep from going over the edge.
+            end = start + 2 * windowSize - 1;
+            end = end < len - 1 ? end : len - 1;
+            // Skip lopsided runs to avoid doing useless work
+            if (mid > end)
+                continue;
+            Merge(array, start, mid, end, lBuffer, rBuffer, comparefn);
+        }
+    }
+    return array;
+}
+
+// Rearranges the elements in array[from:to + 1] and returns an index j such that:
+// - from < j < to
+// - each element in array[from:j] is less than or equal to array[j]
+// - each element in array[j + 1:to + 1] greater than or equal to array[j].
+function Partition(array, from, to, comparefn) {
+    assert(to - from >= 3, "Partition will not work with less than three elements");
+
+    var medianIndex = (from + to) >> 1;
+
+    var i = from + 1;
+    var j = to;
+
+    SwapArrayElements(array, medianIndex, i);
+
+    // Median of three pivot selection.
+    if (comparefn(array[from], array[to]) > 0)
+        SwapArrayElements(array, from, to);
+
+    if (comparefn(array[i], array[to]) > 0)
+        SwapArrayElements(array, i, to);
+
+    if (comparefn(array[from], array[i]) > 0)
+        SwapArrayElements(array, from, i);
+
+    var pivotIndex = i;
+
+    // Hoare partition method.
+    for(;;) {
+        do i++; while (comparefn(array[i], array[pivotIndex]) < 0);
+        do j--; while (comparefn(array[j], array[pivotIndex]) > 0);
+        if (i > j)
+            break;
+        SwapArrayElements(array, i, j);
+    }
+
+    SwapArrayElements(array, pivotIndex, j);
+    return j;
+}
+
+// In-place QuickSort.
+function QuickSort(array, len, comparefn) {
+    // Managing the stack ourselves seems to provide a small performance boost.
+    var stack = new List();
+    var top = 0;
+
+    var start = 0;
+    var end   = len - 1;
+
+    var pivotIndex, i, j, leftLen, rightLen;
+
+    for (;;) {
+        // Insertion sort for the first N elements where N is some value
+        // determined by performance testing.
+        if (end - start <= 23) {
+            InsertionSort(array, start, end, comparefn);
+            if (top < 1)
+                break;
+            end   = stack[--top];
+            start = stack[--top];
+        } else {
+            pivotIndex = Partition(array, start, end, comparefn);
+
+            // Calculate the left and right sub-array lengths and save
+            // stack space by directly modifying start/end so that
+            // we sort the longest of the two during the next iteration.
+            // This reduces the maximum stack size to log2(len).
+            leftLen = (pivotIndex - 1) - start;
+            rightLen = end - (pivotIndex + 1);
+
+            if (rightLen > leftLen) {
+                stack[top++] = start;
+                stack[top++] = pivotIndex - 1;
+                start = pivotIndex + 1;
+            } else {
+                stack[top++] = pivotIndex + 1;
+                stack[top++] = end;
+                end = pivotIndex - 1;
+            }
+
+        }
+    }
+    return array;
+}
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -934,122 +934,16 @@ function TypedArraySome(callbackfn, this
         if (testResult)
             return true;
     }
 
     // Step 10.
     return false;
 }
 
-// For sorting small arrays
-function InsertionSort(array, from, to, comparefn) {
-    var item, swap;
-    for (var i = from + 1; i <= to; i++) {
-        item = array[i];
-        for (var j = i - 1; j >= from; j--) {
-            swap = array[j];
-            if (comparefn(swap, item) <= 0)
-                break
-            array[j + 1] = swap;
-        }
-        array[j + 1] = item;
-    }
-}
-
-function SwapArrayElements(array, i, j) {
-    var swap = array[i];
-    array[i] = array[j];
-    array[j] = swap;
-}
-
-// Rearranges the elements in array[from:to + 1] and returns an index j such that:
-// - from < j < to
-// - each element in array[from:j] is less than or equal to array[j]
-// - each element in array[j + 1:to + 1] greater than or equal to array[j].
-function Partition(array, from, to, comparefn) {
-    assert(to - from >= 3,
-           "Partition will not work with less than three elements");
-
-    var median_i = (from + to) >> 1;
-
-    var i = from + 1;
-    var j = to;
-
-    SwapArrayElements(array, median_i, i);
-
-    // Median of three pivot selection
-    if (comparefn(array[from], array[to]) > 0)
-        SwapArrayElements(array, from, to);
-
-    if (comparefn(array[i], array[to]) > 0)
-        SwapArrayElements(array, i, to);
-
-    if (comparefn(array[from], array[i]) > 0)
-        SwapArrayElements(array, from, i);
-
-    var pivot_i = i;
-
-    // Hoare partition method
-    for(;;) {
-        do i++; while (comparefn(array[i], array[pivot_i]) < 0);
-        do j--; while (comparefn(array[j], array[pivot_i]) > 0);
-        if (i > j)
-            break;
-        SwapArrayElements(array, i, j);
-    }
-
-    SwapArrayElements(array, pivot_i, j);
-    return j;
-}
-
-// In-place QuickSort
-function QuickSort(array, len, comparefn) {
-    // Managing the stack ourselves seems to provide a small performance boost
-    var stack = new List();
-    var top = 0;
-
-    var start = 0;
-    var end   = len - 1;
-
-    var pivot_i, i, j, l_len, r_len;
-
-    for (;;) {
-        // Insertion sort for the first N elements where N is some value
-        // determined by performance testing.
-        if (end - start <= 23) {
-            InsertionSort(array, start, end, comparefn);
-            if (top < 1)
-                break;
-            end   = stack[--top];
-            start = stack[--top];
-        } else {
-            pivot_i = Partition(array, start, end, comparefn);
-
-            // Calculate the left and right sub-array lengths and save
-            // stack space by directly modifying start/end so that
-            // we sort the longest of the two during the next iteration.
-            // This reduces the maximum stack size to log2(len)
-            l_len = (pivot_i - 1) - start;
-            r_len = end - (pivot_i + 1);
-
-            if (r_len > l_len) {
-                stack[top++] = start;
-                stack[top++] = pivot_i - 1;
-                start = pivot_i + 1;
-            } else {
-                stack[top++] = pivot_i + 1;
-                stack[top++] = end;
-                end = pivot_i - 1;
-            }
-
-        }
-    }
-    return array;
-}
-
 // ES6 draft 20151210 22.2.3.26
 // Cases are ordered according to likelihood of occurrence
 // as opposed to the ordering in the spec.
 function TypedArrayCompare(x, y) {
     // Step 1.
     assert(typeof x === "number" && typeof y === "number",
            "x and y are not numbers.");
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2897,34 +2897,16 @@ elif test "$GNU_CC"; then
         MOZ_C_SUPPORTS_WARNING(-W, no-error=coverage-mismatch, ac_c_has_noerror_coverage_mismatch)
         MOZ_CXX_SUPPORTS_WARNING(-W, no-error=coverage-mismatch, ac_cxx_has_noerror_coverage_mismatch)
         MOZ_C_SUPPORTS_WARNING(-W, no-error=free-nonheap-object, ac_c_has_noerror_free_nonheap_object)
         MOZ_CXX_SUPPORTS_WARNING(-W, no-error=free-nonheap-object, ac_cxx_has_noerror_free_nonheap_object)
     fi
 fi
 
 dnl ========================================================
-dnl = Enable DMD
-dnl ========================================================
-
-MOZ_ARG_ENABLE_BOOL(dmd,
-[  --enable-dmd            Enable DMD; also enables jemalloc and replace-malloc],
-    MOZ_DMD=1,
-    MOZ_DMD= )
-
-if test "$MOZ_DMD"; then
-    AC_DEFINE(MOZ_DMD)
-
-    if test "${CPU_ARCH}" = "arm"; then
-        CFLAGS="$CFLAGS -funwind-tables"
-        CXXFLAGS="$CXXFLAGS -funwind-tables"
-    fi
-fi
-
-dnl ========================================================
 dnl = Enable jemalloc
 dnl ========================================================
 
 LOCAL_MOZ_MEMORY=1
 MOZ_ARG_DISABLE_BOOL(jemalloc,
 [  --disable-jemalloc       Don't replace memory allocator with jemalloc],
     LOCAL_MOZ_MEMORY=)
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -13,21 +13,16 @@
 #include "jsgc.h"
 
 #include "gc/Heap.h"
 #include "gc/Nursery.h"
 #include "gc/Statistics.h"
 #include "gc/StoreBuffer.h"
 #include "gc/Tracer.h"
 
-/* Perform validation of incremental marking in debug builds but not on B2G. */
-#if defined(DEBUG) && !defined(MOZ_B2G)
-#define JS_GC_MARKING_VALIDATION
-#endif
-
 namespace js {
 
 class AutoLockGC;
 class VerifyPreTracer;
 
 namespace gc {
 
 typedef Vector<JS::Zone*, 4, SystemAllocPolicy> ZoneVector;
@@ -1177,17 +1172,17 @@ class GCRuntime
 
     /*
      * Incremental compacting state.
      */
     bool startedCompacting;
     js::gc::ZoneList zonesToMaybeCompact;
     ArenaHeader* relocatedArenasToRelease;
 
-#ifdef JS_GC_MARKING_VALIDATION
+#ifdef JS_GC_ZEAL
     js::gc::MarkingValidator* markingValidator;
 #endif
 
     /*
      * Indicates that a GC slice has taken place in the middle of an animation
      * frame, rather than at the beginning. In this case, the next slice will be
      * delayed so that we don't get back-to-back slices.
      */
--- a/js/src/jit-test/tests/self-hosting/invoke-self-hosted-with-primitive-this.js
+++ b/js/src/jit-test/tests/self-hosting/invoke-self-hosted-with-primitive-this.js
@@ -1,7 +1,7 @@
 try {
 	[0,0].sort(Array.some)
 	"".replace(RegExp(), Array.reduce)
 } catch (error) {
-	if (!(error instanceof TypeError && error.message == "0 is not a function"))
+	if (!(error instanceof TypeError && /^\w is not a function$/.test(error.message)))
 		throw error;
-}
\ No newline at end of file
+}
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1812,16 +1812,53 @@ js::array_sort(JSContext* cx, unsigned a
     } else {
         fval.setNull();
     }
 
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
+    ComparatorMatchResult comp = MatchNumericComparator(cx, fval);
+    if (comp == Match_Failure)
+        return false;
+
+    if (!fval.isNull() && comp == Match_None) {
+        /*
+         * Non-optimized user supplied comparators perform much better when
+         * called from within a self-hosted sorting function.
+         */
+        RootedAtom selfHostedSortAtom(cx, Atomize(cx, "ArraySort", 9));
+        RootedPropertyName selfHostedSortName(cx, selfHostedSortAtom->asPropertyName());
+        RootedValue selfHostedSortValue(cx);
+
+        if (!GlobalObject::getIntrinsicValue(cx, cx->global(), selfHostedSortName,
+            &selfHostedSortValue)) {
+            return false;
+        }
+
+        MOZ_ASSERT(selfHostedSortValue.isObject());
+        MOZ_ASSERT(selfHostedSortValue.toObject().is<JSFunction>());
+
+        InvokeArgs iargs(cx);
+
+        if (!iargs.init(1))
+            return false;
+
+        iargs.setCallee(selfHostedSortValue);
+        iargs.setThis(args.thisv());
+        iargs[0].set(fval);
+
+        if (!Invoke(cx, iargs))
+            return false;
+
+        args.rval().set(iargs.rval());
+        return true;
+    }
+
     uint32_t len;
     if (!GetLengthProperty(cx, obj, &len))
         return false;
     if (len < 2) {
         /* [] and [a] remain unchanged when sorted. */
         args.rval().setObject(*obj);
         return true;
     }
@@ -1912,37 +1949,23 @@ js::array_sort(JSContext* cx, unsigned a
                                SortComparatorLexicographicInt32())) {
                     return false;
                 }
             } else {
                 if (!SortLexicographically(cx, &vec, n))
                     return false;
             }
         } else {
-            ComparatorMatchResult comp = MatchNumericComparator(cx, fval);
-            if (comp == Match_Failure)
-                return false;
-
-            if (comp != Match_None) {
-                if (allInts) {
-                    JS_ALWAYS_TRUE(vec.resize(n * 2));
-                    if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorInt32s[comp]))
-                        return false;
-                } else {
-                    if (!SortNumerically(cx, &vec, n, comp))
-                        return false;
-                }
+            if (allInts) {
+                JS_ALWAYS_TRUE(vec.resize(n * 2));
+                if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorInt32s[comp]))
+                    return false;
             } else {
-                FastInvokeGuard fig(cx, fval);
-                JS_ALWAYS_TRUE(vec.resize(n * 2));
-                if (!MergeSort(vec.begin(), n, vec.begin() + n,
-                               SortComparatorFunction(cx, fval, fig)))
-                {
+                if (!SortNumerically(cx, &vec, n, comp))
                     return false;
-                }
             }
         }
 
         if (!InitArrayElements(cx, obj, 0, uint32_t(n), vec.begin(), ShouldUpdateTypes::DontUpdate))
             return false;
     }
 
     /* Set undefs that sorted after the rest of elements. */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1145,17 +1145,17 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
     zoneGroups(nullptr),
     currentZoneGroup(nullptr),
     sweepZone(nullptr),
     sweepKindIndex(0),
     abortSweepAfterCurrentGroup(false),
     arenasAllocatedDuringSweep(nullptr),
     startedCompacting(false),
     relocatedArenasToRelease(nullptr),
-#ifdef JS_GC_MARKING_VALIDATION
+#ifdef JS_GC_ZEAL
     markingValidator(nullptr),
 #endif
     interFrameGC(false),
     defaultTimeBudget_(SliceBudget::UnlimitedTimeBudget),
     incrementalAllowed(true),
     generationalDisabled(0),
     compactingEnabled(true),
     compactingDisabledCount(0),
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -706,16 +706,17 @@ selfhosted.inputs = [
     'builtin/Map.js',
     'builtin/Module.js',
     'builtin/Number.js',
     'builtin/Object.js',
     'builtin/Reflect.js',
     'builtin/RegExp.js',
     'builtin/String.js',
     'builtin/Set.js',
+    'builtin/Sorting.js',
     'builtin/TypedArray.js',
     'builtin/TypedObject.js',
     'builtin/WeakSet.js'
 ]
 
 if CONFIG['JS_HAS_CTYPES']:
     if CONFIG['MOZ_NATIVE_FFI']:
         CXXFLAGS += CONFIG['MOZ_FFI_CFLAGS']
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/sort_basics.js
@@ -0,0 +1,34 @@
+// Note: failed runs should include their "SEED" value in error messages,
+// setting "const SEED" to that value will recreate the data from any such run.
+const SEED = (Math.random() * 10) + 1;
+
+// Create an array filled with random values, 'size' is the desired length of
+// the array and 'seed' is an initial value supplied to a pseudo-random number
+// generator.
+function genRandomArray(size, seed) {
+    return Array.from(XorShiftGenerator(seed, size));
+}
+
+function SortTest(size, seed) {
+    let arrOne    = genRandomArray(size, seed);
+    let arrTwo    = Array.from(arrOne);
+    let arrThree  = Array.from(arrOne);
+
+    // Test numeric comparators against typed array sort.
+    assertDeepEq(Array.from((Int32Array.from(arrOne)).sort()),
+                 arrTwo.sort((x, y) => (x - y)),
+                 `The arr is not properly sorted! seed: ${SEED}`);
+
+    // Use multiplication to kill comparator optimization and trigger
+    // self-hosted sorting.
+    assertDeepEq(Array.from((Int32Array.from(arrOne)).sort()),
+                 arrThree.sort((x, y) => (1*x - 1*y)),
+                 `The arr is not properly sorted! seed: ${SEED}`);
+}
+
+SortTest(2048, SEED);
+SortTest(16, SEED);
+SortTest(0, SEED);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/sort_small.js
@@ -0,0 +1,33 @@
+// Sort every possible permutation of some arrays.
+function sortAllPermutations(data, comparefn) {
+    for (let permutation of Permutations(Array.from(data))) {
+        let sorted = (Array.from(permutation)).sort(comparefn);
+        for (let i in sorted) {
+            assertEq(sorted[i], data[i],
+            [`[${permutation}].sort(${comparefn})`,
+            `returned ${sorted}, expected ${data}`].join(' '));
+        }
+    }
+}
+
+let lex  = [2112, "bob", "is", "my", "name"];
+let nans = [1/undefined, NaN, Number.NaN]
+
+let num1  = [-11, 0, 0, 100, 101];
+let num2  = [-11, 100, 201234.23, undefined, undefined];
+
+sortAllPermutations(lex);
+sortAllPermutations(nans);
+
+sortAllPermutations(nans, (x, y) => x - y);
+// Multiplication kills comparator optimization.
+sortAllPermutations(nans, (x, y) => (1*x - 1*y));
+
+sortAllPermutations(num1, (x, y) => x - y);
+sortAllPermutations(num1, (x, y) => (1*x - 1*y));
+
+sortAllPermutations(num2, (x, y) => x - y);
+sortAllPermutations(num2, (x, y) => (1*x - 1*y));
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/ecma_6/TypedArray/sort_basics.js
+++ b/js/src/tests/ecma_6/TypedArray/sort_basics.js
@@ -1,37 +1,23 @@
 // Note: failed runs should include their "SEED" value in error messages,
 // setting "const SEED" to that value will recreate the data from any such run.
 const SEED = (Math.random() * 10) + 1;
 
-// An xorshift pseudo-random number generator see:
-// https://en.wikipedia.org/wiki/Xorshift#xorshift.2A
-// This generator will always produce a value, n, where
-// 0 <= n <= 255
-function *xorShiftGenerator(seed, size) {
-    let x = seed;
-    for (let i = 0; i < size; i++) {
-        x ^= x >> 12;
-        x ^= x << 25;
-        x ^= x >> 27;
-        yield x % 256;
-    }
-}
-
 // Fill up an array buffer with random values and return it in raw form.
 // 'size' is the desired length of the view we will place atop the buffer,
 // 'width' is the bit-width of the view we plan on placing atop the buffer,
 // and 'seed' is an initial value supplied to a pseudo-random number generator.
 function genRandomArrayBuffer(size, width, seed) {
     let buf = new ArrayBuffer((width / 8) * size);
     let arr = new Uint8Array(buf);
     let len = 0;
     // We generate a random number, n, where 0 <= n <= 255 for every space
     // available in our buffer.
-    for (let n of xorShiftGenerator(seed, buf.byteLength))
+    for (let n of XorShiftGenerator(seed, buf.byteLength))
         arr[len++] = n;
     return buf;
 }
 
 // Because we can generate any possible combination of bits, some floating point
 // entries will take on -Infinity, Infinity, and NaN values. This function ensures
 // that a is <= b, where, like the default comparator,  -Infinity < Infinity and
 // every non-NaN < NaN.
--- a/js/src/tests/ecma_6/TypedArray/sort_small.js
+++ b/js/src/tests/ecma_6/TypedArray/sort_small.js
@@ -1,27 +1,8 @@
-function swapElements(arr, i, j) {
-    var swap = arr[i];
-    arr[i] = arr[j];
-    arr[j] = swap;
-}
-
-// Yield every permutation of the elements in some iterable.
-function *permutations(items) {
-    if (items.length == 0) {
-        yield [];
-    } else {
-        for (let i = 0; i < items.length; i++) {
-            swapElements(items, 0, i);
-            for (let e of permutations(items.slice(1, items.length)))
-                yield [items[0]].concat(e);
-        }
-    }
-}
-
 // Pre-sorted test data, it's important that these arrays remain in ascending order.
 let i32  = [-2147483648, -320000, -244000, 2147483647]
 let u32  = [0, 987632, 4294967295]
 let i16  = [-32768, -999, 1942, 32767]
 let u16  = [0, 65535, 65535]
 let i8   = [-128, 127]
 let u8   = [255]
 
@@ -30,17 +11,17 @@ let u8   = [255]
 // Float Arrays are used because all other types convert -0 and NaN to +0.
 let f32  = [-2147483647, -2147483646.99, -0, 0, 2147483646.99, NaN]
 let f64  = [-2147483646.99, -0, 0, 4147483646.99, NaN]
 let nans = [1/undefined, NaN, Number.NaN]
 
 // Sort every possible permutation of an arrays
 function sortAllPermutations(dataType, testData) {
     let reference = new dataType(testData);
-    for (let permutation of permutations(testData))
+    for (let permutation of Permutations(testData))
         assertDeepEq((new dataType(permutation)).sort(), reference);
 }
 
 sortAllPermutations(Int32Array,   i32);
 sortAllPermutations(Uint32Array,  u32);
 sortAllPermutations(Int16Array,   i16);
 sortAllPermutations(Uint16Array,  u16);
 sortAllPermutations(Int8Array,    i8);
--- a/js/src/tests/shell.js
+++ b/js/src/tests/shell.js
@@ -368,16 +368,50 @@ function enterFunc (funcName)
 {
   if (!funcName.match(/\(\)$/))
     funcName += "()";
 
   callStack.push(funcName);
 }
 
 /*
+ * An xorshift pseudo-random number generator see:
+ * https://en.wikipedia.org/wiki/Xorshift#xorshift.2A
+ * This generator will always produce a value, n, where
+ * 0 <= n <= 255
+ */
+function *XorShiftGenerator(seed, size) {
+    let x = seed;
+    for (let i = 0; i < size; i++) {
+        x ^= x >> 12;
+        x ^= x << 25;
+        x ^= x >> 27;
+        yield x % 256;
+    }
+}
+
+/*
+ * Yield every permutation of the elements in some iterable.
+ */
+function *Permutations(items) {
+    if (items.length == 0) {
+        yield [];
+    } else {
+        let swap;
+        for (let i = 0; i < items.length; i++) {
+            swap = items[0];
+            items[0] = items[i];
+            items[i] = swap;
+            for (let e of Permutations(items.slice(1, items.length)))
+                yield [items[0]].concat(e);
+        }
+    }
+}
+
+/*
  * Pops the top funcName off the call stack.  funcName is optional, and can be
  * used to check push-pop balance.
  */
 function exitFunc (funcName)
 {
   var lastFunc = callStack.pop();
 
   if (funcName)
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1061,17 +1061,18 @@ void
 SavedStacks::clear()
 {
     frames.clear();
 }
 
 size_t
 SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
-    return frames.sizeOfExcludingThis(mallocSizeOf);
+    return frames.sizeOfExcludingThis(mallocSizeOf) +
+           pcLocationMap.sizeOfExcludingThis(mallocSizeOf);
 }
 
 bool
 SavedStacks::insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFrame frame,
                           unsigned maxFrameCount)
 {
     // In order to lookup a cached SavedFrame object, we need to have its parent
     // SavedFrame, which means we need to walk the stack from oldest frame to
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1851,17 +1851,17 @@ IsFrameReceivingPointerEvents(nsIFrame* 
 // the results of hit testing.
 struct FramesWithDepth
 {
   explicit FramesWithDepth(float aDepth) :
     mDepth(aDepth)
   {}
 
   bool operator<(const FramesWithDepth& aOther) const {
-    if (mDepth != aOther.mDepth) {
+    if (!FuzzyEqual(mDepth, aOther.mDepth, 0.1f)) {
       // We want to sort so that the shallowest item (highest depth value) is first
       return mDepth > aOther.mDepth;
     }
     return this < &aOther;
   }
   bool operator==(const FramesWithDepth& aOther) const {
     return this == &aOther;
   }
@@ -1914,34 +1914,37 @@ void nsDisplayList::HitTest(nsDisplayLis
     // Pop element off the end of the buffer. We want to shorten the buffer
     // so that recursive calls to HitTest have more buffer space.
     item = aState->mItemBuffer[i];
     aState->mItemBuffer.SetLength(i);
 
     bool snap;
     nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
     auto itemType = item->GetType();
-    bool alwaysIntersect =
+    bool same3DContext =
       (itemType == nsDisplayItem::TYPE_TRANSFORM &&
        static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
       (itemType == nsDisplayItem::TYPE_PERSPECTIVE &&
        static_cast<nsDisplayPerspective*>(item)->Frame()->Extend3DContext());
-    if (alwaysIntersect &&
+    if (same3DContext &&
         !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
+      if (!item->GetClip().MayIntersect(aRect)) {
+        continue;
+      }
       nsAutoTArray<nsIFrame*, 1> neverUsed;
       // Start gethering leaves of the 3D rendering context, and
       // append leaves at the end of mItemBuffer.  Leaves are
       // processed at following iterations.
       aState->mInPreserves3D = true;
       item->HitTest(aBuilder, aRect, aState, &neverUsed);
       aState->mInPreserves3D = false;
       i = aState->mItemBuffer.Length();
       continue;
     }
-    if (alwaysIntersect || item->GetClip().MayIntersect(r)) {
+    if (same3DContext || item->GetClip().MayIntersect(r)) {
       nsAutoTArray<nsIFrame*, 16> outFrames;
       item->HitTest(aBuilder, aRect, aState, &outFrames);
 
       // For 3d transforms with preserve-3d we add hit frames into the temp list
       // so we can sort them later, otherwise we add them directly to the output list.
       nsTArray<nsIFrame*> *writeFrames = aOutFrames;
       if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
           static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 # Android: SLOW_DIRECTORY; Mulet: bug 1048441, bug 1087611, bug 1112988, etc.
 skip-if = toolkit == 'android' || buildapp == 'mulet'
 support-files =
   Ahem.ttf
   border_radius_hit_testing_iframe.html
   preserve3d_sorting_hit_testing_iframe.html
+  preserve3d_sorting_hit_testing2_iframe.html
   image_rgrg-256x256.png
   image_rrgg-256x256.png
   bug369950-subframe.xml
   file_bug842853.sjs
   file_bug842853.html
   ../../../dom/plugins/test/mochitest/plugin-utils.js
   bug558663.html
   bug956530-1.html
@@ -36,16 +37,17 @@ support-files =
   bug1132768-1-ref.html
   selection-utils.js
   multi-range-user-select.html
   multi-range-user-select-ref.html
   multi-range-script-select.html
   multi-range-script-select-ref.html
 
 [test_preserve3d_sorting_hit_testing.html]
+[test_preserve3d_sorting_hit_testing2.html]
 [test_after_paint_pref.html]
 [test_bug993936.html]
 skip-if = e10s
 [test_border_radius_hit_testing.html]
 [test_bug66619.html]
 [test_bug93077-1.html]
 [test_bug93077-2.html]
 [test_bug93077-3.html]
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/preserve3d_sorting_hit_testing2_iframe.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style>
+body {
+  background: #333;
+  overflow: hidden;
+}
+
+::-webkit-scrollbar {
+  display: none;
+}
+
+div {
+  margin: 0;
+  padding: 0;
+  -webkit-transform-style: preserve-3d;
+  transform-style: preserve-3d;
+  position: absolute;
+}
+
+#container {
+  font-family: UnifrakturMaguntia;
+  width: 350px;
+  height: 70%;
+  max-height: 500px;
+  -webkit-perspective: 5000px;
+  perspective: 5000px;
+  transform: translate(-50%, -50%) rotateY(20deg);
+}
+
+#container p {
+  padding: 0 5px 0 5px;
+}
+
+#container hr {
+  margin: 0 20px 0 20px;
+}
+
+#content {
+  -ms-overflow-style: none;
+  overflow: -moz-scrollbars-none;
+  overflow-y: scroll;
+  height: 100%;
+  background: #fefee0;
+}
+
+
+#lorem {
+  font-size: 7em;
+  float: left;
+  color: red;
+  border: 1px solid black;
+  margin-right: 5px;
+}
+
+#tree {
+  float: right;
+  width: 10em;
+  height: 10em;
+  border: 1px solid black;
+  margin: 0 5px 0 2px;
+}
+</style>
+</head>
+<body>
+  <div id="container">
+    <div id="content">
+        <p>
+      <span id="lorem">L</span>orem ipsum dolor sit amet, consectetur adipiscing elit. Integer sagittis nisi urna, a ultrices est facilisis a. Morbi porttitor vulputate odio, eu lacinia nisi. Suspendisse felis sapien, facilisis nec ex in, blandit tincidunt tellus. Sed at commodo nunc. In nibh lectus, facilisis nec magna nec, bibendum egestas nunc. Nam varius lorem in fringilla cursus. Integer dignissim, lectus vitae sodales molestie, libero purus malesuada arcu, vitae facilisis nunc dolor non mi. In nunc tortor, tempor non pharetra vitae, mattis a purus. Nulla rhoncus vitae metus vel ornare. Nunc augue dui, suscipit ac urna vel, consectetur volutpat ipsum. Nunc ac nulla ut enim laoreet placerat. Sed luctus aliquam purus, sollicitudin blandit dui blandit id. Aenean venenatis risus dolor, at viverra urna aliquam non. Morbi sit amet pellentesque justo, eget viverra augue.
+      </p>
+      <p>&nbsp;&nbsp;&nbsp;&nbsp;
+      Praesent posuere ultricies orci sit amet lacinia. Suspendisse lacinia scelerisque risus, sodales egestas turpis cursus sed. Proin sed mollis mauris, vitae ultricies nibh. Nulla bibendum leo a mauris luctus, sit amet iaculis arcu blandit. Etiam pulvinar, odio et rutrum egestas, elit mi maximus ex, id elementum est tortor id turpis. Duis rhoncus et lorem vel maximus. Aenean at justo sagittis, aliquet eros eget, iaculis magna. Nam non orci congue, dapibus dui eget, sagittis nisl. Phasellus venenatis id est et tempor. Aenean condimentum tristique nibh sit amet varius. Vestibulum et lectus quis eros dapibus consectetur nec auctor dolor. Sed euismod eu felis aliquam fermentum. Donec lacinia fringilla erat, at eleifend velit tempus at.
+      </p>
+      <hr>
+      <p>&nbsp;&nbsp;&nbsp;&nbsp;
+      Cras justo turpis, vulputate eget venenatis sit amet, bibendum quis dolor. Cras at interdum libero. Quisque convallis rutrum magna in ultrices. Donec ut magna dolor. Mauris pulvinar ut sapien a posuere. Sed nisi elit, tincidunt vitae magna eu, dapibus suscipit purus. Maecenas tincidunt mollis eros et dictum. Duis est nulla, rhoncus tincidunt velit at, venenatis elementum velit. Phasellus lobortis sem tellus, id sodales quam dignissim nec. Phasellus pulvinar metus ex, nec gravida nunc elementum vel. Ut mattis varius fringilla. Phasellus imperdiet sit amet risus a elementum. Donec pulvinar ante sit amet massa blandit ullamcorper. Donec vitae malesuada nisl, et laoreet sem.
+      </p>
+      <p>&nbsp;&nbsp;&nbsp;&nbsp;
+      Suspendisse bibendum elit blandit arcu vulputate, nec hendrerit dui vehicula. Vestibulum porta finibus odio vitae maximus. Duis in vulputate risus. Donec mattis turpis ex, vitae semper sem ultrices eu. Aliquam in ex blandit erat ultrices sollicitudin. Vestibulum porta nisl in porttitor rutrum. Integer consectetur porttitor ligula facilisis malesuada. Proin placerat enim sed lacus commodo mollis nec eu arcu. In hac habitasse platea dictumst. Curabitur luctus est risus, sit amet fringilla nunc condimentum vel. Integer mauris lorem, molestie ut nisl sit amet, pellentesque mollis quam. Aliquam posuere purus non nisi molestie semper.
+      </p>
+      <hr>
+      <p>&nbsp;&nbsp;&nbsp;&nbsp;
+      Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris facilisis nisi diam, eu pulvinar ex sollicitudin sed. Maecenas sed eros id quam suscipit ultricies ut tincidunt quam. Donec iaculis, justo at fringilla laoreet, quam sem dapibus urna, ut eleifend odio eros et ligula. Proin urna ante, condimentum vitae sollicitudin sit amet, egestas ac nunc. Aenean sapien velit, porta a eros quis, iaculis dignissim felis. Suspendisse mollis vulputate metus vel interdum. Aliquam hendrerit elementum erat, sit amet commodo velit suscipit et. Sed semper sem at mauris rhoncus, id efficitur arcu molestie. Nam feugiat lorem pretium, consectetur felis et, fringilla dolor. Nunc dui velit, elementum non hendrerit nec, sagittis vitae odio. Curabitur nec leo tincidunt, pellentesque metus at, condimentum risus.
+      </p>
+    </div>
+  </div>
+</body>
+
+<script type="application/javascript">
+  window.onload = function() {
+    opener.child_opened(document);
+  };
+</script>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/test_preserve3d_sorting_hit_testing2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1241394
+-->
+<head>
+  <title>Test for Bug 1241394</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1241394">Mozilla Bug 1241394</a>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1241394 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+  var win;
+
+  window.child_opened = function(doc) {
+    var container= doc.getElementById("container");
+
+    isnot(doc.elementFromPoint(60, 50).id, container.id,
+          "point (50, 50): should not hit background");
+
+    win.close();
+    SimpleTest.finish();
+  }
+
+  win = window.open("preserve3d_sorting_hit_testing2_iframe.html");
+}
+
+
+</script>
+</pre>
+</body>
+</html>
--- a/media/mtransport/third_party/nICEr/src/ice/ice_socket.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_socket.c
@@ -60,17 +60,17 @@ static void nr_ice_socket_readable_cb(NR
 
     r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): Socket ready to read",sock->ctx->label);
 
     /* Re-arm first! */
     if (sock->type != NR_ICE_SOCKET_TYPE_STREAM_TCP)
       NR_ASYNC_WAIT(s,how,nr_ice_socket_readable_cb,cb_arg);
 
     if(r=nr_socket_recvfrom(sock->sock,buf,sizeof(buf),&len_s,0,&addr)){
-      if (r != R_WOULDBLOCK && (sock->type == NR_ICE_SOCKET_TYPE_STREAM_TURN)) {
+      if (r != R_WOULDBLOCK && (sock->type != NR_ICE_SOCKET_TYPE_DGRAM)) {
         /* Report this error upward. Bug 946423 */
         r_log(LOG_ICE,LOG_ERR,"ICE(%s): Error %d on reliable socket. Abandoning.",sock->ctx->label, r);
         NR_ASYNC_CANCEL(s, NR_ASYNC_WAIT_READ);
       }
       return;
     }
 
     /* Deal with the fact that sizeof(int) and sizeof(size_t) may not
--- a/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c
+++ b/media/mtransport/third_party/nICEr/src/net/nr_proxy_tunnel.c
@@ -42,23 +42,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #define MAX_HTTP_CONNECT_ADDR_SIZE 256
 #define MAX_HTTP_CONNECT_BUFFER_SIZE 1024
 #define MAX_ALPN_LENGTH 64
 #ifndef CRLF
 #define CRLF "\r\n"
 #endif
 #define END_HEADERS CRLF CRLF
 
+typedef enum {
+  PROXY_TUNNEL_NONE=0,
+  PROXY_TUNNEL_REQUESTED,
+  PROXY_TUNNEL_CONNECTED,
+  PROXY_TUNNEL_CLOSED,
+  PROXY_TUNNEL_FAILED
+} nr_socket_proxy_tunnel_state;
+
 typedef struct nr_socket_proxy_tunnel_ {
   nr_proxy_tunnel_config *config;
   nr_socket *inner;
   nr_transport_addr remote_addr;
-  int connect_requested;
-  int connect_answered;
-  int connect_failed;
+  nr_socket_proxy_tunnel_state state;
   char buffer[MAX_HTTP_CONNECT_BUFFER_SIZE];
   size_t buffered_bytes;
   void *resolver_handle;
 } nr_socket_proxy_tunnel;
 
 typedef struct nr_socket_wrapper_factory_proxy_tunnel_ {
   nr_proxy_tunnel_config *config;
 } nr_socket_wrapper_factory_proxy_tunnel;
@@ -138,17 +144,17 @@ static int send_http_connect(nr_socket_p
   }
 
   if (bytes_sent < offset) {
     /* TODO(bug 1116583): buffering and wait for */
     r_log(LOG_GENERIC,LOG_DEBUG,"send_http_connect should be buffering %lu", (unsigned long)bytes_sent);
     ABORT(R_IO_ERROR);
   }
 
-  sock->connect_requested = 1;
+  sock->state = PROXY_TUNNEL_REQUESTED;
 
   _status = 0;
 abort:
   return(_status);
 }
 
 static char *find_http_terminator(char *response, size_t len)
 {
@@ -168,16 +174,19 @@ static char *find_http_terminator(char *
 static int parse_http_response(char *begin, char *end, unsigned int *status)
 {
   size_t len = end - begin;
   char response[MAX_HTTP_CONNECT_BUFFER_SIZE + 1];
 
   // len should *never* be greater than nr_socket_proxy_tunnel::buffered_bytes.
   // Which in turn should never be greater nr_socket_proxy_tunnel::buffer size.
   assert(len <= MAX_HTTP_CONNECT_BUFFER_SIZE);
+  if (len > MAX_HTTP_CONNECT_BUFFER_SIZE) {
+    return R_BAD_DATA;
+  }
 
   memcpy(response, begin, len);
   response[len] = '\0';
 
   // http://www.rfc-editor.org/rfc/rfc7230.txt
   // status-line    = HTTP-version SP status-code SP reason-phrase CRLF
   // HTTP-version   = HTTP-name "/" DIGIT "." DIGIT
   // HTTP-name      = "HTTP" ; "HTTP", case-sensitive
@@ -244,16 +253,20 @@ static int nr_socket_proxy_tunnel_resolv
 
   if (proxy_addr) {
     r_log(LOG_GENERIC,LOG_DEBUG,"Resolved proxy address %s -> %s",
         sock->config->proxy_host, proxy_addr->as_string);
   }
   else {
     r_log(LOG_GENERIC,LOG_WARNING,"Failed to resolve proxy %s",
         sock->config->proxy_host);
+    /* TODO: Mozilla bug 1241758: because of the callback the return value goes
+     * nowhere, so we can't mark the candidate as failed, so everything depends
+     * on the overall timeouts in this case. */
+    sock->state = PROXY_TUNNEL_FAILED;
     ABORT(R_NOT_FOUND);
   }
 
   if ((r=nr_socket_connect(sock->inner, proxy_addr))) {
     ABORT(r);
   }
 
   _status = 0;
@@ -331,23 +344,30 @@ abort:
 int nr_socket_proxy_tunnel_write(void *obj, const void *msg, size_t len,
                                  size_t *written)
 {
   int r, _status;
   nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
 
   r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_write");
 
-  if (!sock->connect_requested) {
+  if (sock->state >= PROXY_TUNNEL_CLOSED) {
+    return R_FAILED;
+  }
+
+  if (sock->state == PROXY_TUNNEL_NONE) {
     if ((r=send_http_connect(sock))) {
       ABORT(r);
     }
   }
 
-  /* TODO (bug 1117984): we cannot assume it's safe to write until we receive a response. */
+  if (sock->state != PROXY_TUNNEL_CONNECTED) {
+    return R_WOULDBLOCK;
+  }
+
   if ((r=nr_socket_write(sock->inner, msg, len, written, 0))) {
     ABORT(r);
   }
 
   _status=0;
 abort:
   return(_status);
 }
@@ -361,21 +381,21 @@ int nr_socket_proxy_tunnel_read(void *ob
   size_t pending;
   nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
   unsigned int http_status;
 
   r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_read");
 
   *len = 0;
 
-  if (sock->connect_failed) {
+  if (sock->state >= PROXY_TUNNEL_CLOSED) {
     return R_FAILED;
   }
 
-  if (sock->connect_answered) {
+  if (sock->state == PROXY_TUNNEL_CONNECTED) {
     return nr_socket_read(sock->inner, buf, maxlen, len, 0);
   }
 
   if (sock->buffered_bytes >= sizeof(sock->buffer)) {
     r_log(LOG_GENERIC,LOG_ERR,"buffer filled waiting for CONNECT response");
     assert(sock->buffered_bytes == sizeof(sock->buffer));
     ABORT(R_INTERNAL);
   }
@@ -386,61 +406,63 @@ int nr_socket_proxy_tunnel_read(void *ob
   if ((r=nr_socket_read(sock->inner, sock->buffer + sock->buffered_bytes,
           maxlen_int, &bytes_read, 0))) {
     ABORT(r);
   }
 
   sock->buffered_bytes += bytes_read;
 
   if (http_term = find_http_terminator(sock->buffer, sock->buffered_bytes)) {
-    sock->connect_answered = 1;
-
     if ((r = parse_http_response(sock->buffer, http_term, &http_status))) {
       ABORT(r);
     }
 
     /* TODO (bug 1115934): Handle authentication challenges. */
     if (http_status < 200 || http_status >= 300) {
       r_log(LOG_GENERIC,LOG_ERR,"nr_socket_proxy_tunnel_read unable to connect %u",
             http_status);
       ABORT(R_FAILED);
     }
 
+    sock->state = PROXY_TUNNEL_CONNECTED;
+
     ptr = http_term + strlen(END_HEADERS);
     pending = sock->buffered_bytes - (ptr - sock->buffer);
 
     if (pending == 0) {
       ABORT(R_WOULDBLOCK);
     }
 
     assert(pending <= maxlen);
     *len = pending;
 
     memcpy(buf, ptr, *len);
   }
 
   _status=0;
 abort:
   if (_status && _status != R_WOULDBLOCK) {
-      sock->connect_failed = 1;
+      sock->state = PROXY_TUNNEL_FAILED;
   }
   return(_status);
 }
 
 int nr_socket_proxy_tunnel_close(void *obj)
 {
   nr_socket_proxy_tunnel *sock = (nr_socket_proxy_tunnel*)obj;
 
   r_log(LOG_GENERIC,LOG_DEBUG,"nr_socket_proxy_tunnel_close");
 
   if (sock->resolver_handle) {
     nr_resolver_cancel(sock->config->resolver, sock->resolver_handle);
     sock->resolver_handle = 0;
   }
 
+  sock->state = PROXY_TUNNEL_CLOSED;
+
   return nr_socket_close(sock->inner);
 }
 
 int nr_proxy_tunnel_config_create(nr_proxy_tunnel_config **configpp)
 {
   int _status;
   nr_proxy_tunnel_config *configp=0;
 
--- a/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
+++ b/media/mtransport/third_party/nICEr/src/stun/nr_socket_buffered_stun.c
@@ -519,16 +519,20 @@ abort:
 }
 
 static void nr_socket_buffered_stun_writable_cb(NR_SOCKET s, int how, void *arg)
 {
   nr_socket_buffered_stun *sock = (nr_socket_buffered_stun *)arg;
   int r,_status;
   nr_p_buf *n1, *n2;
 
+  if (sock->read_state == NR_ICE_SOCKET_READ_FAILED) {
+    ABORT(R_FAILED);
+  }
+
   /* Try to flush */
   STAILQ_FOREACH_SAFE(n1, &sock->pending_writes, entry, n2) {
     size_t written = 0;
 
     if ((r=nr_socket_write(sock->inner, n1->data + n1->r_offset,
                            n1->length - n1->r_offset,
                            &written, 0))) {
 
--- a/media/mtransport/transportlayer.cpp
+++ b/media/mtransport/transportlayer.cpp
@@ -44,24 +44,9 @@ void TransportLayer::SetState(State stat
     MOZ_MTLOG(state == TS_ERROR ? ML_ERROR : ML_DEBUG,
               file << ":" << line << ": " <<
               LAYER_INFO << "state " << state_ << "->" << state);
     state_ = state;
     SignalStateChange(this, state);
   }
 }
 
-nsresult TransportLayer::RunOnThread(nsIRunnable *event) {
-  if (target_) {
-    nsIThread *thr;
-
-    DebugOnly<nsresult> rv = NS_GetCurrentThread(&thr);
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-
-    if (target_ != thr) {
-      return target_->Dispatch(event, NS_DISPATCH_SYNC);
-    }
-  }
-
-  return event->Run();
-}
-
 }  // close namespace
--- a/media/mtransport/transportlayer.h
+++ b/media/mtransport/transportlayer.h
@@ -52,20 +52,16 @@ class TransportLayer : public sigslot::h
   virtual nsresult InitInternal() { return NS_OK; } // Called by Init
 
   // Called when inserted into a flow
   virtual void Inserted(TransportFlow *flow, TransportLayer *downward);
 
   // Downward interface
   TransportLayer *downward() { return downward_; }
 
-  // Dispatch a call onto our thread (or run on the same thread if
-  // thread is not set). This is always synchronous.
-  nsresult RunOnThread(nsIRunnable *event);
-
   // Get the state
   State state() const { return state_; }
   // Must be implemented by derived classes
   virtual TransportResult SendPacket(const unsigned char *data, size_t len) = 0;
 
   // Get the thread.
   const nsCOMPtr<nsIEventTarget> GetThread() const {
     return target_;
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -2083,36 +2083,37 @@ JsepSessionImpl::SetupDefaultCodecs()
                                     "PCMA",
                                     8000,
                                     1,
                                     8000 / 50,   // frequency / 50
                                     8 * 8000 * 1 // 8 * frequency * channels
                                     ));
 
   // Supported video codecs.
+  // Note: order here implies priority for building offers!
+  JsepVideoCodecDescription* vp9 = new JsepVideoCodecDescription(
+      "121",
+      "VP9",
+      90000
+      );
+  // Defaults for mandatory params
+  vp9->mConstraints.maxFs = 12288; // Enough for 2048x1536
+  vp9->mConstraints.maxFps = 60;
+  mSupportedCodecs.values.push_back(vp9);
+
   JsepVideoCodecDescription* vp8 = new JsepVideoCodecDescription(
       "120",
       "VP8",
       90000
       );
   // Defaults for mandatory params
   vp8->mConstraints.maxFs = 12288; // Enough for 2048x1536
   vp8->mConstraints.maxFps = 60;
   mSupportedCodecs.values.push_back(vp8);
 
-  JsepVideoCodecDescription* vp9 = new JsepVideoCodecDescription(
-      "121",
-      "VP9",
-      90000
-      );
-  // Defaults for mandatory params
-  vp9->mConstraints.maxFs = 12288; // Enough for 2048x1536
-  vp9->mConstraints.maxFps = 60;
-  mSupportedCodecs.values.push_back(vp9);
-
   JsepVideoCodecDescription* h264_1 = new JsepVideoCodecDescription(
       "126",
       "H264",
       90000
       );
   h264_1->mPacketizationMode = 1;
   // Defaults for mandatory params
   h264_1->mProfileLevelId = 0x42E00D;
--- a/media/webrtc/signaling/test/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/test/jsep_session_unittest.cpp
@@ -2651,18 +2651,18 @@ TEST_F(JsepSessionTest, ValidateOfferedC
 
   ASSERT_EQ(2U, outputSdp->GetMediaSectionCount());
   auto& video_section = outputSdp->GetMediaSection(1);
   ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType());
   auto& video_attrs = video_section.GetAttributeList();
   ASSERT_EQ(SdpDirectionAttribute::kSendrecv, video_attrs.GetDirection());
 
   ASSERT_EQ(4U, video_section.GetFormats().size());
-  ASSERT_EQ("120", video_section.GetFormats()[0]);
-  ASSERT_EQ("121", video_section.GetFormats()[1]);
+  ASSERT_EQ("121", video_section.GetFormats()[0]);
+  ASSERT_EQ("120", video_section.GetFormats()[1]);
   ASSERT_EQ("126", video_section.GetFormats()[2]);
   ASSERT_EQ("97", video_section.GetFormats()[3]);
 
   // Validate rtpmap
   ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute));
   auto& rtpmaps = video_attrs.GetRtpmap();
   ASSERT_TRUE(rtpmaps.HasEntry("120"));
   ASSERT_TRUE(rtpmaps.HasEntry("121"));
@@ -2785,53 +2785,55 @@ TEST_F(JsepSessionTest, ValidateAnswered
   auto& video_section = outputSdp->GetMediaSection(1);
   ASSERT_EQ(SdpMediaSection::kVideo, video_section.GetMediaType());
   auto& video_attrs = video_section.GetAttributeList();
   ASSERT_EQ(SdpDirectionAttribute::kSendrecv, video_attrs.GetDirection());
 
   // TODO(bug 1099351): Once fixed, this stuff will need to be updated.
   ASSERT_EQ(1U, video_section.GetFormats().size());
   // ASSERT_EQ(3U, video_section.GetFormats().size());
-  ASSERT_EQ("120", video_section.GetFormats()[0]);
+  ASSERT_EQ("121", video_section.GetFormats()[0]);
   // ASSERT_EQ("126", video_section.GetFormats()[1]);
   // ASSERT_EQ("97", video_section.GetFormats()[2]);
 
   // Validate rtpmap
   ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kRtpmapAttribute));
   auto& rtpmaps = video_attrs.GetRtpmap();
-  ASSERT_TRUE(rtpmaps.HasEntry("120"));
+  ASSERT_TRUE(rtpmaps.HasEntry("121"));
   // ASSERT_TRUE(rtpmaps.HasEntry("126"));
   // ASSERT_TRUE(rtpmaps.HasEntry("97"));
 
-  auto& vp8_entry = rtpmaps.GetEntry("120");
+  //auto& vp8_entry = rtpmaps.GetEntry("120");
+  auto& vp9_entry = rtpmaps.GetEntry("121");
   // auto& h264_1_entry = rtpmaps.GetEntry("126");
   // auto& h264_0_entry = rtpmaps.GetEntry("97");
 
-  ASSERT_EQ("VP8", vp8_entry.name);
+  //ASSERT_EQ("VP8", vp8_entry.name);
+  ASSERT_EQ("VP9", vp9_entry.name);
   // ASSERT_EQ("H264", h264_1_entry.name);
   // ASSERT_EQ("H264", h264_0_entry.name);
 
   // Validate fmtps
   ASSERT_TRUE(video_attrs.HasAttribute(SdpAttribute::kFmtpAttribute));
   auto& fmtps = video_attrs.GetFmtp().mFmtps;
 
   ASSERT_EQ(1U, fmtps.size());
   // ASSERT_EQ(3U, fmtps.size());
 
-  // VP8
-  ASSERT_EQ("120", fmtps[0].format);
+  // VP9
+  ASSERT_EQ("121", fmtps[0].format);
   ASSERT_TRUE(!!fmtps[0].parameters);
-  ASSERT_EQ(SdpRtpmapAttributeList::kVP8, fmtps[0].parameters->codec_type);
-
-  auto& parsed_vp8_params =
+  ASSERT_EQ(SdpRtpmapAttributeList::kVP9, fmtps[0].parameters->codec_type);
+
+  auto& parsed_vp9_params =
       *static_cast<const SdpFmtpAttributeList::VP8Parameters*>(
           fmtps[0].parameters.get());
 
-  ASSERT_EQ((uint32_t)12288, parsed_vp8_params.max_fs);
-  ASSERT_EQ((uint32_t)60, parsed_vp8_params.max_fr);
+  ASSERT_EQ((uint32_t)12288, parsed_vp9_params.max_fs);
+  ASSERT_EQ((uint32_t)60, parsed_vp9_params.max_fr);
 
 
   SetLocalAnswer(answer);
   SetRemoteAnswer(answer);
 
   auto offerPairs = mSessionOff.GetNegotiatedTrackPairs();
   ASSERT_EQ(2U, offerPairs.size());
   ASSERT_TRUE(offerPairs[1].mSending);
--- a/media/webrtc/trunk/webrtc/base/base.gyp
+++ b/media/webrtc/trunk/webrtc/base/base.gyp
@@ -24,18 +24,23 @@
     }],
   ],
   'targets': [
     {
       # The subset of rtc_base approved for use outside of libjingle.
       'target_name': 'rtc_base_approved',
       'type': 'static_library',
       'sources': [
+        'bitbuffer.cc',
+        'bitbuffer.h',
+        'buffer.cc',
+        'buffer.h',
         'checks.cc',
         'checks.h',
+        'constructormagic.h',
         'event.cc',
         'event.h',
         'event_tracer.cc',
         'event_tracer.h',
         'exp_filter.cc',
         'exp_filter.h',
         'md5.cc',
         'md5.h',
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/base/bitbuffer.cc
@@ -0,0 +1,296 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/bitbuffer.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "webrtc/base/checks.h"
+
+namespace {
+
+// Returns the lowest (right-most) |bit_count| bits in |byte|.
+uint8_t LowestBits(uint8_t byte, size_t bit_count) {
+  RTC_DCHECK_LE(bit_count, 8u);
+  return byte & ((1 << bit_count) - 1);
+}
+
+// Returns the highest (left-most) |bit_count| bits in |byte|, shifted to the
+// lowest bits (to the right).
+uint8_t HighestBits(uint8_t byte, size_t bit_count) {
+  RTC_DCHECK_LE(bit_count, 8u);
+  uint8_t shift = 8 - static_cast<uint8_t>(bit_count);
+  uint8_t mask = 0xFF << shift;
+  return (byte & mask) >> shift;
+}
+
+// Returns the highest byte of |val| in a uint8_t.
+uint8_t HighestByte(uint64_t val) {
+  return static_cast<uint8_t>(val >> 56);
+}
+
+// Returns the result of writing partial data from |source|, of
+// |source_bit_count| size in the highest bits, to |target| at
+// |target_bit_offset| from the highest bit.
+uint8_t WritePartialByte(uint8_t source,
+                         size_t source_bit_count,
+                         uint8_t target,
+                         size_t target_bit_offset) {
+  RTC_DCHECK(target_bit_offset < 8);
+  RTC_DCHECK(source_bit_count < 9);
+  RTC_DCHECK(source_bit_count <= (8 - target_bit_offset));
+  // Generate a mask for just the bits we're going to overwrite, so:
+  uint8_t mask =
+      // The number of bits we want, in the most significant bits...
+      static_cast<uint8_t>(0xFF << (8 - source_bit_count))
+      // ...shifted over to the target offset from the most signficant bit.
+      >> target_bit_offset;
+
+  // We want the target, with the bits we'll overwrite masked off, or'ed with
+  // the bits from the source we want.
+  return (target & ~mask) | (source >> target_bit_offset);
+}
+
+// Counts the number of bits used in the binary representation of val.
+size_t CountBits(uint64_t val) {
+  size_t bit_count = 0;
+  while (val != 0) {
+    bit_count++;
+    val >>= 1;
+  }
+  return bit_count;
+}
+
+}  // namespace
+
+namespace rtc {
+
+BitBuffer::BitBuffer(const uint8_t* bytes, size_t byte_count)
+    : bytes_(bytes), byte_count_(byte_count), byte_offset_(), bit_offset_() {
+  RTC_DCHECK(static_cast<uint64_t>(byte_count_) <=
+             std::numeric_limits<uint32_t>::max());
+}
+
+uint64_t BitBuffer::RemainingBitCount() const {
+  return (static_cast<uint64_t>(byte_count_) - byte_offset_) * 8 - bit_offset_;
+}
+
+bool BitBuffer::ReadUInt8(uint8_t* val) {
+  uint32_t bit_val;
+  if (!ReadBits(&bit_val, sizeof(uint8_t) * 8)) {
+    return false;
+  }
+  RTC_DCHECK(bit_val <= std::numeric_limits<uint8_t>::max());
+  *val = static_cast<uint8_t>(bit_val);
+  return true;
+}
+
+bool BitBuffer::ReadUInt16(uint16_t* val) {
+  uint32_t bit_val;
+  if (!ReadBits(&bit_val, sizeof(uint16_t) * 8)) {
+    return false;
+  }
+  RTC_DCHECK(bit_val <= std::numeric_limits<uint16_t>::max());
+  *val = static_cast<uint16_t>(bit_val);
+  return true;
+}
+
+bool BitBuffer::ReadUInt32(uint32_t* val) {
+  return ReadBits(val, sizeof(uint32_t) * 8);
+}
+
+bool BitBuffer::PeekBits(uint32_t* val, size_t bit_count) {
+  if (!val || bit_count > RemainingBitCount() || bit_count > 32) {
+    return false;
+  }
+  const uint8_t* bytes = bytes_ + byte_offset_;
+  size_t remaining_bits_in_current_byte = 8 - bit_offset_;
+  uint32_t bits = LowestBits(*bytes++, remaining_bits_in_current_byte);
+  // If we're reading fewer bits than what's left in the current byte, just
+  // return the portion of this byte that we need.
+  if (bit_count < remaining_bits_in_current_byte) {
+    *val = HighestBits(bits, bit_offset_ + bit_count);
+    return true;
+  }
+  // Otherwise, subtract what we've read from the bit count and read as many
+  // full bytes as we can into bits.
+  bit_count -= remaining_bits_in_current_byte;
+  while (bit_count >= 8) {
+    bits = (bits << 8) | *bytes++;
+    bit_count -= 8;
+  }
+  // Whatever we have left is smaller than a byte, so grab just the bits we need
+  // and shift them into the lowest bits.
+  if (bit_count > 0) {
+    bits <<= bit_count;
+    bits |= HighestBits(*bytes, bit_count);
+  }
+  *val = bits;
+  return true;
+}
+
+bool BitBuffer::ReadBits(uint32_t* val, size_t bit_count) {
+  return PeekBits(val, bit_count) && ConsumeBits(bit_count);
+}
+
+bool BitBuffer::ConsumeBytes(size_t byte_count) {
+  return ConsumeBits(byte_count * 8);
+}
+
+bool BitBuffer::ConsumeBits(size_t bit_count) {
+  if (bit_count > RemainingBitCount()) {
+    return false;
+  }
+
+  byte_offset_ += (bit_offset_ + bit_count) / 8;
+  bit_offset_ = (bit_offset_ + bit_count) % 8;
+  return true;
+}
+
+bool BitBuffer::ReadExponentialGolomb(uint32_t* val) {
+  if (!val) {
+    return false;
+  }
+  // Store off the current byte/bit offset, in case we want to restore them due
+  // to a failed parse.
+  size_t original_byte_offset = byte_offset_;
+  size_t original_bit_offset = bit_offset_;
+
+  // Count the number of leading 0 bits by peeking/consuming them one at a time.
+  size_t zero_bit_count = 0;
+  uint32_t peeked_bit;
+  while (PeekBits(&peeked_bit, 1) && peeked_bit == 0) {
+    zero_bit_count++;
+    ConsumeBits(1);
+  }
+
+  // We should either be at the end of the stream, or the next bit should be 1.
+  RTC_DCHECK(!PeekBits(&peeked_bit, 1) || peeked_bit == 1);
+
+  // The bit count of the value is the number of zeros + 1. Make sure that many
+  // bits fits in a uint32_t and that we have enough bits left for it, and then
+  // read the value.
+  size_t value_bit_count = zero_bit_count + 1;
+  if (value_bit_count > 32 || !ReadBits(val, value_bit_count)) {
+    RTC_CHECK(Seek(original_byte_offset, original_bit_offset));
+    return false;
+  }
+  *val -= 1;
+  return true;
+}
+
+bool BitBuffer::ReadSignedExponentialGolomb(int32_t* val) {
+  uint32_t unsigned_val;
+  if (!ReadExponentialGolomb(&unsigned_val)) {
+    return false;
+  }
+  if ((unsigned_val & 1) == 0) {
+    *val = -static_cast<int32_t>(unsigned_val / 2);
+  } else {
+    *val = (unsigned_val + 1) / 2;
+  }
+  return true;
+}
+
+void BitBuffer::GetCurrentOffset(
+    size_t* out_byte_offset, size_t* out_bit_offset) {
+  RTC_CHECK(out_byte_offset != NULL);
+  RTC_CHECK(out_bit_offset != NULL);
+  *out_byte_offset = byte_offset_;
+  *out_bit_offset = bit_offset_;
+}
+
+bool BitBuffer::Seek(size_t byte_offset, size_t bit_offset) {
+  if (byte_offset > byte_count_ || bit_offset > 7 ||
+      (byte_offset == byte_count_ && bit_offset > 0)) {
+    return false;
+  }
+  byte_offset_ = byte_offset;
+  bit_offset_ = bit_offset;
+  return true;
+}
+
+BitBufferWriter::BitBufferWriter(uint8_t* bytes, size_t byte_count)
+    : BitBuffer(bytes, byte_count), writable_bytes_(bytes) {
+}
+
+bool BitBufferWriter::WriteUInt8(uint8_t val) {
+  return WriteBits(val, sizeof(uint8_t) * 8);
+}
+
+bool BitBufferWriter::WriteUInt16(uint16_t val) {
+  return WriteBits(val, sizeof(uint16_t) * 8);
+}
+
+bool BitBufferWriter::WriteUInt32(uint32_t val) {
+  return WriteBits(val, sizeof(uint32_t) * 8);
+}
+
+bool BitBufferWriter::WriteBits(uint64_t val, size_t bit_count) {
+  if (bit_count > RemainingBitCount()) {
+    return false;
+  }
+  size_t total_bits = bit_count;
+
+  // For simplicity, push the bits we want to read from val to the highest bits.
+  val <<= (sizeof(uint64_t) * 8 - bit_count);
+
+  uint8_t* bytes = writable_bytes_ + byte_offset_;
+
+  // The first byte is relatively special; the bit offset to write to may put us
+  // in the middle of the byte, and the total bit count to write may require we
+  // save the bits at the end of the byte.
+  size_t remaining_bits_in_current_byte = 8 - bit_offset_;
+  size_t bits_in_first_byte =
+      std::min(bit_count, remaining_bits_in_current_byte);
+  *bytes = WritePartialByte(
+      HighestByte(val), bits_in_first_byte, *bytes, bit_offset_);
+  if (bit_count <= remaining_bits_in_current_byte) {
+    // Nothing left to write, so quit early.
+    return ConsumeBits(total_bits);
+  }
+
+  // Subtract what we've written from the bit count, shift it off the value, and
+  // write the remaining full bytes.
+  val <<= bits_in_first_byte;
+  bytes++;
+  bit_count -= bits_in_first_byte;
+  while (bit_count >= 8) {
+    *bytes++ = HighestByte(val);
+    val <<= 8;
+    bit_count -= 8;
+  }
+
+  // Last byte may also be partial, so write the remaining bits from the top of
+  // val.
+  if (bit_count > 0) {
+    *bytes = WritePartialByte(HighestByte(val), bit_count, *bytes, 0);
+  }
+
+  // All done! Consume the bits we've written.
+  return ConsumeBits(total_bits);
+}
+
+bool BitBufferWriter::WriteExponentialGolomb(uint32_t val) {
+  // We don't support reading UINT32_MAX, because it doesn't fit in a uint32_t
+  // when encoded, so don't support writing it either.
+  if (val == std::numeric_limits<uint32_t>::max()) {
+    return false;
+  }
+  uint64_t val_to_encode = static_cast<uint64_t>(val) + 1;
+
+  // We need to write CountBits(val+1) 0s and then val+1. Since val (as a
+  // uint64_t) has leading zeros, we can just write the total golomb encoded
+  // size worth of bits, knowing the value will appear last.
+  return WriteBits(val_to_encode, CountBits(val_to_encode) * 2 - 1);
+}
+
+}  // namespace rtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/base/bitbuffer.h
@@ -0,0 +1,122 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_BASE_BITBUFFER_H_
+#define WEBRTC_BASE_BITBUFFER_H_
+
+#include <stdint.h>  // For integer types.
+#include <stddef.h>  // For size_t.
+
+#include "webrtc/base/constructormagic.h"
+
+namespace rtc {
+
+// A class, similar to ByteBuffer, that can parse bit-sized data out of a set of
+// bytes. Has a similar API to ByteBuffer, plus methods for reading bit-sized
+// and exponential golomb encoded data. For a writable version, use
+// BitBufferWriter. Unlike ByteBuffer, this class doesn't make a copy of the
+// source bytes, so it can be used on read-only data.
+// Sizes/counts specify bits/bytes, for clarity.
+// Byte order is assumed big-endian/network.
+class BitBuffer {
+ public:
+  BitBuffer(const uint8_t* bytes, size_t byte_count);
+
+  // Gets the current offset, in bytes/bits, from the start of the buffer. The
+  // bit offset is the offset into the current byte, in the range [0,7].
+  void GetCurrentOffset(size_t* out_byte_offset, size_t* out_bit_offset);
+
+  // The remaining bits in the byte buffer.
+  uint64_t RemainingBitCount() const;
+
+  // Reads byte-sized values from the buffer. Returns false if there isn't
+  // enough data left for the specified type.
+  bool ReadUInt8(uint8_t* val);
+  bool ReadUInt16(uint16_t* val);
+  bool ReadUInt32(uint32_t* val);
+
+  // Reads bit-sized values from the buffer. Returns false if there isn't enough
+  // data left for the specified bit count..
+  bool ReadBits(uint32_t* val, size_t bit_count);
+
+  // Peeks bit-sized values from the buffer. Returns false if there isn't enough
+  // data left for the specified number of bits. Doesn't move the current
+  // offset.
+  bool PeekBits(uint32_t* val, size_t bit_count);
+
+  // Reads the exponential golomb encoded value at the current offset.
+  // Exponential golomb values are encoded as:
+  // 1) x = source val + 1
+  // 2) In binary, write [countbits(x) - 1] 0s, then x
+  // To decode, we count the number of leading 0 bits, read that many + 1 bits,
+  // and increment the result by 1.
+  // Returns false if there isn't enough data left for the specified type, or if
+  // the value wouldn't fit in a uint32_t.
+  bool ReadExponentialGolomb(uint32_t* val);
+  // Reads signed exponential golomb values at the current offset. Signed
+  // exponential golomb values are just the unsigned values mapped to the
+  // sequence 0, 1, -1, 2, -2, etc. in order.
+  bool ReadSignedExponentialGolomb(int32_t* val);
+
+  // Moves current position |byte_count| bytes forward. Returns false if
+  // there aren't enough bytes left in the buffer.
+  bool ConsumeBytes(size_t byte_count);
+  // Moves current position |bit_count| bits forward. Returns false if
+  // there aren't enough bits left in the buffer.
+  bool ConsumeBits(size_t bit_count);
+
+  // Sets the current offset to the provied byte/bit offsets. The bit
+  // offset is from the given byte, in the range [0,7].
+  bool Seek(size_t byte_offset, size_t bit_offset);
+
+ protected:
+  const uint8_t* const bytes_;
+  // The total size of |bytes_|.
+  size_t byte_count_;
+  // The current offset, in bytes, from the start of |bytes_|.
+  size_t byte_offset_;
+  // The current offset, in bits, into the current byte.
+  size_t bit_offset_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(BitBuffer);
+};
+
+// A BitBuffer API for write operations. Supports symmetric write APIs to the
+// reading APIs of BitBuffer. Note that the read/write offset is shared with the
+// BitBuffer API, so both reading and writing will consume bytes/bits.
+class BitBufferWriter : public BitBuffer {
+ public:
+  // Constructs a bit buffer for the writable buffer of |bytes|.
+  BitBufferWriter(uint8_t* bytes, size_t byte_count);
+
+  // Writes byte-sized values from the buffer. Returns false if there isn't
+  // enough data left for the specified type.
+  bool WriteUInt8(uint8_t val);
+  bool WriteUInt16(uint16_t val);
+  bool WriteUInt32(uint32_t val);
+
+  // Writes bit-sized values to the buffer. Returns false if there isn't enough
+  // room left for the specified number of bits.
+  bool WriteBits(uint64_t val, size_t bit_count);
+
+  // Writes the exponential golomb encoded version of the supplied value.
+  // Returns false if there isn't enough room left for the value.
+  bool WriteExponentialGolomb(uint32_t val);
+
+ private:
+  // The buffer, as a writable array.
+  uint8_t* const writable_bytes_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(BitBufferWriter);
+};
+
+}  // namespace rtc
+
+#endif  // WEBRTC_BASE_BITBUFFER_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/base/bitbuffer_unittest.cc
@@ -0,0 +1,330 @@
+/*
+ *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/base/arraysize.h"
+#include "webrtc/base/bitbuffer.h"
+#include "webrtc/base/bytebuffer.h"
+#include "webrtc/base/common.h"
+#include "webrtc/base/gunit.h"
+
+namespace rtc {
+
+TEST(BitBufferTest, ConsumeBits) {
+  const uint8_t bytes[64] = {0};
+  BitBuffer buffer(bytes, 32);
+  uint64_t total_bits = 32 * 8;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  total_bits -= 3;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  total_bits -= 3;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+  EXPECT_TRUE(buffer.ConsumeBits(15));
+  total_bits -= 15;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+  EXPECT_TRUE(buffer.ConsumeBits(37));
+  total_bits -= 37;
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+
+  EXPECT_FALSE(buffer.ConsumeBits(32 * 8));
+  EXPECT_EQ(total_bits, buffer.RemainingBitCount());
+}
+
+TEST(BitBufferTest, ReadBytesAligned) {
+  const uint8_t bytes[] = {0x0A, 0xBC, 0xDE, 0xF1, 0x23, 0x45, 0x67, 0x89};
+  uint8_t val8;
+  uint16_t val16;
+  uint32_t val32;
+  BitBuffer buffer(bytes, 8);
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0x0Au, val8);
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0xBCu, val8);
+  EXPECT_TRUE(buffer.ReadUInt16(&val16));
+  EXPECT_EQ(0xDEF1u, val16);
+  EXPECT_TRUE(buffer.ReadUInt32(&val32));
+  EXPECT_EQ(0x23456789u, val32);
+}
+
+TEST(BitBufferTest, ReadBytesOffset4) {
+  const uint8_t bytes[] = {0x0A, 0xBC, 0xDE, 0xF1, 0x23,
+                           0x45, 0x67, 0x89, 0x0A};
+  uint8_t val8;
+  uint16_t val16;
+  uint32_t val32;
+  BitBuffer buffer(bytes, 9);
+  EXPECT_TRUE(buffer.ConsumeBits(4));
+
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0xABu, val8);
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0xCDu, val8);
+  EXPECT_TRUE(buffer.ReadUInt16(&val16));
+  EXPECT_EQ(0xEF12u, val16);
+  EXPECT_TRUE(buffer.ReadUInt32(&val32));
+  EXPECT_EQ(0x34567890u, val32);
+}
+
+TEST(BitBufferTest, ReadBytesOffset3) {
+  // The pattern we'll check against is counting down from 0b1111. It looks
+  // weird here because it's all offset by 3.
+  // Byte pattern is:
+  //    56701234
+  //  0b00011111,
+  //  0b11011011,
+  //  0b10010111,
+  //  0b01010011,
+  //  0b00001110,
+  //  0b11001010,
+  //  0b10000110,
+  //  0b01000010
+  //       xxxxx <-- last 5 bits unused.
+
+  // The bytes. It almost looks like counting down by two at a time, except the
+  // jump at 5->3->0, since that's when the high bit is turned off.
+  const uint8_t bytes[] = {0x1F, 0xDB, 0x97, 0x53, 0x0E, 0xCA, 0x86, 0x42};
+
+  uint8_t val8;
+  uint16_t val16;
+  uint32_t val32;
+  BitBuffer buffer(bytes, 8);
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0xFEu, val8);
+  EXPECT_TRUE(buffer.ReadUInt16(&val16));
+  EXPECT_EQ(0xDCBAu, val16);
+  EXPECT_TRUE(buffer.ReadUInt32(&val32));
+  EXPECT_EQ(0x98765432u, val32);
+  // 5 bits left unread. Not enough to read a uint8_t.
+  EXPECT_EQ(5u, buffer.RemainingBitCount());
+  EXPECT_FALSE(buffer.ReadUInt8(&val8));
+}
+
+TEST(BitBufferTest, ReadBits) {
+  // Bit values are:
+  //  0b01001101,
+  //  0b00110010
+  const uint8_t bytes[] = {0x4D, 0x32};
+  uint32_t val;
+  BitBuffer buffer(bytes, 2);
+  EXPECT_TRUE(buffer.ReadBits(&val, 3));
+  // 0b010
+  EXPECT_EQ(0x2u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 2));
+  // 0b01
+  EXPECT_EQ(0x1u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 7));
+  // 0b1010011
+  EXPECT_EQ(0x53u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 2));
+  // 0b00
+  EXPECT_EQ(0x0u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 1));
+  // 0b1
+  EXPECT_EQ(0x1u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 1));
+  // 0b0
+  EXPECT_EQ(0x0u, val);
+
+  EXPECT_FALSE(buffer.ReadBits(&val, 1));
+}
+
+TEST(BitBufferTest, SetOffsetValues) {
+  uint8_t bytes[4] = {0};
+  BitBufferWriter buffer(bytes, 4);
+
+  size_t byte_offset, bit_offset;
+  // Bit offsets are [0,7].
+  EXPECT_TRUE(buffer.Seek(0, 0));
+  EXPECT_TRUE(buffer.Seek(0, 7));
+  buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+  EXPECT_EQ(0u, byte_offset);
+  EXPECT_EQ(7u, bit_offset);
+  EXPECT_FALSE(buffer.Seek(0, 8));
+  buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+  EXPECT_EQ(0u, byte_offset);
+  EXPECT_EQ(7u, bit_offset);
+  // Byte offsets are [0,length]. At byte offset length, the bit offset must be
+  // 0.
+  EXPECT_TRUE(buffer.Seek(0, 0));
+  EXPECT_TRUE(buffer.Seek(2, 4));
+  buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+  EXPECT_EQ(2u, byte_offset);
+  EXPECT_EQ(4u, bit_offset);
+  EXPECT_TRUE(buffer.Seek(4, 0));
+  EXPECT_FALSE(buffer.Seek(5, 0));
+  buffer.GetCurrentOffset(&byte_offset, &bit_offset);
+  EXPECT_EQ(4u, byte_offset);
+  EXPECT_EQ(0u, bit_offset);
+  EXPECT_FALSE(buffer.Seek(4, 1));
+
+  // Disable death test on Android because it relies on fork() and doesn't play
+  // nicely.
+#if defined(GTEST_HAS_DEATH_TEST)
+#if !defined(WEBRTC_ANDROID)
+  // Passing a NULL out parameter is death.
+  EXPECT_DEATH(buffer.GetCurrentOffset(&byte_offset, NULL), "");
+#endif
+#endif
+}
+
+uint64_t GolombEncoded(uint32_t val) {
+  val++;
+  uint32_t bit_counter = val;
+  uint64_t bit_count = 0;
+  while (bit_counter > 0) {
+    bit_count++;
+    bit_counter >>= 1;
+  }
+  return static_cast<uint64_t>(val) << (64 - (bit_count * 2 - 1));
+}
+
+TEST(BitBufferTest, GolombUint32Values) {
+  ByteBuffer byteBuffer;
+  byteBuffer.Resize(16);
+  BitBuffer buffer(reinterpret_cast<const uint8_t*>(byteBuffer.Data()),
+                   byteBuffer.Capacity());
+  // Test over the uint32_t range with a large enough step that the test doesn't
+  // take forever. Around 20,000 iterations should do.
+  const int kStep = std::numeric_limits<uint32_t>::max() / 20000;
+  for (uint32_t i = 0; i < std::numeric_limits<uint32_t>::max() - kStep;
+       i += kStep) {
+    uint64_t encoded_val = GolombEncoded(i);
+    byteBuffer.Clear();
+    byteBuffer.WriteUInt64(encoded_val);
+    uint32_t decoded_val;
+    EXPECT_TRUE(buffer.Seek(0, 0));
+    EXPECT_TRUE(buffer.ReadExponentialGolomb(&decoded_val));
+    EXPECT_EQ(i, decoded_val);
+  }
+}
+
+TEST(BitBufferTest, SignedGolombValues) {
+  uint8_t golomb_bits[] = {
+      0x80,  // 1
+      0x40,  // 010
+      0x60,  // 011
+      0x20,  // 00100
+      0x38,  // 00111
+  };
+  int32_t expected[] = {0, 1, -1, 2, -3};
+  for (size_t i = 0; i < sizeof(golomb_bits); ++i) {
+    BitBuffer buffer(&golomb_bits[i], 1);
+    int32_t decoded_val;
+    ASSERT_TRUE(buffer.ReadSignedExponentialGolomb(&decoded_val));
+    EXPECT_EQ(expected[i], decoded_val)
+        << "Mismatch in expected/decoded value for golomb_bits[" << i
+        << "]: " << static_cast<int>(golomb_bits[i]);
+  }
+}
+
+TEST(BitBufferTest, NoGolombOverread) {
+  const uint8_t bytes[] = {0x00, 0xFF, 0xFF};
+  // Make sure the bit buffer correctly enforces byte length on golomb reads.
+  // If it didn't, the above buffer would be valid at 3 bytes.
+  BitBuffer buffer(bytes, 1);
+  uint32_t decoded_val;
+  EXPECT_FALSE(buffer.ReadExponentialGolomb(&decoded_val));
+
+  BitBuffer longer_buffer(bytes, 2);
+  EXPECT_FALSE(longer_buffer.ReadExponentialGolomb(&decoded_val));
+
+  BitBuffer longest_buffer(bytes, 3);
+  EXPECT_TRUE(longest_buffer.ReadExponentialGolomb(&decoded_val));
+  // Golomb should have read 9 bits, so 0x01FF, and since it is golomb, the
+  // result is 0x01FF - 1 = 0x01FE.
+  EXPECT_EQ(0x01FEu, decoded_val);
+}
+
+TEST(BitBufferWriterTest, SymmetricReadWrite) {
+  uint8_t bytes[16] = {0};
+  BitBufferWriter buffer(bytes, 4);
+
+  // Write some bit data at various sizes.
+  EXPECT_TRUE(buffer.WriteBits(0x2u, 3));
+  EXPECT_TRUE(buffer.WriteBits(0x1u, 2));
+  EXPECT_TRUE(buffer.WriteBits(0x53u, 7));
+  EXPECT_TRUE(buffer.WriteBits(0x0u, 2));
+  EXPECT_TRUE(buffer.WriteBits(0x1u, 1));
+  EXPECT_TRUE(buffer.WriteBits(0x1ABCDu, 17));
+  // That should be all that fits in the buffer.
+  EXPECT_FALSE(buffer.WriteBits(1, 1));
+
+  EXPECT_TRUE(buffer.Seek(0, 0));
+  uint32_t val;
+  EXPECT_TRUE(buffer.ReadBits(&val, 3));
+  EXPECT_EQ(0x2u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 2));
+  EXPECT_EQ(0x1u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 7));
+  EXPECT_EQ(0x53u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 2));
+  EXPECT_EQ(0x0u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 1));
+  EXPECT_EQ(0x1u, val);
+  EXPECT_TRUE(buffer.ReadBits(&val, 17));
+  EXPECT_EQ(0x1ABCDu, val);
+  // And there should be nothing left.
+  EXPECT_FALSE(buffer.ReadBits(&val, 1));
+}
+
+TEST(BitBufferWriterTest, SymmetricBytesMisaligned) {
+  uint8_t bytes[16] = {0};
+  BitBufferWriter buffer(bytes, 16);
+
+  // Offset 3, to get things misaligned.
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  EXPECT_TRUE(buffer.WriteUInt8(0x12u));
+  EXPECT_TRUE(buffer.WriteUInt16(0x3456u));
+  EXPECT_TRUE(buffer.WriteUInt32(0x789ABCDEu));
+
+  buffer.Seek(0, 3);
+  uint8_t val8;
+  uint16_t val16;
+  uint32_t val32;
+  EXPECT_TRUE(buffer.ReadUInt8(&val8));
+  EXPECT_EQ(0x12u, val8);
+  EXPECT_TRUE(buffer.ReadUInt16(&val16));
+  EXPECT_EQ(0x3456u, val16);
+  EXPECT_TRUE(buffer.ReadUInt32(&val32));
+  EXPECT_EQ(0x789ABCDEu, val32);
+}
+
+TEST(BitBufferWriterTest, SymmetricGolomb) {
+  char test_string[] = "my precious";
+  uint8_t bytes[64] = {0};
+  BitBufferWriter buffer(bytes, 64);
+  for (size_t i = 0; i < arraysize(test_string); ++i) {
+    EXPECT_TRUE(buffer.WriteExponentialGolomb(test_string[i]));
+  }
+  buffer.Seek(0, 0);
+  for (size_t i = 0; i < arraysize(test_string); ++i) {
+    uint32_t val;
+    EXPECT_TRUE(buffer.ReadExponentialGolomb(&val));
+    EXPECT_LE(val, std::numeric_limits<uint8_t>::max());
+    EXPECT_EQ(test_string[i], static_cast<char>(val));
+  }
+}
+
+TEST(BitBufferWriterTest, WriteClearsBits) {
+  uint8_t bytes[] = {0xFF, 0xFF};
+  BitBufferWriter buffer(bytes, 2);
+  EXPECT_TRUE(buffer.ConsumeBits(3));
+  EXPECT_TRUE(buffer.WriteBits(0, 1));
+  EXPECT_EQ(0xEFu, bytes[0]);
+  EXPECT_TRUE(buffer.WriteBits(0, 3));
+  EXPECT_EQ(0xE1u, bytes[0]);
+  EXPECT_TRUE(buffer.WriteBits(0, 2));
+  EXPECT_EQ(0xE0u, bytes[0]);
+  EXPECT_EQ(0x7F, bytes[1]);
+}
+
+}  // namespace rtc
--- a/media/webrtc/trunk/webrtc/base/buffer.h
+++ b/media/webrtc/trunk/webrtc/base/buffer.h
@@ -8,17 +8,18 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
 #ifndef WEBRTC_BASE_BUFFER_H_
 #define WEBRTC_BASE_BUFFER_H_
 
 #include <string.h>
 
-#include "webrtc/base/common.h"
+// common.h isn't in the rtc_approved list
+//#include "webrtc/base/common.h"
 #include "webrtc/base/scoped_ptr.h"
 
 namespace rtc {
 
 // Basic buffer class, can be grown and shrunk dynamically.
 // Unlike std::string/vector, does not initialize data when expanding capacity.
 class Buffer {
  public:
@@ -47,22 +48,22 @@ class Buffer {
   bool operator==(const Buffer& buf) const {
     return (size_ == buf.size() && memcmp(data_.get(), buf.data(), size_) == 0);
   }
   bool operator!=(const Buffer& buf) const {
     return !operator==(buf);
   }
 
   void SetData(const void* data, size_t size) {
-    ASSERT(data != NULL || size == 0);
+    assert(data != NULL || size == 0);
     SetSize(size);
     memcpy(data_.get(), data, size);
   }
   void AppendData(const void* data, size_t size) {
-    ASSERT(data != NULL || size == 0);
+    assert(data != NULL || size == 0);
     size_t old_size = size_;
     SetSize(size_ + size);
     memcpy(data_.get() + old_size, data, size);
   }
   void SetSize(size_t size) {
     SetCapacity(size);
     size_ = size;
   }
@@ -71,17 +72,17 @@ class Buffer {
       rtc::scoped_ptr<char[]> data(new char[capacity]);
       memcpy(data.get(), data_.get(), size_);
       data_.swap(data);
       capacity_ = capacity;
     }
   }
 
   void TransferTo(Buffer* buf) {
-    ASSERT(buf != NULL);
+    assert(buf != NULL);
     buf->data_.reset(data_.release());
     buf->size_ = size_;
     buf->capacity_ = capacity_;
     Construct(NULL, 0, 0);
   }
 
  protected:
   void Construct(const void* data, size_t size, size_t capacity) {
--- a/media/webrtc/trunk/webrtc/base/checks.h
+++ b/media/webrtc/trunk/webrtc/base/checks.h
@@ -86,16 +86,18 @@ namespace rtc {
 // compilation mode.
 //
 // We make sure CHECK et al. always evaluates their arguments, as
 // doing CHECK(FunctionWithSideEffect()) is a common idiom.
 #define CHECK(condition)                                                    \
   LAZY_STREAM(rtc::FatalMessage(__FILE__, __LINE__).stream(), !(condition)) \
   << "Check failed: " #condition << std::endl << "# "
 
+#define RTC_CHECK(condition) CHECK(condition)
+
 // Helper macro for binary operators.
 // Don't use this macro directly in your code, use CHECK_EQ et al below.
 //
 // TODO(akalin): Rewrite this so that constructs like if (...)
 // CHECK_EQ(...) else { ... } work properly.
 #define CHECK_OP(name, op, val1, val2)                      \
   if (std::string* _result =                                \
       rtc::Check##name##Impl((val1), (val2),                \
@@ -180,16 +182,46 @@ DEFINE_CHECK_OP_IMPL(GT, > )
 #define DCHECK_EQ(v1, v2) EAT_STREAM_PARAMETERS((v1) == (v2))
 #define DCHECK_NE(v1, v2) EAT_STREAM_PARAMETERS((v1) != (v2))
 #define DCHECK_LE(v1, v2) EAT_STREAM_PARAMETERS((v1) <= (v2))
 #define DCHECK_LT(v1, v2) EAT_STREAM_PARAMETERS((v1) < (v2))
 #define DCHECK_GE(v1, v2) EAT_STREAM_PARAMETERS((v1) >= (v2))
 #define DCHECK_GT(v1, v2) EAT_STREAM_PARAMETERS((v1) > (v2))
 #endif
 
+#define RTC_CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2)
+#define RTC_CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2)
+#define RTC_CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2)
+#define RTC_CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2)
+#define RTC_CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2)
+#define RTC_CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2)
+
+// The RTC_DCHECK macro is equivalent to RTC_CHECK except that it only generates
+// code in debug builds. It does reference the condition parameter in all cases,
+// though, so callers won't risk getting warnings about unused variables.
+#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
+#define RTC_DCHECK_IS_ON 1
+#define RTC_DCHECK(condition) CHECK(condition)
+#define RTC_DCHECK_EQ(v1, v2) CHECK_EQ(v1, v2)
+#define RTC_DCHECK_NE(v1, v2) CHECK_NE(v1, v2)
+#define RTC_DCHECK_LE(v1, v2) CHECK_LE(v1, v2)
+#define RTC_DCHECK_LT(v1, v2) CHECK_LT(v1, v2)
+#define RTC_DCHECK_GE(v1, v2) CHECK_GE(v1, v2)
+#define RTC_DCHECK_GT(v1, v2) CHECK_GT(v1, v2)
+#else
+#define RTC_DCHECK_IS_ON 0
+#define RTC_DCHECK(condition) EAT_STREAM_PARAMETERS(condition)
+#define RTC_DCHECK_EQ(v1, v2) EAT_STREAM_PARAMETERS((v1) == (v2))
+#define RTC_DCHECK_NE(v1, v2) EAT_STREAM_PARAMETERS((v1) != (v2))
+#define RTC_DCHECK_LE(v1, v2) EAT_STREAM_PARAMETERS((v1) <= (v2))
+#define RTC_DCHECK_LT(v1, v2) EAT_STREAM_PARAMETERS((v1) < (v2))
+#define RTC_DCHECK_GE(v1, v2) EAT_STREAM_PARAMETERS((v1) >= (v2))
+#define RTC_DCHECK_GT(v1, v2) EAT_STREAM_PARAMETERS((v1) > (v2))
+#endif
+
 // This is identical to LogMessageVoidify but in name.
 class FatalMessageVoidify {
  public:
   FatalMessageVoidify() { }
   // This has to be an operator with a precedence lower than << but
   // higher than ?:
   void operator&(std::ostream&) { }
 };
--- a/media/webrtc/trunk/webrtc/base/constructormagic.h
+++ b/media/webrtc/trunk/webrtc/base/constructormagic.h
@@ -12,34 +12,42 @@
 #define WEBRTC_BASE_CONSTRUCTORMAGIC_H_
 
 // Undefine macros first, just in case. Some third-party includes have their own
 // version.
 
 #undef DISALLOW_ASSIGN
 #define DISALLOW_ASSIGN(TypeName) \
   void operator=(const TypeName&)
+#define RTC_DISALLOW_ASSIGN(TypeName) \
+  void operator=(const TypeName&) = delete
 
 // A macro to disallow the evil copy constructor and operator= functions
 // This should be used in the private: declarations for a class.
 #undef DISALLOW_COPY_AND_ASSIGN
 #define DISALLOW_COPY_AND_ASSIGN(TypeName)    \
   TypeName(const TypeName&);                    \
   DISALLOW_ASSIGN(TypeName)
+#define RTC_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&) = delete;          \
+  RTC_DISALLOW_ASSIGN(TypeName)
 
 // Alternative, less-accurate legacy name.
 #undef DISALLOW_EVIL_CONSTRUCTORS
 #define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
   DISALLOW_COPY_AND_ASSIGN(TypeName)
 
 // A macro to disallow all the implicit constructors, namely the
 // default constructor, copy constructor and operator= functions.
 //
 // This should be used in the private: declarations for a class
 // that wants to prevent anyone from instantiating it. This is
 // especially useful for classes containing only static methods.
 #undef DISALLOW_IMPLICIT_CONSTRUCTORS
 #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
   TypeName();                                    \
   DISALLOW_EVIL_CONSTRUCTORS(TypeName)
+#define RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+  TypeName() = delete;                               \
+  RTC_DISALLOW_COPY_AND_ASSIGN(TypeName)
 
 
 #endif  // WEBRTC_BASE_CONSTRUCTORMAGIC_H_
--- a/media/webrtc/trunk/webrtc/common_types.h
+++ b/media/webrtc/trunk/webrtc/common_types.h
@@ -634,16 +634,19 @@ struct VideoCodecVP8 {
 struct VideoCodecVP9 {
   VideoCodecComplexity complexity;
   int                  resilience;
   unsigned char        numberOfTemporalLayers;
   bool                 denoisingOn;
   bool                 frameDroppingOn;
   int                  keyFrameInterval;
   bool                 adaptiveQpMode;
+  bool                 automaticResizeOn;
+  unsigned char        numberOfSpatialLayers;
+  bool                 flexibleMode;
 };
 
 // H264 specific.
 struct VideoCodecH264 {
   VideoCodecProfile profile;
   uint8_t        profile_byte;
   uint8_t        constraints;
   uint8_t        level;
--- a/media/webrtc/trunk/webrtc/modules/interface/module_common_types.h
+++ b/media/webrtc/trunk/webrtc/modules/interface/module_common_types.h
@@ -10,34 +10,43 @@
 
 #ifndef MODULE_COMMON_TYPES_H
 #define MODULE_COMMON_TYPES_H
 
 #include <assert.h>
 #include <string.h>  // memcpy
 
 #include <algorithm>
+#include <limits>
 
 #include "webrtc/base/constructormagic.h"
 #include "webrtc/common_types.h"
 #include "webrtc/common_video/rotation.h"
 #include "webrtc/typedefs.h"
 
 namespace webrtc {
 
 struct RTPAudioHeader {
   uint8_t numEnergy;                  // number of valid entries in arrOfEnergy
   uint8_t arrOfEnergy[kRtpCsrcSize];  // one energy byte (0-9) per channel
   bool isCNG;                         // is this CNG
   uint8_t channel;                    // number of channels 2 = stereo
 };
 
 const int16_t kNoPictureId = -1;
+const int16_t kMaxOneBytePictureId = 0x7F;    // 7 bits
+const int16_t kMaxTwoBytePictureId = 0x7FFF;  // 15 bits
 const int16_t kNoTl0PicIdx = -1;
 const uint8_t kNoTemporalIdx = 0xFF;
+const uint8_t kNoSpatialIdx = 0xFF;
+const uint8_t kNoGofIdx = 0xFF;
+const uint8_t kNumVp9Buffers = 8;
+const size_t kMaxVp9RefPics = 3;
+const size_t kMaxVp9FramesInGof = 0xFF;  // 8 bits
+const size_t kMaxVp9NumberOfSpatialLayers = 8;
 const int kNoKeyIdx = -1;
 
 struct RTPVideoHeaderVP8 {
   void InitRTPVideoHeaderVP8() {
     nonReference = false;
     pictureId = kNoPictureId;
     tl0PicIdx = kNoTl0PicIdx;
     temporalIdx = kNoTemporalIdx;
@@ -56,47 +65,174 @@ struct RTPVideoHeaderVP8 {
   bool layerSync;             // This frame is a layer sync frame.
                               // Disabled if temporalIdx == kNoTemporalIdx.
   int keyIdx;                 // 5 bits; kNoKeyIdx means not used.
   int partitionId;            // VP8 partition ID
   bool beginningOfPartition;  // True if this packet is the first
                               // in a VP8 partition. Otherwise false
 };
 
+enum TemporalStructureMode {
+  kTemporalStructureMode1,    // 1 temporal layer structure - i.e., IPPP...
+  kTemporalStructureMode2,    // 2 temporal layers 0-1-0-1...
+  kTemporalStructureMode3     // 3 temporal layers 0-2-1-2-0-2-1-2...
+};
+
+struct GofInfoVP9 {
+  void SetGofInfoVP9(TemporalStructureMode tm) {
+    switch (tm) {
+      case kTemporalStructureMode1:
+        num_frames_in_gof = 1;
+        temporal_idx[0] = 0;
+        temporal_up_switch[0] = false;
+        num_ref_pics[0] = 1;
+        pid_diff[0][0] = 1;
+        break;
+      case kTemporalStructureMode2:
+        num_frames_in_gof = 2;
+        temporal_idx[0] = 0;
+        temporal_up_switch[0] = false;
+        num_ref_pics[0] = 1;
+        pid_diff[0][0] = 2;
+
+        temporal_idx[1] = 1;
+        temporal_up_switch[1] = true;
+        num_ref_pics[1] = 1;
+        pid_diff[1][0] = 1;
+        break;
+      case kTemporalStructureMode3:
+        num_frames_in_gof = 4;
+        temporal_idx[0] = 0;
+        temporal_up_switch[0] = false;
+        num_ref_pics[0] = 1;
+        pid_diff[0][0] = 4;
+
+        temporal_idx[1] = 2;
+        temporal_up_switch[1] = true;
+        num_ref_pics[1] = 1;
+        pid_diff[1][0] = 1;
+
+        temporal_idx[2] = 1;
+        temporal_up_switch[2] = true;
+        num_ref_pics[2] = 1;
+        pid_diff[2][0] = 2;
+
+        temporal_idx[3] = 2;
+        temporal_up_switch[3] = false;
+        num_ref_pics[3] = 2;
+        pid_diff[3][0] = 1;
+        pid_diff[3][1] = 2;
+        break;
+      default:
+        assert(false);
+    }
+  }
+
+  void CopyGofInfoVP9(const GofInfoVP9& src) {
+    num_frames_in_gof = src.num_frames_in_gof;
+    for (size_t i = 0; i < num_frames_in_gof; ++i) {
+      temporal_idx[i] = src.temporal_idx[i];
+      temporal_up_switch[i] = src.temporal_up_switch[i];
+      num_ref_pics[i] = src.num_ref_pics[i];
+      for (uint8_t r = 0; r < num_ref_pics[i]; ++r) {
+        pid_diff[i][r] = src.pid_diff[i][r];
+      }
+    }
+  }
+
+  size_t num_frames_in_gof;
+  uint8_t temporal_idx[kMaxVp9FramesInGof];
+  bool temporal_up_switch[kMaxVp9FramesInGof];
+  uint8_t num_ref_pics[kMaxVp9FramesInGof];
+  uint8_t pid_diff[kMaxVp9FramesInGof][kMaxVp9RefPics];
+};
+
+struct RTPVideoHeaderVP9 {
+  void InitRTPVideoHeaderVP9() {
+    inter_pic_predicted = false;
+    flexible_mode = false;
+    beginning_of_frame = false;
+    end_of_frame = false;
+    ss_data_available = false;
+    picture_id = kNoPictureId;
+    max_picture_id = kMaxTwoBytePictureId;
+    tl0_pic_idx = kNoTl0PicIdx;
+    temporal_idx = kNoTemporalIdx;
+    spatial_idx = kNoSpatialIdx;
+    temporal_up_switch = false;
+    inter_layer_predicted = false;
+    gof_idx = kNoGofIdx;
+    num_ref_pics = 0;
+    num_spatial_layers = 1;
+  }
+
+  bool inter_pic_predicted;  // This layer frame is dependent on previously
+                             // coded frame(s).
+  bool flexible_mode;        // This frame is in flexible mode.
+  bool beginning_of_frame;   // True if this packet is the first in a VP9 layer
+                             // frame.
+  bool end_of_frame;  // True if this packet is the last in a VP9 layer frame.
+  bool ss_data_available;  // True if SS data is available in this payload
+                           // descriptor.
+  int16_t picture_id;      // PictureID index, 15 bits;
+                              // kNoPictureId if PictureID does not exist.
+  int16_t max_picture_id;  // Maximum picture ID index; either 0x7F or 0x7FFF;
+  int16_t tl0_pic_idx;     // TL0PIC_IDX, 8 bits;
+                              // kNoTl0PicIdx means no value provided.
+  uint8_t temporal_idx;    // Temporal layer index, or kNoTemporalIdx.
+  uint8_t spatial_idx;     // Spatial layer index, or kNoSpatialIdx.
+  bool temporal_up_switch;  // True if upswitch to higher frame rate is possible
+                            // starting from this frame.
+  bool inter_layer_predicted;  // Frame is dependent on directly lower spatial
+                               // layer frame.
+
+  uint8_t gof_idx;  // Index to predefined temporal frame info in SS data.
+
+  uint8_t num_ref_pics;  // Number of reference pictures used by this layer
+                         // frame.
+  uint8_t pid_diff[kMaxVp9RefPics];  // P_DIFF signaled to derive the PictureID
+                                     // of the reference pictures.
+  int16_t ref_picture_id[kMaxVp9RefPics];  // PictureID of reference pictures.
+
+  // SS data.
+  size_t num_spatial_layers;  // Always populated.
+  bool spatial_layer_resolution_present;
+  uint16_t width[kMaxVp9NumberOfSpatialLayers];
+  uint16_t height[kMaxVp9NumberOfSpatialLayers];
+  GofInfoVP9 gof;
+};
+
+#if WEBRTC_48_H264_IMPL
+// The packetization types that we support: single, aggregated, and fragmented.
+enum H264PacketizationTypes {
+  kH264SingleNalu,  // This packet contains a single NAL unit.
+  kH264StapA,       // This packet contains STAP-A (single time
+                    // aggregation) packets. If this packet has an
+                    // associated NAL unit type, it'll be for the
+                    // first such aggregated packet.
+  kH264FuA,         // This packet contains a FU-A (fragmentation
+                    // unit) packet, meaning it is a part of a frame
+                    // that was too large to fit into a single packet.
+};
+
+struct RTPVideoHeaderH264 {
+  uint8_t nalu_type;  // The NAL unit type. If this is a header for a
+                      // fragmented packet, it's the NAL unit type of
+                      // the original data. If this is the header for an
+                      // aggregated packet, it's the NAL unit type of
+                      // the first NAL unit in the packet.
+  H264PacketizationTypes packetization_type;
+};
+#else
+// Mozilla's OpenH264 implementation
 struct RTPVideoHeaderH264 {
   bool stap_a;
   bool single_nalu;
 };
-
-// XXX fix vp9 (bug 1138629)
-struct RTPVideoHeaderVP9 {
-  void InitRTPVideoHeaderVP9() {
-    nonReference = false;
-    pictureId = kNoPictureId;
-    tl0PicIdx = kNoTl0PicIdx;
-    temporalIdx = kNoTemporalIdx;
-    layerSync = false;
-    keyIdx = kNoKeyIdx;
-    partitionId = 0;
-    beginningOfPartition = false;
-  }
-
-  bool nonReference;          // Frame is discardable.
-  int16_t pictureId;          // Picture ID index, 15 bits;
-                              // kNoPictureId if PictureID does not exist.
-  int16_t tl0PicIdx;          // TL0PIC_IDX, 8 bits;
-                              // kNoTl0PicIdx means no value provided.
-  uint8_t temporalIdx;        // Temporal layer index, or kNoTemporalIdx.
-  bool layerSync;             // This frame is a layer sync frame.
-                              // Disabled if temporalIdx == kNoTemporalIdx.
-  int keyIdx;                 // 5 bits; kNoKeyIdx means not used.
-  int partitionId;            // VP9 partition ID
-  bool beginningOfPartition;  // True if this packet is the first
-                              // in a VP9 partition. Otherwise false
-};
+#endif
 
 union RTPVideoTypeHeader {
   RTPVideoHeaderVP8 VP8;
   RTPVideoHeaderVP9 VP9;
   RTPVideoHeaderH264 H264;
 };
 
 enum RtpVideoCodecTypes {
@@ -606,16 +742,28 @@ inline AudioFrame& AudioFrame::Append(co
   int offset = samples_per_channel_ * num_channels_;
   for (int i = 0; i < rhs.samples_per_channel_ * rhs.num_channels_; i++) {
     data_[offset + i] = rhs.data_[i];
   }
   samples_per_channel_ += rhs.samples_per_channel_;
   return *this;
 }
 
+namespace {
+inline int16_t ClampToInt16(int32_t input) {
+  if (input < -0x00008000) {
+    return -0x8000;
+  } else if (input > 0x00007FFF) {
+    return 0x7FFF;
+  } else {
+    return static_cast<int16_t>(input);
+  }
+}
+}
+
 inline AudioFrame& AudioFrame::operator+=(const AudioFrame& rhs) {
   // Sanity check
   assert((num_channels_ > 0) && (num_channels_ < 3));
   assert(interleaved_ == rhs.interleaved_);
   if ((num_channels_ > 2) || (num_channels_ < 1)) return *this;
   if (num_channels_ != rhs.num_channels_) return *this;
 
   bool noPrevData = false;
@@ -638,25 +786,19 @@ inline AudioFrame& AudioFrame::operator+
   if (speech_type_ != rhs.speech_type_) speech_type_ = kUndefined;
 
   if (noPrevData) {
     memcpy(data_, rhs.data_,
            sizeof(int16_t) * rhs.samples_per_channel_ * num_channels_);
   } else {
     // IMPROVEMENT this can be done very fast in assembly
     for (int i = 0; i < samples_per_channel_ * num_channels_; i++) {
-      int32_t wrapGuard =
+      int32_t wrap_guard =
           static_cast<int32_t>(data_[i]) + static_cast<int32_t>(rhs.data_[i]);
-      if (wrapGuard < -32768) {
-        data_[i] = -32768;
-      } else if (wrapGuard > 32767) {
-        data_[i] = 32767;
-      } else {
-        data_[i] = (int16_t)wrapGuard;
-      }
+      data_[i] = ClampToInt16(wrap_guard);
     }
   }
   energy_ = 0xffffffff;
   return *this;
 }
 
 inline AudioFrame& AudioFrame::operator-=(const AudioFrame& rhs) {
   // Sanity check
@@ -669,37 +811,44 @@ inline AudioFrame& AudioFrame::operator-
     return *this;
   }
   if ((vad_activity_ != kVadPassive) || rhs.vad_activity_ != kVadPassive) {
     vad_activity_ = kVadUnknown;
   }
   speech_type_ = kUndefined;
 
   for (int i = 0; i < samples_per_channel_ * num_channels_; i++) {
-    int32_t wrapGuard =
+    int32_t wrap_guard =
         static_cast<int32_t>(data_[i]) - static_cast<int32_t>(rhs.data_[i]);
-    if (wrapGuard < -32768) {
-      data_[i] = -32768;
-    } else if (wrapGuard > 32767) {
-      data_[i] = 32767;
-    } else {
-      data_[i] = (int16_t)wrapGuard;
-    }
+    data_[i] = ClampToInt16(wrap_guard);
   }
   energy_ = 0xffffffff;
   return *this;
 }
 
 inline bool IsNewerSequenceNumber(uint16_t sequence_number,
                                   uint16_t prev_sequence_number) {
+  // Distinguish between elements that are exactly 0x8000 apart.
+  // If s1>s2 and |s1-s2| = 0x8000: IsNewer(s1,s2)=true, IsNewer(s2,s1)=false
+  // rather than having IsNewer(s1,s2) = IsNewer(s2,s1) = false.
+  if (static_cast<uint16_t>(sequence_number - prev_sequence_number) == 0x8000) {
+    return sequence_number > prev_sequence_number;
+  }
   return sequence_number != prev_sequence_number &&
          static_cast<uint16_t>(sequence_number - prev_sequence_number) < 0x8000;
 }
 
 inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) {
+  // Distinguish between elements that are exactly 0x80000000 apart.
+  // If t1>t2 and |t1-t2| = 0x80000000: IsNewer(t1,t2)=true,
+  // IsNewer(t2,t1)=false
+  // rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false.
+  if (static_cast<uint32_t>(timestamp - prev_timestamp) == 0x80000000) {
+    return timestamp > prev_timestamp;
+  }
   return timestamp != prev_timestamp &&
          static_cast<uint32_t>(timestamp - prev_timestamp) < 0x80000000;
 }
 
 inline bool IsNewerOrSameTimestamp(uint32_t timestamp, uint32_t prev_timestamp) {
   return timestamp == prev_timestamp ||
       static_cast<uint32_t>(timestamp - prev_timestamp) < 0x80000000;
 }
@@ -710,11 +859,51 @@ inline uint16_t LatestSequenceNumber(uin
              ? sequence_number1
              : sequence_number2;
 }
 
 inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t timestamp2) {
   return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2;
 }
 
+// Utility class to unwrap a sequence number to a larger type, for easier
+// handling large ranges. Note that sequence numbers will never be unwrapped
+// to a negative value.
+class SequenceNumberUnwrapper {
+ public:
+  SequenceNumberUnwrapper() : last_seq_(-1) {}
+
+  // Get the unwrapped sequence, but don't update the internal state.
+  int64_t UnwrapWithoutUpdate(uint16_t sequence_number) {
+    if (last_seq_ == -1)
+      return sequence_number;
+
+    uint16_t cropped_last = static_cast<uint16_t>(last_seq_);
+    int64_t delta = sequence_number - cropped_last;
+    if (IsNewerSequenceNumber(sequence_number, cropped_last)) {
+      if (delta < 0)
+        delta += (1 << 16);  // Wrap forwards.
+    } else if (delta > 0 && (last_seq_ + delta - (1 << 16)) >= 0) {
+      // If sequence_number is older but delta is positive, this is a backwards
+      // wrap-around. However, don't wrap backwards past 0 (unwrapped).
+      delta -= (1 << 16);
+    }
+
+    return last_seq_ + delta;
+  }
+
+  // Only update the internal state to the specified last (unwrapped) sequence.
+  void UpdateLast(int64_t last_sequence) { last_seq_ = last_sequence; }
+
+  // Unwrap the sequence number and update the internal state.
+  int64_t Unwrap(uint16_t sequence_number) {
+    int64_t unwrapped = UnwrapWithoutUpdate(sequence_number);
+    UpdateLast(unwrapped);
+    return unwrapped;
+  }
+
+ private:
+  int64_t last_seq_;
+};
+
 }  // namespace webrtc
 
 #endif  // MODULE_COMMON_TYPES_H
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi
@@ -87,16 +87,18 @@
         'source/rtp_sender_video.h',
         'source/video_codec_information.h',
         'source/rtp_format.cc',
         'source/rtp_format.h',
         'source/rtp_format_h264.cc',
         'source/rtp_format_h264.h',
         'source/rtp_format_vp8.cc',
         'source/rtp_format_vp8.h',
+        'source/rtp_format_vp9.cc',
+        'source/rtp_format_vp9.h',
         'source/rtp_format_video_generic.cc',
         'source/rtp_format_video_generic.h',
         'source/vp8_partition_aggregator.cc',
         'source/vp8_partition_aggregator.h',
         # Mocks
         'mocks/mock_rtp_rtcp.h',
         'source/mock/mock_rtp_payload_strategy.h',
       ], # source
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format.cc
@@ -8,44 +8,48 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
 #include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
 
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h"
 
 namespace webrtc {
 RtpPacketizer* RtpPacketizer::Create(RtpVideoCodecTypes type,
                                      size_t max_payload_len,
                                      const RTPVideoTypeHeader* rtp_type_header,
                                      FrameType frame_type) {
   switch (type) {
     case kRtpVideoH264:
       return new RtpPacketizerH264(frame_type, max_payload_len);
     case kRtpVideoVp8:
       assert(rtp_type_header != NULL);
       return new RtpPacketizerVp8(rtp_type_header->VP8, max_payload_len);
     case kRtpVideoVp9:
+      assert(rtp_type_header != NULL);
+      return new RtpPacketizerVp9(rtp_type_header->VP9, max_payload_len);
     case kRtpVideoGeneric:
       return new RtpPacketizerGeneric(frame_type, max_payload_len);
     case kRtpVideoNone:
       assert(false);
   }
   return NULL;
 }
 
 RtpDepacketizer* RtpDepacketizer::Create(RtpVideoCodecTypes type) {
   switch (type) {
     case kRtpVideoH264:
       return new RtpDepacketizerH264();
     case kRtpVideoVp8:
       return new RtpDepacketizerVp8();
-    case kRtpVideoVp9: // XXX fix vp9 packetization (bug 1138629)
+    case kRtpVideoVp9:
+      return new RtpDepacketizerVp9();
     case kRtpVideoGeneric:
       return new RtpDepacketizerGeneric();
     case kRtpVideoNone:
       assert(false);
   }
   return NULL;
 }
 }  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp9.cc
@@ -0,0 +1,743 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include <cmath>
+
+#include "webrtc/base/bitbuffer.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+
+#define RETURN_FALSE_ON_ERROR(x) \
+  if (!(x)) {                    \
+    return false;                \
+  }
+
+namespace webrtc {
+namespace {
+// Length of VP9 payload descriptors' fixed part.
+const size_t kFixedPayloadDescriptorBytes = 1;
+
+// Packet fragmentation mode. If true, packets are split into (almost) equal
+// sizes. Otherwise, as many bytes as possible are fit into one packet.
+const bool kBalancedMode = true;
+
+const uint32_t kReservedBitValue0 = 0;
+
+uint8_t TemporalIdxField(const RTPVideoHeaderVP9& hdr, uint8_t def) {
+  return (hdr.temporal_idx == kNoTemporalIdx) ? def : hdr.temporal_idx;
+}
+
+uint8_t SpatialIdxField(const RTPVideoHeaderVP9& hdr, uint8_t def) {
+  return (hdr.spatial_idx == kNoSpatialIdx) ? def : hdr.spatial_idx;
+}
+
+int16_t Tl0PicIdxField(const RTPVideoHeaderVP9& hdr, uint8_t def) {
+  return (hdr.tl0_pic_idx == kNoTl0PicIdx) ? def : hdr.tl0_pic_idx;
+}
+
+// Picture ID:
+//
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  |   M:0 => picture id is 7 bits.
+//      +-+-+-+-+-+-+-+-+   M:1 => picture id is 15 bits.
+// M:   | EXTENDED PID  |
+//      +-+-+-+-+-+-+-+-+
+//
+size_t PictureIdLength(const RTPVideoHeaderVP9& hdr) {
+  if (hdr.picture_id == kNoPictureId)
+    return 0;
+  return (hdr.max_picture_id == kMaxOneBytePictureId) ? 1 : 2;
+}
+
+bool PictureIdPresent(const RTPVideoHeaderVP9& hdr) {
+  return PictureIdLength(hdr) > 0;
+}
+
+// Layer indices:
+//
+// Flexible mode (F=1):     Non-flexible mode (F=0):
+//
+//      +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+
+//                          |   TL0PICIDX   |
+//                          +-+-+-+-+-+-+-+-+
+//
+size_t LayerInfoLength(const RTPVideoHeaderVP9& hdr) {
+  if (hdr.temporal_idx == kNoTemporalIdx &&
+      hdr.spatial_idx == kNoSpatialIdx) {
+    return 0;
+  }
+  return hdr.flexible_mode ? 1 : 2;
+}
+
+bool LayerInfoPresent(const RTPVideoHeaderVP9& hdr) {
+  return LayerInfoLength(hdr) > 0;
+}
+
+// Reference indices:
+//
+//      +-+-+-+-+-+-+-+-+                P=1,F=1: At least one reference index
+// P,F: | P_DIFF      |N|  up to 3 times          has to be specified.
+//      +-+-+-+-+-+-+-+-+                    N=1: An additional P_DIFF follows
+//                                                current P_DIFF.
+//
+size_t RefIndicesLength(const RTPVideoHeaderVP9& hdr) {
+  if (!hdr.inter_pic_predicted || !hdr.flexible_mode)
+    return 0;
+
+  RTC_DCHECK_GT(hdr.num_ref_pics, 0U);
+  RTC_DCHECK_LE(hdr.num_ref_pics, kMaxVp9RefPics);
+  return hdr.num_ref_pics;
+}
+
+// Scalability structure (SS).
+//
+//      +-+-+-+-+-+-+-+-+
+// V:   | N_S |Y|G|-|-|-|
+//      +-+-+-+-+-+-+-+-+              -|
+// Y:   |     WIDTH     | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+               . N_S + 1 times
+//      |     HEIGHT    | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+              -|
+// G:   |      N_G      | (OPTIONAL)
+//      +-+-+-+-+-+-+-+-+                           -|
+// N_G: |  T  |U| R |-|-| (OPTIONAL)                 .
+//      +-+-+-+-+-+-+-+-+              -|            . N_G times
+//      |    P_DIFF     | (OPTIONAL)    . R times    .
+//      +-+-+-+-+-+-+-+-+              -|           -|
+//
+size_t SsDataLength(const RTPVideoHeaderVP9& hdr) {
+  if (!hdr.ss_data_available)
+    return 0;
+
+  RTC_DCHECK_GT(hdr.num_spatial_layers, 0U);
+  RTC_DCHECK_LE(hdr.num_spatial_layers, kMaxVp9NumberOfSpatialLayers);
+  RTC_DCHECK_LE(hdr.gof.num_frames_in_gof, kMaxVp9FramesInGof);
+  size_t length = 1;                           // V
+  if (hdr.spatial_layer_resolution_present) {
+    length += 4 * hdr.num_spatial_layers;      // Y
+  }
+  if (hdr.gof.num_frames_in_gof > 0) {
+    ++length;                                  // G
+  }
+  // N_G
+  length += hdr.gof.num_frames_in_gof;  // T, U, R
+  for (size_t i = 0; i < hdr.gof.num_frames_in_gof; ++i) {
+    RTC_DCHECK_LE(hdr.gof.num_ref_pics[i], kMaxVp9RefPics);
+    length += hdr.gof.num_ref_pics[i];  // R times
+  }
+  return length;
+}
+
+size_t PayloadDescriptorLengthMinusSsData(const RTPVideoHeaderVP9& hdr) {
+  return kFixedPayloadDescriptorBytes + PictureIdLength(hdr) +
+         LayerInfoLength(hdr) + RefIndicesLength(hdr);
+}
+
+size_t PayloadDescriptorLength(const RTPVideoHeaderVP9& hdr) {
+  return PayloadDescriptorLengthMinusSsData(hdr) + SsDataLength(hdr);
+}
+
+void QueuePacket(size_t start_pos,
+                 size_t size,
+                 bool layer_begin,
+                 bool layer_end,
+                 RtpPacketizerVp9::PacketInfoQueue* packets) {
+  RtpPacketizerVp9::PacketInfo packet_info;
+  packet_info.payload_start_pos = start_pos;
+  packet_info.size = size;
+  packet_info.layer_begin = layer_begin;
+  packet_info.layer_end = layer_end;
+  packets->push(packet_info);
+}
+
+// Picture ID:
+//
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  |   M:0 => picture id is 7 bits.
+//      +-+-+-+-+-+-+-+-+   M:1 => picture id is 15 bits.
+// M:   | EXTENDED PID  |
+//      +-+-+-+-+-+-+-+-+
+//
+bool WritePictureId(const RTPVideoHeaderVP9& vp9,
+                    rtc::BitBufferWriter* writer) {
+  bool m_bit = (PictureIdLength(vp9) == 2);
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(m_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.picture_id, m_bit ? 15 : 7));
+  return true;
+}
+
+// Layer indices:
+//
+// Flexible mode (F=1):
+//
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+
+//
+bool WriteLayerInfoCommon(const RTPVideoHeaderVP9& vp9,
+                          rtc::BitBufferWriter* writer) {
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(TemporalIdxField(vp9, 0), 3));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.temporal_up_switch ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(SpatialIdxField(vp9, 0), 3));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.inter_layer_predicted ? 1: 0, 1));
+  return true;
+}
+
+// Non-flexible mode (F=0):
+//
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+
+//      |   TL0PICIDX   |
+//      +-+-+-+-+-+-+-+-+
+//
+bool WriteLayerInfoNonFlexibleMode(const RTPVideoHeaderVP9& vp9,
+                                   rtc::BitBufferWriter* writer) {
+  RETURN_FALSE_ON_ERROR(writer->WriteUInt8(Tl0PicIdxField(vp9, 0)));
+  return true;
+}
+
+bool WriteLayerInfo(const RTPVideoHeaderVP9& vp9,
+                    rtc::BitBufferWriter* writer) {
+  if (!WriteLayerInfoCommon(vp9, writer))
+    return false;
+
+  if (vp9.flexible_mode)
+    return true;
+
+  return WriteLayerInfoNonFlexibleMode(vp9, writer);
+}
+
+// Reference indices:
+//
+//      +-+-+-+-+-+-+-+-+                P=1,F=1: At least one reference index
+// P,F: | P_DIFF      |N|  up to 3 times          has to be specified.
+//      +-+-+-+-+-+-+-+-+                    N=1: An additional P_DIFF follows
+//                                                current P_DIFF.
+//
+bool WriteRefIndices(const RTPVideoHeaderVP9& vp9,
+                     rtc::BitBufferWriter* writer) {
+  if (!PictureIdPresent(vp9) ||
+      vp9.num_ref_pics == 0 || vp9.num_ref_pics > kMaxVp9RefPics) {
+    return false;
+  }
+  for (uint8_t i = 0; i < vp9.num_ref_pics; ++i) {
+    bool n_bit = !(i == vp9.num_ref_pics - 1);
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.pid_diff[i], 7));
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(n_bit ? 1 : 0, 1));
+  }
+  return true;
+}
+
+// Scalability structure (SS).
+//
+//      +-+-+-+-+-+-+-+-+
+// V:   | N_S |Y|G|-|-|-|
+//      +-+-+-+-+-+-+-+-+              -|
+// Y:   |     WIDTH     | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+               . N_S + 1 times
+//      |     HEIGHT    | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+              -|
+// G:   |      N_G      | (OPTIONAL)
+//      +-+-+-+-+-+-+-+-+                           -|
+// N_G: |  T  |U| R |-|-| (OPTIONAL)                 .
+//      +-+-+-+-+-+-+-+-+              -|            . N_G times
+//      |    P_DIFF     | (OPTIONAL)    . R times    .
+//      +-+-+-+-+-+-+-+-+              -|           -|
+//
+bool WriteSsData(const RTPVideoHeaderVP9& vp9, rtc::BitBufferWriter* writer) {
+  RTC_DCHECK_GT(vp9.num_spatial_layers, 0U);
+  RTC_DCHECK_LE(vp9.num_spatial_layers, kMaxVp9NumberOfSpatialLayers);
+  RTC_DCHECK_LE(vp9.gof.num_frames_in_gof, kMaxVp9FramesInGof);
+  bool g_bit = vp9.gof.num_frames_in_gof > 0;
+
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.num_spatial_layers - 1, 3));
+  RETURN_FALSE_ON_ERROR(
+      writer->WriteBits(vp9.spatial_layer_resolution_present ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(g_bit ? 1 : 0, 1));  // G
+  RETURN_FALSE_ON_ERROR(writer->WriteBits(kReservedBitValue0, 3));
+
+  if (vp9.spatial_layer_resolution_present) {
+    for (size_t i = 0; i < vp9.num_spatial_layers; ++i) {
+      RETURN_FALSE_ON_ERROR(writer->WriteUInt16(vp9.width[i]));
+      RETURN_FALSE_ON_ERROR(writer->WriteUInt16(vp9.height[i]));
+    }
+  }
+  if (g_bit) {
+    RETURN_FALSE_ON_ERROR(writer->WriteUInt8(vp9.gof.num_frames_in_gof));
+  }
+  for (size_t i = 0; i < vp9.gof.num_frames_in_gof; ++i) {
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.gof.temporal_idx[i], 3));
+    RETURN_FALSE_ON_ERROR(
+        writer->WriteBits(vp9.gof.temporal_up_switch[i] ? 1 : 0, 1));
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(vp9.gof.num_ref_pics[i], 2));
+    RETURN_FALSE_ON_ERROR(writer->WriteBits(kReservedBitValue0, 2));
+    for (uint8_t r = 0; r < vp9.gof.num_ref_pics[i]; ++r) {
+      RETURN_FALSE_ON_ERROR(writer->WriteUInt8(vp9.gof.pid_diff[i][r]));
+    }
+  }
+  return true;
+}
+
+// Picture ID:
+//
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  |   M:0 => picture id is 7 bits.
+//      +-+-+-+-+-+-+-+-+   M:1 => picture id is 15 bits.
+// M:   | EXTENDED PID  |
+//      +-+-+-+-+-+-+-+-+
+//
+bool ParsePictureId(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  uint32_t picture_id;
+  uint32_t m_bit;
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&m_bit, 1));
+  if (m_bit) {
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&picture_id, 15));
+    vp9->max_picture_id = kMaxTwoBytePictureId;
+  } else {
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&picture_id, 7));
+    vp9->max_picture_id = kMaxOneBytePictureId;
+  }
+  vp9->picture_id = picture_id;
+  return true;
+}
+
+// Layer indices (flexible mode):
+//
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+
+//
+bool ParseLayerInfoCommon(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  uint32_t t, u_bit, s, d_bit;
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&t, 3));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&u_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&s, 3));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&d_bit, 1));
+  vp9->temporal_idx = t;
+  vp9->temporal_up_switch = u_bit ? true : false;
+  vp9->spatial_idx = s;
+  vp9->inter_layer_predicted = d_bit ? true : false;
+  return true;
+}
+
+// Layer indices (non-flexible mode):
+//
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D|
+//      +-+-+-+-+-+-+-+-+
+//      |   TL0PICIDX   |
+//      +-+-+-+-+-+-+-+-+
+//
+bool ParseLayerInfoNonFlexibleMode(rtc::BitBuffer* parser,
+                                   RTPVideoHeaderVP9* vp9) {
+  uint8_t tl0picidx;
+  RETURN_FALSE_ON_ERROR(parser->ReadUInt8(&tl0picidx));
+  vp9->tl0_pic_idx = tl0picidx;
+  return true;
+}
+
+bool ParseLayerInfo(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  if (!ParseLayerInfoCommon(parser, vp9))
+    return false;
+
+  if (vp9->flexible_mode)
+    return true;
+
+  return ParseLayerInfoNonFlexibleMode(parser, vp9);
+}
+
+// Reference indices:
+//
+//      +-+-+-+-+-+-+-+-+                P=1,F=1: At least one reference index
+// P,F: | P_DIFF      |N|  up to 3 times          has to be specified.
+//      +-+-+-+-+-+-+-+-+                    N=1: An additional P_DIFF follows
+//                                                current P_DIFF.
+//
+bool ParseRefIndices(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  if (vp9->picture_id == kNoPictureId)
+    return false;
+
+  vp9->num_ref_pics = 0;
+  uint32_t n_bit;
+  do {
+    if (vp9->num_ref_pics == kMaxVp9RefPics)
+      return false;
+
+    uint32_t p_diff;
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&p_diff, 7));
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&n_bit, 1));
+
+    vp9->pid_diff[vp9->num_ref_pics] = p_diff;
+    uint32_t scaled_pid = vp9->picture_id;
+    if (p_diff > scaled_pid) {
+      // TODO(asapersson): Max should correspond to the picture id of last wrap.
+      scaled_pid += vp9->max_picture_id + 1;
+    }
+    vp9->ref_picture_id[vp9->num_ref_pics++] = scaled_pid - p_diff;
+  } while (n_bit);
+
+  return true;
+}
+
+// Scalability structure (SS).
+//
+//      +-+-+-+-+-+-+-+-+
+// V:   | N_S |Y|G|-|-|-|
+//      +-+-+-+-+-+-+-+-+              -|
+// Y:   |     WIDTH     | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+               . N_S + 1 times
+//      |     HEIGHT    | (OPTIONAL)    .
+//      +               +               .
+//      |               | (OPTIONAL)    .
+//      +-+-+-+-+-+-+-+-+              -|
+// G:   |      N_G      | (OPTIONAL)
+//      +-+-+-+-+-+-+-+-+                           -|
+// N_G: |  T  |U| R |-|-| (OPTIONAL)                 .
+//      +-+-+-+-+-+-+-+-+              -|            . N_G times
+//      |    P_DIFF     | (OPTIONAL)    . R times    .
+//      +-+-+-+-+-+-+-+-+              -|           -|
+//
+bool ParseSsData(rtc::BitBuffer* parser, RTPVideoHeaderVP9* vp9) {
+  uint32_t n_s, y_bit, g_bit;
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&n_s, 3));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&y_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser->ReadBits(&g_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser->ConsumeBits(3));
+  vp9->num_spatial_layers = n_s + 1;
+  vp9->spatial_layer_resolution_present = y_bit ? true : false;
+  vp9->gof.num_frames_in_gof = 0;
+
+  if (y_bit) {
+    for (size_t i = 0; i < vp9->num_spatial_layers; ++i) {
+      RETURN_FALSE_ON_ERROR(parser->ReadUInt16(&vp9->width[i]));
+      RETURN_FALSE_ON_ERROR(parser->ReadUInt16(&vp9->height[i]));
+    }
+  }
+  if (g_bit) {
+    uint8_t n_g;
+    RETURN_FALSE_ON_ERROR(parser->ReadUInt8(&n_g));
+    vp9->gof.num_frames_in_gof = n_g;
+  }
+  for (size_t i = 0; i < vp9->gof.num_frames_in_gof; ++i) {
+    uint32_t t, u_bit, r;
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&t, 3));
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&u_bit, 1));
+    RETURN_FALSE_ON_ERROR(parser->ReadBits(&r, 2));
+    RETURN_FALSE_ON_ERROR(parser->ConsumeBits(2));
+    vp9->gof.temporal_idx[i] = t;
+    vp9->gof.temporal_up_switch[i] = u_bit ? true : false;
+    vp9->gof.num_ref_pics[i] = r;
+
+    for (uint8_t p = 0; p < vp9->gof.num_ref_pics[i]; ++p) {
+      uint8_t p_diff;
+      RETURN_FALSE_ON_ERROR(parser->ReadUInt8(&p_diff));
+      vp9->gof.pid_diff[i][p] = p_diff;
+    }
+  }
+  return true;
+}
+
+// Gets the size of next payload chunk to send. Returns 0 on error.
+size_t CalcNextSize(size_t max_length, size_t rem_bytes) {
+  if (max_length == 0 || rem_bytes == 0) {
+    return 0;
+  }
+  if (kBalancedMode) {
+    size_t num_frags = std::ceil(static_cast<double>(rem_bytes) / max_length);
+    return static_cast<size_t>(
+        static_cast<double>(rem_bytes) / num_frags + 0.5);
+  }
+  return max_length >= rem_bytes ? rem_bytes : max_length;
+}
+}  // namespace
+
+
+RtpPacketizerVp9::RtpPacketizerVp9(const RTPVideoHeaderVP9& hdr,
+                                   size_t max_payload_length)
+    : hdr_(hdr),
+      max_payload_length_(max_payload_length),
+      payload_(nullptr),
+      payload_size_(0) {
+}
+
+RtpPacketizerVp9::~RtpPacketizerVp9() {
+}
+
+ProtectionType RtpPacketizerVp9::GetProtectionType() {
+  bool protect =
+      hdr_.temporal_idx == 0 || hdr_.temporal_idx == kNoTemporalIdx;
+  return protect ? kProtectedPacket : kUnprotectedPacket;
+}
+
+StorageType RtpPacketizerVp9::GetStorageType(uint32_t retransmission_settings) {
+  StorageType storage = kAllowRetransmission;
+  if (hdr_.temporal_idx == 0 &&
+      !(retransmission_settings & kRetransmitBaseLayer)) {
+    storage = kDontRetransmit;
+  } else if (hdr_.temporal_idx != kNoTemporalIdx && hdr_.temporal_idx > 0 &&
+             !(retransmission_settings & kRetransmitHigherLayers)) {
+    storage = kDontRetransmit;
+  }
+  return storage;
+}
+
+std::string RtpPacketizerVp9::ToString() {
+  return "RtpPacketizerVp9";
+}
+
+void RtpPacketizerVp9::SetPayloadData(
+    const uint8_t* payload,
+    size_t payload_size,
+    const RTPFragmentationHeader* fragmentation) {
+  payload_ = payload;
+  payload_size_ = payload_size;
+  GeneratePackets();
+}
+
+void RtpPacketizerVp9::GeneratePackets() {
+  if (max_payload_length_ < PayloadDescriptorLength(hdr_) + 1) {
+    LOG(LS_ERROR) << "Payload header and one payload byte won't fit.";
+    return;
+  }
+  size_t bytes_processed = 0;
+  while (bytes_processed < payload_size_) {
+    size_t rem_bytes = payload_size_ - bytes_processed;
+    size_t rem_payload_len = max_payload_length_ -
+         (bytes_processed ? PayloadDescriptorLengthMinusSsData(hdr_)
+                          : PayloadDescriptorLength(hdr_));
+
+    size_t packet_bytes = CalcNextSize(rem_payload_len, rem_bytes);
+    if (packet_bytes == 0) {
+      LOG(LS_ERROR) << "Failed to generate VP9 packets.";
+      while (!packets_.empty())
+        packets_.pop();
+      return;
+    }
+    QueuePacket(bytes_processed, packet_bytes, bytes_processed == 0,
+                rem_bytes == packet_bytes, &packets_);
+    bytes_processed += packet_bytes;
+  }
+  assert(bytes_processed == payload_size_);
+}
+
+bool RtpPacketizerVp9::NextPacket(uint8_t* buffer,
+                                  size_t* bytes_to_send,
+                                  bool* last_packet) {
+  if (packets_.empty()) {
+    return false;
+  }
+  PacketInfo packet_info = packets_.front();
+  packets_.pop();
+
+  if (!WriteHeaderAndPayload(packet_info, buffer, bytes_to_send)) {
+    return false;
+  }
+  *last_packet =
+      packets_.empty() && (hdr_.spatial_idx == kNoSpatialIdx ||
+                           hdr_.spatial_idx == hdr_.num_spatial_layers - 1);
+  return true;
+}
+
+// VP9 format:
+//
+// Payload descriptor for F = 1 (flexible mode)
+//       0 1 2 3 4 5 6 7
+//      +-+-+-+-+-+-+-+-+
+//      |I|P|L|F|B|E|V|-| (REQUIRED)
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  | (RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+// M:   | EXTENDED PID  | (RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D| (CONDITIONALLY RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+                             -|
+// P,F: | P_DIFF      |N| (CONDITIONALLY RECOMMENDED)  . up to 3 times
+//      +-+-+-+-+-+-+-+-+                             -|
+// V:   | SS            |
+//      | ..            |
+//      +-+-+-+-+-+-+-+-+
+//
+// Payload descriptor for F = 0 (non-flexible mode)
+//       0 1 2 3 4 5 6 7
+//      +-+-+-+-+-+-+-+-+
+//      |I|P|L|F|B|E|V|-| (REQUIRED)
+//      +-+-+-+-+-+-+-+-+
+// I:   |M| PICTURE ID  | (RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+// M:   | EXTENDED PID  | (RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+// L:   |  T  |U|  S  |D| (CONDITIONALLY RECOMMENDED)
+//      +-+-+-+-+-+-+-+-+
+//      |   TL0PICIDX   | (CONDITIONALLY REQUIRED)
+//      +-+-+-+-+-+-+-+-+
+// V:   | SS            |
+//      | ..            |
+//      +-+-+-+-+-+-+-+-+
+
+bool RtpPacketizerVp9::WriteHeaderAndPayload(const PacketInfo& packet_info,
+                                             uint8_t* buffer,
+                                             size_t* bytes_to_send) const {
+  size_t header_length;
+  if (!WriteHeader(packet_info, buffer, &header_length))
+    return false;
+
+  // Copy payload data.
+  memcpy(&buffer[header_length],
+         &payload_[packet_info.payload_start_pos], packet_info.size);
+
+  *bytes_to_send = header_length + packet_info.size;
+  return true;
+}
+
+bool RtpPacketizerVp9::WriteHeader(const PacketInfo& packet_info,
+                                   uint8_t* buffer,
+                                   size_t* header_length) const {
+  // Required payload descriptor byte.
+  bool i_bit = PictureIdPresent(hdr_);
+  bool p_bit = hdr_.inter_pic_predicted;
+  bool l_bit = LayerInfoPresent(hdr_);
+  bool f_bit = hdr_.flexible_mode;
+  bool b_bit = packet_info.layer_begin;
+  bool e_bit = packet_info.layer_end;
+  bool v_bit = hdr_.ss_data_available && b_bit;
+
+  rtc::BitBufferWriter writer(buffer, max_payload_length_);
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(i_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(p_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(l_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(f_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(b_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(e_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(v_bit ? 1 : 0, 1));
+  RETURN_FALSE_ON_ERROR(writer.WriteBits(kReservedBitValue0, 1));
+
+  // Add fields that are present.
+  if (i_bit && !WritePictureId(hdr_, &writer)) {
+    LOG(LS_ERROR) << "Failed writing VP9 picture id.";
+    return false;
+  }
+  if (l_bit && !WriteLayerInfo(hdr_, &writer)) {
+    LOG(LS_ERROR) << "Failed writing VP9 layer info.";
+    return false;
+  }
+  if (p_bit && f_bit && !WriteRefIndices(hdr_, &writer)) {
+    LOG(LS_ERROR) << "Failed writing VP9 ref indices.";
+    return false;
+  }
+  if (v_bit && !WriteSsData(hdr_, &writer)) {
+    LOG(LS_ERROR) << "Failed writing VP9 SS data.";
+    return false;
+  }
+
+  size_t offset_bytes = 0;
+  size_t offset_bits = 0;
+  writer.GetCurrentOffset(&offset_bytes, &offset_bits);
+  assert(offset_bits == 0);
+
+  *header_length = offset_bytes;
+  return true;
+}
+
+bool RtpDepacketizerVp9::Parse(ParsedPayload* parsed_payload,
+                               const uint8_t* payload,
+                               size_t payload_length) {
+  assert(parsed_payload != nullptr);
+  if (payload_length == 0) {
+    LOG(LS_ERROR) << "Payload length is zero.";
+    return false;
+  }
+
+  // Parse mandatory first byte of payload descriptor.
+  rtc::BitBuffer parser(payload, payload_length);
+  uint32_t i_bit, p_bit, l_bit, f_bit, b_bit, e_bit, v_bit;
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&i_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&p_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&l_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&f_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&b_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&e_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ReadBits(&v_bit, 1));
+  RETURN_FALSE_ON_ERROR(parser.ConsumeBits(1));
+
+  // Parsed payload.
+  parsed_payload->type.Video.width = 0;
+  parsed_payload->type.Video.height = 0;
+  parsed_payload->type.Video.simulcastIdx = 0;
+  parsed_payload->type.Video.codec = kRtpVideoVp9;
+
+  parsed_payload->frame_type = p_bit ? kVideoFrameDelta : kVideoFrameKey;
+
+  RTPVideoHeaderVP9* vp9 = &parsed_payload->type.Video.codecHeader.VP9;
+  vp9->InitRTPVideoHeaderVP9();
+  vp9->inter_pic_predicted = p_bit ? true : false;
+  vp9->flexible_mode = f_bit ? true : false;
+  vp9->beginning_of_frame = b_bit ? true : false;
+  vp9->end_of_frame = e_bit ? true : false;
+  vp9->ss_data_available = v_bit ? true : false;
+  vp9->spatial_idx = 0;
+
+  // Parse fields that are present.
+  if (i_bit && !ParsePictureId(&parser, vp9)) {
+    LOG(LS_ERROR) << "Failed parsing VP9 picture id.";
+    return false;
+  }
+  if (l_bit && !ParseLayerInfo(&parser, vp9)) {
+    LOG(LS_ERROR) << "Failed parsing VP9 layer info.";
+    return false;
+  }
+  if (p_bit && f_bit && !ParseRefIndices(&parser, vp9)) {
+    LOG(LS_ERROR) << "Failed parsing VP9 ref indices.";
+    return false;
+  }
+  if (v_bit) {
+    if (!ParseSsData(&parser, vp9)) {
+      LOG(LS_ERROR) << "Failed parsing VP9 SS data.";
+      return false;
+    }
+    if (vp9->spatial_layer_resolution_present) {
+      // TODO(asapersson): Add support for spatial layers.
+      parsed_payload->type.Video.width = vp9->width[0];
+      parsed_payload->type.Video.height = vp9->height[0];
+    }
+  }
+  parsed_payload->type.Video.isFirstPacket =
+      b_bit && (!l_bit || !vp9->inter_layer_predicted);
+
+  uint64_t rem_bits = parser.RemainingBitCount();
+  assert(rem_bits % 8 == 0);
+  parsed_payload->payload_length = rem_bits / 8;
+  if (parsed_payload->payload_length == 0) {
+    LOG(LS_ERROR) << "Failed parsing VP9 payload data.";
+    return false;
+  }
+  parsed_payload->payload =
+      payload + payload_length - parsed_payload->payload_length;
+
+  return true;
+}
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+//
+// This file contains the declaration of the VP9 packetizer class.
+// A packetizer object is created for each encoded video frame. The
+// constructor is called with the payload data and size.
+//
+// After creating the packetizer, the method NextPacket is called
+// repeatedly to get all packets for the frame. The method returns
+// false as long as there are more packets left to fetch.
+//
+
+#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP9_H_
+#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP9_H_
+
+#include <queue>
+#include <string>
+
+#include "webrtc/base/constructormagic.h"
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class RtpPacketizerVp9 : public RtpPacketizer {
+ public:
+  RtpPacketizerVp9(const RTPVideoHeaderVP9& hdr, size_t max_payload_length);
+
+  virtual ~RtpPacketizerVp9();
+
+  ProtectionType GetProtectionType() override;
+
+  StorageType GetStorageType(uint32_t retransmission_settings) override;
+
+  std::string ToString() override;
+
+  // The payload data must be one encoded VP9 frame.
+  void SetPayloadData(const uint8_t* payload,
+                      size_t payload_size,
+                      const RTPFragmentationHeader* fragmentation) override;
+
+  // Gets the next payload with VP9 payload header.
+  // |buffer| is a pointer to where the output will be written.
+  // |bytes_to_send| is an output variable that will contain number of bytes
+  // written to buffer.
+  // |last_packet| is true for the last packet of the frame, false otherwise
+  // (i.e. call the function again to get the next packet).
+  // Returns true on success, false otherwise.
+  bool NextPacket(uint8_t* buffer,
+                  size_t* bytes_to_send,
+                  bool* last_packet) override;
+
+  typedef struct {
+    size_t payload_start_pos;
+    size_t size;
+    bool layer_begin;
+    bool layer_end;
+  } PacketInfo;
+  typedef std::queue<PacketInfo> PacketInfoQueue;
+
+ private:
+  // Calculates all packet sizes and loads info to packet queue.
+  void GeneratePackets();
+
+  // Writes the payload descriptor header and copies payload to the |buffer|.
+  // |packet_info| determines which part of the payload to write.
+  // |bytes_to_send| contains the number of written bytes to the buffer.
+  // Returns true on success, false otherwise.
+  bool WriteHeaderAndPayload(const PacketInfo& packet_info,
+                             uint8_t* buffer,
+                             size_t* bytes_to_send) const;
+
+  // Writes payload descriptor header to |buffer|.
+  // Returns true on success, false otherwise.
+  bool WriteHeader(const PacketInfo& packet_info,
+                   uint8_t* buffer,
+                   size_t* header_length) const;
+
+  const RTPVideoHeaderVP9 hdr_;
+  const size_t max_payload_length_;  // The max length in bytes of one packet.
+  const uint8_t* payload_;           // The payload data to be packetized.
+  size_t payload_size_;              // The size in bytes of the payload data.
+  PacketInfoQueue packets_;
+
+  DISALLOW_COPY_AND_ASSIGN(RtpPacketizerVp9);
+};
+
+
+class RtpDepacketizerVp9 : public RtpDepacketizer {
+ public:
+  virtual ~RtpDepacketizerVp9() {}
+
+  bool Parse(ParsedPayload* parsed_payload,
+             const uint8_t* payload,
+             size_t payload_length) override;
+};
+
+}  // namespace webrtc
+#endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_FORMAT_VP9_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_format_vp9_unittest.cc
@@ -0,0 +1,690 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+namespace {
+void VerifyHeader(const RTPVideoHeaderVP9& expected,
+                  const RTPVideoHeaderVP9& actual) {
+  EXPECT_EQ(expected.inter_layer_predicted, actual.inter_layer_predicted);
+  EXPECT_EQ(expected.inter_pic_predicted, actual.inter_pic_predicted);
+  EXPECT_EQ(expected.flexible_mode, actual.flexible_mode);
+  EXPECT_EQ(expected.beginning_of_frame, actual.beginning_of_frame);
+  EXPECT_EQ(expected.end_of_frame, actual.end_of_frame);
+  EXPECT_EQ(expected.ss_data_available, actual.ss_data_available);
+  EXPECT_EQ(expected.picture_id, actual.picture_id);
+  EXPECT_EQ(expected.max_picture_id, actual.max_picture_id);
+  EXPECT_EQ(expected.temporal_idx, actual.temporal_idx);
+  EXPECT_EQ(expected.spatial_idx == kNoSpatialIdx ? 0 : expected.spatial_idx,
+      actual.spatial_idx);
+  EXPECT_EQ(expected.gof_idx, actual.gof_idx);
+  EXPECT_EQ(expected.tl0_pic_idx, actual.tl0_pic_idx);
+  EXPECT_EQ(expected.temporal_up_switch, actual.temporal_up_switch);
+
+  EXPECT_EQ(expected.num_ref_pics, actual.num_ref_pics);
+  for (uint8_t i = 0; i < expected.num_ref_pics; ++i) {
+    EXPECT_EQ(expected.pid_diff[i], actual.pid_diff[i]);
+    EXPECT_EQ(expected.ref_picture_id[i], actual.ref_picture_id[i]);
+  }
+  if (expected.ss_data_available) {
+    EXPECT_EQ(expected.spatial_layer_resolution_present,
+              actual.spatial_layer_resolution_present);
+    EXPECT_EQ(expected.num_spatial_layers, actual.num_spatial_layers);
+    if (expected.spatial_layer_resolution_present) {
+      for (size_t i = 0; i < expected.num_spatial_layers; i++) {
+        EXPECT_EQ(expected.width[i], actual.width[i]);
+        EXPECT_EQ(expected.height[i], actual.height[i]);
+      }
+    }
+    EXPECT_EQ(expected.gof.num_frames_in_gof, actual.gof.num_frames_in_gof);
+    for (size_t i = 0; i < expected.gof.num_frames_in_gof; i++) {
+      EXPECT_EQ(expected.gof.temporal_up_switch[i],
+                actual.gof.temporal_up_switch[i]);
+      EXPECT_EQ(expected.gof.temporal_idx[i], actual.gof.temporal_idx[i]);
+      EXPECT_EQ(expected.gof.num_ref_pics[i], actual.gof.num_ref_pics[i]);
+      for (uint8_t j = 0; j < expected.gof.num_ref_pics[i]; j++) {
+        EXPECT_EQ(expected.gof.pid_diff[i][j], actual.gof.pid_diff[i][j]);
+      }
+    }
+  }
+}
+
+void VerifyPayload(const RtpDepacketizer::ParsedPayload& parsed,
+                   const uint8_t* payload,
+                   size_t payload_length) {
+  EXPECT_EQ(payload, parsed.payload);
+  EXPECT_EQ(payload_length, parsed.payload_length);
+  EXPECT_THAT(std::vector<uint8_t>(parsed.payload,
+                                   parsed.payload + parsed.payload_length),
+              ::testing::ElementsAreArray(payload, payload_length));
+}
+
+void ParseAndCheckPacket(const uint8_t* packet,
+                         const RTPVideoHeaderVP9& expected,
+                         size_t expected_hdr_length,
+                         size_t expected_length) {
+  rtc::scoped_ptr<RtpDepacketizer> depacketizer(new RtpDepacketizerVp9());
+  RtpDepacketizer::ParsedPayload parsed;
+  ASSERT_TRUE(depacketizer->Parse(&parsed, packet, expected_length));
+  EXPECT_EQ(kRtpVideoVp9, parsed.type.Video.codec);
+  VerifyHeader(expected, parsed.type.Video.codecHeader.VP9);
+  const size_t kExpectedPayloadLength = expected_length - expected_hdr_length;
+  VerifyPayload(parsed, packet + expected_hdr_length, kExpectedPayloadLength);
+}
+}  // namespace
+
+// Payload descriptor for flexible mode
+//        0 1 2 3 4 5 6 7
+//        +-+-+-+-+-+-+-+-+
+//        |I|P|L|F|B|E|V|-| (REQUIRED)
+//        +-+-+-+-+-+-+-+-+
+//   I:   |M| PICTURE ID  | (RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//   M:   | EXTENDED PID  | (RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//   L:   |  T  |U|  S  |D| (CONDITIONALLY RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+                             -|
+//   P,F: | P_DIFF      |N| (CONDITIONALLY RECOMMENDED)  . up to 3 times
+//        +-+-+-+-+-+-+-+-+                             -|
+//   V:   | SS            |
+//        | ..            |
+//        +-+-+-+-+-+-+-+-+
+//
+// Payload descriptor for non-flexible mode
+//        0 1 2 3 4 5 6 7
+//        +-+-+-+-+-+-+-+-+
+//        |I|P|L|F|B|E|V|-| (REQUIRED)
+//        +-+-+-+-+-+-+-+-+
+//   I:   |M| PICTURE ID  | (RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//   M:   | EXTENDED PID  | (RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//   L:   |  T  |U|  S  |D| (CONDITIONALLY RECOMMENDED)
+//        +-+-+-+-+-+-+-+-+
+//        |   TL0PICIDX   | (CONDITIONALLY REQUIRED)
+//        +-+-+-+-+-+-+-+-+
+//   V:   | SS            |
+//        | ..            |
+//        +-+-+-+-+-+-+-+-+
+
+class RtpPacketizerVp9Test : public ::testing::Test {
+ protected:
+  RtpPacketizerVp9Test() {}
+  virtual void SetUp() {
+    expected_.InitRTPVideoHeaderVP9();
+  }
+
+  rtc::scoped_ptr<uint8_t[]> packet_;
+  rtc::scoped_ptr<uint8_t[]> payload_;
+  size_t payload_size_;
+  size_t payload_pos_;
+  RTPVideoHeaderVP9 expected_;
+  rtc::scoped_ptr<RtpPacketizerVp9> packetizer_;
+
+  void Init(size_t payload_size, size_t packet_size) {
+    payload_.reset(new uint8_t[payload_size]);
+    memset(payload_.get(), 7, payload_size);
+    payload_size_ = payload_size;
+    payload_pos_ = 0;
+    packetizer_.reset(new RtpPacketizerVp9(expected_, packet_size));
+    packetizer_->SetPayloadData(payload_.get(), payload_size_, NULL);
+
+    const int kMaxPayloadDescriptorLength = 100;
+    packet_.reset(new uint8_t[payload_size_ + kMaxPayloadDescriptorLength]);
+  }
+
+  void CheckPayload(const uint8_t* packet,
+                    size_t start_pos,
+                    size_t end_pos,
+                    bool last) {
+    for (size_t i = start_pos; i < end_pos; ++i) {
+      EXPECT_EQ(packet[i], payload_[payload_pos_++]);
+    }
+    EXPECT_EQ(last, payload_pos_ == payload_size_);
+  }
+
+  void CreateParseAndCheckPackets(const size_t* expected_hdr_sizes,
+                                  const size_t* expected_sizes,
+                                  size_t expected_num_packets) {
+    ASSERT_TRUE(packetizer_.get() != NULL);
+    size_t length = 0;
+    bool last = false;
+    if (expected_num_packets == 0) {
+      EXPECT_FALSE(packetizer_->NextPacket(packet_.get(), &length, &last));
+      return;
+    }
+    for (size_t i = 0; i < expected_num_packets; ++i) {
+      EXPECT_TRUE(packetizer_->NextPacket(packet_.get(), &length, &last));
+      EXPECT_EQ(expected_sizes[i], length);
+      RTPVideoHeaderVP9 hdr = expected_;
+      hdr.beginning_of_frame = (i == 0);
+      hdr.end_of_frame = last;
+      ParseAndCheckPacket(packet_.get(), hdr, expected_hdr_sizes[i], length);
+      CheckPayload(packet_.get(), expected_hdr_sizes[i], length, last);
+    }
+    EXPECT_TRUE(last);
+  }
+};
+
+TEST_F(RtpPacketizerVp9Test, TestEqualSizedMode_OnePacket) {
+  const size_t kFrameSize = 25;
+  const size_t kPacketSize = 26;
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:0, F:0, B:1, E:1, V:0  (1hdr + 25 payload)
+  const size_t kExpectedHdrSizes[] = {1};
+  const size_t kExpectedSizes[] = {26};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestEqualSizedMode_TwoPackets) {
+  const size_t kFrameSize = 27;
+  const size_t kPacketSize = 27;
+  Init(kFrameSize, kPacketSize);
+
+  // Two packets:
+  // I:0, P:0, L:0, F:0, B:1, E:0, V:0  (1hdr + 14 payload)
+  // I:0, P:0, L:0, F:0, B:0, E:1, V:0  (1hdr + 13 payload)
+  const size_t kExpectedHdrSizes[] = {1, 1};
+  const size_t kExpectedSizes[] = {15, 14};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestTooShortBufferToFitPayload) {
+  const size_t kFrameSize = 1;
+  const size_t kPacketSize = 1;
+  Init(kFrameSize, kPacketSize);  // 1hdr + 1 payload
+
+  const size_t kExpectedNum = 0;
+  CreateParseAndCheckPackets(NULL, NULL, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestOneBytePictureId) {
+  const size_t kFrameSize = 30;
+  const size_t kPacketSize = 12;
+
+  expected_.picture_id = kMaxOneBytePictureId;   // 2 byte payload descriptor
+  expected_.max_picture_id = kMaxOneBytePictureId;
+  Init(kFrameSize, kPacketSize);
+
+  // Three packets:
+  // I:1, P:0, L:0, F:0, B:1, E:0, V:0 (2hdr + 10 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:0, V:0 (2hdr + 10 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:1, V:0 (2hdr + 10 payload)
+  const size_t kExpectedHdrSizes[] = {2, 2, 2};
+  const size_t kExpectedSizes[] = {12, 12, 12};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestTwoBytePictureId) {
+  const size_t kFrameSize = 31;
+  const size_t kPacketSize = 13;
+
+  expected_.picture_id = kMaxTwoBytePictureId;  // 3 byte payload descriptor
+  Init(kFrameSize, kPacketSize);
+
+  // Four packets:
+  // I:1, P:0, L:0, F:0, B:1, E:0, V:0 (3hdr + 8 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:0, V:0 (3hdr + 8 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:0, V:0 (3hdr + 8 payload)
+  // I:1, P:0, L:0, F:0, B:0, E:1, V:0 (3hdr + 7 payload)
+  const size_t kExpectedHdrSizes[] = {3, 3, 3, 3};
+  const size_t kExpectedSizes[] = {11, 11, 11, 10};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestLayerInfoWithNonFlexibleMode) {
+  const size_t kFrameSize = 30;
+  const size_t kPacketSize = 25;
+
+  expected_.temporal_idx = 3;
+  expected_.temporal_up_switch = true;  // U
+  expected_.num_spatial_layers = 3;
+  expected_.spatial_idx = 2;
+  expected_.inter_layer_predicted = true;  // D
+  expected_.tl0_pic_idx = 117;
+  Init(kFrameSize, kPacketSize);
+
+  // Two packets:
+  //    | I:0, P:0, L:1, F:0, B:1, E:0, V:0 | (3hdr + 15 payload)
+  // L: | T:3, U:1, S:2, D:1 | TL0PICIDX:117 |
+  //    | I:0, P:0, L:1, F:0, B:0, E:1, V:0 | (3hdr + 15 payload)
+  // L: | T:3, U:1, S:2, D:1 | TL0PICIDX:117 |
+  const size_t kExpectedHdrSizes[] = {3, 3};
+  const size_t kExpectedSizes[] = {18, 18};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestLayerInfoWithFlexibleMode) {
+  const size_t kFrameSize = 21;
+  const size_t kPacketSize = 23;
+
+  expected_.flexible_mode = true;
+  expected_.temporal_idx = 3;
+  expected_.temporal_up_switch = true;  // U
+  expected_.num_spatial_layers = 3;
+  expected_.spatial_idx = 2;
+  expected_.inter_layer_predicted = false;  // D
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:1, F:1, B:1, E:1, V:0 (2hdr + 21 payload)
+  // L:   T:3, U:1, S:2, D:0
+  const size_t kExpectedHdrSizes[] = {2};
+  const size_t kExpectedSizes[] = {23};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestRefIdx) {
+  const size_t kFrameSize = 16;
+  const size_t kPacketSize = 21;
+
+  expected_.inter_pic_predicted = true;  // P
+  expected_.flexible_mode = true;        // F
+  expected_.picture_id = 2;
+  expected_.max_picture_id = kMaxOneBytePictureId;
+
+  expected_.num_ref_pics = 3;
+  expected_.pid_diff[0] = 1;
+  expected_.pid_diff[1] = 3;
+  expected_.pid_diff[2] = 127;
+  expected_.ref_picture_id[0] = 1;    // 2 - 1 = 1
+  expected_.ref_picture_id[1] = 127;  // (kMaxPictureId + 1) + 2 - 3 = 127
+  expected_.ref_picture_id[2] = 3;    // (kMaxPictureId + 1) + 2 - 127 = 3
+  Init(kFrameSize, kPacketSize);
+
+  // Two packets:
+  // I:1, P:1, L:0, F:1, B:1, E:1, V:0 (5hdr + 16 payload)
+  // I:   2
+  // P,F: P_DIFF:1,   N:1
+  //      P_DIFF:3,   N:1
+  //      P_DIFF:127, N:0
+  const size_t kExpectedHdrSizes[] = {5};
+  const size_t kExpectedSizes[] = {21};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestRefIdxFailsWithoutPictureId) {
+  const size_t kFrameSize = 16;
+  const size_t kPacketSize = 21;
+
+  expected_.inter_pic_predicted = true;
+  expected_.flexible_mode = true;
+  expected_.num_ref_pics = 1;
+  expected_.pid_diff[0] = 3;
+  Init(kFrameSize, kPacketSize);
+
+  const size_t kExpectedNum = 0;
+  CreateParseAndCheckPackets(NULL, NULL, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestSsDataWithoutSpatialResolutionPresent) {
+  const size_t kFrameSize = 21;
+  const size_t kPacketSize = 26;
+
+  expected_.ss_data_available = true;
+  expected_.num_spatial_layers = 1;
+  expected_.spatial_layer_resolution_present = false;
+  expected_.gof.num_frames_in_gof = 1;
+  expected_.gof.temporal_idx[0] = 0;
+  expected_.gof.temporal_up_switch[0] = true;
+  expected_.gof.num_ref_pics[0] = 1;
+  expected_.gof.pid_diff[0][0] = 4;
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:0, F:0, B:1, E:1, V:1 (5hdr + 21 payload)
+  // N_S:0, Y:0, G:1
+  // N_G:1
+  // T:0, U:1, R:1 | P_DIFF[0][0]:4
+  const size_t kExpectedHdrSizes[] = {5};
+  const size_t kExpectedSizes[] = {26};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestSsDataWithoutGbitPresent) {
+  const size_t kFrameSize = 21;
+  const size_t kPacketSize = 23;
+
+  expected_.ss_data_available = true;
+  expected_.num_spatial_layers = 1;
+  expected_.spatial_layer_resolution_present = false;
+  expected_.gof.num_frames_in_gof = 0;
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:0, F:0, B:1, E:1, V:1 (2hdr + 21 payload)
+  // N_S:0, Y:0, G:0
+  const size_t kExpectedHdrSizes[] = {2};
+  const size_t kExpectedSizes[] = {23};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestSsData) {
+  const size_t kFrameSize = 21;
+  const size_t kPacketSize = 40;
+
+  expected_.ss_data_available = true;
+  expected_.num_spatial_layers = 2;
+  expected_.spatial_layer_resolution_present = true;
+  expected_.width[0] = 640;
+  expected_.width[1] = 1280;
+  expected_.height[0] = 360;
+  expected_.height[1] = 720;
+  expected_.gof.num_frames_in_gof = 3;
+  expected_.gof.temporal_idx[0] = 0;
+  expected_.gof.temporal_idx[1] = 1;
+  expected_.gof.temporal_idx[2] = 2;
+  expected_.gof.temporal_up_switch[0] = true;
+  expected_.gof.temporal_up_switch[1] = true;
+  expected_.gof.temporal_up_switch[2] = false;
+  expected_.gof.num_ref_pics[0] = 0;
+  expected_.gof.num_ref_pics[1] = 3;
+  expected_.gof.num_ref_pics[2] = 2;
+  expected_.gof.pid_diff[1][0] = 5;
+  expected_.gof.pid_diff[1][1] = 6;
+  expected_.gof.pid_diff[1][2] = 7;
+  expected_.gof.pid_diff[2][0] = 8;
+  expected_.gof.pid_diff[2][1] = 9;
+  Init(kFrameSize, kPacketSize);
+
+  // One packet:
+  // I:0, P:0, L:0, F:0, B:1, E:1, V:1 (19hdr + 21 payload)
+  // N_S:1, Y:1, G:1
+  // WIDTH:640   // 2 bytes
+  // HEIGHT:360  // 2 bytes
+  // WIDTH:1280  // 2 bytes
+  // HEIGHT:720  // 2 bytes
+  // N_G:3
+  // T:0, U:1, R:0
+  // T:1, U:1, R:3 | P_DIFF[1][0]:5 | P_DIFF[1][1]:6 | P_DIFF[1][2]:7
+  // T:2, U:0, R:2 | P_DIFF[2][0]:8 | P_DIFF[2][0]:9
+  const size_t kExpectedHdrSizes[] = {19};
+  const size_t kExpectedSizes[] = {40};
+  const size_t kExpectedNum = GTEST_ARRAY_SIZE_(kExpectedSizes);
+  CreateParseAndCheckPackets(kExpectedHdrSizes, kExpectedSizes, kExpectedNum);
+}
+
+TEST_F(RtpPacketizerVp9Test, TestBaseLayerProtectionAndStorageType) {
+  const size_t kFrameSize = 10;
+  const size_t kPacketSize = 12;
+
+  // I:0, P:0, L:1, F:1, B:1, E:1, V:0 (2hdr + 10 payload)
+  // L:   T:0, U:0, S:0, D:0
+  expected_.flexible_mode = true;
+  expected_.temporal_idx = 0;
+  Init(kFrameSize, kPacketSize);
+  EXPECT_EQ(kProtectedPacket, packetizer_->GetProtectionType());
+  EXPECT_EQ(kAllowRetransmission,
+            packetizer_->GetStorageType(kRetransmitBaseLayer));
+  EXPECT_EQ(kDontRetransmit, packetizer_->GetStorageType(kRetransmitOff));
+}
+
+TEST_F(RtpPacketizerVp9Test, TestHigherLayerProtectionAndStorageType) {
+  const size_t kFrameSize = 10;
+  const size_t kPacketSize = 12;
+
+  // I:0, P:0, L:1, F:1, B:1, E:1, V:0 (2hdr + 10 payload)
+  // L:   T:1, U:0, S:0, D:0
+  expected_.flexible_mode = true;
+  expected_.temporal_idx = 1;
+  Init(kFrameSize, kPacketSize);
+  EXPECT_EQ(kUnprotectedPacket, packetizer_->GetProtectionType());
+  EXPECT_EQ(kDontRetransmit, packetizer_->GetStorageType(kRetransmitBaseLayer));
+  EXPECT_EQ(kAllowRetransmission,
+            packetizer_->GetStorageType(kRetransmitHigherLayers));
+}
+
+
+class RtpDepacketizerVp9Test : public ::testing::Test {
+ protected:
+  RtpDepacketizerVp9Test()
+      : depacketizer_(new RtpDepacketizerVp9()) {}
+
+  virtual void SetUp() {
+    expected_.InitRTPVideoHeaderVP9();
+  }
+
+  RTPVideoHeaderVP9 expected_;
+  rtc::scoped_ptr<RtpDepacketizer> depacketizer_;
+};
+
+TEST_F(RtpDepacketizerVp9Test, ParseBasicHeader) {
+  const uint8_t kHeaderLength = 1;
+  uint8_t packet[4] = {0};
+  packet[0] = 0x0C;  // I:0 P:0 L:0 F:0 B:1 E:1 V:0 R:0
+  expected_.beginning_of_frame = true;
+  expected_.end_of_frame = true;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseOneBytePictureId) {
+  const uint8_t kHeaderLength = 2;
+  uint8_t packet[10] = {0};
+  packet[0] = 0x80;  // I:1 P:0 L:0 F:0 B:0 E:0 V:0 R:0
+  packet[1] = kMaxOneBytePictureId;
+
+  expected_.picture_id = kMaxOneBytePictureId;
+  expected_.max_picture_id = kMaxOneBytePictureId;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseTwoBytePictureId) {
+  const uint8_t kHeaderLength = 3;
+  uint8_t packet[10] = {0};
+  packet[0] = 0x80;  // I:1 P:0 L:0 F:0 B:0 E:0 V:0 R:0
+  packet[1] = 0x80 | ((kMaxTwoBytePictureId >> 8) & 0x7F);
+  packet[2] = kMaxTwoBytePictureId & 0xFF;
+
+  expected_.picture_id = kMaxTwoBytePictureId;
+  expected_.max_picture_id = kMaxTwoBytePictureId;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseLayerInfoWithNonFlexibleMode) {
+  const uint8_t kHeaderLength = 3;
+  const uint8_t kTemporalIdx = 2;
+  const uint8_t kUbit = 1;
+  const uint8_t kSpatialIdx = 1;
+  const uint8_t kDbit = 1;
+  const uint8_t kTl0PicIdx = 17;
+  uint8_t packet[13] = {0};
+  packet[0] = 0x20;  // I:0 P:0 L:1 F:0 B:0 E:0 V:0 R:0
+  packet[1] = (kTemporalIdx << 5) | (kUbit << 4) | (kSpatialIdx << 1) | kDbit;
+  packet[2] = kTl0PicIdx;
+
+  // T:2 U:1 S:1 D:1
+  // TL0PICIDX:17
+  expected_.temporal_idx = kTemporalIdx;
+  expected_.temporal_up_switch = kUbit ? true : false;
+  expected_.spatial_idx = kSpatialIdx;
+  expected_.inter_layer_predicted = kDbit ? true : false;
+  expected_.tl0_pic_idx = kTl0PicIdx;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseLayerInfoWithFlexibleMode) {
+  const uint8_t kHeaderLength = 2;
+  const uint8_t kTemporalIdx = 2;
+  const uint8_t kUbit = 1;
+  const uint8_t kSpatialIdx = 0;
+  const uint8_t kDbit = 0;
+  uint8_t packet[13] = {0};
+  packet[0] = 0x38;  // I:0 P:0 L:1 F:1 B:1 E:0 V:0 R:0
+  packet[1] = (kTemporalIdx << 5) | (kUbit << 4) | (kSpatialIdx << 1) | kDbit;
+
+  // I:0 P:0 L:1 F:1 B:1 E:0 V:0
+  // L:   T:2 U:1 S:0 D:0
+  expected_.beginning_of_frame = true;
+  expected_.flexible_mode = true;
+  expected_.temporal_idx = kTemporalIdx;
+  expected_.temporal_up_switch = kUbit ? true : false;
+  expected_.spatial_idx = kSpatialIdx;
+  expected_.inter_layer_predicted = kDbit ? true : false;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseRefIdx) {
+  const uint8_t kHeaderLength = 6;
+  const int16_t kPictureId = 17;
+  const uint8_t kPdiff1 = 17;
+  const uint8_t kPdiff2 = 18;
+  const uint8_t kPdiff3 = 127;
+  uint8_t packet[13] = {0};
+  packet[0] = 0xD8;  // I:1 P:1 L:0 F:1 B:1 E:0 V:0 R:0
+  packet[1] = 0x80 | ((kPictureId >> 8) & 0x7F);  // Two byte pictureID.
+  packet[2] = kPictureId;
+  packet[3] = (kPdiff1 << 1) | 1;  // P_DIFF N:1
+  packet[4] = (kPdiff2 << 1) | 1;  // P_DIFF N:1
+  packet[5] = (kPdiff3 << 1) | 0;  // P_DIFF N:0
+
+  // I:1 P:1 L:0 F:1 B:1 E:0 V:0
+  // I:    PICTURE ID:17
+  // I:
+  // P,F:  P_DIFF:17  N:1 => refPicId = 17 - 17 = 0
+  // P,F:  P_DIFF:18  N:1 => refPicId = (kMaxPictureId + 1) + 17 - 18 = 0x7FFF
+  // P,F:  P_DIFF:127 N:0 => refPicId = (kMaxPictureId + 1) + 17 - 127 = 32658
+  expected_.beginning_of_frame = true;
+  expected_.inter_pic_predicted = true;
+  expected_.flexible_mode = true;
+  expected_.picture_id = kPictureId;
+  expected_.num_ref_pics = 3;
+  expected_.pid_diff[0] = kPdiff1;
+  expected_.pid_diff[1] = kPdiff2;
+  expected_.pid_diff[2] = kPdiff3;
+  expected_.ref_picture_id[0] = 0;
+  expected_.ref_picture_id[1] = 0x7FFF;
+  expected_.ref_picture_id[2] = 32658;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseRefIdxFailsWithNoPictureId) {
+  const uint8_t kPdiff = 3;
+  uint8_t packet[13] = {0};
+  packet[0] = 0x58;            // I:0 P:1 L:0 F:1 B:1 E:0 V:0 R:0
+  packet[1] = (kPdiff << 1);   // P,F:  P_DIFF:3 N:0
+
+  RtpDepacketizer::ParsedPayload parsed;
+  EXPECT_FALSE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseRefIdxFailsWithTooManyRefPics) {
+  const uint8_t kPdiff = 3;
+  uint8_t packet[13] = {0};
+  packet[0] = 0xD8;                  // I:1 P:1 L:0 F:1 B:1 E:0 V:0 R:0
+  packet[1] = kMaxOneBytePictureId;  // I:    PICTURE ID:127
+  packet[2] = (kPdiff << 1) | 1;     // P,F:  P_DIFF:3 N:1
+  packet[3] = (kPdiff << 1) | 1;     // P,F:  P_DIFF:3 N:1
+  packet[4] = (kPdiff << 1) | 1;     // P,F:  P_DIFF:3 N:1
+  packet[5] = (kPdiff << 1) | 0;     // P,F:  P_DIFF:3 N:0
+
+  RtpDepacketizer::ParsedPayload parsed;
+  EXPECT_FALSE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseSsData) {
+  const uint8_t kHeaderLength = 6;
+  const uint8_t kYbit = 0;
+  const size_t kNs = 2;
+  const size_t kNg = 2;
+  uint8_t packet[23] = {0};
+  packet[0] = 0x0A;  // I:0 P:0 L:0 F:0 B:1 E:0 V:1 R:0
+  packet[1] = ((kNs - 1) << 5) | (kYbit << 4) | (1 << 3);  // N_S Y G:1 -
+  packet[2] = kNg;                                         // N_G
+  packet[3] = (0 << 5) | (1 << 4) | (0 << 2) | 0;          // T:0 U:1 R:0 -
+  packet[4] = (2 << 5) | (0 << 4) | (1 << 2) | 0;          // T:2 U:0 R:1 -
+  packet[5] = 33;
+
+  expected_.beginning_of_frame = true;
+  expected_.ss_data_available = true;
+  expected_.num_spatial_layers = kNs;
+  expected_.spatial_layer_resolution_present = kYbit ? true : false;
+  expected_.gof.num_frames_in_gof = kNg;
+  expected_.gof.temporal_idx[0] = 0;
+  expected_.gof.temporal_idx[1] = 2;
+  expected_.gof.temporal_up_switch[0] = true;
+  expected_.gof.temporal_up_switch[1] = false;
+  expected_.gof.num_ref_pics[0] = 0;
+  expected_.gof.num_ref_pics[1] = 1;
+  expected_.gof.pid_diff[1][0] = 33;
+  ParseAndCheckPacket(packet, expected_, kHeaderLength, sizeof(packet));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseFirstPacketInKeyFrame) {
+  uint8_t packet[2] = {0};
+  packet[0] = 0x08;  // I:0 P:0 L:0 F:0 B:1 E:0 V:0 R:0
+
+  RtpDepacketizer::ParsedPayload parsed;
+  ASSERT_TRUE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+  EXPECT_EQ(kVideoFrameKey, parsed.frame_type);
+  EXPECT_TRUE(parsed.type.Video.isFirstPacket);
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseLastPacketInDeltaFrame) {
+  uint8_t packet[2] = {0};
+  packet[0] = 0x44;  // I:0 P:1 L:0 F:0 B:0 E:1 V:0 R:0
+
+  RtpDepacketizer::ParsedPayload parsed;
+  ASSERT_TRUE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+  EXPECT_EQ(kVideoFrameDelta, parsed.frame_type);
+  EXPECT_FALSE(parsed.type.Video.isFirstPacket);
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseResolution) {
+  const uint16_t kWidth[2] = {640, 1280};
+  const uint16_t kHeight[2] = {360, 720};
+  uint8_t packet[20] = {0};
+  packet[0] = 0x0A;  // I:0 P:0 L:0 F:0 B:1 E:0 V:1 R:0
+  packet[1] = (1 << 5) | (1 << 4) | 0;  // N_S:1 Y:1 G:0
+  packet[2] = kWidth[0] >> 8;
+  packet[3] = kWidth[0] & 0xFF;
+  packet[4] = kHeight[0] >> 8;
+  packet[5] = kHeight[0] & 0xFF;
+  packet[6] = kWidth[1] >> 8;
+  packet[7] = kWidth[1] & 0xFF;
+  packet[8] = kHeight[1] >> 8;
+  packet[9] = kHeight[1] & 0xFF;
+
+  RtpDepacketizer::ParsedPayload parsed;
+  ASSERT_TRUE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+  EXPECT_EQ(kWidth[0], parsed.type.Video.width);
+  EXPECT_EQ(kHeight[0], parsed.type.Video.height);
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseFailsForNoPayloadLength) {
+  uint8_t packet[1] = {0};
+  RtpDepacketizer::ParsedPayload parsed;
+  EXPECT_FALSE(depacketizer_->Parse(&parsed, packet, 0));
+}
+
+TEST_F(RtpDepacketizerVp9Test, ParseFailsForTooShortBufferToFitPayload) {
+  const uint8_t kHeaderLength = 1;
+  uint8_t packet[kHeaderLength] = {0};
+  RtpDepacketizer::ParsedPayload parsed;
+  EXPECT_FALSE(depacketizer_->Parse(&parsed, packet, sizeof(packet)));
+}
+
+}  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
@@ -9,21 +9,25 @@
  */
 
 #include "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h"
 
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "webrtc/base/checks.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/trace_event.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
 #include "webrtc/modules/rtp_rtcp/source/byte_io.h"
 #include "webrtc/modules/rtp_rtcp/source/producer_fec.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_vp8.h"
+#include "webrtc/modules/rtp_rtcp/source/rtp_format_vp9.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_h264.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/trace_event.h"
 
 namespace webrtc {
 enum { REDForFECHeaderLength = 1 };
 
@@ -318,17 +322,17 @@ bool RTPSenderVideo::Send(const RtpVideo
 
   rtc::scoped_ptr<RtpPacketizer> packetizer(RtpPacketizer::Create(
       videoType, max_payload_length, &(rtpHdr->codecHeader), frameType));
 
   // TODO(changbin): we currently don't support to configure the codec to
   // output multiple partitions for VP8. Should remove below check after the
   // issue is fixed.
   const RTPFragmentationHeader* frag =
-      (videoType == kRtpVideoVp8 || videoType == kRtpVideoVp9) ? NULL : fragmentation;
+      (videoType == kRtpVideoVp8) ? NULL : fragmentation;
 
   packetizer->SetPayloadData(data, payload_bytes_to_send, frag);
 
   bool last = false;
   while (!last) {
     uint8_t dataBuffer[IP_PACKET_SIZE] = {0};
     size_t payload_bytes_in_packet = 0;
     if (!packetizer->NextPacket(
@@ -355,17 +359,17 @@ bool RTPSenderVideo::Send(const RtpVideo
     if (!rtpHdr) {
       assert(!_rtpSender.IsRtpHeaderExtensionRegistered(
           kRtpExtensionVideoRotation));
     } else if (cvo_mode == RTPSenderInterface::kCVOActivated) {
       // Checking whether CVO header extension is registered will require taking
       // a lock. It'll be a no-op if it's not registered.
       // TODO(guoweis): For now, all packets sent will carry the CVO such that
       // the RTP header length is consistent, although the receiver side will
-      // only exam the packets with market bit set.
+      // only exam the packets with marker bit set.
       size_t packetSize = payloadSize + rtp_header_length;
       RtpUtility::RtpHeaderParser rtp_parser(dataBuffer, packetSize);
       RTPHeader rtp_header;
       rtp_parser.Parse(rtp_header);
       _rtpSender.UpdateVideoRotation(dataBuffer, packetSize, rtp_header,
                                      rtpHdr->rotation);
     }
     if (SendVideoPacket(dataBuffer,
--- a/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/linux/device_info_linux.cc
@@ -159,17 +159,17 @@ int32_t DeviceInfoLinux::GetDeviceName(
         {
             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
                        "buffer passed is too small");
             return -1;
         }
     } else {
         // if there's no bus info to use for uniqueId, invent one - and it has to be repeatable
         if (snprintf(deviceUniqueIdUTF8, deviceUniqueIdUTF8Length, "fake_%u", device_index) >=
-            deviceUniqueIdUTF8Length)
+            (int) deviceUniqueIdUTF8Length)
         {
             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id,
                        "buffer passed is too small");
             return -1;
         }
     }
 
     return 0;
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/interface/video_codec_interface.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/interface/video_codec_interface.h
@@ -38,26 +38,45 @@ struct CodecSpecificInfoVP8 {
   uint8_t simulcastIdx;
   uint8_t temporalIdx;
   bool layerSync;
   int tl0PicIdx;  // Negative value to skip tl0PicIdx.
   int8_t keyIdx;  // Negative value to skip keyIdx.
 };
 
 struct CodecSpecificInfoVP9 {
-  bool hasReceivedSLI;
-  uint8_t pictureIdSLI;
-  bool hasReceivedRPSI;
-  uint64_t pictureIdRPSI;
-  int16_t pictureId;  // Negative value to skip pictureId.
-  bool nonReference;
-  uint8_t temporalIdx;
-  bool layerSync;
-  int tl0PicIdx;  // Negative value to skip tl0PicIdx.
-  int8_t keyIdx;  // Negative value to skip keyIdx.
+  bool has_received_sli;
+  uint8_t picture_id_sli;
+  bool has_received_rpsi;
+  uint64_t picture_id_rpsi;
+  int16_t picture_id;  // Negative value to skip pictureId.
+
+  bool inter_pic_predicted;  // This layer frame is dependent on previously
+                             // coded frame(s).
+  bool flexible_mode;
+  bool ss_data_available;
+
+  int tl0_pic_idx;  // Negative value to skip tl0PicIdx.
+  uint8_t temporal_idx;
+  uint8_t spatial_idx;
+  bool temporal_up_switch;
+  bool inter_layer_predicted;  // Frame is dependent on directly lower spatial
+                               // layer frame.
+  uint8_t gof_idx;
+
+  // SS data.
+  size_t num_spatial_layers;  // Always populated.
+  bool spatial_layer_resolution_present;
+  uint16_t width[kMaxVp9NumberOfSpatialLayers];
+  uint16_t height[kMaxVp9NumberOfSpatialLayers];
+  GofInfoVP9 gof;
+
+  // Frame reference data.
+  uint8_t num_ref_pics;
+  uint8_t p_diff[kMaxVp9RefPics];
 };
 
 struct CodecSpecificInfoGeneric {
   uint8_t simulcast_idx;
 };
 
 struct CodecSpecificInfoH264 {
   bool    single_nalu;
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/screenshare_layers.cc
@@ -0,0 +1,93 @@
+/* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+
+#include <algorithm>
+#include "webrtc/modules/video_coding/codecs/vp9/screenshare_layers.h"
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+ScreenshareLayersVP9::ScreenshareLayersVP9(uint8_t num_layers)
+    : num_layers_(num_layers),
+      start_layer_(0),
+      last_timestamp_(0),
+      timestamp_initialized_(false) {
+  DCHECK_GT(num_layers, 0);
+  DCHECK_LE(num_layers, kMaxVp9NumberOfSpatialLayers);
+  memset(bits_used_, 0, sizeof(bits_used_));
+  memset(threshold_kbps_, 0, sizeof(threshold_kbps_));
+}
+
+uint8_t ScreenshareLayersVP9::GetStartLayer() const {
+  return start_layer_;
+}
+
+void ScreenshareLayersVP9::ConfigureBitrate(int threshold_kbps,
+                                            uint8_t layer_id) {
+  // The upper layer is always the layer we spill frames
+  // to when the bitrate becomes to high, therefore setting
+  // a max limit is not allowed. The top layer bitrate is
+  // never used either so configuring it makes no difference.
+  DCHECK_LT(layer_id, num_layers_ - 1);
+  threshold_kbps_[layer_id] = threshold_kbps;
+}
+
+void ScreenshareLayersVP9::LayerFrameEncoded(unsigned int size_bytes,
+                                             uint8_t layer_id) {
+  DCHECK_LT(layer_id, num_layers_);
+  bits_used_[layer_id] += size_bytes * 8;
+}
+
+VP9EncoderImpl::SuperFrameRefSettings
+ScreenshareLayersVP9::GetSuperFrameSettings(uint32_t timestamp,
+                                            bool is_keyframe) {
+  VP9EncoderImpl::SuperFrameRefSettings settings;
+  if (!timestamp_initialized_) {
+    last_timestamp_ = timestamp;
+    timestamp_initialized_ = true;
+  }
+  float time_diff = (timestamp - last_timestamp_) / 90.f;
+  float total_bits_used = 0;
+  float total_threshold_kbps = 0;
+  start_layer_ = 0;
+
+  // Up to (num_layers - 1) because we only have
+  // (num_layers - 1) thresholds to check.
+  for (int layer_id = 0; layer_id < num_layers_ - 1; ++layer_id) {
+    bits_used_[layer_id] = std::max(
+        0.f, bits_used_[layer_id] - time_diff * threshold_kbps_[layer_id]);
+    total_bits_used += bits_used_[layer_id];
+    total_threshold_kbps += threshold_kbps_[layer_id];
+
+    // If this is a keyframe then there should be no
+    // references to any previous frames.
+    if (!is_keyframe) {
+      settings.layer[layer_id].ref_buf1 = layer_id;
+      if (total_bits_used > total_threshold_kbps * 1000)
+        start_layer_ = layer_id + 1;
+    }
+
+    settings.layer[layer_id].upd_buf = layer_id;
+  }
+  // Since the above loop does not iterate over the last layer
+  // the reference of the last layer has to be set after the loop,
+  // and if this is a keyframe there should be no references to
+  // any previous frames.
+  if (!is_keyframe)
+    settings.layer[num_layers_ - 1].ref_buf1 = num_layers_ - 1;
+
+  settings.layer[num_layers_ - 1].upd_buf = num_layers_ - 1;
+  settings.is_keyframe = is_keyframe;
+  settings.start_layer = start_layer_;
+  settings.stop_layer = num_layers_ - 1;
+  last_timestamp_ = timestamp;
+  return settings;
+}
+
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/screenshare_layers.h
@@ -0,0 +1,66 @@
+/* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+*
+*  Use of this source code is governed by a BSD-style license
+*  that can be found in the LICENSE file in the root of the source
+*  tree. An additional intellectual property rights grant can be found
+*  in the file PATENTS.  All contributing project authors may
+*  be found in the AUTHORS file in the root of the source tree.
+*/
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_SCREENSHARE_LAYERS_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_SCREENSHARE_LAYERS_H_
+
+#include "webrtc/modules/video_coding/codecs/vp9/vp9_impl.h"
+
+namespace webrtc {
+
+class ScreenshareLayersVP9 {
+ public:
+  explicit ScreenshareLayersVP9(uint8_t num_layers);
+
+  // The target bitrate for layer with id layer_id.
+  void ConfigureBitrate(int threshold_kbps, uint8_t layer_id);
+
+  // The current start layer.
+  uint8_t GetStartLayer() const;
+
+  // Update the layer with the size of the layer frame.
+  void LayerFrameEncoded(unsigned int size_bytes, uint8_t layer_id);
+
+  // Get the layer settings for the next superframe.
+  //
+  // In short, each time the GetSuperFrameSettings is called the
+  // bitrate of every layer is calculated and if the cummulative
+  // bitrate exceeds the configured cummulative bitrates
+  // (ConfigureBitrate to configure) up to and including that
+  // layer then the resulting encoding settings for the
+  // superframe will only encode layers above that layer.
+  VP9EncoderImpl::SuperFrameRefSettings GetSuperFrameSettings(
+      uint32_t timestamp,
+      bool is_keyframe);
+
+ private:
+  // How many layers that are used.
+  uint8_t num_layers_;
+
+  // The index of the first layer to encode.
+  uint8_t start_layer_;
+
+  // Cummulative target kbps for the different layers.
+  float threshold_kbps_[kMaxVp9NumberOfSpatialLayers - 1];
+
+  // How many bits that has been used for a certain layer. Increased in
+  // FrameEncoded() by the size of the encoded frame and decreased in
+  // GetSuperFrameSettings() depending on the time between frames.
+  float bits_used_[kMaxVp9NumberOfSpatialLayers];
+
+  // Timestamp of last frame.
+  uint32_t last_timestamp_;
+
+  // If the last_timestamp_ has been set.
+  bool timestamp_initialized_;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_SCREENSHARE_LAYERS_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/screenshare_layers_unittest.cc
@@ -0,0 +1,323 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <limits>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "vpx/vp8cx.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/modules/video_coding/codecs/vp9/screenshare_layers.h"
+#include "webrtc/modules/video_coding/codecs/vp9/vp9_impl.h"
+#include "webrtc/system_wrappers/include/clock.h"
+
+namespace webrtc {
+
+typedef VP9EncoderImpl::SuperFrameRefSettings Settings;
+
+const uint32_t kTickFrequency = 90000;
+
+class ScreenshareLayerTestVP9 : public ::testing::Test {
+ protected:
+  ScreenshareLayerTestVP9() : clock_(0) {}
+  virtual ~ScreenshareLayerTestVP9() {}
+
+  void InitScreenshareLayers(int layers) {
+    layers_.reset(new ScreenshareLayersVP9(layers));
+  }
+
+  void ConfigureBitrateForLayer(int kbps, uint8_t layer_id) {
+    layers_->ConfigureBitrate(kbps, layer_id);
+  }
+
+  void AdvanceTime(int64_t milliseconds) {
+    clock_.AdvanceTimeMilliseconds(milliseconds);
+  }
+
+  void AddKilobitsToLayer(int kilobits, uint8_t layer_id) {
+    layers_->LayerFrameEncoded(kilobits * 1000 / 8, layer_id);
+  }
+
+  void EqualRefsForLayer(const Settings& actual, uint8_t layer_id) {
+    EXPECT_EQ(expected_.layer[layer_id].upd_buf,
+              actual.layer[layer_id].upd_buf);
+    EXPECT_EQ(expected_.layer[layer_id].ref_buf1,
+              actual.layer[layer_id].ref_buf1);
+    EXPECT_EQ(expected_.layer[layer_id].ref_buf2,
+              actual.layer[layer_id].ref_buf2);
+    EXPECT_EQ(expected_.layer[layer_id].ref_buf3,
+              actual.layer[layer_id].ref_buf3);
+  }
+
+  void EqualRefs(const Settings& actual) {
+    for (unsigned int layer_id = 0; layer_id < kMaxVp9NumberOfSpatialLayers;
+         ++layer_id) {
+      EqualRefsForLayer(actual, layer_id);
+    }
+  }
+
+  void EqualStartStopKeyframe(const Settings& actual) {
+    EXPECT_EQ(expected_.start_layer, actual.start_layer);
+    EXPECT_EQ(expected_.stop_layer, actual.stop_layer);
+    EXPECT_EQ(expected_.is_keyframe, actual.is_keyframe);
+  }
+
+  // Check that the settings returned by GetSuperFrameSettings() is
+  // equal to the expected_ settings.
+  void EqualToExpected() {
+    uint32_t frame_timestamp_ =
+        clock_.TimeInMilliseconds() * (kTickFrequency / 1000);
+    Settings actual =
+        layers_->GetSuperFrameSettings(frame_timestamp_, expected_.is_keyframe);
+    EqualRefs(actual);
+    EqualStartStopKeyframe(actual);
+  }
+
+  Settings expected_;
+  SimulatedClock clock_;
+  rtc::scoped_ptr<ScreenshareLayersVP9> layers_;
+};
+
+TEST_F(ScreenshareLayerTestVP9, NoRefsOnKeyFrame) {
+  const int kNumLayers = kMaxVp9NumberOfSpatialLayers;
+  InitScreenshareLayers(kNumLayers);
+  expected_.start_layer = 0;
+  expected_.stop_layer = kNumLayers - 1;
+
+  for (int l = 0; l < kNumLayers; ++l) {
+    expected_.layer[l].upd_buf = l;
+  }
+  expected_.is_keyframe = true;
+  EqualToExpected();
+
+  for (int l = 0; l < kNumLayers; ++l) {
+    expected_.layer[l].ref_buf1 = l;
+  }
+  expected_.is_keyframe = false;
+  EqualToExpected();
+}
+
+// Test if it is possible to send at a high bitrate (over the threshold)
+// after a longer period of low bitrate. This should not be possible.
+TEST_F(ScreenshareLayerTestVP9, DontAccumelateAvailableBitsOverTime) {
+  InitScreenshareLayers(2);
+  ConfigureBitrateForLayer(100, 0);
+
+  expected_.layer[0].upd_buf = 0;
+  expected_.layer[0].ref_buf1 = 0;
+  expected_.layer[1].upd_buf = 1;
+  expected_.layer[1].ref_buf1 = 1;
+  expected_.start_layer = 0;
+  expected_.stop_layer = 1;
+
+  // Send 10 frames at a low bitrate (50 kbps)
+  for (int i = 0; i < 10; ++i) {
+    AdvanceTime(200);
+    EqualToExpected();
+    AddKilobitsToLayer(10, 0);
+  }
+
+  AdvanceTime(200);
+  EqualToExpected();
+  AddKilobitsToLayer(301, 0);
+
+  // Send 10 frames at a high bitrate (200 kbps)
+  expected_.start_layer = 1;
+  for (int i = 0; i < 10; ++i) {
+    AdvanceTime(200);
+    EqualToExpected();
+    AddKilobitsToLayer(40, 1);
+  }
+}
+
+// Test if used bits are accumelated over layers, as they should;
+TEST_F(ScreenshareLayerTestVP9, AccumelateUsedBitsOverLayers) {
+  const int kNumLayers = kMaxVp9NumberOfSpatialLayers;
+  InitScreenshareLayers(kNumLayers);
+  for (int l = 0; l < kNumLayers - 1; ++l)
+    ConfigureBitrateForLayer(100, l);
+  for (int l = 0; l < kNumLayers; ++l) {
+    expected_.layer[l].upd_buf = l;
+    expected_.layer[l].ref_buf1 = l;
+  }
+
+  expected_.start_layer = 0;
+  expected_.stop_layer = kNumLayers - 1;
+  EqualToExpected();
+
+  for (int layer = 0; layer < kNumLayers - 1; ++layer) {
+    expected_.start_layer = layer;
+    EqualToExpected();
+    AddKilobitsToLayer(101, layer);
+  }
+}
+
+// General testing of the bitrate controller.
+TEST_F(ScreenshareLayerTestVP9, 2LayerBitrate) {
+  InitScreenshareLayers(2);
+  ConfigureBitrateForLayer(100, 0);
+
+  expected_.layer[0].upd_buf = 0;
+  expected_.layer[1].upd_buf = 1;
+  expected_.layer[0].ref_buf1 = -1;
+  expected_.layer[1].ref_buf1 = -1;
+  expected_.start_layer = 0;
+  expected_.stop_layer = 1;
+
+  expected_.is_keyframe = true;
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+
+  expected_.layer[0].ref_buf1 = 0;
+  expected_.layer[1].ref_buf1 = 1;
+  expected_.is_keyframe = false;
+  AdvanceTime(199);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+
+  expected_.start_layer = 1;
+  for (int frame = 0; frame < 3; ++frame) {
+    AdvanceTime(200);
+    EqualToExpected();
+    AddKilobitsToLayer(100, 1);
+  }
+
+  // Just before enough bits become available for L0 @0.999 seconds.
+  AdvanceTime(199);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 1);
+
+  // Just after enough bits become available for L0 @1.0001 seconds.
+  expected_.start_layer = 0;
+  AdvanceTime(2);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+
+  // Keyframes always encode all layers, even if it is over budget.
+  expected_.layer[0].ref_buf1 = -1;
+  expected_.layer[1].ref_buf1 = -1;
+  expected_.is_keyframe = true;
+  AdvanceTime(499);
+  EqualToExpected();
+  expected_.layer[0].ref_buf1 = 0;
+  expected_.layer[1].ref_buf1 = 1;
+  expected_.start_layer = 1;
+  expected_.is_keyframe = false;
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+
+  // 400 kb in L0 --> @3 second mark to fall below the threshold..
+  // just before @2.999 seconds.
+  expected_.is_keyframe = false;
+  AdvanceTime(1499);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 1);
+
+  // just after @3.001 seconds.
+  expected_.start_layer = 0;
+  AdvanceTime(2);
+  EqualToExpected();
+  AddKilobitsToLayer(100, 0);
+}
+
+// General testing of the bitrate controller.
+TEST_F(ScreenshareLayerTestVP9, 3LayerBitrate) {
+  InitScreenshareLayers(3);
+  ConfigureBitrateForLayer(100, 0);
+  ConfigureBitrateForLayer(100, 1);
+
+  for (int l = 0; l < 3; ++l) {
+    expected_.layer[l].upd_buf = l;
+    expected_.layer[l].ref_buf1 = l;
+  }
+  expected_.start_layer = 0;
+  expected_.stop_layer = 2;
+
+  EqualToExpected();
+  AddKilobitsToLayer(105, 0);
+  AddKilobitsToLayer(30, 1);
+
+  AdvanceTime(199);
+  EqualToExpected();
+  AddKilobitsToLayer(105, 0);
+  AddKilobitsToLayer(30, 1);
+
+  expected_.start_layer = 1;
+  AdvanceTime(200);
+  EqualToExpected();
+  AddKilobitsToLayer(130, 1);
+
+  expected_.start_layer = 2;
+  AdvanceTime(200);
+  EqualToExpected();
+
+  // 400 kb in L1 --> @1.0 second mark to fall below threshold.
+  // 210 kb in L0 --> @1.1 second mark to fall below threshold.
+  // Just before L1 @0.999 seconds.
+  AdvanceTime(399);
+  EqualToExpected();
+
+  // Just after L1 @1.001 seconds.
+  expected_.start_layer = 1;
+  AdvanceTime(2);
+  EqualToExpected();
+
+  // Just before L0 @1.099 seconds.
+  AdvanceTime(99);
+  EqualToExpected();
+
+  // Just after L0 @1.101 seconds.
+  expected_.start_layer = 0;
+  AdvanceTime(2);
+  EqualToExpected();
+
+  // @1.1 seconds
+  AdvanceTime(99);
+  EqualToExpected();
+  AddKilobitsToLayer(200, 1);
+
+  expected_.is_keyframe = true;
+  for (int l = 0; l < 3; ++l)
+    expected_.layer[l].ref_buf1 = -1;
+  AdvanceTime(200);
+  EqualToExpected();
+
+  expected_.is_keyframe = false;
+  expected_.start_layer = 2;
+  for (int l = 0; l < 3; ++l)
+    expected_.layer[l].ref_buf1 = l;
+  AdvanceTime(200);
+  EqualToExpected();
+}
+
+// Test that the bitrate calculations are
+// correct when the timestamp wrap.
+TEST_F(ScreenshareLayerTestVP9, TimestampWrap) {
+  InitScreenshareLayers(2);
+  ConfigureBitrateForLayer(100, 0);
+
+  expected_.layer[0].upd_buf = 0;
+  expected_.layer[0].ref_buf1 = 0;
+  expected_.layer[1].upd_buf = 1;
+  expected_.layer[1].ref_buf1 = 1;
+  expected_.start_layer = 0;
+  expected_.stop_layer = 1;
+
+  // Advance time to just before the timestamp wraps.
+  AdvanceTime(std::numeric_limits<uint32_t>::max() / (kTickFrequency / 1000));
+  EqualToExpected();
+  AddKilobitsToLayer(200, 0);
+
+  // Wrap
+  expected_.start_layer = 1;
+  AdvanceTime(1);
+  EqualToExpected();
+}
+
+}  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp
@@ -17,22 +17,30 @@
       'dependencies': [
         '<(webrtc_root)/common_video/common_video.gyp:common_video',
         '<(webrtc_root)/modules/video_coding/utility/video_coding_utility.gyp:video_coding_utility',
         '<(webrtc_root)/system_wrappers/system_wrappers.gyp:system_wrappers',
       ],
       'conditions': [
         ['build_libvpx==1', {
           'dependencies': [
-            '<(libvpx_dir)/libvpx.gyp:libvpx',
+            '<(libvpx_dir)/libvpx.gyp:libvpx_new',
           ],
-        }],
+        }, {
+          'include_dirs': [
+            '../../../../../../../libvpx',
+          ],
+	}],
         ['build_vp9==1', {
           'sources': [
             'include/vp9.h',
+            'screenshare_layers.cc',
+            'screenshare_layers.h',
+            'vp9_frame_buffer_pool.cc',
+            'vp9_frame_buffer_pool.h',
             'vp9_impl.cc',
             'vp9_impl.h',
           ],
         }, {
           'sources': [
             'vp9_dummy_impl.cc',
           ],
         }],
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.cc
@@ -0,0 +1,136 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ *
+ */
+
+#include "webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
+
+#include "vpx/vpx_codec.h"
+#include "vpx/vpx_decoder.h"
+#include "vpx/vpx_frame_buffer.h"
+
+#include "webrtc/base/checks.h"
+#include "webrtc/system_wrappers/interface/logging.h"
+
+namespace webrtc {
+
+uint8_t* Vp9FrameBufferPool::Vp9FrameBuffer::GetData() {
+  return (uint8_t*)(data_.data()); //data<uint8_t>();
+}
+
+size_t Vp9FrameBufferPool::Vp9FrameBuffer::GetDataSize() const {
+  return data_.size();
+}
+
+void Vp9FrameBufferPool::Vp9FrameBuffer::SetSize(size_t size) {
+  data_.SetSize(size);
+}
+
+bool Vp9FrameBufferPool::InitializeVpxUsePool(
+    vpx_codec_ctx* vpx_codec_context) {
+  DCHECK(vpx_codec_context);
+  // Tell libvpx to use this pool.
+  if (vpx_codec_set_frame_buffer_functions(
+          // In which context to use these callback functions.
+          vpx_codec_context,
+          // Called by libvpx when it needs another frame buffer.
+          &Vp9FrameBufferPool::VpxGetFrameBuffer,
+          // Called by libvpx when it no longer uses a frame buffer.
+          &Vp9FrameBufferPool::VpxReleaseFrameBuffer,
+          // |this| will be passed as |user_priv| to VpxGetFrameBuffer.
+          this)) {
+    // Failed to configure libvpx to use Vp9FrameBufferPool.
+    return false;
+  }
+  return true;
+}
+
+rtc::scoped_refptr<Vp9FrameBufferPool::Vp9FrameBuffer>
+Vp9FrameBufferPool::GetFrameBuffer(size_t min_size) {
+  DCHECK_GT(min_size, 0u);
+  rtc::scoped_refptr<Vp9FrameBuffer> available_buffer = nullptr;
+  {
+    rtc::CritScope cs(&buffers_lock_);
+    // Do we have a buffer we can recycle?
+    for (const auto& buffer : allocated_buffers_) {
+      if (buffer->HasOneRef()) {
+        available_buffer = buffer;
+        break;
+      }
+    }
+    // Otherwise create one.
+    if (available_buffer == nullptr) {
+      available_buffer = new rtc::RefCountedObject<Vp9FrameBuffer>();
+      allocated_buffers_.push_back(available_buffer);
+      if (allocated_buffers_.size() > max_num_buffers_) {
+        LOG(LS_WARNING)
+            << allocated_buffers_.size() << " Vp9FrameBuffers have been "
+            << "allocated by a Vp9FrameBufferPool (exceeding what is "
+            << "considered reasonable, " << max_num_buffers_ << ").";
+        RTC_NOTREACHED();
+      }
+    }
+  }
+
+  available_buffer->SetSize(min_size);
+  return available_buffer;
+}
+
+int Vp9FrameBufferPool::GetNumBuffersInUse() const {
+  int num_buffers_in_use = 0;
+  rtc::CritScope cs(&buffers_lock_);
+  for (const auto& buffer : allocated_buffers_) {
+    if (!buffer->HasOneRef())
+      ++num_buffers_in_use;
+  }
+  return num_buffers_in_use;
+}
+
+void Vp9FrameBufferPool::ClearPool() {
+  rtc::CritScope cs(&buffers_lock_);
+  allocated_buffers_.clear();
+}
+
+// static
+int32_t Vp9FrameBufferPool::VpxGetFrameBuffer(void* user_priv,
+                                              size_t min_size,
+                                              vpx_codec_frame_buffer* fb) {
+  DCHECK(user_priv);
+  DCHECK(fb);
+  Vp9FrameBufferPool* pool = static_cast<Vp9FrameBufferPool*>(user_priv);
+
+  rtc::scoped_refptr<Vp9FrameBuffer> buffer = pool->GetFrameBuffer(min_size);
+  fb->data = buffer->GetData();
+  fb->size = buffer->GetDataSize();
+  // Store Vp9FrameBuffer* in |priv| for use in VpxReleaseFrameBuffer.
+  // This also makes vpx_codec_get_frame return images with their |fb_priv| set
+  // to |buffer| which is important for external reference counting.
+  // Release from refptr so that the buffer's |ref_count_| remains 1 when
+  // |buffer| goes out of scope.
+  fb->priv = static_cast<void*>(buffer.release());
+  return 0;
+}
+
+// static
+int32_t Vp9FrameBufferPool::VpxReleaseFrameBuffer(void* user_priv,
+                                                  vpx_codec_frame_buffer* fb) {
+  DCHECK(user_priv);
+  DCHECK(fb);
+  Vp9FrameBuffer* buffer = static_cast<Vp9FrameBuffer*>(fb->priv);
+  if (buffer != nullptr) {
+    buffer->Release();
+    // When libvpx fails to decode and you continue to try to decode (and fail)
+    // libvpx can for some reason try to release the same buffer multiple times.
+    // Setting |priv| to null protects against trying to Release multiple times.
+    fb->priv = nullptr;
+  }
+  return 0;
+}
+
+}  // namespace webrtc
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h
@@ -0,0 +1,117 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ *
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_FRAME_BUFFER_POOL_H_
+#define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_FRAME_BUFFER_POOL_H_
+
+#include <vector>
+
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/refcount.h"
+#include "webrtc/base/scoped_ref_ptr.h"
+
+struct vpx_codec_ctx;
+struct vpx_codec_frame_buffer;
+
+namespace webrtc {
+
+// This memory pool is used to serve buffers to libvpx for decoding purposes in
+// VP9, which is set up in InitializeVPXUsePool. After the initialization any
+// time libvpx wants to decode a frame it will use buffers provided and released
+// through VpxGetFrameBuffer and VpxReleaseFrameBuffer.
+// The benefit of owning the pool that libvpx relies on for decoding is that the
+// decoded frames returned by libvpx (from vpx_codec_get_frame) use parts of our
+// buffers for the decoded image data. By retaining ownership of this buffer
+// using scoped_refptr, the image buffer can be reused by VideoFrames and no
+// frame copy has to occur during decoding and frame delivery.
+//
+// Pseudo example usage case:
+//    Vp9FrameBufferPool pool;
+//    pool.InitializeVpxUsePool(decoder_ctx);
+//    ...
+//
+//    // During decoding, libvpx will get and release buffers from the pool.
+//    vpx_codec_decode(decoder_ctx, ...);
+//
+//    vpx_image_t* img = vpx_codec_get_frame(decoder_ctx, &iter);
+//    // Important to use scoped_refptr to protect it against being recycled by
+//    // the pool.
+//    scoped_refptr<Vp9FrameBuffer> img_buffer = (Vp9FrameBuffer*)img->fb_priv;
+//    ...
+//
+//    // Destroying the codec will make libvpx release any buffers it was using.
+//    vpx_codec_destroy(decoder_ctx);
+class Vp9FrameBufferPool {
+ public:
+  class Vp9FrameBuffer : public rtc::RefCountInterface {
+   public:
+    uint8_t* GetData();
+    size_t GetDataSize() const;
+    void SetSize(size_t size);
+
+    virtual bool HasOneRef() const = 0;
+
+   private:
+    // Data as an easily resizable buffer.
+    rtc::Buffer data_;
+  };
+
+  // Configures libvpx to, in the specified context, use this memory pool for
+  // buffers used to decompress frames. This is only supported for VP9.
+  bool InitializeVpxUsePool(vpx_codec_ctx* vpx_codec_context);
+
+  // Gets a frame buffer of at least |min_size|, recycling an available one or
+  // creating a new one. When no longer referenced from the outside the buffer
+  // becomes recyclable.
+  rtc::scoped_refptr<Vp9FrameBuffer> GetFrameBuffer(size_t min_size);
+  // Gets the number of buffers currently in use (not ready to be recycled).
+  int GetNumBuffersInUse() const;
+  // Releases allocated buffers, deleting available buffers. Buffers in use are
+  // not deleted until they are no longer referenced.
+  void ClearPool();
+
+  // InitializeVpxUsePool configures libvpx to call this function when it needs
+  // a new frame buffer. Parameters:
+  // |user_priv| Private data passed to libvpx, InitializeVpxUsePool sets it up
+  //             to be a pointer to the pool.
+  // |min_size|  Minimum size needed by libvpx (to decompress a frame).
+  // |fb|        Pointer to the libvpx frame buffer object, this is updated to
+  //             use the pool's buffer.
+  // Returns 0 on success. Returns < 0 on failure.
+  static int32_t VpxGetFrameBuffer(void* user_priv,
+                                   size_t min_size,
+                                   vpx_codec_frame_buffer* fb);
+
+  // InitializeVpxUsePool configures libvpx to call this function when it has
+  // finished using one of the pool's frame buffer. Parameters:
+  // |user_priv| Private data passed to libvpx, InitializeVpxUsePool sets it up
+  //             to be a pointer to the pool.
+  // |fb|        Pointer to the libvpx frame buffer object, its |priv| will be
+  //             a pointer to one of the pool's Vp9FrameBuffer.
+  static int32_t VpxReleaseFrameBuffer(void* user_priv,
+                                       vpx_codec_frame_buffer* fb);
+
+ private:
+  // Protects |allocated_buffers_|.
+  mutable rtc::CriticalSection buffers_lock_;
+  // All buffers, in use or ready to be recycled.
+  std::vector<rtc::scoped_refptr<Vp9FrameBuffer>> allocated_buffers_
+      GUARDED_BY(buffers_lock_);
+  // If more buffers than this are allocated we print warnings, and crash if
+  // in debug mode.
+  static const size_t max_num_buffers_ = 10;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_FRAME_BUFFER_POOL_H_
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc
@@ -16,40 +16,78 @@
 #include <time.h>
 #include <vector>
 
 #include "vpx/vpx_encoder.h"
 #include "vpx/vpx_decoder.h"
 #include "vpx/vp8cx.h"
 #include "vpx/vp8dx.h"
 
+#include "webrtc/base/bind.h"
 #include "webrtc/base/checks.h"
+#include "webrtc/base/trace_event.h"
 #include "webrtc/common.h"
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
 #include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/video_coding/codecs/vp9/screenshare_layers.h"
+#include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/tick_util.h"
-#include "webrtc/system_wrappers/interface/trace_event.h"
+
+namespace {
+
+// VP9DecoderImpl::ReturnFrame helper function used with WrappedI420Buffer.
+static void WrappedI420BufferNoLongerUsedCb(
+    webrtc::Vp9FrameBufferPool::Vp9FrameBuffer* img_buffer) {
+  img_buffer->Release();
+}
+
+}  // anonymous namespace
 
 namespace webrtc {
 
+// Only positive speeds, range for real-time coding currently is: 5 - 8.
+// Lower means slower/better quality, higher means fastest/lower quality.
+int GetCpuSpeed(int width, int height) {
+  // For smaller resolutions, use lower speed setting (get some coding gain at
+  // the cost of increased encoding complexity).
+  if (width * height <= 352 * 288)
+    return 5;
+  else
+    return 7;
+}
+
 VP9Encoder* VP9Encoder::Create() {
   return new VP9EncoderImpl();
 }
 
+void VP9EncoderImpl::EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt,
+                                                      void* user_data) {
+  VP9EncoderImpl* enc = (VP9EncoderImpl*)(user_data);
+  enc->GetEncodedLayerFrame(pkt);
+}
+
 VP9EncoderImpl::VP9EncoderImpl()
     : encoded_image_(),
       encoded_complete_callback_(NULL),
       inited_(false),
       timestamp_(0),
       picture_id_(0),
       cpu_speed_(3),
       rc_max_intra_target_(0),
       encoder_(NULL),
       config_(NULL),
-      raw_(NULL) {
+      raw_(NULL),
+      input_image_(NULL),
+      tl0_pic_idx_(0),
+      frames_since_kf_(0),
+      num_temporal_layers_(0),
+      num_spatial_layers_(0),
+      frames_encoded_(0),
+      // Use two spatial when screensharing with flexible mode.
+      spatial_layer_(new ScreenshareLayersVP9(2)) {
   memset(&codec_, 0, sizeof(codec_));
   uint32_t seed = static_cast<uint32_t>(TickTime::MillisecondTimestamp());
   srand(seed);
 }
 
 VP9EncoderImpl::~VP9EncoderImpl() {
   Release();
 }
@@ -73,16 +111,101 @@ int VP9EncoderImpl::Release() {
   if (raw_ != NULL) {
     vpx_img_free(raw_);
     raw_ = NULL;
   }
   inited_ = false;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+bool VP9EncoderImpl::ExplicitlyConfiguredSpatialLayers() const {
+  // We check target_bitrate_bps of the 0th layer to see if the spatial layers
+  // (i.e. bitrates) were explicitly configured.
+#ifdef LIBVPX_SVC
+  return num_spatial_layers_ > 1 &&
+         codec_.spatialLayers[0].target_bitrate_bps > 0;
+#else
+  return false;
+#endif
+}
+
+bool VP9EncoderImpl::SetSvcRates() {
+  uint8_t i = 0;
+
+  if (ExplicitlyConfiguredSpatialLayers()) {
+#ifdef LIBVPX_SVC
+    if (num_temporal_layers_ > 1) {
+      LOG(LS_ERROR) << "Multiple temporal layers when manually specifying "
+                       "spatial layers not implemented yet!";
+      return false;
+    }
+    int total_bitrate_bps = 0;
+    for (i = 0; i < num_spatial_layers_; ++i)
+      total_bitrate_bps += codec_.spatialLayers[i].target_bitrate_bps;
+    // If total bitrate differs now from what has been specified at the
+    // beginning, update the bitrates in the same ratio as before.
+    for (i = 0; i < num_spatial_layers_; ++i) {
+      config_->ss_target_bitrate[i] = config_->layer_target_bitrate[i] =
+          static_cast<int>(static_cast<int64_t>(config_->rc_target_bitrate) *
+                           codec_.spatialLayers[i].target_bitrate_bps /
+                           total_bitrate_bps);
+    }
+#endif
+  } else {
+    float rate_ratio[VPX_MAX_LAYERS] = {0};
+    float total = 0;
+
+    for (i = 0; i < num_spatial_layers_; ++i) {
+      if (svc_internal_.svc_params.scaling_factor_num[i] <= 0 ||
+          svc_internal_.svc_params.scaling_factor_den[i] <= 0) {
+        LOG(LS_ERROR) << "Scaling factors not specified!";
+        return false;
+      }
+      rate_ratio[i] =
+          static_cast<float>(svc_internal_.svc_params.scaling_factor_num[i]) /
+          svc_internal_.svc_params.scaling_factor_den[i];
+      total += rate_ratio[i];
+    }
+
+    for (i = 0; i < num_spatial_layers_; ++i) {
+      config_->ss_target_bitrate[i] = static_cast<unsigned int>(
+          config_->rc_target_bitrate * rate_ratio[i] / total);
+      if (num_temporal_layers_ == 1) {
+        config_->layer_target_bitrate[i] = config_->ss_target_bitrate[i];
+      } else if (num_temporal_layers_ == 2) {
+        config_->layer_target_bitrate[i * num_temporal_layers_] =
+            config_->ss_target_bitrate[i] * 2 / 3;
+        config_->layer_target_bitrate[i * num_temporal_layers_ + 1] =
+            config_->ss_target_bitrate[i];
+      } else if (num_temporal_layers_ == 3) {
+        config_->layer_target_bitrate[i * num_temporal_layers_] =
+            config_->ss_target_bitrate[i] / 2;
+        config_->layer_target_bitrate[i * num_temporal_layers_ + 1] =
+            config_->layer_target_bitrate[i * num_temporal_layers_] +
+            (config_->ss_target_bitrate[i] / 4);
+        config_->layer_target_bitrate[i * num_temporal_layers_ + 2] =
+            config_->ss_target_bitrate[i];
+      } else {
+        LOG(LS_ERROR) << "Unsupported number of temporal layers: "
+                      << num_temporal_layers_;
+        return false;
+      }
+    }
+  }
+
+  // For now, temporal layers only supported when having one spatial layer.
+  if (num_spatial_layers_ == 1) {
+    for (i = 0; i < num_temporal_layers_; ++i) {
+      config_->ts_target_bitrate[i] = config_->layer_target_bitrate[i];
+    }
+  }
+
+  return true;
+}
+
 int VP9EncoderImpl::SetRates(uint32_t new_bitrate_kbit,
                              uint32_t new_framerate) {
   if (!inited_) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
   if (encoder_->err) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
@@ -90,16 +213,22 @@ int VP9EncoderImpl::SetRates(uint32_t ne
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   // Update bit rate
   if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {
     new_bitrate_kbit = codec_.maxBitrate;
   }
   config_->rc_target_bitrate = new_bitrate_kbit;
   codec_.maxFramerate = new_framerate;
+  spatial_layer_->ConfigureBitrate(new_bitrate_kbit, 0);
+
+  if (!SetSvcRates()) {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+
   // Update encoder context
   if (vpx_codec_enc_config_set(encoder_, config_)) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9EncoderImpl::InitEncode(const VideoCodec* inst,
@@ -116,30 +245,44 @@ int VP9EncoderImpl::InitEncode(const Vid
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   if (inst->width < 1 || inst->height < 1) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
   if (number_of_cores < 1) {
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
   }
+  if (inst->codecSpecific.VP9.numberOfTemporalLayers > 3) {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+  // libvpx currently supports only one or two spatial layers.
+  if (inst->codecSpecific.VP9.numberOfSpatialLayers > 2) {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+
   int retVal = Release();
   if (retVal < 0) {
     return retVal;
   }
   if (encoder_ == NULL) {
     encoder_ = new vpx_codec_ctx_t;
   }
   if (config_ == NULL) {
     config_ = new vpx_codec_enc_cfg_t;
   }
   timestamp_ = 0;
   if (&codec_ != inst) {
     codec_ = *inst;
   }
+
+  num_spatial_layers_ = inst->codecSpecific.VP9.numberOfSpatialLayers;
+  num_temporal_layers_ = inst->codecSpecific.VP9.numberOfTemporalLayers;
+  if (num_temporal_layers_ == 0)
+    num_temporal_layers_ = 1;
+
   // Random start 16 bits is enough.
   picture_id_ = static_cast<uint16_t>(rand()) & 0x7FFF;
   // Allocate memory for encoded image
   if (encoded_image_._buffer != NULL) {
     delete [] encoded_image_._buffer;
   }
   encoded_image_._size = CalcBufferSize(kI420, codec_.width, codec_.height);
   encoded_image_._buffer = new uint8_t[encoded_image_._size];
@@ -177,21 +320,67 @@ int VP9EncoderImpl::InitEncode(const Vid
   // Set the maximum target size of any key-frame.
   rc_max_intra_target_ = MaxIntraTarget(config_->rc_buf_optimal_sz);
   if (inst->codecSpecific.VP9.keyFrameInterval  > 0) {
     config_->kf_mode = VPX_KF_AUTO;
     config_->kf_max_dist = inst->codecSpecific.VP9.keyFrameInterval;
   } else {
     config_->kf_mode = VPX_KF_DISABLED;
   }
-
+  config_->rc_resize_allowed = inst->codecSpecific.VP9.automaticResizeOn ?
+      1 : 0;
   // Determine number of threads based on the image size and #cores.
   config_->g_threads = NumberOfThreads(config_->g_w,
                                        config_->g_h,
                                        number_of_cores);
+
+  cpu_speed_ = GetCpuSpeed(config_->g_w, config_->g_h);
+
+  // TODO(asapersson): Check configuration of temporal switch up and increase
+  // pattern length.
+  is_flexible_mode_ = inst->codecSpecific.VP9.flexibleMode;
+  if (is_flexible_mode_) {
+    config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_BYPASS;
+    config_->ts_number_layers = num_temporal_layers_;
+    if (codec_.mode == kScreensharing)
+      spatial_layer_->ConfigureBitrate(inst->startBitrate, 0);
+  } else if (num_temporal_layers_ == 1) {
+    gof_.SetGofInfoVP9(kTemporalStructureMode1);
+    config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_NOLAYERING;
+    config_->ts_number_layers = 1;
+    config_->ts_rate_decimator[0] = 1;
+    config_->ts_periodicity = 1;
+    config_->ts_layer_id[0] = 0;
+  } else if (num_temporal_layers_ == 2) {
+    gof_.SetGofInfoVP9(kTemporalStructureMode2);
+    config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_0101;
+    config_->ts_number_layers = 2;
+    config_->ts_rate_decimator[0] = 2;
+    config_->ts_rate_decimator[1] = 1;
+    config_->ts_periodicity = 2;
+    config_->ts_layer_id[0] = 0;
+    config_->ts_layer_id[1] = 1;
+  } else if (num_temporal_layers_ == 3) {
+    gof_.SetGofInfoVP9(kTemporalStructureMode3);
+    config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_0212;
+    config_->ts_number_layers = 3;
+    config_->ts_rate_decimator[0] = 4;
+    config_->ts_rate_decimator[1] = 2;
+    config_->ts_rate_decimator[2] = 1;
+    config_->ts_periodicity = 4;
+    config_->ts_layer_id[0] = 0;
+    config_->ts_layer_id[1] = 2;
+    config_->ts_layer_id[2] = 1;
+    config_->ts_layer_id[3] = 2;
+  } else {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+
+  tl0_pic_idx_ = static_cast<uint8_t>(rand());
+
   return InitAndSetControlSettings(inst);
 }
 
 int VP9EncoderImpl::NumberOfThreads(int width,
                                     int height,
                                     int number_of_cores) {
   // Keep the number of encoder threads equal to the possible number of column
   // tiles, which is (1, 2, 4, 8). See comments below for VP9E_SET_TILE_COLUMNS.
@@ -201,40 +390,83 @@ int VP9EncoderImpl::NumberOfThreads(int 
     return 2;
   } else {
     // 1 thread less than VGA.
     return 1;
   }
 }
 
 int VP9EncoderImpl::InitAndSetControlSettings(const VideoCodec* inst) {
+  config_->ss_number_layers = num_spatial_layers_;
+
+  if (ExplicitlyConfiguredSpatialLayers()) {
+#ifdef LIBVPX_SVC
+    for (int i = 0; i < num_spatial_layers_; ++i) {
+      const auto& layer = codec_.spatialLayers[i];
+      svc_internal_.svc_params.max_quantizers[i] = config_->rc_max_quantizer;
+      svc_internal_.svc_params.min_quantizers[i] = config_->rc_min_quantizer;
+      svc_internal_.svc_params.scaling_factor_num[i] = layer.scaling_factor_num;
+      svc_internal_.svc_params.scaling_factor_den[i] = layer.scaling_factor_den;
+    }
+#endif
+  } else {
+    int scaling_factor_num = 256;
+    for (int i = num_spatial_layers_ - 1; i >= 0; --i) {
+      svc_internal_.svc_params.max_quantizers[i] = config_->rc_max_quantizer;
+      svc_internal_.svc_params.min_quantizers[i] = config_->rc_min_quantizer;
+      // 1:2 scaling in each dimension.
+      svc_internal_.svc_params.scaling_factor_num[i] = scaling_factor_num;
+      svc_internal_.svc_params.scaling_factor_den[i] = 256;
+      if (codec_.mode != kScreensharing)
+        scaling_factor_num /= 2;
+    }
+  }
+
+  if (!SetSvcRates()) {
+    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  }
+
   if (vpx_codec_enc_init(encoder_, vpx_codec_vp9_cx(), config_, 0)) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
-  // Only positive speeds, currently: 0 - 8.
-  // O means slowest/best quality, 8 means fastest/lower quality.
-  cpu_speed_ = 7;
-  // Note: some of these codec controls still use "VP8" in the control name.
-  // TODO(marpan): Update this in the next/future libvpx version.
   vpx_codec_control(encoder_, VP8E_SET_CPUUSED, cpu_speed_);
   vpx_codec_control(encoder_, VP8E_SET_MAX_INTRA_BITRATE_PCT,
                     rc_max_intra_target_);
   vpx_codec_control(encoder_, VP9E_SET_AQ_MODE,
                     inst->codecSpecific.VP9.adaptiveQpMode ? 3 : 0);
+
+  vpx_codec_control(
+      encoder_, VP9E_SET_SVC,
+      (num_temporal_layers_ > 1 || num_spatial_layers_ > 1) ? 1 : 0);
+  if (num_temporal_layers_ > 1 || num_spatial_layers_ > 1) {
+    vpx_codec_control(encoder_, VP9E_SET_SVC_PARAMETERS,
+                      &svc_internal_.svc_params);
+  }
+  // Register callback for getting each spatial layer.
+  vpx_codec_priv_output_cx_pkt_cb_pair_t cbp = {
+      VP9EncoderImpl::EncoderOutputCodedPacketCallback, (void*)(this)};
+  vpx_codec_control(encoder_, VP9E_REGISTER_CX_CALLBACK, (void*)(&cbp));
+
   // Control function to set the number of column tiles in encoding a frame, in
   // log2 unit: e.g., 0 = 1 tile column, 1 = 2 tile columns, 2 = 4 tile columns.
   // The number tile columns will be capped by the encoder based on image size
   // (minimum width of tile column is 256 pixels, maximum is 4096).
   vpx_codec_control(encoder_, VP9E_SET_TILE_COLUMNS, (config_->g_threads >> 1));
-#if !defined(WEBRTC_ARCH_ARM)
+#if !defined(WEBRTC_ARCH_ARM) && !defined(WEBRTC_ARCH_ARM64)
   // Note denoiser is still off by default until further testing/optimization,
   // i.e., codecSpecific.VP9.denoisingOn == 0.
   vpx_codec_control(encoder_, VP9E_SET_NOISE_SENSITIVITY,
                     inst->codecSpecific.VP9.denoisingOn ? 1 : 0);
 #endif
+  if (codec_.mode == kScreensharing) {
+    // Adjust internal parameters to screen content.
+    vpx_codec_control(encoder_, VP9E_SET_TUNE_CONTENT, 1);
+  }
+  // Enable encoder skip of static/low content blocks.
+  vpx_codec_control(encoder_, VP8E_SET_STATIC_THRESHOLD, 1);
   inited_ = true;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 uint32_t VP9EncoderImpl::MaxIntraTarget(uint32_t optimal_buffer_size) {
   // Set max to the optimal buffer level (normalized by target BR),
   // and scaled by a scale_par.
   // Max target size = scale_par * optimal_buffer_size * targetBR[Kbps].
@@ -263,111 +495,329 @@ int VP9EncoderImpl::Encode(const I420Vid
   }
   VideoFrameType frame_type = kDeltaFrame;
   // We only support one stream at the moment.
   if (frame_types && frame_types->size() > 0) {
     frame_type = (*frame_types)[0];
   }
   DCHECK_EQ(input_image.width(), static_cast<int>(raw_->d_w));
   DCHECK_EQ(input_image.height(), static_cast<int>(raw_->d_h));
+
+  // Set input image for use in the callback.
+  // This was necessary since you need some information from input_image.
+  // You can save only the necessary information (such as timestamp) instead of
+  // doing this.
+  input_image_ = &input_image;
+
   // Image in vpx_image_t format.
   // Input image is const. VPX's raw image is not defined as const.
   raw_->planes[VPX_PLANE_Y] = const_cast<uint8_t*>(input_image.buffer(kYPlane));
   raw_->planes[VPX_PLANE_U] = const_cast<uint8_t*>(input_image.buffer(kUPlane));
   raw_->planes[VPX_PLANE_V] = const_cast<uint8_t*>(input_image.buffer(kVPlane));
   raw_->stride[VPX_PLANE_Y] = input_image.stride(kYPlane);
   raw_->stride[VPX_PLANE_U] = input_image.stride(kUPlane);
   raw_->stride[VPX_PLANE_V] = input_image.stride(kVPlane);
 
-  int flags = 0;
+  vpx_enc_frame_flags_t flags = 0;
   bool send_keyframe = (frame_type == kKeyFrame);
   if (send_keyframe) {
     // Key frame request from caller.
     flags = VPX_EFLAG_FORCE_KF;
   }
+
+#ifdef LIBVPX_SVC
+  if (is_flexible_mode_) {
+    SuperFrameRefSettings settings;
+
+    // These structs are copied when calling vpx_codec_control,
+    // therefore it is ok for them to go out of scope.
+    vpx_svc_ref_frame_config enc_layer_conf;
+    vpx_svc_layer_id layer_id;
+
+    if (codec_.mode == kRealtimeVideo) {
+      // Real time video not yet implemented in flexible mode.
+      RTC_NOTREACHED();
+    } else {
+      settings = spatial_layer_->GetSuperFrameSettings(input_image.timestamp(),
+                                                       send_keyframe);
+    }
+    enc_layer_conf = GenerateRefsAndFlags(settings);
+    layer_id.temporal_layer_id = 0;
+    layer_id.spatial_layer_id = settings.start_layer;
+    vpx_codec_control(encoder_, VP9E_SET_SVC_LAYER_ID, &layer_id);
+    vpx_codec_control(encoder_, VP9E_SET_SVC_REF_FRAME_CONFIG, &enc_layer_conf);
+  }
+#endif
+
   assert(codec_.maxFramerate > 0);
   uint32_t duration = 90000 / codec_.maxFramerate;
   if (vpx_codec_encode(encoder_, raw_, timestamp_, duration, flags,
                        VPX_DL_REALTIME)) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
   timestamp_ += duration;
-  return GetEncodedPartitions(input_image);
+
+  return WEBRTC_VIDEO_CODEC_OK;
 }
 
 void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
                                        const vpx_codec_cx_pkt& pkt,
                                        uint32_t timestamp) {
   assert(codec_specific != NULL);
   codec_specific->codecType = kVideoCodecVP9;
   CodecSpecificInfoVP9 *vp9_info = &(codec_specific->codecSpecific.VP9);
-  vp9_info->pictureId = picture_id_;
-  vp9_info->keyIdx = kNoKeyIdx;
-  vp9_info->nonReference = (pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE) != 0;
-  // TODO(marpan): Temporal layers are supported in the current VP9 version,
-  // but for now use 1 temporal layer encoding. Will update this when temporal
-  // layer support for VP9 is added in webrtc.
-  vp9_info->temporalIdx = kNoTemporalIdx;
-  vp9_info->layerSync = false;
-  vp9_info->tl0PicIdx = kNoTl0PicIdx;
-  picture_id_ = (picture_id_ + 1) & 0x7FFF;
+  // TODO(asapersson): Set correct values.
+  vp9_info->inter_pic_predicted =
+      (pkt.data.frame.flags & VPX_FRAME_IS_KEY) ? false : true;
+  vp9_info->flexible_mode = codec_.codecSpecific.VP9.flexibleMode;
+  vp9_info->ss_data_available = ((pkt.data.frame.flags & VPX_FRAME_IS_KEY) &&
+                                 !codec_.codecSpecific.VP9.flexibleMode)
+                                    ? true
+                                    : false;
+
+  vpx_svc_layer_id_t layer_id = {0};
+  vpx_codec_control(encoder_, VP9E_GET_SVC_LAYER_ID, &layer_id);
+
+  assert(num_temporal_layers_ > 0);
+  assert(num_spatial_layers_ > 0);
+  if (num_temporal_layers_ == 1) {
+    assert(layer_id.temporal_layer_id == 0);
+    vp9_info->temporal_idx = kNoTemporalIdx;
+  } else {
+    vp9_info->temporal_idx = layer_id.temporal_layer_id;
+  }
+  if (num_spatial_layers_ == 1) {
+    assert(layer_id.spatial_layer_id == 0);
+    vp9_info->spatial_idx = kNoSpatialIdx;
+  } else {
+    vp9_info->spatial_idx = layer_id.spatial_layer_id;
+  }
+  if (layer_id.spatial_layer_id != 0) {
+    vp9_info->ss_data_available = false;
+  }
+
+  // TODO(asapersson): this info has to be obtained from the encoder.
+  vp9_info->temporal_up_switch = true;
+
+  bool is_first_frame = false;
+  if (is_flexible_mode_) {
+    is_first_frame =
+        layer_id.spatial_layer_id == spatial_layer_->GetStartLayer();
+  } else {
+    is_first_frame = layer_id.spatial_layer_id == 0;
+  }
+
+  if (is_first_frame) {
+    picture_id_ = (picture_id_ + 1) & 0x7FFF;
+    // TODO(asapersson): this info has to be obtained from the encoder.
+    vp9_info->inter_layer_predicted = false;
+    ++frames_since_kf_;
+  } else {
+    // TODO(asapersson): this info has to be obtained from the encoder.
+    vp9_info->inter_layer_predicted = true;
+  }
+
+  if (pkt.data.frame.flags & VPX_FRAME_IS_KEY) {
+    frames_since_kf_ = 0;
+  }
+
+  vp9_info->picture_id = picture_id_;
+
+  if (!vp9_info->flexible_mode) {
+    if (layer_id.temporal_layer_id == 0 && layer_id.spatial_layer_id == 0) {
+      tl0_pic_idx_++;
+    }
+    vp9_info->tl0_pic_idx = tl0_pic_idx_;
+  }
+
+  // Always populate this, so that the packetizer can properly set the marker
+  // bit.
+  vp9_info->num_spatial_layers = num_spatial_layers_;
+
+  vp9_info->num_ref_pics = 0;
+  if (vp9_info->flexible_mode) {
+    vp9_info->gof_idx = kNoGofIdx;
+    vp9_info->num_ref_pics = num_ref_pics_[layer_id.spatial_layer_id];
+    for (int i = 0; i < num_ref_pics_[layer_id.spatial_layer_id]; ++i) {
+      vp9_info->p_diff[i] = p_diff_[layer_id.spatial_layer_id][i];
+    }
+  } else {
+    vp9_info->gof_idx =
+        static_cast<uint8_t>(frames_since_kf_ % gof_.num_frames_in_gof);
+    vp9_info->temporal_up_switch = gof_.temporal_up_switch[vp9_info->gof_idx];
+  }
+
+  if (vp9_info->ss_data_available) {
+    vp9_info->spatial_layer_resolution_present = true;
+    for (size_t i = 0; i < vp9_info->num_spatial_layers; ++i) {
+      vp9_info->width[i] = codec_.width *
+                           svc_internal_.svc_params.scaling_factor_num[i] /
+                           svc_internal_.svc_params.scaling_factor_den[i];
+      vp9_info->height[i] = codec_.height *
+                            svc_internal_.svc_params.scaling_factor_num[i] /
+                            svc_internal_.svc_params.scaling_factor_den[i];
+    }
+    if (!vp9_info->flexible_mode) {
+      vp9_info->gof.CopyGofInfoVP9(gof_);
+    }
+  }
 }
 
-int VP9EncoderImpl::GetEncodedPartitions(const I420VideoFrame& input_image) {
-  vpx_codec_iter_t iter = NULL;
+int VP9EncoderImpl::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) {
   encoded_image_._length = 0;
   encoded_image_._frameType = kDeltaFrame;
   RTPFragmentationHeader frag_info;
   // Note: no data partitioning in VP9, so 1 partition only. We keep this
   // fragmentation data for now, until VP9 packetizer is implemented.
   frag_info.VerifyAndAllocateFragmentationHeader(1);
   int part_idx = 0;
   CodecSpecificInfo codec_specific;
-  const vpx_codec_cx_pkt_t *pkt = NULL;
-  while ((pkt = vpx_codec_get_cx_data(encoder_, &iter)) != NULL) {
-    switch (pkt->kind) {
-      case VPX_CODEC_CX_FRAME_PKT: {
-        memcpy(&encoded_image_._buffer[encoded_image_._length],
-               pkt->data.frame.buf,
-               pkt->data.frame.sz);
-        frag_info.fragmentationOffset[part_idx] = encoded_image_._length;
-        frag_info.fragmentationLength[part_idx] =
-            static_cast<uint32_t>(pkt->data.frame.sz);
-        frag_info.fragmentationPlType[part_idx] = 0;
-        frag_info.fragmentationTimeDiff[part_idx] = 0;
-        encoded_image_._length += static_cast<uint32_t>(pkt->data.frame.sz);
-        assert(encoded_image_._length <= encoded_image_._size);
-        break;
-      }
-      default: {
-        break;
-      }
-    }
-    // End of frame.
-    if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) {
-      // Check if encoded frame is a key frame.
-      if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
-        encoded_image_._frameType = kKeyFrame;
-      }
-      PopulateCodecSpecific(&codec_specific, *pkt, input_image.timestamp());
-      break;
-    }
+
+  assert(pkt->kind == VPX_CODEC_CX_FRAME_PKT);
+  memcpy(&encoded_image_._buffer[encoded_image_._length], pkt->data.frame.buf,
+         pkt->data.frame.sz);
+  frag_info.fragmentationOffset[part_idx] = encoded_image_._length;
+  frag_info.fragmentationLength[part_idx] =
+      static_cast<uint32_t>(pkt->data.frame.sz);
+  frag_info.fragmentationPlType[part_idx] = 0;
+  frag_info.fragmentationTimeDiff[part_idx] = 0;
+  encoded_image_._length += static_cast<uint32_t>(pkt->data.frame.sz);
+
+  vpx_svc_layer_id_t layer_id = {0};
+  vpx_codec_control(encoder_, VP9E_GET_SVC_LAYER_ID, &layer_id);
+  if (is_flexible_mode_ && codec_.mode == kScreensharing)
+    spatial_layer_->LayerFrameEncoded(
+        static_cast<unsigned int>(encoded_image_._length),
+        layer_id.spatial_layer_id);
+
+  assert(encoded_image_._length <= encoded_image_._size);
+
+  // End of frame.
+  // Check if encoded frame is a key frame.
+  if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) {
+    encoded_image_._frameType = kKeyFrame;
   }
+  PopulateCodecSpecific(&codec_specific, *pkt, input_image_->timestamp());
+
   if (encoded_image_._length > 0) {
     TRACE_COUNTER1("webrtc", "EncodedFrameSize", encoded_image_._length);
-    encoded_image_._timeStamp = input_image.timestamp();
-    encoded_image_.capture_time_ms_ = input_image.render_time_ms();
+    encoded_image_._timeStamp = input_image_->timestamp();
+    encoded_image_.capture_time_ms_ = input_image_->render_time_ms();
     encoded_image_._encodedHeight = raw_->d_h;
     encoded_image_._encodedWidth = raw_->d_w;
     encoded_complete_callback_->Encoded(encoded_image_, &codec_specific,
-                                      &frag_info);
+                                        &frag_info);
   }
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+#ifdef LIBVPX_SVC
+vpx_svc_ref_frame_config VP9EncoderImpl::GenerateRefsAndFlags(
+    const SuperFrameRefSettings& settings) {
+  static const vpx_enc_frame_flags_t kAllFlags =
+      VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_LAST |
+      VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_GF;
+  vpx_svc_ref_frame_config sf_conf = {};
+  if (settings.is_keyframe) {
+    // Used later on to make sure we don't make any invalid references.
+    memset(buffer_updated_at_frame_, -1, sizeof(buffer_updated_at_frame_));
+    for (int layer = settings.start_layer; layer <= settings.stop_layer;
+         ++layer) {
+      num_ref_pics_[layer] = 0;
+      buffer_updated_at_frame_[settings.layer[layer].upd_buf] = frames_encoded_;
+      // When encoding a keyframe only the alt_fb_idx is used
+      // to specify which layer ends up in which buffer.
+      sf_conf.alt_fb_idx[layer] = settings.layer[layer].upd_buf;
+    }
+  } else {
+    for (int layer_idx = settings.start_layer; layer_idx <= settings.stop_layer;
+         ++layer_idx) {
+      vpx_enc_frame_flags_t layer_flags = kAllFlags;
+      num_ref_pics_[layer_idx] = 0;
+      int8_t refs[3] = {settings.layer[layer_idx].ref_buf1,
+                        settings.layer[layer_idx].ref_buf2,
+                        settings.layer[layer_idx].ref_buf3};
+
+      for (unsigned int ref_idx = 0; ref_idx < kMaxVp9RefPics; ++ref_idx) {
+        if (refs[ref_idx] == -1)
+          continue;
+
+        DCHECK_GE(refs[ref_idx], 0);
+        DCHECK_LE(refs[ref_idx], 7);
+        // Easier to remove flags from all flags rather than having to
+        // build the flags from 0.
+        switch (num_ref_pics_[layer_idx]) {
+          case 0: {
+            sf_conf.lst_fb_idx[layer_idx] = refs[ref_idx];
+            layer_flags &= ~VP8_EFLAG_NO_REF_LAST;
+            break;
+          }
+          case 1: {
+            sf_conf.gld_fb_idx[layer_idx] = refs[ref_idx];
+            layer_flags &= ~VP8_EFLAG_NO_REF_GF;
+            break;
+          }
+          case 2: {
+            sf_conf.alt_fb_idx[layer_idx] = refs[ref_idx];
+            layer_flags &= ~VP8_EFLAG_NO_REF_ARF;
+            break;
+          }
+        }
+        // Make sure we don't reference a buffer that hasn't been
+        // used at all or hasn't been used since a keyframe.
+        DCHECK_NE(buffer_updated_at_frame_[refs[ref_idx]], -1);
+
+        p_diff_[layer_idx][num_ref_pics_[layer_idx]] =
+            frames_encoded_ - buffer_updated_at_frame_[refs[ref_idx]];
+        num_ref_pics_[layer_idx]++;
+      }
+
+      bool upd_buf_same_as_a_ref = false;
+      if (settings.layer[layer_idx].upd_buf != -1) {
+        for (unsigned int ref_idx = 0; ref_idx < kMaxVp9RefPics; ++ref_idx) {
+          if (settings.layer[layer_idx].upd_buf == refs[ref_idx]) {
+            switch (ref_idx) {
+              case 0: {
+                layer_flags &= ~VP8_EFLAG_NO_UPD_LAST;
+                break;
+              }
+              case 1: {
+                layer_flags &= ~VP8_EFLAG_NO_UPD_GF;
+                break;
+              }
+              case 2: {
+                layer_flags &= ~VP8_EFLAG_NO_UPD_ARF;
+                break;
+              }
+            }
+            upd_buf_same_as_a_ref = true;
+            break;
+          }
+        }
+        if (!upd_buf_same_as_a_ref) {
+          // If we have three references and a buffer is specified to be
+          // updated, then that buffer must be the same as one of the
+          // three references.
+          RTC_CHECK_LT(num_ref_pics_[layer_idx], kMaxVp9RefPics);
+
+          sf_conf.alt_fb_idx[layer_idx] = settings.layer[layer_idx].upd_buf;
+          layer_flags ^= VP8_EFLAG_NO_UPD_ARF;
+        }
+
+        int updated_buffer = settings.layer[layer_idx].upd_buf;
+        buffer_updated_at_frame_[updated_buffer] = frames_encoded_;
+        sf_conf.frame_flags[layer_idx] = layer_flags;
+      }
+    }
+  }
+  ++frames_encoded_;
+  return sf_conf;
+}
+#endif
+
 int VP9EncoderImpl::SetChannelParameters(uint32_t packet_loss, int64_t rtt) {
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9EncoderImpl::RegisterEncodeCompleteCallback(
     EncodedImageCallback* callback) {
   encoded_complete_callback_ = callback;
   return WEBRTC_VIDEO_CODEC_OK;
@@ -383,16 +833,24 @@ VP9DecoderImpl::VP9DecoderImpl()
       decoder_(NULL),
       key_frame_required_(true) {
   memset(&codec_, 0, sizeof(codec_));
 }
 
 VP9DecoderImpl::~VP9DecoderImpl() {
   inited_ = true;  // in order to do the actual release
   Release();
+  int num_buffers_in_use = frame_buffer_pool_.GetNumBuffersInUse();
+  if (num_buffers_in_use > 0) {
+    // The frame buffers are reference counted and frames are exposed after
+    // decoding. There may be valid usage cases where previous frames are still
+    // referenced after ~VP9DecoderImpl that is not a leak.
+    LOG(LS_INFO) << num_buffers_in_use << " Vp9FrameBuffers are still "
+                 << "referenced during ~VP9DecoderImpl.";
+  }
 }
 
 int VP9DecoderImpl::Reset() {
   if (!inited_) {
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
   }
   InitDecode(&codec_, 1);
   return WEBRTC_VIDEO_CODEC_OK;
@@ -416,16 +874,21 @@ int VP9DecoderImpl::InitDecode(const Vid
   vpx_codec_flags_t flags = 0;
   if (vpx_codec_dec_init(decoder_, vpx_codec_vp9_dx(), &cfg, flags)) {
     return WEBRTC_VIDEO_CODEC_MEMORY;
   }
   if (&codec_ != inst) {
     // Save VideoCodec instance for later; mainly for duplicating the decoder.
     codec_ = *inst;
   }
+
+  if (!frame_buffer_pool_.InitializeVpxUsePool(decoder_)) {
+    return WEBRTC_VIDEO_CODEC_MEMORY;
+  }
+
   inited_ = true;
   // Always start with a complete key frame.
   key_frame_required_ = true;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9DecoderImpl::Decode(const EncodedImage& input_image,
                            bool missing_frames,
@@ -450,60 +913,99 @@ int VP9DecoderImpl::Decode(const Encoded
     }
   }
   vpx_codec_iter_t iter = NULL;
   vpx_image_t* img;
   uint8_t* buffer = input_image._buffer;
   if (input_image._length == 0) {
     buffer = NULL;  // Triggers full frame concealment.
   }
+  // During decode libvpx may get and release buffers from |frame_buffer_pool_|.
+  // In practice libvpx keeps a few (~3-4) buffers alive at a time.
   if (vpx_codec_decode(decoder_,
                        buffer,
                        static_cast<unsigned int>(input_image._length),
                        0,
                        VPX_DL_REALTIME)) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
+  // |img->fb_priv| contains the image data, a reference counted Vp9FrameBuffer.
+  // It may be released by libvpx during future vpx_codec_decode or
+  // vpx_codec_destroy calls.
   img = vpx_codec_get_frame(decoder_, &iter);
   int ret = ReturnFrame(img, input_image._timeStamp);
   if (ret != 0) {
     return ret;
   }
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9DecoderImpl::ReturnFrame(const vpx_image_t* img, uint32_t timestamp) {
   if (img == NULL) {
     // Decoder OK and NULL image => No show frame.
     return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
   }
+
+#ifdef USE_WRAPPED_I420_BUFFER
+  // This buffer contains all of |img|'s image data, a reference counted
+  // Vp9FrameBuffer. Performing AddRef/Release ensures it is not released and
+  // recycled during use (libvpx is done with the buffers after a few
+  // vpx_codec_decode calls or vpx_codec_destroy).
+  Vp9FrameBufferPool::Vp9FrameBuffer* img_buffer =
+      static_cast<Vp9FrameBufferPool::Vp9FrameBuffer*>(img->fb_priv);
+  img_buffer->AddRef();
+  // The buffer can be used directly by the VideoFrame (without copy) by
+  // using a WrappedI420Buffer.
+  rtc::scoped_refptr<WrappedI420Buffer> img_wrapped_buffer(
+      new rtc::RefCountedObject<webrtc::WrappedI420Buffer>(
+          img->d_w, img->d_h,
+          img->d_w, img->d_h,
+          img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y],
+          img->planes[VPX_PLANE_U], img->stride[VPX_PLANE_U],
+          img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V],
+          // WrappedI420Buffer's mechanism for allowing the release of its frame
+          // buffer is through a callback function. This is where we should
+          // release |img_buffer|.
+          rtc::Bind(&WrappedI420BufferNoLongerUsedCb, img_buffer)));
+
+  I420VideoFrame decoded_image_;
+  decoded_image_.set_video_frame_buffer(img_wrapped_buffer);
+#else
   decoded_image_.CreateFrame(img->planes[VPX_PLANE_Y],
                              img->planes[VPX_PLANE_U],
                              img->planes[VPX_PLANE_V],
                              img->d_w, img->d_h,
                              img->stride[VPX_PLANE_Y],
                              img->stride[VPX_PLANE_U],
                              img->stride[VPX_PLANE_V]);
+#endif
   decoded_image_.set_timestamp(timestamp);
+
   int ret = decode_complete_callback_->Decoded(decoded_image_);
   if (ret != 0)
     return ret;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9DecoderImpl::RegisterDecodeCompleteCallback(
     DecodedImageCallback* callback) {
   decode_complete_callback_ = callback;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int VP9DecoderImpl::Release() {
   if (decoder_ != NULL) {
+    // When a codec is destroyed libvpx will release any buffers of
+    // |frame_buffer_pool_| it is currently using.
     if (vpx_codec_destroy(decoder_)) {
       return WEBRTC_VIDEO_CODEC_MEMORY;
     }
     delete decoder_;
     decoder_ = NULL;
   }
+  // Releases buffers from the pool. Any buffers not in use are deleted. Buffers
+  // still referenced externally are deleted once fully released, not returning
+  // to the pool.
+  frame_buffer_pool_.ClearPool();
   inited_ = false;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 }  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9_impl.h
@@ -8,22 +8,26 @@
  *  be found in the AUTHORS file in the root of the source tree.
  *
  */
 
 #ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_
 #define WEBRTC_MODULES_VIDEO_CODING_CODECS_VP9_IMPL_H_
 
 #include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h"
+#include "webrtc/modules/video_coding/codecs/vp9/vp9_frame_buffer_pool.h"
 
+#include "vpx/svc_context.h"
 #include "vpx/vpx_decoder.h"
 #include "vpx/vpx_encoder.h"
 
 namespace webrtc {
 
+class ScreenshareLayersVP9;
+
 class VP9EncoderImpl : public VP9Encoder {
  public:
   VP9EncoderImpl();
 
   virtual ~VP9EncoderImpl();
 
   int Release() override;
 
@@ -36,28 +40,60 @@ class VP9EncoderImpl : public VP9Encoder
              const std::vector<VideoFrameType>* frame_types) override;
 
   int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) override;
 
   int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
 
   int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate) override;
 
+  struct LayerFrameRefSettings {
+    int8_t upd_buf = -1;   // -1 - no update,    0..7 - update buffer 0..7
+    int8_t ref_buf1 = -1;  // -1 - no reference, 0..7 - reference buffer 0..7
+    int8_t ref_buf2 = -1;  // -1 - no reference, 0..7 - reference buffer 0..7
+    int8_t ref_buf3 = -1;  // -1 - no reference, 0..7 - reference buffer 0..7
+  };
+
+  struct SuperFrameRefSettings {
+    LayerFrameRefSettings layer[kMaxVp9NumberOfSpatialLayers];
+    uint8_t start_layer = 0;  // The first spatial layer to be encoded.
+    uint8_t stop_layer = 0;   // The last spatial layer to be encoded.
+    bool is_keyframe = false;
+  };
+
  private:
   // Determine number of encoder threads to use.
   int NumberOfThreads(int width, int height, int number_of_cores);
 
   // Call encoder initialize function and set control settings.
   int InitAndSetControlSettings(const VideoCodec* inst);
 
   void PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
                              const vpx_codec_cx_pkt& pkt,
                              uint32_t timestamp);
 
-  int GetEncodedPartitions(const I420VideoFrame& input_image);
+  bool ExplicitlyConfiguredSpatialLayers() const;
+  bool SetSvcRates();
+
+#ifdef LIBVPX_SVC
+  // Used for flexible mode to set the flags and buffer references used
+  // by the encoder. Also calculates the references used by the RTP
+  // packetizer.
+  //
+  // Has to be called for every frame (keyframes included) to update the
+  // state used to calculate references.
+  vpx_svc_ref_frame_config GenerateRefsAndFlags(
+      const SuperFrameRefSettings& settings);
+#endif
+  
+  virtual int GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt);
+
+  // Callback function for outputting packets per spatial layer.
+  static void EncoderOutputCodedPacketCallback(vpx_codec_cx_pkt* pkt,
+                                               void* user_data);
 
   // Determine maximum target for Intra frames
   //
   // Input:
   //    - optimal_buffer_size : Optimal buffer size
   // Return Value             : Max target size for Intra frames represented as
   //                            percentage of the per frame bandwidth
   uint32_t MaxIntraTarget(uint32_t optimal_buffer_size);
@@ -68,16 +104,32 @@ class VP9EncoderImpl : public VP9Encoder
   bool inited_;
   int64_t timestamp_;
   uint16_t picture_id_;
   int cpu_speed_;
   uint32_t rc_max_intra_target_;
   vpx_codec_ctx_t* encoder_;
   vpx_codec_enc_cfg_t* config_;
   vpx_image_t* raw_;
+  SvcInternal_t svc_internal_;
+  const I420VideoFrame* input_image_;
+  GofInfoVP9 gof_;       // Contains each frame's temporal information for
+                         // non-flexible mode.
+  uint8_t tl0_pic_idx_;  // Only used in non-flexible mode.
+  size_t frames_since_kf_;
+  uint8_t num_temporal_layers_;
+  uint8_t num_spatial_layers_;
+
+  // Used for flexible mode.
+  bool is_flexible_mode_;
+  int64_t buffer_updated_at_frame_[kNumVp9Buffers];
+  int64_t frames_encoded_;
+  uint8_t num_ref_pics_[kMaxVp9NumberOfSpatialLayers];
+  uint8_t p_diff_[kMaxVp9NumberOfSpatialLayers][kMaxVp9RefPics];
+  rtc::scoped_ptr<ScreenshareLayersVP9> spatial_layer_;
 };
 
 
 class VP9DecoderImpl : public VP9Decoder {
  public:
   VP9DecoderImpl();
 
   virtual ~VP9DecoderImpl();
@@ -94,17 +146,23 @@ class VP9DecoderImpl : public VP9Decoder
 
   int Release() override;
 
   int Reset() override;
 
  private:
   int ReturnFrame(const vpx_image_t* img, uint32_t timeStamp);
 
+#ifndef USE_WRAPPED_I420_BUFFER
+  // Temporarily keep VideoFrame in a separate buffer
+  // Once we debug WrappedI420VideoFrame usage, we can get rid of this
   I420VideoFrame decoded_image_;
+#endif
+  // Memory pool used to share buffers between libvpx and webrtc.
+  Vp9FrameBufferPool frame_buffer_pool_;
   DecodedImageCallback* decode_complete_callback_;
   bool inited_;
   vpx_codec_ctx_t* decoder_;
   VideoCodec codec_;
   bool key_frame_required_;
 };
 }  // namespace webrtc
 
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/codec_database.cc
@@ -52,17 +52,19 @@ VideoCodecVP9 VideoEncoder::GetDefaultVp
   memset(&vp9_settings, 0, sizeof(vp9_settings));
 
   vp9_settings.resilience = 1;
   vp9_settings.numberOfTemporalLayers = 1;
   vp9_settings.denoisingOn = false;
   vp9_settings.frameDroppingOn = true;
   vp9_settings.keyFrameInterval = 3000;
   vp9_settings.adaptiveQpMode = true;
-
+  vp9_settings.automaticResizeOn = true;
+  vp9_settings.numberOfSpatialLayers = 1;
+  vp9_settings.flexibleMode = false;
   return vp9_settings;
 }
 
 VideoCodecH264 VideoEncoder::GetDefaultH264Settings() {
   VideoCodecH264 h264_settings;
   memset(&h264_settings, 0, sizeof(h264_settings));
 
   h264_settings.profile = kProfileBase;
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/encoded_frame.cc
@@ -136,37 +136,77 @@ void VCMEncodedFrame::CopyCodecSpecific(
         _codecSpecificInfo.codecSpecific.H264.single_nalu =
             header->codecHeader.H264.single_nalu;
         _codecSpecificInfo.codecType = kVideoCodecH264;
         break;
       }
       case kRtpVideoVp9: {
         if (_codecSpecificInfo.codecType != kVideoCodecVP9) {
           // This is the first packet for this frame.
-          _codecSpecificInfo.codecSpecific.VP9.pictureId = -1;
-          _codecSpecificInfo.codecSpecific.VP9.temporalIdx = 0;
-          _codecSpecificInfo.codecSpecific.VP9.layerSync = false;
-          _codecSpecificInfo.codecSpecific.VP9.keyIdx = -1;
+          _codecSpecificInfo.codecSpecific.VP9.picture_id = -1;
+          _codecSpecificInfo.codecSpecific.VP9.temporal_idx = 0;
+          _codecSpecificInfo.codecSpecific.VP9.spatial_idx = 0;
+          _codecSpecificInfo.codecSpecific.VP9.gof_idx = 0;
+          _codecSpecificInfo.codecSpecific.VP9.inter_layer_predicted = false;
+          _codecSpecificInfo.codecSpecific.VP9.tl0_pic_idx = -1;
           _codecSpecificInfo.codecType = kVideoCodecVP9;
         }
-        _codecSpecificInfo.codecSpecific.VP9.nonReference =
-            header->codecHeader.VP9.nonReference;
-        if (header->codecHeader.VP9.pictureId != kNoPictureId) {
-          _codecSpecificInfo.codecSpecific.VP9.pictureId =
-              header->codecHeader.VP9.pictureId;
+        _codecSpecificInfo.codecSpecific.VP9.inter_pic_predicted =
+            header->codecHeader.VP9.inter_pic_predicted;
+        _codecSpecificInfo.codecSpecific.VP9.flexible_mode =
+            header->codecHeader.VP9.flexible_mode;
+        _codecSpecificInfo.codecSpecific.VP9.num_ref_pics =
+            header->codecHeader.VP9.num_ref_pics;
+        for (uint8_t r = 0; r < header->codecHeader.VP9.num_ref_pics; ++r) {
+          _codecSpecificInfo.codecSpecific.VP9.p_diff[r] =
+              header->codecHeader.VP9.pid_diff[r];
+        }
+        _codecSpecificInfo.codecSpecific.VP9.ss_data_available =
+            header->codecHeader.VP9.ss_data_available;
+        if (header->codecHeader.VP9.picture_id != kNoPictureId) {
+          _codecSpecificInfo.codecSpecific.VP9.picture_id =
+              header->codecHeader.VP9.picture_id;
+        }
+        if (header->codecHeader.VP9.tl0_pic_idx != kNoTl0PicIdx) {
+          _codecSpecificInfo.codecSpecific.VP9.tl0_pic_idx =
+              header->codecHeader.VP9.tl0_pic_idx;
+        }
+        if (header->codecHeader.VP9.temporal_idx != kNoTemporalIdx) {
+          _codecSpecificInfo.codecSpecific.VP9.temporal_idx =
+              header->codecHeader.VP9.temporal_idx;
+          _codecSpecificInfo.codecSpecific.VP9.temporal_up_switch =
+              header->codecHeader.VP9.temporal_up_switch;
         }
-        if (header->codecHeader.VP9.temporalIdx != kNoTemporalIdx) {
-          _codecSpecificInfo.codecSpecific.VP9.temporalIdx =
-              header->codecHeader.VP9.temporalIdx;
-          _codecSpecificInfo.codecSpecific.VP9.layerSync =
-              header->codecHeader.VP9.layerSync;
+        if (header->codecHeader.VP9.spatial_idx != kNoSpatialIdx) {
+          _codecSpecificInfo.codecSpecific.VP9.spatial_idx =
+              header->codecHeader.VP9.spatial_idx;
+          _codecSpecificInfo.codecSpecific.VP9.inter_layer_predicted =
+              header->codecHeader.VP9.inter_layer_predicted;
+        }
+        if (header->codecHeader.VP9.gof_idx != kNoGofIdx) {
+          _codecSpecificInfo.codecSpecific.VP9.gof_idx =
+              header->codecHeader.VP9.gof_idx;
         }
-        if (header->codecHeader.VP9.keyIdx != kNoKeyIdx) {
-          _codecSpecificInfo.codecSpecific.VP9.keyIdx =
-              header->codecHeader.VP9.keyIdx;
+        if (header->codecHeader.VP9.ss_data_available) {
+          _codecSpecificInfo.codecSpecific.VP9.num_spatial_layers =
+              header->codecHeader.VP9.num_spatial_layers;
+          _codecSpecificInfo.codecSpecific.VP9
+              .spatial_layer_resolution_present =
+              header->codecHeader.VP9.spatial_layer_resolution_present;
+          if (header->codecHeader.VP9.spatial_layer_resolution_present) {
+            for (size_t i = 0; i < header->codecHeader.VP9.num_spatial_layers;
+                 ++i) {
+              _codecSpecificInfo.codecSpecific.VP9.width[i] =
+                  header->codecHeader.VP9.width[i];
+              _codecSpecificInfo.codecSpecific.VP9.height[i] =
+                  header->codecHeader.VP9.height[i];
+            }
+          }
+          _codecSpecificInfo.codecSpecific.VP9.gof.CopyGofInfoVP9(
+              header->codecHeader.VP9.gof);
         }
         break;
       }
       default: {
         _codecSpecificInfo.codecType = kVideoCodecUnknown;
         break;
       }
     }
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.cc
@@ -70,16 +70,25 @@ bool VCMFrameBuffer::LayerSync() const {
 int VCMFrameBuffer::Tl0PicId() const {
   return _sessionInfo.Tl0PicId();
 }
 
 bool VCMFrameBuffer::NonReference() const {
   return _sessionInfo.NonReference();
 }
 
+void VCMFrameBuffer::SetGofInfo(const GofInfoVP9& gof_info, size_t idx) {
+  _sessionInfo.SetGofInfo(gof_info, idx);
+  // TODO(asapersson): Consider adding hdr->VP9.ref_picture_id for testing.
+  _codecSpecificInfo.codecSpecific.VP9.temporal_idx =
+      gof_info.temporal_idx[idx];
+  _codecSpecificInfo.codecSpecific.VP9.temporal_up_switch =
+      gof_info.temporal_up_switch[idx];
+}
+
 bool
 VCMFrameBuffer::IsSessionComplete() const {
     return _sessionInfo.complete();
 }
 
 // Insert packet
 VCMFrameBufferEnum
 VCMFrameBuffer::InsertPacket(const VCMPacket& packet,
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/frame_buffer.h
@@ -56,16 +56,18 @@ class VCMFrameBuffer : public VCMEncoded
   int32_t GetHighSeqNum() const;
 
   int PictureId() const;
   int TemporalId() const;
   bool LayerSync() const;
   int Tl0PicId() const;
   bool NonReference() const;
 
+  void SetGofInfo(const GofInfoVP9& gof_info, size_t idx);
+
   // Increments a counter to keep track of the number of packets of this frame
   // which were NACKed before they arrived.
   void IncrementNackCount();
   // Returns the number of packets of this frame which were NACKed before they
   // arrived.
   int16_t GetNackCount() const;
 
   int64_t LatestPacketTimeMs() const;
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_encoder.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/generic_encoder.cc
@@ -31,31 +31,59 @@ void CopyCodecSpecific(const CodecSpecif
           info->codecSpecific.VP8.nonReference;
       rtp->codecHeader.VP8.temporalIdx = info->codecSpecific.VP8.temporalIdx;
       rtp->codecHeader.VP8.layerSync = info->codecSpecific.VP8.layerSync;
       rtp->codecHeader.VP8.tl0PicIdx = info->codecSpecific.VP8.tl0PicIdx;
       rtp->codecHeader.VP8.keyIdx = info->codecSpecific.VP8.keyIdx;
       rtp->simulcastIdx = info->codecSpecific.VP8.simulcastIdx;
       return;
     }
+    case kVideoCodecVP9: {
+      rtp->codec = kRtpVideoVp9;
+      rtp->codecHeader.VP9.InitRTPVideoHeaderVP9();
+      rtp->codecHeader.VP9.inter_pic_predicted =
+          info->codecSpecific.VP9.inter_pic_predicted;
+      rtp->codecHeader.VP9.flexible_mode =
+          info->codecSpecific.VP9.flexible_mode;
+      rtp->codecHeader.VP9.ss_data_available =
+          info->codecSpecific.VP9.ss_data_available;
+      rtp->codecHeader.VP9.picture_id = info->codecSpecific.VP9.picture_id;
+      rtp->codecHeader.VP9.tl0_pic_idx = info->codecSpecific.VP9.tl0_pic_idx;
+      rtp->codecHeader.VP9.temporal_idx = info->codecSpecific.VP9.temporal_idx;
+      rtp->codecHeader.VP9.spatial_idx = info->codecSpecific.VP9.spatial_idx;
+      rtp->codecHeader.VP9.temporal_up_switch =
+          info->codecSpecific.VP9.temporal_up_switch;
+      rtp->codecHeader.VP9.inter_layer_predicted =
+          info->codecSpecific.VP9.inter_layer_predicted;
+      rtp->codecHeader.VP9.gof_idx = info->codecSpecific.VP9.gof_idx;
+      rtp->codecHeader.VP9.num_spatial_layers =
+          info->codecSpecific.VP9.num_spatial_layers;
+
+      if (info->codecSpecific.VP9.ss_data_available) {
+        rtp->codecHeader.VP9.spatial_layer_resolution_present =
+            info->codecSpecific.VP9.spatial_layer_resolution_present;
+        if (info->codecSpecific.VP9.spatial_layer_resolution_present) {
+          for (size_t i = 0; i < info->codecSpecific.VP9.num_spatial_layers;
+               ++i) {
+            rtp->codecHeader.VP9.width[i] = info->codecSpecific.VP9.width[i];
+            rtp->codecHeader.VP9.height[i] = info->codecSpecific.VP9.height[i];
+          }
+        }
+        rtp->codecHeader.VP9.gof.CopyGofInfoVP9(info->codecSpecific.VP9.gof);
+      }
+
+      rtp->codecHeader.VP9.num_ref_pics = info->codecSpecific.VP9.num_ref_pics;
+      for (int i = 0; i < info->codecSpecific.VP9.num_ref_pics; ++i)
+        rtp->codecHeader.VP9.pid_diff[i] = info->codecSpecific.VP9.p_diff[i];
+      return;
+    }
     case kVideoCodecH264:
       rtp->codec = kRtpVideoH264;
       rtp->simulcastIdx = info->codecSpecific.H264.simulcastIdx;
       return;
-    case kVideoCodecVP9:
-      rtp->codec = kRtpVideoVp9;
-      rtp->codecHeader.VP9.InitRTPVideoHeaderVP9();
-      rtp->codecHeader.VP9.pictureId = info->codecSpecific.VP9.pictureId;
-      rtp->codecHeader.VP9.nonReference =
-          info->codecSpecific.VP9.nonReference;
-      rtp->codecHeader.VP9.temporalIdx = info->codecSpecific.VP9.temporalIdx;
-      rtp->codecHeader.VP9.layerSync = info->codecSpecific.VP9.layerSync;
-      rtp->codecHeader.VP9.tl0PicIdx = info->codecSpecific.VP9.tl0PicIdx;
-      rtp->codecHeader.VP9.keyIdx = info->codecSpecific.VP9.keyIdx;
-      return;
     case kVideoCodecGeneric:
       rtp->codec = kRtpVideoGeneric;
       rtp->simulcastIdx = info->codecSpecific.generic.simulcast_idx;
       return;
     default:
       return;
   }
 }
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.cc
@@ -9,32 +9,37 @@
  */
 #include "webrtc/modules/video_coding/main/source/jitter_buffer.h"
 
 #include <assert.h>
 
 #include <algorithm>
 #include <utility>
 
+#include "webrtc/base/checks.h"
+#include "webrtc/base/trace_event.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
 #include "webrtc/modules/video_coding/main/interface/video_coding.h"
 #include "webrtc/modules/video_coding/main/source/frame_buffer.h"
 #include "webrtc/modules/video_coding/main/source/inter_frame_delay.h"
 #include "webrtc/modules/video_coding/main/source/internal_defines.h"
 #include "webrtc/modules/video_coding/main/source/jitter_buffer_common.h"
 #include "webrtc/modules/video_coding/main/source/jitter_estimator.h"
 #include "webrtc/modules/video_coding/main/source/packet.h"
 #include "webrtc/system_wrappers/interface/clock.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/event_wrapper.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 #include "webrtc/system_wrappers/interface/metrics.h"
-#include "webrtc/system_wrappers/interface/trace_event.h"
 
 namespace webrtc {
 
+// Interval for updating SS data.
+static const uint32_t kSsCleanupIntervalSec = 60;
+
 // Use this rtt if no value has been reported.
 static const int64_t kDefaultRtt = 200;
 
 typedef std::pair<uint32_t, VCMFrameBuffer*> FrameListPair;
 
 bool IsKeyFrame(FrameListPair pair) {
   return pair.second->FrameType() == kVideoFrameKey;
 }
@@ -141,16 +146,108 @@ void FrameList::CleanUpOldOrEmptyFrames(
 void FrameList::Reset(UnorderedFrameList* free_frames) {
   while (!empty()) {
     begin()->second->Reset();
     free_frames->push_back(begin()->second);
     erase(begin());
   }
 }
 
+bool Vp9SsMap::Insert(const VCMPacket& packet) {
+  if (!packet.codecSpecificHeader.codecHeader.VP9.ss_data_available)
+    return false;
+
+  ss_map_[packet.timestamp] = packet.codecSpecificHeader.codecHeader.VP9.gof;
+  return true;
+}
+
+void Vp9SsMap::Reset() {
+  ss_map_.clear();
+}
+
+bool Vp9SsMap::Find(uint32_t timestamp, SsMap::iterator* it_out) {
+  bool found = false;
+  for (SsMap::iterator it = ss_map_.begin(); it != ss_map_.end(); ++it) {
+    if (it->first == timestamp || IsNewerTimestamp(timestamp, it->first)) {
+      *it_out = it;
+      found = true;
+    }
+  }
+  return found;
+}
+
+void Vp9SsMap::RemoveOld(uint32_t timestamp) {
+  if (!TimeForCleanup(timestamp))
+    return;
+
+  SsMap::iterator it;
+  if (!Find(timestamp, &it))
+    return;
+
+  ss_map_.erase(ss_map_.begin(), it);
+  AdvanceFront(timestamp);
+}
+
+bool Vp9SsMap::TimeForCleanup(uint32_t timestamp) const {
+  if (ss_map_.empty() || !IsNewerTimestamp(timestamp, ss_map_.begin()->first))
+    return false;
+
+  uint32_t diff = timestamp - ss_map_.begin()->first;
+  return diff / kVideoPayloadTypeFrequency >= kSsCleanupIntervalSec;
+}
+
+void Vp9SsMap::AdvanceFront(uint32_t timestamp) {
+  DCHECK(!ss_map_.empty());
+  GofInfoVP9 gof = ss_map_.begin()->second;
+  ss_map_.erase(ss_map_.begin());
+  ss_map_[timestamp] = gof;
+}
+
+// TODO(asapersson): Update according to updates in RTP payload profile.
+bool Vp9SsMap::UpdatePacket(VCMPacket* packet) {
+  uint8_t gof_idx = packet->codecSpecificHeader.codecHeader.VP9.gof_idx;
+  if (gof_idx == kNoGofIdx)
+    return false;  // No update needed.
+
+  SsMap::iterator it;
+  if (!Find(packet->timestamp, &it))
+    return false;  // Corresponding SS not yet received.
+
+  if (gof_idx >= it->second.num_frames_in_gof)
+    return false;  // Assume corresponding SS not yet received.
+
+  RTPVideoHeaderVP9* vp9 = &packet->codecSpecificHeader.codecHeader.VP9;
+  vp9->temporal_idx = it->second.temporal_idx[gof_idx];
+  vp9->temporal_up_switch = it->second.temporal_up_switch[gof_idx];
+
+  // TODO(asapersson): Set vp9.ref_picture_id[i] and add usage.
+  vp9->num_ref_pics = it->second.num_ref_pics[gof_idx];
+  for (uint8_t i = 0; i < it->second.num_ref_pics[gof_idx]; ++i) {
+    vp9->pid_diff[i] = it->second.pid_diff[gof_idx][i];
+  }
+  return true;
+}
+
+void Vp9SsMap::UpdateFrames(FrameList* frames) {
+  for (const auto& frame_it : *frames) {
+    uint8_t gof_idx =
+        frame_it.second->CodecSpecific()->codecSpecific.VP9.gof_idx;
+    if (gof_idx == kNoGofIdx) {
+      continue;
+    }
+    SsMap::iterator ss_it;
+    if (Find(frame_it.second->TimeStamp(), &ss_it)) {
+      if (gof_idx >= ss_it->second.num_frames_in_gof) {
+        continue;  // Assume corresponding SS not yet received.
+      }
+      frame_it.second->SetGofInfo(ss_it->second, gof_idx);
+    }
+  }
+}
+
 VCMJitterBuffer::VCMJitterBuffer(Clock* clock, EventFactory* event_factory)
     : clock_(clock),
       running_(false),
       crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
       frame_event_(event_factory->CreateEvent()),
       max_number_of_frames_(kStartNumberOfFrames),
       free_frames_(),
       decodable_frames_(),
@@ -199,17 +296,17 @@ VCMJitterBuffer::~VCMJitterBuffer() {
   for (FrameList::iterator it = decodable_frames_.begin();
        it != decodable_frames_.end(); ++it) {
     delete it->second;
   }
   delete crit_sect_;
 }
 
 void VCMJitterBuffer::UpdateHistograms() {
-  if (num_packets_ <= 0) {
+  if (num_packets_ <= 0 || !running_) {
     return;
   }
   int64_t elapsed_sec =
       (clock_->TimeInMilliseconds() - time_first_packet_ms_) / 1000;
   if (elapsed_sec < metrics::kMinRunTimeInSeconds) {
     return;
   }
 
@@ -619,16 +716,19 @@ VCMFrameBufferEnum VCMJitterBuffer::Inse
         stats_callback_->OnDiscardedPacketsUpdated(num_discarded_packets_);
     }
     // Update last decoded sequence number if the packet arrived late and
     // belongs to a frame with a timestamp equal to the last decoded
     // timestamp.
     last_decoded_state_.UpdateOldPacket(&packet);
     DropPacketsFromNackList(last_decoded_state_.sequence_num());
 
+    // Also see if this old packet made more incomplete frames continuous.
+    FindAndInsertContinuousFramesWithState(last_decoded_state_);
+
     if (num_consecutive_old_packets_ > kMaxConsecutiveOldPackets) {
       LOG(LS_WARNING)
           << num_consecutive_old_packets_
           << " consecutive old packets received. Flushing the jitter buffer.";
       Flush();
       return kFlushIndicator;
     }
     return kOldPacket;
@@ -795,24 +895,35 @@ bool VCMJitterBuffer::IsContinuous(const
   return false;
 }
 
 void VCMJitterBuffer::FindAndInsertContinuousFrames(
     const VCMFrameBuffer& new_frame) {
   VCMDecodingState decoding_state;
   decoding_state.CopyFrom(last_decoded_state_);
   decoding_state.SetState(&new_frame);
+  FindAndInsertContinuousFramesWithState(decoding_state);
+}
+
+void VCMJitterBuffer::FindAndInsertContinuousFramesWithState(
+    const VCMDecodingState& original_decoded_state) {
+  // Copy original_decoded_state so we can move the state forward with each
+  // decodable frame we find.
+  VCMDecodingState decoding_state;
+  decoding_state.CopyFrom(original_decoded_state);
+
   // When temporal layers are available, we search for a complete or decodable
   // frame until we hit one of the following:
   // 1. Continuous base or sync layer.
   // 2. The end of the list was reached.
   for (FrameList::iterator it = incomplete_frames_.begin();
        it != incomplete_frames_.end();)  {
     VCMFrameBuffer* frame = it->second;
-    if (IsNewerTimestamp(new_frame.TimeStamp(), frame->TimeStamp())) {
+    if (IsNewerTimestamp(original_decoded_state.time_stamp(),
+                         frame->TimeStamp())) {
       ++it;
       continue;
     }
     if (IsContinuousInState(*frame, decoding_state)) {
       decodable_frames_.InsertFrame(frame);
       incomplete_frames_.erase(it++);
       decoding_state.SetState(frame);
     } else if (frame->TemporalId() <= 0) {
@@ -853,17 +964,17 @@ void VCMJitterBuffer::SetNackMode(VCMNac
   }
   assert(low_rtt_nack_threshold_ms >= -1 && high_rtt_nack_threshold_ms >= -1);
   assert(high_rtt_nack_threshold_ms == -1 ||
          low_rtt_nack_threshold_ms <= high_rtt_nack_threshold_ms);
   assert(low_rtt_nack_threshold_ms > -1 || high_rtt_nack_threshold_ms == -1);
   low_rtt_nack_threshold_ms_ = low_rtt_nack_threshold_ms;
   high_rtt_nack_threshold_ms_ = high_rtt_nack_threshold_ms;
   // Don't set a high start rtt if high_rtt_nack_threshold_ms_ is used, to not
-  // disable NACK in hybrid mode.
+  // disable NACK in |kNack| mode.
   if (rtt_ms_ == kDefaultRtt && high_rtt_nack_threshold_ms_ != -1) {
     rtt_ms_ = 0;
   }
   if (!WaitForRetransmissions()) {
     jitter_estimate_.ResetNackCount();
   }
 }
 
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_buffer.h
@@ -70,16 +70,47 @@ class FrameList
   VCMFrameBuffer* Back() const;
   int RecycleFramesUntilKeyFrame(FrameList::iterator* key_frame_it,
       UnorderedFrameList* free_frames);
   void CleanUpOldOrEmptyFrames(VCMDecodingState* decoding_state,
                                UnorderedFrameList* free_frames);
   void Reset(UnorderedFrameList* free_frames);
 };
 
+class Vp9SsMap {
+ public:
+  typedef std::map<uint32_t, GofInfoVP9, TimestampLessThan> SsMap;
+  bool Insert(const VCMPacket& packet);
+  void Reset();
+
+  // Removes SS data that are older than |timestamp|.
+  // The |timestamp| should be an old timestamp, i.e. packets with older
+  // timestamps should no longer be inserted.
+  void RemoveOld(uint32_t timestamp);
+
+  bool UpdatePacket(VCMPacket* packet);
+  void UpdateFrames(FrameList* frames);
+
+  // Public for testing.
+  // Returns an iterator to the corresponding SS data for the input |timestamp|.
+  bool Find(uint32_t timestamp, SsMap::iterator* it);
+
+ private:
+  // These two functions are called by RemoveOld.
+  // Checks if it is time to do a clean up (done each kSsCleanupIntervalSec).
+  bool TimeForCleanup(uint32_t timestamp) const;
+
+  // Advances the oldest SS data to handle timestamp wrap in cases where SS data
+  // are received very seldom (e.g. only once in beginning, second when
+  // IsNewerTimestamp is not true).
+  void AdvanceFront(uint32_t timestamp);
+
+  SsMap ss_map_;
+};
+
 class VCMJitterBuffer {
  public:
   VCMJitterBuffer(Clock* clock,
                   EventFactory* event_factory);
   virtual ~VCMJitterBuffer();
 
   // Initializes and starts jitter buffer.
   void Start();
@@ -210,16 +241,22 @@ class VCMJitterBuffer {
   // decodable frames into account.
   bool IsContinuousInState(const VCMFrameBuffer& frame,
                            const VCMDecodingState& decoding_state) const
       EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
   // Returns true if |frame| is continuous in the |last_decoded_state_|, taking
   // all decodable frames into account.
   bool IsContinuous(const VCMFrameBuffer& frame) const
       EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
+  // Looks for frames in |incomplete_frames_| which are continuous in the
+  // provided |decoded_state|. Starts the search from the timestamp of
+  // |decoded_state|.
+  void FindAndInsertContinuousFramesWithState(
+      const VCMDecodingState& decoded_state)
+      EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
   // Looks for frames in |incomplete_frames_| which are continuous in
   // |last_decoded_state_| taking all decodable frames into account. Starts
   // the search from |new_frame|.
   void FindAndInsertContinuousFrames(const VCMFrameBuffer& new_frame)
       EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
   VCMFrameBuffer* NextFrame() const EXCLUSIVE_LOCKS_REQUIRED(crit_sect_);
   // Returns true if the NACK list was updated to cover sequence numbers up to
   // |sequence_number|. If false a key frame is needed to get into a state where
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.cc
@@ -54,50 +54,89 @@ int VCMSessionInfo::HighSequenceNumber()
   if (packets_.empty())
     return empty_seq_num_high_;
   if (empty_seq_num_high_ == -1)
     return packets_.back().seqNum;
   return LatestSequenceNumber(packets_.back().seqNum, empty_seq_num_high_);
 }
 
 int VCMSessionInfo::PictureId() const {
-  if (packets_.empty() ||
-      packets_.front().codecSpecificHeader.codec != kRtpVideoVp8)
+  if (packets_.empty())
     return kNoPictureId;
-  return packets_.front().codecSpecificHeader.codecHeader.VP8.pictureId;
+  if (packets_.front().codecSpecificHeader.codec == kRtpVideoVp8) {
+    return packets_.front().codecSpecificHeader.codecHeader.VP8.pictureId;
+  } else if (packets_.front().codecSpecificHeader.codec == kRtpVideoVp9) {
+    return packets_.front().codecSpecificHeader.codecHeader.VP9.picture_id;
+  } else {
+    return kNoPictureId;
+  }
 }
 
 int VCMSessionInfo::TemporalId() const {
-  if (packets_.empty() ||
-      packets_.front().codecSpecificHeader.codec != kRtpVideoVp8)
+  if (packets_.empty())
     return kNoTemporalIdx;
-  return packets_.front().codecSpecificHeader.codecHeader.VP8.temporalIdx;
+  if (packets_.front().codecSpecificHeader.codec == kRtpVideoVp8) {
+    return packets_.front().codecSpecificHeader.codecHeader.VP8.temporalIdx;
+  } else if (packets_.front().codecSpecificHeader.codec == kRtpVideoVp9) {
+    return packets_.front().codecSpecificHeader.codecHeader.VP9.temporal_idx;
+  } else {
+    return kNoTemporalIdx;
+  }
 }
 
 bool VCMSessionInfo::LayerSync() const {
-  if (packets_.empty() ||
-        packets_.front().codecSpecificHeader.codec != kRtpVideoVp8)
+  if (packets_.empty())
     return false;
-  return packets_.front().codecSpecificHeader.codecHeader.VP8.layerSync;
+  if (packets_.front().codecSpecificHeader.codec == kRtpVideoVp8) {
+    return packets_.front().codecSpecificHeader.codecHeader.VP8.layerSync;
+  } else if (packets_.front().codecSpecificHeader.codec == kRtpVideoVp9) {
+    return
+        packets_.front().codecSpecificHeader.codecHeader.VP9.temporal_up_switch;
+  } else {
+    return false;
+  }
 }
 
 int VCMSessionInfo::Tl0PicId() const {
-  if (packets_.empty() ||
-      packets_.front().codecSpecificHeader.codec != kRtpVideoVp8)
+  if (packets_.empty())
     return kNoTl0PicIdx;
-  return packets_.front().codecSpecificHeader.codecHeader.VP8.tl0PicIdx;
+  if (packets_.front().codecSpecificHeader.codec == kRtpVideoVp8) {
+    return packets_.front().codecSpecificHeader.codecHeader.VP8.tl0PicIdx;
+  } else if (packets_.front().codecSpecificHeader.codec == kRtpVideoVp9) {
+    return packets_.front().codecSpecificHeader.codecHeader.VP9.tl0_pic_idx;
+  } else {
+    return kNoTl0PicIdx;
+  }
 }
 
 bool VCMSessionInfo::NonReference() const {
   if (packets_.empty() ||
       packets_.front().codecSpecificHeader.codec != kRtpVideoVp8)
     return false;
   return packets_.front().codecSpecificHeader.codecHeader.VP8.nonReference;
 }
 
+void VCMSessionInfo::SetGofInfo(const GofInfoVP9& gof_info, size_t idx) {
+  if (packets_.empty() ||
+      packets_.front().codecSpecificHeader.codec != kRtpVideoVp9 ||
+      packets_.front().codecSpecificHeader.codecHeader.VP9.flexible_mode) {
+    return;
+  }
+  packets_.front().codecSpecificHeader.codecHeader.VP9.temporal_idx =
+      gof_info.temporal_idx[idx];
+  packets_.front().codecSpecificHeader.codecHeader.VP9.temporal_up_switch =
+      gof_info.temporal_up_switch[idx];
+  packets_.front().codecSpecificHeader.codecHeader.VP9.num_ref_pics =
+      gof_info.num_ref_pics[idx];
+  for (uint8_t i = 0; i < gof_info.num_ref_pics[idx]; ++i) {
+    packets_.front().codecSpecificHeader.codecHeader.VP9.pid_diff[i] =
+        gof_info.pid_diff[idx][i];
+  }
+}
+
 void VCMSessionInfo::Reset() {
   session_nack_ = false;
   complete_ = false;
   decodable_ = false;
   frame_type_ = kVideoFrameDelta;
   packets_.clear();
   empty_seq_num_low_ = -1;
   empty_seq_num_high_ = -1;
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.h
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/session_info.h
@@ -83,16 +83,18 @@ class VCMSessionInfo {
   // Returns highest sequence number, media or empty.
   int HighSequenceNumber() const;
   int PictureId() const;
   int TemporalId() const;
   bool LayerSync() const;
   int Tl0PicId() const;
   bool NonReference() const;
 
+  void SetGofInfo(const GofInfoVP9& gof_info, size_t idx);
+
   // The number of packets discarded because the decoder can't make use of
   // them.
   int packets_not_decodable() const;
 
  private:
   enum { kMaxVP8Partitions = 9 };
 
   typedef std::list<VCMPacket> PacketList;
--- a/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/vie_codec_impl.cc
@@ -84,16 +84,17 @@ static void LogCodec(const VideoCodec& c
                  << ", keyFrameInterval: "
                  << codec.codecSpecific.H264.keyFrameInterval
                  << ", spslen: "
                  << codec.codecSpecific.H264.spsLen
                  << ", ppslen: "
                  << codec.codecSpecific.H264.ppsLen;
   } else if (codec.codecType == kVideoCodecVP9) {
     LOG(LS_INFO) << "VP9 specific settings";
+    // XXX FIX!! log VP9 specific settings
   }
 }
 
 
 ViECodec* ViECodec::GetInterface(VideoEngine* video_engine) {
 #ifdef WEBRTC_VIDEO_ENGINE_CODEC_API
   if (!video_engine) {
     return NULL;
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -7,20 +7,21 @@
 #include <ctype.h>
 #include <errno.h>
 #include <limits.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#if !defined(MOZ_PROFILING)
+#error "DMD requires MOZ_PROFILING"
+#endif
+
 #ifdef XP_WIN
-#if defined(MOZ_OPTIMIZE) && !defined(MOZ_PROFILING)
-#error "Optimized, DMD-enabled builds on Windows must be built with --enable-profiling"
-#endif
 #include <windows.h>
 #include <process.h>
 #else
 #include <unistd.h>
 #endif
 
 #ifdef ANDROID
 #include <android/log.h>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -377,16 +377,17 @@ pref("media.navigator.video.default_heig
 pref("media.peerconnection.enabled", true);
 pref("media.peerconnection.video.enabled", true);
 pref("media.navigator.video.max_fs", 1200); // 640x480 == 1200mb
 pref("media.navigator.video.max_fr", 30);
 pref("media.navigator.video.h264.level", 12); // 0x42E00C - level 1.2
 pref("media.navigator.video.h264.max_br", 700); // 8x10
 pref("media.navigator.video.h264.max_mbps", 11880); // CIF@30fps
 pref("media.peerconnection.video.h264_enabled", false);
+pref("media.peerconnection.video.vp9_enabled", false);
 pref("media.getusermedia.aec", 4);
 // Gonk typically captures at QVGA, and so min resolution is QQVGA or
 // 160x120; 100Kbps is plenty for that.
 pref("media.peerconnection.video.min_bitrate", 100);
 pref("media.peerconnection.video.start_bitrate", 220);
<