summaryrefslogtreecommitdiff
path: root/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp')
-rw-r--r--dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp400
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