LCOV - code coverage report
Current view: top level - dom/media - VideoFrameContainer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 199 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 39 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "VideoFrameContainer.h"
       8             : 
       9             : #include "mozilla/dom/HTMLMediaElement.h"
      10             : #include "mozilla/Telemetry.h"
      11             : 
      12             : #include "nsIFrame.h"
      13             : #include "nsDisplayList.h"
      14             : #include "nsSVGEffects.h"
      15             : 
      16             : using namespace mozilla::layers;
      17             : 
      18             : namespace mozilla {
      19             : static LazyLogModule gVideoFrameContainerLog("VideoFrameContainer");
      20             : #define CONTAINER_LOG(type, msg) MOZ_LOG(gVideoFrameContainerLog, type, msg)
      21             : 
      22             : #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
      23             : 
      24             : namespace {
      25             : template<Telemetry::HistogramID ID>
      26           0 : class AutoTimer
      27             : {
      28             :   // Set a threshold to reduce performance overhead
      29             :   // for we're measuring hot spots.
      30             :   static const uint32_t sThresholdMS = 1000;
      31             : public:
      32           0 :   ~AutoTimer()
      33             :   {
      34           0 :     auto end = TimeStamp::Now();
      35           0 :     auto diff = uint32_t((end - mStart).ToMilliseconds());
      36           0 :     if (diff > sThresholdMS) {
      37           0 :       Telemetry::Accumulate(ID, diff);
      38             :     }
      39           0 :   }
      40             : private:
      41             :   const TimeStamp mStart = TimeStamp::Now();
      42             : };
      43             : }
      44             : 
      45           0 : VideoFrameContainer::VideoFrameContainer(
      46             :   dom::HTMLMediaElement* aElement,
      47           0 :   already_AddRefed<ImageContainer> aContainer)
      48             :   : mElement(aElement)
      49             :   , mImageContainer(aContainer)
      50             :   , mMutex("nsVideoFrameContainer")
      51             :   , mBlackImage(nullptr)
      52             :   , mFrameID(0)
      53             :   , mPendingPrincipalHandle(PRINCIPAL_HANDLE_NONE)
      54             :   , mFrameIDForPendingPrincipalHandle(0)
      55           0 :   , mMainThread(aElement->AbstractMainThread())
      56             : {
      57           0 :   NS_ASSERTION(aElement, "aElement must not be null");
      58           0 :   NS_ASSERTION(mImageContainer, "aContainer must not be null");
      59           0 : }
      60             : 
      61           0 : VideoFrameContainer::~VideoFrameContainer()
      62           0 : {}
      63             : 
      64           0 : PrincipalHandle VideoFrameContainer::GetLastPrincipalHandle()
      65             : {
      66           0 :   MutexAutoLock lock(mMutex);
      67           0 :   return GetLastPrincipalHandleLocked();
      68             : }
      69             : 
      70           0 : PrincipalHandle VideoFrameContainer::GetLastPrincipalHandleLocked()
      71             : {
      72           0 :   return mLastPrincipalHandle;
      73             : }
      74             : 
      75           0 : void VideoFrameContainer::UpdatePrincipalHandleForFrameID(const PrincipalHandle& aPrincipalHandle,
      76             :                                                           const ImageContainer::FrameID& aFrameID)
      77             : {
      78           0 :   MutexAutoLock lock(mMutex);
      79           0 :   UpdatePrincipalHandleForFrameIDLocked(aPrincipalHandle, aFrameID);
      80           0 : }
      81             : 
      82           0 : void VideoFrameContainer::UpdatePrincipalHandleForFrameIDLocked(const PrincipalHandle& aPrincipalHandle,
      83             :                                                                 const ImageContainer::FrameID& aFrameID)
      84             : {
      85           0 :   if (mPendingPrincipalHandle == aPrincipalHandle) {
      86           0 :     return;
      87             :   }
      88           0 :   mPendingPrincipalHandle = aPrincipalHandle;
      89           0 :   mFrameIDForPendingPrincipalHandle = aFrameID;
      90             : }
      91             : 
      92             : static void
      93           0 : SetImageToBlackPixel(PlanarYCbCrImage* aImage)
      94             : {
      95           0 :   uint8_t blackPixel[] = { 0x10, 0x80, 0x80 };
      96             : 
      97           0 :   PlanarYCbCrData data;
      98           0 :   data.mYChannel = blackPixel;
      99           0 :   data.mCbChannel = blackPixel + 1;
     100           0 :   data.mCrChannel = blackPixel + 2;
     101           0 :   data.mYStride = data.mCbCrStride = 1;
     102           0 :   data.mPicSize = data.mYSize = data.mCbCrSize = gfx::IntSize(1, 1);
     103           0 :   aImage->CopyData(data);
     104           0 : }
     105             : 
     106           0 : class VideoFrameContainerInvalidateRunnable : public Runnable {
     107             : public:
     108           0 :   explicit VideoFrameContainerInvalidateRunnable(VideoFrameContainer* aVideoFrameContainer)
     109           0 :     : Runnable("VideoFrameContainerInvalidateRunnable")
     110           0 :     , mVideoFrameContainer(aVideoFrameContainer)
     111           0 :   {}
     112           0 :   NS_IMETHOD Run()
     113             :   {
     114           0 :     MOZ_ASSERT(NS_IsMainThread());
     115             : 
     116           0 :     mVideoFrameContainer->Invalidate();
     117             : 
     118           0 :     return NS_OK;
     119             :   }
     120             : private:
     121             :   RefPtr<VideoFrameContainer> mVideoFrameContainer;
     122             : };
     123             : 
     124           0 : void VideoFrameContainer::SetCurrentFrames(const VideoSegment& aSegment)
     125             : {
     126           0 :   if (aSegment.IsEmpty()) {
     127           0 :     return;
     128             :   }
     129             : 
     130           0 :   MutexAutoLock lock(mMutex);
     131           0 :   AutoTimer<Telemetry::VFC_SETVIDEOSEGMENT_LOCK_HOLD_MS> lockHold;
     132             : 
     133             :   // Collect any new frames produced in this iteration.
     134           0 :   AutoTArray<ImageContainer::NonOwningImage,4> newImages;
     135           0 :   PrincipalHandle lastPrincipalHandle = PRINCIPAL_HANDLE_NONE;
     136             : 
     137           0 :   VideoSegment::ConstChunkIterator iter(aSegment);
     138           0 :   while (!iter.IsEnded()) {
     139           0 :     VideoChunk chunk = *iter;
     140             : 
     141           0 :     const VideoFrame* frame = &chunk.mFrame;
     142           0 :     if (*frame == mLastPlayedVideoFrame) {
     143           0 :       iter.Next();
     144           0 :       continue;
     145             :     }
     146             : 
     147           0 :     Image* image = frame->GetImage();
     148           0 :     CONTAINER_LOG(LogLevel::Verbose,
     149             :                   ("VideoFrameContainer %p writing video frame %p (%d x %d)",
     150             :                   this, image, frame->GetIntrinsicSize().width,
     151             :                   frame->GetIntrinsicSize().height));
     152             : 
     153           0 :     if (frame->GetForceBlack()) {
     154           0 :       if (!mBlackImage) {
     155           0 :         mBlackImage = GetImageContainer()->CreatePlanarYCbCrImage();
     156           0 :         if (mBlackImage) {
     157             :           // Sets the image to a single black pixel, which will be scaled to
     158             :           // fill the rendered size.
     159           0 :           SetImageToBlackPixel(mBlackImage->AsPlanarYCbCrImage());
     160             :         }
     161             :       }
     162           0 :       if (mBlackImage) {
     163           0 :         image = mBlackImage;
     164             :       }
     165             :     }
     166             :     // Don't append null image to the newImages.
     167           0 :     if (!image) {
     168           0 :       iter.Next();
     169           0 :       continue;
     170             :     }
     171           0 :     newImages.AppendElement(ImageContainer::NonOwningImage(image, chunk.mTimeStamp));
     172             : 
     173           0 :     lastPrincipalHandle = chunk.GetPrincipalHandle();
     174             : 
     175           0 :     mLastPlayedVideoFrame = *frame;
     176           0 :     iter.Next();
     177             :   }
     178             : 
     179             :   // Don't update if there are no changes.
     180           0 :   if (newImages.IsEmpty()) {
     181           0 :     return;
     182             :   }
     183             : 
     184           0 :   AutoTArray<ImageContainer::NonOwningImage,4> images;
     185             : 
     186             :   bool principalHandleChanged =
     187           0 :      lastPrincipalHandle != PRINCIPAL_HANDLE_NONE &&
     188           0 :      lastPrincipalHandle != GetLastPrincipalHandleLocked();
     189             : 
     190             :   // Add the frames from this iteration.
     191           0 :   for (auto& image : newImages) {
     192           0 :     image.mFrameID = NewFrameID();
     193           0 :     images.AppendElement(image);
     194             :   }
     195             : 
     196           0 :   if (principalHandleChanged) {
     197           0 :     UpdatePrincipalHandleForFrameIDLocked(lastPrincipalHandle,
     198           0 :                                           newImages.LastElement().mFrameID);
     199             :   }
     200             : 
     201           0 :   SetCurrentFramesLocked(mLastPlayedVideoFrame.GetIntrinsicSize(), images);
     202             :   nsCOMPtr<nsIRunnable> event =
     203           0 :     new VideoFrameContainerInvalidateRunnable(this);
     204           0 :   mMainThread->Dispatch(event.forget());
     205             : 
     206           0 :   images.ClearAndRetainStorage();
     207             : }
     208             : 
     209           0 : void VideoFrameContainer::ClearFrames()
     210             : {
     211           0 :   ClearFutureFrames();
     212           0 : }
     213             : 
     214           0 : void VideoFrameContainer::SetCurrentFrame(const gfx::IntSize& aIntrinsicSize,
     215             :                                           Image* aImage,
     216             :                                           const TimeStamp& aTargetTime)
     217             : {
     218           0 :   if (aImage) {
     219           0 :     MutexAutoLock lock(mMutex);
     220           0 :     AutoTimer<Telemetry::VFC_SETCURRENTFRAME_LOCK_HOLD_MS> lockHold;
     221           0 :     AutoTArray<ImageContainer::NonOwningImage,1> imageList;
     222             :     imageList.AppendElement(
     223           0 :         ImageContainer::NonOwningImage(aImage, aTargetTime, ++mFrameID));
     224           0 :     SetCurrentFramesLocked(aIntrinsicSize, imageList);
     225             :   } else {
     226           0 :     ClearCurrentFrame(aIntrinsicSize);
     227             :   }
     228           0 : }
     229             : 
     230           0 : void VideoFrameContainer::SetCurrentFrames(const gfx::IntSize& aIntrinsicSize,
     231             :                                            const nsTArray<ImageContainer::NonOwningImage>& aImages)
     232             : {
     233           0 :   MutexAutoLock lock(mMutex);
     234           0 :   AutoTimer<Telemetry::VFC_SETIMAGES_LOCK_HOLD_MS> lockHold;
     235           0 :   SetCurrentFramesLocked(aIntrinsicSize, aImages);
     236           0 : }
     237             : 
     238           0 : void VideoFrameContainer::SetCurrentFramesLocked(const gfx::IntSize& aIntrinsicSize,
     239             :                                                  const nsTArray<ImageContainer::NonOwningImage>& aImages)
     240             : {
     241           0 :   mMutex.AssertCurrentThreadOwns();
     242             : 
     243           0 :   if (aIntrinsicSize != mIntrinsicSize) {
     244           0 :     mIntrinsicSize = aIntrinsicSize;
     245           0 :     RefPtr<VideoFrameContainer> self = this;
     246           0 :     mMainThread->Dispatch(NS_NewRunnableFunction(
     247           0 :       "IntrinsicSizeChanged", [this, self, aIntrinsicSize]() {
     248           0 :         mMainThreadState.mIntrinsicSize = aIntrinsicSize;
     249           0 :         mMainThreadState.mIntrinsicSizeChanged = true;
     250           0 :       }));
     251             :   }
     252             : 
     253           0 :   gfx::IntSize oldFrameSize = mImageContainer->GetCurrentSize();
     254             : 
     255             :   // When using the OMX decoder, destruction of the current image can indirectly
     256             :   //  block on main thread I/O. If we let this happen while holding onto
     257             :   //  |mImageContainer|'s lock, then when the main thread then tries to
     258             :   //  composite it can then block on |mImageContainer|'s lock, causing a
     259             :   //  deadlock. We use this hack to defer the destruction of the current image
     260             :   //  until it is safe.
     261           0 :   nsTArray<ImageContainer::OwningImage> oldImages;
     262           0 :   mImageContainer->GetCurrentImages(&oldImages);
     263             : 
     264           0 :   PrincipalHandle principalHandle = PRINCIPAL_HANDLE_NONE;
     265             :   ImageContainer::FrameID lastFrameIDForOldPrincipalHandle =
     266           0 :     mFrameIDForPendingPrincipalHandle - 1;
     267           0 :   if (mPendingPrincipalHandle != PRINCIPAL_HANDLE_NONE &&
     268           0 :        ((!oldImages.IsEmpty() &&
     269           0 :           oldImages.LastElement().mFrameID >= lastFrameIDForOldPrincipalHandle) ||
     270           0 :         (!aImages.IsEmpty() &&
     271           0 :           aImages[0].mFrameID > lastFrameIDForOldPrincipalHandle))) {
     272             :     // We are releasing the last FrameID prior to `lastFrameIDForOldPrincipalHandle`
     273             :     // OR
     274             :     // there are no FrameIDs prior to `lastFrameIDForOldPrincipalHandle` in the new
     275             :     // set of images.
     276             :     // This means that the old principal handle has been flushed out and we can
     277             :     // notify our video element about this change.
     278           0 :     principalHandle = mPendingPrincipalHandle;
     279           0 :     mLastPrincipalHandle = mPendingPrincipalHandle;
     280           0 :     mPendingPrincipalHandle = PRINCIPAL_HANDLE_NONE;
     281           0 :     mFrameIDForPendingPrincipalHandle = 0;
     282             :   }
     283             : 
     284           0 :   if (aImages.IsEmpty()) {
     285           0 :     mImageContainer->ClearAllImages();
     286             :   } else {
     287           0 :     mImageContainer->SetCurrentImages(aImages);
     288             :   }
     289           0 :   gfx::IntSize newFrameSize = mImageContainer->GetCurrentSize();
     290           0 :   bool imageSizeChanged = (oldFrameSize != newFrameSize);
     291             : 
     292           0 :   if (principalHandle != PRINCIPAL_HANDLE_NONE || imageSizeChanged) {
     293           0 :     RefPtr<VideoFrameContainer> self = this;
     294           0 :     mMainThread->Dispatch(NS_NewRunnableFunction(
     295             :       "PrincipalHandleOrImageSizeChanged",
     296           0 :       [this, self, principalHandle, imageSizeChanged]() {
     297           0 :         mMainThreadState.mImageSizeChanged = imageSizeChanged;
     298           0 :         if (mElement && principalHandle != PRINCIPAL_HANDLE_NONE) {
     299           0 :           mElement->PrincipalHandleChangedForVideoFrameContainer(
     300           0 :             this, principalHandle);
     301             :         }
     302           0 :       }));
     303             :   }
     304           0 : }
     305             : 
     306           0 : void VideoFrameContainer::ClearCurrentFrame()
     307             : {
     308           0 :   MutexAutoLock lock(mMutex);
     309           0 :   AutoTimer<Telemetry::VFC_CLEARCURRENTFRAME_LOCK_HOLD_MS> lockHold;
     310             : 
     311             :   // See comment in SetCurrentFrame for the reasoning behind
     312             :   // using a kungFuDeathGrip here.
     313           0 :   nsTArray<ImageContainer::OwningImage> kungFuDeathGrip;
     314           0 :   mImageContainer->GetCurrentImages(&kungFuDeathGrip);
     315             : 
     316           0 :   mImageContainer->ClearAllImages();
     317           0 :   mImageContainer->ClearCachedResources();
     318           0 : }
     319             : 
     320           0 : void VideoFrameContainer::ClearFutureFrames()
     321             : {
     322           0 :   MutexAutoLock lock(mMutex);
     323           0 :   AutoTimer<Telemetry::VFC_CLEARFUTUREFRAMES_LOCK_HOLD_MS> lockHold;
     324             : 
     325             :   // See comment in SetCurrentFrame for the reasoning behind
     326             :   // using a kungFuDeathGrip here.
     327           0 :   nsTArray<ImageContainer::OwningImage> kungFuDeathGrip;
     328           0 :   mImageContainer->GetCurrentImages(&kungFuDeathGrip);
     329             : 
     330           0 :   if (!kungFuDeathGrip.IsEmpty()) {
     331           0 :     nsTArray<ImageContainer::NonOwningImage> currentFrame;
     332           0 :     const ImageContainer::OwningImage& img = kungFuDeathGrip[0];
     333           0 :     currentFrame.AppendElement(ImageContainer::NonOwningImage(img.mImage,
     334           0 :         img.mTimeStamp, img.mFrameID, img.mProducerID));
     335           0 :     mImageContainer->SetCurrentImages(currentFrame);
     336             :   }
     337           0 : }
     338             : 
     339             : void
     340           0 : VideoFrameContainer::ClearCachedResources()
     341             : {
     342           0 :   mImageContainer->ClearCachedResources();
     343           0 : }
     344             : 
     345           0 : ImageContainer* VideoFrameContainer::GetImageContainer() {
     346           0 :   return mImageContainer;
     347             : }
     348             : 
     349             : 
     350           0 : double VideoFrameContainer::GetFrameDelay()
     351             : {
     352           0 :   return mImageContainer->GetPaintDelay().ToSeconds();
     353             : }
     354             : 
     355           0 : void VideoFrameContainer::InvalidateWithFlags(uint32_t aFlags)
     356             : {
     357           0 :   NS_ASSERTION(NS_IsMainThread(), "Must call on main thread");
     358             : 
     359           0 :   if (!mElement) {
     360             :     // Element has been destroyed
     361           0 :     return;
     362             :   }
     363             : 
     364           0 :   nsIFrame* frame = mElement->GetPrimaryFrame();
     365           0 :   bool invalidateFrame = mMainThreadState.mImageSizeChanged;
     366           0 :   mMainThreadState.mImageSizeChanged = false;
     367             : 
     368           0 :   if (mMainThreadState.mIntrinsicSizeChanged) {
     369           0 :     mElement->UpdateMediaSize(mMainThreadState.mIntrinsicSize);
     370           0 :     mMainThreadState.mIntrinsicSizeChanged = false;
     371           0 :     if (frame) {
     372           0 :       nsPresContext* presContext = frame->PresContext();
     373           0 :       nsIPresShell* presShell = presContext->PresShell();
     374             :       presShell->FrameNeedsReflow(
     375           0 :         frame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
     376             :     }
     377             :   }
     378             : 
     379           0 :   bool asyncInvalidate = mImageContainer &&
     380           0 :                          mImageContainer->IsAsync() &&
     381           0 :                          !(aFlags & INVALIDATE_FORCE);
     382             : 
     383           0 :   if (frame) {
     384           0 :     if (invalidateFrame) {
     385           0 :       frame->InvalidateFrame();
     386             :     } else {
     387           0 :       frame->InvalidateLayer(nsDisplayItem::TYPE_VIDEO, nullptr, nullptr,
     388           0 :                              asyncInvalidate ? nsIFrame::UPDATE_IS_ASYNC : 0);
     389             :     }
     390             :   }
     391             : 
     392           0 :   nsSVGEffects::InvalidateDirectRenderingObservers(mElement);
     393             : }
     394             : 
     395             : } // namespace mozilla
     396             : 
     397             : #undef NS_DispatchToMainThread

Generated by: LCOV version 1.13