diff options
Diffstat (limited to 'dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp')
-rw-r--r-- | dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp | 400 |
1 files changed, 0 insertions, 400 deletions
diff --git a/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp b/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp deleted file mode 100644 index 70d2fd8e08..0000000000 --- a/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp +++ /dev/null @@ -1,400 +0,0 @@ -/* -*- 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); - Log("WidevineVideoDecoder created this=%p", this); - - // Corresponding Release is in DecodingComplete(). - AddRef(); -} - -WidevineVideoDecoder::~WidevineVideoDecoder() -{ - Log("WidevineVideoDecoder destroyed this=%p", this); -} - -static -VideoDecoderConfig::VideoCodecProfile -ToCDMH264Profile(uint8_t aProfile) -{ - switch (aProfile) { - case 66: return VideoDecoderConfig::kH264ProfileBaseline; - case 77: return VideoDecoderConfig::kH264ProfileMain; - case 88: return VideoDecoderConfig::kH264ProfileExtended; - case 100: return VideoDecoderConfig::kH264ProfileHigh; - case 110: return VideoDecoderConfig::kH264ProfileHigh10; - case 122: return VideoDecoderConfig::kH264ProfileHigh422; - case 144: return VideoDecoderConfig::kH264ProfileHigh444Predictive; - } - return VideoDecoderConfig::kUnknownVideoCodecProfile; -} - -void -WidevineVideoDecoder::InitDecode(const GMPVideoCodec& aCodecSettings, - const uint8_t* aCodecSpecific, - uint32_t aCodecSpecificLength, - GMPVideoDecoderCallback* aCallback, - int32_t aCoreCount) -{ - mCallback = aCallback; - VideoDecoderConfig config; - mCodecType = aCodecSettings.mCodecType; - if (mCodecType == kGMPVideoCodecH264) { - config.codec = VideoDecoderConfig::kCodecH264; - const GMPVideoCodecH264* h264 = (const GMPVideoCodecH264*)(aCodecSpecific); - config.profile = ToCDMH264Profile(h264->mAVCC.mProfile); - } else if (mCodecType == kGMPVideoCodecVP8) { - config.codec = VideoDecoderConfig::kCodecVp8; - config.profile = VideoDecoderConfig::kProfileNotNeeded; - } else if (mCodecType == kGMPVideoCodecVP9) { - 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(); - 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) -{ - // We should not be given new input if a drain has been initiated - MOZ_ASSERT(!mDrainPending); - // 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(), 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(); - aInputFrame = nullptr; - - if (rv == kSuccess) { - if (!ReturnOutput(frame)) { - Log("WidevineVideoDecoder::Decode() Failed in ReturnOutput()"); - mCallback->Error(GMPDecodeErr); - return; - } - // A reset should only be started at most at level mReturnOutputCallDepth 1, - // and if it's started it should be finished by that call by the time - // the it returns, so it should always be false by this point. - MOZ_ASSERT(!mResetInProgress); - // Only request more data if we don't have pending samples. - if (mFrameAllocationQueue.empty()) { - MOZ_ASSERT(mCDMWrapper); - mCallback->InputDataExhausted(); - } - } else if (rv == kNeedMoreData) { - MOZ_ASSERT(mCDMWrapper); - mCallback->InputDataExhausted(); - } else { - mCallback->Error(ToGMPErr(rv)); - } - // Finish a drain if pending and we have no pending ReturnOutput calls on the stack. - if (mDrainPending && mReturnOutputCallDepth == 0) { - Drain(); - } -} - -// Util class to assist with counting mReturnOutputCallDepth. -class CounterHelper { -public: - // RAII, increment counter - explicit CounterHelper(int32_t& counter) - : mCounter(counter) - { - mCounter++; - } - - // RAII, decrement counter - ~CounterHelper() - { - mCounter--; - } - -private: - int32_t& mCounter; -}; - -// Util class to make sure GMP frames are freed. Holds a GMPVideoi420Frame* -// and will destroy it when the helper is destroyed unless the held frame -// if forgotten with ForgetFrame. -class FrameDestroyerHelper { -public: - explicit FrameDestroyerHelper(GMPVideoi420Frame*& frame) - : frame(frame) - { - } - - // RAII, destroy frame if held. - ~FrameDestroyerHelper() - { - if (frame) { - frame->Destroy(); - } - frame = nullptr; - } - - // Forget the frame without destroying it. - void ForgetFrame() - { - frame = nullptr; - } - -private: - GMPVideoi420Frame* frame; -}; - - -// Special handing is needed around ReturnOutput as it spins the IPC message -// queue when creating an empty frame and can end up with reentrant calls into -// the class methods. -bool -WidevineVideoDecoder::ReturnOutput(WidevineVideoFrame& aCDMFrame) -{ - MOZ_ASSERT(mReturnOutputCallDepth >= 0); - CounterHelper counterHelper(mReturnOutputCallDepth); - mFrameAllocationQueue.push_back(Move(aCDMFrame)); - if (mReturnOutputCallDepth > 1) { - // In a reentrant call. - return true; - } - while (!mFrameAllocationQueue.empty()) { - MOZ_ASSERT(mReturnOutputCallDepth == 1); - // If we're at call level 1 a reset should not have been started. A - // reset may be received during CreateEmptyFrame below, but we should not - // be in a reset at this stage -- this would indicate receiving decode - // messages before completing our reset, which we should not. - MOZ_ASSERT(!mResetInProgress); - WidevineVideoFrame currentCDMFrame = Move(mFrameAllocationQueue.front()); - mFrameAllocationQueue.pop_front(); - GMPVideoFrame* f = nullptr; - auto err = mVideoHost->CreateFrame(kGMPI420VideoFrame, &f); - if (GMP_FAILED(err) || !f) { - Log("Failed to create i420 frame!\n"); - return false; - } - auto gmpFrame = static_cast<GMPVideoi420Frame*>(f); - FrameDestroyerHelper frameDestroyerHelper(gmpFrame); - Size size = currentCDMFrame.Size(); - const int32_t yStride = currentCDMFrame.Stride(VideoFrame::kYPlane); - const int32_t uStride = currentCDMFrame.Stride(VideoFrame::kUPlane); - const int32_t vStride = currentCDMFrame.Stride(VideoFrame::kVPlane); - const int32_t halfHeight = size.height / 2; - // This call can cause a shmem alloc, during this alloc other calls - // may be made to this class and placed on the stack. ***WARNING***: - // other IPC calls can happen during this call, resulting in calls - // being made to the CDM. After this call state can have changed, - // and should be reevaluated. - err = gmpFrame->CreateEmptyFrame(size.width, - size.height, - yStride, - uStride, - vStride); - // Assert possible reentrant calls or resets haven't altered level unexpectedly. - MOZ_ASSERT(mReturnOutputCallDepth == 1); - ENSURE_GMP_SUCCESS(err, false); - - // If a reset started we need to dump the current frame and complete the reset. - if (mResetInProgress) { - MOZ_ASSERT(mCDMWrapper); - MOZ_ASSERT(mFrameAllocationQueue.empty()); - CompleteReset(); - return true; - } - - err = gmpFrame->SetWidth(size.width); - ENSURE_GMP_SUCCESS(err, false); - - err = gmpFrame->SetHeight(size.height); - ENSURE_GMP_SUCCESS(err, false); - - Buffer* buffer = currentCDMFrame.FrameBuffer(); - uint8_t* outBuffer = gmpFrame->Buffer(kGMPYPlane); - ENSURE_TRUE(outBuffer != nullptr, false); - MOZ_ASSERT(gmpFrame->AllocatedSize(kGMPYPlane) >= yStride*size.height); - memcpy(outBuffer, - buffer->Data() + currentCDMFrame.PlaneOffset(VideoFrame::kYPlane), - yStride * size.height); - - outBuffer = gmpFrame->Buffer(kGMPUPlane); - ENSURE_TRUE(outBuffer != nullptr, false); - MOZ_ASSERT(gmpFrame->AllocatedSize(kGMPUPlane) >= uStride * halfHeight); - memcpy(outBuffer, - buffer->Data() + currentCDMFrame.PlaneOffset(VideoFrame::kUPlane), - uStride * halfHeight); - - outBuffer = gmpFrame->Buffer(kGMPVPlane); - ENSURE_TRUE(outBuffer != nullptr, false); - MOZ_ASSERT(gmpFrame->AllocatedSize(kGMPVPlane) >= vStride * halfHeight); - memcpy(outBuffer, - buffer->Data() + currentCDMFrame.PlaneOffset(VideoFrame::kVPlane), - vStride * halfHeight); - - gmpFrame->SetTimestamp(currentCDMFrame.Timestamp()); - - auto d = mFrameDurations.find(currentCDMFrame.Timestamp()); - if (d != mFrameDurations.end()) { - gmpFrame->SetDuration(d->second); - mFrameDurations.erase(d); - } - - // Forget frame so it's not deleted, call back taking ownership. - frameDestroyerHelper.ForgetFrame(); - mCallback->Decoded(gmpFrame); - } - - return true; -} - -void -WidevineVideoDecoder::Reset() -{ - Log("WidevineVideoDecoder::Reset() mSentInput=%d", mSentInput); - // We shouldn't reset if a drain is pending. - MOZ_ASSERT(!mDrainPending); - mResetInProgress = true; - if (mSentInput) { - CDM()->ResetDecoder(kStreamTypeVideo); - } - // Remove queued frames, but do not reset mReturnOutputCallDepth, let the - // ReturnOutput calls unwind and decrement the counter as needed. - mFrameAllocationQueue.clear(); - mFrameDurations.clear(); - // Only if no ReturnOutput calls are in progress can we complete, otherwise - // ReturnOutput needs to finalize the reset. - if (mReturnOutputCallDepth == 0) { - CompleteReset(); - } -} - -void -WidevineVideoDecoder::CompleteReset() -{ - mCallback->ResetComplete(); - mSentInput = false; - mResetInProgress = false; -} - -void -WidevineVideoDecoder::Drain() -{ - Log("WidevineVideoDecoder::Drain()"); - if (mReturnOutputCallDepth > 0) { - Log("Drain call is reentrant, postponing drain"); - mDrainPending = true; - return; - } - - Status rv = kSuccess; - while (rv == kSuccess) { - WidevineVideoFrame frame; - InputBuffer sample; - Status rv = CDM()->DecryptAndDecodeFrame(sample, &frame); - Log("WidevineVideoDecoder::Drain(); DecryptAndDecodeFrame() rv=%d", rv); - if (frame.Format() == kUnknownVideoFormat) { - break; - } - if (rv == kSuccess) { - if (!ReturnOutput(frame)) { - Log("WidevineVideoDecoder::Decode() Failed in ReturnOutput()"); - } - } - } - // Shouldn't be reset while draining. - MOZ_ASSERT(!mResetInProgress); - - CDM()->ResetDecoder(kStreamTypeVideo); - mDrainPending = false; - mCallback->DrainComplete(); -} - -void -WidevineVideoDecoder::DecodingComplete() -{ - Log("WidevineVideoDecoder::DecodingComplete()"); - if (mCDMWrapper) { - CDM()->DeinitializeDecoder(kStreamTypeVideo); - mCDMWrapper = nullptr; - } - // Release that corresponds to AddRef() in constructor. - Release(); -} - -} // namespace mozilla |