2012-02-15 08:35:01 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
|
|
/* 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 "VideoFrameContainer.h"
|
|
|
|
|
2013-03-19 16:23:54 +04:00
|
|
|
#include "mozilla/dom/HTMLMediaElement.h"
|
2012-02-15 08:35:01 +04:00
|
|
|
#include "nsIFrame.h"
|
|
|
|
#include "nsDisplayList.h"
|
|
|
|
#include "nsSVGEffects.h"
|
|
|
|
|
|
|
|
using namespace mozilla::layers;
|
|
|
|
|
|
|
|
namespace mozilla {
|
2015-12-24 05:43:28 +03:00
|
|
|
PRLogModuleInfo* gVideoFrameContainerLog;
|
|
|
|
#define CONTAINER_LOG(type, msg) MOZ_LOG(gVideoFrameContainerLog, type, msg)
|
2012-02-15 08:35:01 +04:00
|
|
|
|
2013-03-19 16:23:54 +04:00
|
|
|
VideoFrameContainer::VideoFrameContainer(dom::HTMLMediaElement* aElement,
|
2012-08-21 08:06:46 +04:00
|
|
|
already_AddRefed<ImageContainer> aContainer)
|
|
|
|
: mElement(aElement),
|
|
|
|
mImageContainer(aContainer), mMutex("nsVideoFrameContainer"),
|
2015-12-24 05:43:28 +03:00
|
|
|
mBlackImage(nullptr),
|
2015-07-03 13:13:48 +03:00
|
|
|
mFrameID(0),
|
2016-02-04 04:27:09 +03:00
|
|
|
mIntrinsicSizeChanged(false), mImageSizeChanged(false),
|
|
|
|
mPendingPrincipalHandle(PRINCIPAL_HANDLE_NONE), mFrameIDForPendingPrincipalHandle(0)
|
2012-08-21 08:06:46 +04:00
|
|
|
{
|
|
|
|
NS_ASSERTION(aElement, "aElement must not be null");
|
|
|
|
NS_ASSERTION(mImageContainer, "aContainer must not be null");
|
2015-12-24 05:43:28 +03:00
|
|
|
if (!gVideoFrameContainerLog) {
|
|
|
|
gVideoFrameContainerLog = PR_NewLogModule("VideoFrameContainer");
|
|
|
|
}
|
2012-08-21 08:06:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
VideoFrameContainer::~VideoFrameContainer()
|
|
|
|
{}
|
|
|
|
|
2016-02-04 04:27:09 +03:00
|
|
|
PrincipalHandle VideoFrameContainer::GetLastPrincipalHandle()
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
return mLastPrincipalHandle;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoFrameContainer::UpdatePrincipalHandleForFrameID(const PrincipalHandle& aPrincipalHandle,
|
|
|
|
const ImageContainer::FrameID& aFrameID)
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
if (mPendingPrincipalHandle == aPrincipalHandle) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mPendingPrincipalHandle = aPrincipalHandle;
|
|
|
|
mFrameIDForPendingPrincipalHandle = aFrameID;
|
|
|
|
}
|
|
|
|
|
2015-12-24 05:43:28 +03:00
|
|
|
static void
|
|
|
|
SetImageToBlackPixel(PlanarYCbCrImage* aImage)
|
|
|
|
{
|
|
|
|
uint8_t blackPixel[] = { 0x10, 0x80, 0x80 };
|
|
|
|
|
|
|
|
PlanarYCbCrData data;
|
|
|
|
data.mYChannel = blackPixel;
|
|
|
|
data.mCbChannel = blackPixel + 1;
|
|
|
|
data.mCrChannel = blackPixel + 2;
|
|
|
|
data.mYStride = data.mCbCrStride = 1;
|
|
|
|
data.mPicSize = data.mYSize = data.mCbCrSize = gfx::IntSize(1, 1);
|
|
|
|
aImage->CopyData(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
class VideoFrameContainerInvalidateRunnable : public Runnable {
|
|
|
|
public:
|
|
|
|
explicit VideoFrameContainerInvalidateRunnable(VideoFrameContainer* aVideoFrameContainer)
|
|
|
|
: mVideoFrameContainer(aVideoFrameContainer)
|
|
|
|
{}
|
|
|
|
NS_IMETHOD Run()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
|
|
|
|
mVideoFrameContainer->Invalidate();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
RefPtr<VideoFrameContainer> mVideoFrameContainer;
|
|
|
|
};
|
|
|
|
|
|
|
|
void VideoFrameContainer::SetCurrentFrames(const VideoSegment& aSegment)
|
|
|
|
{
|
|
|
|
if (aSegment.IsEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
// Collect any new frames produced in this iteration.
|
|
|
|
AutoTArray<ImageContainer::NonOwningImage,4> newImages;
|
|
|
|
PrincipalHandle lastPrincipalHandle = PRINCIPAL_HANDLE_NONE;
|
|
|
|
|
|
|
|
VideoSegment::ConstChunkIterator iter(aSegment);
|
|
|
|
while (!iter.IsEnded()) {
|
|
|
|
VideoChunk chunk = *iter;
|
|
|
|
|
|
|
|
const VideoFrame* frame = &chunk.mFrame;
|
|
|
|
if (*frame == mLastPlayedVideoFrame) {
|
|
|
|
iter.Next();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Image* image = frame->GetImage();
|
|
|
|
CONTAINER_LOG(LogLevel::Verbose,
|
|
|
|
("VideoFrameContainer %p writing video frame %p (%d x %d)",
|
|
|
|
this, image, frame->GetIntrinsicSize().width,
|
|
|
|
frame->GetIntrinsicSize().height));
|
|
|
|
|
|
|
|
if (frame->GetForceBlack()) {
|
|
|
|
if (!mBlackImage) {
|
|
|
|
mBlackImage = GetImageContainer()->CreatePlanarYCbCrImage();
|
|
|
|
if (mBlackImage) {
|
|
|
|
// Sets the image to a single black pixel, which will be scaled to
|
|
|
|
// fill the rendered size.
|
|
|
|
SetImageToBlackPixel(mBlackImage->AsPlanarYCbCrImage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mBlackImage) {
|
|
|
|
image = mBlackImage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Don't append null image to the newImages.
|
|
|
|
if (!image) {
|
|
|
|
iter.Next();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
newImages.AppendElement(ImageContainer::NonOwningImage(image, chunk.mTimeStamp));
|
|
|
|
|
|
|
|
lastPrincipalHandle = chunk.GetPrincipalHandle();
|
|
|
|
|
|
|
|
mLastPlayedVideoFrame = *frame;
|
|
|
|
iter.Next();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't update if there are no changes.
|
|
|
|
if (newImages.IsEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AutoTArray<ImageContainer::NonOwningImage,4> images;
|
|
|
|
|
|
|
|
bool principalHandleChanged =
|
|
|
|
lastPrincipalHandle != PRINCIPAL_HANDLE_NONE &&
|
|
|
|
lastPrincipalHandle != GetLastPrincipalHandle();
|
|
|
|
|
|
|
|
// Add the frames from this iteration.
|
|
|
|
for (auto& image : newImages) {
|
|
|
|
image.mFrameID = NewFrameID();
|
|
|
|
images.AppendElement(image);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (principalHandleChanged) {
|
|
|
|
UpdatePrincipalHandleForFrameID(lastPrincipalHandle,
|
|
|
|
newImages.LastElement().mFrameID);
|
|
|
|
}
|
|
|
|
|
|
|
|
SetCurrentFramesLocked(mLastPlayedVideoFrame.GetIntrinsicSize(), images);
|
|
|
|
nsCOMPtr<nsIRunnable> event =
|
|
|
|
new VideoFrameContainerInvalidateRunnable(this);
|
|
|
|
NS_DispatchToMainThread(event.forget());
|
|
|
|
|
|
|
|
images.ClearAndRetainStorage();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoFrameContainer::ClearFrames()
|
|
|
|
{
|
|
|
|
ClearFutureFrames();
|
|
|
|
}
|
|
|
|
|
2015-09-23 21:49:05 +03:00
|
|
|
void VideoFrameContainer::SetCurrentFrame(const gfx::IntSize& aIntrinsicSize,
|
2012-02-15 08:35:01 +04:00
|
|
|
Image* aImage,
|
2015-03-30 07:51:32 +03:00
|
|
|
const TimeStamp& aTargetTime)
|
2015-03-30 08:11:37 +03:00
|
|
|
{
|
|
|
|
if (aImage) {
|
|
|
|
MutexAutoLock lock(mMutex);
|
2016-02-02 18:36:30 +03:00
|
|
|
AutoTArray<ImageContainer::NonOwningImage,1> imageList;
|
2015-03-30 08:11:37 +03:00
|
|
|
imageList.AppendElement(
|
|
|
|
ImageContainer::NonOwningImage(aImage, aTargetTime, ++mFrameID));
|
|
|
|
SetCurrentFramesLocked(aIntrinsicSize, imageList);
|
|
|
|
} else {
|
|
|
|
ClearCurrentFrame(aIntrinsicSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-23 21:49:05 +03:00
|
|
|
void VideoFrameContainer::SetCurrentFrames(const gfx::IntSize& aIntrinsicSize,
|
2015-03-30 08:11:37 +03:00
|
|
|
const nsTArray<ImageContainer::NonOwningImage>& aImages)
|
2012-02-15 08:35:01 +04:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
2015-03-30 08:11:37 +03:00
|
|
|
SetCurrentFramesLocked(aIntrinsicSize, aImages);
|
|
|
|
}
|
|
|
|
|
2015-09-23 21:49:05 +03:00
|
|
|
void VideoFrameContainer::SetCurrentFramesLocked(const gfx::IntSize& aIntrinsicSize,
|
2015-03-30 08:11:37 +03:00
|
|
|
const nsTArray<ImageContainer::NonOwningImage>& aImages)
|
|
|
|
{
|
|
|
|
mMutex.AssertCurrentThreadOwns();
|
2012-02-15 08:35:01 +04:00
|
|
|
|
|
|
|
if (aIntrinsicSize != mIntrinsicSize) {
|
|
|
|
mIntrinsicSize = aIntrinsicSize;
|
|
|
|
mIntrinsicSizeChanged = true;
|
|
|
|
}
|
|
|
|
|
2013-12-13 21:32:02 +04:00
|
|
|
gfx::IntSize oldFrameSize = mImageContainer->GetCurrentSize();
|
2012-09-27 08:33:43 +04:00
|
|
|
|
|
|
|
// When using the OMX decoder, destruction of the current image can indirectly
|
|
|
|
// block on main thread I/O. If we let this happen while holding onto
|
|
|
|
// |mImageContainer|'s lock, then when the main thread then tries to
|
|
|
|
// composite it can then block on |mImageContainer|'s lock, causing a
|
|
|
|
// deadlock. We use this hack to defer the destruction of the current image
|
|
|
|
// until it is safe.
|
2016-02-04 04:27:09 +03:00
|
|
|
nsTArray<ImageContainer::OwningImage> oldImages;
|
|
|
|
mImageContainer->GetCurrentImages(&oldImages);
|
|
|
|
|
|
|
|
ImageContainer::FrameID lastFrameIDForOldPrincipalHandle =
|
|
|
|
mFrameIDForPendingPrincipalHandle - 1;
|
|
|
|
if (mPendingPrincipalHandle != PRINCIPAL_HANDLE_NONE &&
|
|
|
|
((!oldImages.IsEmpty() &&
|
|
|
|
oldImages.LastElement().mFrameID >= lastFrameIDForOldPrincipalHandle) ||
|
|
|
|
(!aImages.IsEmpty() &&
|
|
|
|
aImages[0].mFrameID > lastFrameIDForOldPrincipalHandle))) {
|
|
|
|
// We are releasing the last FrameID prior to `lastFrameIDForOldPrincipalHandle`
|
|
|
|
// OR
|
|
|
|
// there are no FrameIDs prior to `lastFrameIDForOldPrincipalHandle` in the new
|
|
|
|
// set of images.
|
|
|
|
// This means that the old principal handle has been flushed out and we can
|
|
|
|
// notify our video element about this change.
|
|
|
|
RefPtr<VideoFrameContainer> self = this;
|
|
|
|
PrincipalHandle principalHandle = mPendingPrincipalHandle;
|
|
|
|
mLastPrincipalHandle = mPendingPrincipalHandle;
|
|
|
|
mPendingPrincipalHandle = PRINCIPAL_HANDLE_NONE;
|
|
|
|
mFrameIDForPendingPrincipalHandle = 0;
|
|
|
|
NS_DispatchToMainThread(NS_NewRunnableFunction([self, principalHandle]() {
|
|
|
|
if (self->mElement) {
|
|
|
|
self->mElement->PrincipalHandleChangedForVideoFrameContainer(self, principalHandle);
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
}
|
2012-09-27 08:33:43 +04:00
|
|
|
|
2015-03-30 08:11:37 +03:00
|
|
|
if (aImages.IsEmpty()) {
|
2015-07-07 00:58:18 +03:00
|
|
|
mImageContainer->ClearAllImages();
|
2015-03-30 08:11:37 +03:00
|
|
|
} else {
|
|
|
|
mImageContainer->SetCurrentImages(aImages);
|
2015-07-07 00:58:18 +03:00
|
|
|
}
|
2013-12-13 21:32:02 +04:00
|
|
|
gfx::IntSize newFrameSize = mImageContainer->GetCurrentSize();
|
2012-02-15 08:35:01 +04:00
|
|
|
if (oldFrameSize != newFrameSize) {
|
|
|
|
mImageSizeChanged = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-23 13:45:35 +03:00
|
|
|
void VideoFrameContainer::ClearCurrentFrame()
|
2012-09-27 08:33:43 +04:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
// See comment in SetCurrentFrame for the reasoning behind
|
|
|
|
// using a kungFuDeathGrip here.
|
2015-03-27 02:07:53 +03:00
|
|
|
nsTArray<ImageContainer::OwningImage> kungFuDeathGrip;
|
|
|
|
mImageContainer->GetCurrentImages(&kungFuDeathGrip);
|
2012-09-27 08:33:43 +04:00
|
|
|
|
2013-10-17 19:09:15 +04:00
|
|
|
mImageContainer->ClearAllImages();
|
2016-06-28 04:25:01 +03:00
|
|
|
mImageContainer->ClearCachedResources();
|
2015-08-31 14:33:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void VideoFrameContainer::ClearFutureFrames()
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
// See comment in SetCurrentFrame for the reasoning behind
|
|
|
|
// using a kungFuDeathGrip here.
|
|
|
|
nsTArray<ImageContainer::OwningImage> kungFuDeathGrip;
|
|
|
|
mImageContainer->GetCurrentImages(&kungFuDeathGrip);
|
|
|
|
|
|
|
|
if (!kungFuDeathGrip.IsEmpty()) {
|
|
|
|
nsTArray<ImageContainer::NonOwningImage> currentFrame;
|
|
|
|
const ImageContainer::OwningImage& img = kungFuDeathGrip[0];
|
|
|
|
currentFrame.AppendElement(ImageContainer::NonOwningImage(img.mImage,
|
|
|
|
img.mTimeStamp, img.mFrameID, img.mProducerID));
|
|
|
|
mImageContainer->SetCurrentImages(currentFrame);
|
|
|
|
}
|
2012-09-27 08:33:43 +04:00
|
|
|
}
|
|
|
|
|
2012-08-21 08:06:46 +04:00
|
|
|
ImageContainer* VideoFrameContainer::GetImageContainer() {
|
|
|
|
return mImageContainer;
|
|
|
|
}
|
|
|
|
|
Backout b3a8618f901c (bug 829042), 34a9ef8f929d (bug 822933), 4c1215cefbab (bug 826349), 70bb7f775178 (bug 825325), e9c8447fb197 (bug 828713), eb6ebf01eafe (bug 828901), f1f3ef647920 (bug 825329), f9d7b5722d4f (bug 825329), 5add564d4546 (bug 819377), 55e93d1fa972 (bug 804875), f14639a3461e (bug 804875), 23456fc21052 (bug 814308) for Windows pgo-only mochitest-1 media test timeouts on a CLOSED TREE
2013-01-16 19:16:23 +04:00
|
|
|
|
2012-02-15 08:35:01 +04:00
|
|
|
double VideoFrameContainer::GetFrameDelay()
|
|
|
|
{
|
2015-07-03 10:39:09 +03:00
|
|
|
return mImageContainer->GetPaintDelay().ToSeconds();
|
2012-02-15 08:35:01 +04:00
|
|
|
}
|
|
|
|
|
2013-10-02 07:05:34 +04:00
|
|
|
void VideoFrameContainer::InvalidateWithFlags(uint32_t aFlags)
|
2012-02-15 08:35:01 +04:00
|
|
|
{
|
|
|
|
NS_ASSERTION(NS_IsMainThread(), "Must call on main thread");
|
2012-08-10 19:42:53 +04:00
|
|
|
|
2012-02-15 08:35:01 +04:00
|
|
|
if (!mElement) {
|
|
|
|
// Element has been destroyed
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame* frame = mElement->GetPrimaryFrame();
|
|
|
|
bool invalidateFrame = false;
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
// Get mImageContainerSizeChanged while holding the lock.
|
|
|
|
invalidateFrame = mImageSizeChanged;
|
|
|
|
mImageSizeChanged = false;
|
|
|
|
|
|
|
|
if (mIntrinsicSizeChanged) {
|
|
|
|
mElement->UpdateMediaSize(mIntrinsicSize);
|
|
|
|
mIntrinsicSizeChanged = false;
|
|
|
|
|
|
|
|
if (frame) {
|
|
|
|
nsPresContext* presContext = frame->PresContext();
|
|
|
|
nsIPresShell *presShell = presContext->PresShell();
|
|
|
|
presShell->FrameNeedsReflow(frame,
|
|
|
|
nsIPresShell::eStyleChange,
|
|
|
|
NS_FRAME_IS_DIRTY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-02 07:05:49 +04:00
|
|
|
bool asyncInvalidate = mImageContainer &&
|
|
|
|
mImageContainer->IsAsync() &&
|
|
|
|
!(aFlags & INVALIDATE_FORCE);
|
|
|
|
|
2012-02-15 08:35:01 +04:00
|
|
|
if (frame) {
|
|
|
|
if (invalidateFrame) {
|
2012-08-29 09:39:31 +04:00
|
|
|
frame->InvalidateFrame();
|
2012-02-15 08:35:01 +04:00
|
|
|
} else {
|
2015-02-11 12:11:41 +03:00
|
|
|
frame->InvalidateLayer(nsDisplayItem::TYPE_VIDEO, nullptr, nullptr,
|
2013-10-02 07:05:49 +04:00
|
|
|
asyncInvalidate ? nsIFrame::UPDATE_IS_ASYNC : 0);
|
2012-02-15 08:35:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsSVGEffects::InvalidateDirectRenderingObservers(mElement);
|
|
|
|
}
|
|
|
|
|
2015-07-13 18:25:42 +03:00
|
|
|
} // namespace mozilla
|