Bug 1315850 - Implement CDM video decoder drain. r=jya draft
authorChris Pearce <cpearce@mozilla.com>
Tue, 07 Mar 2017 16:37:21 +1300
changeset 504175 3f9636503523f0c6effab15fa89cce25a961a0b4
parent 504174 8ee1ae28a36779484717c6b105ef7730dd1896b3
child 504176 5a2f77ffe84f9b99b4668520c838b29a428578d3
push id50748
push userbmo:cpearce@mozilla.com
push dateFri, 24 Mar 2017 01:10:17 +0000
reviewersjya
bugs1315850
milestone55.0a1
Bug 1315850 - Implement CDM video decoder drain. r=jya MozReview-Commit-ID: 5RbrWyLglRf
dom/media/gmp/ChromiumCDMChild.cpp
dom/media/gmp/ChromiumCDMChild.h
dom/media/gmp/ChromiumCDMParent.cpp
dom/media/gmp/ChromiumCDMParent.h
dom/media/gmp/PChromiumCDM.ipdl
dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.cpp
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -483,48 +483,69 @@ ChromiumCDMChild::RecvDecryptAndDecodeFr
 
   WidevineVideoFrame frame;
   cdm::Status rv = mCDM->DecryptAndDecodeFrame(input, &frame);
   GMP_LOG("WidevineVideoDecoder::Decode(timestamp=%" PRId64 ") rv=%d",
           input.timestamp,
           rv);
 
   if (rv == cdm::kSuccess) {
-    // TODO: WidevineBuffers should hold a shmem instead of a array, and we can
-    // send the handle instead of copying the array here.
-
-    gmp::CDMVideoFrame output;
-    output.mFormat() = static_cast<cdm::VideoFormat>(frame.Format());
-    output.mImageWidth() = frame.Size().width;
-    output.mImageHeight() = frame.Size().height;
-    output.mData() = Move(
-      reinterpret_cast<WidevineBuffer*>(frame.FrameBuffer())->ExtractBuffer());
-    output.mYPlane() = { frame.PlaneOffset(cdm::VideoFrame::kYPlane),
-                         frame.Stride(cdm::VideoFrame::kYPlane) };
-    output.mUPlane() = { frame.PlaneOffset(cdm::VideoFrame::kUPlane),
-                         frame.Stride(cdm::VideoFrame::kUPlane) };
-    output.mVPlane() = { frame.PlaneOffset(cdm::VideoFrame::kVPlane),
-                         frame.Stride(cdm::VideoFrame::kVPlane) };
-    output.mTimestamp() = frame.Timestamp();
-
-    uint64_t duration = 0;
-    if (mFrameDurations.Find(frame.Timestamp(), duration)) {
-      output.mDuration() = duration;
-    }
-
-    Unused << SendDecoded(output);
+    ReturnOutput(frame);
   } else if (rv == cdm::kNeedMoreData) {
     Unused << SendDecoded(gmp::CDMVideoFrame());
   } else {
     Unused << SendDecodeFailed(rv);
   }
 
   return IPC_OK();
 }
 
+void
+ChromiumCDMChild::ReturnOutput(WidevineVideoFrame& aFrame)
+{
+  // TODO: WidevineBuffers should hold a shmem instead of a array, and we can
+  // send the handle instead of copying the array here.
+  gmp::CDMVideoFrame output;
+  output.mFormat() = static_cast<cdm::VideoFormat>(aFrame.Format());
+  output.mImageWidth() = aFrame.Size().width;
+  output.mImageHeight() = aFrame.Size().height;
+  output.mData() = Move(
+    reinterpret_cast<WidevineBuffer*>(aFrame.FrameBuffer())->ExtractBuffer());
+  output.mYPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kYPlane),
+                       aFrame.Stride(cdm::VideoFrame::kYPlane) };
+  output.mUPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kUPlane),
+                       aFrame.Stride(cdm::VideoFrame::kUPlane) };
+  output.mVPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kVPlane),
+                       aFrame.Stride(cdm::VideoFrame::kVPlane) };
+  output.mTimestamp() = aFrame.Timestamp();
+
+  uint64_t duration = 0;
+  if (mFrameDurations.Find(aFrame.Timestamp(), duration)) {
+    output.mDuration() = duration;
+  }
+
+  Unused << SendDecoded(output);
+}
+
+mozilla::ipc::IPCResult
+ChromiumCDMChild::RecvDrain()
+{
+  WidevineVideoFrame frame;
+  cdm::InputBuffer sample;
+  cdm::Status rv = mCDM->DecryptAndDecodeFrame(sample, &frame);
+  CDM_LOG("ChromiumCDMChild::RecvDrain();  DecryptAndDecodeFrame() rv=%d", rv);
+  if (rv == cdm::kSuccess) {
+    MOZ_ASSERT(frame.Format() != cdm::kUnknownVideoFormat);
+    ReturnOutput(frame);
+  } else {
+    Unused << SendDrainComplete();
+  }
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvDestroy()
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvDestroy()");
 
   MOZ_ASSERT(!mDecoderInitialized);
 
--- a/dom/media/gmp/ChromiumCDMChild.h
+++ b/dom/media/gmp/ChromiumCDMChild.h
@@ -1,19 +1,20 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 #ifndef ChromiumCDMChild_h_
 #define ChromiumCDMChild_h_
 
+#include "content_decryption_module.h"
 #include "mozilla/gmp/PChromiumCDMChild.h"
-#include "content_decryption_module.h"
 #include "SimpleMap.h"
+#include "WidevineVideoFrame.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentChild;
 
 class ChromiumCDMChild : public PChromiumCDMChild
                        , public cdm::Host_8
@@ -97,19 +98,21 @@ protected:
   ipc::IPCResult RecvDecrypt(const uint32_t& aId,
                              const CDMInputBuffer& aBuffer) override;
   ipc::IPCResult RecvInitializeVideoDecoder(
     const CDMVideoDecoderConfig& aConfig) override;
   ipc::IPCResult RecvDeinitializeVideoDecoder() override;
   ipc::IPCResult RecvResetVideoDecoder() override;
   ipc::IPCResult RecvDecryptAndDecodeFrame(
     const CDMInputBuffer& aBuffer) override;
+  ipc::IPCResult RecvDrain() override;
   ipc::IPCResult RecvDestroy() override;
 
   void DecryptFailed(uint32_t aId, cdm::Status aStatus);
+  void ReturnOutput(WidevineVideoFrame& aFrame);
 
   GMPContentChild* mPlugin = nullptr;
   cdm::ContentDecryptionModule_8* mCDM = nullptr;
 
   typedef SimpleMap<uint64_t> DurationMap;
   DurationMap mFrameDurations;
 
   bool mDecoderInitialized = false;
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -603,10 +603,28 @@ ChromiumCDMParent::FlushVideoDecoder()
 
 ipc::IPCResult
 ChromiumCDMParent::RecvResetVideoDecoderComplete()
 {
   mFlushDecoderPromise.Resolve(true, __func__);
   return IPC_OK();
 }
 
+RefPtr<MediaDataDecoder::DecodePromise>
+ChromiumCDMParent::Drain()
+{
+  MOZ_ASSERT(mDecodePromise.IsEmpty(), "Must wait for decoding to complete");
+
+  RefPtr<MediaDataDecoder::DecodePromise> p = mDecodePromise.Ensure(__func__);
+  if (!SendDrain()) {
+    mDecodePromise.Resolve(MediaDataDecoder::DecodedData(), __func__);
+  }
+  return p;
+}
+
+ipc::IPCResult
+ChromiumCDMParent::RecvDrainComplete()
+{
+  mDecodePromise.ResolveIfExists(MediaDataDecoder::DecodedData(), __func__);
+  return IPC_OK();
+}
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -66,16 +66,18 @@ public:
     const VideoInfo& aInfo,
     RefPtr<layers::ImageContainer> aImageContainer);
 
   RefPtr<MediaDataDecoder::DecodePromise> DecryptAndDecodeFrame(
     MediaRawData* aSample);
 
   RefPtr<MediaDataDecoder::FlushPromise> FlushVideoDecoder();
 
