Bug 1351954 - Change ClearKey CDM to allocate its video frame buffers optimally. r=gerald
The WMF decoder gives us video frames in buffers with the planes 16 row-aligned
for some reason. If we allocate our video frames the same size we waste memory.
So let's have the ClearKey CDM not allocate its video frames with the extra
padding rows.
Excluding the padding in our copy of the decoded data also makes my work in
bug
1351953 easier.
MozReview-Commit-ID: 9dD40P6ST68
--- a/media/gmp-clearkey/0.1/VideoDecoder.cpp
+++ b/media/gmp-clearkey/0.1/VideoDecoder.cpp
@@ -17,16 +17,17 @@
#include <algorithm>
#include <cstdint>
#include <limits>
#include "BigEndian.h"
#include "ClearKeyDecryptionManager.h"
#include "ClearKeyUtils.h"
#include "VideoDecoder.h"
+#include "mozilla/CheckedInt.h"
using namespace wmf;
using namespace cdm;
VideoDecoder::VideoDecoder(Host_8 *aHost)
: mHost(aHost)
, mHasShutdown(false)
{
@@ -167,35 +168,38 @@ Status VideoDecoder::OutputFrame(VideoFr
// The Chromium CDM API doesn't have support for negative strides, though
// they are theoretically possible in real world data.
if (mDecoder->GetStride() <= 0) {
CK_LOGD("VideoDecoder::OutputFrame Failed! (negative stride)");
return Status::kDecodeError;
}
+ const IntRect& picture = mDecoder->GetPictureRegion();
hr = SampleToVideoFrame(result,
- mDecoder->GetFrameWidth(),
+ picture.width,
+ picture.height,
+ mDecoder->GetStride(),
mDecoder->GetFrameHeight(),
- mDecoder->GetStride(),
aVideoFrame);
if (FAILED(hr)) {
CK_LOGD("VideoDecoder::OutputFrame Failed!");
return Status::kDecodeError;
}
CK_LOGD("VideoDecoder::OutputFrame Succeeded.");
return Status::kSuccess;
}
HRESULT
VideoDecoder::SampleToVideoFrame(IMFSample* aSample,
- int32_t aWidth,
- int32_t aHeight,
+ int32_t aPictureWidth,
+ int32_t aPictureHeight,
int32_t aStride,
+ int32_t aFrameHeight,
VideoFrame* aVideoFrame)
{
CK_LOGD("[%p] VideoDecoder::SampleToVideoFrame()", this);
ENSURE(aSample != nullptr, E_POINTER);
ENSURE(aVideoFrame != nullptr, E_POINTER);
HRESULT hr;
@@ -218,67 +222,81 @@ VideoDecoder::SampleToVideoFrame(IMFSamp
hr = twoDBuffer->Lock2D(&data, &stride);
ENSURE(SUCCEEDED(hr), hr);
} else {
hr = mediaBuffer->Lock(&data, nullptr, nullptr);
ENSURE(SUCCEEDED(hr), hr);
stride = aStride;
}
- // The U and V planes are stored 16-row-aligned, so we need to add padding
- // to the row heights to ensure the Y'CbCr planes are referenced properly.
+ // WMF stores the U and V planes 16-row-aligned, so we need to add padding
+ // to the row heights to ensure the source offsets of the Y'CbCr planes are
+ // referenced properly.
// YV12, planar format: [YYYY....][UUUU....][VVVV....]
// i.e., Y, then U, then V.
uint32_t padding = 0;
- if (aHeight % 16 != 0) {
- padding = 16 - (aHeight % 16);
+ if (aFrameHeight % 16 != 0) {
+ padding = 16 - (aFrameHeight % 16);
}
- uint32_t ySize = stride * (aHeight + padding);
- uint32_t uSize = stride * (aHeight + padding) / 4;
+ uint32_t srcYSize = stride * (aFrameHeight + padding);
+ uint32_t srcUVSize = stride * (aFrameHeight + padding) / 4;
uint32_t halfStride = (stride + 1) / 2;
- uint32_t halfHeight = (aHeight + 1) / 2;
aVideoFrame->SetStride(VideoFrame::kYPlane, stride);
aVideoFrame->SetStride(VideoFrame::kUPlane, halfStride);
aVideoFrame->SetStride(VideoFrame::kVPlane, halfStride);
- aVideoFrame->SetSize(Size(aWidth, aHeight));
+ aVideoFrame->SetSize(Size(aPictureWidth, aPictureHeight));
- uint64_t bufferSize = ySize + 2 * uSize;
+ // Note: We allocate the minimal sized buffer required to send the
+ // frame back over to the parent process. This is so that we request the
+ // same sized frame as the buffer allocator expects.
+ using mozilla::CheckedUint32;
+ CheckedUint32 bufferSize = CheckedUint32(stride) * aPictureHeight +
+ ((CheckedUint32(stride) * aPictureHeight) / 4) * 2;
// If the buffer is bigger than the max for a 32 bit, fail to avoid buffer
// overflows.
- if (bufferSize > UINT32_MAX) {
+ if (!bufferSize.isValid()) {
CK_LOGD("VideoDecoder::SampleToFrame Buffersize bigger than UINT32_MAX");
return E_FAIL;
}
// Get the buffer from the host.
- Buffer* buffer = mHost->Allocate(bufferSize);
+ Buffer* buffer = mHost->Allocate(bufferSize.value());
aVideoFrame->SetFrameBuffer(buffer);
// Make sure the buffer is non-null (allocate guarantees it will be of
// sufficient size).
if (!buffer) {
CK_LOGD("VideoDecoder::SampleToFrame Out of memory");
return E_OUTOFMEMORY;
}
uint8_t* outBuffer = buffer->Data();
aVideoFrame->SetPlaneOffset(VideoFrame::kYPlane, 0);
- // Offset is the size of the copied y data.
- aVideoFrame->SetPlaneOffset(VideoFrame::kUPlane, ySize);
+ // Offset of U plane is the size of the Y plane, excluding the padding that
+ // WMF adds.
+ uint32_t dstUOffset = stride * aPictureHeight;
+ aVideoFrame->SetPlaneOffset(VideoFrame::kUPlane, dstUOffset);
- // Offset is the size of the copied y data + the size of the copied u data.
- aVideoFrame->SetPlaneOffset(VideoFrame::kVPlane, ySize + uSize);
+ // Offset of the V plane is the size of the Y plane + the size of the U plane,
+ // excluding any padding WMF adds.
+ uint32_t dstVOffset = stride * aPictureHeight + (stride * aPictureHeight) / 4;
+ aVideoFrame->SetPlaneOffset(VideoFrame::kVPlane, dstVOffset);
- // Copy the data.
- memcpy(outBuffer, data, ySize + uSize * 2);
+ // Copy the pixel data, excluding WMF's padding.
+ memcpy(outBuffer, data, stride * aPictureHeight);
+ memcpy(
+ outBuffer + dstUOffset, data + srcYSize, (stride * aPictureHeight) / 4);
+ memcpy(outBuffer + dstVOffset,
+ data + srcYSize + srcUVSize,
+ (stride * aPictureHeight) / 4);
if (twoDBuffer) {
twoDBuffer->Unlock2D();
} else {
mediaBuffer->Unlock();
}
LONGLONG hns = 0;
--- a/media/gmp-clearkey/0.1/VideoDecoder.h
+++ b/media/gmp-clearkey/0.1/VideoDecoder.h
@@ -53,19 +53,20 @@ private:
std::vector<uint8_t> mBuffer;
uint64_t mTimestamp = 0;
CryptoMetaData mCrypto;
};
cdm::Status OutputFrame(cdm::VideoFrame* aVideoFrame);
HRESULT SampleToVideoFrame(IMFSample* aSample,
- int32_t aWidth,
- int32_t aHeight,
+ int32_t aPictureWidth,
+ int32_t aPictureHeight,
int32_t aStride,
+ int32_t aFrameHeight,
cdm::VideoFrame* aVideoFrame);
cdm::Host_8* mHost;
wmf::AutoPtr<wmf::WMFH264Decoder> mDecoder;
std::queue<wmf::CComPtr<IMFSample>> mOutputQueue;
bool mHasShutdown;
--- a/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
@@ -100,22 +100,16 @@ WMFH264Decoder::ConfigureVideoFrameGeome
width, height,
mStride,
mPictureRegion.x, mPictureRegion.y, mPictureRegion.width, mPictureRegion.height);
return S_OK;
}
int32_t
-WMFH264Decoder::GetFrameWidth() const
-{
- return mVideoWidth;
-}
-
-int32_t
WMFH264Decoder::GetFrameHeight() const
{
return mVideoHeight;
}
const IntRect&
WMFH264Decoder::GetPictureRegion() const
{
--- a/media/gmp-clearkey/0.1/WMFH264Decoder.h
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.h
@@ -31,17 +31,16 @@ public:
HRESULT Input(const uint8_t* aData,
uint32_t aDataSize,
Microseconds aTimestamp);
HRESULT Output(IMFSample** aOutput);
HRESULT Reset();
- int32_t GetFrameWidth() const;
int32_t GetFrameHeight() const;
const IntRect& GetPictureRegion() const;
int32_t GetStride() const;
HRESULT Drain();
private: