LCOV - code coverage report
Current view: top level - dom/media/platforms/omx - OmxDataDecoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 522 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 123 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "OmxDataDecoder.h"
       8             : 
       9             : #include "OMX_Audio.h"
      10             : #include "OMX_Component.h"
      11             : #include "OMX_Types.h"
      12             : 
      13             : #include "OmxPlatformLayer.h"
      14             : 
      15             : #include "mozilla/IntegerPrintfMacros.h"
      16             : #include "mozilla/SizePrintfMacros.h"
      17             : 
      18             : #ifdef LOG
      19             : #undef LOG
      20             : #undef LOGL
      21             : #endif
      22             : 
      23             : #define LOG(arg, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, ("OmxDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
      24             : 
      25             : #define LOGL(arg, ...)                                                     \
      26             :   {                                                                        \
      27             :     void* p = self;                                              \
      28             :     MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug,                         \
      29             :             ("OmxDataDecoder(%p)::%s: " arg, p, __func__, ##__VA_ARGS__)); \
      30             :   }
      31             : 
      32             : #define CHECK_OMX_ERR(err)     \
      33             :   if (err != OMX_ErrorNone) {  \
      34             :     NotifyError(err, __func__);\
      35             :     return;                    \
      36             :   }                            \
      37             : 
      38             : namespace mozilla {
      39             : 
      40             : static const char*
      41           0 : StateTypeToStr(OMX_STATETYPE aType)
      42             : {
      43           0 :   MOZ_ASSERT(aType == OMX_StateLoaded ||
      44             :              aType == OMX_StateIdle ||
      45             :              aType == OMX_StateExecuting ||
      46             :              aType == OMX_StatePause ||
      47             :              aType == OMX_StateWaitForResources ||
      48             :              aType == OMX_StateInvalid);
      49             : 
      50           0 :   switch (aType) {
      51             :     case OMX_StateLoaded:
      52           0 :       return "OMX_StateLoaded";
      53             :     case OMX_StateIdle:
      54           0 :       return "OMX_StateIdle";
      55             :     case OMX_StateExecuting:
      56           0 :       return "OMX_StateExecuting";
      57             :     case OMX_StatePause:
      58           0 :       return "OMX_StatePause";
      59             :     case OMX_StateWaitForResources:
      60           0 :       return "OMX_StateWaitForResources";
      61             :     case OMX_StateInvalid:
      62           0 :       return "OMX_StateInvalid";
      63             :     default:
      64           0 :       return "Unknown";
      65             :   }
      66             : }
      67             : 
      68             : // A helper class to retrieve AudioData or VideoData.
      69             : class MediaDataHelper {
      70             : protected:
      71           0 :   virtual ~MediaDataHelper() {}
      72             : 
      73             : public:
      74           0 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDataHelper)
      75             : 
      76             :   MediaDataHelper(const TrackInfo* aTrackInfo,
      77             :                   layers::ImageContainer* aImageContainer,
      78             :                   OmxPromiseLayer* aOmxLayer);
      79             : 
      80             :   already_AddRefed<MediaData> GetMediaData(BufferData* aBufferData, bool& aPlatformDepenentData);
      81             : 
      82             : protected:
      83             :   already_AddRefed<AudioData> CreateAudioData(BufferData* aBufferData);
      84             : 
      85             :   already_AddRefed<VideoData> CreateYUV420VideoData(BufferData* aBufferData);
      86             : 
      87             :   const TrackInfo* mTrackInfo;
      88             : 
      89             :   OMX_PARAM_PORTDEFINITIONTYPE mOutputPortDef;
      90             : 
      91             :   // audio output
      92             :   MediaQueue<AudioData> mAudioQueue;
      93             : 
      94             :   AudioCompactor mAudioCompactor;
      95             : 
      96             :   // video output
      97             :   RefPtr<layers::ImageContainer> mImageContainer;
      98             : };
      99             : 
     100           0 : OmxDataDecoder::OmxDataDecoder(const TrackInfo& aTrackInfo,
     101           0 :                                layers::ImageContainer* aImageContainer)
     102           0 :   : mOmxTaskQueue(CreateMediaDecodeTaskQueue("OmxDataDecoder::mOmxTaskQueue"))
     103             :   , mImageContainer(aImageContainer)
     104             :   , mWatchManager(this, mOmxTaskQueue)
     105             :   , mOmxState(OMX_STATETYPE::OMX_StateInvalid, "OmxDataDecoder::mOmxState")
     106           0 :   , mTrackInfo(aTrackInfo.Clone())
     107             :   , mFlushing(false)
     108             :   , mShuttingDown(false)
     109             :   , mCheckingInputExhausted(false)
     110           0 :   , mPortSettingsChanged(-1, "OmxDataDecoder::mPortSettingsChanged")
     111             : {
     112           0 :   LOG("");
     113           0 :   mOmxLayer = new OmxPromiseLayer(mOmxTaskQueue, this, aImageContainer);
     114           0 : }
     115             : 
     116           0 : OmxDataDecoder::~OmxDataDecoder()
     117             : {
     118           0 :   LOG("");
     119           0 : }
     120             : 
     121             : void
     122           0 : OmxDataDecoder::InitializationTask()
     123             : {
     124           0 :   mWatchManager.Watch(mOmxState, &OmxDataDecoder::OmxStateRunner);
     125           0 :   mWatchManager.Watch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
     126           0 : }
     127             : 
     128             : void
     129           0 : OmxDataDecoder::EndOfStream()
     130             : {
     131           0 :   LOG("");
     132           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     133             : 
     134           0 :   RefPtr<OmxDataDecoder> self = this;
     135           0 :   mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
     136             :     ->Then(mOmxTaskQueue, __func__,
     137           0 :         [self, this] () {
     138           0 :           mDrainPromise.ResolveIfExists(mDecodedData, __func__);
     139           0 :             mDecodedData.Clear();
     140           0 :         },
     141           0 :         [self, this] () {
     142           0 :           mDrainPromise.ResolveIfExists(mDecodedData, __func__);
     143           0 :           mDecodedData.Clear();
     144           0 :         });
     145           0 : }
     146             : 
     147             : RefPtr<MediaDataDecoder::InitPromise>
     148           0 : OmxDataDecoder::Init()
     149             : {
     150           0 :   LOG("");
     151             : 
     152           0 :   RefPtr<OmxDataDecoder> self = this;
     153           0 :   return InvokeAsync(mOmxTaskQueue, __func__, [self, this]() {
     154           0 :     InitializationTask();
     155             : 
     156           0 :     RefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
     157           0 :     mOmxLayer->Init(mTrackInfo.get())
     158             :       ->Then(mOmxTaskQueue, __func__,
     159           0 :              [self, this]() {
     160             :                // Omx state should be OMX_StateIdle.
     161           0 :                mOmxState = mOmxLayer->GetState();
     162           0 :                MOZ_ASSERT(mOmxState != OMX_StateIdle);
     163           0 :              },
     164           0 :              [self, this]() {
     165           0 :                RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     166           0 :              });
     167           0 :     return p;
     168           0 :   });
     169             : }
     170             : 
     171             : RefPtr<MediaDataDecoder::DecodePromise>
     172           0 : OmxDataDecoder::Decode(MediaRawData* aSample)
     173             : {
     174           0 :   LOG("sample %p", aSample);
     175           0 :   MOZ_ASSERT(mInitPromise.IsEmpty());
     176             : 
     177           0 :   RefPtr<OmxDataDecoder> self = this;
     178           0 :   RefPtr<MediaRawData> sample = aSample;
     179           0 :   return InvokeAsync(mOmxTaskQueue, __func__, [self, this, sample]() {
     180           0 :     RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
     181           0 :     mMediaRawDatas.AppendElement(Move(sample));
     182             : 
     183             :     // Start to fill/empty buffers.
     184           0 :     if (mOmxState == OMX_StateIdle ||
     185           0 :         mOmxState == OMX_StateExecuting) {
     186           0 :       FillAndEmptyBuffers();
     187             :     }
     188           0 :     return p;
     189           0 :   });
     190             : }
     191             : 
     192             : RefPtr<MediaDataDecoder::FlushPromise>
     193           0 : OmxDataDecoder::Flush()
     194             : {
     195           0 :   LOG("");
     196             : 
     197           0 :   mFlushing = true;
     198             : 
     199           0 :   return InvokeAsync(mOmxTaskQueue, this, __func__, &OmxDataDecoder::DoFlush);
     200             : }
     201             : 
     202             : RefPtr<MediaDataDecoder::DecodePromise>
     203           0 : OmxDataDecoder::Drain()
     204             : {
     205           0 :   LOG("");
     206             : 
     207           0 :   RefPtr<OmxDataDecoder> self = this;
     208           0 :   return InvokeAsync(mOmxTaskQueue, __func__, [self, this]() {
     209           0 :     RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
     210           0 :     SendEosBuffer();
     211           0 :     return p;
     212           0 :   });
     213             : }
     214             : 
     215             : RefPtr<ShutdownPromise>
     216           0 : OmxDataDecoder::Shutdown()
     217             : {
     218           0 :   LOG("");
     219             : 
     220           0 :   mShuttingDown = true;
     221             : 
     222             :   return InvokeAsync(mOmxTaskQueue, this, __func__,
     223           0 :                      &OmxDataDecoder::DoAsyncShutdown);
     224             : }
     225             : 
     226             : RefPtr<ShutdownPromise>
     227           0 : OmxDataDecoder::DoAsyncShutdown()
     228             : {
     229           0 :   LOG("");
     230           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     231           0 :   MOZ_ASSERT(!mFlushing);
     232             : 
     233           0 :   mWatchManager.Unwatch(mOmxState, &OmxDataDecoder::OmxStateRunner);
     234           0 :   mWatchManager.Unwatch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
     235             : 
     236             :   // Flush to all ports, so all buffers can be returned from component.
     237           0 :   RefPtr<OmxDataDecoder> self = this;
     238           0 :   mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
     239           0 :     ->Then(mOmxTaskQueue, __func__,
     240           0 :            [self] () -> RefPtr<OmxCommandPromise> {
     241           0 :              LOGL("DoAsyncShutdown: flush complete");
     242           0 :              return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
     243             :            },
     244           0 :            [self] (const OmxCommandFailureHolder& aError) {
     245           0 :              self->mOmxLayer->Shutdown();
     246           0 :              return OmxCommandPromise::CreateAndReject(aError, __func__);
     247           0 :            })
     248           0 :     ->Then(mOmxTaskQueue, __func__,
     249           0 :            [self] () -> RefPtr<OmxCommandPromise> {
     250             :              RefPtr<OmxCommandPromise> p =
     251           0 :                self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
     252             : 
     253             :              // According to spec 3.1.1.2.2.1:
     254             :              // OMX_StateLoaded needs to be sent before releasing buffers.
     255             :              // And state transition from OMX_StateIdle to OMX_StateLoaded
     256             :              // is completed when all of the buffers have been removed
     257             :              // from the component.
     258             :              // Here the buffer promises are not resolved due to displaying
     259             :              // in layer, it needs to wait before the layer returns the
     260             :              // buffers.
     261           0 :              LOGL("DoAsyncShutdown: releasing buffers...");
     262           0 :              self->ReleaseBuffers(OMX_DirInput);
     263           0 :              self->ReleaseBuffers(OMX_DirOutput);
     264             : 
     265           0 :              return p;
     266             :            },
     267           0 :            [self] (const OmxCommandFailureHolder& aError) {
     268           0 :              self->mOmxLayer->Shutdown();
     269           0 :              return OmxCommandPromise::CreateAndReject(aError, __func__);
     270             :            })
     271             :     ->Then(mOmxTaskQueue, __func__,
     272           0 :            [self] () {
     273           0 :              LOGL("DoAsyncShutdown: OMX_StateLoaded, it is safe to shutdown omx");
     274           0 :              self->mOmxLayer->Shutdown();
     275           0 :              self->mWatchManager.Shutdown();
     276           0 :              self->mOmxLayer = nullptr;
     277           0 :              self->mMediaDataHelper = nullptr;
     278             : 
     279           0 :              self->mShuttingDown = false;
     280           0 :              self->mOmxTaskQueue->BeginShutdown();
     281           0 :              self->mOmxTaskQueue->AwaitShutdownAndIdle();
     282           0 :              self->mShutdownPromise.Resolve(true, __func__);
     283           0 :            },
     284           0 :            [self] () {
     285           0 :              self->mOmxLayer->Shutdown();
     286           0 :              self->mWatchManager.Shutdown();
     287           0 :              self->mOmxLayer = nullptr;
     288           0 :              self->mMediaDataHelper = nullptr;
     289             : 
     290           0 :              self->mShuttingDown = false;
     291           0 :              self->mOmxTaskQueue->BeginShutdown();
     292           0 :              self->mOmxTaskQueue->AwaitShutdownAndIdle();
     293           0 :              self->mShutdownPromise.Resolve(true, __func__);
     294           0 :            });
     295           0 :   return mShutdownPromise.Ensure(__func__);
     296             : }
     297             : 
     298             : void
     299           0 : OmxDataDecoder::FillBufferDone(BufferData* aData)
     300             : {
     301           0 :   MOZ_ASSERT(!aData || aData->mStatus == BufferData::BufferStatus::OMX_CLIENT);
     302             : 
     303             :   // Don't output sample when flush or shutting down, especially for video
     304             :   // decoded frame. Because video decoded frame has a promise in BufferData
     305             :   // waiting for layer to resolve it via recycle callback on Gonk, if other
     306             :   // module doesn't send it to layer, it will cause a unresolved promise and
     307             :   // waiting for resolve infinitely.
     308           0 :   if (mFlushing || mShuttingDown) {
     309           0 :     LOG("mFlush or mShuttingDown, drop data");
     310           0 :     aData->mStatus = BufferData::BufferStatus::FREE;
     311           0 :     return;
     312             :   }
     313             : 
     314           0 :   if (aData->mBuffer->nFlags & OMX_BUFFERFLAG_EOS) {
     315             :     // Reach eos, it's an empty data so it doesn't need to output.
     316           0 :     EndOfStream();
     317           0 :     aData->mStatus = BufferData::BufferStatus::FREE;
     318             :   } else {
     319           0 :     Output(aData);
     320           0 :     FillAndEmptyBuffers();
     321             :   }
     322             : }
     323             : 
     324             : void
     325           0 : OmxDataDecoder::Output(BufferData* aData)
     326             : {
     327           0 :   if (!mMediaDataHelper) {
     328           0 :     mMediaDataHelper = new MediaDataHelper(mTrackInfo.get(), mImageContainer, mOmxLayer);
     329             :   }
     330             : 
     331           0 :   bool isPlatformData = false;
     332           0 :   RefPtr<MediaData> data = mMediaDataHelper->GetMediaData(aData, isPlatformData);
     333           0 :   if (!data) {
     334           0 :     aData->mStatus = BufferData::BufferStatus::FREE;
     335           0 :     return;
     336             :   }
     337             : 
     338           0 :   if (isPlatformData) {
     339             :     // If the MediaData is platform dependnet data, it's mostly a kind of
     340             :     // limited resource, for example, GraphicBuffer on Gonk. So we use promise
     341             :     // to notify when the resource is free.
     342           0 :     aData->mStatus = BufferData::BufferStatus::OMX_CLIENT_OUTPUT;
     343             : 
     344           0 :     MOZ_RELEASE_ASSERT(aData->mPromise.IsEmpty());
     345           0 :     RefPtr<OmxBufferPromise> p = aData->mPromise.Ensure(__func__);
     346             : 
     347           0 :     RefPtr<OmxDataDecoder> self = this;
     348           0 :     RefPtr<BufferData> buffer = aData;
     349             :     p->Then(mOmxTaskQueue, __func__,
     350           0 :         [self, buffer] () {
     351           0 :           MOZ_RELEASE_ASSERT(buffer->mStatus == BufferData::BufferStatus::OMX_CLIENT_OUTPUT);
     352           0 :           buffer->mStatus = BufferData::BufferStatus::FREE;
     353           0 :           self->FillAndEmptyBuffers();
     354           0 :         },
     355           0 :         [buffer] () {
     356           0 :           MOZ_RELEASE_ASSERT(buffer->mStatus == BufferData::BufferStatus::OMX_CLIENT_OUTPUT);
     357           0 :           buffer->mStatus = BufferData::BufferStatus::FREE;
     358           0 :         });
     359             :   } else {
     360           0 :     aData->mStatus = BufferData::BufferStatus::FREE;
     361             :   }
     362             : 
     363           0 :   mDecodedData.AppendElement(Move(data));
     364             : }
     365             : 
     366             : void
     367           0 : OmxDataDecoder::FillBufferFailure(OmxBufferFailureHolder aFailureHolder)
     368             : {
     369           0 :   NotifyError(aFailureHolder.mError, __func__);
     370           0 : }
     371             : 
     372             : void
     373           0 : OmxDataDecoder::EmptyBufferDone(BufferData* aData)
     374             : {
     375           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     376           0 :   MOZ_ASSERT(!aData || aData->mStatus == BufferData::BufferStatus::OMX_CLIENT);
     377             : 
     378             :   // Nothing to do when status of input buffer is OMX_CLIENT.
     379           0 :   aData->mStatus = BufferData::BufferStatus::FREE;
     380           0 :   FillAndEmptyBuffers();
     381             : 
     382             :   // There is no way to know if component gets enough raw samples to generate
     383             :   // output, especially for video decoding. So here it needs to request raw
     384             :   // samples aggressively.
     385           0 :   if (!mCheckingInputExhausted && !mMediaRawDatas.Length()) {
     386           0 :     mCheckingInputExhausted = true;
     387             : 
     388           0 :     RefPtr<OmxDataDecoder> self = this;
     389             :     nsCOMPtr<nsIRunnable> r =
     390           0 :       NS_NewRunnableFunction("OmxDataDecoder::EmptyBufferDone", [self, this]() {
     391           0 :         mCheckingInputExhausted = false;
     392             : 
     393           0 :         if (mMediaRawDatas.Length()) {
     394           0 :           return;
     395             :         }
     396             : 
     397           0 :         mDecodePromise.ResolveIfExists(mDecodedData, __func__);
     398           0 :         mDecodedData.Clear();
     399           0 :       });
     400             : 
     401           0 :     mOmxTaskQueue->Dispatch(r.forget());
     402             :   }
     403           0 : }
     404             : 
     405             : void
     406           0 : OmxDataDecoder::EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder)
     407             : {
     408           0 :   NotifyError(aFailureHolder.mError, __func__);
     409           0 : }
     410             : 
     411             : void
     412           0 : OmxDataDecoder::NotifyError(OMX_ERRORTYPE aOmxError, const char* aLine, const MediaResult& aError)
     413             : {
     414           0 :   LOG("NotifyError %d (%" PRIu32 ") at %s", static_cast<int>(aOmxError),
     415             :       static_cast<uint32_t>(aError.Code()), aLine);
     416           0 :   mDecodedData.Clear();
     417           0 :   mDecodePromise.RejectIfExists(aError, __func__);
     418           0 :   mDrainPromise.RejectIfExists(aError, __func__);
     419           0 :   mFlushPromise.RejectIfExists(aError, __func__);
     420           0 : }
     421             : 
     422             : void
     423           0 : OmxDataDecoder::FillAndEmptyBuffers()
     424             : {
     425           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     426           0 :   MOZ_ASSERT(mOmxState == OMX_StateExecuting);
     427             : 
     428             :   // During the port setting changed, it is forbidden to do any buffer operation.
     429           0 :   if (mPortSettingsChanged != -1 || mShuttingDown || mFlushing) {
     430           0 :     return;
     431             :   }
     432             : 
     433             :   // Trigger input port.
     434           0 :   while (!!mMediaRawDatas.Length()) {
     435             :     // input buffer must be used by component if there is data available.
     436           0 :     RefPtr<BufferData> inbuf = FindAvailableBuffer(OMX_DirInput);
     437           0 :     if (!inbuf) {
     438           0 :       LOG("no input buffer!");
     439           0 :       break;
     440             :     }
     441             : 
     442           0 :     RefPtr<MediaRawData> data = mMediaRawDatas[0];
     443             :     // Buffer size should large enough for raw data.
     444           0 :     MOZ_RELEASE_ASSERT(inbuf->mBuffer->nAllocLen >= data->Size());
     445             : 
     446           0 :     memcpy(inbuf->mBuffer->pBuffer, data->Data(), data->Size());
     447           0 :     inbuf->mBuffer->nFilledLen = data->Size();
     448           0 :     inbuf->mBuffer->nOffset = 0;
     449           0 :     inbuf->mBuffer->nFlags = inbuf->mBuffer->nAllocLen > data->Size() ?
     450             :                              OMX_BUFFERFLAG_ENDOFFRAME : 0;
     451           0 :     inbuf->mBuffer->nTimeStamp = data->mTime.ToMicroseconds();
     452           0 :     if (data->Size()) {
     453           0 :       inbuf->mRawData = mMediaRawDatas[0];
     454             :     } else {
     455           0 :        LOG("send EOS buffer");
     456           0 :       inbuf->mBuffer->nFlags |= OMX_BUFFERFLAG_EOS;
     457             :     }
     458             : 
     459           0 :     LOG("feed sample %p to omx component, len %ld, flag %lX", data.get(),
     460             :         inbuf->mBuffer->nFilledLen, inbuf->mBuffer->nFlags);
     461           0 :     mOmxLayer->EmptyBuffer(inbuf)->Then(mOmxTaskQueue, __func__, this,
     462             :                                         &OmxDataDecoder::EmptyBufferDone,
     463           0 :                                         &OmxDataDecoder::EmptyBufferFailure);
     464           0 :     mMediaRawDatas.RemoveElementAt(0);
     465             :   }
     466             : 
     467             :   // Trigger output port.
     468             :   while (true) {
     469           0 :     RefPtr<BufferData> outbuf = FindAvailableBuffer(OMX_DirOutput);
     470           0 :     if (!outbuf) {
     471           0 :       break;
     472             :     }
     473             : 
     474           0 :     mOmxLayer->FillBuffer(outbuf)->Then(mOmxTaskQueue, __func__, this,
     475             :                                         &OmxDataDecoder::FillBufferDone,
     476           0 :                                         &OmxDataDecoder::FillBufferFailure);
     477           0 :   }
     478             : }
     479             : 
     480             : OmxPromiseLayer::BufferData*
     481           0 : OmxDataDecoder::FindAvailableBuffer(OMX_DIRTYPE aType)
     482             : {
     483           0 :   BUFFERLIST* buffers = GetBuffers(aType);
     484             : 
     485           0 :   for (uint32_t i = 0; i < buffers->Length(); i++) {
     486           0 :     BufferData* buf = buffers->ElementAt(i);
     487           0 :     if (buf->mStatus == BufferData::BufferStatus::FREE) {
     488           0 :       return buf;
     489             :     }
     490             :   }
     491             : 
     492           0 :   return nullptr;
     493             : }
     494             : 
     495             : nsresult
     496           0 : OmxDataDecoder::AllocateBuffers(OMX_DIRTYPE aType)
     497             : {
     498           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     499             : 
     500           0 :   return mOmxLayer->AllocateOmxBuffer(aType, GetBuffers(aType));
     501             : }
     502             : 
     503             : nsresult
     504           0 : OmxDataDecoder::ReleaseBuffers(OMX_DIRTYPE aType)
     505             : {
     506           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     507             : 
     508           0 :   return mOmxLayer->ReleaseOmxBuffer(aType, GetBuffers(aType));
     509             : }
     510             : 
     511             : nsTArray<RefPtr<OmxPromiseLayer::BufferData>>*
     512           0 : OmxDataDecoder::GetBuffers(OMX_DIRTYPE aType)
     513             : {
     514           0 :   MOZ_ASSERT(aType == OMX_DIRTYPE::OMX_DirInput ||
     515             :              aType == OMX_DIRTYPE::OMX_DirOutput);
     516             : 
     517           0 :   if (aType == OMX_DIRTYPE::OMX_DirInput) {
     518           0 :     return &mInPortBuffers;
     519             :   }
     520           0 :   return &mOutPortBuffers;
     521             : }
     522             : 
     523             : void
     524           0 : OmxDataDecoder::ResolveInitPromise(const char* aMethodName)
     525             : {
     526           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     527           0 :   LOG("called from %s", aMethodName);
     528           0 :   mInitPromise.ResolveIfExists(mTrackInfo->GetType(), aMethodName);
     529           0 : }
     530             : 
     531             : void
     532           0 : OmxDataDecoder::RejectInitPromise(MediaResult aError, const char* aMethodName)
     533             : {
     534           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     535           0 :   mInitPromise.RejectIfExists(aError, aMethodName);
     536           0 : }
     537             : 
     538             : void
     539           0 : OmxDataDecoder::OmxStateRunner()
     540             : {
     541           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     542           0 :   LOG("OMX state: %s", StateTypeToStr(mOmxState));
     543             : 
     544             :   // TODO: maybe it'd be better to use promise CompletionPromise() to replace
     545             :   //       this state machine.
     546           0 :   if (mOmxState == OMX_StateLoaded) {
     547           0 :     ConfigCodec();
     548             : 
     549             :     // Send OpenMax state command to OMX_StateIdle.
     550           0 :     RefPtr<OmxDataDecoder> self = this;
     551           0 :     mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr)
     552             :       ->Then(mOmxTaskQueue, __func__,
     553           0 :              [self] () {
     554             :                // Current state should be OMX_StateIdle.
     555           0 :                self->mOmxState = self->mOmxLayer->GetState();
     556           0 :                MOZ_ASSERT(self->mOmxState == OMX_StateIdle);
     557           0 :              },
     558           0 :              [self] () {
     559           0 :                self->RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     560           0 :              });
     561             : 
     562             :     // Allocate input and output buffers.
     563           0 :     OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput};
     564           0 :     for(const auto id : types) {
     565           0 :       if (NS_FAILED(AllocateBuffers(id))) {
     566           0 :         LOG("Failed to allocate buffer on port %d", id);
     567           0 :         RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     568           0 :         break;
     569             :       }
     570             :     }
     571           0 :   } else if (mOmxState == OMX_StateIdle) {
     572           0 :     RefPtr<OmxDataDecoder> self = this;
     573           0 :     mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateExecuting, nullptr)
     574             :       ->Then(mOmxTaskQueue, __func__,
     575           0 :              [self] () {
     576           0 :                self->mOmxState = self->mOmxLayer->GetState();
     577           0 :                MOZ_ASSERT(self->mOmxState == OMX_StateExecuting);
     578             : 
     579           0 :                self->ResolveInitPromise(__func__);
     580           0 :              },
     581           0 :              [self] () {
     582           0 :                self->RejectInitPromise(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     583           0 :              });
     584           0 :   } else if (mOmxState == OMX_StateExecuting) {
     585             :     // Configure codec once it gets OMX_StateExecuting state.
     586           0 :     FillCodecConfigDataToOmx();
     587             :   } else {
     588           0 :     MOZ_ASSERT(0);
     589             :   }
     590           0 : }
     591             : 
     592             : void
     593           0 : OmxDataDecoder::ConfigCodec()
     594             : {
     595           0 :   OMX_ERRORTYPE err = mOmxLayer->Config();
     596           0 :   CHECK_OMX_ERR(err);
     597             : }
     598             : 
     599             : void
     600           0 : OmxDataDecoder::FillCodecConfigDataToOmx()
     601             : {
     602             :   // Codec configure data should be the first sample running on Omx TaskQueue.
     603           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     604           0 :   MOZ_ASSERT(!mMediaRawDatas.Length());
     605           0 :   MOZ_ASSERT(mOmxState == OMX_StateIdle || mOmxState == OMX_StateExecuting);
     606             : 
     607             : 
     608           0 :   RefPtr<BufferData> inbuf = FindAvailableBuffer(OMX_DirInput);
     609           0 :   RefPtr<MediaByteBuffer> csc;
     610           0 :   if (mTrackInfo->IsAudio()) {
     611           0 :     csc = mTrackInfo->GetAsAudioInfo()->mCodecSpecificConfig;
     612           0 :   } else if (mTrackInfo->IsVideo()) {
     613           0 :     csc = mTrackInfo->GetAsVideoInfo()->mExtraData;
     614             :   }
     615             : 
     616           0 :   MOZ_RELEASE_ASSERT(csc);
     617             : 
     618             :   // Some codecs like h264, its codec specific data is at the first packet, not in container.
     619           0 :   if (csc->Length()) {
     620           0 :     memcpy(inbuf->mBuffer->pBuffer,
     621           0 :            csc->Elements(),
     622           0 :            csc->Length());
     623           0 :     inbuf->mBuffer->nFilledLen = csc->Length();
     624           0 :     inbuf->mBuffer->nOffset = 0;
     625           0 :     inbuf->mBuffer->nFlags = (OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG);
     626             : 
     627           0 :     LOG("Feed codec configure data to OMX component");
     628           0 :     mOmxLayer->EmptyBuffer(inbuf)->Then(mOmxTaskQueue, __func__, this,
     629             :                                         &OmxDataDecoder::EmptyBufferDone,
     630           0 :                                         &OmxDataDecoder::EmptyBufferFailure);
     631             :   }
     632           0 : }
     633             : 
     634             : bool
     635           0 : OmxDataDecoder::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2)
     636             : {
     637           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     638             : 
     639           0 :   if (mOmxLayer->Event(aEvent, aData1, aData2)) {
     640           0 :     return true;
     641             :   }
     642             : 
     643           0 :   switch (aEvent) {
     644             :     case OMX_EventPortSettingsChanged:
     645             :     {
     646             :       // Don't always disable port. See bug 1235340.
     647           0 :       if (aData2 == 0 ||
     648             :           aData2 == OMX_IndexParamPortDefinition) {
     649             :         // According to spec: "To prevent the loss of any input data, the
     650             :         // component issuing the OMX_EventPortSettingsChanged event on its input
     651             :         // port should buffer all input port data that arrives between the
     652             :         // emission of the OMX_EventPortSettingsChanged event and the arrival of
     653             :         // the command to disable the input port."
     654             :         //
     655             :         // So client needs to disable port and reallocate buffers.
     656           0 :         MOZ_ASSERT(mPortSettingsChanged == -1);
     657           0 :         mPortSettingsChanged = aData1;
     658             :       }
     659           0 :       LOG("Got OMX_EventPortSettingsChanged event");
     660           0 :       break;
     661             :     }
     662             :     default:
     663             :     {
     664             :       // Got error during decoding, send msg to MFR skipping to next key frame.
     665           0 :       if (aEvent == OMX_EventError && mOmxState == OMX_StateExecuting) {
     666           0 :         NotifyError((OMX_ERRORTYPE)aData1, __func__,
     667           0 :                     MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__));
     668           0 :         return true;
     669             :       }
     670           0 :       LOG("WARNING: got none handle event: %d, aData1: %ld, aData2: %ld",
     671             :           aEvent, aData1, aData2);
     672           0 :       return false;
     673             :     }
     674             :   }
     675             : 
     676           0 :   return true;
     677             : }
     678             : 
     679             : bool
     680           0 : OmxDataDecoder::BuffersCanBeReleased(OMX_DIRTYPE aType)
     681             : {
     682           0 :   BUFFERLIST* buffers = GetBuffers(aType);
     683           0 :   uint32_t len = buffers->Length();
     684           0 :   for (uint32_t i = 0; i < len; i++) {
     685           0 :     BufferData::BufferStatus buf_status = buffers->ElementAt(i)->mStatus;
     686           0 :     if (buf_status == BufferData::BufferStatus::OMX_COMPONENT ||
     687             :         buf_status == BufferData::BufferStatus::OMX_CLIENT_OUTPUT) {
     688           0 :       return false;
     689             :     }
     690             :   }
     691           0 :   return true;
     692             : }
     693             : 
     694             : OMX_DIRTYPE
     695           0 : OmxDataDecoder::GetPortDirection(uint32_t aPortIndex)
     696             : {
     697             :   OMX_PARAM_PORTDEFINITIONTYPE def;
     698           0 :   InitOmxParameter(&def);
     699           0 :   def.nPortIndex = mPortSettingsChanged;
     700             : 
     701           0 :   OMX_ERRORTYPE err = mOmxLayer->GetParameter(OMX_IndexParamPortDefinition,
     702             :                                               &def,
     703           0 :                                               sizeof(def));
     704           0 :   if (err != OMX_ErrorNone) {
     705           0 :     return OMX_DirMax;
     706             :   }
     707           0 :   return def.eDir;
     708             : }
     709             : 
     710             : RefPtr<OmxPromiseLayer::OmxBufferPromise::AllPromiseType>
     711           0 : OmxDataDecoder::CollectBufferPromises(OMX_DIRTYPE aType)
     712             : {
     713           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     714             : 
     715           0 :   nsTArray<RefPtr<OmxBufferPromise>> promises;
     716           0 :   OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput};
     717           0 :   for (const auto type : types) {
     718           0 :     if ((aType == type) || (aType == OMX_DirMax)) {
     719             :       // find the buffer which has promise.
     720           0 :       BUFFERLIST* buffers = GetBuffers(type);
     721             : 
     722           0 :       for (uint32_t i = 0; i < buffers->Length(); i++) {
     723           0 :         BufferData* buf = buffers->ElementAt(i);
     724           0 :         if (!buf->mPromise.IsEmpty()) {
     725             :           // OmxBufferPromise is not exclusive, it can be multiple "Then"s, so it
     726             :           // is safe to call "Ensure" here.
     727           0 :           promises.AppendElement(buf->mPromise.Ensure(__func__));
     728             :         }
     729             :       }
     730             :     }
     731             :   }
     732             : 
     733           0 :   LOG("CollectBufferPromises: type %d, total %" PRIuSIZE " promiese", aType, promises.Length());
     734           0 :   if (promises.Length()) {
     735           0 :     return OmxBufferPromise::All(mOmxTaskQueue, promises);
     736             :   }
     737             : 
     738           0 :   nsTArray<BufferData*> headers;
     739           0 :   return OmxBufferPromise::AllPromiseType::CreateAndResolve(headers, __func__);
     740             : }
     741             : 
     742             : void
     743           0 : OmxDataDecoder::PortSettingsChanged()
     744             : {
     745           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     746             : 
     747           0 :   if (mPortSettingsChanged == -1 || mOmxState == OMX_STATETYPE::OMX_StateInvalid) {
     748           0 :     return;
     749             :   }
     750             : 
     751             :   // The PortSettingsChanged algorithm:
     752             :   //
     753             :   //   1. disable port.
     754             :   //   2. wait for port buffers return to client and then release these buffers.
     755             :   //   3. enable port.
     756             :   //   4. allocate port buffers.
     757             :   //
     758             : 
     759             :   // Disable port. Get port definition if the target port is enable.
     760             :   OMX_PARAM_PORTDEFINITIONTYPE def;
     761           0 :   InitOmxParameter(&def);
     762           0 :   def.nPortIndex = mPortSettingsChanged;
     763             : 
     764           0 :   OMX_ERRORTYPE err = mOmxLayer->GetParameter(OMX_IndexParamPortDefinition,
     765             :                                               &def,
     766           0 :                                               sizeof(def));
     767           0 :   CHECK_OMX_ERR(err);
     768             : 
     769           0 :   RefPtr<OmxDataDecoder> self = this;
     770           0 :   if (def.bEnabled) {
     771             :     // 1. disable port.
     772           0 :     LOG("PortSettingsChanged: disable port %lu", def.nPortIndex);
     773           0 :     mOmxLayer->SendCommand(OMX_CommandPortDisable, mPortSettingsChanged, nullptr)
     774           0 :       ->Then(mOmxTaskQueue, __func__,
     775           0 :              [self, def] () -> RefPtr<OmxCommandPromise> {
     776             :                // 3. enable port.
     777             :                // Send enable port command.
     778             :                RefPtr<OmxCommandPromise> p =
     779           0 :                  self->mOmxLayer->SendCommand(OMX_CommandPortEnable,
     780           0 :                                               self->mPortSettingsChanged,
     781           0 :                                               nullptr);
     782             : 
     783             :                // 4. allocate port buffers.
     784             :                // Allocate new port buffers.
     785           0 :                nsresult rv = self->AllocateBuffers(def.eDir);
     786           0 :                if (NS_FAILED(rv)) {
     787           0 :                  self->NotifyError(OMX_ErrorUndefined, __func__);
     788             :                }
     789             : 
     790           0 :                return p;
     791             :              },
     792           0 :              [self] (const OmxCommandFailureHolder& aError) {
     793           0 :                self->NotifyError(OMX_ErrorUndefined, __func__);
     794           0 :                return OmxCommandPromise::CreateAndReject(aError, __func__);
     795           0 :              })
     796             :       ->Then(mOmxTaskQueue, __func__,
     797           0 :              [self] () {
     798           0 :                LOGL("PortSettingsChanged: port settings changed complete");
     799             :                // finish port setting changed.
     800           0 :                self->mPortSettingsChanged = -1;
     801           0 :                self->FillAndEmptyBuffers();
     802           0 :              },
     803           0 :              [self] () {
     804           0 :                self->NotifyError(OMX_ErrorUndefined, __func__);
     805           0 :              });
     806             : 
     807             :     // 2. wait for port buffers return to client and then release these buffers.
     808             :     //
     809             :     // Port buffers will be returned to client soon once OMX_CommandPortDisable
     810             :     // command is sent. Then releasing these buffers.
     811           0 :     CollectBufferPromises(def.eDir)
     812             :       ->Then(mOmxTaskQueue, __func__,
     813           0 :           [self, def] () {
     814           0 :             MOZ_ASSERT(self->BuffersCanBeReleased(def.eDir));
     815           0 :             nsresult rv = self->ReleaseBuffers(def.eDir);
     816           0 :             if (NS_FAILED(rv)) {
     817           0 :               MOZ_RELEASE_ASSERT(0);
     818             :               self->NotifyError(OMX_ErrorUndefined, __func__);
     819             :             }
     820           0 :           },
     821           0 :           [self] () {
     822           0 :             self->NotifyError(OMX_ErrorUndefined, __func__);
     823           0 :           });
     824             :   }
     825             : }
     826             : 
     827             : void
     828           0 : OmxDataDecoder::SendEosBuffer()
     829             : {
     830           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     831             : 
     832             :   // There is no 'Drain' API in OpenMax, so it needs to wait for output sample
     833             :   // with EOS flag. However, MediaRawData doesn't provide EOS information,
     834             :   // so here it generates an empty BufferData with eos OMX_BUFFERFLAG_EOS in queue.
     835             :   // This behaviour should be compliant with spec, I think...
     836           0 :   RefPtr<MediaRawData> eos_data = new MediaRawData();
     837           0 :   mMediaRawDatas.AppendElement(eos_data);
     838           0 :   FillAndEmptyBuffers();
     839           0 : }
     840             : 
     841             : RefPtr<MediaDataDecoder::FlushPromise>
     842           0 : OmxDataDecoder::DoFlush()
     843             : {
     844           0 :   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
     845             : 
     846           0 :   mDecodedData.Clear();
     847           0 :   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     848           0 :   mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     849             : 
     850           0 :   RefPtr<FlushPromise> p = mFlushPromise.Ensure(__func__);
     851             : 
     852             :   // 1. Call OMX command OMX_CommandFlush in Omx TaskQueue.
     853             :   // 2. Remove all elements in mMediaRawDatas when flush is completed.
     854           0 :   mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
     855             :     ->Then(mOmxTaskQueue, __func__, this,
     856             :            &OmxDataDecoder::FlushComplete,
     857           0 :            &OmxDataDecoder::FlushFailure);
     858             : 
     859           0 :   return p;
     860             : }
     861             : 
     862             : void
     863           0 : OmxDataDecoder::FlushComplete(OMX_COMMANDTYPE aCommandType)
     864             : {
     865           0 :   mMediaRawDatas.Clear();
     866           0 :   mFlushing = false;
     867             : 
     868           0 :   LOG("Flush complete");
     869           0 :   mFlushPromise.ResolveIfExists(true, __func__);
     870           0 : }
     871             : 
     872           0 : void OmxDataDecoder::FlushFailure(OmxCommandFailureHolder aFailureHolder)
     873             : {
     874           0 :   mFlushing = false;
     875           0 :   mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     876           0 : }
     877             : 
     878           0 : MediaDataHelper::MediaDataHelper(const TrackInfo* aTrackInfo,
     879             :                                  layers::ImageContainer* aImageContainer,
     880           0 :                                  OmxPromiseLayer* aOmxLayer)
     881             :   : mTrackInfo(aTrackInfo)
     882             :   , mAudioCompactor(mAudioQueue)
     883           0 :   , mImageContainer(aImageContainer)
     884             : {
     885           0 :   InitOmxParameter(&mOutputPortDef);
     886           0 :   mOutputPortDef.nPortIndex = aOmxLayer->OutputPortIndex();
     887           0 :   aOmxLayer->GetParameter(OMX_IndexParamPortDefinition, &mOutputPortDef, sizeof(mOutputPortDef));
     888           0 : }
     889             : 
     890             : already_AddRefed<MediaData>
     891           0 : MediaDataHelper::GetMediaData(BufferData* aBufferData, bool& aPlatformDepenentData)
     892             : {
     893           0 :   aPlatformDepenentData = false;
     894           0 :   RefPtr<MediaData> data;
     895             : 
     896           0 :   if (mTrackInfo->IsAudio()) {
     897           0 :     if (!aBufferData->mBuffer->nFilledLen) {
     898           0 :       return nullptr;
     899             :     }
     900           0 :     data = CreateAudioData(aBufferData);
     901           0 :   } else if (mTrackInfo->IsVideo()) {
     902           0 :     data = aBufferData->GetPlatformMediaData();
     903           0 :     if (data) {
     904           0 :       aPlatformDepenentData = true;
     905             :     } else {
     906           0 :       if (!aBufferData->mBuffer->nFilledLen) {
     907           0 :         return nullptr;
     908             :       }
     909             :       // Get YUV VideoData, it uses more CPU, in most cases, on software codec.
     910           0 :       data = CreateYUV420VideoData(aBufferData);
     911             :     }
     912             : 
     913             :     // Update video time code, duration... from the raw data.
     914           0 :     VideoData* video(data->As<VideoData>());
     915           0 :     if (aBufferData->mRawData) {
     916           0 :       video->mTime = aBufferData->mRawData->mTime;
     917           0 :       video->mTimecode = aBufferData->mRawData->mTimecode;
     918           0 :       video->mOffset = aBufferData->mRawData->mOffset;
     919           0 :       video->mDuration = aBufferData->mRawData->mDuration;
     920           0 :       video->mKeyframe = aBufferData->mRawData->mKeyframe;
     921             :     }
     922             :   }
     923             : 
     924           0 :   return data.forget();
     925             : }
     926             : 
     927             : already_AddRefed<AudioData>
     928           0 : MediaDataHelper::CreateAudioData(BufferData* aBufferData)
     929             : {
     930           0 :   RefPtr<AudioData> audio;
     931           0 :   OMX_BUFFERHEADERTYPE* buf = aBufferData->mBuffer;
     932           0 :   const AudioInfo* info = mTrackInfo->GetAsAudioInfo();
     933           0 :   if (buf->nFilledLen) {
     934           0 :     uint64_t offset = 0;
     935           0 :     uint32_t frames = buf->nFilledLen / (2 * info->mChannels);
     936           0 :     if (aBufferData->mRawData) {
     937           0 :       offset = aBufferData->mRawData->mOffset;
     938             :     }
     939             :     typedef AudioCompactor::NativeCopy OmxCopy;
     940           0 :     mAudioCompactor.Push(offset,
     941           0 :                          buf->nTimeStamp,
     942           0 :                          info->mRate,
     943             :                          frames,
     944           0 :                          info->mChannels,
     945           0 :                          OmxCopy(buf->pBuffer + buf->nOffset,
     946             :                                  buf->nFilledLen,
     947           0 :                                  info->mChannels));
     948           0 :     audio = mAudioQueue.PopFront();
     949             :   }
     950             : 
     951           0 :   return audio.forget();
     952             : }
     953             : 
     954             : already_AddRefed<VideoData>
     955           0 : MediaDataHelper::CreateYUV420VideoData(BufferData* aBufferData)
     956             : {
     957           0 :   uint8_t *yuv420p_buffer = (uint8_t *)aBufferData->mBuffer->pBuffer;
     958           0 :   int32_t stride = mOutputPortDef.format.video.nStride;
     959           0 :   int32_t slice_height = mOutputPortDef.format.video.nSliceHeight;
     960           0 :   int32_t width = mTrackInfo->GetAsVideoInfo()->mImage.width;
     961           0 :   int32_t height = mTrackInfo->GetAsVideoInfo()->mImage.height;
     962             : 
     963             :   // TODO: convert other formats to YUV420.
     964           0 :   if (mOutputPortDef.format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar) {
     965           0 :     return nullptr;
     966             :   }
     967             : 
     968           0 :   size_t yuv420p_y_size = stride * slice_height;
     969           0 :   size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
     970           0 :   uint8_t *yuv420p_y = yuv420p_buffer;
     971           0 :   uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
     972           0 :   uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
     973             : 
     974           0 :   VideoData::YCbCrBuffer b;
     975           0 :   b.mPlanes[0].mData = yuv420p_y;
     976           0 :   b.mPlanes[0].mWidth = width;
     977           0 :   b.mPlanes[0].mHeight = height;
     978           0 :   b.mPlanes[0].mStride = stride;
     979           0 :   b.mPlanes[0].mOffset = 0;
     980           0 :   b.mPlanes[0].mSkip = 0;
     981             : 
     982           0 :   b.mPlanes[1].mData = yuv420p_u;
     983           0 :   b.mPlanes[1].mWidth = (width + 1) / 2;
     984           0 :   b.mPlanes[1].mHeight = (height + 1) / 2;
     985           0 :   b.mPlanes[1].mStride = (stride + 1) / 2;
     986           0 :   b.mPlanes[1].mOffset = 0;
     987           0 :   b.mPlanes[1].mSkip = 0;
     988             : 
     989           0 :   b.mPlanes[2].mData = yuv420p_v;
     990           0 :   b.mPlanes[2].mWidth =(width + 1) / 2;
     991           0 :   b.mPlanes[2].mHeight = (height + 1) / 2;
     992           0 :   b.mPlanes[2].mStride = (stride + 1) / 2;
     993           0 :   b.mPlanes[2].mOffset = 0;
     994           0 :   b.mPlanes[2].mSkip = 0;
     995             : 
     996           0 :   VideoInfo info(*mTrackInfo->GetAsVideoInfo());
     997             :   RefPtr<VideoData> data =
     998           0 :     VideoData::CreateAndCopyData(info,
     999             :                                  mImageContainer,
    1000             :                                  0, // Filled later by caller.
    1001           0 :                                  media::TimeUnit::Zero(), // Filled later by caller.
    1002           0 :                                  media::TimeUnit::FromMicroseconds(1), // We don't know the duration.
    1003             :                                  b,
    1004             :                                  0, // Filled later by caller.
    1005           0 :                                  media::TimeUnit::FromMicroseconds(-1),
    1006           0 :                                  info.ImageRect());
    1007             : 
    1008           0 :   LOG("YUV420 VideoData: disp width %d, height %d, pic width %d, height %d, time %lld",
    1009             :       info.mDisplay.width, info.mDisplay.height, info.mImage.width,
    1010             :       info.mImage.height, aBufferData->mBuffer->nTimeStamp);
    1011             : 
    1012           0 :   return data.forget();
    1013             : }
    1014             : 
    1015             : }

Generated by: LCOV version 1.13