From c48055d6ea9a63ee8a19a38c3e8759e3630b00fe Mon Sep 17 00:00:00 2001 From: Jeremy Andrews Date: Mon, 23 Oct 2023 22:20:36 -0500 Subject: Issue #2357 - VPXDecoder does not decode alpha frames. Another requirement to have transparency in videos. Straight port of Firefox 53 implementation, save for some oddities relating to needing to put uint8_t instead of uint8 in yuv_convert to get this to compile. Ref: BZ 1321076, 1329104 --- dom/media/MediaData.cpp | 139 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 109 insertions(+), 30 deletions(-) (limited to 'dom/media/MediaData.cpp') diff --git a/dom/media/MediaData.cpp b/dom/media/MediaData.cpp index c113b62fe6..9f1205a0e1 100644 --- a/dom/media/MediaData.cpp +++ b/dom/media/MediaData.cpp @@ -7,6 +7,8 @@ #include "MediaInfo.h" #include "VideoUtils.h" #include "ImageContainer.h" +#include "mozilla/layers/SharedRGBImage.h" +#include "YCbCrUtils.h" #include @@ -89,6 +91,45 @@ ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane) aPlane.mStride > 0 && aPlane.mWidth <= aPlane.mStride; } +static bool ValidateBufferAndPicture(const VideoData::YCbCrBuffer& aBuffer, + const IntRect& aPicture) +{ + // The following situation should never happen unless there is a bug + // in the decoder + if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth || + aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) { + NS_ERROR("C planes with different sizes"); + return false; + } + + // The following situations could be triggered by invalid input + if (aPicture.width <= 0 || aPicture.height <= 0) { + // In debug mode, makes the error more noticeable + MOZ_ASSERT(false, "Empty picture rect"); + return false; + } + if (!ValidatePlane(aBuffer.mPlanes[0]) || + !ValidatePlane(aBuffer.mPlanes[1]) || + !ValidatePlane(aBuffer.mPlanes[2])) { + NS_WARNING("Invalid plane size"); + return false; + } + + // Ensure the picture size specified in the headers can be extracted out of + // the frame we've been supplied without indexing out of bounds. + CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width); + CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height); + if (!xLimit.isValid() || xLimit.value() > aBuffer.mPlanes[0].mStride || + !yLimit.isValid() || yLimit.value() > aBuffer.mPlanes[0].mHeight) + { + // The specified picture dimensions can't be contained inside the video + // frame, we'll stomp memory if we try to copy it. Fail. + NS_WARNING("Overflowing picture rect"); + return false; + } + return true; +} + VideoData::VideoData(int64_t aOffset, int64_t aTime, int64_t aDuration, @@ -242,36 +283,7 @@ VideoData::CreateAndCopyData(const VideoInfo& aInfo, return v.forget(); } - // The following situation should never happen unless there is a bug - // in the decoder - if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth || - aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) { - NS_ERROR("C planes with different sizes"); - return nullptr; - } - - // The following situations could be triggered by invalid input - if (aPicture.width <= 0 || aPicture.height <= 0) { - // In debug mode, makes the error more noticeable - MOZ_ASSERT(false, "Empty picture rect"); - return nullptr; - } - if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) || - !ValidatePlane(aBuffer.mPlanes[2])) { - NS_WARNING("Invalid plane size"); - return nullptr; - } - - // Ensure the picture size specified in the headers can be extracted out of - // the frame we've been supplied without indexing out of bounds. - CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width); - CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height); - if (!xLimit.isValid() || xLimit.value() > aBuffer.mPlanes[0].mStride || - !yLimit.isValid() || yLimit.value() > aBuffer.mPlanes[0].mHeight) - { - // The specified picture dimensions can't be contained inside the video - // frame, we'll stomp memory if we try to copy it. Fail. - NS_WARNING("Overflowing picture rect"); + if (!ValidateBufferAndPicture(aBuffer, aPicture)) { return nullptr; } @@ -305,6 +317,73 @@ VideoData::CreateAndCopyData(const VideoInfo& aInfo, return v.forget(); } +/* static */ +already_AddRefed +VideoData::CreateAndCopyData(const VideoInfo& aInfo, + ImageContainer* aContainer, + int64_t aOffset, + int64_t aTime, + int64_t aDuration, + const YCbCrBuffer& aBuffer, + const YCbCrBuffer::Plane &aAlphaPlane, + bool aKeyframe, + int64_t aTimecode, + const IntRect& aPicture) +{ + if (!aContainer) { + // Create a dummy VideoData with no image. This gives us something to + // send to media streams if necessary. + RefPtr v(new VideoData(aOffset, + aTime, + aDuration, + aKeyframe, + aTimecode, + aInfo.mDisplay, + 0)); + return v.forget(); + } + + if (!ValidateBufferAndPicture(aBuffer, aPicture)) { + return nullptr; + } + + RefPtr v(new VideoData(aOffset, + aTime, + aDuration, + aKeyframe, + aTimecode, + aInfo.mDisplay, + 0)); + + // Convert from YUVA to BGRA format on the software side. + RefPtr videoImage = + aContainer->CreateSharedRGBImage(); + v->mImage = videoImage; + + if (!v->mImage) { + return nullptr; + } + if (!videoImage->Allocate(IntSize(aBuffer.mPlanes[0].mWidth, + aBuffer.mPlanes[0].mHeight), + SurfaceFormat::B8G8R8A8)) { + return nullptr; + } + uint8_t* argb_buffer = videoImage->GetBuffer(); + IntSize size = videoImage->GetSize(); + + // The naming convention for libyuv and associated utils is word-order. + // The naming convention in the gfx stack is byte-order. + ConvertYCbCrAToARGB(aBuffer.mPlanes[0].mData, + aBuffer.mPlanes[1].mData, + aBuffer.mPlanes[2].mData, + aAlphaPlane.mData, + aBuffer.mPlanes[0].mStride, aBuffer.mPlanes[1].mStride, + argb_buffer, size.width * 4, + size.width, size.height); + + return v.forget(); +} + /* static */ already_AddRefed VideoData::CreateFromImage(const VideoInfo& aInfo, -- cgit v1.2.3