diff options
Diffstat (limited to 'media/gmp-clearkey/0.1/VideoDecoder.cpp')
-rw-r--r-- | media/gmp-clearkey/0.1/VideoDecoder.cpp | 455 |
1 files changed, 0 insertions, 455 deletions
diff --git a/media/gmp-clearkey/0.1/VideoDecoder.cpp b/media/gmp-clearkey/0.1/VideoDecoder.cpp deleted file mode 100644 index a3f25402b1..0000000000 --- a/media/gmp-clearkey/0.1/VideoDecoder.cpp +++ /dev/null @@ -1,455 +0,0 @@ -/* - * Copyright 2013, Mozilla Foundation and contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <cstdint> -#include <limits> - -#include "AnnexB.h" -#include "BigEndian.h" -#include "ClearKeyDecryptionManager.h" -#include "ClearKeyUtils.h" -#include "gmp-task-utils.h" -#include "VideoDecoder.h" - -using namespace wmf; - -VideoDecoder::VideoDecoder(GMPVideoHost *aHostAPI) - : mHostAPI(aHostAPI) - , mCallback(nullptr) - , mWorkerThread(nullptr) - , mMutex(nullptr) - , mNumInputTasks(0) - , mSentExtraData(false) - , mIsFlushing(false) - , mHasShutdown(false) -{ - // We drop the ref in DecodingComplete(). - AddRef(); -} - -VideoDecoder::~VideoDecoder() -{ - if (mMutex) { - mMutex->Destroy(); - } -} - -void -VideoDecoder::InitDecode(const GMPVideoCodec& aCodecSettings, - const uint8_t* aCodecSpecific, - uint32_t aCodecSpecificLength, - GMPVideoDecoderCallback* aCallback, - int32_t aCoreCount) -{ - mCallback = aCallback; - assert(mCallback); - mDecoder = new WMFH264Decoder(); - HRESULT hr = mDecoder->Init(aCoreCount); - if (FAILED(hr)) { - CK_LOGD("VideoDecoder::InitDecode failed to init WMFH264Decoder"); - mCallback->Error(GMPGenericErr); - return; - } - - auto err = GetPlatform()->createmutex(&mMutex); - if (GMP_FAILED(err)) { - CK_LOGD("VideoDecoder::InitDecode failed to create GMPMutex"); - mCallback->Error(GMPGenericErr); - return; - } - - // The first byte is mPacketizationMode, which is only relevant for - // WebRTC/OpenH264 usecase. - const uint8_t* avcc = aCodecSpecific + 1; - const uint8_t* avccEnd = aCodecSpecific + aCodecSpecificLength; - mExtraData.insert(mExtraData.end(), avcc, avccEnd); - - AnnexB::ConvertConfig(mExtraData, mAnnexB); -} - -void -VideoDecoder::EnsureWorker() -{ - if (!mWorkerThread) { - GetPlatform()->createthread(&mWorkerThread); - if (!mWorkerThread) { - mCallback->Error(GMPAllocErr); - return; - } - } -} - -void -VideoDecoder::Decode(GMPVideoEncodedFrame* aInputFrame, - bool aMissingFrames, - const uint8_t* aCodecSpecificInfo, - uint32_t aCodecSpecificInfoLength, - int64_t aRenderTimeMs) -{ - if (aInputFrame->BufferType() != GMP_BufferLength32) { - // Gecko should only send frames with 4 byte NAL sizes to GMPs. - mCallback->Error(GMPGenericErr); - return; - } - - EnsureWorker(); - - { - AutoLock lock(mMutex); - mNumInputTasks++; - } - - // Note: we don't need the codec specific info on a per-frame basis. - // It's mostly useful for WebRTC use cases. - - // Make a copy of the data, so we can release aInputFrame ASAP, - // to avoid too many shmem handles being held by the GMP process. - // If the GMP process holds on to too many shmem handles, the Gecko - // side can fail to allocate a shmem to send more input. This is - // particularly a problem in Gecko mochitests, which can open multiple - // actors at once which share the same pool of shmems. - DecodeData* data = new DecodeData(); - Assign(data->mBuffer, aInputFrame->Buffer(), aInputFrame->Size()); - data->mTimestamp = aInputFrame->TimeStamp(); - data->mDuration = aInputFrame->Duration(); - data->mIsKeyframe = (aInputFrame->FrameType() == kGMPKeyFrame); - const GMPEncryptedBufferMetadata* crypto = aInputFrame->GetDecryptionData(); - if (crypto) { - data->mCrypto.Init(crypto); - } - aInputFrame->Destroy(); - mWorkerThread->Post(WrapTaskRefCounted(this, - &VideoDecoder::DecodeTask, - data)); -} - -void -VideoDecoder::DecodeTask(DecodeData* aData) -{ - CK_LOGD("VideoDecoder::DecodeTask"); - AutoPtr<DecodeData> d(aData); - HRESULT hr; - - { - AutoLock lock(mMutex); - mNumInputTasks--; - assert(mNumInputTasks >= 0); - } - - if (mIsFlushing) { - CK_LOGD("VideoDecoder::DecodeTask rejecting frame: flushing."); - return; - } - - if (!aData || !mHostAPI || !mDecoder) { - CK_LOGE("Decode job not set up correctly!"); - return; - } - - std::vector<uint8_t>& buffer = aData->mBuffer; - if (aData->mCrypto.IsValid()) { - // Plugin host should have set up its decryptor/key sessions - // before trying to decode! - GMPErr rv = - ClearKeyDecryptionManager::Get()->Decrypt(buffer, aData->mCrypto); - - if (GMP_FAILED(rv)) { - MaybeRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::Error, rv)); - return; - } - } - - AnnexB::ConvertFrameInPlace(buffer); - - if (aData->mIsKeyframe) { - // We must send the SPS and PPS to Windows Media Foundation's decoder. - // Note: We do this *after* decryption, otherwise the subsample info - // would be incorrect. - buffer.insert(buffer.begin(), mAnnexB.begin(), mAnnexB.end()); - } - - hr = mDecoder->Input(buffer.data(), - buffer.size(), - aData->mTimestamp, - aData->mDuration); - - CK_LOGD("VideoDecoder::DecodeTask() Input ret hr=0x%x\n", hr); - if (FAILED(hr)) { - CK_LOGE("VideoDecoder::DecodeTask() decode failed ret=0x%x%s\n", - hr, - ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : "")); - return; - } - - while (hr == S_OK) { - CComPtr<IMFSample> output; - hr = mDecoder->Output(&output); - CK_LOGD("VideoDecoder::DecodeTask() output ret=0x%x\n", hr); - if (hr == S_OK) { - MaybeRunOnMainThread( - WrapTaskRefCounted(this, - &VideoDecoder::ReturnOutput, - CComPtr<IMFSample>(output), - mDecoder->GetFrameWidth(), - mDecoder->GetFrameHeight(), - mDecoder->GetStride())); - } - if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - AutoLock lock(mMutex); - if (mNumInputTasks == 0) { - // We have run all input tasks. We *must* notify Gecko so that it will - // send us more data. - MaybeRunOnMainThread( - WrapTask(mCallback, - &GMPVideoDecoderCallback::InputDataExhausted)); - } - } - if (FAILED(hr)) { - CK_LOGE("VideoDecoder::DecodeTask() output failed hr=0x%x\n", hr); - } - } -} - -void -VideoDecoder::ReturnOutput(IMFSample* aSample, - int32_t aWidth, - int32_t aHeight, - int32_t aStride) -{ - CK_LOGD("[%p] VideoDecoder::ReturnOutput()\n", this); - assert(aSample); - - HRESULT hr; - - GMPVideoFrame* f = nullptr; - auto err = mHostAPI->CreateFrame(kGMPI420VideoFrame, &f); - if (GMP_FAILED(err) || !f) { - CK_LOGE("Failed to create i420 frame!\n"); - return; - } - if (HasShutdown()) { - // Note: GMPVideoHost::CreateFrame() can process messages before returning, - // including a message that calls VideoDecoder::DecodingComplete(), i.e. - // we can shutdown during the call! - CK_LOGD("Shutdown while waiting on GMPVideoHost::CreateFrame()!\n"); - f->Destroy(); - return; - } - - auto vf = static_cast<GMPVideoi420Frame*>(f); - - hr = SampleToVideoFrame(aSample, aWidth, aHeight, aStride, vf); - ENSURE(SUCCEEDED(hr), /*void*/); - - mCallback->Decoded(vf); -} - -HRESULT -VideoDecoder::SampleToVideoFrame(IMFSample* aSample, - int32_t aWidth, - int32_t aHeight, - int32_t aStride, - GMPVideoi420Frame* aVideoFrame) -{ - ENSURE(aSample != nullptr, E_POINTER); - ENSURE(aVideoFrame != nullptr, E_POINTER); - - HRESULT hr; - CComPtr<IMFMediaBuffer> mediaBuffer; - - // Must convert to contiguous mediaBuffer to use IMD2DBuffer interface. - hr = aSample->ConvertToContiguousBuffer(&mediaBuffer); - ENSURE(SUCCEEDED(hr), hr); - - // Try and use the IMF2DBuffer interface if available, otherwise fallback - // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient, - // but only some systems (Windows 8?) support it. - BYTE* data = nullptr; - LONG stride = 0; - CComPtr<IMF2DBuffer> twoDBuffer; - hr = mediaBuffer->QueryInterface(static_cast<IMF2DBuffer**>(&twoDBuffer)); - if (SUCCEEDED(hr)) { - hr = twoDBuffer->Lock2D(&data, &stride); - ENSURE(SUCCEEDED(hr), hr); - } else { - hr = mediaBuffer->Lock(&data, NULL, NULL); - ENSURE(SUCCEEDED(hr), hr); - stride = aStride; - } - - // The V and U planes are stored 16-row-aligned, so we need to add padding - // to the row heights to ensure the Y'CbCr planes are referenced properly. - // YV12, planar format: [YYYY....][VVVV....][UUUU....] - // i.e., Y, then V, then U. - uint32_t padding = 0; - if (aHeight % 16 != 0) { - padding = 16 - (aHeight % 16); - } - int32_t y_size = stride * (aHeight + padding); - int32_t v_size = stride * (aHeight + padding) / 4; - int32_t halfStride = (stride + 1) / 2; - int32_t halfHeight = (aHeight + 1) / 2; - - auto err = aVideoFrame->CreateEmptyFrame(stride, aHeight, stride, halfStride, halfStride); - ENSURE(GMP_SUCCEEDED(err), E_FAIL); - - err = aVideoFrame->SetWidth(aWidth); - ENSURE(GMP_SUCCEEDED(err), E_FAIL); - err = aVideoFrame->SetHeight(aHeight); - ENSURE(GMP_SUCCEEDED(err), E_FAIL); - - uint8_t* outBuffer = aVideoFrame->Buffer(kGMPYPlane); - ENSURE(outBuffer != nullptr, E_FAIL); - assert(aVideoFrame->AllocatedSize(kGMPYPlane) >= stride*aHeight); - memcpy(outBuffer, data, stride*aHeight); - - outBuffer = aVideoFrame->Buffer(kGMPUPlane); - ENSURE(outBuffer != nullptr, E_FAIL); - assert(aVideoFrame->AllocatedSize(kGMPUPlane) >= halfStride*halfHeight); - memcpy(outBuffer, data+y_size, halfStride*halfHeight); - - outBuffer = aVideoFrame->Buffer(kGMPVPlane); - ENSURE(outBuffer != nullptr, E_FAIL); - assert(aVideoFrame->AllocatedSize(kGMPVPlane) >= halfStride*halfHeight); - memcpy(outBuffer, data + y_size + v_size, halfStride*halfHeight); - - if (twoDBuffer) { - twoDBuffer->Unlock2D(); - } else { - mediaBuffer->Unlock(); - } - - LONGLONG hns = 0; - hr = aSample->GetSampleTime(&hns); - ENSURE(SUCCEEDED(hr), hr); - aVideoFrame->SetTimestamp(HNsToUsecs(hns)); - - hr = aSample->GetSampleDuration(&hns); - ENSURE(SUCCEEDED(hr), hr); - aVideoFrame->SetDuration(HNsToUsecs(hns)); - - return S_OK; -} - -void -VideoDecoder::ResetCompleteTask() -{ - mIsFlushing = false; - if (mCallback) { - MaybeRunOnMainThread(WrapTask(mCallback, - &GMPVideoDecoderCallback::ResetComplete)); - } -} - -void -VideoDecoder::Reset() -{ - mIsFlushing = true; - if (mDecoder) { - mDecoder->Reset(); - } - - // Schedule ResetComplete callback to run after existing frames have been - // flushed out of the task queue. - EnsureWorker(); - mWorkerThread->Post(WrapTaskRefCounted(this, - &VideoDecoder::ResetCompleteTask)); -} - -void -VideoDecoder::DrainTask() -{ - mDecoder->Drain(); - - // Return any pending output. - HRESULT hr = S_OK; - while (hr == S_OK) { - CComPtr<IMFSample> output; - hr = mDecoder->Output(&output); - CK_LOGD("VideoDecoder::DrainTask() output ret=0x%x\n", hr); - if (hr == S_OK) { - MaybeRunOnMainThread( - WrapTaskRefCounted(this, - &VideoDecoder::ReturnOutput, - CComPtr<IMFSample>(output), - mDecoder->GetFrameWidth(), - mDecoder->GetFrameHeight(), - mDecoder->GetStride())); - } - } - MaybeRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete)); -} - -void -VideoDecoder::Drain() -{ - if (!mDecoder) { - if (mCallback) { - mCallback->DrainComplete(); - } - return; - } - EnsureWorker(); - mWorkerThread->Post(WrapTaskRefCounted(this, - &VideoDecoder::DrainTask)); -} - -void -VideoDecoder::DecodingComplete() -{ - if (mWorkerThread) { - mWorkerThread->Join(); - } - mHasShutdown = true; - - // Release the reference we added in the constructor. There may be - // WrapRefCounted tasks that also hold references to us, and keep - // us alive a little longer. - Release(); -} - -void -VideoDecoder::MaybeRunOnMainThread(GMPTask* aTask) -{ - class MaybeRunTask : public GMPTask - { - public: - MaybeRunTask(VideoDecoder* aDecoder, GMPTask* aTask) - : mDecoder(aDecoder), mTask(aTask) - { } - - virtual void Run(void) { - if (mDecoder->HasShutdown()) { - CK_LOGD("Trying to dispatch to main thread after VideoDecoder has shut down"); - return; - } - - mTask->Run(); - } - - virtual void Destroy() - { - mTask->Destroy(); - delete this; - } - - private: - RefPtr<VideoDecoder> mDecoder; - GMPTask* mTask; - }; - - GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask)); -} |