/* -*- 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 "mozilla/dom/VideoFrame.h" #include "mozilla/dom/VideoFrameBinding.h" #include #include #include #include "ImageContainer.h" #include "VideoColorSpace.h" #include "WebCodecsUtils.h" #include "js/StructuredClone.h" #include "mozilla/Maybe.h" #include "mozilla/ResultVariant.h" #include "mozilla/ScopeExit.h" #include "mozilla/Try.h" #include "mozilla/UniquePtr.h" #include "mozilla/dom/CanvasUtils.h" #include "mozilla/dom/DOMRect.h" #include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/HTMLVideoElement.h" #include "mozilla/dom/ImageBitmap.h" #include "mozilla/dom/ImageUtils.h" #include "mozilla/dom/OffscreenCanvas.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/SVGImageElement.h" #include "mozilla/dom/StructuredCloneHolder.h" #include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/dom/UnionTypes.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Swizzle.h" #include "mozilla/layers/LayersSurfaces.h" #include "nsLayoutUtils.h" #include "nsIPrincipal.h" #include "nsIURI.h" extern mozilla::LazyLogModule gWebCodecsLog; namespace mozilla::dom { #ifdef LOG_INTERNAL # undef LOG_INTERNAL #endif // LOG_INTERNAL #define LOG_INTERNAL(level, msg, ...) \ MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__)) #ifdef LOG # undef LOG #endif // LOG #define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__) #ifdef LOGW # undef LOGW #endif // LOGW #define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__) NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(VideoFrame) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VideoFrame) tmp->CloseIfNeeded(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VideoFrame) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTING_ADDREF(VideoFrame) // VideoFrame should be released as soon as its refcount drops to zero, // without waiting for async deletion by the cycle collector, since it may hold // a large-size image. NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(VideoFrame, CloseIfNeeded()) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(VideoFrame) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END /* * The following are helpers to read the image data from the given buffer and * the format. The data layout is illustrated in the comments for * `VideoFrame::Format` below. */ static int32_t CeilingOfHalf(int32_t aValue) { MOZ_ASSERT(aValue >= 0); return aValue / 2 + (aValue % 2); } class YUVBufferReaderBase { public: YUVBufferReaderBase(const Span& aBuffer, int32_t aWidth, int32_t aHeight) : mWidth(aWidth), mHeight(aHeight), mStrideY(aWidth), mBuffer(aBuffer) {} virtual ~YUVBufferReaderBase() = default; const uint8_t* DataY() const { return mBuffer.data(); } const int32_t mWidth; const int32_t mHeight; const int32_t mStrideY; protected: CheckedInt YByteSize() const { return CheckedInt(mStrideY) * mHeight; } const Span mBuffer; }; class I420ABufferReader; class I420BufferReader : public YUVBufferReaderBase { public: I420BufferReader(const Span& aBuffer, int32_t aWidth, int32_t aHeight) : YUVBufferReaderBase(aBuffer, aWidth, aHeight), mStrideU(CeilingOfHalf(aWidth)), mStrideV(CeilingOfHalf(aWidth)) {} virtual ~I420BufferReader() = default; const uint8_t* DataU() const { return &mBuffer[YByteSize().value()]; } const uint8_t* DataV() const { return &mBuffer[YByteSize().value() + UByteSize().value()]; } virtual I420ABufferReader* AsI420ABufferReader() { return nullptr; } const int32_t mStrideU; const int32_t mStrideV; protected: CheckedInt UByteSize() const { return CheckedInt(CeilingOfHalf(mHeight)) * mStrideU; } CheckedInt VSize() const { return CheckedInt(CeilingOfHalf(mHeight)) * mStrideV; } }; class I420ABufferReader final : public I420BufferReader { public: I420ABufferReader(const Span& aBuffer, int32_t aWidth, int32_t aHeight) : I420BufferReader(aBuffer, aWidth, aHeight), mStrideA(aWidth) { MOZ_ASSERT(mStrideA == mStrideY); } virtual ~I420ABufferReader() = default; const uint8_t* DataA() const { return &mBuffer[YByteSize().value() + UByteSize().value() + VSize().value()]; } virtual I420ABufferReader* AsI420ABufferReader() override { return this; } const int32_t mStrideA; }; class NV12BufferReader final : public YUVBufferReaderBase { public: NV12BufferReader(const Span& aBuffer, int32_t aWidth, int32_t aHeight) : YUVBufferReaderBase(aBuffer, aWidth, aHeight), mStrideUV(aWidth + aWidth % 2) {} virtual ~NV12BufferReader() = default; const uint8_t* DataUV() const { return &mBuffer[YByteSize().value()]; } const int32_t mStrideUV; }; /* * The followings are helpers defined in * https://w3c.github.io/webcodecs/#videoframe-algorithms */ static bool IsSameOrigin(nsIGlobalObject* aGlobal, const VideoFrame& aFrame) { MOZ_ASSERT(aGlobal); MOZ_ASSERT(aFrame.GetParentObject()); nsIPrincipal* principalX = aGlobal->PrincipalOrNull(); nsIPrincipal* principalY = aFrame.GetParentObject()->PrincipalOrNull(); // If both of VideoFrames are created in worker, they are in the same origin // domain. if (!principalX) { return !principalY; } // Otherwise, check their domains. return principalX->Equals(principalY); } static bool IsSameOrigin(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoElement) { MOZ_ASSERT(aGlobal); // If CORS is in use, consider the video source is same-origin. if (aVideoElement.GetCORSMode() != CORS_NONE) { return true; } // Otherwise, check if video source has cross-origin redirect or not. if (aVideoElement.HadCrossOriginRedirects()) { return false; } // Finally, compare the VideoFrame's domain and video's one. nsIPrincipal* principal = aGlobal->PrincipalOrNull(); nsCOMPtr elementPrincipal = aVideoElement.GetCurrentVideoPrincipal(); //