tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

OmxDataDecoder.cpp (35467B)


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