+  RefPtr<MediaDataDecoder::DecodePromise> Drain();
+
 protected:
   ~ChromiumCDMParent() {}
 
   ipc::IPCResult Recv__delete__() override;
   ipc::IPCResult RecvOnResolveNewSessionPromise(
     const uint32_t& aPromiseId,
     const nsCString& aSessionId) override;
   ipc::IPCResult RecvOnResolvePromise(const uint32_t& aPromiseId) override;
@@ -100,16 +102,17 @@ protected:
   ipc::IPCResult RecvDecrypted(const uint32_t& aId,
                                const uint32_t& aStatus,
                                nsTArray<uint8_t>&& aData) override;
   ipc::IPCResult RecvOnDecoderInitDone(const uint32_t& aStatus) override;
   ipc::IPCResult RecvDecoded(const CDMVideoFrame& aFrame) override;
   ipc::IPCResult RecvDecodeFailed(const uint32_t& aStatus) override;
   ipc::IPCResult RecvShutdown() override;
   ipc::IPCResult RecvResetVideoDecoderComplete() override;
+  ipc::IPCResult RecvDrainComplete() override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   void RejectPromise(uint32_t aPromiseId,
                      nsresult aError,
                      const nsCString& aErrorMessage);
 
   void ResolvePromise(uint32_t aPromiseId);
 
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -41,16 +41,18 @@ child:
   async InitializeVideoDecoder(CDMVideoDecoderConfig aConfig);
 
   async DeinitializeVideoDecoder();
 
   async ResetVideoDecoder();
 
   async DecryptAndDecodeFrame(CDMInputBuffer aBuffer);
 
+  async Drain();
+
   async Destroy();
 
 parent:
   async __delete__();
 
   // cdm::Host8
   async OnResolveNewSessionPromise(uint32_t aPromiseId, nsCString aSessionId);
 
@@ -84,13 +86,15 @@ parent:
   async OnDecoderInitDone(uint32_t aStatus);
 
   // Return values of cdm::ContentDecryptionModule8::DecryptAndDecodeFrame
   async Decoded(CDMVideoFrame aFrame);
   async DecodeFailed(uint32_t aStatus);
 
   async ResetVideoDecoderComplete();
 
+  async DrainComplete();
+
   async Shutdown();
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.cpp
@@ -121,19 +121,19 @@ ChromiumCDMVideoDecoder::Flush()
   RefPtr<gmp::ChromiumCDMParent> cdm = mCDMParent;
   return InvokeAsync(
     mGMPThread, __func__, [cdm]() { return cdm->FlushVideoDecoder(); });
 }
 
 RefPtr<MediaDataDecoder::DecodePromise>
 ChromiumCDMVideoDecoder::Drain()
 {
-  return DecodePromise::CreateAndReject(
-    MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, RESULT_DETAIL("Unimplemented")),
-    __func__);
+  MOZ_ASSERT(mCDMParent);
+  RefPtr<gmp::ChromiumCDMParent> cdm = mCDMParent;
+  return InvokeAsync(mGMPThread, __func__, [cdm]() { return cdm->Drain(); });
 }
 
 RefPtr<ShutdownPromise>
 ChromiumCDMVideoDecoder::Shutdown()
 {
   return ShutdownPromise::CreateAndResolve(true, __func__);
 }