Bug 1339748 - Convert samples to AnnexB before passing to CDMs, rather than inside CDMs. r?jya
MozReview-Commit-ID: 6i3uXC8ily9
--- 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 (!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=%lld) 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/eme/EMEDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
@@ -364,21 +364,31 @@ EMEDecoderModule::CreateAudioDecoder(con
decoder, mProxy, AbstractThread::GetCurrent()->AsTaskQueue(),
aParams.mType, aParams.mOnWaitingForKeyEvent));
return emeDecoder.forget();
}
PlatformDecoderModule::ConversionRequired
EMEDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
{
- if (aConfig.IsVideo() && MP4Decoder::IsH264(aConfig.mMimeType)) {
- return ConversionRequired::kNeedAVCC;
- } else {
- return ConversionRequired::kNeedNone;
+ if (MP4Decoder::IsH264(aConfig.mMimeType)) {
+ // We need AnnexB if the GMP is decoding H.264, or if we're using
+ // Gecko's decoders on Android or Windows.
+ if (SupportsMimeType(aConfig.mMimeType, nullptr)
+#if defined(__ANDROID__) || defined(XP_WIN)
+ || true
+#endif
+ ) {
+ return ConversionRequired::kNeedAnnexB;
+ } else {
+ // All other wrapped decoders take AVCC.
+ return ConversionRequired::kNeedAVCC;
+ }
}
+ return ConversionRequired::kNeedNone;
}
bool
EMEDecoderModule::SupportsMimeType(const nsACString& aMimeType,
DecoderDoctorDiagnostics* aDiagnostics) const
{
Maybe<nsCString> gmp;
gmp.emplace(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
--- 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)
{