Bug 1339748: P3. Convert samples to AnnexB before passing to CDMs, rather than inside CDMs. r?jya draft
authorChris Pearce <cpearce@mozilla.com>
Wed, 15 Feb 2017 16:03:09 +1300
changeset 486429 dcc440838eb1c03689a9300f0bfd4ebb779bd793
parent 486428 429ad20c55a6c40294ba8597dbe244f0ea83d45c
child 546234 0c48822a28c2810ea11687a8c693dd2898113310
push id45972
push userbmo:jyavenard@mozilla.com
push dateFri, 17 Feb 2017 22:13:49 +0000
reviewersjya
bugs1339748
milestone54.0a1
Bug 1339748: P3. Convert samples to AnnexB before passing to CDMs, rather than inside CDMs. r?jya MozReview-Commit-ID: 6i3uXC8ily9
dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp
dom/media/gmp/widevine-adapter/WidevineVideoDecoder.h
dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
media/libstagefright/binding/AnnexB.cpp
--- a/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp
@@ -1,29 +1,26 @@
 /* -*- 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/. */
 
 #include "WidevineVideoDecoder.h"
-
-#include "mp4_demuxer/AnnexB.h"
 #include "WidevineUtils.h"
 #include "WidevineVideoFrame.h"
 #include "mozilla/Move.h"
 
 using namespace cdm;
 
 namespace mozilla {
 
 WidevineVideoDecoder::WidevineVideoDecoder(GMPVideoHost* aVideoHost,
                                            RefPtr<CDMWrapper> aCDMWrapper)
   : mVideoHost(aVideoHost)
   , mCDMWrapper(Move(aCDMWrapper))
-  , mExtraData(new MediaByteBuffer())
   , mSentInput(false)
   , mCodecType(kGMPVideoCodecInvalid)
   , mReturnOutputCallDepth(0)
   , mDrainPending(false)
   , mResetInProgress(false)
 {
   // Expect to start with a CDM wrapper, will release it in DecodingComplete().
   MOZ_ASSERT(mCDMWrapper);
@@ -75,26 +72,29 @@ WidevineVideoDecoder::InitDecode(const G
     config.codec = VideoDecoderConfig::kCodecVp9;
     config.profile = VideoDecoderConfig::kProfileNotNeeded;
   } else {
     mCallback->Error(GMPInvalidArgErr);
     return;
   }
   config.format = kYv12;
   config.coded_size = Size(aCodecSettings.mWidth, aCodecSettings.mHeight);
-  mExtraData->AppendElements(aCodecSpecific + 1, aCodecSpecificLength);
-  config.extra_data = mExtraData->Elements();
-  config.extra_data_size = mExtraData->Length();
+  nsTArray<uint8_t> extraData;
+  if (aCodecSpecificLength) {
+    // Note: first byte is GMP's packetization mode, and is ignored.
+    extraData.AppendElements(aCodecSpecific + 1, aCodecSpecificLength - 1);
+    config.extra_data = extraData.Elements();
+    config.extra_data_size = extraData.Length();
+  }
   Status rv = CDM()->InitializeVideoDecoder(config);
   if (rv != kSuccess) {
     mCallback->Error(ToGMPErr(rv));
     return;
   }
   Log("WidevineVideoDecoder::InitDecode() rv=%d", rv);
-  mAnnexB = mp4_demuxer::AnnexB::ConvertExtraDataToAnnexB(mExtraData);
 }
 
 void
 WidevineVideoDecoder::Decode(GMPVideoEncodedFrame* aInputFrame,
                              bool aMissingFrames,
                              const uint8_t* aCodecSpecificInfo,
                              uint32_t aCodecSpecificInfoLength,
                              int64_t aRenderTimeMs)
@@ -104,46 +104,21 @@ WidevineVideoDecoder::Decode(GMPVideoEnc
   // We may not get the same out of the CDM decoder as we put in, and there
   // may be some latency, i.e. we may need to input (say) 30 frames before
   // we receive output. So we need to store the durations of the frames input,
   // and retrieve them on output.
   mFrameDurations[aInputFrame->TimeStamp()] = aInputFrame->Duration();
 
   mSentInput = true;
   InputBuffer sample;
-
-  RefPtr<MediaRawData> raw(
-    new MediaRawData(aInputFrame->Buffer(), aInputFrame->Size()));
-  if (aInputFrame->Size() && !raw->Data()) {
-    // OOM.
-    mCallback->Error(GMPAllocErr);
-    return;
-  }
-  raw->mExtraData = mExtraData;
-  raw->mKeyframe = (aInputFrame->FrameType() == kGMPKeyFrame);
-  if (mCodecType == kGMPVideoCodecH264) {
-    // Convert input from AVCC, which GMPAPI passes in, to AnnexB, which
-    // Chromium uses internally.
-    mp4_demuxer::AnnexB::ConvertSampleToAnnexB(raw);
-  }
-
-  const GMPEncryptedBufferMetadata* crypto = aInputFrame->GetDecryptionData();
   nsTArray<SubsampleEntry> subsamples;
-  InitInputBuffer(crypto, aInputFrame->TimeStamp(), raw->Data(), raw->Size(),
+  InitInputBuffer(aInputFrame->GetDecryptionData(), aInputFrame->TimeStamp(),
+                  aInputFrame->Buffer(), aInputFrame->Size(),
                   sample, subsamples);
 
-  // For keyframes, ConvertSampleToAnnexB will stick the AnnexB extra data
-  // at the start of the input. So we need to account for that as clear data
-  // in the subsamples.
-  if (raw->mKeyframe
-      && !subsamples.IsEmpty()
-      && mCodecType == kGMPVideoCodecH264) {
-    subsamples[0].clear_bytes += mAnnexB->Length();
-  }
-
   WidevineVideoFrame frame;
   Status rv = CDM()->DecryptAndDecodeFrame(sample, &frame);
   Log("WidevineVideoDecoder::Decode(timestamp=%" PRId64 ") rv=%d", sample.timestamp,
       rv);
 
   // Destroy frame, so that the shmem is now free to be used to return
   // output to the Gecko process.
   aInputFrame->Destroy();
--- a/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.h
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.h
@@ -5,17 +5,16 @@
 
 #ifndef WidevineVideoDecoder_h_
 #define WidevineVideoDecoder_h_
 
 #include "stddef.h"
 #include "content_decryption_module.h"
 #include "gmp-api/gmp-video-decode.h"
 #include "gmp-api/gmp-video-host.h"
-#include "MediaData.h"
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
 #include "WidevineDecryptor.h"
 #include "WidevineVideoFrame.h"
 #include <map>
 #include <deque>
 
 namespace mozilla {
@@ -54,18 +53,16 @@ private:
     return mCDMWrapper->GetCDM();
   }
 
   bool ReturnOutput(WidevineVideoFrame& aFrame);
   void CompleteReset();
 
   GMPVideoHost* mVideoHost;
   RefPtr<CDMWrapper> mCDMWrapper;
-  RefPtr<MediaByteBuffer> mExtraData;
-  RefPtr<MediaByteBuffer> mAnnexB;
   GMPVideoDecoderCallback* mCallback = nullptr;
   std::map<uint64_t, uint64_t> mFrameDurations;
   bool mSentInput;
   GMPVideoCodecType mCodecType;
   // Frames waiting on allocation
   std::deque<WidevineVideoFrame> mFrameAllocationQueue;
   // Number of calls of ReturnOutput currently in progress.
   int32_t mReturnOutputCallDepth;
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
@@ -39,17 +39,17 @@ public:
   RefPtr<FlushPromise> Flush() override;
   RefPtr<ShutdownPromise> Shutdown() override;
   const char* GetDescriptionName() const override
   {
     return "GMP video decoder";
   }
   ConversionRequired NeedsConversion() const override
   {
-    return ConversionRequired::kNeedAVCC;
+    return ConversionRequired::kNeedAnnexB;
   }
 
   // GMPVideoDecoderCallbackProxy
   // All those methods are called on the GMP thread.
   void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
   void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) override;
   void ReceivedDecodedFrame(const uint64_t aPictureId) override;
   void InputDataExhausted() override;
--- a/media/libstagefright/binding/AnnexB.cpp
+++ b/media/libstagefright/binding/AnnexB.cpp
@@ -64,16 +64,26 @@ AnnexB::ConvertSampleToAnnexB(mozilla::M
 
   // Prepend the Annex B NAL with SPS and PPS tables to keyframes.
   if (aAddSPS && aSample->mKeyframe) {
     RefPtr<MediaByteBuffer> annexB =
       ConvertExtraDataToAnnexB(aSample->mExtraData);
     if (!samplewriter->Prepend(annexB->Elements(), annexB->Length())) {
       return false;
     }
+
+    // Prepending the SPS/PPS NAL will mess up the encryption subsample
+    // offsets. So we need to account for the extra bytes by increasing
+    // the length of the first clear data subsample. Otherwise decryption
+    // will fail.
+    if (aSample->mCrypto.mValid) {
+      MOZ_ASSERT(samplewriter->mCrypto.mPlainSizes.Length() > 0);
+      samplewriter->mCrypto.mPlainSizes[0] += annexB->Length();
+    }
+
   }
 
   return true;
 }
 
 already_AddRefed<mozilla::MediaByteBuffer>
 AnnexB::ConvertExtraDataToAnnexB(const mozilla::MediaByteBuffer* aExtraData)
 {