/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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 "OmxPromiseLayer.h" #include "ImageContainer.h" #include "OmxDataDecoder.h" #include "OmxPlatformLayer.h" #ifdef LOG # undef LOG #endif #define LOG(arg, ...) \ MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \ ("OmxPromiseLayer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) namespace mozilla { OmxPromiseLayer::OmxPromiseLayer(TaskQueue* aTaskQueue, OmxDataDecoder* aDataDecoder, layers::ImageContainer* aImageContainer) : mTaskQueue(aTaskQueue) { mPlatformLayer.reset(OmxPlatformLayer::Create(aDataDecoder, this, aTaskQueue, aImageContainer)); MOZ_ASSERT(!!mPlatformLayer); } RefPtr OmxPromiseLayer::Init( const TrackInfo* aInfo) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); OMX_ERRORTYPE err = mPlatformLayer->InitOmxToStateLoaded(aInfo); if (err != OMX_ErrorNone) { OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandStateSet); return OmxCommandPromise::CreateAndReject(failure, __func__); } OMX_STATETYPE state = GetState(); if (state == OMX_StateLoaded) { return OmxCommandPromise::CreateAndResolve(OMX_CommandStateSet, __func__); } if (state == OMX_StateIdle) { return SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr); } OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandStateSet); return OmxCommandPromise::CreateAndReject(failure, __func__); } OMX_ERRORTYPE OmxPromiseLayer::Config() { MOZ_ASSERT(GetState() == OMX_StateLoaded); return mPlatformLayer->Config(); } RefPtr OmxPromiseLayer::FillBuffer( BufferData* aData) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); LOG("buffer %p", aData->mBuffer); RefPtr p = aData->mPromise.Ensure(__func__); OMX_ERRORTYPE err = mPlatformLayer->FillThisBuffer(aData); if (err != OMX_ErrorNone) { OmxBufferFailureHolder failure(err, aData); aData->mPromise.Reject(std::move(failure), __func__); } else { aData->mStatus = BufferData::BufferStatus::OMX_COMPONENT; GetBufferHolders(OMX_DirOutput)->AppendElement(aData); } return p; } RefPtr OmxPromiseLayer::EmptyBuffer( BufferData* aData) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); LOG("buffer %p, size %lu", aData->mBuffer, aData->mBuffer->nFilledLen); RefPtr p = aData->mPromise.Ensure(__func__); OMX_ERRORTYPE err = mPlatformLayer->EmptyThisBuffer(aData); if (err != OMX_ErrorNone) { OmxBufferFailureHolder failure(err, aData); aData->mPromise.Reject(std::move(failure), __func__); } else { if (aData->mRawData) { mRawDatas.AppendElement(std::move(aData->mRawData)); } aData->mStatus = BufferData::BufferStatus::OMX_COMPONENT; GetBufferHolders(OMX_DirInput)->AppendElement(aData); } return p; } OmxPromiseLayer::BUFFERLIST* OmxPromiseLayer::GetBufferHolders( OMX_DIRTYPE aType) { MOZ_ASSERT(aType == OMX_DirInput || aType == OMX_DirOutput); if (aType == OMX_DirInput) { return &mInbufferHolders; } return &mOutbufferHolders; } already_AddRefed OmxPromiseLayer::FindAndRemoveRawData( OMX_TICKS aTimecode) { for (auto raw : mRawDatas) { if (raw->mTime.ToMicroseconds() == aTimecode) { mRawDatas.RemoveElement(raw); return raw.forget(); } } return nullptr; } already_AddRefed OmxPromiseLayer::FindAndRemoveBufferHolder( OMX_DIRTYPE aType, BufferData::BufferID aId) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); RefPtr holder; BUFFERLIST* holders = GetBufferHolders(aType); for (uint32_t i = 0; i < holders->Length(); i++) { if (holders->ElementAt(i)->ID() == aId) { holder = holders->ElementAt(i); holders->RemoveElementAt(i); return holder.forget(); } } return nullptr; } already_AddRefed OmxPromiseLayer::FindBufferById( OMX_DIRTYPE aType, BufferData::BufferID aId) { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); RefPtr holder; BUFFERLIST* holders = GetBufferHolders(aType); for (uint32_t i = 0; i < holders->Length(); i++) { if (holders->ElementAt(i)->ID() == aId) { holder = holders->ElementAt(i); return holder.forget(); } } return nullptr; } void OmxPromiseLayer::EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData* aData) { if (aData) { LOG("type %d, buffer %p", aType, aData->mBuffer); if (aType == OMX_DirOutput) { aData->mRawData = nullptr; aData->mRawData = FindAndRemoveRawData(aData->mBuffer->nTimeStamp); } aData->mStatus = BufferData::BufferStatus::OMX_CLIENT; aData->mPromise.Resolve(aData, __func__); } else { LOG("type %d, no buffer", aType); } } void OmxPromiseLayer::EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData::BufferID aID) { RefPtr holder = FindAndRemoveBufferHolder(aType, aID); EmptyFillBufferDone(aType, holder); } RefPtr OmxPromiseLayer::SendCommand( OMX_COMMANDTYPE aCmd, OMX_U32 aParam1, OMX_PTR aCmdData) { if (aCmd == OMX_CommandFlush) { // It doesn't support another flush commands before previous one is // completed. MOZ_RELEASE_ASSERT(!mFlushCommands.Length()); // Some coomponents don't send event with OMX_ALL, they send flush complete // event with input port and another event for output port. // In prupose of better compatibility, we interpret the OMX_ALL to // OMX_DirInput and OMX_DirOutput flush separately. OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput}; for (const auto type : types) { if ((aParam1 == type) || (aParam1 == OMX_ALL)) { mFlushCommands.AppendElement(FlushCommand({type, aCmdData})); } if (type == OMX_DirInput) { // Clear all buffered raw data. mRawDatas.Clear(); } } // Don't overlay more than one flush command, some components can't overlay // flush commands. So here we send another flush after receiving the // previous flush completed event. if (mFlushCommands.Length()) { OMX_ERRORTYPE err = mPlatformLayer->SendCommand( OMX_CommandFlush, mFlushCommands.ElementAt(0).type, mFlushCommands.ElementAt(0).cmd); if (err != OMX_ErrorNone) { OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush); return OmxCommandPromise::CreateAndReject(failure, __func__); } } else { LOG("OMX_CommandFlush parameter error"); OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush); return OmxCommandPromise::CreateAndReject(failure, __func__); } } else { OMX_ERRORTYPE err = mPlatformLayer->SendCommand(aCmd, aParam1, aCmdData); if (err != OMX_ErrorNone) { OmxCommandFailureHolder failure(OMX_ErrorNotReady, aCmd); return OmxCommandPromise::CreateAndReject(failure, __func__); } } RefPtr p; if (aCmd == OMX_CommandStateSet) { p = mCommandStatePromise.Ensure(__func__); } else if (aCmd == OMX_CommandFlush) { p = mFlushPromise.Ensure(__func__); } else if (aCmd == OMX_CommandPortEnable) { p = mPortEnablePromise.Ensure(__func__); } else if (aCmd == OMX_CommandPortDisable) { p = mPortDisablePromise.Ensure(__func__); } else { LOG("error unsupport command"); MOZ_ASSERT(0); } return p; } bool OmxPromiseLayer::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2) { OMX_COMMANDTYPE cmd = (OMX_COMMANDTYPE)aData1; switch (aEvent) { case OMX_EventCmdComplete: { if (cmd == OMX_CommandStateSet) { mCommandStatePromise.Resolve(OMX_CommandStateSet, __func__); } else if (cmd == OMX_CommandFlush) { MOZ_RELEASE_ASSERT(mFlushCommands.ElementAt(0).type == aData2); LOG("OMX_CommandFlush completed port type %lu", aData2); mFlushCommands.RemoveElementAt(0); // Sending next flush command. if (mFlushCommands.Length()) { OMX_ERRORTYPE err = mPlatformLayer->SendCommand( OMX_CommandFlush, mFlushCommands.ElementAt(0).type, mFlushCommands.ElementAt(0).cmd); if (err != OMX_ErrorNone) { OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush); mFlushPromise.Reject(failure, __func__); } } else { mFlushPromise.Resolve(OMX_CommandFlush, __func__); } } else if (cmd == OMX_CommandPortDisable) { mPortDisablePromise.Resolve(OMX_CommandPortDisable, __func__); } else if (cmd == OMX_CommandPortEnable) { mPortEnablePromise.Resolve(OMX_CommandPortEnable, __func__); } break; } case OMX_EventError: { if (cmd == OMX_CommandStateSet) { OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandStateSet); mCommandStatePromise.Reject(failure, __func__); } else if (cmd == OMX_CommandFlush) { OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandFlush); mFlushPromise.Reject(failure, __func__); } else if (cmd == OMX_CommandPortDisable) { OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandPortDisable); mPortDisablePromise.Reject(failure, __func__); } else if (cmd == OMX_CommandPortEnable) { OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandPortEnable); mPortEnablePromise.Reject(failure, __func__); } else { return false; } break; } default: { return false; } } return true; } nsresult OmxPromiseLayer::AllocateOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBuffers) { return mPlatformLayer->AllocateOmxBuffer(aType, aBuffers); } nsresult OmxPromiseLayer::ReleaseOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBuffers) { return mPlatformLayer->ReleaseOmxBuffer(aType, aBuffers); } OMX_STATETYPE OmxPromiseLayer::GetState() { OMX_STATETYPE state; OMX_ERRORTYPE err = mPlatformLayer->GetState(&state); return err == OMX_ErrorNone ? state : OMX_StateInvalid; } OMX_ERRORTYPE OmxPromiseLayer::GetParameter(OMX_INDEXTYPE aParamIndex, OMX_PTR aComponentParameterStructure, OMX_U32 aComponentParameterSize) { return mPlatformLayer->GetParameter(aParamIndex, aComponentParameterStructure, aComponentParameterSize); } OMX_ERRORTYPE OmxPromiseLayer::SetParameter(OMX_INDEXTYPE aParamIndex, OMX_PTR aComponentParameterStructure, OMX_U32 aComponentParameterSize) { return mPlatformLayer->SetParameter(aParamIndex, aComponentParameterStructure, aComponentParameterSize); } OMX_U32 OmxPromiseLayer::InputPortIndex() { return mPlatformLayer->InputPortIndex(); } OMX_U32 OmxPromiseLayer::OutputPortIndex() { return mPlatformLayer->OutputPortIndex(); } nsresult OmxPromiseLayer::Shutdown() { LOG(""); MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); MOZ_ASSERT(!GetBufferHolders(OMX_DirInput)->Length()); MOZ_ASSERT(!GetBufferHolders(OMX_DirOutput)->Length()); return mPlatformLayer->Shutdown(); } } // namespace mozilla