Bug 1351954 - Change ClearKey CDM to allocate its video frame buffers optimally. r=gerald draft
authorChris Pearce <cpearce@mozilla.com>
Tue, 28 Mar 2017 18:00:01 +1300
changeset 554895 a6c4fea01e8bf2deef8edc78d0a041e8fed0c0b8
parent 554894 f003e5994271ce4b75684eeb7d09b5e4eca00582
child 622465 af72eb894571b1797b5f0ced1efa7d41afe81951
push id52082
push userbmo:cpearce@mozilla.com
push dateMon, 03 Apr 2017 07:16:02 +0000
reviewersgerald
bugs1351954, 1351953
milestone55.0a1
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
media/gmp-clearkey/0.1/VideoDecoder.cpp
media/gmp-clearkey/0.1/VideoDecoder.h
media/gmp-clearkey/0.1/WMFH264Decoder.cpp
media/gmp-clearkey/0.1/WMFH264Decoder.h
--- 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: